Передача параметра в DB.execute для WHERE IN... INT list

В спецификации API Python DB вы можете передать аргумент параметров методу execute(). Часть моего утверждения - это предложение WHERE IN, и я использовал кортеж для заполнения IN. Например:

params = ((3, 2, 1), )
stmt = "SELECT * FROM table WHERE id IN %s"
db.execute(stmt, params)

Но когда я сталкиваюсь с ситуацией, когда кортеж параметра является только кортежем из 1 элемента, выполнение не выполняется.

ProgrammingError: ERROR: ошибка синтаксиса в точке или рядом ")"
LINE 13: WHERE id IN (3,)

Как я могу заставить кортеж работать с предложением правильно?

Ответ 1

Изменить: Пожалуйста, как @rspeer упоминает в комментарии, предпринимайте меры предосторожности, чтобы защитить себя от атаки SQL-инъекции.

Тестирование с помощью pg8000 (совместимый с DB-API 2.0 интерфейс Pure-Python для механизма базы данных PostgreSQL):

Это рекомендуемый способ передачи нескольких параметров в предложение "IN".

params = [3,2,1]
stmt = 'SELECT * FROM table WHERE id IN (%s)' % ','.join('%s' for i in params)
cursor.execute(stmt, params)

Другое редактирование (полностью протестированный и рабочий пример):

>>> from pg8000 import DBAPI
>>> conn = DBAPI.connect(user="a", database="d", host="localhost", password="p")
>>> c = conn.cursor()
>>> prms = [1,2,3]
>>> stmt = 'SELECT * FROM table WHERE id IN (%s)' % ','.join('%s' for i in prms)
>>> c.execute(stmt,prms)
>>> c.fetchall()
((1, u'myitem1'), (2, u'myitem2'), (3, u'myitem3'))

Ответ 2

Ошибка возникает из запятой после 3. Просто оставьте ее для одиночных значений и вы установите.

params = ((3), ... )
stmt = "SELECT * FROM table WHERE id IN %s"
db.execute(stmt, params)

Ответ 3

Это, возможно, не ответ на вопрос, который вы задали, но я думаю, что он может решить вашу проблему.

Python DB-API, похоже, не дает вам возможности передавать кортежи в качестве безопасно замененных параметров. В принятом ответе от bernie используется оператор Python % для замещения, что небезопасно.

Однако вам может не понадобиться передавать кортежи в качестве параметров, особенно если кортеж, который вы хотите, является результатом другого SQL-запроса (как вы указали Даниэлю). Вместо этого вы можете использовать подзапросы SQL.

Если набор идентификаторов, которые вы хотите в своем разделе IN, является результатом SELECT id FROM other_table WHERE use=true, например:

stmt = "SELECT * FROM table WHERE id IN (SELECT id FROM other_table WHERE use=true)"
db.execute(stmt)

И это может быть параметризовано (безопасным способом) тоже. Если идентификаторы, которые вы хотите выбрать, это те, у которых задан parent_id:

stmt = "SELECT * FROM table WHERE id IN (SELECT id FROM other_table WHERE parent_id=%s)"
params = (parent_id,)
db.execute(stmt, params)

Ответ 4

Принятый ответ угрожает SQL-инъекцией; вы никогда не должны пропускать пользовательский ввод непосредственно в базу данных. Вместо этого создайте запрос с правильным количеством заполнителей, а затем пусть pg8000 выполнит экранирование:

params = [3,2,1]
# SELECT * from table where id in (%s,%s,%s)
stmt = 'SELECT * FROM table WHERE id IN ({})'.format(','.join(['%s']*len(params)))
cursor.execute(stmt, tuple(params))

Ответ 5

Решение с помощью f-строки.

params = [...]
stmt = f"SELECT * FROM table WHERE id IN ({','.join(['%s']*len(params ),)})"
db.execute(stmt, params)

Если есть другой параметр-заполнитель, он будет таким

age = 18
params = [...]
stmt = f"SELECT * FROM table WHERE age>%s AND id IN ({','.join(['%s']*len(params ),)})"
db.execute(stmt, tuple([age] + params))