MYSQL存儲過程 註釋詳解

0.環境說明:

軟件 版本
mysql 8.0
navicat

1.使用說明

存儲過程時數據庫的一個重要的對象,可以封裝SQL語句集,可以用來完成一些較復雜的業務邏輯,並且可以入參出參(類似於java中的方法的書寫)。

​ 創建時會預先編譯後保存,用戶後續的調用都不需要再次編譯。

// 把editUser類比成一個存儲過程
public void editUser(User user,String username){
    String a = "nihao";
    user.setUsername(username);
}
main(){
    User user = new User();
 editUser(user,"張三");
    user.getUseranme();   //java基礎
}

大傢可能會思考,用sql處理業務邏輯還要重新學,我用java來處理邏輯(比如循環判斷、循環查詢等)不行嗎?那麼,為什麼還要用存儲過程處理業務邏輯呢?

優點:

在生產環境下,可以通過直接修改存儲過程的方式修改業務邏輯(或bug),而不用重啟服務器。
 執行速度快,存儲過程經過編譯之後會比單獨一條一條執行要快。
 減少網絡傳輸流量。
 方便優化。

缺點:

過程化編程,復雜業務處理的維護成本高。
 調試不便
 不同數據庫之間可移植性差。– 不同數據庫語法不一致!

2.準備

數據庫參閱資料中的sql腳本:

delimiter $$ --聲明結束符

3.語法

官方參考網址:

https://dev.mysql.com/doc/refman/5.6/en/sql-statements.html
https://dev.mysql.com/doc/refman/5.6/en/sql-compound-statements.html

#### 3.0 語法結構

