6

I am trying to pass a list like below to a sql query

x = ['1000000000164774783','1000000000253252111']

I am using sqlalchemy and pyodbc to connect to sql:

import pandas as pd
from pandas import Series,DataFrame
import pyodbc
import sqlalchemy

cnx=sqlalchemy.create_engine("mssql+pyodbc://Omnius:[email protected]:1433/Basis?driver=/opt/microsoft/sqlncli/lib64/libsqlncli-11.0.so.1790.0")

I tried using various string format functions to be used in the sql query. below is one of them

  xx = ', '.join(x)
  sql = "select * from Pretty_Txns where Send_Customer in (%s)" % xx
  df = pd.read_sql(sql,cnx)

All of them seem to convert it into a numeric format

(1000000000164774783, 1000000000253252111) instead of ('1000000000164774783','1000000000253252111')

And hence the query fails as datatype of Send_Customer is varchar(50) in SQL

 ProgrammingError: (pyodbc.ProgrammingError) ('42000', '[42000] [Microsoft][SQL Server Native Client 11.0]
  [SQL Server]Error converting data type varchar to numeric. (8114) (SQLExecDirectW)') 
 [SQL: 'select * from Pretty_Txns where Send_Customer in (1000000000164774783, 1000000000253252111)']

4 Answers 4

17

As stated in the comment to the other answer, that approach can fail for a variety of reasons. What you really want to do is create an SQL statement with the required number of parameter placeholders and then use the params= parameter of .read_sql_query() to supply the values:

x = ['1000000000164774783','1000000000253252111']
placeholders = ','.join('?' for i in range(len(x)))  # '?,?'
sql = f"select * from Pretty_Txns where Send_Customer in ({placeholders})"
df = pd.read_sql_query(sql, cnx, params=x)
Sign up to request clarification or add additional context in comments.

1 Comment

This WORKED! I've been trying to find the solution and was on several Stackoverflow pages, attempting their solutions but to no avail. Thanks @Gord
3

Here's the SQL query you need

sql = f"select * from Pretty_Txns where Send_Customer in {tuple(x)}"
df = pd.read_sql(sql,cnx)

1 Comment

If x contains only one value, {tuple(x)} translates to "(my_val, )" and will cause the SQL query to fail
0

Used the below approach and it worked fine:

       sql = "select * from Pretty_Txns where Send_Customer in %s" % str(tuple(x))
      df = pd.read_sql(sql,cnx)

3 Comments

Terrible idea. That's prone to SQL injection and str does not necessarily escape the values in the way that SQL expects (e.g. a unicode object in Python 2 will output u'whatever').
Is the accepted answer really any safer to SQL injection attacks?
@Mr.LanceESloan - Yes, because the string formatting is only inserting the ?,?, … string which is under the complete control of our code. SQL injection issues arise when dynamic SQL uses user input to embed data values into the statement.
0

Making sqlalchemey, pyodbc to work with pandas read_sql() is a hairy and messy thing. After much frustration and bumping into various solutions and documentation from pandas and psycopg, here's the correct (so far) way to do a query with named parameter:

import pandas as pd
import psycopg2
# import pyodbc 
import sqlalchemy
from sqlalchemy import text # this is crucial

cnx=sqlalchemy.create_engine(...)
x = ['1000000000164774783','1000000000253252111']
sql = "select * from Pretty_Txns where Send_Customer in (:id);" # named parameter
df = pd.read_sql(text(sql), cnx, params={'id':x}) # note how `sql`
                                                   # string is cast with text() 
                                                   # and key-pair value is passed for 
                                                   # named parameter 'id'
df.head()

I've made it work with the PostgreSQL database. I hope it wouldn't be too much different for MySQL.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.