Truy vấn tham số MySQL


80

Tôi đang gặp khó khăn khi sử dụng mô-đun MySQLdb để chèn thông tin vào cơ sở dữ liệu của mình. Tôi cần chèn 6 biến vào bảng.

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

""")

Ai đó có thể giúp tôi với cú pháp ở đây?

Câu trả lời:


262

Cẩn thận với việc sử dụng nội suy chuỗi cho các truy vấn SQL, vì nó sẽ không thoát khỏi các tham số đầu vào một cách chính xác và sẽ khiến ứng dụng của bạn mở ra các lỗ hổng SQL injection. Sự khác biệt có vẻ nhỏ, nhưng thực tế là rất lớn .

Không chính xác (với các vấn đề bảo mật)

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

Đúng (có thoát)

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

Nó làm tăng thêm sự nhầm lẫn rằng các công cụ sửa đổi được sử dụng để liên kết các tham số trong một câu lệnh SQL khác nhau giữa các triển khai DB API khác nhau và thư viện máy khách mysql sử dụng printfcú pháp kiểu thay vì '?' Được chấp nhận phổ biến hơn. marker (được sử dụng bởi ví dụ. python-sqlite).


3
@Specto IMO có ý nghĩa khi luôn tuân thủ các cách triển khai đúng và an toàn. Nó tạo ra những thói quen đúng đắn và một văn hóa lập trình tốt. Ngoài ra, không ai biết mã của bạn sẽ được sử dụng như thế nào trong tương lai; ai đó có thể sử dụng nó sau này cho hệ thống hoặc trang web khác.
Roman Podlinov

@BryanHunt Bạn có thể bật sử dụng? với một lập luận ở đâu đó, nhưng nó không được khuyến khích vì nó không cho bạn biết nhiều về việc lập luận nào sẽ đi đến đâu. (Điều này cũng có thể được nói về% s, tất nhiên, mà không được khuyến khích vì lý do tương tự.) Thông tin chi tiết ở đây: python.org/dev/peps/pep-0249/#paramstyle
kqr

1
Đến từ php / pdo, tôi vô cùng bối rối nếu đánh dấu printfkiểu %s, và tôi thực sự kinh hãi rằng tôi đang viết các truy vấn dễ bị tấn công. Cảm ơn vì đã xóa tan lo lắng đó! :)
Darragh Enright

18
Khi nó một tham số duy nhất nhớ để giữ dấu phẩy: c.execute("SELECT * FROM foo WHERE bar = %s", (param1,))
Marius Lian

1
Đối với ngữ cảnh về các tham số trong con trỏ thực thi (và tại sao vẫn cần% s), tham chiếu api cho con trỏ MySQLdb có
RedBarron

50

Bạn có một số tùy chọn có sẵn. Bạn sẽ muốn cảm thấy thoải mái với nội suy chuỗi của python. Đó là một thuật ngữ mà bạn có thể tìm kiếm thành công hơn trong tương lai khi bạn muốn biết những thứ như thế này.

Tốt hơn cho các truy vấn:

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)

Xem xét bạn có thể đã có tất cả dữ liệu của mình trong một đối tượng hoặc từ điển, định dạng thứ hai sẽ phù hợp với bạn hơn. Ngoài ra, thật tệ khi phải đếm số lần xuất hiện "% s" trong một chuỗi khi bạn phải quay lại và cập nhật phương thức này trong một năm :)


2
Có vẻ như cách tiếp cận từ điển hoạt động tốt hơn trong trường hợp một biến liên kết nhất định phải được sử dụng ở nhiều nơi trong câu lệnh SQL. Với cách tiếp cận vị trí, chúng ta cần truyền biến nhiều lần khi nó được tham chiếu, điều này không được mong muốn lắm.
codeforester

15

Các tài liệu được liên kết đưa ra ví dụ sau:

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

Vì vậy, bạn chỉ cần điều chỉnh nó với mã của riêng bạn - ví dụ:

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

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

(Nếu SongLength là số, bạn có thể cần sử dụng% d thay vì% s).


1
sẽ làm việc này nơi var1 và var2 có charecters như "hay".
Sheki

3
AFAIK bạn phải sử dụng %sở đó bất kể loại nào. @sheki: Có
ThiefMaster

7

Trên thực tế, ngay cả khi biến của bạn (SongLength) là số, bạn vẫn sẽ phải định dạng nó bằng% s để liên kết tham số một cách chính xác. Nếu bạn cố gắng sử dụng% d, bạn sẽ gặp lỗi. Đây là một đoạn trích nhỏ từ liên kết này http://mysql-python.sourceforge.net/MySQLdb.html :

Để thực hiện một truy vấn, trước tiên bạn cần một con trỏ, sau đó bạn có thể thực hiện các truy vấn trên đó:

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

Trong ví dụ này, max_price = 5 Vậy tại sao lại sử dụng% s trong chuỗi? Bởi vì MySQLdb sẽ chuyển đổi nó thành một giá trị theo nghĩa đen của SQL, đó là chuỗi '5'. Khi kết thúc, truy vấn sẽ thực sự nói, "... WHERE price <5".


Vâng, đây là một điều kỳ lạ được thôi ... bạn nghĩ rằng định dạng "printf" có nghĩa là ... thực sự là định dạng printf và không chỉ sử dụng% s ở mọi nơi.
fthinker

6

Để thay thế cho câu trả lời đã chọn và với cùng ngữ nghĩa an toàn của Marcel, đây là một cách nhỏ gọn để sử dụng từ điển Python để chỉ định các giá trị. Nó có lợi ích là dễ dàng sửa đổi khi bạn thêm hoặc xóa các cột để chèn:

  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()

Trong đó meta là từ điển chứa các giá trị cần chèn. Cập nhật có thể được thực hiện theo cách tương tự:

  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
Tôi rất ấn tượng ... bạn đã quản lý để làm cho mã python không thể đọc được!
Iharob Al Asimi,

1

Giải pháp đầu tiên hoạt động tốt. Tôi muốn thêm một chi tiết nhỏ ở đây. Đảm bảo rằng biến bạn đang cố gắng thay thế / cập nhật nó sẽ phải là kiểu str. Kiểu mysql của tôi là số thập phân nhưng tôi phải tạo biến tham số là str để có thể thực hiện truy vấn.

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

0

Đây là một cách khác để làm điều đó. Nó được ghi lại trên trang web chính thức của MySQL. https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-execute.html

Về mặt tinh thần, nó sử dụng cùng một cơ chế trong câu trả lời của @Trey Stout. Tuy nhiên, tôi thấy cái này đẹp hơn và dễ đọc hơn.

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)

Và để minh họa rõ hơn bất kỳ nhu cầu nào về các biến:

NB: lưu ý việc thoát đang được thực hiện.

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.