список python у запиті sql як параметр


124

У мене є список пітонів, скажімо, л

l = [1,5,8]

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

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

Як я це досягну?

Відповіді:


111

Досі відповіді складали шаблони значень у звичайний рядок 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))буде трохи коротше, поки ще читається імхо
ThiefMaster

7
@Tistentmaster: так, ([placeholder]*len(l))у загальному випадку це повинно бути, оскільки у заповнювача може бути кілька символів.
bobince

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

59

Найпростіший спосіб - повернути список tupleпершим

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

1
Це насправді правильна відповідь. Я не впевнений, чому це було проігноровано. Спочатку ви встановлюєте список l, потім використовуєте кортеж, а потім передаєте кортеж у запит. молодець.
MEdwin

11
Як зазначає @renstrm, це не працює, якщо я просто містить один елемент, ви отримаєте id IN (1,). Що є синтаксичною помилкою.
Подвоєння

1
@Boris, якщо ви не можете гарантувати, що ваше введення завжди є списком, ви можете зробити щось подібне до цього одного вкладиша stackoverflow.com/questions/38821586/…, перш ніж передати аргумент на ваш запит
Амір Імані

10
Цей, як і більшість відповідей тут, крім прийнятого, є вразливим до ін'єкції SQL і його не слід використовувати.
Шматочки бекону

2
@BaconBits Справедливе попередження, але для деяких програм, де введення SQL не є проблемою (наприклад, аналіз бази даних, де я єдиний користувач), це зробить.
Ірен

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
Якщо я містить лише 1 елемент, будьте впевнені, що це кортеж, і він буде працювати. Це рішення на мою думку чіткіше, ніж прийняте.
Алессандро Дентелла

2
Але він все одно не буде працювати, якщо кортеж порожній, тому перед виконанням запиту необхідно додатково перевірити.
Никита Конин

2
Це не працює для мене sqlite3. З якою бібліотекою ви протестували це?
Нік Шаммас

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

23

SQL, який ви хочете, є

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

Якщо ви хочете побудувати це з python, який ви могли б використовувати

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 не підтримує послідовностей.
S.Lott

Ой? Я не усвідомлював. Знову ж таки, я не розумів, що ми збираємось рішення для прив'язки SQLite.
Блер Конрад

Вибачте, не пропонуючи або маючи на увазі SQLite - просто сумно, що прив'язки для списків там не працюють.
S.Lott

11

string.поєднайтеся зі списком, розділеними комами, і використовуйте оператор формату для формування рядка запиту.

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

Рішення для @umount відповіді, оскільки це порушилось з одноелементним кортежем, оскільки (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 string:

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

5
Це не краще рішення через введення sql.
Анураг

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. якщо це a, 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Нотація здається простіше. (Пітон 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.