Skip to content

Commit 1e84f1d

Browse files
Merge pull request #10983 from stephenpascoe/issue-10846
TST/DOC #10846 Test and document use of SQLAlchemy expressions in read_sql()
2 parents f920bf2 + c9476bd commit 1e84f1d

File tree

3 files changed

+70
-5
lines changed

3 files changed

+70
-5
lines changed

doc/source/io.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3918,6 +3918,42 @@ connecting to.
39183918
For more information see the examples the SQLAlchemy `documentation <http://docs.sqlalchemy.org/en/rel_0_9/core/engines.html>`__
39193919

39203920

3921+
Advanced SQLAlchemy queries
3922+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
3923+
3924+
You can use SQLAlchemy constructs to describe your query.
3925+
3926+
Use :func:`sqlalchemy.text` to specify query parameters in a backend-neutral way
3927+
3928+
.. ipython:: python
3929+
3930+
import sqlalchemy as sa
3931+
pd.read_sql(sa.text('SELECT * FROM data where Col_1=:col1'), engine, params={'col1': 'X'})
3932+
3933+
If you have an SQLAlchemy description of your database you can express where conditions using SQLAlchemy expressions
3934+
3935+
.. ipython:: python
3936+
3937+
metadata = sa.MetaData()
3938+
data_table = sa.Table('data', metadata,
3939+
sa.Column('index', sa.Integer),
3940+
sa.Column('Date', sa.DateTime),
3941+
sa.Column('Col_1', sa.String),
3942+
sa.Column('Col_2', sa.Float),
3943+
sa.Column('Col_3', sa.Boolean),
3944+
)
3945+
3946+
pd.read_sql(sa.select([data_table]).where(data_table.c.Col_3 == True), engine)
3947+
3948+
You can combine SQLAlchemy expressions with parameters passed to :func:`read_sql` using :func:`sqlalchemy.bindparam`
3949+
3950+
.. ipython:: python
3951+
3952+
import datetime as dt
3953+
expr = sa.select([data_table]).where(data_table.c.Date > sa.bindparam('date'))
3954+
pd.read_sql(expr, engine, params={'date': dt.datetime(2010, 10, 18)})
3955+
3956+
39213957
Sqlite fallback
39223958
'''''''''''''''
39233959

pandas/io/sql.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,9 @@ def read_sql_query(sql, con, index_col=None, coerce_float=True, params=None,
364364
365365
Parameters
366366
----------
367-
sql : string
368-
SQL query to be executed
369-
con : SQLAlchemy connectable(engine/connection) or database string URI
367+
sql : string SQL query or SQLAlchemy Selectable (select or text object)
368+
to be executed.
369+
con : SQLAlchemy connectable(engine/connection) or database string URI
370370
or sqlite3 DBAPI2 connection
371371
Using SQLAlchemy makes it possible to use any DB supported by that
372372
library.
@@ -423,8 +423,8 @@ def read_sql(sql, con, index_col=None, coerce_float=True, params=None,
423423
424424
Parameters
425425
----------
426-
sql : string
427-
SQL query to be executed or database table name.
426+
sql : string SQL query or SQLAlchemy Selectable (select or text object)
427+
to be executed, or database table name.
428428
con : SQLAlchemy connectable(engine/connection) or database string URI
429429
or DBAPI2 connection (fallback mode)
430430
Using SQLAlchemy makes it possible to use any DB supported by that

pandas/io/tests/test_sql.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,35 @@ def test_to_sql_read_sql_with_database_uri(self):
951951
tm.assert_frame_equal(test_frame1, test_frame3)
952952
tm.assert_frame_equal(test_frame1, test_frame4)
953953

954+
def _make_iris_table_metadata(self):
955+
sa = sqlalchemy
956+
metadata = sa.MetaData()
957+
iris = sa.Table('iris', metadata,
958+
sa.Column('SepalLength', sa.REAL),
959+
sa.Column('SepalWidth', sa.REAL),
960+
sa.Column('PetalLength', sa.REAL),
961+
sa.Column('PetalWidth', sa.REAL),
962+
sa.Column('Name', sa.TEXT)
963+
)
964+
965+
return iris
966+
967+
def test_query_by_text_obj(self):
968+
# WIP : GH10846
969+
name_text = sqlalchemy.text('select * from iris where name=:name')
970+
iris_df = sql.read_sql(name_text, self.conn, params={'name': 'Iris-versicolor'})
971+
all_names = set(iris_df['Name'])
972+
self.assertEqual(all_names, set(['Iris-versicolor']))
973+
974+
def test_query_by_select_obj(self):
975+
# WIP : GH10846
976+
iris = self._make_iris_table_metadata()
977+
978+
name_select = sqlalchemy.select([iris]).where(iris.c.Name == sqlalchemy.bindparam('name'))
979+
iris_df = sql.read_sql(name_select, self.conn, params={'name': 'Iris-setosa'})
980+
all_names = set(iris_df['Name'])
981+
self.assertEqual(all_names, set(['Iris-setosa']))
982+
954983

955984
class _EngineToConnMixin(object):
956985
"""

0 commit comments

Comments
 (0)