2010-01-06 49 views
2

考虑:MySQL数据库。有时,数据库模式更改和更新将推出(以sql脚本的形式)。为了保证应用(没有重复的更新,没有更新丢失等)更新的正确顺序我计划部署以下解决方案:中止MySQL的脚本有条件

  • CREATE TABLE元(名字TEXT PRIMARY KEY,VAL TEXT);
  • INSERT INTO元VALUES( '版本', '0');

每个更新脚本执行其被顺序地分配一个版本N。在执行更新之前,脚本会检查meta.version是否与之前的脚本版本N-1相匹配。执行更新后,meta.version更新为N.我不需要防止多个脚本并行运行。

的问题:如何检查版本并中止脚本,如果它不匹配?我想到执行

CALL `raise error` 

会打破脚本,但如何有条件地执行它取决于meta.version?不允许存储过程。有意义的错误信息是一个优点。 This不提供合适的解决方案。

回答

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

当然,您可以修改原始脚本以不选择任何行,这完全取决于您。

+0

是的,这是完美的包装脚本,但然后我需要确保每个人都使用这个包装脚本的更新。当然,这不会奏效。我需要“automagic”解决方案 - 无论是sql更新脚本还是不行。 – ygrek 2010-01-06 10:35:36

+0

怎么样然后http://stackoverflow.com/questions/773889/way-to-abort-execution-of-mysql-scripts-raising-error-orhaps/1275431#1275431 – 2010-01-06 10:51:28

+0

非常可怕:) 看起来像是有没有“直接”的解决方案.. – ygrek 2010-01-11 10:43:42

1

好的,新答案。这与其说是赏金让我更有创意,而我觉得少了克制,提出一个解决方案的不直考虑:)

(我仍然假设你想避免存储套路可言成本)

解决方案1:使用预处理语句

假设你uprades脚本是一个简单的:

ALTER TABLE t ADD COLUMN c INT DEFAULT 1 

那么如何AB出这样的:

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来解释它:)

+0

现在我看到你第一次使用包装脚本的答案是最简单的麻烦。我会走这条路。谢谢。 – ygrek 2010-01-12 09:09:23

+0

谢谢ygrek。尽管功能不完善,但我希望您仍然可以完成您的工作。我也希望它更容易。 – 2010-01-12 09:29:55

+0

@RolandBouman,您的“Mysql Proxy”链接已关闭。顺便说一下,使用MySQL“代理”并简单地在应用程序中执行它,有什么区别?通过Java或PHP? – Pacerier 2015-03-10 06:38:51