Skip to content

Commit f5388ca

Browse files
committed
ENH: Support multi row inserts in to_sql when using the sqlite fallback
Currently we do not support multi row inserts into sqlite databases when `to_sql` is passed `method="multi"` - despite the documentation suggesting that this is supported. Adding support for this is straightforward - it only needs us to implement a single method on the SQLiteTable class and so this PR does just that.
1 parent 171a1ed commit f5388ca

File tree

3 files changed

+16
-4
lines changed

3 files changed

+16
-4
lines changed

doc/source/whatsnew/v1.0.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,7 @@ I/O
10741074
- Bug in :func:`pandas.io.json.json_normalize` where a missing value in the location specified by `record_path` would raise a ``TypeError`` (:issue:`30148`)
10751075
- :func:`read_excel` now accepts binary data (:issue:`15914`)
10761076
- Bug in :meth:`read_csv` in which encoding handling was limited to just the string `utf-16` for the C engine (:issue:`24130`)
1077+
- When writing directly to a sqlite connection :func:`to_sql` now supports the ``multi`` method (:issue:`29921`)
10771078

10781079
Plotting
10791080
^^^^^^^^

pandas/io/sql.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,7 +1440,7 @@ def _execute_create(self):
14401440
for stmt in self.table:
14411441
conn.execute(stmt)
14421442

1443-
def insert_statement(self):
1443+
def insert_statement(self, *, num_rows):
14441444
names = list(map(str, self.frame.columns))
14451445
wld = "?" # wildcard char
14461446
escape = _get_valid_sqlite_name
@@ -1451,15 +1451,22 @@ def insert_statement(self):
14511451

14521452
bracketed_names = [escape(column) for column in names]
14531453
col_names = ",".join(bracketed_names)
1454-
wildcards = ",".join([wld] * len(names))
1454+
1455+
row_wildcards = ",".join([wld] * len(names))
1456+
wildcards = ",".join(f"({row_wildcards})" for _ in range(num_rows))
14551457
insert_statement = (
1456-
f"INSERT INTO {escape(self.name)} ({col_names}) VALUES ({wildcards})"
1458+
f"INSERT INTO {escape(self.name)} ({col_names}) VALUES {wildcards}"
14571459
)
14581460
return insert_statement
14591461

14601462
def _execute_insert(self, conn, keys, data_iter):
14611463
data_list = list(data_iter)
1462-
conn.executemany(self.insert_statement(), data_list)
1464+
conn.executemany(self.insert_statement(num_rows=1), data_list)
1465+
1466+
def _execute_insert_multi(self, conn, keys, data_iter):
1467+
data_list = list(data_iter)
1468+
flattened_data = [x for row in data_list for x in row]
1469+
conn.execute(self.insert_statement(num_rows=len(data_list)), flattened_data)
14631470

14641471
def _create_table_setup(self):
14651472
"""

pandas/tests/io/test_sql.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,6 +2148,10 @@ def test_to_sql_replace(self):
21482148
def test_to_sql_append(self):
21492149
self._to_sql_append()
21502150

2151+
def test_to_sql_method_multi(self):
2152+
# GH 29921
2153+
self._to_sql(method="multi")
2154+
21512155
def test_create_and_drop_table(self):
21522156
temp_frame = DataFrame(
21532157
{"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]}

0 commit comments

Comments
 (0)