理解Mysql prepare預處理語句


MySQL 5.1對服務器一方的預制語句提供支持。如果您使用合適的客戶端編程界面,則這種支持可以發揮在MySQL 4.1中實施的高效客戶端/服務器二進制協議的優勢。候選界面包括MySQL C API客戶端庫(用於C程序)、MySQL Connector/J(用於Java程序)和MySQL Connector/NET。例如,C API可以提供一套能組成預制語句API的函數調用。其它語言界面可以對使用了二進制協議(通過在C客戶端庫中鏈接)的預制語句提供支持。對預制語句,還有一個SQL界面可以利用。與在整個預制語句API中使用二進制協議相比,本界面效率沒有那么高,但是它不要求編程,因為在SQL層級,可以直接利用本界面

· 當您無法利用編程界面時,您可以使用本界面。

· 有些程序允許您發送SQL語句到將被執行的服務器中,比如mysql客戶端程序。您可以從這些程序中使用本界面。

· 即使客戶端正在使用舊版本的客戶端庫,您也可以使用本界面。唯一的要求是,您能夠連接到一個支持預制語句SQL語法的服務器上。

預制語句的SQL語法在以下情況下使用:

· 在編代碼前,您想要測試預制語句在您的應用程序中運行得如何。或者也許一個應用程序在執行預制語句時有問題,您想要確定問題是什么。

· 您想要創建一個測試案例,該案例描述了您使用預制語句時出現的問題,以便您編制程序錯誤報告。

· 您需要使用預制語句,但是您無法使用支持預制語句的編程API。

預制語句的SQL語法基於三個SQL語句:

1
2
3
4
5
PREPARE stmt_name FROM preparable_stmt;
  
EXECUTE stmt_name [USING @var_name [, @var_name] ...];
  
{ DEALLOCATE | DROP } PREPARE stmt_name;

PREPARE語句用於預備一個語句,並賦予它名稱stmt_name,借此在以后引用該語句。語句名稱對案例不敏感。preparable_stmt可以是一個文字字符串,也可以是一個包含了語句文本的用戶變量。該文本必須展現一個單一的SQL語句,而不是多個語句。使用本語句,‘?'字符可以被用於制作參數,以指示當您執行查詢時,數據值在哪里與查詢結合在一起。‘?'字符不應加引號,即使您想要把它們與字符串值結合在一起,也不要加引號。參數制作符只能被用於數據值應該出現的地方,不用於SQL關鍵詞和標識符等。

如果帶有此名稱的預制語句已經存在,則在新的語言被預備以前,它會被隱含地解除分配。這意味着,如果新語句包含一個錯誤並且不能被預備,則會返回一個錯誤,並且不存在帶有給定名稱語句。

預制語句的范圍是客戶端會話。在此會話內,語句被創建。其它客戶端看不到它。

在預備了一個語句后,您可使用一個EXECUTE語句(該語句引用了預制語句名稱)來執行它。如果預制語句包含任何參數制造符,則您必須提供一個列舉了用戶變量(其中包含要與參數結合的值)的USING子句。參數值只能有用戶變量提供,USING子句必須准確地指明用戶變量。用戶變量的數目與語句中的參數制造符的數量一樣多。

您可以多次執行一個給定的預制語句,在每次執行前,把不同的變量傳遞給它,或把變量設置為不同的值。

要對一個預制語句解除分配,需使用DEALLOCATE PREPARE語句。嘗試在解除分配后執行一個預制語句會導致錯誤。

如果您終止了一個客戶端會話,同時沒有對以前已預制的語句解除分配,則服務器會自動解除分配。

以下SQL語句可以被用在預制語句中:CREATE TABLE, DELETE, DO, INSERT, REPLACE, SELECT, SET, UPDATE和多數的SHOW語句。目前不支持其它語句。

以下例子顯示了預備一個語句的兩種方法。該語句用於在給定了兩個邊的長度時,計算三角形的斜邊。

第一個例子顯示如何通過使用文字字符串來創建一個預制語句,以提供語句的文本:

