MySQL DBA生涯篇,对mysql数据库存储过程的新认识

春节小长假一放完,自己都还没有来得及见见同学什么的,公司要求开始上班了。

今年过年的事情就不再说了,对于公司离家还是不太远的我倒是没有什么影响,虽然每次回家都要花上4、5个小时。

初八开始上班,刚刚放完假回来完全是没有动力去创造价值,磨磨的就把来的两天过了。

第二周,自己仿佛又回到了完美的工作状态,虽然自己的效率是有点低,但是还是学到了很多东西。

处理了几个业务,也练习了自己的能力。

虽然业务都是mssql的特殊数据查询,但是我还是做的津津有味。特别是那个多条件链接的查询,把我坑的说不出话来。废话不多说直接来代码:

SELECT 
TABLE2.PlatName,
TABLE1.* 
FROM 
	(SELECT 
	A.GameID,
	A.PlatID,
	A.ServerID,
	A.Date,
	isnull (A.Points,0) AS "充值总点数",
	isnull (A.PayNum,0) AS "充值人数",
	isnull (B.BuyPoint,0) AS "消费总点数",
	isnull (B.BuyNum,0) AS "消费人数"
	FROM 
		(SELECT
		GameID,
		PlatID,
		ServerID,
		convert(varchar(10),[PayTime],120) AS Date,
		SUM(BasePoints) AS Points,
		COUNT(DISTINCT UserID) AS PayNum  
		FROM [库1].[Account].[dbo].[ChargeLog]
		WHERE GameID=16 --xb
		AND PayTime BETWEEN '2014-01-01 00:00:00.000' AND GETDATE() 
		GROUP BY  GameID,PlatID,ServerID,convert(varchar(10),[PayTime],120)) as a
	FULL JOIN
		(SELECT 
		[GameID],[ServerID]     
		,convert(varchar(10),[BuyTime],120) AS Date,
		-SUM([BuyPoint]) AS BuyPoint,
		COUNT(DISTINCT UserID) AS BuyNum    
		FROM [库1].[Account].[dbo].[PlayerBuyLog]
		WHERE GameID=16 --xb
		AND PointType=0
		AND [BuyTime] BETWEEN '2014-01-01 00:00:00.000' AND GETDATE() 
		GROUP BY [GameID],ServerID,convert(varchar(10),[BuyTime],120)) as b
	ON 
	a.GameID=B.GameID
	AND A.ServerID=B.ServerID
	AND A.Date=B.Date  )

AS TABLE1
	INNER JOIN 
	(SELECT * FROM [库2].[PlatInfo].[dbo].[tb_Plat])
AS TABLE2
ON TABLE1.PlatID=TABLE2.PlatID
ORDER BY TABLE1.PlatID,TABLE1.ServerID,TABLE1.Date 


目的很简单的,就是两个结果集做个连接就好了。但是这个存储过程却是有问题的,连接条件几个的时候是有误的。我这里上面一个查询单查出来是79条记录,下面一个是39条作全连接记录变成了37条,不知道有没有做DBA的同学遇到过。问题先记录在这儿吧。

还是说正文,这篇文章是说mysql存储过程的:

在去年年末的时候开始接触mysql存储过程,因为以前用mysql好像都没有在意用存储过程。

提供了事务性和非事务性存储引擎,这是mysql的特点,以前都用的是MyISAM引擎。

大家也看到了,mysql提供多种拉风的存储引擎,大家有兴趣可以去百度、google一下。对于各种引擎介绍我也就不说了,因为是要存储过程就得支持事务了额,那我们就得说说MyISAM和InnoDB了,这两个是mysql极为常见使用的存储引擎。

可能有的同学还没有关心这个,因为以前都是默认的,默认是MyISAM引擎,这个是一个高速存取的数据库引擎,不支持事务。然后事务作为存储过程的一个亮点,显然MyISAM引擎此时就没有了发挥的空间了。自3.23.49版以来,InnoDB事务存储引擎一直很稳定。InnoDB正用于大型、重负荷生产系统。InnoDB是MySQL 5.x版本频繁支持的,既然开始支持了,这一个分支的发展也就快了起来,但是官方发布的MySQL版本好像对存储过程的开发做的并不像mssql做的那么好。以至于好多的开发者不愿意使用mysql的存储过程。事实上,mysql的存储过程比起mssql,应该说只有更好没有差的。因为mssql为开发者做的太多了,反而限制了开发者的发挥。

