Подготовленные инструкции Python. Проблемы с ВЫБОРОМ В

#python #mysql #sql #prepared-statement

#python #mysql — сервер #sql #подготовленное утверждение #mysql

Вопрос:

У меня возникла проблема с подготовленным оператором на Python, которую я пока не могу решить.

Запрос, который должен быть выполнен, например:

  SELECT md5 FROM software WHERE software_id IN (1, 2, 4)
  

Итак, я попытался выполнить запрос, подобный этому:

 software_id_string = "(2, 3, 4)"
cursor.execute("SELECT md5 FROM software WHERE software_id IN %s", 
                software_id_string)
  

Проблема в том, что в строку » добавлены —> ‘(2, 3, 4)’, так что запрос будет:

 SELECT md5 FROM software WHERE software_id IN ''(2, 3, 4)''
  

Я также попытался перестроить скрипт следующим образом:

 software_id_string = " 1 OR software_id = 2"
cursor.execute("SELECT md5 FROm software WHERE software_id = %s", 
              software_id_string)
  

Это работает только для первого идентификатора, который будет отправлен (в данном случае 1), поскольку OR-part не будет интерпретироваться как оператор SQL…

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

Ответ №1:

Вам нужен один заполнитель для каждого элемента в вашем списке параметров.
Вы можете использовать операции со строками, чтобы выполнить эту часть:

  1. Создайте по одному %s для каждого параметра и
  2. Соедините их вместе запятой.

На следующем шаге вы можете передать свои два аргумента в execute() , как рекомендовано в документации DB-API.

 software_id_string = (1,2,4)
qry = '''SELECT md5 
           FROM software 
          WHERE software_id IN (%s)''' % ','.join(['%s']*len(software_id_string))
# // 'SELECT md5 FROM software WHERE software_id IN (%s,%s,%s)'
cursor.execute(qry, software_id_string)
  

Комментарии:

1. Это отличная идея! Большое вам спасибо!

Ответ №2:

Я рекомендую создать преобразователь типов для явной обработки IN предложений. Это способ, которым psycopg2 модуль обрабатывает это:

 from MySQLdb.converters import conversions

class SQL_IN(object):
    def __init__(self, seq):
        self.seq = seq

    @classmethod
    def escape(cls, obj, d):
        return '('   ','.join((d[type(o)](o, d) for o in obj.seq))   ')'

# add this before you call MySQLdb.connect()
conversions[SQL_IN] = SQL_IN.escape
  

Реальный пример:

 db = MySQLdb.connect()
cursor = db.cursor()

SQL = "SELECT * FROM emp WHERE emp_id IN %s"
in_values = (1, 2, 3)
cursor.execute(SQL, (SQL_IN(in_values),))
print cursor._last_executed
print cursor.fetchall()

SQL = "SELECT * FROM emp WHERE name IN %s"
in_values = ("bob", "fred")
cursor.execute(SQL, (SQL_IN(in_values),))
print cursor._last_executed
print cursor.fetchall()
  

Вывод:

 SELECT * FROM emp WHERE emp_id IN (1,2,3)
((1L, 'bob'), (2L, 'larry'), (3L, 'fred'))
SELECT * FROM emp WHERE name IN ('bob','fred')
((1L, 'bob'), (3L, 'fred'))
  

Ответ №3:

Вы хотите

 cursor.execute("SELECT md5 FROM software WHERE software_id IN %s" % software_id_string)
  

т.е. % вместо запятой

Комментарии:

1. Большое вам спасибо! Это также предотвратит SQL-инъекции? Или это заменит только %s на software_id_string?

2. Он только заменит %s на software_id_string.

3. Не хочу быть слишком педантичным, но, к сожалению, это уязвимо для внедрения SQL. Но даже если у вас были только надежные входные данные, рекомендуемый способ (передача отдельных аргументов в execute() ) все равно предпочтительнее, потому что вам не нужно вручную управлять цитированием значений, DB-API обработает это за вас бесплатно.

4. Переменная software_id_string установлена в предыдущей строке, а не получена от пользователя, поэтому я сказал, что все в порядке.