是一組爲了完成特定功能的SQL語句集,是利用SQL Server所提供的Transact-SQL語言所編寫的程序。經編譯後存儲在數據庫中。存儲過程是數據庫中的一個重要對象,用戶通過指定存儲過程的名字並給出參數(如果該存儲過程帶有參數)來執行它。存儲過程是由流控制和SQL語句書寫的過程,這個過程經編譯和優化後存儲在數據庫服務器中,存儲過程可由應用程序通過一個調用來執行,而且允許用戶聲明變量。同時,存儲過程可以接收和輸出參數、返回執行存儲過程的狀態值,也可以嵌套調用。
爲什麼要使用存儲過程:
ü 存儲過程是已經被認證的技術!
ü 存儲過程會使系統運行更快!
ü 存儲過程是可複用的組件!它是數據庫邏輯而不是應用程序。
ü 存儲過程將被保存!
存儲過程的優點:
存儲過程只在創造時進行編譯,以後每次執行存儲過程都不需再重新編譯,而一般SQL語句每執行一次就編譯一次,所以使用存儲過程可提高數據庫執行速度。
當對數據庫進行復雜操作時(如對多個表進行Update、Insert、Query、Delete時),可將此複雜操作用存儲過程封裝起來與數據庫提供的事務處理結合一起使用。
存儲過程可以重複使用,可減少數據庫開發人員的工作量。
安全性高,可設定只有某此用戶才具有對指定存儲過程的使用權。
存儲過程與函數的區別:
自定義函數有且只有一個返回值,就像普通的函數一樣,可以直接在表達式中嵌入調用。存儲過程可以沒有返回值,也可以有任意個輸出參數,必須單獨調用。
執行的本質都一樣。只是函數有如只能返回一個變量的限制。而存儲過程可以返回多個。而函數是可以嵌入在sql中使用的,可以在select中調用,而存儲過程不行。
函數限制比較多,比如不能用臨時表,只能用表變量。還有一些函數都不可用等等。而存儲過程的限制相對就比較少
函數限制比較多,比如不能用臨時表,只能用表變量。還有一些函數都不可用等等。而存儲過程的限制相對就比較少
一般來說,存儲過程實現的功能要複雜一點,而函數的實現的功能針對性比較強。對於存儲過程來說可以返回參數,而函數只能返回值或者表對象。
存儲過程一般是作爲一個獨立的部分來執行,而函數可以作爲查詢語句的一個部分來調用,由於函數可以返回一個表對象,因此它可以在查詢語句中位於FROM關鍵字的後面。
創建存儲過程:
[DEFINER = { user | CURRENT_USER }]
PROCEDURE sp_name ([proc_parameter[,...]])
[characteristic ...] routine_body
說明:
ü DEFINER:指明使用存儲過程的訪問權限。
ü sp_name: 存儲過程名稱。
ü proc_parameter: [ IN | OUT | INOUT ] param_name type
in:表示向存儲過程中傳入參數;存儲過程默認爲傳入參數,所以參數in可以省略;
out:表示向外傳出參數;
inout:表示定義的參數可傳入存儲過程,並可以被存儲過程修改後傳出存儲過程;
param_name:參數名;
type:參數的類型,可以爲mysql任何合法得數據類型。
如果有多個參數,參數之間可以用逗號進行分割。
ü Characteristic:
LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'string'
這個LANGUAGE SQL子句是沒有作用的。僅僅是爲了說明下面過程的主體使用SQL語言編寫。這條是系統默認的。
如果程序或線程總是對同樣的輸入參數產生同樣的結果,則被認爲它是“確定的”( DETERMINISTIC),否則就是“非確定”的。默認的就是NOT DETERMINISTIC。
CONTAINS SQL表示子程序不包含讀或寫數據的語句。
NO SQL表示子程序不包含SQL語句。
READS SQL DATA表示子程序包含讀數據的語句,但不包含寫數據的語句。
MODIFIES SQL DATA表示子程序包含寫數據的語句。如果這些特徵沒有明確給定,默認的是CONTAINS SQL。
SQL SECURITY特徵可以用來指定子程序該用創建子程序者的許可來執行,還是使用調用者的許可來執行。默認值是DEFINER。
COMMENT子句是一個MySQL的擴展,它可以被用來描述存儲程序。這個信息被SHOW CREATE PROCEDURE和 SHOW CREATE FUNCTION語句來顯示。存儲子程序不能使用LOAD DATA INFILE。
特徵子句也有默認值,如果省略了就相當於:LANGUAGE SQL NOT DETERMINISTIC SQL SECURITY DEFINER COMMENT ''
ü routine_body:包含合法的SQL過程語句。可以使用複合語句語法, 複合語句可以包含聲明,循環和其它控制結構語句。
修改存儲過程:
ALTER {PROCEDURE} sp_name [characteristic ...]
說明:
ü 這個語句可以被用來改變一個存儲程序的特徵。必須用ALTER ROUTINE權限纔可用此子程序。這個權限被自動授予子程序的創建者。
ü 在ALTER PROCEDURE語句中,可以指定超過一個的改變。
刪除存儲過程:
DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name
不能在一個存儲過程中刪除另一個存儲過程,只能調用另一個存儲過程
顯示存儲過程:
SHOW CREATE {PROCEDURE} sp_name
似於SHOW CREATE TABLE,它返回一個可用來重新創建已命名子程序的確切字符串。
顯示存儲過程特徵:
SHOW {PROCEDURE} STATUS [LIKE 'pattern']
它返回子程序的特徵,如數據庫,名字,類型,創建者及創建和修改日期。
調用存儲過程:
CALL sp_name([parameter[,...]])
調用一個先前用CREATE PROCEDURE創建的程序。
CALL語句可以用聲明爲OUT或的INOUT參數的參數給它的調用者傳回值。它也“返回”受影響的行數,客戶端程序可以在SQL級別通過調用ROW_COUNT()函數獲得這個數,從C中是調用the mysql_affected_rows() C API函數來獲得。
存儲過程實例(基本的創建、調用、刪除語法):
delimiter //
DROP PROCEDURE IF EXISTS test // 如果存在test則刪除
CREATE PROCEDURE test /* 存儲過程名 */
(IN inparms INT, OUT outparams varchar(32)) /* 輸入、輸出參數 */
BEGIN /* 語句塊頭 */
DECLARE var CHAR(10); /* 變量聲明 */
IF inparms = 1 THEN /* IF條件開始*/
SET var = 'hello'; /* 賦值 */
ELSE
SET var = 'world';
END IF; /* IF結束 */
INSERT INTO t1 VALUES (var); /* SQL語句 */
SELECT name FROM t1 LIMIT 1 INTO outparams;
END
//
delimiter ;
call test(1, @out); /*調用存儲過程*/
存儲過程的變量:
l 聲明變量:
DECLARE var_name[,...] type [DEFAULT value]
這個語句被用來聲明局部變量。要給變量提供一個默認值,需要包含一個DEFAULT子句。值可以被指定爲一個表達式,不需要爲一個常數。如果沒有DEFAULT子句,初始值爲NULL。局部變量的作用範圍在它被聲明的BEGIN ... END塊內。它可以被用在嵌套的塊中,除了那些用相同名字聲明變量的塊。
l 變量賦值,SET語句:
SET var_name = expr [, var_name = expr] ...
也可以用語句代替SET來爲用戶變量分配一個值。在這種情況下,分配符必須爲:=而不能用=,因爲在非SET語句中=被視爲一個比較操作符,如下所示:
mysql> SET @t1=0, @t2=0, @t3=0;
mysql> SELECT @t1:=0,@t2:=0,@t3:=0;
對於使用select語句爲變量賦值的情況,若返回結果爲空,即沒有記錄,此時變量的值爲上一次變量賦值時的值,如果沒有對變量賦過值,則爲NULL。
l 變量賦值,SELECT ... INTO語句
SELECT col_name[,...] INTO var_name[,...] table_expr
這個SELECT語法把選定的列直接存儲到變量。因此,只有單一的行可以被取回。
SELECT id,data INTO x,y FROM test.t1 LIMIT 1;
存儲過程的語句:
l BEGIN...END複合語句
[begin_label:] BEGIN
[statement_list]
END [end_label]
存儲子程序可以使用BEGIN ... END複合語句來包含多個語句。statement_list 代表一個或多個語句的列表。statement_list之內每個語句都必須用分號(;)來結尾。複合語句可以被標記。除非begin_label存在,否則end_label不能被給出,並且如果二者都存在,他們必須是同樣的
l 流程控制結構
IF語句
IF search_condition THEN statement_list
[ELSEIF search_condition THEN statement_list] ...
[ELSE statement_list]
END IF
statement_list可以包括一個或多個語句。
舉例:
DELIMITER //
CREATE PROCEDURE p1(IN parameter1 INT)
BEGIN
DECLARE variable1 INT;
SET variable1 = parameter1 + 1;
IF variable1 = 0 THEN
INSERT INTO t VALUES (17);
END IF;
IF parameter1 = 0 THEN
UPDATE t SET s1 = s1 + 1;
ELSE
UPDATE t SET s1 = s1 + 2;
END IF;
END; //
DELIMITER ;
CASE語句
CASE case_value
WHEN when_value THEN statement_list
[WHEN when_value THEN statement_list] ...
[ELSE statement_list]
END CASE
Or:
CASE
WHEN search_condition THEN statement_list
[WHEN search_condition THEN statement_list] ...
[ELSE statement_list]
END CASE
舉例:
CREATE PROCEDURE p2 (IN parameter1 INT)
BEGIN
DECLARE variable1 INT;
SET variable1 = parameter1 + 1;
CASE variable1
WHEN 0 THEN INSERT INTO t VALUES (17);
WHEN 1 THEN INSERT INTO t VALUES (18);
ELSE INSERT INTO t VALUES (19);
END CASE;
END; //
循環語句
WHILE … END WHILE
LOOP … END LOOP
REPEAT … END REPEAT
GOTO
前三種是標準的循環方式,至於GOTO就如C語言裏的GOTO一樣,儘量少用!
在循環中還穿插一些循環控制語句,如LEAVE(類似C語言的break)、ITERATE(類似C語言的continue)等。
LEAVE語句
LEAVE label 這個語句被用來退出任何被標註的流程控制構造。它和BEGIN ... END或循環一起被使用。
ITERATE語句
ITERATE label ITERATE只可以出現在LOOP, REPEAT, 和WHILE語句內。ITERATE意思爲:再次循環。
WHILE … END WHILE 舉例:
CREATE PROCEDURE p4 ()
BEGIN
DECLARE v INT;
SET v = 0;
WHILE v < 5 DO
INSERT INTO t VALUES (v);
SET v = v + 1;
END WHILE;
END; //
LOOP … END LOOP 舉例:
CREATE PROCEDURE p5 ()
BEGIN
DECLARE v INT;
SET v = 0;
loop_label: LOOP
INSERT INTO t VALUES (v);
SET v = v + 1;
IF v >= 5 THEN
LEAVE loop_label;
END IF;
END LOOP;
END; //
[begin_label:] LOOP
statement_list
END LOOP [end_label]
LOOP允許某特定語句或語句羣的重複執行,實現一個簡單的循環構造。在循環內的語句一直重複直到循環被退出,退出通常伴隨着一個LEAVE 語句。
REPEAT … END REPEAT 舉例:
CREATE PROCEDURE p6 ()
BEGIN
DECLARE v INT;
SET v = 0;
REPEAT
INSERT INTO t VALUES (v);
SET v = v + 1;
UNTIL v >= 5 END REPEAT;
END; //
迭代(ITERATE)語句
CREATE PROCEDURE p7 ()
BEGIN
DECLARE v INT;
SET v = 0;
loop_label: LOOP
IF v = 3 THEN
SET v = v + 1;
ITERATE loop_label;
END IF;
INSERT INTO t VALUES (v);
SET v = v + 1;
IF v >= 5 THEN
LEAVE loop_label;
END IF;
END LOOP;
END; //
存儲過程的註釋語法:
mysql存儲過程可使用兩種風格的註釋
雙模槓:--,該風格一般用於單行註釋
c風格:/* 註釋內容 */,一般用於多行註釋
存儲過程的條件和異常處理程序:
DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement
handler_type:
CONTINUE | EXIT | UNDO
condition_value:
SQLSTATE [VALUE] sqlstate_value | condition_name | SQLWARNING | NOT FOUND | SQLEXCEPTION
這個語句指定每個可以處理一個或多個條件的處理程序。如果產生一個或多個條件,指定的語句被執行。
對一個CONTINUE處理程序,當前子程序的執行在執行處理程序語句之後繼續。對於EXIT處理程序,當前BEGIN...END複合語句的執行被終止。UNDO 處理程序類型語句還不被支持。
SQLWARNING是對所有以01開頭的SQLSTATE代碼的速記。
NOT FOUND是對所有以02開頭的SQLSTATE代碼的速記。
SQLEXCEPTION是對所有沒有被SQLWARNING或NOT FOUND捕獲的SQLSTATE代碼的速記。
聲明自定義條件:
DECLARE condition_name CONDITION FOR condition_value
condition_value:
SQLSTATE [VALUE] sqlstate_value
舉例:
CREATE TABLE test.t (s1 int,primary key (s1));
delimiter //
CREATE PROCEDURE handlerdemo ()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
SET @x = 1;
INSERT INTO test.t VALUES (1);
SET @x = 2;
INSERT INTO test.t VALUES (1);
SET @x = 3;
END;//
delimiter ;
存儲過程綜合實例:(包含事務,參數,嵌套調用,遊標,循環等)
drop procedure if exists pro_rep_shadow_rs;
delimiter |
----------------------------------
-- rep_shadow_rs
-- 用來處理信息的增加,更新和刪除
-- 每次只更新上次以來沒有做過的數據
-- 根據不同的標誌位
-- 需要一個輸出的參數,
-- 如果返回爲0,則調用失敗,事務回滾
-- 如果返回爲1,調用成功,事務提交
--
-- 測試方法
-- call pro_rep_shadow_rs(@rtn);
-- select @rtn;
----------------------------------
create procedure pro_rep_shadow_rs(out rtn int)
begin
-- 聲明變量,所有的聲明必須在非聲明的語句前面
declare iLast_rep_sync_id int default -1;
declare iMax_rep_sync_id int default -1;
-- 如果出現異常,或自動處理並rollback,但不再通知調用方了
-- 如果希望應用獲得異常,需要將下面這一句,以及啓動事務和提交事務的語句全部去掉
declare exit handler for sqlexception rollback;
-- 查找上一次的
select eid into iLast_rep_sync_id from rep_de_proc_log where tbl='rep_shadow_rs';
-- 如果不存在,則增加一行
if iLast_rep_sync_id=-1 then
insert into rep_de_proc_log(rid,eid,tbl) values(0,0,'rep_shadow_rs');
set iLast_rep_sync_id = 0;
end if;
-- 下一個數字
set iLast_rep_sync_id=iLast_rep_sync_id+1;
-- 設置默認的返回值爲0:失敗
set rtn=0;
-- 啓動事務
start transaction;
-- 查找最大編號
select max(rep_sync_id) into iMax_rep_sync_id from rep_shadow_rs;
-- 有新數據
if iMax_rep_sync_id>=iLast_rep_sync_id then
-- 調用
call pro_rep_shadow_rs_do(iLast_rep_sync_id,iMax_rep_sync_id);
-- 更新日誌
update rep_de_proc_log set rid=iLast_rep_sync_id,eid=iMax_rep_sync_id where tbl='rep_shadow_rs';
end if;
-- 運行沒有異常,提交事務
commit;
-- 設置返回值爲1
set rtn=1;
end;
|
delimiter ;
drop procedure if exists pro_rep_shadow_rs_do;
delimiter |
---------------------------------
-- 處理指定編號範圍內的數據
-- 需要輸入2個參數
-- last_rep_sync_id 是編號的最小值
-- max_rep_sync_id 是編號的最大值
-- 無返回值
---------------------------------
create procedure pro_rep_shadow_rs_do(last_rep_sync_id int, max_rep_sync_id int)
begin
declare iRep_operationtype varchar(1);
declare iRep_status varchar(1);
declare iRep_Sync_id int;
declare iId int;
-- 這個用於處理遊標到達最後一行的情況
declare stop int default 0;
-- 聲明遊標
declare cur cursor for select id,Rep_operationtype,iRep_status,rep_sync_id from rep_shadow_rs where rep_sync_id between last_rep_sync_id and max_rep_sync_id;
-- 聲明遊標的異常處理,設置一個終止標記
declare CONTINUE HANDLER FOR SQLSTATE '02000' SET stop=1;
-- 打開遊標
open cur;
-- 讀取一行數據到變量
fetch cur into iId,iRep_operationtype,iRep_status,iRep_Sync_id;
-- 這個就是判斷是否遊標已經到達了最後
while stop <> 1 do
-- 各種判斷
if iRep_operationtype='I' then
insert into rs0811 (id,fnbm) select id,fnbm from rep_shadow_rs where rep_sync_id=iRep_sync_id;
elseif iRep_operationtype='U' then
begin
if iRep_status='A' then
insert into rs0811 (id,fnbm) select id,fnbm from rep_shadow_rs where rep_sync_id=iRep_sync_id;
elseif iRep_status='B' then
delete from rs0811 where id=iId;
end if;
end;
elseif iRep_operationtype='D' then
delete from rs0811 where id=iId;
end if;
-- 讀取下一行的數據
fetch cur into iId,iRep_operationtype,iRep_status,iRep_Sync_id;
end while; -- 循環結束
close cur; -- 關閉遊標
end;
|
存儲過程操作符:
l 算術運算符
+ 加 SET var1=2+2; 4
- 減 SET var2=3-2; 1
* 乘 SET var3=3*2; 6
/ 除 SET var4=10/3; 3.3333
DIV 整除 SET var5=10 DIV 3; 3
% 取模 SET var6=10%3 ; 1
l 比較運算符
> 大於 1>2 False
< 小於 2<1 False
<= 小於等於 2<=2 True
>= 大於等於 3>=2 True
BETWEEN 在兩值之間 5 BETWEEN 1 AND 10 True
NOT BETWEEN 不在兩值之間 5 NOT BETWEEN 1 AND 10 False
IN 在集合中 5 IN (1,2,3,4) False
NOT IN 不在集合中 5 NOT IN (1,2,3,4) True
= 等於 2=3 False
<>, != 不等於 2<>3 False
<=> 嚴格比較兩個NULL值是否相等 NULL<=>NULL True
LIKE 簡單模式匹配 "Guy Harrison" LIKE "Guy%" True
REGEXP 正則式匹配 "Guy Harrison" REGEXP "[Gg]reg" False
IS NULL 爲空 0 IS NULL False
IS NOT NULL 不爲空 0 IS NOT NULL True
l 邏輯運算符
與(AND)
AND | TRUE | FALSE | NULL |
TRUE | TRUE | FALSE | NULL |
FALSE | FALSE | FALSE | NULL |
NULL | NULL | NULL | NULL |
或(OR)
OR | TRUE | FALSE | NULL |
TRUE | TRUE | TRUE | TRUE |
FALSE | TRUE | FALSE | NULL |
NULL | TRUE | NULL | NULL |
異或(XOR)
XOR | TRUE | FALSE | NULL |
TRUE | FALSE | TRUE | NULL |
FALSE | TRUE | FALSE | NULL |
NULL | NULL | NULL | NULL |
l 位運算符
| 位或
& 位與
<< 左移位
>> 右移位
~ 位非(單目運算,按位取反)
存儲過程基本函數:
mysq存儲過程中常用的函數,字符串類型操作,數學類,日期時間類
l 字符串類
CHARSET(str) //返回字串字符集
CONCAT (string2 [,... ]) //連接字串
INSTR (string ,substring )//返回substring首次在string中出現的位置,不存在返回0
LCASE (string2 ) //轉換成小寫
LEFT (string2 ,length ) //從string2中的左邊起取length個字符
LENGTH (string ) //string長度
LOAD_FILE (file_name ) //從文件讀取內容
LOCATE (substring , string [,start_position ] ) 同INSTR,但可指定開始位置
LPAD (string2 ,length ,pad ) //重複用pad加在string開頭,直到字串長度爲length
LTRIM (string2 ) //去除前端空格
REPEAT (string2 ,count ) //重複count次
REPLACE (str ,search_str ,replace_str ) //在str中用replace_str替換search_str
RPAD (string2 ,length ,pad) //在str後用pad補充,直到長度爲length
RTRIM (string2 ) //去除後端空格
STRCMP (string1 ,string2 ) //逐字符比較兩字串大小,
SUBSTRING (str , position [,length ]) //從str的position開始,取length個字符,
注:mysql中處理字符串時,默認第一個字符下標爲1,即參數position必須大於等於1
mysql> select substring(’abcd’,0,2);
+———————–+
| substring(’abcd’,0,2) |
+———————–+
| |
+———————–+
1 row in set (0.00 sec)
mysql> select substring(’abcd’,1,2);
+———————–+
| substring(’abcd’,1,2) |
+———————–+
| ab |
+———————–+
1 row in set (0.02 sec)
TRIM([[BOTH|LEADING|TRAILING] [padding] FROM]string2) //去除指定位置的指定字符
UCASE (string2 ) //轉換成大寫
RIGHT(string2,length) //取string2最後length個字符
SPACE(count) //生成count個空格
l 數學類
ABS (number2 ) //絕對值
BIN (decimal_number ) //十進制轉二進制
CEILING (number2 ) //向上取整
CONV(number2,from_base,to_base) //進制轉換
FLOOR (number2 ) //向下取整
FORMAT (number,decimal_places ) //保留小數位數
HEX (DecimalNumber ) //轉十六進制
注:HEX()中可傳入字符串,則返回其ASC-11碼,如HEX(’DEF’)返回4142143
也可以傳入十進制整數,返回其十六進制編碼,如HEX(25)返回19
LEAST (number , number2 [,..]) //求最小值
MOD (numerator ,denominator ) //求餘
POWER (number ,power ) //求指數
RAND([seed]) //隨機數
ROUND (number [,decimals ]) //四捨五入,decimals爲小數位數]
注:返回類型並非均爲整數,如:
(1)默認變爲整形值
mysql> select round(1.23);
+————-+
| round(1.23) |
+————-+
| 1 |
+————-+
1 row in set (0.00 sec)
mysql> select round(1.56);
+————-+
| round(1.56) |
+————-+
| 2 |
+————-+
1 row in set (0.00 sec)
(2)可以設定小數位數,返回浮點型數據
mysql> select round(1.567,2);
+—————-+
| round(1.567,2) |
+—————-+
| 1.57 |
+—————-+
1 row in set (0.00 sec)
SIGN (number2 ) //返回符號,正負或0
SQRT(number2) //開平方
l 日期時間類
ADDTIME (date2 ,time_interval ) //將time_interval加到date2
CONVERT_TZ (datetime2 ,fromTZ ,toTZ ) //轉換時區
CURRENT_DATE ( ) //當前日期
CURRENT_TIME ( ) //當前時間
CURRENT_TIMESTAMP ( ) //當前時間戳
DATE (datetime ) //返回datetime的日期部分
DATE_ADD (date2 , INTERVAL d_value d_type ) //在date2中加上日期或時間
DATE_FORMAT (datetime ,FormatCodes ) //使用formatcodes格式顯示datetime
DATE_SUB (date2 , INTERVAL d_value d_type ) //在date2上減去一個時間
DATEDIFF (date1 ,date2 ) //兩個日期差
DAY (date ) //返回日期的天
DAYNAME (date ) //英文星期
DAYOFWEEK (date ) //星期(1-7) ,1爲星期天
DAYOFYEAR (date ) //一年中的第幾天
EXTRACT (interval_name FROM date ) //從date中提取日期的指定部分
MAKEDATE (year ,day ) //給出年及年中的第幾天,生成日期串
MAKETIME (hour ,minute ,second ) //生成時間串
MONTHNAME (date ) //英文月份名
NOW ( ) //當前時間
SEC_TO_TIME (seconds ) //秒數轉成時間
STR_TO_DATE (string ,format ) //字串轉成時間,以format格式顯示
TIMEDIFF (datetime1 ,datetime2 ) //兩個時間差
TIME_TO_SEC (time ) //時間轉秒數]
WEEK (date_time [,start_of_week ]) //第幾周
YEAR (datetime ) //年份
DAYOFMONTH(datetime) //月的第幾天
HOUR(datetime) //小時
LAST_DAY(date) //date的月的最後日期
MICROSECOND(datetime) //微秒
MONTH(datetime) //月
MINUTE(datetime) //分
附:可用在INTERVAL中的類型
DAY ,DAY_HOUR ,DAY_MINUTE ,DAY_SECOND ,HOUR ,HOUR_MINUTE ,HOUR_SECOND ,MINUTE ,MINUTE_SECOND,MONTH ,SECOND ,YEAR