【问题标题】:SQL IN operator using pyodbc and SQL Server使用 pyodbc 和 SQL Server 的 SQL IN 运算符
【发布时间】:2011-01-27 16:57:16
【问题描述】:

我正在使用 pyodbc 查询 SQL Server 数据库

import datetime
import pyodbc    
conn = pyodbc.connect("Driver={SQL Server};Server='dbserver',Database='db',
                       TrustedConnection=Yes")
cursor = conn.cursor()
ratings = ("PG-13", "PG", "G")
st_dt = datetime(2010, 1, 1)
end_dt = datetime(2010, 12, 31)
cursor.execute("""Select title, director, producer From movies 
                Where rating In ? And release_dt Between ? And ?""", 
                ratings, str(st_dt), str(end_dt))

但收到以下错误。元组参数是否需要以不同的方式处理?有没有更好的方法来构造这个查询?

('42000', "[42000] [Microsoft][ODBC SQL Server Driver][SQL Server]Line 9: 
  Incorrect syntax near '@P1'. (170) (SQLExecDirectW); 
  [42000] [Microsoft][ODBC SQL Server Driver][SQL Server]
  Statement(s) could not be prepared. (8180)")

更新:

我能够使用字符串格式化运算符让这个查询工作,这并不理想,因为它引入了安全问题。

import datetime
import pyodbc    
conn = pyodbc.connect("Driver={SQL Server};Server='dbserver',Database='db',
                       TrustedConnection=Yes")
cursor = conn.cursor()
ratings = ("PG-13", "PG", "G")
st_dt = datetime(2010, 1, 1)
end_dt = datetime(2010, 12, 31)
cursor.execute("""Select title, director, producer From movies 
                Where rating In %s And release_dt Between '%s' And '%s'""" % 
                (ratings, st_dt, end_dt))

【问题讨论】:

    标签: python sql pyodbc


    【解决方案1】:

    为了扩展拉里的第二个选项——动态创建参数化字符串,我成功地使用了以下内容:

    placeholders = ",".join("?" * len(code_list))
    sql = "delete from dbo.Results where RESULT_ID = ? AND CODE IN (%s)" % placeholders
    params = [result_id]
    params.extend(code_list)
    cursor.execute(sql, params)
    

    给出以下带有适当参数的 SQL:

    delete from dbo.Results where RESULT_ID = ? AND CODE IN (?,?,?)
    

    【讨论】:

      【解决方案2】:

      您不能使用单个字符串参数参数化 IN () 子句中的多个值。实现这一目标的唯一方法是:

      1. 字符串替换(和你一样)。

      2. IN (?, ?, . . ., ?) 的形式构建一个参数化查询,然后为每个占位符传入一个单独的 参数。我不是 Python 到 ODBC 的专家,但我想这在 Python 这样的语言中特别容易做到。这样更安全,因为您可以获得参数化的全部价值。

      【讨论】:

      • 对于您的第二种选择,是否需要根据希望传递给 IN 语句的元组的大小动态创建 SQL 语句?
      • 您可以执行以下操作: cursor.execute("SELECT * FROM movies WHERE rating IN ({})".format(','.join('?' * len(ratings))) , 评分)
      【解决方案3】:

      扩展 Larry 和 geographika 的答案:

      ratings = ('PG-13', 'PG', 'G')
      st_dt = datetime(2010, 1, 1)
      end_dt = datetime(2010, 12, 31)
      
      placeholders = ', '.join('?' * len(ratings))
      vars = (*ratings, st_dt, end_dt)
      query = '''
          select title, director, producer
          from movies
          where rating in (%s)
             and release_dt between ? and ?
      ''' % placeholders
      
      cursor.execute(query, vars)
      

      使用占位符,这将返回一个查询:

          select title, director, producer
          from movies
          where rating in (?, ?, ?)
             and release_dt between ? and ?
      

      如果您传入ratings,它将尝试将其所有项目放入一个?。但是,如果我们传入*ratings,那么ratings 中的每一项都会在in() 子句中占据一席之地。因此,我们将元组(*ratings, st_dt, end_dt) 传递给cursor.execute()

      【讨论】:

        【解决方案4】:

        问题是你的元组。 ODBC 连接需要一个字符串来构造查询,并且您正在发送一个 python 元组。请记住,您必须正确引用字符串。我假设您要查找的评分数量会有所不同。可能有更好的方法,但我的 pyodbc 往往简单明了。

        尝试以下方法:

        import datetime
        import pyodbc    
        conn = pyodbc.connect("Driver={SQL Server};Server='dbserver',Database='db',
                               TrustedConnection=Yes")
        
        def List2SQLList(items):
            sqllist = "%s" % "\",\"".join(items)
            return sqllist
        
        
        cursor = conn.cursor()
        ratings = ("PG-13", "PG", "G")
        st_dt = datetime(2010, 1, 1)
        end_dt = datetime(2010, 12, 31)
        cursor.execute("""Select title, director, producer From movies 
                        Where rating In (?) And release_dt Between ? And ?""", 
                        List2SQLList(ratings), str(st_dt), str(end_dt))
        

        【讨论】:

        • 我仍然收到第一个参数的“语法错误”错误。 pyodbc 是否可以将列表视为字符串而不是列表?
        • 我仍然收到参数错误。我确实找到了一种使用字符串格式运算符使查询工作的方法(请参阅上面的更新),并且由于此代码只能在本地运行,因此可能会忽略安全问题。我认为 cursor.executemany 可能是另一种选择。
        猜你喜欢
        • 1970-01-01
        • 2014-05-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-04
        • 2010-11-02
        • 1970-01-01
        • 2013-05-19
        相关资源
        最近更新 更多