```sql
-- 存儲過程結構
CREATE
    [DEFINER = user]
 PROCEDURE sp_name ([proc_parameter[,...]])
    [characteristic ...] routine_body
    
-- 1. proc_parameter參數部分,可以如下書寫:
 [ IN | OUT | INOUT ] param_name type
 -- type類型可以是MySQL支持的所有類型
 
-- 2. routine_body(程序體)部分,可以書寫合法的SQL語句 BEGIN ... END

簡單演示:

-- 聲明結束符。因為MySQL默認使用‘;'作為結束符,而在存儲過程中,會使用‘;'作為一段語句的結束,導致‘;'使用沖突
delimiter $$

CREATE PROCEDURE hello_procedure ()
BEGIN
 SELECT 'hello procedure';
END $$

call hello_procedure();

3.1 變量及賦值

類比一下java中的局部變量和成員變量的聲明和使用

局部變量:

用戶自定義,在begin/end塊中有效

語法:
聲明變量 declare var_name type [default var_value];
舉例:declare nickname varchar(32);

-- set賦值
delimiter $$
create procedure sp_var01()
begin
 declare nickname varchar(32) default 'unkown';
 set nickname = 'ZS';
 -- set nickname := 'SF';
 select nickname;
end$$
-- into賦值
delimiter $$
create procedure sp_var_into()
begin
 declare emp_name varchar(32) default 'unkown' ;
 declare emp_no int default 0;
 select e.empno,e.ename into emp_no,emp_name from emp e where e.empno = 7839;
 select emp_no,emp_name;
end$$

用戶變量:
用戶自定義,當前會話(連接)有效。類比java的成員變量

語法:
@var_name
不需要提前聲明,使用即聲明

-- 賦值
delimiter $$
create procedure sp_var02()
begin
 set @nickname = 'ZS';
 -- set nickname := 'SF';
end$$
call sp_var02() $$
select @nickname$$  --可以看到結果


會話變量:
由系統提供,當前會話(連接)有效

語法:

@@session.var_name

show session variables; -- 查看會話變量
select @@session.unique_checks; -- 查看某會話變量
set @@session.unique_checks = 0; --修改會話變量


全局變量:
由系統提供,整個mysql服務器有效

語法:
@@global.var_name

舉例:

— 查看全局變量中變量名有char的記錄

show global variables like '%char%'; 

-- 查看全局變量character_set_client的值
select @@global.character_set_client; 

3.2 入參出參

— 語法
in | out | inout param_name type

舉例:

-- IN類型演示
delimiter $$
create procedure sp_param01(in age int)
begin
 set @user_age = age;
end$$
call sp_param01(10) $$
select @user_age$$
-- OUT類型,隻負責輸出!
-- 需求:輸出傳入的地址字符串對應的部門編號。
delimiter $$

create procedure sp_param02(in loc varchar(64),out dept_no int(11))
begin
 select d.deptno into dept_no from dept d where d.loc = loc;
 --此處強調,要麼表起別名,要麼入參名不與字段名一致
end$$
delimiter ;

--測試
set @dept_no = 100;
call sp_param02('DALLAS',@dept_no);
select @dept_no;
-- INOUT類型 
delimiter $$
create procedure sp_param03(inout name varchar(49))
begin
 set name = concat('hello' ,name);
end$$
delimiter ;

set @user_name = '小明';
call sp_param03(@user_name);
select @user_name;

3.3 流程控制-判斷

官網說明
https://dev.mysql.com/doc/refman/5.6/en/flow-control-statements.html

if

-- 語法
IF search_condition THEN statement_list
    [ELSEIF search_condition THEN statement_list] ...
    [ELSE statement_list]
END IF


舉例:

-- 前置知識點:timestampdiff(unit,exp1,exp2) 取差值exp2-exp1差值,單位是unit
select timestampdiff(year,e.hiredate,now()) from emp e where e.empno = '7499         ';
delimiter $$
-- DROP PROCEDURE IF EXISTS sp_param04;
create procedure sp_param05(in ages timestamp)
begin
 declare result varchar(32);
 if timestampdiff(year,ages,now())>40 
  then set result = '元老';
 elseif timestampdiff(year,ages,now())>38 
  then set result = '老員工';
 ELSE 
  SET result = '新手';
 end if;
 select result;
end $$
delimiter;
 
call sp_param05('1970-02-26 10:00:25');
-- 註意:MYSQL時間戳必須從1970年開始。

case

此語法是不僅可以用在存儲過程,查詢語句也可以用!

-- 語法一(類比java的switch):
CASE case_value
    WHEN when_value THEN statement_list
    [WHEN when_value THEN statement_list] ...
    [ELSE statement_list]
END CASE
-- 語法二:
CASE
    WHEN search_condition THEN statement_list
    [WHEN search_condition THEN statement_list] ...
    [ELSE statement_list]
END CASE


舉例:

-- 需求:入職年限年齡<=38是新手 >38並 <=40老員工 >40元老
delimiter $$
create procedure sp_hire_case()
begin
 declare result varchar(32);
 declare message varchar(64);
 case
    when timestampdiff(year,'2001-01-01',now()) > 40 
  then 
   set result = '元老';
   set message = '老爺爺';
 when timestampdiff(year,'2001-01-01',now()) > 38
  then 
   set result = '老員工';
   set message = '油膩中年人';
 else 
  set result = '新手';
  set message = '萌新';
 end case;
 select result;
end$$
delimiter ;

3.4 流程控制-循環

loop

-- 語法
[begin_label:] LOOP
    statement_list
END LOOP [end_label]

舉例:

需要說明,loop是死循環,需要手動退出循環,我們可以使用leave來退出。

可以把leave看成我們java中的break;與之對應的,就有iterate(繼續循環)——類比java的continue

--需求:循環打印1到10
-- leave控制循環的退出
delimiter $$
create procedure sp_flow_loop()
begin
 declare c_index int default 1;
 declare result_str  varchar(256) default '1';
 cnt:loop
 
  if c_index >= 10
  then leave cnt;
  end if;

  set c_index = c_index + 1;
  set result_str = concat(result_str,',',c_index);
  
 end loop cnt;
 
 select result_str;
end$$

-- iterate + leave控制循環
delimiter $$
create procedure sp_flow_loop02()
begin
 declare c_index int default 1;
 declare result_str  varchar(256) default '1';
 cnt:loop

  set c_index = c_index + 1;
  set result_str = concat(result_str,',',c_index);
  if c_index < 10 then 
   iterate cnt; 
  end if;
  -- 下面這句話能否執行到?什麼時候執行到? 當c_index < 10為false時執行
  leave cnt;
  
 end loop cnt;
 select result_str;
 
end$$

repeat

[begin_label:] REPEAT
    statement_list
UNTIL search_condition -- 直到…為止,才退出循環
END REPEAT [end_label]
-- 需求:循環打印1到10
delimiter $$
create procedure sp_flow_repeat()
begin
 declare c_index int default 1;
 -- 收集結果字符串
 declare result_str varchar(256) default '1';
 count_lab:repeat
  set c_index = c_index + 1;
  set result_str = concat(result_str,',',c_index);
  until c_index >= 10
 end repeat count_lab;
 select result_str;
end$$


while

類比java的while(){}
[begin_label:] WHILE search_condition DO
    statement_list
END WHILE [end_label]
-- 需求:循環打印1到10
delimiter $$
create procedure sp_flow_while()
begin
 declare c_index int default 1;
 -- 收集結果字符串
 declare result_str varchar(256) default '1';
 while c_index < 10 do
  set c_index = c_index + 1;
  set result_str = concat(result_str,',',c_index);
 end while;
 select result_str;
end$$

3.5 流程控制-退出、繼續循環

leave

類比java的breake
-- 退出 LEAVE can be used within BEGIN ... END or loop constructs (LOOP, REPEAT, WHILE).
LEAVE label

iterate

類比java的continue
-- 繼續循環 ITERATE can appear only within LOOP, REPEAT, and WHILE statements
ITERATE label



3.6 遊標

用遊標得到某一個結果集,逐行處理數據。

類比jdbc的ResultSet
-- 聲明語法
DECLARE cursor_name CURSOR FOR select_statement
-- 打開語法
OPEN cursor_name
-- 取值語法
FETCH cursor_name INTO var_name [, var_name] ...
-- 關閉語法
CLOSE cursor_name
-- 需求:按照部門名稱查詢員工,通過select查看員工的編號、姓名、薪資。(註意,此處僅僅演示遊標用法)
-- 更改結束符為$$
delimiter $$
-- 創造存儲過程(帶一個入參)
create procedure sp_create_table02(in dept_name varchar(32))
begin
-- 必須先聲明變量
 declare e_no int;
 declare e_name varchar(32);
 declare e_sal decimal(7,2);
 
 declare lp_flag boolean default true;
-- 其次聲明遊標:遊標值為query(dept_name)得到的table(e.empno,e.ename,e.sal)
 declare emp_cursor cursor for 
  select e.empno,e.ename,e.sal
  from emp e,dept d
  where e.deptno = d.deptno and d.dname = dept_name;
  
-- 然後聲明 handler 句柄:
-- 關於句柄:https://blog.csdn.net/qq_43427482/article/details/109898934
-- 看完還沒理解,再看:https://www.cnblogs.com/phpper/p/7587556.html
-- 這裡涉及瞭SQL STATE:https://blog.csdn.net/u014653854/article/details/78986780
-- 聲明handler句柄:當每條SQL傳遞ERROR STATE為沒有值的報錯時,設定變量lp_flag為非真,同時繼續執行SQL(如不聲明,當某條循環報錯時,整個SQL將直接停止循環)
 declare continue handler for NOT FOUND set lp_flag = false;
-- 打開遊標
 open emp_cursor;
-- 開啟LOOP循環:emp_loop
 emp_loop:loop
-- 將遊標值傳遞給三個變量
  fetch emp_cursor into e_no,e_name,e_sal;
-- 如果變量lp_flag為真,則獲取這三個參數的值;否則打斷emp_loop循環
  if lp_flag then
   select e_no,e_name,e_sal;
  else
   leave emp_loop;
  end if;
-- 結束循環
 end loop emp_loop;
-- 定義用戶變量並賦值(用戶變量不需要提前聲明、僅當前會話有效)>鄙人沒有理解這一步什麼意義
 set @end_falg = 'exit_flag';
-- 關閉遊標
 close emp_cursor;
-- 結束存儲過程
end$$
-- 恢復;結束符
delimiter;

-- 使用該存儲過程並傳參
call sp_create_table02('RESEARCH');

特別註意:

在語法中,變量聲明、遊標聲明、handler聲明是必須按照先後順序書寫的,否則創建存儲過程出錯。

3.7 存儲過程中的handler

handler句柄用於定義條件處理

DECLARE handler_action HANDLER
    FOR condition_value [, condition_value] ...
    statement

handler_action: {
    CONTINUE  -- 繼續執行
  | EXIT      -- 退出執行
  | UNDO      -- 什麼都不做
}

CONTINUE: Execution of the current program continues. -- 繼續執行當前程序
EXIT: Execution terminates for the BEGIN ... END compound statement in which the handler is declared. This is true even if the condition occurs in an inner block. -- 停止執行在handler被聲明的BEGIN... END的組合程序,即使是在程序內部發生該條件。

condition_value: {
    mysql_error_code
  | SQLSTATE [VALUE] sqlstate_value
  | condition_name
  | SQLWARNING
  | NOT FOUND
  | SQLEXCEPTION
}

SQLWARNING: Shorthand for the class of SQLSTATE values that begin with '01'. -- 即SQL STATE以01開頭的集合代稱
NOT FOUND: Shorthand for the class of SQLSTATE values that begin with '02'. -- 即SQL STATE以O2開頭的集合代稱
SQLEXCEPTION: Shorthand for the class of SQLSTATE values that do not begin with '00', '01', or '02'. -- 即SQL STATE不以00、01、02開頭的集合代稱
-- 各種寫法:
 DECLARE exit HANDLER FOR SQLSTATE '42S01' set @res_table = 'EXISTS';
 DECLARE continue HANDLER FOR 1050 set @res_table = 'EXISTS';
 DECLARE continue HANDLER FOR not found set @res_table = 'EXISTS';

4.練習

——大傢註意,存儲過程的業務過程在java代碼中一般也可以實現,我們下面的需求是為瞭練習存儲過程

4.1 利用存儲過程更新數據

為某部門(需指定)的人員漲薪100;如果是公司總裁,則不漲薪。

delimiter // -- 定義結束符
create procedure high_sal(in dept_name varchar(32)) -- 創建存儲過程
begin -- 開始存儲過程
 declare e_no int; -- 聲明變量
 declare e_name varchar(32);
 declare e_sal decimal(7,2);
 
 declare lp_flag boolean default true;
 
 declare emp_cursor cursor for  -- 聲明遊標
  select e.empno,e.ename,e.sal
  from emp e,dept d
  where e.deptno = d.deptno and d.dname = dept_name;
  
 -- 聲明handler句柄(條件處理)
 declare continue handler for NOT FOUND set lp_flag = false;
  
 open emp_cursor; -- 打開遊標
 
 emp_loop:loop -- 開啟循環
  fetch emp_cursor into e_no,e_name,e_sal; -- 變量賦值
  
  if lp_flag then -- 流程控制
   if e_name = 'king' then 
    iterate emp_loop; -- 繼續循環
   else 
    update emp e set e.sal = e.sal + 100 where e.empno = e_no; -- 更新數據
   end if;
  else
   leave emp_loop; -- 離開循環
  end if; -- 結束流程
  
 end loop emp_loop; -- 結束循環
 set @end_falg = 'exit_flag'; -- 聲明用戶變量
 close emp_cursor; -- 變比遊標
end // -- 結束存儲過程
delimiter; -- 恢復結束符


call high_sal('ACCOUNTING');

4.2 循環創建表

創建下個月的每天對應的表comp_2020_04_01、comp_2020_04_02、...

(模擬)需求描述:

我們需要用某個表記錄很多數據,比如記錄某某用戶的搜索、購買行為(註意,此處是假設用數據庫保存),當每天記錄較多時,如果把所有數據都記錄到一張表中太龐大,需要分表,我們的要求是,每天一張表,存當天的統計數據,就要求提前生產這些表——每月月底創建下一個月每天的表!

-- 知識點 預處理 prepare語句from後使用局部變量會報錯 
-- https://dev.mysql.com/doc/refman/5.6/en/sql-prepared-statements.html
-- 看不懂英文文檔的看這個:https://www.cnblogs.com/geaozhang/p/9891338.html;話說我也該去做一點英文技術文檔的閱讀提升瞭
PREPARE stmt_name FROM preparable_stmt
EXECUTE stmt_name [USING @var_name [, @var_name] ...]
{DEALLOCATE | DROP} PREPARE stmt_name

-- 貼一段預處理的案例,需求是:利用字符串定義預處理 SQL (直角三角形斜邊hypotenuse計算)
PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse'; -- POW(x,y)函數,用於計算x的y次方(http://c.biancheng.net/mysql/pow_power.html);SQRT函數用於求平方根(https://blog.csdn.net/weixin_39554172/article/details/113124290)
SET @a = 3;
SET @b = 4;  -- 用戶變量使用即聲明
EXECUTE stmt1 USING @a, @b; -- 結果為5
DEALLOCATE PREPARE stmt1; 

-- 知識點 時間的處理
-- EXTRACT(unit FROM date)截取時間的指定位置值
-- DATE_ADD(date,INTERVAL expr unit)  日期運算
-- LAST_DAY(date)  獲取日期的最後一天的日期
-- YEAR(date) 返回日期中的年
-- MONTH(date) 返回日期的月
-- DAYOFMONTH(date) 返回日 -- 註:根據https://stackoverflow.com/questions/35838321/day-vs-dayofmonth-in-mysql,其實DAY(date)效果是一樣的
-- 思路:循環構建表名 comp_2020_05_01 到 comp_2020_05_31;並執行create語句。
-- 分析:1. 循環構築表,僅表名不同,考慮使用存儲過程執行循環處理、使用預處理提高效率。 2. 首先需要一個變量存儲執行SQL;然後年份需要一個變量、月份需要一個變量、日期需要一個變量、拼接出來的表名需要一個變量;除此之外,還需要一個數字用來累增;至此,我們得到瞭至少需要6個變量。為瞭補全日期0,增加兩個月份、日的變量用來補充0形成01、02···。 3. 考慮到預處的格式(from後不能有局部變量),寫7個局部變量,1個用戶變量
delimiter //  --聲明結束符
create procedure sp_create_table()
begin
--  定義一堆局部變量
 declare next_year int;
 declare next_month int;
 declare next_month_maxday int;
  
 declare next_month_str char(2);
 declare next_month_maxday_str char(2);
 
 -- 處理每天的表名
 declare table_name_str char(10);
 -- 統計序列
 declare t_index int default 1;
 -- declare create_table_sql varchar(200);
 
 -- 獲取下個月的年份
 set next_year = year(date_add(now(),INTERVAL 1 month));
 -- 獲取下個月是幾月 
 set next_month = month(date_add(now(),INTERVAL 1 month));
 -- 下個月最後一天是幾號
 set next_month_maxday = dayofmonth(LAST_DAY(date_add(now(),INTERVAL 1 month)));
 
 -- 把1-9月補上0:  01,02····,09
 if next_month < 10
  then set next_month_str = concat('0',next_month);
 else
  set next_month_str = concat('',next_month);
 end if;
 
 
 while t_index <= next_month_maxday do
  
  -- 同上,對天數補0
  if (t_index < 10)
   then set next_month_maxday_str = concat('0',t_index);
  else
   set next_month_maxday_str = concat('',t_index);
  end if;
  
  -- 2020_05_01
  set table_name_str = concat(next_year,'_',next_month_str,'_',next_month_maxday_str);
  -- 拼接create sql語句(用戶變量)
  set @create_table_sql = concat(
     'create table comp_',
     table_name_str,
     '(`grade` INT(11) NULL,`losal` INT(11) NULL,`hisal` INT(11) NULL) COLLATE=\'utf8_general_ci\' ENGINE=InnoDB');
  -- FROM後面不能使用局部變量!這就是我們為什麼使用用戶變量的原因
  prepare create_table_stmt FROM @create_table_sql;
  execute create_table_stmt;
  DEALLOCATE prepare create_table_stmt;
  
  set t_index = t_index + 1;
  
 end while; 
end//
delimiter;

call sp_create_table()

-- 以下為個人精簡版

delimiter //
CREATE PROCEDURE sp_createtable1 () BEGIN-- 統計序列
 DECLARE
  t_index INT DEFAULT 1;
 WHILE
   t_index <= DAY (
    LAST_DAY(
    date_add( now(), INTERVAL 1 MONTH ))) DO
   
   SET @create_table_sql = concat(
    'CREATE TABLE comp_',
    YEAR (
    date_add( now(), INTERVAL 1 MONTH )),
    '_',
    MONTH (
    date_add( now(), INTERVAL 1 MONTH )),
    '_',
    t_index,
    '(`grade` INT(11) NULL,`losal` INT(11) NULL,`hisal` INT(11) NULL) COLLATE=\'utf8_general_ci\' ENGINE=InnoDB' 
   );-- FROM後面不能使用局部變量!這就是我們為什麼使用用戶變量的原因
  PREPARE create_table_stmt 
  FROM
   @create_table_sql;
  EXECUTE create_table_stmt;
  DEALLOCATE PREPARE create_table_stmt;
  
  SET t_index = t_index + 1;
  
 END WHILE;
 
END // 
delimiter;
CALL sp_createtable1 ()

4.3 其他場景:

“為用戶添加購物積分,並更新到用戶的總積分表中”等需要對多張表進行CRUD操作的業務。
而且內部可以使用事務命令。

5.其他

5.1 characteristic

characteristic:
    COMMENT 'string'
  | LANGUAGE SQL
  | [NOT] DETERMINISTIC
  | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
  | SQL SECURITY { DEFINER | INVOKER }


其中,SQL SECURITY的含義如下:

 MySQL存儲過程是通過指定SQL SECURITY子句指定執行存儲過程的實際用戶;
 如果SQL SECURITY子句指定為DEFINER(定義者),存儲過程將使用存儲過程的DEFINER執行存儲過程,驗證調用存儲過程的用戶是否具有存儲過程的execute權限和DEFINER用戶是否具有存儲過程引用的相關對象(存儲過程中的表等對象)的權限;
 如果SQL SECURITY子句指定為INVOKER(調用者),那麼MySQL將使用當前調用存儲過程的用戶執行此過程,並驗證用戶是否具有存儲過程的execute權限和存儲過程引用的相關對象的權限;
 如果不顯示的指定SQL SECURITY子句,MySQL默認將以DEFINER執行存儲過程。

5.2 死循環處理

-- 如有死循環處理,可以通過下面的命令查看並結束
show processlist;
kill id;

5.3 可以在select語句中寫case

https://dev.mysql.com/doc/refman/5.6/en/control-flow-functions.html
select 
 case
  when timestampdiff(year,e.hiredate,now()) <= 38 then '新手'
  when timestampdiff(year,e.hiredate,now()) <= 40 then '老員工'
  else '元老'
 end hir_loc,
 e.*
from emp e;


5.4 臨時表

臨時表在關閉會話後會被自動銷毀。
https://www.runoob.com/mysql/mysql-temporary-tables.html

create temporary table 表名(
  字段名 類型 [約束],
  name varchar(20) 
)Engine=InnoDB default charset utf8;

-- 需求:按照部門名稱查詢員工,通過select查看員工的編號、姓名、薪資。(註意,此處僅僅演示遊標用法)
delimiter $$
create procedure sp_create_table02(in dept_name varchar(32))
begin
 declare emp_no int;
 declare emp_name varchar(32);
 declare emp_sal decimal(7,2);
 declare exit_flag int default 0;
 
 declare emp_cursor cursor for
  select e.empno,e.ename,e.sal
  from emp e inner join dept d on e.deptno = d.deptno where d.dname = dept_name;
 
 declare continue handler for not found set exit_flag = 1;
 
 -- 創建臨時表收集數據
 CREATE temporary TABLE `temp_table_emp` (
  `empno` INT(11) NOT NULL COMMENT '員工編號',
  `ename` VARCHAR(32) NULL COMMENT '員工姓名' COLLATE 'utf8_general_ci',
  `sal` DECIMAL(7,2) NOT NULL DEFAULT '0.00' COMMENT '薪資',
  PRIMARY KEY (`empno`) USING BTREE
 )
 COLLATE='utf8_general_ci'
 ENGINE=InnoDB; 
 
 open emp_cursor;
 
 c_loop:loop
  fetch emp_cursor into emp_no,emp_name,emp_sal;
  
  
  if exit_flag != 1 then
   insert into temp_table_emp values(emp_no,emp_name,emp_sal); 
  else
   leave c_loop;
  end if;
  
 end loop c_loop;
 
 select * from temp_table_emp;
 
 select @sex_res; -- 僅僅是看一下會不會執行到
 close emp_cursor;
 
end$$

call sp_create_table02('RESEARCH');

附:本例建表SQL

CREATE TABLE `dept` (
 `deptno` INT(11) NOT NULL COMMENT '部門編號',
 `dname` VARCHAR(32) NULL COMMENT '部門名稱' COLLATE 'utf8_general_ci',
 `loc` VARCHAR(64) NULL COMMENT '部門地址' COLLATE 'utf8_general_ci',
 PRIMARY KEY (`deptno`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
INSERT INTO DEPT VALUES
 (10,'ACCOUNTING','NEW YORK');
INSERT INTO DEPT VALUES (20,'RESEARCH','DALLAS');
INSERT INTO DEPT VALUES
 (30,'SALES','CHICAGO');
INSERT INTO DEPT VALUES
 (40,'OPERATIONS','BOSTON');
 
CREATE TABLE `emp` (
 `empno` INT(11) NOT NULL COMMENT '員工編號',
 `ename` VARCHAR(32) NULL COMMENT '員工姓名' COLLATE 'utf8_general_ci',
 `job` VARCHAR(10) NULL COMMENT '職位' COLLATE 'utf8_general_ci',
 `mgr` INT(11) NULL COMMENT '上級編號',
 `hiredate` DATE NOT NULL COMMENT '入職時間',
 `sal` DECIMAL(7,2) NOT NULL DEFAULT '0.00' COMMENT '薪資',
 `comm` DECIMAL(7,2) NULL COMMENT '年終獎金',
 `deptno` INT(11) NOT NULL COMMENT '部門編號',
 PRIMARY KEY (`empno`) USING BTREE,
 INDEX `FK_emp_dept` (`deptno`) USING BTREE,
 CONSTRAINT `FK_emp_dept` FOREIGN KEY (`deptno`) REFERENCES `procedure_demo`.`dept` (`deptno`) ON UPDATE RESTRICT ON DELETE RESTRICT
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;

insert into emp values
(7369,'smith','clerk',7902,'1980-12-17',800,null,20);
insert into emp values
(7499,'allen','salesman',7698,'1981-02-20',1600,300,30);
insert into emp values
(7521,'ward','salesman',7698,'1981-02-22',1250,500,30);
insert into emp values
(7566,'jones','manager',7839,'1981-02-04',2975,null,20);
insert into emp values
(7654,'martin','salesman',7698,'1981-09-28',1250,1400,30);
insert into emp values
(7698,'blake','manager',7839,'1981-05-01',2850,null,30);
insert into emp values
(7782,'clark','manager',7839,'1981-06-09',2450,null,10);
insert into emp values
(7788,'scott','analyst',7566,'1987-07-13')-85,3000,null,20);
insert into emp values
(7839,'king','president',null,'1981-11-17',5000,null,10);
insert into emp values
(7844,'turner','salesman',7698,'1981-09-08',1500,0,30);
insert into emp values
(7876,'adams','clerk',7788,'1987-07-13')-51,1100,null,20);
insert into emp values
(7900,'james','clerk',7698,'1981-12-03',950,null,30);
insert into emp values
(7902,'ford','analyst',7566,'1981-12-03',3000,null,20);
insert into emp values
(7934,'miller','clerk',7782,'1982-01-23',1300,null,10);

CREATE TABLE `salgrade` (
 `grade` INT(11) NULL,
 `losal` INT(11) NULL,
 `hisal` INT(11) NULL
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
insert into salgrade values (1,700,1200);
insert into salgrade values (2,1201,1400);
insert into salgrade values (3,1401,2000);
insert into salgrade values (4,2001,3000);
insert into salgrade values (5,3001,9999);

到此這篇關於MYSQL存儲過程 註釋詳解的文章就介紹到這瞭,更多相關MYSQL存儲過程內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: