将值列表从 Python 传递到 SQL 查询的 IN 子句

Posted

技术标签:

【中文标题】将值列表从 Python 传递到 SQL 查询的 IN 子句【英文标题】:Passing a list of values from Python to the IN clause of an SQL query 【发布时间】:2017-06-26 16:10:16 【问题描述】:

我正在尝试将如下列表传递给 sql 查询

x = ['1000000000164774783','1000000000253252111']

我正在使用 sqlalchemypyodbc 连接到 sql:

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

cnx=sqlalchemy.create_engine("mssql+pyodbc://Omnius:MainBrain1@172.31.163.135:1433/Basis?driver=/opt/microsoft/sqlncli/lib64/libsqlncli-11.0.so.1790.0")

我尝试在 sql 查询中使用各种字符串格式函数。以下是其中之一

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

他们似乎都将其转换为数字格式

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

因此查询失败,因为 Send_Customer 的数据类型在 SQL 中是 varchar(50)

 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)']

【问题讨论】:

【参考方案1】:

正如对另一个答案的评论中所述,该方法可能因多种原因而失败。你真正想做的是创建一个带有所需参数占位符数量的 SQL 语句,然后使用 read_sqlparams= 参数来提供值:

x = ['1000000000164774783','1000000000253252111']
placeholders = ','.join('?' for i in range(len(x)))  # '?,?'
sql = "select * from Pretty_Txns where Send_Customer in (%s)" % placeholders
df = pd.read_sql(sql, cnx, params=x)

【讨论】:

【参考方案2】:

使用以下方法,效果很好:

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

【讨论】:

糟糕的想法。这很容易发生 SQL 注入,str 不一定以 SQL 期望的方式转义值(例如,Python 2 中的 unicode 对象将输出 u'whatever')。 对于 SQL 注入攻击,接受的答案真的更安全吗?【参考方案3】:

sqlalchemey, pyodbc 与 pandas read_sql() 一起工作是一件麻烦事。在遇到来自pandas 和psycopg 的各种解决方案和文档后,这是正确的(到目前为止)使用命名参数进行查询的方法:

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()

我已经使它与 PostgreSQL 数据库一起工作。我希望 mysql 不会有太大的不同。

【讨论】:

【参考方案4】:

这是您需要的 SQL 查询

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

【讨论】:

以上是关于将值列表从 Python 传递到 SQL 查询的 IN 子句的主要内容,如果未能解决你的问题,请参考以下文章

从file.Python3将值变量插入sql查询

将值从文本框传递到 SQL 查询中的“TOP n”子句

将值的动态列表传递到 Table.Combine

如何将值从动态列表视图传递到活动?

如何将值从列表视图传递到 detalis Activity

如何使用 SQL 查询将值传递给具有空间/地理数据类型的存储过程