2

给定:MySQL 数据库。有时会推出 db 架构更改和更新(以 sql 脚本的形式)。为了保证应用更新的正确顺序(没有重复更新、没有更新丢失等),我计划部署以下解决方案:

  • CREATE TABLE 元(名称 TEXT PRIMARY KEY,val TEXT);
  • 插入元值('版本','0');

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

问题:如果不匹配,如何检查版本并中止脚本?我发现执行

CALL `raise error`

会破坏脚本,但是如何根据 meta.version 有条件地执行它?不允许存储过程。有意义的错误信息是一个加号。没有提供合适的解决方案。

4

2 回答 2

1

好的,新的答案。与其说是赏金让我更有创造力,倒不如说我觉得提出一个不认为直接的解决方案时没有那么克制:)

(我仍然假设您想不惜一切代价避免存储例程)

解决方案 #1:使用准备好的语句

假设您的升级脚本很简单:

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

于 2010-01-11T12:06:07.370 回答
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

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

于 2010-01-06T10:15:02.413 回答