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?
Respostas:
Eu acho que soulmerge está certo. você terá que construir a string de consulta.
correção: dan, você estava certo. corrigiu o código (não o testou)
edit: chris (comments) e someoneisintrouble sugeriram que o loop foreach ...
... pode ser redundante, então o
foreach
loop e o$stmt->execute
podem ser substituídos por apenas ...(novamente, eu não testei)
fonte
$foreach
ebindValue()
não é necessário - basta executar com o array. Exemplo:$stmt->execute($ids);
str_repeat('?,', count($array) - 1). '?';
Para algo rápido:
fonte
$input_list = substr(str_repeat(',?', count($ids)), 1);
str_repeat('?,', count($ids) - 1) . '?'
. Menos uma chamada de função.execute
uma matriz de IDsÉ tão importante usar a
IN
declaração? Tente usarFIND_IN_SET
op.Por exemplo, há uma consulta no DOP como essa
Então você só precisa vincular uma matriz de valores implodida por vírgula, como esta
e está feito.
UPD: Como algumas pessoas apontaram nos comentários a esta resposta, há algumas questões que devem ser explicitadas explicitamente.
FIND_IN_SET
nã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.implode
usar 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.
fonte
... FIND_IN_SET(description,'simple,search')
, funcionará, masFIND_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"
Como eu faço muitas consultas dinâmicas, essa é uma função auxiliar super simples que eu criei.
Use-o assim:
Retorna uma string
:id1,:id2,:id3
e também atualiza suas$bindArray
ligações necessárias quando for a hora de executar sua consulta. Fácil!fonte
A solução da EvilRygy não funcionou para mim. No Postgres, você pode executar outra solução alternativa:
fonte
ERROR: operator does not exist: integer = text
. Pelo menos você precisa adicionar conversão explícita.Uma maneira muito limpa para o postgres é usar o postgres-array ("{}"):
fonte
Aqui está a minha solução:
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:
E isso estava falhando.
fonte
$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. `Estendi a DOP para fazer algo semelhante ao sugerido pelos stefs, e foi mais fácil para mim a longo prazo:
Você pode usá-lo assim:
fonte
:array
estiver em uma sequência na consulta.if (is_array($data))
desnecessárias as adivinhações (como uma), mas torna o processamento de dados muito mais preciso.Observando PDO: Constantes Predefinidas, não há PDO :: PARAM_ARRAY que você precisaria, conforme listado em PDOStatement-> bindParam
Então, eu não acho que é viável.
fonte
Quando você tem outro parâmetro, pode fazer o seguinte:
fonte
Para mim, a solução mais sexy é construir uma matriz associativa dinâmica e usá-la
fonte
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:
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:
Código editado para a versão de trabalho.
fonte
Uma pequena edição sobre o código de Schnalle
fonte
Qual banco de dados você está usando? No PostgreSQL, eu gosto de usar QUALQUER (array). Então, para reutilizar seu exemplo:
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_query
página no PHP.NET para mais algumas reflexões sobre o assunto e exemplos desse cenário.fonte
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)
:... 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:
Mas eu não testei este.
fonte
Como eu sei, não há nenhuma possibilidade de vincular uma matriz à declaração PDO.
Mas existem 2 soluções comuns:
Use espaços reservados posicionais (?,?,?,?) Ou espaços reservados nomeados (: id1,: id2,: id3)
$ whereIn = implode (',', array_fill (0, count ($ ids), '?'));
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.
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.
fonte
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:
Isso não deve ser vulnerável a nenhuma injeção de SQL.
fonte
aqui está a minha solução. Também estendi a classe DOP:
Aqui está um exemplo de uso para o código acima:
Diz-me o que pensas
fonte
BindArrayParam
a matriz associativa, pois você parece estar limitando isso a números inteiros.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:
Se você quiser continuar usando
bindParam
, faça o seguinte:Se você deseja usar
?
espaços reservados, faça o seguinte:Se você não sabe se
$ids
está 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, ...).fonte
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.
fonte
você primeiro definiu o número de "?" na consulta e, em seguida, com um "for" envia parâmetros como este:
fonte