Skip to content

Commit bfd280a

Browse files
committed
Merge pull request #7120 from jorisvandenbossche/sql-release
SQL: add release notes for refactor (GH6292)
2 parents 76f6cf0 + ad97d27 commit bfd280a

File tree

6 files changed

+111
-41
lines changed

6 files changed

+111
-41
lines changed

doc/source/io.rst

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3160,7 +3160,8 @@ your database.
31603160
.. versionadded:: 0.14.0
31613161

31623162
If SQLAlchemy is not installed, a fallback is only provided for sqlite (and
3163-
for mysql for backwards compatibility, but this is deprecated).
3163+
for mysql for backwards compatibility, but this is deprecated and will be
3164+
removed in a future version).
31643165
This mode requires a Python database adapter which respect the `Python
31653166
DB-API <http://www.python.org/dev/peps/pep-0249/>`__.
31663167

@@ -3190,14 +3191,12 @@ engine. You can use a temporary SQLite database where data are stored in
31903191
To connect with SQLAlchemy you use the :func:`create_engine` function to create an engine
31913192
object from database URI. You only need to create the engine once per database you are
31923193
connecting to.
3193-
31943194
For more information on :func:`create_engine` and the URI formatting, see the examples
31953195
below and the SQLAlchemy `documentation <http://docs.sqlalchemy.org/en/rel_0_9/core/engines.html>`__
31963196

31973197
.. ipython:: python
31983198
31993199
from sqlalchemy import create_engine
3200-
from pandas.io import sql
32013200
# Create your connection.
32023201
engine = create_engine('sqlite:///:memory:')
32033202
@@ -3280,8 +3279,6 @@ to pass to :func:`pandas.to_datetime`:
32803279
32813280
You can check if a table exists using :func:`~pandas.io.sql.has_table`
32823281

3283-
In addition, the class :class:`~pandas.io.sql.PandasSQLWithEngine` can be
3284-
instantiated directly for more manual control over the SQL interaction.
32853282

32863283
Querying
32873284
~~~~~~~~
@@ -3310,18 +3307,18 @@ variant appropriate for your database.
33103307

33113308
.. code-block:: python
33123309
3310+
from pandas.io import sql
33133311
sql.execute('SELECT * FROM table_name', engine)
3314-
33153312
sql.execute('INSERT INTO table_name VALUES(?, ?, ?)', engine, params=[('id', 1, 12.2, True)])
33163313
33173314
3318-
In addition, the class :class:`~pandas.io.sql.PandasSQLWithEngine` can be
3319-
instantiated directly for more manual control over the SQL interaction.
3320-
3321-
33223315
Engine connection examples
33233316
~~~~~~~~~~~~~~~~~~~~~~~~~~
33243317

3318+
To connect with SQLAlchemy you use the :func:`create_engine` function to create an engine
3319+
object from database URI. You only need to create the engine once per database you are
3320+
connecting to.
3321+
33253322
.. code-block:: python
33263323
33273324
from sqlalchemy import create_engine
@@ -3341,6 +3338,8 @@ Engine connection examples
33413338
# or absolute, starting with a slash:
33423339
engine = create_engine('sqlite:////absolute/path/to/foo.db')
33433340
3341+
For more information see the examples the SQLAlchemy `documentation <http://docs.sqlalchemy.org/en/rel_0_9/core/engines.html>`__
3342+
33443343

33453344
Sqlite fallback
33463345
~~~~~~~~~~~~~~~
@@ -3354,16 +3353,14 @@ You can create connections like so:
33543353
.. code-block:: python
33553354
33563355
import sqlite3
3357-
from pandas.io import sql
3358-
cnx = sqlite3.connect(':memory:')
3356+
con = sqlite3.connect(':memory:')
33593357
33603358
And then issue the following queries:
33613359

33623360
.. code-block:: python
33633361
33643362
data.to_sql('data', cnx)
3365-
3366-
sql.read_sql("SELECT * FROM data", cnx)
3363+
pd.read_sql_query("SELECT * FROM data", con)
33673364
33683365
33693366
.. _io.bigquery:

