MySQL параметризованные запросы

Мне трудно использовать модуль MySQLdb для вставки информации в мою базу данных. Мне нужно вставить 6 переменных в таблицу.

cursor.execute ("""
    INSERT INTO Songs (SongName, SongArtist, SongAlbum, SongGenre, SongLength, SongLocation)
    VALUES
        (var1, var2, var3, var4, var5, var6)

""")

Может ли кто-нибудь помочь мне с синтаксисом здесь?


person Specto    schedule 22.04.2009    source источник


Ответы (7)


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

Неверно (с проблемами безопасности)

c.execute("SELECT * FROM foo WHERE bar = %s AND baz = %s" % (param1, param2))

Правильно (с побегом)

c.execute("SELECT * FROM foo WHERE bar = %s AND baz = %s", (param1, param2))

Это добавляет путаницы из-за того, что модификаторы, используемые для привязки параметров в операторе SQL, различаются в разных реализациях API БД и что клиентская библиотека mysql использует синтаксис стиля printf вместо более общепринятого '?' маркер (используется, например, python-sqlite).

person Emil H    schedule 22.04.2009
comment
@Specto IMO имеет смысл всегда придерживаться правильных и безопасных способов реализации. Это формирует правильные привычки и хорошую культуру программирования. Также никто не знает, как ваш код будет использоваться в будущем; кто-то может использовать его позже для другой системы или веб-сайта. - person Roman Podlinov; 21.05.2013
comment
@BryanHunt Вы можете включить использование ? с аргументом где-то, но это обескураживает, потому что это не говорит вам много о том, какой аргумент куда идет. (Конечно, то же самое можно сказать и о %s, который не рекомендуется по той же причине.) Дополнительная информация здесь: python.org/dev/peps/pep-0249/#paramstyle - person kqr; 15.01.2015
comment
Исходя из php/pdo, я был очень сбит с толку, если маркер printf в стиле %s, и я был в ужасе от того, что пишу уязвимые запросы. Спасибо, что развеяли эту тревогу! :) - person Darragh Enright; 28.06.2015
comment
У вас есть какие-либо документы, в которых рассматривается разница между % и ,? - person bzupnick; 03.04.2016
comment
@bzupnick Оператор % помещает переменные в строку, это обычный код Python. , просто разделяет аргументы в вызове функции/метода, поэтому ваш список переменных становится вторым аргументом. Этот метод execute, по-видимому, имеет необязательный второй аргумент. - person Luc; 14.05.2016
comment
Если это один параметр, не забудьте оставить запятую: c.execute("SELECT * FROM foo WHERE bar = %s", (param1,)) - person Marius Lian; 27.07.2016
comment
Для получения информации о параметрах выполнения курсора (и о том, почему %s все еще необходим), ссылка на API для курсора MySQLdb находится по адресу mysql-python.sourceforge.net/MySQLdb-1.2.2/public/ - person RedBarron; 16.03.2017
comment
Если я не ошибаюсь, это также способ вставки в строку функций регистратора, таких как информация, ошибка и т. Д.... - person Ciasto piekarz; 12.02.2018

У вас есть несколько вариантов. Вы захотите освоиться с итерполяцией строк Python. Это термин, который вы, возможно, добьетесь большего успеха в поиске в будущем, когда захотите узнать что-то подобное.

Лучше для запросов:

some_dictionary_with_the_data = {
    'name': 'awesome song',
    'artist': 'some band',
    etc...
}
cursor.execute ("""
            INSERT INTO Songs (SongName, SongArtist, SongAlbum, SongGenre, SongLength, SongLocation)
            VALUES
                (%(name)s, %(artist)s, %(album)s, %(genre)s, %(length)s, %(location)s)

        """, some_dictionary_with_the_data)

Учитывая, что вы, вероятно, уже имеете все свои данные в объекте или словаре, второй формат подойдет вам лучше. Также отстойно подсчитывать появление «%s» в строке, когда вам нужно вернуться и обновить этот метод через год :)

person Trey Stout    schedule 22.04.2009
comment
Похоже, что подход со словарем работает лучше, если данная переменная связывания должна использоваться более чем в одном месте в операторе SQL. При позиционном подходе нам нужно передавать переменную столько раз, сколько на нее ссылаются, что не очень желательно. - person codeforester; 19.12.2018

Связанные документы дают следующий пример:

   cursor.execute ("""
         UPDATE animal SET name = %s
         WHERE name = %s
       """, ("snake", "turtle"))
   print "Number of rows updated: %d" % cursor.rowcount

Так что вам просто нужно адаптировать это к вашему собственному коду - пример:

cursor.execute ("""
            INSERT INTO Songs (SongName, SongArtist, SongAlbum, SongGenre, SongLength, SongLocation)
            VALUES
                (%s, %s, %s, %s, %s, %s)

        """, (var1, var2, var3, var4, var5, var6))

(Если SongLength является числовым, вам может понадобиться использовать %d вместо %s).

person Marcel Guzman    schedule 22.04.2009
comment
будет ли это работать, если var1 и var2 имеют такие символы, как или ' . - person sheki; 24.10.2011
comment
Насколько я знаю, вы должны использовать %s независимо от типа. @sheki: Да - person ThiefMaster; 27.05.2016

На самом деле, даже если ваша переменная (SongLength) является числовой, вам все равно придется отформатировать ее с помощью %s, чтобы правильно связать параметр. Если вы попытаетесь использовать %d, вы получите сообщение об ошибке. Вот небольшой отрывок из этой ссылки http://mysql-python.sourceforge.net/MySQLdb.html< /а>:

Для выполнения запроса сначала нужен курсор, а потом уже можно выполнять запросы по нему:

c=db.cursor()
max_price=5
c.execute("""SELECT spam, eggs, sausage FROM breakfast
          WHERE price < %s""", (max_price,))

В этом примере max_price=5 Зачем тогда использовать %s в строке? Потому что MySQLdb преобразует его в буквальное значение SQL, которое представляет собой строку «5». Когда он будет завершен, запрос на самом деле скажет: «...ГДЕ цена ‹ 5».

person daSong    schedule 27.03.2011
comment
Да, это странно... вы думаете, что формат printf будет означать... на самом деле формат printf, а не просто использование %s везде. - person fthinker; 10.10.2013

В качестве альтернативы выбранному ответу и с той же безопасной семантикой, что и у Марселя, вот компактный способ использования словаря Python для указания значений. Его преимущество заключается в том, что его легко изменить при добавлении или удалении столбцов для вставки:

  meta_cols = ('SongName','SongArtist','SongAlbum','SongGenre')
  insert = 'insert into Songs ({0}) values ({1})'.format(
      ','.join(meta_cols), ','.join( ['%s']*len(meta_cols)))
  args = [ meta[i] for i in meta_cols ]
  cursor = db.cursor()
  cursor.execute(insert,args)
  db.commit()

Где meta — это словарь, содержащий значения для вставки. Обновление можно сделать так же:

  meta_cols = ('SongName','SongArtist','SongAlbum','SongGenre')
  update='update Songs set {0} where id=%s'.
        .format(','.join([ '{0}=%s'.format(c) for c in meta_cols ]))
  args = [ meta[i] for i in meta_cols ]
  args.append(songid)
  cursor=db.cursor()
  cursor.execute(update,args)
  db.commit()
person FDS    schedule 12.04.2013
comment
Я впечатлен... вам удалось сделать код Python нечитаемым! - person Iharob Al Asimi; 01.12.2016

Первое решение работает хорошо. Здесь я хочу добавить одну маленькую деталь. Убедитесь, что переменная, которую вы пытаетесь заменить/обновить, должна иметь тип str. Мой тип mysql десятичный, но мне пришлось сделать переменную параметра как str, чтобы иметь возможность выполнить запрос.

temp = "100"
myCursor.execute("UPDATE testDB.UPS SET netAmount = %s WHERE auditSysNum = '42452'",(temp,))
myCursor.execute(var)
person Parth Shah    schedule 12.09.2018

Вот еще один способ сделать это. Это задокументировано на официальном сайте MySQL. https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-execute.html

По духу он использует ту же механику, что и ответ @Trey Stout. Тем не менее, я нахожу это более красивым и более читабельным.

insert_stmt = (
  "INSERT INTO employees (emp_no, first_name, last_name, hire_date) "
  "VALUES (%s, %s, %s, %s)"
)
data = (2, 'Jane', 'Doe', datetime.date(2012, 3, 23))
cursor.execute(insert_stmt, data)

И чтобы лучше проиллюстрировать необходимость переменных:

NB: обратите внимание на побег.

employee_id = 2
first_name = "Jane"
last_name = "Doe"

insert_stmt = (
  "INSERT INTO employees (emp_no, first_name, last_name, hire_date) "
  "VALUES (%s, %s, %s, %s)"
)
data = (employee_id, conn.escape_string(first_name), conn.escape_string(last_name), datetime.date(2012, 3, 23))
cursor.execute(insert_stmt, data)
person Djidiouf    schedule 01.02.2018