深刻MySQL用戶自定義變量

1、到底MySQL的變量分哪幾類?

MySQL變量一共分爲兩大類:用戶自定義變量和系統變量。以下:php

  • 用戶自定義變量
    • 局部變量
    • 會話變量
  • 系統變量
    • 會話變量
    • 全局變量

本文涉及的內容爲用戶自定義會話變量,若對其餘分類無感,請點擊這裏html

PS:用戶定義的會話變量和系統定義的會話變量有什麼區別?mysql

局部變量

局部變量通常用於SQL的語句塊中,好比存儲過程當中的begin和end語句塊。其做用域僅限於該語句塊內。生命週期也僅限於該存儲過程的調用期間。sql

1
2
3
4
5
6
7
8
9
10
11
drop procedure if exists add ;
create procedure add
(
     in a int ,
     in b int
)
begin
     declare c int default 0;
     set c = a + b;
     select c as c;
end ;

上述存儲過程當中定義的變量c就是局部變量。服務器

會話變量

會話變量即爲服務器爲每一個客戶端鏈接維護的變量。在客戶端鏈接時,使用相應全局變量的當前值對客戶端的回話變量進行初始化。設置會話變量不須要特殊權限,但客戶端只能更改本身的會話變量。其做用域與生命週期均限於當前客戶端鏈接。session

會話變量的賦值:函數

1
2
3
set session var_name = value;
set @@session.var_name = value;
set var_name = value;

會話變量的查詢:單元測試

1
2
3
select @@var_name;
select @@session.var_name;
show session variables like "%var%" ;

全局變量

全局變量影響服務器總體操做。當服務器啓動時,它將全部全局變量初始化爲默認值。這些默認值能夠在選項文件中或在命令行中指定的選項進行更改。要想更改全局變量,必須具備SUPER權限。全局變量做用於server的整個生命週期,可是不能跨重啓。即重啓後全部設置的全局變量均失效。要想讓全局變量重啓後繼續生效,須要更改相應的配置文件。學習

全局變量的設置:測試

1
2
set global var_name = value; //注意:此處的 global 不能省略。根據手冊, set 命令設置變量時若不指定 GLOBAL 、SESSION或者 LOCAL ,默認使用SESSION
set @@ global .var_name = value; //同上

全局變量的查詢:

1
2
select @@ global .var_name;
show global variables like "%var%" ;

2、MySQL用戶自定義變量詳解

你能夠利用SQL語句將值存儲在用戶自定義變量中,而後再利用另外一條SQL語句來查詢用戶自定義變量。這樣以來,能夠再不一樣的SQL間傳遞值。

用戶自定義變量的聲明方法形如:@var_name,其中變量名稱由字母、數字、「.」、「_」和「$」組成。固然,在以字符串或者標識符引用時也能夠包含其餘字符(例如:@'my-var',@"my-var",或者@`my-var`)。

用戶自定義變量是會話級別的變量。其變量的做用域僅限於聲明其的客戶端連接。當這個客戶端斷開時,其全部的會話變量將會被釋放。

用戶自定義變量是不區分大小寫的。

使用SET語句來聲明用戶自定義變量:

1
SET @var_name = expr[, @var_name = expr] ...

在使用SET設置變量時,可使用「=」或者「:=」操做符進行賦值。

固然,除了SET語句還有其餘賦值的方式。好比下面這個例子,可是賦值操做符只能使用「:=」。由於「=」操做符將會被認爲是比較操做符。

mysql> SET @t1=1, @t2=2, @t3:=4;
mysql> SELECT @t1, @t2, @t3, @t4 := @t1+@t2+@t3;
+------+------+------+--------------------+
| @t1  | @t2  | @t3  | @t4 := @t1+@t2+@t3 |
+------+------+------+--------------------+
|    1 |    2 |    4 |                  7 |
+------+------+------+--------------------+

用戶變量的類型僅限於:整形、浮點型、二進制與非二進制串和NULL。在賦值浮點數時,系統不會保留精度。其餘類型的值將會被轉成相應的上述類型。好比:一個包含時間或者空間數據類型(temporal or spatial data type)的值將會轉換成一個二進制串。

若是用戶自定義變量的值以結果集形式返回,系統會將其轉換成字符串形式。

若是查詢一個沒有初始化的變量,將會以字符串類型返回NULL。

不要在同一個非SET語句中同時賦值並使用同一個用戶自定義變量

用戶自定義變量能夠用於不少上下文中。可是目前並不包括那些顯式使用常量的表達式中,好比SELECT中的LIMIT子句,或者LOAD DATA中的IGNORE N LINES的字句中。

一般來講,除了在SET語句中,不要再同一個SQL語句中同時賦值並使用同一個用戶自定義變量。舉個變量自增的例子,下面的是沒問題的:

1
SET @a = @a + 1;

