Je télécharge des données Json à partir d'un site Web d'API et j'utilise la fonction to_sql de sqlalchemy, pyodbc et pandas pour insérer ces données dans un serveur MSSQL.
Je peux télécharger jusqu'à 10000 lignes, mais je dois limiter la taille des blocs à 10 sinon j'obtiens l'erreur suivante:
DBAPIError: (pyodbc.Error) ('07002', '[07002] [Microsoft] [SQL Server Native Client 11.0] Champ COUNT incorrect ou erreur de syntaxe (0) (SQLExecDirectW)') [SQL: 'INSERT INTO [TEMP_producing_entity_details]
Il y a environ 500 millions de lignes à télécharger, c'est juste ramper à cette vitesse. Un conseil sur une solution de contournement?
Merci,
METTRE À JOUR:
pandas 0.23.1 a annulé les changements problématiques introduits dans 0.23.0. Cependant, la meilleure solution pour les performances brutes reste l'approche CSV -> bcp
comme décrit ci-dessous.
METTRE À JOUR:
pandas 0.24.0 a apparemment réintroduit le problème (ref: ici )
(Réponse originale)
Avant pandas version 0.23.0, to_sql
générerait un INSERT distinct pour chaque ligne du DataTable:
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6)',
N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2)',
0,N'row000'
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6)',
N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2)',
1,N'row001'
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6)',
N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2)',
2,N'row002'
Vraisemblablement pour améliorer les performances, pandas 0.23.0 génère désormais un constructeur de valeur de table pour insérer plusieurs lignes par appel
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6),@P3 int,@P4 nvarchar(6),@P5 int,@P6 nvarchar(6)',
N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2), (@P3, @P4), (@P5, @P6)',
0,N'row000',1,N'row001',2,N'row002'
Le problème est que les procédures stockées SQL Server (y compris les procédures stockées système comme sp_prepexec
) sont limités à 2100 paramètres, donc si le DataFrame a 100 colonnes alors to_sql
ne peut insérer qu'environ 20 lignes à la fois.
Nous pouvons calculer le chunksize
requis en utilisant
# df is an existing DataFrame
#
# limit based on sp_prepexec parameter count
tsql_chunksize = 2097 // len(df.columns)
# cap at 1000 (limit for number of rows inserted by table-value constructor)
tsql_chunksize = 1000 if tsql_chunksize > 1000 else tsql_chunksize
#
df.to_sql('tablename', engine, if_exists='replace', index=False, chunksize=tsql_chunksize)
Cependant, l'approche la plus rapide est toujours susceptible d'être:
vider le DataFrame dans un fichier CSV (ou similaire), puis
avoir Python appelez l'utilitaire SQL Server bcp
pour télécharger ce fichier dans la table.
Apporté quelques modifications basées sur la réponse de Gord Thompson. Cela calculera automatiquement la taille du bloc et la gardera à la valeur entière la plus proche qui correspond à la limite des paramètres 2100:
import math
df_num_of_cols=len(df.columns)
chunknum=math.floor(2100/df_num_of_cols)
df.to_sql('MY_TABLE',con=engine,schema='myschema',chunksize=chunknum,if_exists='append',method='multi',index=False )