MySQL?SQL預(yù)處理(Prepared)的語法實(shí)例與注意事項(xiàng)
一、SQL 語句的執(zhí)行處理
1、即時(shí) SQL
一條 SQL 在 DB 接收到最終執(zhí)行完畢返回,大致的過程如下:
1. 詞法和語義解析;
2. 優(yōu)化 SQL 語句,制定執(zhí)行計(jì)劃;
3. 執(zhí)行并返回結(jié)果;
如上,一條 SQL 直接是走流程處理,一次編譯,單次運(yùn)行,此類普通語句被稱作 Immediate Statements (即時(shí) SQL)。
2、預(yù)處理 SQL
但是,絕大多數(shù)情況下,某需求某一條 SQL 語句可能會被反復(fù)調(diào)用執(zhí)行,或者每次執(zhí)行的時(shí)候只有個(gè)別的值不同(比如 select 的 where 子句值不同,update 的 set 子句值不同,insert 的 values 值不同)。如果每次都需要經(jīng)過上面的詞法語義解析、語句優(yōu)化、制定執(zhí)行計(jì)劃等,則效率就明顯不行了。
所謂預(yù)編譯語句就是將此類 SQL 語句中的值用占位符替代,可以視為將 SQL 語句模板化或者說參數(shù)化,一般稱這類語句叫Prepared Statements。
預(yù)編譯語句的優(yōu)勢在于歸納為:一次編譯、多次運(yùn)行,省去了解析優(yōu)化等過程;此外預(yù)編譯語句能防止 SQL 注入。
注意:
雖然可能是通過預(yù)處理 SQL 的方式一定程度的提高了效率,但是對于優(yōu)化而言,最優(yōu)的執(zhí)行計(jì)劃不是光靠 SQL 語句的模板化來實(shí)現(xiàn)的,往往還是需要通過具體值來預(yù)估出成本代價(jià)。
二、Prepared SQL Statement Syntax
MySQL 官方將 prepare、execute、deallocate 統(tǒng)稱為 PREPARE STATEMENT。翻譯也就習(xí)慣的稱其為預(yù)處理語句。
MySQL 預(yù)處理語句的支持版本較早,所以我們目前普遍使用的 MySQL 版本都是支持這一語法的。
語法:
# 定義預(yù)處理語句 PREPARE stmt_name FROM preparable_stmt; # 執(zhí)行預(yù)處理語句 EXECUTE stmt_name [USING @var_name [, @var_name] ...]; # 刪除(釋放)定義 {DEALLOCATE | DROP} PREPARE stmt_name;
1、利用字符串定義預(yù)處理 SQL (直角三角形計(jì)算)
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)
2、利用變量定義預(yù)處理 SQL (直角三角形計(jì)算)
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)
3、解決無法傳參問題
我們知道,對于 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)
如此一來,結(jié)合2中介紹的利用變量定義預(yù)處理 SQL 也就基本解決了傳參時(shí)語法報(bào)錯(cuò)問題了,類似的:用變量傳參做表名時(shí),MySQL 會把變量名當(dāng)做表名,這樣既不是本意,也會是語法錯(cuò)誤,在 SQL Server 的解決辦法是利用字符串拼接穿插變量進(jìn)行傳參,再將整條 SQL 語句作為變量,最后是用 sp_executesql 調(diào)用該拼接 SQL 執(zhí)行,而 Prepared SQL Statement 可謂異曲同工之妙。
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)
三、預(yù)處理 SQL 使用注意點(diǎn)
1、stmt_name 作為 preparable_stmt 的接收者,唯一標(biāo)識,不區(qū)分大小寫。
2、preparable_stmt 語句中的 ? 是個(gè)占位符,所代表的是一個(gè)字符串,不需要將 ? 用引號包含起來。
3、定義一個(gè)已存在的 stmt_name ,原有的將被立即釋放,類似于變量的重新賦值。
4、PREPARE stmt_name 的作用域是session級
可以通過 max_prepared_stmt_count 變量來控制全局最大的存儲的預(yù)處理語句。
mysql> show variables like 'max_prepared%'; +-------------------------+-------+ | Variable_name | Value | +-------------------------+-------+ | max_prepared_stmt_count | 16382 | +-------------------------+-------+ 1 row in set (0.00 sec)
預(yù)處理編譯 SQL 是占用資源的,所以在使用后注意及時(shí)使用 DEALLOCATE PREPARE 釋放資源,這是一個(gè)好習(xí)慣。
四、Prepared Statements優(yōu)點(diǎn)
1.安全
Prepared Statements通過sql邏輯與數(shù)據(jù)的分離來增加安全,sql邏輯與數(shù)據(jù)的分離能防止普通類型的sql注入攻擊(SQL injection attack)。
2.性能
Prepared Statements只語法分析一次,你初始話Prepared Statements時(shí),mysql將檢查語法并準(zhǔn)備語句的運(yùn)行,當(dāng)你執(zhí)行query 多次時(shí),這樣就不會在有額外的負(fù)擔(dān)了,如果,當(dāng)運(yùn)行query 很多次的時(shí)候(如:insert)這種預(yù)處理有很大的性能提高
他使用binary protocol協(xié)議,這樣更能提高效率。
總結(jié)
到此這篇關(guān)于MySQL SQL預(yù)處理(Prepared)的文章就介紹到這了,更多相關(guān)MySQL SQL預(yù)處理(Prepared)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
CentOS 7中源碼安裝MySQL 5.7.6+詳細(xì)教程
最近在CentOS 7中源碼安裝MySQL 5.7.6+,發(fā)現(xiàn)MySQL5.7.6+以后的安裝方式真的與以前版本的MySQL安裝方式大大的不同呀。不自己安裝一把,你都不知道不同之處在哪,下面這篇文章是通過自己的安裝過程總結(jié)的一篇安裝教程,有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-12-12使用SQL實(shí)現(xiàn)小計(jì),合計(jì)以及排序
本篇文章是對SQL實(shí)現(xiàn)小計(jì),合計(jì)以及排序進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06MySQL查詢優(yōu)化--調(diào)整內(nèi)部變量的詳解
本篇文章是對MySQL查詢優(yōu)化中的調(diào)整內(nèi)部變量進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06