web-dev-qa-db-fra.com

champ de comptage to_sql pyodbc incorrect ou erreur de syntaxe

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,

10
Ryan

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.

14
Gord Thompson

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 )
0
Amit S