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