Je cherche le moyen le plus efficace d'insérer en masse des millions de nuplets dans une base de données. J'utilise Python, PostgreSQL et psycopg2 .
J'ai créé une longue liste de tulpes qui devraient être insérés dans la base de données, parfois avec des modificateurs tels que géométrique Simplify
.
La façon naïve de le faire serait de formater une liste d'instructions INSERT
, mais il y a trois autres méthodes que j'ai déjà lues:
pyformat
style pour une insertion paramétriqueexecutemany
sur la liste des n-uplets, et COPY
.Il semble que le premier moyen soit le plus efficace, mais j'apprécierais vos idées et vos extraits de code me disant comment bien faire les choses.
Oui, je voterais pour COPY, à condition que vous puissiez écrire un fichier sur le disque dur de server (et non sur celui sur lequel l'application est exécutée) car COPY lira uniquement le serveur.
Il existe un nouveau psycopg2 manual contenant des exemples pour toutes les options.
L'option COPY est la plus efficace. Puis l'exécutemany. Puis exécuter avec pyformat.
d'après mon expérience, executemany
n'est pas plus rapide que d'exécuter vous-même de nombreuses insertions, est le moyen le plus rapide de formater une seule INSERT
avec beaucoup de valeurs vous-même, peut-être que dans l'avenir, executemany
s'améliorera, mais pour l'instant, il est assez lent
je sous-classe un list
et surcharger la méthode append, donc quand a la liste atteint une certaine taille je formate l'INSERT pour l'exécuter
Vous pouvez utiliser une nouvelle bibliothèque upsert :
$ pip install upsert
(vous devrez peut-être pip install decorator
en premier)
conn = psycopg2.connect('dbname=mydatabase')
cur = conn.cursor()
upsert = Upsert(cur, 'mytable')
for (selector, setter) in myrecords:
upsert.row(selector, setter)
Où selector
est un objet dict
comme {'name': 'Chris Smith'}
et setter
est un dict
comme { 'age': 28, 'state': 'WI' }
C'est presque aussi rapide que d'écrire du code INSERT [/ UPDATE] personnalisé et de l'exécuter directement avec psycopg2
... et que cela n'explosera pas si la ligne existe déjà.
Toute personne utilisant SQLalchemy pourrait essayer la version 1.2 qui ajoutait la prise en charge de l'insertion en bloc pour utiliser psycopg2.extras.execute_batch () à la place de executemany lorsque vous initialisez votre moteur avec use_batch_mode = True comme:
engine = create_engine(
"postgresql+psycopg2://scott:tiger@Host/dbname",
use_batch_mode=True)
http://docs.sqlalchemy.org/en/latest/changelog/migration_12.html#change-4109
Ensuite, quelqu'un devra utiliser SQLalchmey pour ne pas essayer différentes combinaisons de sqla et psycopg2 et diriger SQL ensemble.
Le premier et le second seraient utilisés ensemble, pas séparément. Le troisième serait le plus efficace en termes de serveur, puisque le serveur ferait all le travail acharné.
Après quelques tests, unnest semble souvent être une option extrêmement rapide, comme je l’ai appris de @Clodoaldo Neto 's answer à une question similaire.
data = [(1, 100), (2, 200), ...] # list of tuples
cur.execute("""CREATE TABLE table1 AS
SELECT u.id, u.var1
FROM unnest(%s) u(id INT, var1 INT)""", (data,))
Cependant, il peut être délicat avec des données extrêmement volumineuses .
Une question très liée: Insertion en bloc avec SQLAlchemy ORM
_ {Tous les chemins mènent à Rome}, mais certains traversent des montagnes, nécessitent des ferries, mais si vous voulez vous y rendre rapidement, prenez simplement l'autoroute.
Dans ce cas, l’autoroute doit utiliser le execute_batch () feature de psycopg2 . La documentation dit le meilleur:
L’actuelle implémentation de executemany()
(avec un euphémisme extrêmement charitable) n’est pas particulièrement performante. Ces fonctions peuvent être utilisées pour accélérer l'exécution répétée d'une instruction par rapport à un ensemble de paramètres. En réduisant le nombre d'allers-retours sur les serveurs, les performances peuvent être meilleures que par le biais de executemany()
.
Dans mon propre test, execute_batch()
est environ deux fois plus rapide que executemany()
, et donne la possibilité de configurer la taille de la page pour une optimisation supplémentaire (si vous souhaitez extraire les derniers 2 ou 3% des performances du pilote).
La même fonctionnalité peut facilement être activée si vous utilisez SQLAlchemy en définissant use_batch_mode=True
en tant que paramètre lorsque vous instanciez le moteur avec create_engine()
.