理解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層級,能夠直接利用本界面php

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

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

· 即便客戶端正在使用舊版本的客戶端庫,您也可使用本界面。惟一的要求是,您可以鏈接到一個支持預製語句SQL語法的服務器上。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關鍵詞和標識符等。oracle

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

預製語句的範圍是客戶端會話。在此會話內,語句被建立。其它客戶端看不到它。

在預備了一個語句後,您可以使用一個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完勝……

寫在最後

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

相關文章
相關標籤/搜索