diff --git a/doc/source/whatsnew/v2.2.0.rst b/doc/source/whatsnew/v2.2.0.rst index ea7850b6f2a5f..cbf409cd24246 100644 --- a/doc/source/whatsnew/v2.2.0.rst +++ b/doc/source/whatsnew/v2.2.0.rst @@ -74,6 +74,7 @@ enhancement2 Other enhancements ^^^^^^^^^^^^^^^^^^ +- :meth:`to_sql` with method parameter set to ``multi`` works with Oracle on the backend - :attr:`Series.attrs` / :attr:`DataFrame.attrs` now uses a deepcopy for propagating ``attrs`` (:issue:`54134`). - :func:`read_csv` now supports ``on_bad_lines`` parameter with ``engine="pyarrow"``. (:issue:`54480`) - :func:`read_spss` now returns a :class:`DataFrame` that stores the metadata in :attr:`DataFrame.attrs`. (:issue:`54264`) diff --git a/pandas/io/sql.py b/pandas/io/sql.py index 0788d9da06eb9..6a62568fc4ce7 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -963,8 +963,12 @@ def _execute_insert_multi(self, conn, keys: list[str], data_iter) -> int: from sqlalchemy import insert data = [dict(zip(keys, row)) for row in data_iter] - stmt = insert(self.table).values(data) - result = conn.execute(stmt) + stmt = insert(self.table) + # conn.execute is used here to ensure compatibility with Oracle. + # Using stmt.values(data) would produce a multi row insert that + # isn't supported by Oracle. + # see: https://docs.sqlalchemy.org/en/20/core/dml.html#sqlalchemy.sql.expression.Insert.values + result = conn.execute(stmt, data) return result.rowcount def insert_data(self) -> tuple[list[str], list[np.ndarray]]: