【已解決】Python MySQL: Not all parameters were used in the SQL statement


一、事故緣起

今天構造了一個超過 50 多個參數的 SQL 插入語句,在執行的時候提示 Not all parameters were used in the SQL statement,提示「SQL 語句中未使用所有參數」的異常,但是前前后后檢查了 SQL 語句,發現每個參數都是與相應的字段一一對應的,類似於下面這樣的代碼塊:

mydb = mysql.connect(...)
cursor = mydb.cursor()

queries = [(...), (...), (...)]  # 當然這幾部分都是動態構造的
sql = "INSERT INTO `db`.`table`(`col01`, `col02`, `col03`) VALUES (%s, %s, %s)"
# 注意上條語句中 %s 和 Python 本身的字符串占位符重合,但其實不能混用
# 它可以用 ? 號代替,但是不能用 Python 的 %d, %f 等格式化符號代替
cursor.executemany(sql, queries)

cursor.close()
mydb.close()

在確定參數一個都沒有少的情況下,開始猜測是系統截斷了什么東西,然后在 StackOverflow 回答 的評論區找到了上述異常的解決方案,在聲明數據庫游標 cursor 的時候,直接加上參數 prepared=True 就搞定了,類似於下面的語句:

cursor = mydb.cursor(prepared=True)

接下來的文章就試着描述一下這個 prepared 預編譯的作用吧,文章要點參考自 PyNative,加入了一些個人見解。

二、什么是參數化查詢

參數化查詢是在原有的查詢語句中,用占位符填充各個參數,然后在執行的過程中,再將參數值傳入的一種方法。這意味着參數化查詢的被調用語句只被編譯一次,但是可以重復傳參。比如下面這條語句:

sql = "UPDATE `transaction` SET `quantity` = %s WHERE `customer_id` = %s"

在上述語句中,我們使用了 MySQL 中的 %s 占位符來傳遞參數值,當然這里用 ? 號或者關鍵詞參數 %(customer_id)s 也是可以的,通過 execute(sql, data_tuple) 能夠有效地防止 SQL 注入攻擊,當然它有更多其他好處。

三、參數化查詢的益處

參數化查詢的特性主要有以下 4 點:

  1. 只編譯一次:在默認的標准查詢中,MySQL 在每次執行的時候都會編譯一次語句。而在參數化查詢中,被執行的查詢語句只被編譯一次,然后在內存中恭候傳入參數進行調用,MySQL 能夠直接執行預編譯好的語句從而減少每次編譯的時間;
  2. 加快執行速度:尤其是需要多次調用相同的 SQL 語句時,次數越多速度提升越明顯,有點像正則表達式的 compile 預編譯;
  3. 能夠使用不同的數據執行相同的操作:假設你擁有幾百行的數據要插入到數據庫表中,使用參數化查詢能夠存入不同的字段值;
  4. 防止 SQL 注入攻擊,有效提升數據安全。

四、如何使用參數化查詢

我們使用 mysql.connection.cursor(prepared=True) 聲明游標,就可以使用參數化查詢執行預編譯語句了,它本身是一個 MySQLCursorPrepared 類,繼承自 MySQLCursor 類對象。

from mysql import connector

connection = connector.connect(
    host="localhost",
    port="3306",
    user="MoonYear530",
    password="StanleyBlog",
    database="query_test"
)
cursor = connection.cursor(prepared=True)
# 上一條語句也可以擬寫如下:
# cursor = connection.cursor(cursor_class=MySQLCursorPrepared)

這樣,在接下來的每一次調用中,傳入的參數雖然不同,但是在准備執行時發現 SQL 語句是一樣的時候,就會跳過編譯了。

五、使用參數化查詢更新數據的示例

from mysql import connector
from mysql.connector import Error

try:
    connection = connector.connect(
        host="localhost",
        port="3306",
        user="MoonYear530",
        password="StanleyBlog",
        database="query_test"
    )  # 創建數據庫連接
    cursor = connection.cursor(prepared=True)  # 聲明數據游標

    sql = """UPDATE `transaction`
             SET `update_date` = %s, `quantity` = %s
             WHERE `customer_id` = %s"""  # 被調用 SQL 語句
    data_tuple = ("2020-09-20", 16530, "SZ0198")  # 傳入的參數元組

    cursor.execute(sql, data_tuple)  # 執行 SQL 語句
    connection.commit()  # 提交事務
    print("恭喜,數據更新成功!")

except Error as error:
    connection.rollback()  # 執行失敗,數據回滾
    print(f"參數化查詢執行異常:{error}")

finally:
    if connection.is_connected():
        cursor.close()  # 關閉數據游標
        connection.close()  # 關閉數據庫連接
        print("老鐵,數據庫連接已關閉。")

以上代碼只是一個簡短的示例,需要按照工程內容適當修改,祝一切順利,代碼無 Bug,身體倍棒,吃嘛嘛香~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM