Pada saat pertanyaan ini diajukan, pandas 0.23.0 baru saja dirilis. Versi itu mengubah perilaku default .to_sql()
dari memanggil DBAPI .executemany()
metode untuk membuat konstruktor nilai tabel (TVC) yang akan meningkatkan kecepatan unggah dengan menyisipkan beberapa baris dengan satu .execute()
panggilan pernyataan INSERT. Sayangnya pendekatan itu sering melebihi batas T-SQL dari 2100 nilai parameter untuk prosedur tersimpan, yang mengarah ke kesalahan yang dikutip dalam pertanyaan.
Tak lama kemudian, rilis panda berikutnya menambahkan method=
argumen ke .to_sql()
. Default – method=None
– memulihkan perilaku sebelumnya menggunakan .executemany()
, sambil menentukan method="multi"
akan memberi tahu .to_sql()
untuk menggunakan pendekatan TVC yang lebih baru.
Sekitar waktu yang sama, SQLAlchemy 1.3 dirilis dan menambahkan fast_executemany=True
argumen ke create_engine()
yang sangat meningkatkan kecepatan unggah menggunakan driver ODBC Microsoft untuk SQL Server. Dengan peningkatan itu, method=None
terbukti setidaknya secepat method="multi"
sambil menghindari batas parameter 2.100.
Jadi dengan versi panda, SQLAlchemy, dan pyodbc saat ini, pendekatan terbaik untuk menggunakan .to_sql()
dengan driver ODBC Microsoft untuk SQL Server adalah menggunakan fast_executemany=True
dan perilaku default .to_sql()
, yaitu,
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")
Ini adalah pendekatan yang disarankan untuk aplikasi yang berjalan di Windows, macOS, dan varian Linux yang didukung Microsoft untuk driver ODBC-nya. Jika Anda perlu menggunakan FreeTDS ODBC, maka .to_sql()
dapat dipanggil dengan method="multi"
dan chunksize=
seperti yang dijelaskan di bawah ini.
(Jawaban asli)
Sebelum pandas versi 0.23.0, to_sql
akan menghasilkan INSERT terpisah untuk setiap baris di 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'
Agaknya untuk meningkatkan kinerja, pandas 0.23.0 sekarang menghasilkan konstruktor nilai tabel untuk menyisipkan beberapa baris per panggilan
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'
Masalahnya adalah bahwa prosedur tersimpan SQL Server (termasuk prosedur tersimpan sistem seperti sp_prepexec
) dibatasi hingga 2100 parameter, jadi jika DataFrame memiliki 100 kolom maka to_sql
hanya dapat menyisipkan sekitar 20 baris sekaligus.
Kami dapat menghitung chunksize
yang diperlukan menggunakan
# 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)
Namun, pendekatan tercepat masih mungkin:
-
membuang DataFrame ke file CSV (atau yang serupa), lalu
-
minta Python memanggil SQL Server
bcp
utilitas untuk mengunggah file itu ke dalam tabel.