34 SQL Prepare
1. 定義
解釋:SQL
預處理
(Prepare),是一種特殊的 SQL 處理方式;預處理不會直接執行 SQL 語句,而是先將 SQL 語句編譯,生成執行計劃,然後通過 Execute 命令攜帶 SQL 引數執行 SQL 語句。
2. 前言
本小節,我們將一起學習 SQL Prepare
。
在生產環境中,我們會多次執行一條 SQL 語句,如果每次都處理該 SQL 語句,生成執行計劃,必然會浪費一定的時間。SQL 預處理是一種特殊的 SQL 處理方式,它會預先根據 SQL 語句模板來生成對應的執行計劃,而後只需攜帶 SQL 引數便能直接執行,提升了 SQL 執行的效能,是一種典型的空間換時間的演算法優化。
本小節測試資料如下,請先在資料庫中執行:
DROP TABLE IF EXISTS imooc_user;
CREATE TABLE imooc_user
(
id int PRIMARY KEY,
username varchar(20),
age int
);
INSERT INTO imooc_user(id,username,age)
VALUES (1,'peter',18),(2,'pedro',24),(3,'jerry',22),(4,'mike',18),(5,'tom',20);
3. 語法
不同資料庫對於 Prepare 的支援差異較大,本小節我們將分別介紹 MySQL 和 PostgreSQL 的預處理語法及案例。
3.1 MySQL
MySQL 預處理是一組 SQL 操作的集合,它沒有固定的語法格式,但多數情況下會按照如下 3 個步驟使用。
- 使用
PREPARE
指令預定義 SQL 語句模板; - 使用
SET
指令定義 SQL 引數; - 使用
EXECUTE
指令攜帶引數執行 SQL 模板。
我們以通過id查詢使用者
為例來詳細說明 Prepare 的使用。
1、按照上述的步驟,我們應先使用 Prepare 來預定義通過“id查詢使用者”的 SQL 模板,如下:
PREPARE finduserbyidstm FROM 'SELECT * FROM imooc_user WHERE id = ?';
Prepare 指令後面便是 SQL 語句模板的名稱,此處我們將模板的名稱定義為finduserbyidstm
注意: 既然是模板,那麼必然會有引數的佔位符,如 MySQL 的佔位符是
?
,而 PostgreSQL 的佔位符則不同,它會根據引數的序列來依次定義,如第一個引數的佔位符是$1
,第二個引數的佔位符則是$2
。
2、定義好預處理 SQL 模板後,我們還需定義 SQL 引數,如下:
SET @id = 1;
SQL 定義變數,需以@
來開頭,如 @id,表示變數名為 id,變數值為 1。
3、通過 EXECUTE 攜帶引數來真正地執行 SQL:
EXECUTE finduserbyidstm USING @id;
EXECUTE 後面是已經定義好的模板名稱 finduserbyidstm,且使用 USING 指令來指定使用到的變數引數。
執行成功後,結果如下:
+----+----------+-----+
| id | username | age |
+----+----------+-----+
| 1 | peter | 18 |
+----+----------+-----+
3.2 PostgreSQL
PostgreSQL 預處理也是一組 SQL 操作的集合,不過它只需要兩個步驟即可完成。
- 使用
PREPARE
指令預定義 SQL 語句模板; - 使用
EXECUTE
指令攜帶引數執行 SQL 模板。
我們還是以通過id查詢使用者
為例來詳細說明 Prepare 的使用。
1、 使用 PREPARE 來預定義模板:
PREPARE finduserbyidstm(int) AS SELECT * FROM imooc_user WHERE id = $1;
PostgreSQL 的模板定義更為嚴格,不僅需要指定模板名稱,還需指定引數型別,如 finduserbyidstm 模板共有一個引數,且型別為 int。模板名稱與語句之間不再使用 FROM 連線,而是使用AS
,且後面直接接上 SQL 語句,不需要 ‘’ 來包裹成字串。佔位符為$1
,若有第二個佔位符,則應該為$2
。
2、 使用 EXECUTE 執行:
EXECUTE finduserbyidstm(1);
PostgreSQL 執行較為簡單,不要定義變數再使用,直接在模板名稱中指定引數值即可,即 1。
執行後的結果如下:
id | username | age
----+----------+-----
1 | peter | 18
4. 實踐
預處理的語法和步驟比較複雜,接下來以一個例項來鞏固一下。
4.1 例1 預處理插入使用者
請書寫 SQL 語句,使用預處理的方式插入一個名為lucy
的使用者,該使用者年齡為17
。
分析:
按照上面流程和語法,依次完成即可。
語句:
整理可得語句如下:
PREPARE insertuserstm FROM 'INSERT INTO imooc_user(id,username,age) VALUES(?,?,?)';
SET @id = 6,@username='lucy',@age=17;
EXECUTE insertuserstm USING @id,@username,@age;
結果如下:
+----+----------+-----+
| id | username | age |
+----+----------+-----+
| 6 | lucy | 17 |
+----+----------+-----+
如果使用 PostgreSQL,則語句如下:
PREPARE insertuserstm(int,varchar,int) AS INSERT INTO imooc_user(id,username,age) VALUES($1,$2,$3);
EXECUTE insertuserstm(6,'lucy',17);
5. 小結
- Prepare 的使用其實十分廣泛,絕大多數 ORM 框架都有 API 支援。
- Prepare 既可以提升 SQL 執行
效能
,還能防止 SQL 注入引發的安全問題。 - Prepare 雖然在每個資料庫中的語法差異很大,但是一般情況下我們都不會手寫 SQL,而是使用 ORM 框架來做。