список Python в запросе sql как параметр


126

У меня есть список питонов, скажем я

l = [1,5,8]

Я хочу написать sql-запрос, чтобы получить данные для всех элементов списка, скажем

select name from students where id = |IN THE LIST l|

Как мне этого добиться?

Ответы:


114

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

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

placeholder= '?' # For SQLite. See DBAPI paramstyle.
placeholders= ', '.join(placeholder for unused in l)
query= 'SELECT name FROM students WHERE id IN (%s)' % placeholders
cursor.execute(query, l)

11
','.join(placeholder * len(l))будет немного короче, но все равно читабельно imho
— ThiefMaster 01

7
@Thiefmaster: да, это должно быть ([placeholder]*len(l))в общем случае, поскольку заполнитель может состоять из нескольких символов.
— bob со

1
@bobince в моем случае я назначил переменную a = 'john' и b = 'snow' и сохранил ее в кортеже с именем got = ['a, b'] и выполнил тот же запрос. При этом я получил ошибку, т.е. ошибку типа: не все аргументы преобразованы во время форматирования строки. Как я должен это решить?
— TechJhola

2
Почему вы хотите присоединиться «вручную», а не оставлять эту работу dbapi? ключ в том, чтобы использовать кортеж, а не список ...
— Алессандро Дентелла

5
На основе этого ответа предлагается однострочное решение для Python 3.6 cursor.execute(f'SELECT name FROM students WHERE id IN ({','.join('?' for _ in l)})', l) . @bobince, вы также должны отметить в своем решении, что использование ?- это безопасный способ избежать внедрения SQL. Здесь есть множество уязвимых ответов, в основном те, которые объединяют строки в python.
— toto_tico

60

Самый простой способ - поставить список tupleпервым

t = tuple(l)
query = "select name from studens where id IN {}".format(t)

1
На самом деле это правильный ответ. Я не уверен, почему это было проигнорировано. Сначала вы настраиваете список l, затем используете кортеж, а затем передаете кортеж в запрос. отлично сработано.
— MEdwin

11
Как указано в @renstrm, это не сработает, если l содержит только один элемент, вы получите id IN (1,). Это синтаксическая ошибка.
— Doubledown

1
@Amirhos Imani, а что, если вы не знаете, сколько элементов у вас есть?
— Борис

10
Это, как и большинство ответов здесь, кроме принятого, уязвимо для SQL-инъекции и не должно использоваться.
— Bacon Bits

2
@BaconBits Справедливое предупреждение, но для некоторых приложений, где SQL-инъекция не является проблемой (например, анализ базы данных, в которой я единственный пользователь), это подойдет.
— irene

26

Не усложняйте это, решение простое.

l = [1,5,8]

l = tuple(l)

params = {'l': l}

cursor.execute('SELECT * FROM table where id in %(l)s',params)

введите описание изображения здесь

Надеюсь, это помогло !!!


10
Это не сработает, если он lсодержит только один элемент, и вы получите id IN (1,). Это синтаксическая ошибка.
— renstrm

3
Если l содержит только 1 элемент, убедитесь, что это кортеж, и он БУДЕТ работать. На мой взгляд, это решение более ясное, чем принятое.
— Алессандро Дентелла

2
Но это все равно не сработает, если кортеж пуст, поэтому перед выполнением запроса должна быть дополнительная проверка.
— Никита Конин

2
У меня это не работает с sqlite3. С какой библиотекой вы это тестировали?
— Ник Чаммас

1
Хорошее решение, но @renstrm и Никита-Конин правы, требуя дополнительных проверок, когда в кортеже есть один элемент или нет элементов.
— Аниш Сана

23

Вам нужен SQL

select name from studens where id in (1, 5, 8)

Если вы хотите построить это из питона, вы можете использовать

l = [1, 5, 8]
sql_query = 'select name from studens where id in (' + ','.join(map(str, l)) + ')'

Функция map преобразует список в список строк, которые можно склеить запятыми с помощью метода str.join .

В качестве альтернативы:

l = [1, 5, 8]
sql_query = 'select name from studens where id in (' + ','.join((str(n) for n in l)) + ')'

если вы предпочитаете выражения генератора функции карты.

