розгортання списку для використання в реченні python MySQLDB IN


84

Я знаю, як зіставити список із рядком:

foostring = ",".join( map(str, list_of_ids) )

І я знаю, що можу використати наступне, щоб перевести цей рядок у речення IN:

cursor.execute("DELETE FROM foo.bar WHERE baz IN ('%s')" % (foostring))

Мені потрібно зробити те ж саме БЕЗПЕЧНО (уникаючи введення SQL) за допомогою MySQLDB. У наведеному вище прикладі, оскільки foostring не передається як аргумент для виконання, він є вразливим. Я також маю цитувати та виходити за межі бібліотеки mysql.

відповідне запитання SO , але перелічені там відповіді або не працюють для MySQLDB, або вразливі до введення SQL.)


Ви можете бути в змозі отримати натхнення від подібного питання , що робиться в PHP stackoverflow.com/questions/327274 / ...
Zoredache


@mluebke Будь-яка ідея про передачу кількох списків у запиті?
Діпен Деданія

Відповіді:


157

Використовуйте list_of_idsбезпосередньо:

format_strings = ','.join(['%s'] * len(list_of_ids))
cursor.execute("DELETE FROM foo.bar WHERE baz IN (%s)" % format_strings,
                tuple(list_of_ids))

Таким чином, вам не доведеться цитувати себе, а також уникати будь-яких ін'єкцій sql.

Зверніть увагу, що дані ( list_of_ids) надходять безпосередньо до драйвера mysql, як параметр (не в тексті запиту), тому введення не відбувається. Ви можете залишити будь-які символи, які хочете, у рядку, не потрібно видаляти чи цитувати символи.


2
@heikogerlach: Я не цитую% s ... Перший рядок створює рядок "% s,% s,% s" ... однаковий розмір list_of_ids довжини.
nosklo

Арг, ти маєш рацію. Потрібно придивитися важче. Якось я це змішав. Хороше рішення.

Чи буде це працювати і в sqlite? Тому що я щойно спробував, і, схоже, вказує на синтаксичні помилки.
Sohaib

@Sohaib в sqlite заміна char ?не є %sтакою, щоб вона працювала, якщо змінити перший рядок на format_strings = ','.join('?' * len(list_of_ids)).
nosklo

1
@kdas у вашому випадку ви не хочете, щоб % format_stringsчастина змінила інші %sзаповнювачі у вашому запиті, лише IN (%s)заповнювач - спосіб досягти цього - подвоїти всі %символи, крім тієї, яку ви хочете замінити:query = ("select distinct cln from vcf_commits where branch like %%s and repository like %%s and filename in (%s) and author not like %%s" % format_strings,); cursor.execute(query, (branch, repository) + tuple(fname_list) + (invalid_author,))
nosklo

5

Хоча це питання досить давнє, я вважав, що було б краще залишити відповідь, якщо хтось інший шукає те, що я хотів

Прийнята відповідь стає брудною, коли у нас багато параметрів або якщо ми хочемо використовувати іменовані параметри

Після деяких випробувань

ids = [5, 3, ...]  # list of ids
cursor.execute('''
SELECT 
...
WHERE
  id IN %(ids)s
  AND created_at > %(start_dt)s
''', {
  'ids': tuple(ids), 'start_dt': '2019-10-31 00:00:00'
})

Випробувано з python2.7,pymysql==0.7.11


2
Це не працює з python 3 та mysql-connector-python 8.0.21. Повертається помилка "Кортеж Python неможливо перетворити на тип MySQL".
Румс

-1

Якщо ви використовуєте Django 2.0 or 2.1і Python 3.6, це правильний спосіб:

from django.db import connection
RESULT_COLS = ['col1', 'col2', 'col3']
RESULT_COLS_STR = ', '.join(['a.'+'`'+i+'`' for i in RESULT_COLS])
QUERY_INDEX = RESULT_COLS[0]

TABLE_NAME = 'test'
search_value = ['ab', 'cd', 'ef']  # <-- a list
query = (
    f'SELECT DISTINCT {RESULT_COLS_STR} FROM {TABLE_NAME} a '
    f'WHERE a.`{RESULT_COLS[0]}` IN %s '
    f'ORDER BY a.`{RESULT_COLS[0]}`;'
)  # <- 'SELECT DISTINCT a.`col1`, a.`col2`, a.`col3` FROM test a WHERE a.`col1` IN %s ORDER BY a.`col1`;'
with connection.cursor() as cursor:
    cursor.execute(query, params=[search_value])  # params is a list with a list as its element

посилання: https://stackoverflow.com/a/23891759/2803344 https://docs.djangoproject.com/en/2.1/topics/db/sql/#passing-parameters-into-raw


-1

Хоча це питання досить давнє. Я ділюсь своїм рішенням, якщо воно може комусь допомогти.

list_to_check = ['A', 'B'] cursor.execute("DELETE FROM foo.bar WHERE baz IN ({})".format(str(list_to_check)[1:-1])

Перевірено за допомогою Python=3.6


Я боюся, що це рішення вразливе до атак введення SQL, оскільки надане list_to_checkне є захищеним від SQL . Ось чому передача значень як параметрів executeдоцільніша. Використовуйте це рішення дуже обережно (тобто вхідні ідентифікатори не отримуються як параметри ззовні вашої програми), оскільки хтось може використовувати це для атаки на вашу систему та доступу до вашої бази даних.
Румс

-2

Ще одне просте рішення із використанням розуміння списку:

# creating a new list of strings and convert to tuple
sql_list = tuple([ key.encode("UTF-8") for key in list_of_ids ])

# replace "{}" with "('id1','id2',...'idlast')"
cursor.execute("DELETE FROM foo.bar WHERE baz IN {}".format(sql_list))

-4
list_of_ids = [ 1, 2, 3]
query = "select * from table where x in %s" % str(tuple(list_of_ids))
print query

Це може спрацювати для деяких випадків використання, якщо ви не хочете мати справу з методом, в якому вам потрібно передавати аргументи для заповнення рядка запиту, і ви хочете викликати справедливий cursror.execute(query).

Іншим способом може бути:

"select * from table where x in (%s)" % ', '.join(str(id) for id in list_of_ids)

-7

Дуже просто: просто скористайтеся наведеним нижче формуванням

rules_id = ["9", "10"]

sql1 = "ВИБЕРІТЬ * ІЗ відвідуваності_правил_служби WHERE ідентифікатор у (" + "," .join (map (str, rules_id)) + ")"

"," .join (map (str, rules_id))


Де він робить sql-цитування і хіба це не використовує літерал замість змінних прив'язки?
eckes

Не потрібно, це просто працює нормально. Ви можете перевірити, тому що формування кортежів безпосередньо перетворюється як рядок із першими фігурними дужками ("9", "10"). Які регулюють формування sql. Отже, вам не потрібно інше утворення, щоб зробити sql придатним
Mizanur Rahman

1
а якщо an rules_idмістить "); DROP TABLES Bobby --?
eckes

Вже сказано "вибухування списку" не ") ... тому перед запитом вам потрібно перевірити
Мізанур Рахман

або скористайтеся: sql1 = "SELECT * FROM відвідуваність_рулеси_стаффа WHERE id in (" + "," .join (map (str, rules_id)) + ")"
Mizanur Rahman
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.