注意:定義參數的時候,不要将參數名和字段名一緻,哪怕是大小寫
自定義Sql異常處理
show errors #顯示錯誤
DECLARE CONTINUE HANDLER for SQLEXCEPTION [sql] #出錯了執行[sql],然後繼續往下執行
DECLARE EXIT HANDLER for SQLEXCEPTION [sql] #異常時僅執行[sql],停止執行
場景:使用者轉賬
調試方式:call SP_TRANSFER('1','2',1)。可以将注釋解除SELECT SLEEP(5),執行第一個存儲過程時,直接将賬戶取空,然後迅速另起一個會話執行一定的金額。是不會出現負數的情況的
參數配置:IN fromuser varchar(50),IN touser varchar(50),IN _money decimal(10,2)
BEGIN
DECLARE fromuser_money DECIMAL(10,2) DEFAULT 0;
DECLARE touser_money DECIMAL(10,2) DEFAULT 0;
DECLARE Is_OK bit DEFAULT TRUE; ###預設是成功
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET Is_OK = FALSE; ###如果出錯則修改辨別
START TRANSACTION;
SELECT user_money into fromuser_money from user_balance WHERE user_id = fromuser for update;
SELECT touser_money into touser_money from user_balance WHERE user_id = touser for update;
IF fromuser_money < _money THEN
ROLLBACK;
SELECT '金額不夠' as result;
ELSE
SET fromuser_money = fromuser_money - _money;
SET touser_money = touser_money + _money;
update user_balance SET user_money = fromuser_money WHERE user_id = fromuser;
###SELECT SLEEP(5); 測試專用,可以趁這個時間另起一個會話執行本存儲過程
update user_balance SET user_money = touser_money WHERE user_id = touser;
END IF;
IF Is_OK = TRUE THEN
COMMIT;
SELECT '轉賬成功' as result;
ELSE
ROLLBACK;
SELECT '出錯' as result;
END IF;
END
場景:使用者餘額變動時,更新(插入)到流水表,并且記錄到日志表中,但這裡是先插入日志,如果成功再進入下一步。
知識點:
1、不存在則更新,存在則插入;
2、事務復原與送出、異常
3、文法學習
調試方式:CALL SP_UPDATE_OR_INSERT(3,1,"使用者充值",'10');
參數配置:IN _user_id int,IN _log_type varchar(255),IN _log_des varchar(255),IN _log_value decimal(10,2)
BEGIN
DECLARE t_error int DEFAULT 0;
DECLARE usercount int DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET t_error = 1;
START TRANSACTION;
#插入使用者充值日志表
INSERT INTO user_balance_log(user_id,log_type,log_des,log_value) values (_user_id,_log_type,_log_des,_log_value);
#代表日志表插入成功
IF ROW_COUNT() > 0 THEN
#查找使用者是否存在表中
SELECT COUNT(*) INTO usercount from user_balance where user_id = _user_id;
#如果存在則更新,否則就插入
IF usercount > 0 THEN
UPDATE user_balance SET user_money = user_money + _log_value WHERE user_id = _user_id;
ELSE
INSERT INTO user_balance(user_id,user_money) VALUES (_user_id,_log_value);
END IF;
END IF;
#判斷錯誤
if t_error = 1 THEN
ROLLBACK; #復原
ELSE
COMMIT; #送出
END IF;
END
場景:生成測試資料
BEGIN
SET @NUM = 1;
WHILE @NUM < 1000000 DO
INSERT INTO TEST_1 (user_name,user_pwd) VALUES (CONCAT('USER',@NUM),'123');
SET @NUM = @NUM + 1;
END WHILE;
END;
場景:使用者登入
調試方式:call SP_USER_LOG('Lee','123')
參數配置:IN _user_name varchar(50),IN _user_pwd varchar(50)
BEGIN
SET @gid = 0;
SET @user_name = '';
SET @_result = 'login success';
SELECT id,user_name INTO @gid,@user_name from user_sys where user_name = _user_name and user_pwd = _user_pwd limit 1;
if @gid = 0 then
set @_result = 'login error';
end if;
select * from (select @_result as _result) a,(select @gid,@user_name) b;
END
場景:點選次數,點贊次數
學習連結:http://www.jtthink.com/course/play/351
調試方式:call SP(1,'192.168.22.14','110');
知識點和注意點:
1、clickdate 的資料類型為“date”,插入值為 CURRENT_DATE
2、clicknum預設值為1
3、prod_click 表的字段如下:id、prod_id、user_ip、clickdate、user_id、clicknum
4、prod_main 表的字段如下:prod_id、prod_name
參數配置:IN _prod_id int,IN _user_ip varchar(15),IN _user_id int
BEGIN
SET @NUM =0;
SET @COUNT = 0;
SELECT * FROM prod_main WHERE prod_id = _prod_id limit 1;
SET @NUM = FOUND_ROWS();
IF @NUM = 1 THEN
SELECT COUNT(*) INTO @COUNT FROM prod_click WHERE prod_id = _prod_id AND
user_ip = _user_ip AND user_id = _user_id AND clickdate = CURRENT_DATE;
IF @COUNT > 0 THEN
UPDATE prod_click SET clicknum = clicknum + 1 WHERE prod_id = _prod_id AND
user_ip = _user_ip AND user_id = _user_id AND clickdate = CURRENT_DATE;
ELSE
INSERT INTO prod_click(prod_id,user_ip,user_id,clickdate) VALUES (_prod_id,_user_ip,_user_id,CURRENT_DATE);
END IF;
END IF;
END
http://www.jtthink.com/course/play/351
場景:通過sql循環周遊Select的資料進行操作
知識點:遊标
BEGIN
DECLARE isend int DEFAULT 0;
DECLARE pid int;
DECLARE cnum int;
DECLARE cur CURSOR FOR SELECT prod_id,sum(clicknum) FROM prod_click GROUP BY prod_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET isend = 1;
open cur;#打開遊标
fetch cur INTO pid,cnum;
WHILE isend != 1 DO
UPDATE prod_main SET prod_click = cnum WHERE prod_id = pid;
fetch cur INTO pid,cnum;
END WHILE;
CLOSE cur;
END
場景:分頁
參數配置:IN startrow int,IN pagesize int
BEGIN
IF startrow = 0 THEN
SELECT
*
FROM
prod_main
ORDER BY
prod_id DESC
LIMIT
pagesize;
ELSE
SELECT
*
FROM
prod_main
WHERE
prod_id > (SELECT * FROM prod_main ORDER BY prod_id DESC LIMIT startrow,1)
ORDER BY
prod_id DESC
LIMIT
pagesize;
END IF;
END
場景:拼接字元串
知識點:CONCAT和遊标、去除最後一個字元串
BEGIN
DECLARE Is_End INT DEFAULT 0;
DECLARE _str VARCHAR(50) DEFAULT '';
DECLARE _id INT;
DECLARE _answer VARCHAR(50);
DECLARE cur CURSOR FOR SELECT id,answer FROM question_library where id > 100 ORDER BY RAND() LIMIT 10;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET Is_End = 1;
OPEN cur;
FETCH cur INTO _id,_answer;
WHILE Is_End != 1 DO
SELECT CONCAT(_id,',',_str) INTO _str;
FETCH cur INTO _id,_answer;
END WHILE;
CLOSE cur;
SELECT left(_str,LENGTH(_str)-1) AS question; #去掉最後一個字元串“,”
END
場景:字元串分割
知識點:臨時表,字元串分割
參數配置:IN _str varchar(50)
BEGIN
set @id = _str;
set @i = 0;
DROP TEMPORARY TABLE IF EXISTS tem_result;
CREATE TEMPORARY TABLE tem_result(id BIGINT(20) NOT NULL);
SET @cnt = 1+(LENGTH(@id) - LENGTH(REPLACE(@id,',','')));
WHILE @i < @cnt DO
SET @i = @i + 1;
SET @result = REVERSE(SUBSTRING_INDEX(REVERSE(SUBSTRING_INDEX(@id,',',@i)),',',1));
INSERT INTO tem_result(id) VALUES (@result);
END WHILE;
SELECT * FROM tem_result;
END
場景:商品添加,表結構請參考:http://www.cnblogs.com/CyLee/p/5686757.html
知識點:動态sql,遊标與臨時表的結合
入參:IN _prod_name varchar(50),IN _class_id int,IN _attr_sql varchar(2000)
調用:
CALL sp_new_prod('測試商品',1,'
SELECT 1 AS attr_id,\'中國出版社\' AS attr_value,0 AS prod_price
UNION SELECT 3 AS attr_id,\'16開\' AS attr_value,30 AS prod_price
UNION SELECT 3 AS attr_id,\'32開\' AS attr_value,35 AS prod_price
');
BEGIN
#Routine body goes here...
DECLARE isend int DEFAULT 0;
DECLARE _prod_id int;
DECLARE _attr_id int;
declare _attr_value varchar(20);
declare _prod_price DECIMAL(5,2);
DECLARE cur CURSOR FOR select prod_id,attr_id,attr_value,prod_price from temp_a;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET isend = 1;
#添加一條記錄到商品主表并且擷取id
set @pid=0;
INSERT into prod_main(prod_name,prod_classid) values(_prod_name,_class_id);
set @pid=LAST_INSERT_ID();
#通過程式擷取商品的屬性和屬性對應的價格,放入臨時表
drop TEMPORARY table if EXISTS temp_a;
set @ss=CONCAT('create TEMPORARY table temp_a as select ? as prod_id, a.* from (',_attr_sql,' ) a');
PREPARE pname from @ss;
EXECUTE pname using @pid;
DEALLOCATE PREPARE pname;
#循環臨時表,循環插入商品價格表
open cur;#打開遊标
fetch cur into _prod_id,_attr_id,_attr_value,_prod_price;
while isend!=1 do
insert into prod_attr(prod_id,attr_id,attr_value) values(_prod_id,_attr_id,_attr_value); #插入商品屬性表
set @getid=LAST_INSERT_ID();
insert into prod_price(prod_id,prod_attr_id,prod_price) values(_prod_id,_attr_id,_prod_price);
fetch cur into _prod_id,_attr_id,_attr_value,_prod_price;
end while;
close cur;#關閉遊标
DROP TEMPORARY table temp_a; #删除臨時表
END
場景:商品添加與屬性添加
入參:IN _prod_classid int,IN _prod_name varchar(50),IN _attr_sql text
begin
insert into `test`.`prod_main` ( `prod_classid`, `prod_name`) values
(_prod_classid,_prod_name);
set @pid = LAST_INSERT_ID();
#insert into prod_attr select 1 as prod_id,a.* from (select 1 as attr_id, '中國出版社' as attr_value UNION select 2 as attr_id, '劉勇' as attr_value) as a
set @ss = CONCAT('insert into prod_attr select ? as prod_id,a.* from (',_attr_sql,' ) as a');
PREPARE pname from @ss;
EXECUTE pname using @pid; #指派給?
DEALLOCATE PREPARE pname;
end