Posso ligar uma matriz a uma condição IN ()?

562

Estou curioso para saber se é possível vincular uma matriz de valores a um espaço reservado usando o PDO. O caso de uso aqui está tentando passar uma matriz de valores para uso com uma IN()condição.

Eu gostaria de poder fazer algo assim:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

E faça com que o PDO ligue e cite todos os valores na matriz.

No momento eu estou fazendo:

<?php
$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
foreach($ids as &$val)
    $val=$db->quote($val); //iterate through array and quote
$in = implode(',',$ids); //create comma separated list
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$in.')'
);
$stmt->execute();
?>

O que certamente faz o trabalho, mas me pergunto se há uma solução embutida que estou faltando?

Andru
fonte
3
Um guia completo sobre como vincular uma matriz a uma condição IN () , incluindo o caso quando você tiver outros espaços reservados na consulta
Seu senso comum
A pergunta foi encerrada como uma duplicata desta pergunta . Inverti a flag duplicada porque esta pergunta é 4 anos mais antiga, tem 4 vezes as visualizações, 3 vezes o número de respostas e 12 vezes a pontuação. É claramente o alvo superior.
miken32 7/03
Qualquer pessoa olhando isso em 2020: você pode tentar o github.com/morris/dop para isso.
morris4

Respostas:

262

Eu acho que soulmerge está certo. você terá que construir a string de consulta.

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids), '?'));

$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

correção: dan, você estava certo. corrigiu o código (não o testou)

edit: chris (comments) e someoneisintrouble sugeriram que o loop foreach ...

(...)
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();

... pode ser redundante, então o foreachloop e o $stmt->executepodem ser substituídos por apenas ...

<?php 
  (...)
  $stmt->execute($ids);
?>

(novamente, eu não testei)

stefs
fonte
7
Essa é uma solução interessante e, embora eu prefira iterar sobre os IDs e chamar PDO :: quote (), acho que o índice do '?' espaços reservados ficarão bagunçados se houver outros espaços reservados primeiro em algum lugar da consulta, certo?
283 Andru
5
Sim, isso seria um problema. mas, nesse caso, você pode criar parâmetros nomeados em vez de?
2828 stefs
9
A pergunta antiga, mas digna de nota, acredito, é que o $foreache bindValue()não é necessário - basta executar com o array. Exemplo:$stmt->execute($ids);
Chris
2
A geração dos espaços reservados deve ser feita assimstr_repeat('?,', count($array) - 1). '?';
Xeoncross
8
Apenas uma dica para quem não sabe, você não pode misturar parâmetros com e sem nome. Portanto, se você usar parâmetros nomeados em sua consulta, alterne-os para? E depois aumente o deslocamento do índice bindValue para corresponder à posição dos IN? S com onde quer que eles estejam em relação ao outro? params.
Justinl
169

Para algo rápido:

//$db = new PDO(...);
//$ids = array(...);

$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);
DonVaughn
fonte
7
Excelente, não tinha pensado em usar o argumento input_parameters dessa maneira. Para aqueles cujas consultas têm mais parâmetros que a lista IN, você pode usar array_unshift e array_push para adicionar os argumentos necessários à frente e ao final da matriz. Além disso, eu prefiro $input_list = substr(str_repeat(',?', count($ids)), 1);
orca
7
Você também pode tentar str_repeat('?,', count($ids) - 1) . '?'. Menos uma chamada de função.
orca
5
@erfling, esta é uma declaração preparada, de onde virá a injeção? Ficarei feliz em fazer as correções se você puder fazer o backup com alguma prova real disso.
DonVaughn
5
@erfling, sim, isso é correto, e ligar os parâmetros é exatamente o que estamos fazendo neste exemplo através do envio de executeuma matriz de IDs
DonVaughn
5
Ah, de fato. De alguma forma, perdi o fato de que você estava passando a matriz. Isso realmente parece ser seguro e uma boa resposta. Me desculpe.
erfling
46

É tão importante usar a INdeclaração? Tente usar FIND_IN_SETop.

Por exemplo, há uma consulta no DOP como essa

SELECT * FROM table WHERE FIND_IN_SET(id, :array)

Então você só precisa vincular uma matriz de valores implodida por vírgula, como esta

$ids_string = implode(',', $array_of_smth); // WITHOUT WHITESPACES BEFORE AND AFTER THE COMMA
$stmt->bindParam('array', $ids_string);

