Atualmente, estou usando esse tipo de SQL no MySQL para inserir várias linhas de valores em uma única consulta:
INSERT INTO `tbl` (`key1`,`key2`) VALUES ('r1v1','r1v2'),('r2v1','r2v2'),...
Nas leituras da DOP, as instruções de uso preparadas devem me proporcionar uma segurança melhor do que as consultas estáticas.
Gostaria, portanto, de saber se é possível gerar "inserindo várias linhas de valores pelo uso de uma consulta" usando instruções preparadas.
Se sim, posso saber como implementá-lo?
php
pdo
insert
prepared-statement
Hoball
fonte
fonte
$stmt->execute($data);
php.net/manual/en/… Basicamente, todos os parâmetros são passados validados como strings. Basta percorrer os dados após criar a consulta e digitar manualmentebindValue
oubindParam
passar como terceiro argumento.Respostas:
Inserção de múltiplos valores com instruções preparadas para DOP
Inserir vários valores em uma instrução de execução. Por que, de acordo com esta página , é mais rápido que as inserções regulares.
mais valores de dados ou você provavelmente tem um loop que preenche os dados.
Com inserções preparadas, você precisa conhecer os campos nos quais está inserindo e o número de campos para criar os? espaços reservados para vincular seus parâmetros.
É basicamente assim que queremos que a instrução insert seja semelhante.
Agora, o código:
Embora no meu teste, houve apenas uma diferença de 1 segundo ao usar várias inserções e inserções regulares preparadas com valor único.
fonte
placeholders()
repetidamente. Chame-o uma vez antes do loop comsizeof($datafields)
e anexe a sequência de resultados ao$question_marks[]
interior do loop.Mesma resposta que o Sr. Balagtas, um pouco mais claro ...
As versões recentes do MySQL e PHP DOP fazer suporte multi-linha
INSERT
declarações.Visão Geral do SQL
O SQL terá algo parecido com isto, assumindo uma tabela de 3 colunas que você gostaria
INSERT
.ON DUPLICATE KEY UPDATE
funciona como esperado, mesmo com um INSERT com várias linhas; acrescente isto:Visão Geral do PHP
Seu código PHP seguirá as chamadas usuais
$pdo->prepare($qry)
e do$stmt->execute($params)
DOP.$params
será uma matriz unidimensional de todos os valores a serem passados paraINSERT
.No exemplo acima, ele deve conter 9 elementos; O PDO usará cada conjunto de 3 como uma única linha de valores. (Inserir 3 linhas de 3 colunas cada = matriz de 9 elementos.)
Implementação
O código abaixo é escrito para maior clareza, não eficiência. Trabalhe com as
array_*()
funções PHP para obter melhores maneiras de mapear ou percorrer seus dados, se desejar. Se você pode usar transações obviamente depende do seu tipo de tabela MySQL.Assumindo:
$tblName
- o nome da string da tabela para INSERT$colNames
- Matriz unidimensional dos nomes das colunas da tabela Esses nomes de colunas devem ser identificadores válidos das colunas do MySQL; escapá-los com backticks (``) se não estiverem$dataVals
- matriz multidimensional, em que cada elemento é uma matriz 1-d de uma linha de valores para INSERTCódigo de amostra
fonte
$rowPlaces
ser necessário:$allPlaces = implode(',', array_fill(0, count($dataVals), '('.str_pad('', (count($colNames)*2)-1, '?,').')'));
votes
ADD ORIGINALunique_index
(user
,email
,address
);array_push($dataToInsert, ...array_values($dataVals));
será muito mais rápido, entãoforeach ($dataVals as $row => $data) {}
Pelo que vale, tenho visto muitos usuários recomendando a iteração através de instruções INSERT, em vez de criar uma consulta de cadeia única como a resposta selecionada. Decidi executar um teste simples com apenas dois campos e uma instrução de inserção muito básica:
Enquanto a própria consulta geral levou milissegundos ou menos, a última (cadeia única) foi consistentemente 8 vezes mais rápida ou mais. Se isso foi criado para refletir uma importação de milhares de linhas em muito mais colunas, a diferença poderia ser enorme.
fonte
bind_param
declaração na segunda rotina de importação"?(?,?)
, certo?A resposta aceita por Herbert Balagtas funciona bem quando a matriz de dados $ é pequena. Com matrizes de dados $ maiores, a função array_merge torna-se proibitivamente lenta. Meu arquivo de teste para criar a matriz de dados $ tem 28 colunas e tem cerca de 80.000 linhas. O script final levou 41s para ser concluído.
O uso de array_push () para criar $ insert_values em vez de array_merge () resultou em uma velocidade de 100X com tempo de execução de 0,41s .
O problemático array_merge ():
Para eliminar a necessidade de array_merge (), você pode criar as duas matrizes a seguir:
Essas matrizes podem ser usadas da seguinte maneira:
fonte
array_push($data, ...array_values($row))
vez de$data = array_merge($data, array_values($row));
. Muito mais rapido.array_push()
está disponível mesmo em php 4.array_push()
, mas porque o @Mark está usando o argumento de descompactação. Notou a...array_values()
ligação lá?array_values()
está disponível no php 4. Não tenho certeza se é isso que você quer dizer comargument unpacking
.Duas abordagens possíveis:
Ou:
Se os dados de todas as linhas estiverem em uma única matriz, eu usaria a segunda solução.
fonte
$stmt->execute();
deveria estar fora do loop foreach?Simplesmente não é assim que você usa instruções preparadas.
Não há problema em inserir uma linha por consulta, porque você pode executar uma instrução preparada várias vezes com parâmetros diferentes. De fato, essa é uma das maiores vantagens, pois permite inserir um grande número de linhas de maneira eficiente, segura e confortável.
Portanto, talvez seja possível implementar o esquema que você propõe, pelo menos para um número fixo de linhas, mas é quase garantido que isso não é exatamente o que você deseja.
fonte
Uma resposta mais curta: achatar a matriz de dados ordenada pelas colunas e depois
Ao inserir mais ou menos 1.000 registros, você não precisa percorrer todos os registros para inseri-los quando tudo o que você precisa é contar os valores.
fonte
Aqui está minha abordagem simples.
fonte
On the readings on PDO, the use prepared statements should give me a better security than static queries.
$workouts_id
, o que pode ter$value
s com dados bastante inesperados. Você não pode garantir que talvez não agora, mas no futuro outro desenvolvedor torne esses dados inseguros. Então, acho muito mais correto fazer a consulta preparada pelo DOP.Aqui está uma classe que escrevi para fazer várias inserções com a opção de limpeza:
fonte
Foi assim que eu fiz:
Primeiro, defina os nomes das colunas que você usará ou deixe em branco e o pdo assumirá que você deseja usar todas as colunas da tabela. Nesse caso, você precisará informar os valores das linhas na ordem exata em que aparecem na tabela. .
Agora, suponha que você já tenha uma matriz bidimensional preparada. Itere-o e construa uma string com seus valores de linha, como:
Agora, o que você acabou de fazer foi verificar se $ linhas já estava definida e, se não, criar e armazenar valores de linha e a sintaxe SQL necessária para que seja uma instrução válida. Observe que as strings devem estar entre aspas duplas e aspas simples, para que sejam prontamente reconhecidas como tal.
Tudo o que resta fazer é preparar a instrução e executar, como tal:
Testado com até 2000 linhas até agora, e o tempo de execução é sombrio. Farei mais alguns testes e voltarei aqui caso eu tenha mais alguma coisa para contribuir.
Saudações.
fonte
Como ainda não foi sugerido, tenho certeza de que LOAD DATA INFILE ainda é a maneira mais rápida de carregar dados, pois desabilita a indexação, insere todos os dados e reativa os índices - tudo em uma única solicitação.
Salvar os dados como um CSV deve ser bastante trivial, tendo em mente o fputcsv. O MyISAM é mais rápido, mas você ainda obtém grande desempenho no InnoDB. Existem outras desvantagens, no entanto, eu seguiria essa rota se estiver inserindo muitos dados e não se incomodar com menos de 100 linhas.
fonte
Embora uma pergunta antiga todas as contribuições tenham me ajudado muito, aqui está a minha solução, que funciona dentro da minha própria
DbContext
classe. O$rows
parâmetro é simplesmente uma matriz de matrizes associativas representando linhas ou modelos:field name => insert value
.Se você usar um padrão que usa modelos, isso se encaixa perfeitamente quando os dados do modelo são transmitidos como uma matriz, digamos de um
ToRowArray
método dentro da classe de modelo.fonte
$tableName
exposto ao usuário, o que não é, está no DAL. Você pode expandir suas reivindicações? Não é útil apenas dizer coisas.$tableName
?Aqui está outra solução (reduzida) para esse problema:
Primeiro, você precisa contar os dados da matriz de origem (aqui: $ aData) com count (). Então você usa array_fill () e gera uma nova matriz com tantas entradas quanto a matriz de origem, cada uma com o valor "(?,?)" (O número de espaços reservados depende dos campos que você usa; aqui: 2). Em seguida, a matriz gerada precisa ser implodida e como cola é usada uma vírgula. No loop foreach, você precisa gerar outro índice com relação ao número de espaços reservados que você usa (número de espaços reservados * índice atual da matriz + 1). Você precisa adicionar 1 ao índice gerado após cada valor vinculado.
fonte
Você pode inserir várias linhas em uma única consulta com esta função:
$ row é uma matriz de matrizes de valores. No seu caso, você chamaria a função com
Isso tem o benefício de você usar instruções preparadas , enquanto insere várias linhas com uma única consulta. Segurança!
fonte
Aqui está a minha solução: https://github.com/sasha-ch/Aura.Sql base na biblioteca auraphp / Aura.Sql.
Exemplo de uso:
Relatórios de erros são bem-vindos.
fonte
Meu exemplo do mundo real para inserir todos os códigos postais alemães em uma tabela vazia (para adicionar nomes de cidades posteriormente):
Como você pode ver, é totalmente flexível. Você não precisa verificar a quantidade de colunas ou em qual posição sua coluna está. Você só precisa definir os dados de inserção:
Estou orgulhoso de alguns dos construtores de string de consulta, pois eles funcionam sem funções de matriz pesadas, como array_merge. Especialmente vsprintf () foi uma boa descoberta.
Finalmente, eu precisei adicionar 2x while () para evitar exceder o limite de memória. Isso depende do seu limite de memória, mas é uma boa solução geral para evitar problemas (e ter 10 consultas ainda é muito melhor que 10.000).
fonte
test.php
Database.php
fonte
Eu tive o mesmo problema e é assim que realizo para mim mesmo e criei uma função para ele (e você pode usá-lo se isso o ajudar).
Exemplo:
INSERIR EM VALORES DE países (país, cidade) (Alemanha, Berlim), (França, Paris);
Se insertMultipleData ($ table, $ multi_params) retornar TRUE , seus dados foram inseridos no banco de dados.
fonte
Com base em meus experimentos, descobri que a instrução de inserção do mysql com várias linhas de valor em uma única transação é a mais rápida.
No entanto, se os dados forem muitos, a
max_allowed_packet
configuração do mysql poderá restringir a inserção de transação única com várias linhas de valor. Portanto, as seguintes funções falharão quando houver dados maiores que omax_allowed_packet
tamanho do mysql :singleTransactionInsertWithRollback
singleTransactionInsertWithPlaceholders
singleTransactionInsert
O mais bem-sucedido no cenário de inserção de dados enormes é o
transactionSpeed
método, mas consome mais tempo os métodos mencionados acima. Portanto, para lidar com esse problema, você pode dividir seus dados em pedaços menores e chamar a inserção de transação única várias vezes ou diminuir a velocidade de execução usandotransactionSpeed
métodoAqui está minha pesquisa
Os resultados para 100.000 entradas para uma tabela que contém apenas duas colunas são os seguintes:
fonte
Isso funcionou para mim
fonte
que tal algo assim:
A idéia por trás disso é percorrer os valores da matriz, adicionando "números de identificação" a cada loop para os espaços reservados de instruções preparadas e, ao mesmo tempo, você adiciona os valores à matriz para os parâmetros de ligação. Se você não gosta de usar o índice "key" da matriz, pode adicionar $ i = 0 e $ i ++ dentro do loop. Ou funciona neste exemplo, mesmo se você tiver matrizes associativas com chaves nomeadas, ainda funcionará desde que as chaves sejam exclusivas. Com um pouco de trabalho, seria bom para matrizes aninhadas também ..
** Observe que substr retira as variáveis $ sql do último espaço e vírgula, se você não tiver um espaço, precisará alterá-lo para -1 em vez de -2.
fonte
A maioria das soluções fornecidas aqui para criar a consulta preparada é mais complexa do que precisa. Usando as funções incorporadas do PHP, você pode criar facilmente a instrução SQL sem sobrecarga significativa.
Dado que
$records
, uma matriz de registros em que cada registro é uma matriz indexada (na forma defield => value
), a função a seguir inserirá os registros na tabela fornecida$table
, em uma conexão PDO$connection
, usando apenas uma única instrução preparada. Observe que esta é uma solução PHP 5.6+ devido ao uso do argumento de descompactação na chamada paraarray_push
:fonte