W momencie, gdy zadano to pytanie, właśnie wydano pandy 0.23.0. Ta wersja zmieniła domyślne zachowanie .to_sql()
od wywołania DBAPI .executemany()
metoda konstruowania konstruktora wartości tabeli (TVC), który poprawiłby szybkość wysyłania poprzez wstawienie wielu wierszy za pomocą jednego .execute()
wywołanie instrukcji INSERT. Niestety takie podejście często przekraczało limit 2100 wartości parametrów T-SQL dla procedury składowanej, co prowadziło do błędu cytowanego w pytaniu.
Wkrótce potem kolejne wydanie pand dodało method=
argument do .to_sql()
. Wartość domyślna — method=None
– przywrócono poprzednie zachowanie przy użyciu .executemany()
, określając method="multi"
powie .to_sql()
aby użyć nowszego podejścia TVC.
Mniej więcej w tym samym czasie wydano SQLAlchemy 1.3 i dodano fast_executemany=True
argument do create_engine()
co znacznie poprawiło prędkość wysyłania przy użyciu sterowników ODBC firmy Microsoft dla SQL Server. Dzięki temu ulepszeniu method=None
okazał się co najmniej tak szybki jak method="multi"
unikając limitu 2100 parametrów.
Tak więc z obecnymi wersjami pand, SQLAlchemy i pyodbc, najlepszym podejściem do używania .to_sql()
ze sterownikami ODBC firmy Microsoft dla SQL Server jest użycie fast_executemany=True
i domyślne zachowanie .to_sql()
, czyli
connection_uri = (
"mssql+pyodbc://scott:tiger^[email protected]/db_name"
"?driver=ODBC+Driver+17+for+SQL+Server"
)
engine = create_engine(connection_uri, fast_executemany=True)
df.to_sql("table_name", engine, index=False, if_exists="append")
Jest to zalecane podejście w przypadku aplikacji działających w systemach Windows, macOS i wariantach systemu Linux obsługiwanych przez firmę Microsoft w przypadku sterownika ODBC. Jeśli potrzebujesz użyć FreeTDS ODBC, to .to_sql()
można wywołać za pomocą method="multi"
i chunksize=
jak opisano poniżej.
(Oryginalna odpowiedź)
Przed wersją pandy 0.23.0, to_sql
wygeneruje osobną WSTAWKĘ dla każdego wiersza w 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'
Przypuszczalnie w celu poprawy wydajności pandas 0.23.0 generuje teraz konstruktor wartości tabeli do wstawiania wielu wierszy na wywołanie
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'
Problem polega na tym, że procedury składowane SQL Server (w tym procedury składowane systemowe, takie jak sp_prepexec
) są ograniczone do 2100 parametrów, więc jeśli DataFrame ma 100 kolumn, to to_sql
może wstawić tylko około 20 wierszy na raz.
Możemy obliczyć wymagany chunksize
za pomocą
# 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, index=False, if_exists='replace',
method='multi', chunksize=tsql_chunksize)
Jednak najszybszym podejściem nadal będzie prawdopodobnie:
-
zrzucić DataFrame do pliku CSV (lub podobnego), a następnie
-
niech Python wywoła SQL Server
bcp
narzędzie do przesyłania tego pliku do tabeli.