doc/source/release.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ New features
6666
in pandas the actual range of dates that you can use is 1678 AD to 2262 AD. (:issue:`4041`)
6767
- Added error bar support to the ``.plot`` method of ``DataFrame`` and ``Series`` (:issue:`3796`, :issue:`6834`)
6868
- Implemented ``Panel.pct_change`` (:issue:`6904`)
69+
- The SQL reading and writing functions now support more database flavors
70+
through SQLAlchemy (:issue:`2717`, :issue:`4163`, :issue:`5950`, :issue:`6292`).
6971

7072
API Changes
7173
~~~~~~~~~~~
@@ -257,6 +259,8 @@ Deprecations
257259
- The support for the 'mysql' flavor when using DBAPI connection objects has been deprecated.
258260
MySQL will be further supported with SQLAlchemy engines (:issue:`6900`).
259261

262+
- The following ``io.sql`` functions have been deprecated: ``tquery``, ``uquery``, ``read_frame``, ``frame_query``, ``write_frame``.
263+
260264
- The `percentile_width` keyword argument in :meth:`~DataFrame.describe` has been deprecated.
261265
Use the `percentiles` keyword instead, which takes a list of percentiles to display. The
262266
default output is unchanged.

doc/source/v0.14.0.txt

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,71 @@ More consistent behaviour for some groupby methods:
340340
SQL
341341
~~~
342342

343+
The SQL reading and writing functions now support more database flavors
344+
through SQLAlchemy (:issue:`2717`, :issue:`4163`, :issue:`5950`, :issue:`6292`).
345+
All databases supported by SQLAlchemy can be used, such
346+
as PostgreSQL, MySQL, Oracle, Microsoft SQL server (see documentation of
347+
SQLAlchemy on `included dialects
348+
<http://sqlalchemy.readthedocs.org/en/latest/dialects/index.html>`_).
349+
350+
The functionality of providing DBAPI connection objects will only be supported
351+
for sqlite3 in the future. The ``'mysql'`` flavor is deprecated.
352+
353+
The new functions :func:`~pandas.read_sql_query` and :func:`~pandas.read_sql_table`
354+
are introduced. The function :func:`~pandas.read_sql` is kept as a convenience
355+
wrapper around the other two and will delegate to specific function depending on
356+
the provided input (database table name or sql query).
357+
358+
In practice, you have to provide a SQLAlchemy ``engine`` to the sql functions.
359+
To connect with SQLAlchemy you use the :func:`create_engine` function to create an engine
360+
object from database URI. You only need to create the engine once per database you are
361+
connecting to. For an in-memory sqlite database:
362+
363+
.. ipython:: python
364+
365+
from sqlalchemy import create_engine
366+
# Create your connection.
367+
engine = create_engine('sqlite:///:memory:')
368+
369+
This ``engine`` can then be used to write or read data to/from this database:
370+
371+
.. ipython:: python
372+
373+
df = pd.DataFrame({'A': [1,2,3], 'B': ['a', 'b', 'c']})
374+
df.to_sql('db_table', engine, index=False)
375+
376+
You can read data from a database by specifying the table name:
377+
378+
.. ipython:: python
379+
380+
pd.read_sql_table('db_table', engine)
381+
382+
or by specifying a sql query:
383+
384+
.. ipython:: python
385+
386+
pd.read_sql_query('SELECT * FROM db_table', engine)
387+
388+
Some other enhancements to the sql functions include:
389+
390+
- support for writing the index. This can be controlled with the ``index``
391+
keyword (default is True).
392+
- specify the column label to use when writing the index with ``index_label``.
393+
- specify string columns to parse as datetimes withh the ``parse_dates``
394+
keyword in :func:`~pandas.read_sql_query` and :func:`~pandas.read_sql_table`.
395+
396+
.. warning::
397+
398+
Some of the existing functions or function aliases have been deprecated
399+
and will be removed in future versions. This includes: ``tquery``, ``uquery``,
400+
``read_frame``, ``frame_query``, ``write_frame``.
401+
402+
.. warning::
403+
404+
The support for the 'mysql' flavor when using DBAPI connection objects has been deprecated.
405+
MySQL will be further supported with SQLAlchemy engines (:issue:`6900`).
406+
407+
343408
.. _whatsnew_0140.slicers:
344409

345410
MultiIndexing Using Slicers
@@ -573,6 +638,8 @@ Deprecations
573638
- The support for the 'mysql' flavor when using DBAPI connection objects has been deprecated.
574639
MySQL will be further supported with SQLAlchemy engines (:issue:`6900`).
575640

