Search code examples
sql-serverpython-3.xpandaspyodbc

Getting a warning when using a pyodbc Connection object with pandas


I am trying to make sense of the following error that I started getting when I setup my python code to run on a VM server, which has 3.9.5 installed instead of 3.8.5 on my desktop. Not sure that matters, but it could be part of the reason.

The error

C:\ProgramData\Miniconda3\lib\site-packages\pandas\io\sql.py:758: UserWarning: pandas only support SQLAlchemy connectable(engine/connection) or
database string URI or sqlite3 DBAPI2 connection
other DBAPI2 objects are not tested, please consider using SQLAlchemy
  warnings.warn(

This is within a fairly simple .py file that imports pyodbc & sqlalchemy fwiw. A fairly generic/simple version of sql calls that yields the warning is:

myserver_string = "xxxxxxxxx,nnnn"
db_string = "xxxxxx"

cnxn = "Driver={ODBC Driver 17 for SQL Server};Server=tcp:"+myserver_string+";Database="+db_string +";TrustServerCertificate=no;Connection Timeout=600;Authentication=ActiveDirectoryIntegrated;"

def readAnyTable(tablename, date):
    conn = pyodbc.connect(cnxn)
    
    query_result = pd.read_sql_query(
            ''' 
                 SELECT *
                 FROM [{0}].[dbo].[{1}]
                where Asof >= '{2}'
            '''.format(db_string,tablename,date,), conn)
            
    conn.close()
    
    return query_result

All the examples I have seen using pyodbc in python look fairly similar. Is pyodbc becoming deprecated? Is there a better way to achieve similar results without warning?


Solution

  • Is pyodbc becoming deprecated?

    No. For at least the last couple of years pandas' documentation has clearly stated that it wants either

    1. a SQLAlchemy Connectable (i.e., an Engine or Connection object),
    2. a string containing a SQLAlchemy connection URL, or
    3. a SQLite DBAPI connection.

    (The switch-over to SQLAlchemy was almost universal, but they continued supporting SQLite connections for backwards compatibility.) People have been passing other DBAPI connections (like pyodbc Connection objects) for read operations and pandas hasn't complained … until now.

    Is there a better way to achieve similar results without warning?

    Yes. You can take your existing ODBC connection string and use it to create a SQLAlchemy Engine object as described in the SQLAlchemy 1.4 documentation:

    from sqlalchemy.engine import URL
    connection_string = "DRIVER={ODBC Driver 17 for SQL Server};SERVER=dagger;DATABASE=test;UID=user;PWD=password"
    connection_url = URL.create("mssql+pyodbc", query={"odbc_connect": connection_string})
    
    from sqlalchemy import create_engine
    engine = create_engine(connection_url)
    

    Then use the SQLAlchemy engine to work with the pandas methods you require. For example, with SQLAlchemy 2.0 and pandas 1.5.3:

    import pandas as pd
    import sqlalchemy as sa
    
    # …
    
    with engine.begin() as conn:
        df = pd.read_sql_query(sa.text("SELECT 'thing' as txt"), conn)