Skip to content

Commit

Permalink
ENH: enable multivalues insert
Browse files Browse the repository at this point in the history
  • Loading branch information
danfrankj committed Feb 23, 2018
1 parent ce77b79 commit c62a9c1
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 3 deletions.
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v0.23.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ Other Enhancements
- :class:`IntervalIndex` and its associated constructor methods (``from_arrays``, ``from_breaks``, ``from_tuples``) have gained a ``dtype`` parameter (:issue:`19262`)
- Added :func:`SeriesGroupBy.is_monotonic_increasing` and :func:`SeriesGroupBy.is_monotonic_decreasing` (:issue:`17015`)
- :func:`DataFrame.from_dict` now accepts a ``columns`` argument that can be used to specify the column names when ``orient='index'`` is used (:issue:`18529`)
- :func:`pd.io.sql.to_sql` now performs a multivalue insert if the underlying connection supports this rather than inserting row by row.
SQL dialects supporting multivalue inserts include mysql, postgresql, sqlite and any dialect with `supports_multivalues_insert`. (:issue:`14315`, :issue:`8953`)

.. _whatsnew_0230.api_breaking:

Expand Down
14 changes: 11 additions & 3 deletions pandas/io/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,8 +572,15 @@ def create(self):
else:
self._execute_create()

def insert_statement(self):
return self.table.insert()
def insert_statement(self, data, conn):
"""
Generate tuple of SQLAlchemy insert statement and any arguments
to be executed by connection (via `_execute_insert`).
"""
dialect = getattr(conn, 'dialect', None)
if dialect and getattr(dialect, 'supports_multivalues_insert', False):
return (self.table.insert(data),)
return (self.table.insert(), data)

def insert_data(self):
if self.index is not None:
Expand Down Expand Up @@ -612,8 +619,9 @@ def insert_data(self):
return column_names, data_list

def _execute_insert(self, conn, keys, data_iter):
"""Insert data into this table with database connection"""
data = [{k: v for k, v in zip(keys, row)} for row in data_iter]
conn.execute(self.insert_statement(), data)
conn.execute(*self.insert_statement(data, conn))

def insert(self, chunksize=None):
keys, data_list = self.insert_data()
Expand Down
26 changes: 26 additions & 0 deletions pandas/tests/io/test_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -1665,6 +1665,29 @@ class Temporary(Base):

tm.assert_frame_equal(df, expected)

def test_insert_multivalues(self):
# issues addressed
# https://github.com/pandas-dev/pandas/issues/14315
# https://github.com/pandas-dev/pandas/issues/8953

db = sql.SQLDatabase(self.conn)
df = DataFrame({'A': [1, 0, 0], 'B': [1.1, 0.2, 4.3]})
table = sql.SQLTable("test_table", db, frame=df)
data = [
{'A': 1, 'B': 0.46},
{'A': 0, 'B': -2.06}
]
statement = table.insert_statement(data, conn=self.conn)[0]

if self.supports_multivalues_insert:
assert statement.parameters == data, (
'insert statement should be multivalues'
)
else:
assert statement.parameters is None, (
'insert statement should not be multivalues'
)


class _TestSQLAlchemyConn(_EngineToConnMixin, _TestSQLAlchemy):

Expand All @@ -1679,6 +1702,7 @@ class _TestSQLiteAlchemy(object):
"""
flavor = 'sqlite'
supports_multivalues_insert = True

@classmethod
def connect(cls):
Expand Down Expand Up @@ -1727,6 +1751,7 @@ class _TestMySQLAlchemy(object):
"""
flavor = 'mysql'
supports_multivalues_insert = True

@classmethod
def connect(cls):
Expand Down Expand Up @@ -1796,6 +1821,7 @@ class _TestPostgreSQLAlchemy(object):
"""
flavor = 'postgresql'
supports_multivalues_insert = True

@classmethod
def connect(cls):
Expand Down

0 comments on commit c62a9c1

Please sign in to comment.