MySQL 매개 변수화 된 쿼리


80

내 데이터베이스에 정보를 삽입하기 위해 MySQLdb 모듈을 사용하는 데 어려움을 겪고 있습니다. 6 개의 변수를 테이블에 삽입해야합니다.

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

""")

누군가 여기에서 구문을 도와 줄 수 있습니까?

답변:


262

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 문에서 매개 변수를 바인딩하는 데 사용되는 수정자가 다른 DB API 구현에 따라 다르며 mysql 클라이언트 라이브러리가 printf일반적으로 허용되는 '?'대신 스타일 구문을 사용한다는 혼란을 더합니다. 마커 (예 :에서 사용 python-sqlite).


3
@Specto IMO는 항상 올 바르고 안전한 구현 방법을 유지하는 것이 좋습니다. 그것은 올바른 습관과 좋은 프로그래밍 문화를 만듭니다. 또한 여러분의 코드가 앞으로 어떻게 사용 될지 아무도 모릅니다. 누군가가 나중에 다른 시스템이나 웹 사이트에 사용할 수 있습니다.
Roman Podlinov

@BryanHunt 당신은 사용을 켤 수 있습니까? 어딘가에 논쟁이 있지만 어느 논쟁이 어디로 가는지에 대해 많이 알려주지 않기 때문에 권장하지 않습니다. (물론 % s에 대해서도 마찬가지입니다. 같은 이유로 권장하지 않습니다.) 자세한 내용은 여기를 참조하십시오. python.org/dev/peps/pep-0249/#paramstyle
kqr

1
php / pdo에서 왔을 때 나는 printf스타일 %s마커가 매우 혼란 스러웠고 , 취약한 쿼리를 작성하고 있다는 사실에 다소 겁이났습니다. 걱정을 해소 해주셔서 감사합니다! :)
Darragh Enright

18
단일 매개 변수 인 경우 쉼표를 유지하는 것을 c.execute("SELECT * FROM foo WHERE bar = %s", (param1,))
Marius Lian

1
커서 실행의 매개 변수에 대한 컨텍스트 (및 % s가 여전히 필요한 이유)의 경우 MySQLdb 커서에 대한 API 참조는 mysql-python.sourceforge.net/MySQLdb-1.2.2/public/…
RedBarron

50

몇 가지 옵션을 사용할 수 있습니다. 파이썬의 문자열 반복에 익숙해지기를 원할 것입니다. 앞으로 이와 같은 것을 알고 싶을 때 더 성공적으로 검색 할 수있는 용어입니다.

쿼리에 더 적합합니다.

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)

이미 모든 데이터가 개체 또는 사전에있을 수 있다는 점을 고려할 때 두 번째 형식이 더 적합 할 것입니다. 또한 1 년 안에 돌아와서이 메서드를 업데이트해야 할 때 문자열에 "% s"개의 출현 횟수를 세는 것도 짜증납니다. :)


2
주어진 바인드 변수가 SQL 문에서 둘 이상의 위치에서 사용되어야하는 경우 사전 접근 방식이 더 잘 작동하는 것 같습니다. 위치 접근 방식을 사용하면 참조되는만큼 변수를 전달해야하는데, 이는 그리 바람직하지 않습니다.
codeforester

15

링크 된 문서는 다음 예제를 제공합니다.

   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가 숫자 인 경우 % s 대신 % d를 사용해야 할 수 있습니다.)


1
윌 VAR1 및 VAR2가 '또는'처럼 많은 charecters이이 작품.
셰키

3
AFAIK는 %s어떤 유형이든 상관없이 사용해야 합니다. @sheki : 예
ThiefMaster

7

실제로 변수 (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')으로 변환하기 때문입니다. 완료되면 쿼리는 실제로 "... WHERE price <5"라고 말합니다.


예, 이것은 이상한 것입니다. "printf"형식이 실제로 printf 형식을 의미한다고 생각합니다. % s를 어디에서나 사용하는 것이 아닙니다.
fthinker

6

선택한 답변에 대한 대안으로 Marcel과 동일한 안전한 의미를 사용하여 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()

7
감명 받았습니다 ... 당신은 파이썬 코드를 읽을 수 없게 만들었습니다!
Iharob Al Asimi 2016

1

첫 번째 솔루션이 잘 작동합니다. 여기에 작은 세부 사항 하나를 추가하고 싶습니다. 교체 / 업데이트하려는 변수가 str 유형이어야합니다. 내 mysql 유형은 십진수이지만 쿼리를 실행할 수 있도록 매개 변수 변수를 str로 만들어야했습니다.

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

0

여기에 다른 방법이 있습니다. 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)

그리고 변수의 필요성을 더 잘 설명하기 위해 :

주의 : 이스케이프가 수행되고 있음을 유의하십시오.

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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.