J'utilise actuellement ce type de SQL sur MySQL pour insérer plusieurs lignes de valeurs dans une seule requête:
INSERT INTO `tbl` (`key1`,`key2`) VALUES ('r1v1','r1v2'),('r2v1','r2v2'),...
Sur les lectures sur PDO, l'utilisation des instructions préparées devrait me donner une meilleure sécurité que les requêtes statiques.
Je voudrais donc savoir s'il est possible de générer "l'insertion de plusieurs lignes de valeurs par l'utilisation d'une seule requête" à l'aide d'instructions préparées.
Si oui, puis-je savoir comment puis-je le mettre en œuvre?
php
pdo
insert
prepared-statement
Hoball
la source
la source
$stmt->execute($data);
php.net/manual/en/... Fondamentalement, tous les paramètres sont passés validés sous forme de chaînes. Il suffit de parcourir les données après la création de la requête, et manuellementbindValue
ou enbindParam
passant le type comme troisième argument.Réponses:
Insertion de valeurs multiples avec instructions préparées par PDO
Insertion de plusieurs valeurs dans une instruction d'exécution. Pourquoi parce que selon cette page, il est plus rapide que les insertions régulières.
plus de valeurs de données ou vous avez probablement une boucle qui remplit les données.
Avec les insertions préparées, vous devez connaître les champs dans lesquels vous insérez et le nombre de champs pour créer le? des espaces réservés pour lier vos paramètres.
C'est essentiellement à quoi nous voulons que l'instruction insert ressemble.
Maintenant, le code:
Bien que dans mon test, il n'y avait qu'une différence de 1 seconde lors de l'utilisation de plusieurs inserts et d'inserts préparés réguliers avec une seule valeur.
la source
placeholders()
encore et encore. Appelez-le une fois avant la boucle avecsizeof($datafields)
et ajoutez la chaîne de résultat à l'$question_marks[]
intérieur de la boucle.Même réponse que M. Balagtas, un peu plus claire ...
Les versions récentes de MySQL et PHP PDO prennent en charge les
INSERT
instructions multi-lignes .Présentation de SQL
Le SQL ressemblera à quelque chose comme ceci, en supposant une table à 3 colonnes que vous souhaitez
INSERT
.ON DUPLICATE KEY UPDATE
fonctionne comme prévu même avec un INSERT à plusieurs lignes; ajoutez ceci:Présentation de PHP
Votre code PHP suivra les appels habituels
$pdo->prepare($qry)
et$stmt->execute($params)
PDO.$params
sera un tableau unidimensionnel de toutes les valeurs à passer auINSERT
.Dans l'exemple ci-dessus, il doit contenir 9 éléments; PDO utilisera chaque ensemble de 3 comme une seule ligne de valeurs. (Insertion de 3 lignes de 3 colonnes chacune = tableau de 9 éléments.)
la mise en oeuvre
Le code ci-dessous est écrit pour plus de clarté, pas d'efficacité. Travaillez avec les
array_*()
fonctions PHP pour de meilleures façons de mapper ou de parcourir vos données si vous le souhaitez. La possibilité d'utiliser des transactions dépend évidemment de votre type de table MySQL.En supposant:
$tblName
- le nom de chaîne de la table à INSÉRER$colNames
- Tableau unidimensionnel des noms de colonnes de la table Ces noms de colonnes doivent être des identifiants de colonne MySQL valides; échappez-leur avec des backticks (``) s'ils ne le sont pas$dataVals
- tableau multidimensionnel, où chaque élément est un tableau 1-d d'une ligne de valeurs à INSÉRERExemple de code
la source
$rowPlaces
ne rend plus nécessaire:$allPlaces = implode(',', array_fill(0, count($dataVals), '('.str_pad('', (count($colNames)*2)-1, '?,').')'));
votes
ADD UNIQUEunique_index
(user
,email
,address
);array_push($dataToInsert, ...array_values($dataVals));
sera beaucoup plus rapide alorsforeach ($dataVals as $row => $data) {}
Pour ce que cela vaut, j'ai vu de nombreux utilisateurs recommander d'itérer les instructions INSERT au lieu de créer une requête de chaîne unique comme le faisait la réponse sélectionnée. J'ai décidé de lancer un test simple avec seulement deux champs et une instruction d'insertion très basique:
Alors que la requête globale elle-même prenait des millisecondes ou moins, la dernière requête (chaîne unique) était systématiquement 8 fois plus rapide ou plus. Si cela était conçu pour refléter une importation de milliers de lignes sur beaucoup plus de colonnes, la différence pourrait être énorme.
la source
bind_param
déclaration dans la deuxième routine d'importation"?(?,?)
, non?La réponse acceptée par Herbert Balagtas fonctionne bien lorsque le tableau $ data est petit. Avec des tableaux $ data plus grands, la fonction array_merge devient excessivement lente. Mon fichier de test pour créer le tableau $ data a 28 cols et fait environ 80 000 lignes. Le script final a pris 41 secondes pour se terminer.
L'utilisation de array_push () pour créer $ insert_values au lieu de array_merge () a entraîné une accélération de 100X avec un temps d'exécution de 0,41 s .
Le problème array_merge ():
Pour éliminer le besoin de array_merge (), vous pouvez créer les deux tableaux suivants à la place:
Ces tableaux peuvent ensuite être utilisés comme suit:
la source
array_push($data, ...array_values($row))
place de$data = array_merge($data, array_values($row));
. Plus vite.array_push()
est disponible même en php 4.array_push()
, mais parce que @Mark utilise la décompression d'arguments. Remarquez l'...array_values()
appel là-bas?array_values()
est également disponible en php 4. Je ne sais pas si c'est ce que vous entendez par làargument unpacking
.Deux approches possibles:
Ou:
Si les données de toutes les lignes sont dans un seul tableau, j'utiliserais la deuxième solution.
la source
$stmt->execute();
devrait être en dehors de la boucle foreach?Ce n'est tout simplement pas la façon dont vous utilisez les déclarations préparées.
Il est parfaitement acceptable d'insérer une ligne par requête car vous pouvez exécuter une instruction préparée plusieurs fois avec des paramètres différents. En fait, c'est l'un des plus grands avantages car il vous permet d'insérer un grand nombre de rangées de manière efficace, sûre et confortable.
Il est donc peut-être possible de mettre en œuvre le schéma que vous proposez, au moins pour un nombre fixe de lignes, mais il est presque garanti que ce n'est pas vraiment ce que vous voulez.
la source
Une réponse plus courte: aplatissez le tableau de données triées par colonnes puis
Lorsque vous insérez environ 1000 enregistrements, vous ne voulez pas avoir à parcourir chaque enregistrement pour les insérer lorsque tout ce dont vous avez besoin est un compte des valeurs.
la source
Voici mon approche simple.
la source
On the readings on PDO, the use prepared statements should give me a better security than static queries.
$workouts_id
, ce qui peut avoir des$value
données assez inattendues. Vous ne pouvez pas garantir que peut-être pas maintenant, mais à l'avenir, un autre développeur rendra ces données non sécurisées. Je pense donc que tout à fait plus juste faire la requête préparée par PDO.Voici une classe que j'ai écrite pour faire plusieurs insertions avec l'option de purge:
la source
Voici comment je l'ai fait:
Définissez d'abord les noms de colonnes que vous utiliserez, ou laissez ce champ vide et pdo supposera que vous voulez utiliser toutes les colonnes de la table - auquel cas vous devrez informer les valeurs de ligne dans l'ordre exact où elles apparaissent sur la table .
Maintenant, supposons que vous ayez déjà préparé un tableau à deux dimensions. Itérez-le et construisez une chaîne avec vos valeurs de ligne, comme suit:
Maintenant, vous venez de vérifier si $ rows était déjà défini, et sinon, créez-le et stockez les valeurs de ligne et la syntaxe SQL nécessaire pour que ce soit une instruction valide. Notez que les chaînes doivent être placées entre guillemets doubles et simples, afin qu'elles soient rapidement reconnues comme telles.
Il ne reste plus qu'à préparer l'instruction et à l'exécuter, en tant que telle:
Testé avec jusqu'à 2000 lignes jusqu'à présent, et le temps d'exécution est lamentable. Fera quelques tests supplémentaires et reviendra ici au cas où j'aurais quelque chose de plus à apporter.
Cordialement.
la source
Comme cela n'a pas encore été suggéré, je suis à peu près sûr que LOAD DATA INFILE est toujours le moyen le plus rapide de charger des données car il désactive l'indexation, insère toutes les données, puis réactive les index - le tout en une seule demande.
L'enregistrement des données au format csv devrait être assez simple en gardant à l'esprit fputcsv. MyISAM est le plus rapide, mais vous obtenez toujours de grandes performances dans InnoDB. Il y a d'autres inconvénients, donc je choisirais cette voie si vous insérez beaucoup de données et que vous ne vous embêtez pas avec moins de 100 lignes.
la source
Bien qu'une vieille question m'ait beaucoup aidé, voici ma solution, qui fonctionne dans ma propre
DbContext
classe. Le$rows
paramètre est simplement un tableau de tableaux associatifs représentant des lignes ou des modèles:field name => insert value
.Si vous utilisez un modèle qui utilise des modèles, cela s'intègre parfaitement lorsque les données du modèle sont transmises sous forme de tableau, par exemple à partir d'une
ToRowArray
méthode de la classe de modèle.la source
$tableName
est exposé à l'utilisateur, ce qui n'est pas le cas, c'est dans la DAL. Pouvez-vous développer vos revendications? Il n'est pas utile de simplement dire des choses.$tableName
?Voici une autre solution (mince) pour ce problème:
Au début, vous devez compter les données du tableau source (ici: $ aData) avec count (). Ensuite, vous utilisez array_fill () et générez un nouveau tableau avec autant d'entrées que le tableau source a, chacune avec la valeur "(?,?)" (Le nombre d'espaces réservés dépend des champs que vous utilisez; ici: 2). Ensuite, le tableau généré doit être implosé et comme colle une virgule est utilisée. Dans la boucle foreach, vous devez générer un autre index concernant le nombre d'espaces réservés que vous utilisez (nombre d'espaces réservés * index du tableau actuel + 1). Vous devez ajouter 1 à l'index généré après chaque valeur liée.
la source
Vous pouvez insérer plusieurs lignes dans une seule requête avec cette fonction:
$ row est un tableau de tableaux de valeurs. Dans votre cas, vous appelleriez la fonction avec
Cela présente l'avantage d'utiliser des instructions préparées , tout en insérant plusieurs lignes avec une seule requête. Sécurité!
la source
Voici ma solution: https://github.com/sasha-ch/Aura.Sql basée sur la bibliothèque auraphp / Aura.Sql.
Exemple d'utilisation:
Les rapports de bogues sont les bienvenus.
la source
Mon exemple du monde réel pour insérer tous les codes postaux allemands dans un tableau vide (pour ajouter des noms de villes plus tard):
Comme vous pouvez le voir, il est entièrement flexible. Vous n'avez pas besoin de vérifier le nombre de colonnes ou de vérifier la position de votre colonne. Il vous suffit de définir les données d'insertion:
Je suis fier de certains des constructeurs de chaînes de requête car ils fonctionnent sans fonctions de tableau lourdes comme array_merge. Surtout vsprintf () était une bonne trouvaille.
Enfin, j'ai dû ajouter 2x while () pour éviter de dépasser la limite de mémoire. Cela dépend de votre limite de mémoire, mais c'est une bonne solution générale pour éviter les problèmes (et avoir 10 requêtes est toujours bien mieux que 10.000).
la source
test.php
Database.php
la source
J'ai eu le même problème et c'est ainsi que j'accomplis pour moi-même, et je me suis créé une fonction pour cela (et vous pouvez l'utiliser si cela vous aide).
Exemple:
INSÉRER DANS LES VALEURS des pays (pays, ville) (Allemagne, Berlin), (France, Paris);
Si insertMultipleData ($ table, $ multi_params) renvoie TRUE , vos données ont été insérées dans votre base de données.
la source
Sur la base de mes expériences, j'ai découvert que l'instruction d'insertion mysql avec plusieurs lignes de valeur dans une seule transaction est la plus rapide.
Cependant, si les données sont trop nombreuses, le
max_allowed_packet
paramètre de mysql peut restreindre l'insertion de transaction unique avec plusieurs lignes de valeurs. Par conséquent, les fonctions suivantes échoueront lorsqu'il y a des données supérieures à lamax_allowed_packet
taille de mysql :singleTransactionInsertWithRollback
singleTransactionInsertWithPlaceholders
singleTransactionInsert
La
transactionSpeed
méthode la plus réussie dans le scénario d'insertion de données volumineuses est la méthode, mais elle prend plus de temps que les méthodes mentionnées ci-dessus. Ainsi, pour gérer ce problème, vous pouvez diviser vos données en plus petits morceaux et appeler plusieurs fois l'insertion de transaction unique ou abandonner la vitesse d'exécution en utilisant latransactionSpeed
méthode.Voici mes recherches
Les résultats pour 100 000 entrées pour un tableau contenant seulement deux colonnes sont les suivants
la source
Cela a fonctionné pour moi
la source
qu'en est-il de quelque chose comme ça:
L'idée sous-jacente est de parcourir les valeurs de votre tableau, en ajoutant des «numéros d'identification» à chaque boucle pour vos espaces réservés d'instructions préparées, tandis qu'en même temps, vous ajoutez les valeurs à votre tableau pour les paramètres de liaison. Si vous n'aimez pas utiliser l'index "key" du tableau, vous pouvez ajouter $ i = 0 et $ i ++ dans la boucle. Les deux fonctionnent dans cet exemple, même si vous avez des tableaux associatifs avec des clés nommées, cela fonctionnerait toujours à condition que les clés soient uniques. Avec un peu de travail, ce serait aussi bien pour les tableaux imbriqués.
** Notez que substr supprime le dernier espace et virgule des variables $ sql, si vous n'avez pas d'espace, vous devez le changer en -1 plutôt que -2.
la source
La plupart des solutions données ici pour créer la requête préparée sont plus complexes qu'elles doivent l'être. En utilisant les fonctions intégrées de PHP, vous pouvez facilement créer l'instruction SQL sans frais généraux importants.
Étant donné
$records
, un tableau d'enregistrements où chaque enregistrement est lui-même un tableau indexé (sous la forme defield => value
), la fonction suivante insérera les enregistrements dans la table donnée$table
, sur une connexion PDO$connection
, en n'utilisant qu'une seule instruction préparée. Notez qu'il s'agit d'une solution PHP 5.6+ en raison de l'utilisation de la décompression d'arguments dans l'appel àarray_push
:la source