ОБНОВЛЕНИЕ: С. Лотт упоминает в комментариях, что привязки Python SQLite не поддерживают последовательности. В этом случае вы можете захотеть

select name from studens where id = 1 or id = 5 or id = 8

Создан

sql_query = 'select name from studens where ' + ' or '.join(('id = ' + str(n) for n in l))

2
:-( Привязка Python SQLite не поддерживает последовательности.
— С.Лотт,

Ой? Я не понимал. Опять же, я не понимал, что мы собираемся использовать решение для привязки SQLite.
— Blair Conrad

Извините, я не предлагаю и не подразумеваю SQLite - просто грустно, что привязки для списков там не работают.
— S.Lott

11

строка. соедините значения списка, разделенные запятыми, и используйте оператор формата для формирования строки запроса.

myquery = "select name from studens where id in (%s)" % ",".join(map(str,mylist))

(Спасибо, Блэр-Конрад )


2
Поскольку этот подход не использует подстановку параметров базы данных, он подвергает вас атакам с использованием SQL-инъекций. Здесь %используется простое форматирование строки Python.
— Ник Чаммас

8

Мне нравится ответ Бобинса:

placeholder= '?' # For SQLite. See DBAPI paramstyle.
placeholders= ', '.join(placeholder for unused in l)
query= 'SELECT name FROM students WHERE id IN (%s)' % placeholders
cursor.execute(query, l)

Но я заметил вот что:

placeholders= ', '.join(placeholder for unused in l)

Можно заменить на:

placeholders= ', '.join(placeholder*len(l))

Я считаю это более прямым, хотя и менее умным и менее общим. Здесь lтребуется указать длину (то есть ссылаться на объект, определяющий __len__метод), что не должно быть проблемой. Но заполнитель также должен быть одиночным символом. Для поддержки использования многосимвольных заполнителей:

placeholders= ', '.join([placeholder]*len(l))

2

Решение для @umounted ответа, потому что это сломалось с одноэлементным кортежем, поскольку (1,) не является допустимым SQL .:

>>> random_ids = [1234,123,54,56,57,58,78,91]
>>> cursor.execute("create table test (id)")
>>> for item in random_ids:
    cursor.execute("insert into test values (%d)" % item)
>>> sublist = [56,57,58]
>>> cursor.execute("select id from test where id in %s" % str(tuple(sublist)).replace(',)',')'))
>>> a = cursor.fetchall()
>>> a
[(56,), (57,), (58,)]

Другое решение для строки sql:

cursor.execute("select id from test where id in (%s)" % ('"'+'", "'.join(l)+'"'))

5
Это не лучшее решение из-за внедрения sql.
— Anurag

2
placeholders= ', '.join("'{"+str(i)+"}'" for i in range(len(l)))
query="select name from students where id (%s)"%placeholders
query=query.format(*l)
cursor.execute(query)

Это должно решить вашу проблему.


2

Если вы используете PostgreSQL с библиотекой Psycopg2, вы можете позволить его адаптации кортежа делать за вас все экранирование и интерполяцию строк, например:

ids = [1,2,3]
cur.execute(
  "SELECT * FROM foo WHERE id IN %s",
  [tuple(ids)])

т.е. просто убедитесь, что вы передаете INпараметр как tuple. если это, listвы можете использовать = ANYсинтаксис массива :

cur.execute(
  "SELECT * FROM foo WHERE id = ANY (%s)",
  [list(ids)])

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


1

Например, если вам нужен запрос sql:

select name from studens where id in (1, 5, 8)

Что о:

my_list = [1, 5, 8]
cur.execute("select name from studens where id in %s" % repr(my_list).replace('[','(').replace(']',')') )


1
l = [1] # or [1,2,3]

query = "SELECT * FROM table WHERE id IN :l"
params = {'l' : tuple(l)}
cursor.execute(query, params)

:varНотация кажется проще. (Python 3.7)


0

Здесь используется подстановка параметров и учитывается случай списка с одним значением:

l = [1,5,8]

get_operator = lambda x: '=' if len(x) == 1 else 'IN'
get_value = lambda x: int(x[0]) if len(x) == 1 else x

query = 'SELECT * FROM table where id ' + get_operator(l) + ' %s'

cursor.execute(query, (get_value(l),))
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.