代码之家  ›  专栏  ›  技术社区  ›  ygrek

有条件地中止MySQL脚本

  •  2
  • ygrek  · 技术社区  · 15 年前

    给定:mysql数据库。有时,数据库模式的更改和更新会以SQL脚本的形式进行。为了确保应用的更新顺序正确(无重复更新、无缺少更新等),我计划部署以下解决方案:

    • 创建表META(名称文本主键,VAL文本);
    • 插入到元数据中(“版本”,“0”);

    每个更新脚本都包含一个按顺序分配的版本n。在执行更新之前,脚本会检查meta.version是否与以前的脚本版本n-1匹配。执行更新后,meta.version更新为n。我不需要保护多个脚本并行运行。

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

    CALL `raise error`
    

    将中断脚本,但如何根据meta.version有条件地执行它?不允许任何存储过程。有意义的错误信息是一个优势。 This 没有提供合适的解决方案。

    2 回复  |  直到 15 年前
        1
  •  1
  •   Roland Bouman    15 年前

    我认为这最好用shell脚本或安装程序应用程序来解决。在SQL脚本中不能有任何控件构造。

    一个非常简单的解决方案是创建一个脚本,该脚本生成命令行,以便根据元表上的select执行实际的脚本。

    假设你有这个剧本, 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"
    

    并运行 蝙蝠 将运行 upgrade1.sql

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

        2
  •  1
  •   Roland Bouman    15 年前

    好的,新答案。这并不是因为赏金让我更有创造力,而是因为我对提出解决方案感到不那么拘束。 不要直截了当:)

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

    解决方案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 无法批处理多个语句)
    • 额外的烦恼,因为你必须把升级脚本中的每一条语句都写成一个字符串…糟糕。
    • 并非所有语句都可以用 准备

    解决方案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 选项以确保在终止连接时不能执行任何语句

    我试着解决这个缺点,并使用 准备 对于其他会阻止当前连接的事情,例如删除用户并取消其权限。不幸的是,这不符合预期(不,也不是在 FLUSH PRIVILEGES )

    解决方案3:使用MySQL代理拦截

    另一种解决方案是:使用代理。使用MySQL代理(请参见: http://forge.mysql.com/wiki/MySQL_Proxy ,您可以在lua中编写一个自定义截取脚本来解释您的自定义命令。使用它可以添加必要的控制结构。缺点:你现在必须设计自己的迷你语言,并学习Lua来解释它:)