Skip to content

Commit 1e5d1cc

Browse files
committed
ENH: to_sql() add parameter "method". Improve docs based on reviews (pandas-dev#8953)
1 parent 21e8c04 commit 1e5d1cc

File tree

3 files changed

+88
-50
lines changed

3 files changed

+88
-50
lines changed

doc/source/io.rst

+51
Original file line numberDiff line numberDiff line change
@@ -4752,6 +4752,57 @@ default ``Text`` type for string columns:
47524752
Because of this, reading the database table back in does **not** generate
47534753
a categorical.
47544754

4755+
.. _io.sql.method:
4756+
4757+
Insertion Method
4758+
++++++++++++++++
4759+
4760+
.. versionadded:: 0.24.0
4761+
4762+
The parameter ``method`` controls the SQL insertion clause used.
4763+
4764+
Possible values are:
4765+
4766+
- `'default'`: Uses standard SQL `INSERT` clause (one per row).
4767+
- `'multi'`: Pass multiple values in a single `INSERT` clause.
4768+
It uses a **special** SQL syntax not supported by all backends.
4769+
This usually provides better performance for Analytic databases
4770+
like *Presto* and *Redshit*, but has worse performance for
4771+
traditional SQL backend if the table contains many columns.
4772+
For more information check SQLAlchemy `documention
4773+
<http://docs.sqlalchemy.org/en/latest/core/dml.html?highlight=multivalues#sqlalchemy.sql.expression.Insert.values.params.*args>`__.
4774+
- callable: with signature `(pd_table, conn, keys, data_iter)`.
4775+
This can be used to implement more performant insertion based on
4776+
specific backend dialect features.
4777+
I.e. using *Postgresql* `COPY clause
4778+
<https://www.postgresql.org/docs/current/static/sql-copy.html>`__.
4779+
4780+
Example of callable for Postgresql *COPY*::
4781+
4782+
# Alternative to_sql() *method* for DBs that support COPY FROM
4783+
import csv
4784+
from io import StringIO
4785+
4786+
def psql_insert_copy(table, conn, keys, data_iter):
4787+
# gets a DBAPI connection that can provide a cursor
4788+
dbapi_conn = conn.connection
4789+
with dbapi_conn.cursor() as cur:
4790+
s_buf = StringIO()
4791+
writer = csv.writer(s_buf)
4792+
writer.writerows(data_iter)
4793+
s_buf.seek(0)
4794+
4795+
columns = ', '.join('"{}"'.format(k) for k in keys)
4796+
if table.schema:
4797+
table_name = '{}.{}'.format(table.schema, table.name)
4798+
else:
4799+
table_name = table.name
4800+
4801+
sql = 'COPY {} ({}) FROM STDIN WITH CSV'.format(
4802+
table_name, columns)
4803+
cur.copy_expert(sql=sql, file=s_buf)
4804+
4805+
47554806
Reading Tables
47564807
''''''''''''''
47574808

pandas/core/generic.py

+9-48
Original file line numberDiff line numberDiff line change
@@ -2055,6 +2055,15 @@ def to_sql(self, name, con, schema=None, if_exists='fail', index=True,
20552055
method : {'default', 'multi', callable}, default 'default'
20562056
Controls the SQL insertion clause used.
20572057
2058+
* `'default'`: Uses standard SQL `INSERT` clause (one per row).
2059+
* `'multi'`: Pass multiple values in a single `INSERT` clause.
2060+
* callable: with signature `(pd_table, conn, keys, data_iter)`.
2061+
2062+
Details and a sample callable implementation on
2063+
section :ref:`insert method <io.sql.method>`.
2064+
2065+
.. versionadded:: 0.24.0
2066+
20582067
Raises
20592068
------
20602069
ValueError
@@ -2122,54 +2131,6 @@ def to_sql(self, name, con, schema=None, if_exists='fail', index=True,
21222131
21232132
>>> engine.execute("SELECT * FROM integers").fetchall()
21242133
[(1,), (None,), (2,)]
2125-
2126-
Insertion method:
2127-
2128-
.. versionadded:: 0.24.0
2129-
2130-
The parameter ``method`` controls the SQL insertion clause used.
2131-
Possible values are:
2132-
2133-
- `'default'`: Uses standard SQL `INSERT` clause
2134-
- `'multi'`: Pass multiple values in a single `INSERT` clause.
2135-
It uses a **special** SQL syntax not supported by all backends.
2136-
This usually provides a big performance for Analytic databases
2137-
like *Presto* and *Redshit*, but has worse performance for
2138-
traditional SQL backend if the table contains many columns.
2139-
For more information check SQLAlchemy `documention <http://docs.sqlalchemy.org/en/latest/core/dml.html?highlight=multivalues#sqlalchemy.sql.expression.Insert.values.params.*args>`__.
2140-
- callable: with signature `(pd_table, conn, keys, data_iter)`.
2141-
This can be used to implement more performant insertion based on
2142-
specific backend dialect features.
2143-
I.e. using *Postgresql* `COPY clause
2144-
<https://www.postgresql.org/docs/current/static/sql-copy.html>`__.
2145-
Check API for details and a sample implementation
2146-
:func:`~pandas.DataFrame.to_sql`.
2147-
2148-
2149-
Example of callable for Postgresql *COPY*::
2150-
2151-
# Alternative to_sql() *method* for DBs that support COPY FROM
2152-
import csv
2153-
from io import StringIO
2154-
2155-
def psql_insert_copy(table, conn, keys, data_iter):
2156-
# gets a DBAPI connection that can provide a cursor
2157-
dbapi_conn = conn.connection
2158-
with dbapi_conn.cursor() as cur:
2159-
s_buf = StringIO()
2160-
writer = csv.writer(s_buf)
2161-
writer.writerows(data_iter)
2162-
s_buf.seek(0)
2163-
2164-
columns = ', '.join('"{}"'.format(k) for k in keys)
2165-
if table.schema:
2166-
table_name = '{}.{}'.format(table.schema, table.name)
2167-
else:
2168-
table_name = table.name
2169-
2170-
sql = 'COPY {} ({}) FROM STDIN WITH CSV'.format(
2171-
table_name, columns)
2172-
cur.copy_expert(sql=sql, file=s_buf)
21732134
"""
21742135
from pandas.io import sql
21752136
sql.to_sql(self, name, con, schema=schema, if_exists=if_exists,

pandas/io/sql.py

+28-2
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,14 @@ def to_sql(frame, name, con, schema=None, if_exists='fail', index=True,
436436
method : {'default', 'multi', callable}, default 'default'
437437
Controls the SQL insertion clause used.
438438
439+
* `'default'`: Uses standard SQL `INSERT` clause (one per row).
440+
* `'multi'`: Pass multiple values in a single `INSERT` clause.
441+
* callable: with signature `(pd_table, conn, keys, data_iter)`.
442+
443+
Details and a sample callable implementation on
444+
section :ref:`insert method <io.sql.method>`.
445+
446+
.. versionadded:: 0.24.0
439447
"""
440448
if if_exists not in ('fail', 'replace', 'append'):
441449
raise ValueError("'{0}' is not valid for if_exists".format(if_exists))
@@ -635,10 +643,10 @@ def insert_data(self):
635643

636644
return column_names, data_list
637645

638-
def insert(self, chunksize=None, method=None):
646+
def insert(self, chunksize=None, method='default'):
639647

640648
# set insert method
641-
if method in (None, 'default'):
649+
if method == 'default':
642650
exec_insert = self._execute_insert
643651
elif method == 'multi':
644652
exec_insert = self._execute_insert_multi
@@ -1142,6 +1150,15 @@ def to_sql(self, frame, name, if_exists='fail', index=True,
11421150
single value can be used.
11431151
method : {'default', 'multi', callable}, default 'default'
11441152
Controls the SQL insertion clause used.
1153+
1154+
* `'default'`: Uses standard SQL `INSERT` clause (one per row).
1155+
* `'multi'`: Pass multiple values in a single `INSERT` clause.
1156+
* callable: with signature `(pd_table, conn, keys, data_iter)`.
1157+
1158+
Details and a sample callable implementation on
1159+
section :ref:`insert method <io.sql.method>`.
1160+
1161+
.. versionadded:: 0.24.0
11451162
"""
11461163
if dtype and not is_dict_like(dtype):
11471164
dtype = {col_name: dtype for col_name in frame}
@@ -1499,6 +1516,15 @@ def to_sql(self, frame, name, if_exists='fail', index=True,
14991516
can be used.
15001517
method : {'default', 'multi', callable}, default 'default'
15011518
Controls the SQL insertion clause used.
1519+
1520+
* `'default'`: Uses standard SQL `INSERT` clause (one per row).
1521+
* `'multi'`: Pass multiple values in a single `INSERT` clause.
1522+
* callable: with signature `(pd_table, conn, keys, data_iter)`.
1523+
1524+
Details and a sample callable implementation on
1525+
section :ref:`insert method <io.sql.method>`.
1526+
1527+
.. versionadded:: 0.24.0
15021528
"""
15031529
if dtype and not is_dict_like(dtype):
15041530
dtype = {col_name: dtype for col_name in frame}

0 commit comments

Comments
 (0)