641+
- The following ``io.sql`` functions have been deprecated: ``tquery``, ``uquery``, ``read_frame``, ``frame_query``, ``write_frame``.
642+
576643
- The `percentile_width` keyword argument in :meth:`~DataFrame.describe` has been deprecated.
577644
Use the `percentiles` keyword instead, which takes a list of percentiles to display. The
578645
default output is unchanged.

pandas/core/generic.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -928,10 +928,11 @@ def to_sql(self, name, con, flavor='sqlite', if_exists='fail', index=True,
928928
con : SQLAlchemy engine or DBAPI2 connection (legacy mode)
929929
Using SQLAlchemy makes it possible to use any DB supported by that
930930
library.
931-
If a DBAPI2 object is given, a supported SQL flavor must also be provided
931+
If a DBAPI2 object, only sqlite3 is supported.
932932
flavor : {'sqlite', 'mysql'}, default 'sqlite'
933933
The flavor of SQL to use. Ignored when using SQLAlchemy engine.
934-
Required when using DBAPI2 connection.
934+
'mysql' is deprecated and will be removed in future versions, but it
935+
will be further supported through SQLAlchemy engines.
935936
if_exists : {'fail', 'replace', 'append'}, default 'fail'
936937
- fail: If table exists, do nothing.
937938
- replace: If table exists, drop it, recreate it, and insert data.

pandas/io/sql.py

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,8 @@ def uquery(sql, con=None, cur=None, retry=True, params=None):
221221
#------------------------------------------------------------------------------
222222
#--- Read and write to DataFrames
223223

224-
def read_sql_table(table_name, con, meta=None, index_col=None,
225-
coerce_float=True, parse_dates=None, columns=None):
224+
def read_sql_table(table_name, con, index_col=None, coerce_float=True,
225+
parse_dates=None, columns=None):
226226
"""Read SQL database table into a DataFrame.
227227
228228
Given a table name and an SQLAlchemy engine, returns a DataFrame.
@@ -234,8 +234,6 @@ def read_sql_table(table_name, con, meta=None, index_col=None,
234234
Name of SQL table in database
235235
con : SQLAlchemy engine
236236
Sqlite DBAPI conncection mode not supported
237-
meta : SQLAlchemy meta, optional
238-
If omitted MetaData is reflected from engine
239237
index_col : string, optional
240238
Column to set as index
241239
coerce_float : boolean, default True
@@ -264,7 +262,7 @@ def read_sql_table(table_name, con, meta=None, index_col=None,
264262
265263
266264
"""
267-
pandas_sql = PandasSQLAlchemy(con, meta=meta)
265+
pandas_sql = PandasSQLAlchemy(con)
268266
table = pandas_sql.read_table(
269267
table_name, index_col=index_col, coerce_float=coerce_float,
270268
parse_dates=parse_dates, columns=columns)
@@ -292,11 +290,10 @@ def read_sql_query(sql, con, index_col=None, coerce_float=True, params=None,
292290
library.
293291
If a DBAPI2 object, only sqlite3 is supported.
294292
index_col : string, optional
295-
column name to use for the returned DataFrame object.
293+
Column name to use as index for the returned DataFrame object.
296294
coerce_float : boolean, default True
297295
Attempt to convert values to non-string, non-numeric objects (like
298296
decimal.Decimal) to floating point, useful for SQL result sets
299-
cur : depreciated, cursor is obtained from connection
300297
params : list, tuple or dict, optional
301298
List of parameters to pass to execute method.
302299
parse_dates : list or dict
@@ -325,8 +322,8 @@ def read_sql_query(sql, con, index_col=None, coerce_float=True, params=None,
325322
parse_dates=parse_dates)
326323

327324

328-
def read_sql(sql, con, index_col=None, flavor='sqlite', coerce_float=True,
329-
params=None, parse_dates=None, columns=None):
325+
def read_sql(sql, con, index_col=None, coerce_float=True, params=None,
326+
parse_dates=None, columns=None):
330327
"""
331328
Read SQL query or database table into a DataFrame.
332329
@@ -339,15 +336,10 @@ def read_sql(sql, con, index_col=None, flavor='sqlite', coerce_float=True,
339336
library.
340337
If a DBAPI2 object, only sqlite3 is supported.
341338
index_col : string, optional
342-
column name to use for the returned DataFrame object.
343-
flavor : string, {'sqlite', 'mysql'}
344-
The flavor of SQL to use. Ignored when using
345-
SQLAlchemy engine. Required when using DBAPI2 connection.
346-
'mysql' is still supported, but will be removed in future versions.
339+
column name to use as index for the returned DataFrame object.
347340
coerce_float : boolean, default True
348341
Attempt to convert values to non-string, non-numeric objects (like
349342
decimal.Decimal) to floating point, useful for SQL result sets
350-
cur : depreciated, cursor is obtained from connection
351343
params : list, tuple or dict, optional
352344
List of parameters to pass to execute method.
353345
parse_dates : list or dict
@@ -360,7 +352,8 @@ def read_sql(sql, con, index_col=None, flavor='sqlite', coerce_float=True,
360352
Especially useful with databases without native Datetime support,
361353
such as SQLite
362354
columns : list
363-
List of column names to select from sql table
355+
List of column names to select from sql table (only used when reading
356+
a table).
364357
365358
Returns
366359
-------
@@ -379,7 +372,7 @@ def read_sql(sql, con, index_col=None, flavor='sqlite', coerce_float=True,
379372
read_sql_query : Read SQL query into a DataFrame
380373
381374
"""
382-
pandas_sql = pandasSQL_builder(con, flavor=flavor)
375+
pandas_sql = pandasSQL_builder(con)
383376

384377
if 'select' in sql.lower():
385378
try:
@@ -419,8 +412,8 @@ def to_sql(frame, name, con, flavor='sqlite', if_exists='fail', index=True,
419412
If a DBAPI2 object, only sqlite3 is supported.
420413
flavor : {'sqlite', 'mysql'}, default 'sqlite'
421414
The flavor of SQL to use. Ignored when using SQLAlchemy engine.
422-
Required when using DBAPI2 connection.
423-
'mysql' is still supported, but will be removed in future versions.
415+
'mysql' is deprecated and will be removed in future versions, but it
416+
will be further supported through SQLAlchemy engines.
424417
if_exists : {'fail', 'replace', 'append'}, default 'fail'
425418
- fail: If table exists, do nothing.
426419
- replace: If table exists, drop it, recreate it, and insert data.
@@ -461,8 +454,8 @@ def has_table(table_name, con, flavor='sqlite'):
461454
If a DBAPI2 object, only sqlite3 is supported.
462455
flavor: {'sqlite', 'mysql'}, default 'sqlite'
463456
The flavor of SQL to use. Ignored when using SQLAlchemy engine.
464-
Required when using DBAPI2 connection.
465-
'mysql' is still supported, but will be removed in future versions.
457+
'mysql' is deprecated and will be removed in future versions, but it
458+
will be further supported through SQLAlchemy engines.
466459
467460
Returns
468461
-------
@@ -1090,15 +1083,23 @@ def _create_sql_schema(self, frame, table_name):
10901083

10911084
def get_schema(frame, name, flavor='sqlite', keys=None, con=None):
10921085
"""
1093-
Get the SQL db table schema for the given frame
1086+
Get the SQL db table schema for the given frame.
10941087
10951088
Parameters
10961089
----------
10971090
frame : DataFrame
1098-
name : name of SQL table
1091+
name : string
1092+
name of SQL table
10991093
flavor : {'sqlite', 'mysql'}, default 'sqlite'
1100-
keys : columns to use a primary key
1094+
The flavor of SQL to use. Ignored when using SQLAlchemy engine.
1095+
'mysql' is deprecated and will be removed in future versions, but it
1096+
will be further supported through SQLAlchemy engines.
1097+
keys : string or sequence
1098+
columns to use a primary key
11011099
con: an open SQL database connection object or an SQLAlchemy engine
1100+
Using SQLAlchemy makes it possible to use any DB supported by that
1101+
library.
1102+
If a DBAPI2 object, only sqlite3 is supported.
11021103
11031104
"""
11041105

pandas/io/tests/test_sql.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ def test_read_sql_iris(self):
340340
def test_legacy_read_frame(self):
341341
with tm.assert_produces_warning(FutureWarning):
342342
iris_frame = sql.read_frame(
343-
"SELECT * FROM iris", self.conn, flavor='sqlite')
343+
"SELECT * FROM iris", self.conn)
344344
self._check_iris_loaded_frame(iris_frame)
345345

346346
def test_to_sql(self):

0 commit comments

Comments
 (0)