MySQL的SQL預處理(Prepared)

Prepared SQL StatementSQL的執行預編譯處理語法注意點mysql

1、SQL 語句的執行處理
一、即時 SQL
  一條 SQL 在 DB 接收到最終執行完畢返回,大體的過程以下:
  1. 詞法和語義解析;
  2. 優化 SQL 語句,制定執行計劃;
  3. 執行並返回結果;
  如上,一條 SQL 直接是走流程處理,一次編譯,單次運行,此類普通語句被稱做 Immediate Statements (即時 SQL)。
二、預處理 SQL
  可是,絕大多數狀況下,某需求某一條 SQL 語句可能會被反覆調用執行,或者每次執行的時候只有個別的值不一樣(好比 select 的 where 子句值不一樣,update 的 set 子句值不一樣,insert 的 values 值不一樣)。若是每次都須要通過上面的詞法語義解析、語句優化、制定執行計劃等,則效率就明顯不行了。
  所謂預編譯語句就是將此類 SQL 語句中的值用佔位符替代,能夠視爲將 SQL 語句模板化或者說參數化,通常稱這類語句叫Prepared Statements。
  預編譯語句的優點在於概括爲:一次編譯、屢次運行,省去了解析優化等過程;此外預編譯語句能防止 SQL 注入。
注意:
  雖然多是經過預處理 SQL 的方式必定程度的提升了效率,可是對於優化而言,最優的執行計劃不是光靠 SQL 語句的模板化來實現的,每每仍是須要經過具體值來預估出成本代價。sql

2、Prepared SQL Statement Syntax
  MySQL 官方將 prepare、execute、deallocate 統稱爲 PREPARE STATEMENT。翻譯也就習慣的稱其爲預處理語句。
  MySQL 預處理語句的支持版本較早,因此咱們目前廣泛使用的 MySQL 版本都是支持這一語法的。
語法:session

# 定義預處理語句 PREPARE stmt_name FROM preparable_stmt; # 執行預處理語句 EXECUTE stmt_name [USING @var_name [, @var_name] ...]; # 刪除(釋放)定義 {DEALLOCATE | DROP} PREPARE stmt_name;

 一、利用字符串定義預處理 SQL (直角三角形計算)優化

mysql> PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse'; Query OK, 0 rows affected (0.00 sec) Statement prepared mysql> SET @a = 3; Query OK, 0 rows affected (0.00 sec) mysql> SET @b = 4; Query OK, 0 rows affected (0.00 sec) mysql> EXECUTE stmt1 USING @a, @b; +------------+
| hypotenuse |
+------------+
|          5 |
+------------+
1 row in set (0.00 sec) mysql> DEALLOCATE PREPARE stmt1; Query OK, 0 rows affected (0.00 sec)

 二、利用變量定義預處理 SQL (直角三角形計算)spa

mysql> SET @s = 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse'; Query OK, 0 rows affected (0.00 sec) mysql> PREPARE stmt2 FROM @s; Query OK, 0 rows affected (0.00 sec) Statement prepared mysql> SET @c = 6; Query OK, 0 rows affected (0.00 sec) mysql> SET @d = 8; Query OK, 0 rows affected (0.00 sec) mysql> EXECUTE stmt2 USING @c, @d; +------------+
| hypotenuse |
+------------+
|         10 |
+------------+
1 row in set (0.00 sec) mysql> DEALLOCATE PREPARE stmt2; Query OK, 0 rows affected (0.00 sec)

 三、解決沒法傳參問題
  咱們知道,對於 LIMIT 子句中的值,必須是常量,不得使用變量,也就是說不能使用:SELECT * FROM TABLE LIMIT @skip, @numrows; 如此,就能夠是用 PREPARE 語句解決此問題。翻譯

mysql> SET @skip = 100; SET @numrows = 3; Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM t1 LIMIT @skip, @numrows; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@skip, @numrows' at line 1 mysql> PREPARE stmt3 FROM "SELECT * FROM t1 LIMIT ?, ?"; Query OK, 0 rows affected (0.00 sec) Statement prepared mysql> EXECUTE stmt3 USING @skip, @numrows; +-----+--------+
| a   | filler |
+-----+--------+
| 100 | filler |
| 101 | filler |
| 102 | filler |
+-----+--------+
3 rows in set (0.00 sec) mysql> DEALLOCATE PREPARE stmt3; Query OK, 0 rows affected (0.00 sec)

   如此一來,結合2中介紹的利用變量定義預處理 SQL 也就基本解決了傳參時語法報錯問題了,相似的:用變量傳參作表名時,MySQL 會把變量名當作表名,這樣既不是本意,也會是語法錯誤,在 SQL Server 的解決辦法是利用字符串拼接穿插變量進行傳參,再將整條 SQL 語句做爲變量,最後是用 sp_executesql 調用該拼接 SQL 執行,而 Prepared SQL Statement 可謂殊途同歸之妙。code

mysql> SET @table = 't2'; Query OK, 0 rows affected (0.00 sec) mysql> SET @s = CONCAT('SELECT * FROM ', @table); Query OK, 0 rows affected (0.00 sec) mysql> PREPARE stmt4 FROM @s; Query OK, 0 rows affected (0.00 sec) Statement prepared mysql> EXECUTE stmt4; +------+-------+-------+
| id   | score | grade |
+------+-------+-------+
|    1 |    99 | A     |
|    2 |    81 | B     |
|    3 |    55 | D     |
|    4 |    69 | C     |
+------+-------+-------+
4 rows in set (0.00 sec) mysql> DROP PREPARE stmt4; Query OK, 0 rows affected (0.00 sec)

 

3、預處理 SQL 使用注意點
一、stmt_name 做爲 preparable_stmt 的接收者,惟一標識,不區分大小寫。
二、preparable_stmt 語句中的 ? 是個佔位符,所表明的是一個字符串,不須要將 ? 用引號包含起來。
三、定義一個已存在的 stmt_name ,原有的將被當即釋放,相似於變量的從新賦值。
四、PREPARE stmt_name 的做用域是session級server


  能夠經過 max_prepared_stmt_count 變量來控制全局最大的存儲的預處理語句。blog

mysql> show variables like 'max_prepared%'; +-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| max_prepared_stmt_count | 16382 |
+-------------------------+-------+
1 row in set (0.00 sec)

   預處理編譯 SQL 是佔用資源的,因此在使用後注意及時使用 DEALLOCATE PREPARE 釋放資源,這是一個好習慣。

ip

相關文章
相關標籤/搜索