對於其餘語句,好比SELECT,也許會獲得指望的效果,但這真心不靠譜。好比下面的語句,也許你天然地會認爲MySQL會先執行@a的值,而後再進行賦值操做:

1
SELECT @a, @a:=@a+1, ...;

然而,用戶自定義變量表達式的計算順序尚未定義呢。

除此以外,還有另外一個問題。變量的默認返回類型由語句開始時的類型決定的,正以下面的例子:

1
2
mysql> SET @a= 'test' ;
mysql> SELECT @a,(@a:=20) FROM tbl_name;

上述的SELECT語句中,MySQL會報告給客戶端第一列的字段類型爲字符串,同時將全部對@a變量的使用均轉換爲字符串處理,儘管在SELECT語句中將@a變量設置爲數字類型。在SELECT語句執行後,@a變量纔會在下一個語句中識別爲數字類型。

爲了不上述問題的發生,要麼不在同一個語句中同時賦值並使用變量,要麼在使用以前,將變量設置爲0,0.0,或者'',以肯定它的數據類型。

變量的值是在SQL發送到客戶端後才計算的

在SELECT語句中,在每個select表達式被髮送給客戶端後,纔會進行計算。這就意味着,在形如HAVING,GROUP BY和ORDER BY只句中有使用在當前select表達式定義的變量的狀況下,該語句將不會獲得如期的效果。

1
mysql> SELECT (@aa:=id) AS a, (@aa+3) AS b FROM tbl_name HAVING b=5;

上述在HAVING只句中使用了在當前的select列表中定義的別名b,其使用了變量@aa。這條語句並不會獲得如期的效果:@aa變量爲上一次SQL語句執行的結果集中的ID值,並不是當前的。

3、MySQL用戶自定義變量的實際應用舉例

項目

超級話題積分系統

術語

積分行爲:如轉發、評論超級話題下的帖子、簽到某超級話題或者帖子被其餘人回覆等行爲。

積分行爲次數:產生積分行爲的累計次數。

業務場景

用戶在某超級話題下,第N次產生累計積分的行爲,如轉發微博,會增長該用戶在該超級話題下的積分總數。具體的積分規則見長文章

問題

曾有用戶反饋說超級話題積分有漏記的狀況:爲何我評論了卻沒有加分;爲何轉發了超級話題帖子沒有加分等等。隨後,咱們當即經過查詢後臺的積分記錄發現,會看到轉發行爲在第5次時,積分的增長卻爲0。這顯然是不正常的。

首先,排除了根據積分行爲的次數來計算積分值的問題。好比第5次轉發微博應增長6分。這塊的規則,利用二分法寫死在程序裏面,也作過單元測試,不會有問題。那麼,問題就鎖定在這個積分行爲的次數。

首先來看看積分次數的獲取:

1
2
3
4
public static function find( $uid , $aid , $status ) { 
     $sql = 'SELECT * FROM ' .self::table( $aid ). ' WHERE uid = ? AND aid = ? AND status = ?' ;
     return Comm_Db::d(Comm_Db::DB_BASIC)->fetchRow( $sql , array ( $uid , $aid , $status ));
}  

而後,利用上述find()方法來取得該用戶在某超級話題下的某積分行爲的累計次數。這是有問題的,在於讀於從庫,但並不保證從庫的值是最新的,因此致使當前獲取的積分行爲次數並不必定是正確的(小於等於實際的值)。

隨後,程序會根據當前的次數計算積分值,並分別更新積分值和該行爲的積分行爲次數值。

因此,此次利用MySQL的用戶自定義會話變量的方式,來解決上述問題。

1
2
3
4
5
6
7
8
public static function incCounter( $uid , $aid , $status ) {
     $db = Comm_Db::d(Comm_Db::DB_BASIC);
     $sql = "UPDATE " . self::table( $aid ) . " SET `ctn_counter`=@ctn_counter:=`ctn_counter`+1 WHERE `uid` = ? AND `aid` = ? AND `status` = ?" ;
     $db ->execute( $sql , array ( $uid , $aid , $status ));
     $sql = "SELECT @ctn_counter" ;
     $rs = $db ->fetchOne( $sql , null, true);
     return $rs ;
}

改進後,如上述函數,程序將先進行調用incCounter()函數,將當前的積分行爲次數自增,並將值存入當前變量中。隨後,當即將其讀取並返回給PHP進行積分處理。這樣一來,就保證了積分行爲次數的正確性。

4、關於MySQL用戶自定義變量的結束語

在此次的「填坑」過程當中,使用了MySQL變量解決了MySQL主從服務同步延遲的問題。這篇文章也算是對於MySQL用戶自定義變量深刻學習的記載。

除此以外,仍有個問題,用戶自定義的會話變量是存在進程內存中的。可是,是存在客戶端進程中仍是服務端進程中的呢?


參考文章:

文章來源:胡旭博客 => 深刻MySQL用戶自定義變量:使用詳解及其使用場景案例

相關文章
相關標籤/搜索