?
1
2
3
4
5
6
7
8
9
10
mysql> PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse' ;
mysql> SET @a = 3;
mysql> SET @b = 4;
mysql> EXECUTE stmt1 USING @a, @b;
+ ------------+
| hypotenuse |
+ ------------+
|     5 |
+ ------------+
mysql> DEALLOCATE PREPARE stmt1;

第二個例子是相似的,不同的是提供了語句的文本,作為一個用戶變量:

?
1
2
3
4
5
6
7
8
9
10
11
mysql> SET @s = 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse' ;
mysql> PREPARE stmt2 FROM @s;
mysql> SET @a = 6;
mysql> SET @b = 8;
mysql> EXECUTE stmt2 USING @a, @b;
+ ------------+
| hypotenuse |
+ ------------+
|     10 |
+ ------------+
mysql> DEALLOCATE PREPARE stmt2;

對於已預備的語句,您可以使用位置保持符。以下語句將從tb1表中返回一行:

?
1
2
3
4
5
mysql> SET @a=1;
 
mysql> PREPARE STMT FROM "SELECT * FROM tbl LIMIT ?" ;
 
mysql> EXECUTE STMT USING @a;

以下語句將從tb1表中返回第二到第六行:

?
1
2
3
4
5
mysql> SET @skip=1; SET @numrows=5;
 
mysql> PREPARE STMT FROM "SELECT * FROM tbl LIMIT ?, ?" ;
 
mysql> EXECUTE STMT USING @skip, @numrows;

預制語句的SQL語法不能被用於帶嵌套的風格中。也就是說,被傳遞給PREPARE的語句本身不能是一個PREPARE, EXECUTE或DEALLOCATE PREPARE語句。

預制語句的SQL語法與使用預制語句API調用不同。例如,您不能使用mysql_stmt_prepare() C API函數來預備一個PREPARE, EXECUTE或DEALLOCATE PREPARE語句。

預制語句的SQL語法可以在已存儲的過程中使用,但是不能在已存儲的函數或觸發程序中使用。

以上就是本文的全部內容,希望對大家的學習有所幫助。

 

 

MySQL中Stmt 預處理提高效率問題的小研究

 

在oracle數據庫中,有一個變量綁定的用法,很多人都比較熟悉,可以調高數據庫效率,應對高並發等,好吧,這其中並不包括我,當同事問我MySQL中有沒有類似的寫法時,我是很茫然的,於是就上網查,找到了如下一種寫法

代碼如下:


DELIMITER $$ 
set @stmt = 'select userid,username from myuser where userid between ? and ?'; 
prepare s1 from @stmt; 
set @s1 = 2; 
set @s2 = 100; 
execute s1 using @s1,@s2; 
deallocate prepare s1; 
$$ 
DELIMITER ; 


用這種形式寫的查詢,可以隨意替換參數,給出代碼的人稱之為預處理,我想這個應該就是MySQL中的變量綁定吧……但是,在查資料的過程中我卻聽到了兩種聲音,一種是,MySQL中有類似Oracle變量綁定的寫法,但沒有其實際作用,也就是只能方便編寫,不能提高效率,這種說法在幾個09年的帖子中看到: 
http://www.itpub.net/thread-1210292-1-1.html 
http://cuda.itpub.net/redirect.php?fid=73&tid=1210572&goto=nextnewset 
另一種說法是MySQL中的變量綁定是能確實提高效率的,這個是希望有的,那到底有木有,還是自己去試驗下吧。 
試驗是在本機進行的,數據量比較小,具體數字並不具有實際意義,但是,能用來說明一些問題,數據庫版本是mysql-5.1.57-win32免安裝版。 
  本着對數據庫不是很熟悉的態度^_^,試驗過程中走了不少彎路,此文以結論為主,就不列出實驗的設計過程,文筆不好,文章寫得有點枯燥,寫出來是希望有人來拍磚,因為我得出的結論是:預處理在有沒有cache的情況下的執行效率都不及直接執行…… 我對自己的實驗結果不願接受。。如果說預處理只為了規范下Query,使cache命中率提高的話個人覺得大材小用了,希望有比較了解的人能指出事實究竟是什么樣子的——NewSilen 
