Оператор SQL IN с использованием pyodbc и SQL Server

Я использую 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))

но я получаю ошибку ниже. Должен ли параметр tuple обрабатываться по-другому? Есть ли лучший способ структурировать этот запрос?

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

3 ответов


вы не можете параметризовать несколько значений IN () пункт через один строковый параметр. Единственный способ добиться этого:

  1. подстановка строк (как и вы).

  2. создать параметризованный запрос в форме IN (?, ?, . . ., ?) и затем пройти в отдельные параметр для каждого держателя места. Я не эксперт в Python для ODBC, но я полагаю, что это особенно легко сделать на таком языке, как Python. Это безопаснее, потому что вы получаете полное значение параметризации.


чтобы развернуть второй вариант Ларри-динамически создавать параметризованную строку, я успешно использовал следующее:

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

проблема в вашем кортеже. Соединение 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))