diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 8cbc95f0349cf..4c14f9735157b 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -19,6 +19,7 @@ Other enhancements ^^^^^^^^^^^^^^^^^^ - :class:`Styler` may now render CSS more efficiently where multiple cells have the same styling (:issue:`30876`) +- When writing directly to a sqlite connection :func:`to_sql` now supports the ``multi`` method (:issue:`29921`) - - diff --git a/pandas/io/sql.py b/pandas/io/sql.py index 58fed0d18dd4a..d6cf0274bac70 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -1440,7 +1440,7 @@ def _execute_create(self): for stmt in self.table: conn.execute(stmt) - def insert_statement(self): + def insert_statement(self, *, num_rows): names = list(map(str, self.frame.columns)) wld = "?" # wildcard char escape = _get_valid_sqlite_name @@ -1451,15 +1451,22 @@ def insert_statement(self): bracketed_names = [escape(column) for column in names] col_names = ",".join(bracketed_names) - wildcards = ",".join([wld] * len(names)) + + row_wildcards = ",".join([wld] * len(names)) + wildcards = ",".join(f"({row_wildcards})" for _ in range(num_rows)) insert_statement = ( - f"INSERT INTO {escape(self.name)} ({col_names}) VALUES ({wildcards})" + f"INSERT INTO {escape(self.name)} ({col_names}) VALUES {wildcards}" ) return insert_statement def _execute_insert(self, conn, keys, data_iter): data_list = list(data_iter) - conn.executemany(self.insert_statement(), data_list) + conn.executemany(self.insert_statement(num_rows=1), data_list) + + def _execute_insert_multi(self, conn, keys, data_iter): + data_list = list(data_iter) + flattened_data = [x for row in data_list for x in row] + conn.execute(self.insert_statement(num_rows=len(data_list)), flattened_data) def _create_table_setup(self): """ diff --git a/pandas/tests/io/test_sql.py b/pandas/tests/io/test_sql.py index 45b3e839a08d1..0ad9f2c1e941f 100644 --- a/pandas/tests/io/test_sql.py +++ b/pandas/tests/io/test_sql.py @@ -2148,6 +2148,10 @@ def test_to_sql_replace(self): def test_to_sql_append(self): self._to_sql_append() + def test_to_sql_method_multi(self): + # GH 29921 + self._to_sql(method="multi") + def test_create_and_drop_table(self): temp_frame = DataFrame( {"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]}