现在公司准备向手游转型,数据库支持换mysql,虽然我是菜鸟也得硬着头皮搞额。

首先,mysql中几乎没有什么高级的函数,基本上所有的东西都要自己写。

这个给我带来一些问题的,mssql许多的特色函数都没有了,mysql只提供了一些基本的。

比如:

mssql提供的ISNUMERIC函数

mysql中我是没有找到有相关的函数可以解决的,后来还是通过自己到MySQL AB的一个论坛上找到了大牛写的源码:


CREATE DEFINER = `root`@`127.0.0.1` FUNCTION `fn_IsNumeric`(myVal VARCHAR(1024))
    RETURNS tinyint(1)
    DETERMINISTIC
RETURN myVal REGEXP '^(-|\\+)?([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+|[0-9]+)$';
其实我想说,没有的我们都可以写,不要怕。mysql数据库一样的出色。

还有mssql中的GetDate()莫名被换成了Now(),DateTime类型和TimeStamp类型区别啊......

做mysql数据库有太多的创新点要我们开发者自己去做去实践,而不是等着别人更新版本然后就只是去体验新功能然后再用什么的。要能做到我们需要什么东西就做什么,也许你做的以后指不定就成为了标准了,这不是一件很自豪的事情么。

DROP PROCEDURE IF EXISTS sp_AccountVerify;

CREATE DEFINER=`root`@`%` PROCEDURE sp_AccountVerify
(
	IN v_Account varchar(50),					-- 登陆账号   
	IN v_Password varchar(32),					-- 登陆密码     
	IN v_ServerID int,						-- 服务器编号 
	IN v_PlayerIP varchar(20),					-- 玩家登陆IP
	IN v_IsLimited int,						-- 是否受限
	IN v_MacAddress bigint,						-- 客户端MAC地址 
	OUT ov_PlayerUID bigint,					-- 账号唯一编号
	OUT ov_CardPoint int,						-- 剩余元宝
	OUT ov_GMLevel smallint,					-- M等级
	OUT ov_UserPassword varchar(20),			        -- 玩家二级密码
	OUT ov_LoginTime timestamp,					-- 登陆时间
	OUT ov_IsDynamicPWD bit,					-- 用户密码是否为动态密码1为是否则不是
	OUT ov_ServerInfo varchar(64),				        -- 上次登陆服务器名字
	OUT ov_RealAccount varchar(36),				        -- 真实账号
	OUT ov_iReturnValue int  					-- 处理结果 ,0表示未处理                
)
/*
    //ov_iReturnValue = 0  
    //ov_iReturnValue = -1
    //ov_iReturnValue = -2 
    //ov_iReturnValue = -3 
    //ov_iReturnValue = -4 
    //ov_iReturnValue = -5 
    //ov_iReturnValue = -6 
    //ov_iReturnValue = -7 
    //ov_iReturnValue > 0  
    //ov_iReturnValue -9   
*/
BEGIN
	DECLARE	var_pwd varchar(32);				-- 账号密码
	DECLARE	var_LockTime timestamp;				-- 锁定时间
	DECLARE	var_UID bigint;					-- 用户ID
	DECLARE	var_GameID int;					-- 游戏编号
	DECLARE	var_UpdateTime timestamp;			-- 更新时间
	DECLARE	var_PlatID int;					-- 平台ID
	DECLARE	var_AllowIP varchar(200);			-- GMIP
	DECLARE	var_RC INT;

	SET ov_RealAccount = v_Account;
	SET ov_CardPoint = 0;
	SET ov_UserPassword = '123456';
	SET ov_LoginTime = NOW();
	SET ov_IsDynamicPWD = 0;
	SET ov_ServerInfo = '';
	SET var_UID = v_Account;
	SET var_GameID = 0;	
	Opt_Label:
	BEGIN
		IF v_PlayerIP IN ('涉密数据库') THEN
			SET OV_iReturnValue = -3;
			LEAVE Opt_Label;
		END IF;
		/*获取用户登陆信息*/
		SELECT UserID,GmLevel,Password,LockTime,UpdateTime,PlatID
		        INTO ov_PlayerUID,ov_GMLevel,var_pwd,var_LockTime,var_UpdateTime,var_PlatID
		        FROM UserID
		        WHERE UserID= var_UID;
		 
		SET var_UpdateTime = (date_add(var_UpdateTime,INTERVAL 24 DAY_HOUR));
		/*用户检查*/
		IF IFNULL(ov_PlayerUID,0) <= 0 THEN
			SET OV_iReturnValue = -6;
			LEAVE Opt_Label;
		END IF;
		/*GM不验证密码,玩家密码检查*/
		IF IFNULL(ov_GMLevel,0) <= 0 THEN
			/*动态口令过期检查*/
			IF var_UpdateTime < NOW() THEN
				SET OV_iReturnValue = -7;
				LEAVE Opt_Label;
			END IF;
			
			IF (v_Password <> var_pwd) THEN
				SET OV_iReturnValue = -7;
				LEAVE Opt_Label;
			END IF;
		END IF;

		CALL sp_CheckUserLock(var_UID,var_GameID,v_ServerID,v_PlayerIP,v_MacAddress,@var_RC);		
		SET var_RC = @var_RC;

		IF var_RC = 1 THEN
			SET OV_iReturnValue = -3;
			LEAVE Opt_Label;
		END IF;
		/*封号检查*/
		IF (var_LockTime IS NOT NULL AND var_LockTime > NOW()) THEN
		    SET OV_iReturnValue = -3;
			LEAVE Opt_Label;
		END IF;

		SELECT MAX(LockTime) INTO var_LockTime FROM UserLock WHERE UserID=var_UID AND GameID=var_GameID AND (ServerID=v_ServerID or ServerID=0);
		/*封号检查*/
		IF(var_LockTime IS NOT NULL AND var_LockTime > NOW()) THEN
			SET OV_iReturnValue = -3;
			LEAVE Opt_Label;
		END IF;
		/*如果GM账号就要检查密码*/
		IF IFNULL(ov_GMLevel,0) > 0 THEN
			SELECT AllowIP INTO var_AllowIP from PlatGMIP where PlatID=var_PlatID;
			IF INSTR(','+var_AllowIP+',' , ','+v_PlayerIP+',') <= 0 THEN		
				SET OV_iReturnValue = -6;
				LEAVE Opt_Label;
			END IF;
		END IF;
		/*主账号表检查*/
		IF EXISTS(SELECT * FROM AccountData where UserID=ov_PlayerUID and GameID=var_GameID and ServerID=v_ServerID LIMIT 1) THEN
			UPDATE AccountData SET LoginTime=ov_LoginTime WHERE UserID=ov_PlayerUID and GameID=var_GameID and ServerID=v_ServerID;
		ELSE
			INSERT INTO AccountData(UserID,GameID, ServerID, LoginTime, LastLogTime, MaxLevel, OnlineTime, FirstLoginTime, PayPoint,IsLogin)
			VALUES(ov_PlayerUID,var_GameID,v_ServerID,ov_LoginTime,NULL,0,0,ov_LoginTime,0,1);
		END IF;

		SET OV_iReturnValue = 1;		
	END Opt_Label;
