список 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)
bobince
источник
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)

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

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

allsyed
источник
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))
jimhark
источник
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)+'"'))
Ximix
источник
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(']',')') )
pgalilea
источник
1

более простое решение:

lst = [1,2,3,a,b,c]

query = f"""SELECT * FROM table WHERE IN {str(lst)[1:-1}"""
Омар Омейри
источник
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)

user13476428
источник
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),))
Роланд Мехлер
источник