一、MySQL 创建存储过程
“pr_add” 是个简单的 MySQL 存储过程,这个存储过程有两个 int 类型的输入参数 “a”、“b”,返回这两个参数的和。
(
a int,
b int
)
begin
declare c int;
set a = 0;
end if;
set b = 0;
end if;
return c;- 不能在 MySQL 存储过程中使用。return 只能出现在函数中。
/
end;
set @b = 20;
创建 MySQL 存储过程的简单语法为:
(
[in|out|inout] 参数 datatype
)
begin
MySQL 语句;
end;
MySQL 存储过程参数如果不显式指定“in”、“out”、“inout”,则默认为“in”。习惯上,对于是“in” 的参数,我们都不会显式指定。
(
@a int,- 错误
b int - 正确
)
(
a int,
b int
)
as - 错误,MySQL不需要 “as”
begin
mysql statement ...;
end;
(
a int,
b int
)
begin
mysql statement 1 ...;
mysql statement 2 ...;
end;
set a = 0;
end if;
这是个
多行 MySQL 注释。
/
set a = 0;
end if;
end;
return c;- 不能在 MySQL 存储过程中使用。return 只能出现在函数中。
/
end;
9. 调用 MySQL 存储过程时候,需要在过程名字后面加“()”,即使没有一个参数,也需要“()”
10. 因为 MySQL 存储过程参数没有默认值,所以在调用 MySQL 存储过程时候,不能省略参数。可以用 null 来替代。
- DELIMITER$$
- DROPPROCEDUREIFEXISTSHelloWorld$$
- CREATEPROCEDUREHelloWorld()
- BEGIN
- SELECT"HelloWorld!";
- END$$
- DELIMITER;
3、变量
使用DECLARE来声明,DEFAULT赋默认值,SET赋值
- DECLAREcounterINTDEFAULT0; - 默认为0
- SETcounter=counter+1; - 自增+1
4、参数
IN为默认类型,值必须在调用时指定,值不能返回(值传递)
OUT值可以返回(指针传递)
INOUT值必须在调用时指定,值可以返回
eg:
- CREATEPROCEDUREtest(aINT,OUTbFLOAT,INOUTcINT)
5、条件判断
eg:
- DELIMITER$$
- DROPPROCEDUREIFEXISTSdiscounted_price$$
- CREATEPROCEDUREdiscunted_price(normal_priceNUMERIC(8,2),OUTdiscount_priceNUMERIC(8,2))
- BEGIN
- IF(normal_price>500)THEN
- SETdiscount_price=normal_price*.8;
- ELSEIF(normal_price>100)THEN
- SETdiscount_price=normal_price*.9;
- ELSE
- SETdiscount_price=normal_price;
- ENDIF;
- END$$
- DELIMITER;
6、循环
LOOP、END LOOP
eg:
- DELIMITER$$
- DROPPROCEDUREIFEXISTSsimple_loop$$
- CREATEPROCEDUREsimple_loop(OUTcounterINT)
- BEGIN
- SETcounter=0;
- my_simple_loop:LOOP
- SETcounter=counter+1;
- IFcounter=10THEN
- LEAVEmy_simple_loop;
- ENDIF;
- ENDLOOPmy_simple_loop;
- END$$
- DELIMITER;
WHILE DO、END WHILE
- DELIMITER$$
- DROPPROCEDUREIFEXISTSsimple_while$$
- CREATEPROCEDUREsimple_while(OUTcounterINT)
- BEGIN
- SETcounter=0;
- WHILEcounter!=10DO
- SETcounter=counter+1;
- ENDWHILE;
- END$$
- DELIMITER;
REPEAT、UNTILL
- DELIMITER$$
- DROPPROCEDUREIFEXISTSsimple_repeat$$
- CREATEPROCEDUREsimple_repeat(OUTcounterINT)
- BEGIN
- SETcounter=0;
- REPEAT
- SETcounter=counter+1;
- UNTILcounter=10ENDREPEAT;
- END$$
- DELIMITER;
7,异常处理
如果用cursor获取SELECT语句返回的所有结果集时应该定义NOT FOUND error handler来防止存储程序提前终结
如果SQL语句可能返回constraint violation等错误时应该创建一个handler来防止程序终结
8,数据库交互
INTO用于存储单行记录的查询结果
- DECLAREtotal_salesNUMERIC(8,2);
- SELECTSUM(sale_value)INTOtotal_salesFROMsalesWHEREcustomer_id=in_customer_id;
CURSOR用于处理多行记录的查询结果
- DELIMITER$$
- DROPPROCEDUREIFEXITScursor_example$$
- CREATEPROCEDUREcursor_example()
- READSSQLDATA
- BEGIN
- DECLAREl_employee_idINT;
- DECLAREl_salaryNUMERIC(8,2);
- DECLAREl_department_idINT;
- DECLAREdoneINTDEFAULT0;
- DECLAREcur1CURSORFORSELECTemployee_id,salary,department_idFROMemployees;
- DECLARECONTINUEHANDLERFORNOTFOUNDSETdone=1;
- OPENcur1;
- emp_loop:LOOP
- FETCHcur1INTOl_employee_id,l_salary,l_department_id;
- IFdone=1THEN
- LEAVEemp_loop;
- ENDIF;
- ENDLOOPemp_loop;
- CLOSEcur1;
- END$$
- DELIMITER;
unbounded SELECT语句用于存储过程返回结果集
- DELIMITER$$
- DROPPROCEDUREIFEXISTSsp_emps_in_dept$$
- CREATEPROCEDUREsp_emps_in_dept(in_employee_idINT)
- BEGIN
- SELECTemployee_id,surname,firstname,address1,address2,zipcode,date_of_birthFROMemployeesWHEREdepartment_id=in_employee_id;
- END$$
- DELIMITER;
UPDATE、INSERT、DELETE、CREATE TABLE等非查询语句也可以嵌入存储过程里
- DELIMITER$$
- DROPPROCEDUREIFEXITSsp_update_salary$$
- CREATEPROCEDUREsp_update_salary(in_employee_idINT,in_new_salaryNUMERIC(8,2))
- BEGIN
- IFin_new_salary<5000ORin_new_salary>500000THEN
- SELECT"Illegalsalary:salarymustbebetween$5000and$500,000";
- ELSE
- UPDATEemployeesSETsalary=in_new_salaryWHEREemployee_id=in_employee_id;
- ENDIF:
- END$$
- DELIMITER;
9,使用CALL调用存储程序
- DELIMITER$$
- DROPPROCEDUREIFEXISTScall_example$$
- CREATEPROCEDUREcall_example(employee_idINT,employee_typeVARCHAR(20))
- NOSQL
- BEGIN
- DECLAREl_bonus_amountNUMERIC(8,2);
- IFemployee_type='MANAGER'THEN
- CALLcalc_manager_bonus(employee_id,l_bonus_amount);
- ELSE
- CALLcalc_minion_bonus(employee_id,l_bonus_amount);
- ENDIF;
- CALLgrant_bonus(employee_id,l_bonus_amount);
- END$$
- DELIMITER;
10,一个复杂的例子
- CREATEPROCEDUREputting_it_all_together(in_department_idINT)
- MODIFIESSQLDATA
- BEGIN
- DECLAREl_employee_idINT;
- DECLAREl_salaryNUMERIC(8,2);
- DECLAREl_department_idINT;
- DECLAREl_new_salaryNUMERIC(8,2);
- DECLAREdoneINTDEFAULT0;
- DECLAREcur1CURSORFOR
- SELECTemployee_id,salary,department_id
- FROMemployees
- WHEREdepartment_id=in_department_id;
- DECLARECONTINUEHANDLERFORNOTFOUNDSETdone=1;
- CREATETEMPORARYTABLEIFNOTEXISTSemp_raises
- (employee_idINT,department_idINT,new_salaryNUMERIC(8,2));
- OPENcur1;
- emp_loop:LOOP
- FETCHcur1INTOl_employee_id,l_salary,l_department_id;
- IFdone=1THEN/*Nomorerows*/
- LEAVEemp_loop;
- ENDIF;
- CALLnew_salary(1_employee_id,l_new_salary);/*Getnewsalary*/
- IF(l_new_salary<>l_salary)THEN/*Salarychanged*/
- UPDATEemployees
- SETsalary=l_new_salary
- WHEREemployee_id=l_employee_id;
- /*Keeptrackofchangedsalaries*/
- INSERTINTOemp_raises(employee_id,department_id,new_salary)
- VALUES(l_employee_id,l_department_id,l_new_salary);
- ENDIF:
- ENDLOOPemp_loop;
- CLOSEcur1;
- /*Printoutthechangedsalaries*/
- SELECTemployee_id,department_id,new_salaryfromemp_raises
- ORDERBYemployee_id;
- END;
11,存储方法
存储方法与存储过程的区别
1,存储方法的参数列表只允许IN类型的参数,而且没必要也不允许指定IN关键字
2,存储方法返回一个单一的值,值的类型在存储方法的头部定义
3,存储方法可以在SQL语句内部调用
4,存储方法不能返回结果集
语法:
- CREATE
- [DEFINER={user|CURRENT_USER}]
- PROCEDUREsp_name([proc_parameter[,...]])
- [characteristic...]routine_body
- CREATE
- [DEFINER={user|CURRENT_USER}]
- FUNCTIONsp_name([func_parameter[,...]])
- RETURNStype
- [characteristic...]routine_body
- proc_parameter:
- [IN|OUT|INOUT]param_nametype
- func_parameter:
- param_nametype
- type:
- AnyvalidMySQLdatatype
- characteristic:
- LANGUAGESQL
- |[NOT]DETERMINISTIC
- |{CONTAINSSQL|NOSQL|READSSQLDATA|MODIFIESSQLDATA}
- |SQLSECURITY{DEFINER|INVOKER}
- |COMMENT'string'
- routine_body:
- ValidSQLprocedurestatement
各参数说明见CREATE PROCEDURE and CREATE FUNCTION Syntax
例子:
- DELIMITER$$
- DROPFUNCTIONIFEXISTSf_discount_price$$
- CREATEFUNCTIONf_discount_price
- (normal_priceNUMERIC(8,2))
- RETURNSNUMERIC(8,2)
- DETERMINISTIC
- BEGIN
- DECLAREdiscount_priceNUMERIC(8,2);
- IF(normal_price>500)THEN
- SETdiscount_price=normal_price*.8;
- ELSEIF(normal_price>100)THEN
- SETdiscount_price=normal_price*.9;
- ELSE
- SETdiscount_price=normal_price;
- ENDIF;
- RETURN(discount_price);
- END$$
- DELIMITER;
12,触发器
触发器在INSERT、UPDATE或DELETE等DML语句修改数据库表时触发
触发器的典型应用场景是重要的业务逻辑、提高性能、监控表的修改等
触发器可以在DML语句执行前或后触发
- DELIMITER$$
- DROPTRIGGERsales_trigger$$
- CREATETRIGGERsales_trigger
- BEFOREINSERTONsales
- FOREACHROW
- BEGIN
- IFNEW.sale_value>500THEN
- SETNEW.free_shipping='Y';
- ELSE
- SETNEW.free_shipping='N';
- ENDIF;
- IFNEW.sale_value>1000THEN
- SETNEW.discount=NEW.sale_value*.15;
- ELSE
- SETNEW.discount=0;
- ENDIF;
- END$$
- DELIMITER;