e está feito.

UPD: Como algumas pessoas apontaram nos comentários a esta resposta, há algumas questões que devem ser explicitadas explicitamente.

  1. FIND_IN_SETnão usa índice em uma tabela e ainda não está implementado - veja esse registro no rastreador de erros do MYSQL . Obrigado a BillKarwin pelo aviso.
  2. Você não pode usar uma string com vírgula dentro como um valor da matriz para pesquisa. É impossível analisar essa sequência da maneira correta depois de implodeusar o símbolo de vírgula como separador. Obrigado a @VaL pela nota.

Em resumo, se você não depende muito de índices e não usa seqüências de caracteres com vírgula para pesquisa, minha solução será muito mais fácil, mais simples e mais rápida que as soluções listadas acima.

Tim Tonkonogov
fonte
25
IN () pode usar um índice e conta como uma varredura de intervalo. FIND_IN_SET () não pode usar um índice.
Bill Karwin
1
Isso é um ponto. Eu não sabia disso. Mas, de qualquer maneira, não há requisitos para desempenho na questão. Para tabelas não tão grandes, é muito mais melhor e mais limpo do que uma classe separada para gerar consultas com diferentes números de espaços reservados.
Tim Tonkonogov
11
Sim, mas quem tem uma mesa não tão grande hoje em dia? ;-)
Bill Karwin 8/13
3
Outro problema com essa abordagem é que, se houver string com vírgula dentro? Por exemplo ... FIND_IN_SET(description,'simple,search'), funcionará, mas FIND_IN_SET(description,'first value,text, with coma inside')falhará. Portanto, a função procurará em "first value", "text", "with coma inside" vez do desejado"first value", "text, with coma inside"
VaL 17/02/2015
33

Como eu faço muitas consultas dinâmicas, essa é uma função auxiliar super simples que eu criei.

public static function bindParamArray($prefix, $values, &$bindArray)
{
    $str = "";
    foreach($values as $index => $value){
        $str .= ":".$prefix.$index.",";
        $bindArray[$prefix.$index] = $value;
    }
    return rtrim($str,",");     
}

Use-o assim:

$bindString = helper::bindParamArray("id", $_GET['ids'], $bindArray);
$userConditions .= " AND users.id IN($bindString)";

Retorna uma string :id1,:id2,:id3e também atualiza suas $bindArrayligações necessárias quando for a hora de executar sua consulta. Fácil!

programador
fonte
6
Essa é uma solução muito melhor, pois não quebra a regra de vinculação de parâmetros. Isso é muito mais seguro do que ter sql embutido, conforme proposto por alguns outros aqui.
Dimitar Darazhanski
1
Maravilha. Elegante. Perfeito.
Ricalsin
17

A solução da EvilRygy não funcionou para mim. No Postgres, você pode executar outra solução alternativa:


$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (string_to_array(:an_array, ','))'
);
$stmt->bindParam(':an_array', implode(',', $ids));
$stmt->execute();
Sergey Galkin
fonte
Isso não funciona: ERROR: operator does not exist: integer = text. Pelo menos você precisa adicionar conversão explícita.
Collimarco
17

Uma maneira muito limpa para o postgres é usar o postgres-array ("{}"):

$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));
ESCOBAR
fonte
bloquear injeção sql?
Fábio Zangirolami 22/02
1
@ FábioZangirolami é DOP, então sim.
ESCOBAR
SIM, DOP! depois de 4 anos. Sua resposta foi boa para mim, muito simples e eficaz. obrigado!!!
Fábio Zangirolami 23/02
14

Aqui está a minha solução:

$total_items = count($array_of_items);
$question_marks = array_fill(0, $total_items, '?');
$sql = 'SELECT * FROM foo WHERE bar IN (' . implode(',', $question_marks ). ')';

$stmt = $dbh->prepare($sql);
$stmt->execute(array_values($array_of_items));

Observe o uso de array_values. Isso pode corrigir os principais problemas de pedidos.

Eu estava mesclando matrizes de IDs e removendo itens duplicados. Eu tinha algo como:

$ids = array(0 => 23, 1 => 47, 3 => 17);

E isso estava falhando.

Progrock
fonte
Esta foi uma excelente solução. Ao contrário da construção da string de consulta, isso usa a ligação corretamente. Eu recomendo fortemente este.
Lindylead 19/01
Nota: Usando sua solução, você pode adicionar itens à frente ou atrás da matriz, para poder incluir outras ligações. $original_array Adicionar itens antes da matriz original: array_unshift($original_array, new_unrelated_item); Adicionar itens após a matriz original: array_push($original_array, new_unrelated_item); Quando os valores estiverem vinculados, os itens new_unrelated serão colocados nos locais corretos. Isso permite que você misture itens de matriz e não matriz. `
Lindylead 19/01
12

Estendi a DOP para fazer algo semelhante ao sugerido pelos stefs, e foi mais fácil para mim a longo prazo:

class Array_Capable_PDO extends PDO {
    /**
     * Both prepare a statement and bind array values to it
     * @param string $statement mysql query with colon-prefixed tokens
     * @param array $arrays associatve array with string tokens as keys and integer-indexed data arrays as values 
     * @param array $driver_options see php documention
     * @return PDOStatement with given array values already bound 
     */
    public function prepare_with_arrays($statement, array $arrays, $driver_options = array()) {

        $replace_strings = array();
        $x = 0;
        foreach($arrays as $token => $data) {
            // just for testing...
            //// tokens should be legit
            //assert('is_string($token)');
            //assert('$token !== ""');
            //// a given token shouldn't appear more than once in the query
            //assert('substr_count($statement, $token) === 1');
            //// there should be an array of values for each token
            //assert('is_array($data)');
            //// empty data arrays aren't okay, they're a SQL syntax error
            //assert('count($data) > 0');

            // replace array tokens with a list of value tokens
            $replace_string_pieces = array();
            foreach($data as $y => $value) {
                //// the data arrays have to be integer-indexed
                //assert('is_int($y)');
                $replace_string_pieces[] = ":{$x}_{$y}";
            }
            $replace_strings[] = '('.implode(', ', $replace_string_pieces).')';
            $x++;
        }
        $statement = str_replace(array_keys($arrays), $replace_strings, $statement);
        $prepared_statement = $this->prepare($statement, $driver_options);

        // bind values to the value tokens
        $x = 0;
        foreach($arrays as $token => $data) {
            foreach($data as $y => $value) {
                $prepared_statement->bindValue(":{$x}_{$y}", $value);
            }
            $x++;
        }

        return $prepared_statement;
    }
}

Você pode usá-lo assim:

$db_link = new Array_Capable_PDO($dsn, $username, $password);

$query = '
    SELECT     *
    FROM       test
    WHERE      field1 IN :array1
     OR        field2 IN :array2
     OR        field3 = :value
';

$pdo_query = $db_link->prepare_with_arrays(
    $query,
    array(
        ':array1' => array(1,2,3),
        ':array2' => array(7,8,9)
    )
);

$pdo_query->bindValue(':value', '10');

$pdo_query->execute();
Chris
fonte
1
Eu lidei com a primeira parte do comentário de Mark, mas, como ele apontou, ainda não é seguro se um token semelhante :arrayestiver em uma sequência na consulta.
25413 Chris
4
Uma observação para todos os futuros leitores: Esta solução nunca deve ser usada. Declarações não se destinam ao código de produção
Seu senso comum
1
YCS: obrigado pelo feedback, interessado em sua opinião sobre a abordagem fora da adequação das declarações.
31513 Chris
1
A idéia é a mesma, mas apenas sem declarações e de maneira mais direta e explícita - não como uma exceção para apenas um caso, mas como uma maneira geral de criar todas as consultas. Cada espaço reservado é marcado com seu tipo. Isso torna if (is_array($data))desnecessárias as adivinhações (como uma), mas torna o processamento de dados muito mais preciso.
Seu senso comum
1
Para qualquer pessoa que esteja lendo os comentários: o problema mencionado por @Your Common Sense foi corrigido na revisão 4 .
usar o seguinte comando
11

Observando PDO: Constantes Predefinidas, não há PDO :: PARAM_ARRAY que você precisaria, conforme listado em PDOStatement-> bindParam

bool PDOStatement :: bindParam (misto parâmetro $, misto & $ variável [, int $ data_type [, int $ length [, misto $ driver_options]]])

Então, eu não acho que é viável.


fonte
1
Não sei se isso funciona. Eu acho que a string implodida é citada.
soulmerge
2
Você está correto, as aspas são escapadas para que não funcionem. Eu removi esse código.
11

Quando você tem outro parâmetro, pode fazer o seguinte:

$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$query = 'SELECT *
            FROM table
           WHERE X = :x
             AND id IN(';
$comma = '';
for($i=0; $i<count($ids); $i++){
  $query .= $comma.':p'.$i;       // :p0, :p1, ...
  $comma = ',';
}
$query .= ')';

$stmt = $db->prepare($query);
$stmt->bindValue(':x', 123);  // some value
for($i=0; $i<count($ids); $i++){
  $stmt->bindValue(':p'.$i, $ids[$i]);
}
$stmt->execute();
Daniel Miloca - Brasil
fonte
Obrigado pela ótima resposta. Este foi o único de todos que realmente funcionou para mim. No entanto, eu vi 1 erro. A variável $ rs deve ser $ stmt #
Piet Piet
11

Para mim, a solução mais sexy é construir uma matriz associativa dinâmica e usá-la

// A dirty array sent by user
$dirtyArray = ['Cecile', 'Gilles', 'Andre', 'Claude'];

// we construct an associative array like this
// [ ':name_0' => 'Cecile', ... , ':name_3' => 'Claude' ]
$params = array_combine(
    array_map(
        // construct param name according to array index
        function ($v) {return ":name_{$v}";},
        // get values of users
        array_keys($dirtyArray)
    ),
    $dirtyArray
);

// construct the query like `.. WHERE name IN ( :name_1, .. , :name_3 )`
$query = "SELECT * FROM user WHERE name IN( " . implode(",", array_keys($params)) . " )";
// here we go
$stmt  = $db->prepare($query);
$stmt->execute($params);
RousseauAlexandre
fonte
É difícil ter certeza sem tentar no cenário real, mas parece bom. + 1
Anant Singh --- Alive to Die
9

Também sei que esse encadeamento é antigo, mas tive um problema único em que, ao converter o driver mysql que seria preterido em driver PDO, tive que criar uma função que pudesse criar dinamicamente ambos os parâmetros normais e INs do mesmo matriz param. Então eu rapidamente construí isso:

/**
 * mysql::pdo_query('SELECT * FROM TBL_WHOOP WHERE type_of_whoop IN :param AND siz_of_whoop = :size', array(':param' => array(1,2,3), ':size' => 3))
 *
 * @param $query
 * @param $params
 */
function pdo_query($query, $params = array()){

    if(!$query)
        trigger_error('Could not query nothing');

    // Lets get our IN fields first
    $in_fields = array();
    foreach($params as $field => $value){
        if(is_array($value)){
            for($i=0,$size=sizeof($value);$i<$size;$i++)
                $in_array[] = $field.$i;

            $query = str_replace($field, "(".implode(',', $in_array).")", $query); // Lets replace the position in the query string with the full version
            $in_fields[$field] = $value; // Lets add this field to an array for use later
            unset($params[$field]); // Lets unset so we don't bind the param later down the line
        }
    }

    $query_obj = $this->pdo_link->prepare($query);
    $query_obj->setFetchMode(PDO::FETCH_ASSOC);

    // Now lets bind normal params.
    foreach($params as $field => $value) $query_obj->bindValue($field, $value);

    // Now lets bind the IN params
    foreach($in_fields as $field => $value){
        for($i=0,$size=sizeof($value);$i<$size;$i++)
            $query_obj->bindValue($field.$i, $value[$i]); // Both the named param index and this index are based off the array index which has not changed...hopefully
    }

    $query_obj->execute();

    if($query_obj->rowCount() <= 0)
        return null;

    return $query_obj;
}

Ainda não foi testado, no entanto, a lógica parece estar lá.

Espero que ajude alguém na mesma posição,

Edit: Depois de alguns testes, descobri:

  • O DOP não gosta de '.' em seus nomes (o que é meio estúpido se você me perguntar)
  • bindParam é a função errada, bindValue é a função correta.

Código editado para a versão de trabalho.

Sammaye
fonte
8

Uma pequena edição sobre o código de Schnalle

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids)-1, '?'));

$db   = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

//implode(',', array_fill(0, count($ids)-1), '?')); 
//'?' this should be inside the array_fill
//$stmt->bindValue(($k+1), $in); 
// instead of $in, it should be $id
Aaron Angelo Vicuna
fonte
1
Eu tive que remover -1 após a contagem ($ ids) para que funcionasse para mim ou sempre haveria um espaço reservado ausente.
Marcel Burkhard #
7

Qual banco de dados você está usando? No PostgreSQL, eu gosto de usar QUALQUER (array). Então, para reutilizar seu exemplo:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

Infelizmente, isso é bastante não portátil.

Em outros bancos de dados, você precisará criar sua própria magia, como outros já mencionaram. Você deseja colocar essa lógica em uma classe / função para torná-la reutilizável em todo o seu programa, é claro. Dê uma olhada nos comentários na mysql_querypágina no PHP.NET para mais algumas reflexões sobre o assunto e exemplos desse cenário.

Ryan Bair
fonte
4

Depois de passar pelo mesmo problema, fui para uma solução mais simples (embora ainda não seja tão elegante quanto uma PDO::PARAM_ARRAY seria):

dada a matriz $ids = array(2, 4, 32):

$newparams = array();
foreach ($ids as $n => $val){ $newparams[] = ":id_$n"; }

try {
    $stmt = $conn->prepare("DELETE FROM $table WHERE ($table.id IN (" . implode(", ",$newparams). "))");
    foreach ($ids as $n => $val){
        $stmt->bindParam(":id_$n", intval($val), PDO::PARAM_INT);
    }
    $stmt->execute();

... e assim por diante

Portanto, se você estiver usando uma matriz de valores mistos, precisará de mais código para testar seus valores antes de atribuir o tipo param:

// inside second foreach..

$valuevar = (is_float($val) ? floatval($val) : is_int($val) ? intval($val) :  is_string($val) ? strval($val) : $val );
$stmt->bindParam(":id_$n", $valuevar, (is_int($val) ? PDO::PARAM_INT :  is_string($val) ? PDO::PARAM_STR : NULL ));

Mas eu não testei este.

alan_mm
fonte
4

Como eu sei, não há nenhuma possibilidade de vincular uma matriz à declaração PDO.

Mas existem 2 soluções comuns:

  1. Use espaços reservados posicionais (?,?,?,?) Ou espaços reservados nomeados (: id1,: id2,: id3)

    $ whereIn = implode (',', array_fill (0, count ($ ids), '?'));

  2. Citação de matriz anteriormente

    $ whereIn = array_map (matriz ($ db, 'citação'), $ ids);

Ambas as opções são boas e seguras. Prefiro o segundo porque é mais curto e posso var_dump parâmetros se precisar. Usando espaços reservados, você deve vincular valores e, no final, seu código SQL será o mesmo.

$sql = "SELECT * FROM table WHERE id IN ($whereIn)";

E o último e importante para mim é evitar o erro "o número de variáveis ​​associadas não corresponde ao número de tokens"

Doutrina é um ótimo exemplo de uso de espaços reservados posicionais, apenas porque ele tem controle interno sobre os parâmetros recebidos.

Oleg Matei
fonte
4

Se a coluna puder conter apenas números inteiros, você provavelmente poderá fazer isso sem espaços reservados e apenas colocar os IDs na consulta diretamente. Você só precisa converter todos os valores da matriz para números inteiros. Como isso:

$listOfIds = implode(',',array_map('intval', $ids));
$stmt = $db->prepare(
    "SELECT *
     FROM table
     WHERE id IN($listOfIds)"
);
$stmt->execute();

Isso não deve ser vulnerável a nenhuma injeção de SQL.

Kodos Johnson
fonte
3

aqui está a minha solução. Também estendi a classe DOP:

class Db extends PDO
{

    /**
     * SELECT ... WHERE fieldName IN (:paramName) workaround
     *
     * @param array  $array
     * @param string $prefix
     *
     * @return string
     */
    public function CreateArrayBindParamNames(array $array, $prefix = 'id_')
    {
        $newparams = [];
        foreach ($array as $n => $val)
        {
            $newparams[] = ":".$prefix.$n;
        }
        return implode(", ", $newparams);
    }

    /**
     * Bind every array element to the proper named parameter
     *
     * @param PDOStatement $stmt
     * @param array        $array
     * @param string       $prefix
     */
    public function BindArrayParam(PDOStatement &$stmt, array $array, $prefix = 'id_')
    {
        foreach($array as $n => $val)
        {
            $val = intval($val);
            $stmt -> bindParam(":".$prefix.$n, $val, PDO::PARAM_INT);
        }
    }
}

Aqui está um exemplo de uso para o código acima:

$idList = [1, 2, 3, 4];
$stmt = $this -> db -> prepare("
  SELECT
    `Name`
  FROM
    `User`
  WHERE
    (`ID` IN (".$this -> db -> CreateArrayBindParamNames($idList)."))");
$this -> db -> BindArrayParam($stmt, $idList);
$stmt -> execute();
foreach($stmt as $row)
{
    echo $row['Name'];
}

Diz-me o que pensas

Lippai Zoltan
fonte
Esqueceu-se de mencionar que isso se baseia na resposta do usuário2188977 abaixo.
Lippai Zoltan
Não sei ao certo o que é getOne (), não parece fazer parte do PDO. Eu só vi isso na PEAR. O que isso faz exatamente?
Lippai Zoltan
@YourCommonSense, você pode postar sua função definida pelo usuário como resposta?
nullability
Eu sugeriria passar o tipo de dados para BindArrayParama matriz associativa, pois você parece estar limitando isso a números inteiros.
11263 Ian Brindley
2

Não é possível usar uma matriz como essa no PDO.

Você precisa criar uma string com um parâmetro (ou usar?) Para cada valor, por exemplo:

:an_array_0, :an_array_1, :an_array_2, :an_array_3, :an_array_4, :an_array_5

Aqui está um exemplo:

<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = join(
    ', ',
    array_map(
        function($index) {
            return ":an_array_$index";
        },
        array_keys($ids)
    )
);
$db = new PDO(
    'mysql:dbname=mydb;host=localhost',
    'user',
    'passwd'
);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$sqlAnArray.')'
);
foreach ($ids as $index => $id) {
    $stmt->bindValue("an_array_$index", $id);
}

Se você quiser continuar usando bindParam, faça o seguinte:

foreach ($ids as $index => $id) {
    $stmt->bindParam("an_array_$index", $ids[$id]);
}

Se você deseja usar ?espaços reservados, faça o seguinte:

<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = '?' . str_repeat(', ?', count($ids)-1);
$db = new PDO(
    'mysql:dbname=dbname;host=localhost',
    'user',
    'passwd'
);
$stmt = $db->prepare(
    'SELECT *
     FROM phone_number_lookup
     WHERE country_code IN('.$sqlAnArray.')'
);
$stmt->execute($ids);

Se você não sabe se $idsestá vazio, deve testá-lo e lidar com esse caso de acordo (retornar uma matriz vazia ou retornar um Objeto Nulo ou lançar uma exceção, ...).

Pedro Amaral Couto
fonte
0

Eu levei um pouco mais longe para obter a resposta mais próxima da pergunta original de usar espaços reservados para vincular os parâmetros.

Essa resposta precisará fazer dois loops na matriz a serem usados ​​na consulta. Mas resolve o problema de ter outros marcadores de coluna para consultas mais seletivas.

//builds placeholders to insert in IN()
foreach($array as $key=>$value) {
    $in_query = $in_query . ' :val_' . $key . ', ';
}

//gets rid of trailing comma and space
$in_query = substr($in_query, 0, -2);

$stmt = $db->prepare(
    "SELECT *
     WHERE id IN($in_query)";

//pind params for your placeholders.
foreach ($array as $key=>$value) {
    $stmt->bindParam(":val_" . $key, $array[$key])
}

$stmt->execute();
Joseph_J
fonte
0

você primeiro definiu o número de "?" na consulta e, em seguida, com um "for" envia parâmetros como este:

require 'dbConnect.php';
$db=new dbConnect();
$array=[];
array_push($array,'value1');
array_push($array,'value2');
$query="SELECT * FROM sites WHERE kind IN (";

foreach ($array as $field){
    $query.="?,";
}
$query=substr($query,0,strlen($query)-1);
$query.=")";
$tbl=$db->connection->prepare($query);
for($i=1;$i<=count($array);$i++)
    $tbl->bindParam($i,$array[$i-1],PDO::PARAM_STR);
$tbl->execute();
$row=$tbl->fetchAll(PDO::FETCH_OBJ);
var_dump($row);
Ali Chegini
fonte