鉴于:MySQL数据库。有时会推出数据库模式更改和更新(以sql脚本的形式)。为了保证应用更新的正确顺序(没有重复更新,没有更新丢失等),我计划部署以下解决方案:
每个更新脚本都带有一个顺序分配的版本N.在执行更新之前,脚本会检查meta.version是否与先前的脚本版本N-1匹配。执行更新后,meta.version更新为N.我不需要防止多个并行运行的脚本。
问题:如果不匹配,如何检查版本并中止脚本?我想出了执行
CALL `raise error`
会破坏脚本,但是如何根据meta.version有条件地执行它?不允许存储过程。有意义的错误消息是一个加号。 This没有提供合适的解决方案。
答案 0 :(得分:1)
我认为最好在shell脚本或安装程序应用中解决。你无法在SQL脚本中真正拥有任何控制结构。
一个非常简单的解决方案是创建一个脚本,根据元表上的选择生成实际脚本的命令行。
因此,假设您有此脚本gen_upgrade_command.sql
:
select concat(
'mysql -u<user> -p<password> -h<host> -e"SOURCE upgrade'
, val + 1
, '.sql"'
)
from meta;
你这样运行
mysql -u<user> -p<password> -h<host> -Nrs < gen_upgrade_command.sql > do_upgrade.bat
现在,do_upgrade.bat
包含此生成的命令行:
mysql -u<usere> -p<password> -h<host> -e"SOURCE upgrade1.sql"
并且正在运行do_upgrade.bat
将运行upgrade1.sql
当然,您可以修改原始脚本,也不要选择任何行,这取决于您。
答案 1 :(得分:1)
好的,新答案。并不是说赏金让我更具创造性,而是我觉得建议解决方案不那么克制我不直接考虑:)
(我仍然假设您要不惜一切代价避免存储例程)
解决方案#1:使用准备好的陈述
假设你的uprades脚本很简单:
ALTER TABLE t ADD COLUMN c INT DEFAULT 1
那怎么样:
SET @version := '1';
SELECT CASE val
WHEN @version THEN
'ALTER TABLE t ADD COLUMN c INT DEFAULT 1'
ELSE 'SELECT ''Wrong version. Nothing to upgrade.'''
END
INTO @stmt
FROM meta
WHERE name = 'Version';
PREPARE stmt FROM @stmt;
EXECUTE stmt;
明显的缺点:
PREPARE
不能批处理多个语句)PREPARE
解决方案#2:美国一个准备好的声明来杀死连接
您已经指出您不喜欢您链接的解决方案......是因为KILL
语句,还是因为存储的函数?如果是因为存储的功能,你可以解决这个问题:
SET @version := '1';
SELECT CASE val
WHEN @version THEN 'SELECT ''Performing upgrade...'''
ELSE CONCAT('KILL CONNECTION', connection_id())
END
INTO @stmt
FROM meta
WHERE name = 'Version';
PREPARE stmt FROM @stmt;
EXECUTE stmt; -- connection is killed if the version is not correct.
-- remainder of the upgrade script goes here...
mysql
必须以--skip-reconnect
选项启动,以确保在连接被终止时不能执行任何语句我试图解决这个问题,并将PREPARE
用于阻止当前连接的其他事情,例如删除用户并撤消其权限。不幸的是,这不能按预期工作(不,也不是在FLUSH PRIVILEGES
之后)
解决方案#3:使用mysql代理拦截
另一个解决方案:使用代理。使用mysql代理(请参阅:http://forge.mysql.com/wiki/MySQL_Proxy),您可以在lua中编写一个自定义拦截脚本来解释您的自定义命令。使用它你可以添加必要的控制结构。缺点:你现在必须设计自己的迷你语言,并学习lua来解释它:)