END

大家看看上面的存储过程写法吧,这是我推荐的写法。

作为公司数据安全有的地方就删除掉了。

在mysql中没有了@作为参数变量的说法了,@只能是用户变量,意思说你只能在调用存储过程的时候可以用。

但是我们的参数还是要有办法解决啥,我的推荐:

格式大家看我那个应该就知道了啥,主要是对于变量的处理,大家各有各的爱好这也是没有办法的,这让其他的人阅读起来带来了巨大的麻烦。希望大家都能做一个比较一致的东西出来,为开源发展继续努力。

我的书写方法也不一定就是好,大家可以提出自己的一件交流一哈:Email:jyf0412@sina.com

注意的地方就是,

1、你的存储过程得有一个执行成功与否的值 

ov_iReturnValue

2、在mysql中没有return一说,所以大家对return相关的东西都得改写,用标签解决。

 
 
 
这个标签名字大家也不要取的太奇葩,随意就行,LOOP最好。 

3、在存储过程内部声明的变量命名这也是一个奇葩的地方,不能用@,你让mssql的DBA如何能习惯。大家用前缀解决吧

DECLARE var_pwd varchar(32); -- 账号密码

DECLARE var_LockTime timestamp; 

谨以此文,开始我的mysql DBA生涯。

或许现在大家对Oracle的数据库期望很高,的确实力也很强。

但是我是一直小小鸟,慢慢来吧。

大家有什么好的建议或者意见可以邮件我,一般每天我都能及时回复,最迟不超过一周。

一起进步吧!


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值