J'ai besoin d'insérer plusieurs lignes avec une requête (le nombre de lignes n'est pas constant), je dois donc exécuter une requête comme celle-ci:
INSERT INTO t (a, b) VALUES (1, 2), (3, 4), (5, 6);
La seule façon dont je sais est
args = [(1,2), (3,4), (5,6)]
args_str = ','.join(cursor.mogrify("%s", (x, )) for x in args)
cursor.execute("INSERT INTO t (a, b) VALUES "+args_str)
mais je veux un moyen plus simple.
python
postgresql
psycopg2
Sergey Fedoseev
la source
la source
execute
stratégie. J'ai vu une accélération d'environ 100x grâce à cela!executemany
- être exécute un commit après chaque insertion. Si vous enveloppez le tout dans une transaction, cela accélérerait peut-être les choses?executemany
ne fait rien de optimal, il boucle juste et fait de nombreusesexecute
déclarations. En utilisant cette méthode, une insertion de 700 lignes vers un serveur distant est passée de 60 s à <2 s.+
semble pouvoir s'ouvrir à une injection SQL, j'ai l'impression que laexecute_values()
solution @Clodoaldo Neto est plus sûre.Nouvelle
execute_values
méthode dans Psycopg 2.7:La manière pythonique de le faire dans Psycopg 2.6:
Explication: Si les données à insérer sont données sous forme de liste de tuples comme dans
alors il est déjà dans le format exact requis comme
la
values
syntaxe de lainsert
clause attend une liste d'enregistrements comme dansinsert into t (a, b) values (1, 'x'),(2, 'y')
Psycopg
adapte un Pythontuple
à un Postgresqlrecord
.Le seul travail nécessaire est de fournir un modèle de liste d'enregistrements à remplir par psycopg
et placez-le dans la
insert
requêteImpression des
insert_query
sortiesPassons maintenant à la
Psycopg
substitution d'arguments habituelleOu simplement tester ce qui sera envoyé au serveur
Production:
la source
execute_values
j'ai pu faire fonctionner mon système à 1k enregistrements par minute jusqu'à 128k enregistrements par minuteMise à jour avec psycopg2 2.7:
Le classique
executemany()
est environ 60 fois plus lent que l'implémentation de @ ant32 (appelée "pliée") comme expliqué dans ce fil: https://www.postgresql.org/message-id/20170130215151.GA7081%40deb76.aryehleib.comCette implémentation a été ajoutée à psycopg2 dans la version 2.7 et s'appelle
execute_values()
:Réponse précédente:
Pour insérer plusieurs lignes, l'utilisation de la
VALUES
syntaxe multirow avecexecute()
est environ 10 fois plus rapide que l'utilisation de psycopg2executemany()
. En effet,executemany()
exécute juste de nombreusesINSERT
déclarations individuelles .Le code de @ ant32 fonctionne parfaitement en Python 2. Mais en Python 3,
cursor.mogrify()
retourne des octets,cursor.execute()
prend des octets ou des chaînes et','.join()
attend unestr
instance.Donc, dans Python 3, vous devrez peut-être modifier le code de @ ant32, en ajoutant
.decode('utf-8')
:Ou en utilisant uniquement des octets (avec
b''
oub""
):la source
curseur.copy_from est de loin la solution la plus rapide que j'ai trouvée pour les insertions en vrac. Voici l'essentiel que j'ai créé contenant une classe nommée IteratorFile qui permet à un itérateur produisant des chaînes d'être lu comme un fichier. Nous pouvons convertir chaque enregistrement d'entrée en une chaîne à l'aide d'une expression de générateur. Donc la solution serait
Pour cette taille triviale d'arguments, cela ne fera pas beaucoup de différence de vitesse, mais je vois de grandes accélérations lorsque l'on traite des milliers + de lignes. Il sera également plus efficace en termes de mémoire que de créer une chaîne de requête géante. Un itérateur ne conserverait jamais qu'un seul enregistrement d'entrée en mémoire à la fois, où à un moment donné vous manquerez de mémoire dans votre processus Python ou dans Postgres en construisant la chaîne de requête.
la source
Un extrait de la page de tutoriel de Psycopg2 sur Postgresql.org (voir en bas) :
Cela n'économise pas beaucoup de code, mais il est définitivement meilleur.
la source
INSERT
déclarations individuelles . Utile, mais pas la même chose qu'un seulVALUE
insert multi- d.Toutes ces techniques sont appelées «insertions étendues» dans la terminologie Postgres, et à partir du 24 novembre 2016, c'est toujours une tonne plus rapide que l'executemany () de psychopg2 et toutes les autres méthodes répertoriées dans ce fil (que j'ai essayé avant d'en venir à cela) répondre).
Voici un code qui n'utilise pas cur.mogrify et qui est agréable et simple à comprendre:
Mais il faut noter que si vous pouvez utiliser copy_from (), vous devez utiliser copy_from;)
la source
J'utilise la réponse de ant32 ci-dessus depuis plusieurs années. Cependant, j'ai trouvé que c'est une erreur dans python 3 car
mogrify
renvoie une chaîne d'octets.La conversion explicite en chaînes bytse est une solution simple pour rendre le code compatible python 3.
la source
Une autre approche agréable et efficace consiste à passer des lignes à insérer en tant qu'argument 1, qui est un tableau d'objets json.
Par exemple, vous passez un argument:
C'est un tableau, qui peut contenir n'importe quelle quantité d'objets à l'intérieur. Ensuite, votre SQL ressemble à:
Remarque: votre postgress doit être suffisamment nouveau pour prendre en charge json
la source
La solution cursor.copyfrom fournie par @ jopseph.sheedy ( https://stackoverflow.com/users/958118/joseph-sheedy ) ci-dessus ( https://stackoverflow.com/a/30721460/11100064 ) est en effet ultra-rapide.
Cependant, les exemples qu'il donne ne sont pas utilisables de manière générique pour un enregistrement avec un nombre quelconque de champs et il m'a fallu du temps pour comprendre comment l'utiliser correctement.
Le IteratorFile doit être instancié avec des champs séparés par des tabulations comme celui-ci (
r
est une liste de dictionnaires où chaque dict est un enregistrement):Pour généraliser pour un nombre arbitraire de champs, nous allons d'abord créer une chaîne de ligne avec le nombre correct d'onglets et d'espaces réservés de champ:
"{}\t{}\t{}....\t{}"
puis utiliser.format()
pour remplir les valeurs de champ*list(r.values())) for r in records
::fonction complète dans essentiel ici .
la source
Si vous utilisez SQLAlchemy, vous n'avez pas besoin de jouer avec la création manuelle de la chaîne, car SQLAlchemy prend en charge la génération d'une
VALUES
clause à plusieurs lignes pour une seuleINSERT
instruction :la source
insert_query
ligne. Ensuite, ilsession.execute()
suffit d'appeler laexecute()
déclaration de psycopg2 avec une seule chaîne massive. Donc, le "truc" consiste à construire tout d'abord l'objet de l'instruction d'insertion. J'utilise ceci pour insérer 200 000 lignes à la fois et j'ai vu des performances massives augmenter en utilisant ce code par rapport à la normaleexecutemany()
.execute_batch a été ajouté à psycopg2 depuis que cette question a été publiée.
Il est plus lent que execute_values mais plus simple à utiliser.
la source
execute_values
est plus rapide queexecute_batch
executemany accepte un tableau de tuples
https://www.postgresqltutorial.com/postgresql-python/insert/
la source
Si vous souhaitez insérer plusieurs lignes dans un même état d'insertion (en supposant que vous n'utilisez pas ORM), le moyen le plus simple jusqu'à présent pour moi serait d'utiliser une liste de dictionnaires. Voici un exemple:
Comme vous pouvez le voir, une seule requête sera exécutée:
la source
Utilisation de aiopg - L'extrait ci-dessous fonctionne parfaitement
la source
Enfin dans la version SQLalchemy1.2, cette nouvelle implémentation est ajoutée pour utiliser psycopg2.extras.execute_batch () au lieu d'executemany lorsque vous initialisez votre moteur avec use_batch_mode = True comme:
http://docs.sqlalchemy.org/en/latest/changelog/migration_12.html#change-4109
Ensuite, quelqu'un devrait utiliser SQLalchmey ne prend pas la peine d'essayer différentes combinaisons de sqla et psycopg2 et de SQL direct ensemble.
la source