實驗准備 
  第一個文件NormalQuery.sql 

復制代碼代碼如下:

Set profiling=1; 
Select * From MyTable where DictID = 100601000004; 
Select DictID from MyTable limit 1,100; 
Select DictID from MyTable limit 2,100; 
/*從limit 1,100 到limit 100,100 此處省略重復代碼*/ 
...... 
Select DictID from MyTable limit 100,100; 
SELECT query_id,seq,STATE,10000*DURATION FROM information_schema.profiling INTO OUTFILE 'd:/NormalResults.csv' FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'; 


第二個sql文件 StmtQuery.sql 

復制代碼代碼如下:

Set profiling=1; 
Select * From MyTable where DictID = 100601000004; 
set @stmt = 'Select DictID from MyTable limit ?,?'; 
prepare s1 from @stmt; 
set @s = 100; 
set @s1 = 101; 
set @s2 = 102; 
...... 
set @s100 =200; 
execute s1 using @s1,@s; 
execute s1 using @s2,@s; 
...... 
execute s1 using @s100,@s; 
SELECT query_id,seq,STATE,10000*DURATION FROM information_schema.profiling INTO OUTFILE 'd:/StmtResults.csv' FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'; 


做幾點小說明: 
1. Set profiling=1; 執行此語句之后,可以從information_schema.profiling這張表中讀出語句執行的詳細信息,其實包含不少內容,包括我需要的時間信息,這是張臨時表,每新開一個會話都要重新設置profiling屬性才能從這張表中讀取數據 
2. Select * From MyTable where DictID = 100601000004; 
  這行代碼貌似和我們的實驗沒什么關系,本來我也是這么認為的,之所以加這句,是我在之前的摸索中發現,執行過程中有個步驟是open table,如果是第一次打開某張表,那時間是相當長的,所以在執行后面的語句前,我先執行了這行代碼打開試驗用的表 
3. MySQL默認在information_schema.profiling表中保存的查詢歷史是15條,可以修改profiling_history_size屬性來進行調整,我希望他大一些讓我能一次取出足夠的數據,不過最大值只有100,盡管我調整為150,最后能夠查到的也只有100條,不過也夠了 
4. SQL代碼我沒有全列出來,因為查詢語句差不多,上面代碼中用省略號表示了,最后的結果是兩個csv文件,個人習慣,你也可以把結果存到數據庫進行分析 
  實驗步驟 
重啟數據庫,執行文件NormalQuery.sql,執行文件StmtQuery.sql,得到兩個結果文件 
再重啟數據庫,執行StmtQuery.sql,執行文件NormalQuery.sql,得到另外兩個結果文件 
  實驗結果 
詳細結果在最后提供了附件下載,有興趣的朋友可以看下 
  結果分析 
每一個SQL文件中執行了一百個查詢語句,沒有重復的查詢語句,不存在查詢cache,統計執行SQL的平均時間得出如下結果 

從結果中可以看出,無論是先執行還是后執行,NormalQuery中的語句都比使用預處理語句的要快一些=.=!

那再來看看每一句查詢具體的情況,Normal和Stmt的query各執行了兩百次,每一步的詳細信息如下:

從這里面可以看出,第一個,normalquery比stmtquery少一個步驟,第二個,雖然stmt在不少步驟上是優於normal的,但在executing一步上輸掉太多,最后結果上也是落敗

 最后,再給出一個查詢緩存的實驗結果,具體步驟就不列了

在查詢緩存的時候,Normal完勝……

寫在最后

大概情況就是這樣,我回憶了一下,網上說預處理可以提高效率的,基本都是用編程的方式去執行查詢,不知道這個有沒有關系,基礎有限,希望園子里的大牛能看到,幫忙解惑
實驗結果附件 


免責聲明!

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



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