【问题标题】:MySQL: Add constraint if not existsMySQL:如果不存在则添加约束
【发布时间】:2011-04-24 13:41:00
【问题描述】:

在我的数据库创建脚本中,创建脚本如下所示:

CREATE TABLE IF NOT EXISTS `rabbits`
(
    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(255) NOT NULL,
    `main_page_id` INT UNSIGNED COMMENT 'What page is the main one',
    PRIMARY KEY (`id`),
    KEY `main_page_id` (`main_page_id`)
)
ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `rabbit_pages`
(
    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    `rabbit_id` INT UNSIGNED NOT NULL,
    `title` VARCHAR(255) NOT NULL,
    `content` TEXT NOT NULL,
    PRIMARY KEY (`id`),
    KEY `rabbit_id` (`rabbit_id`),
    CONSTRAINT `fk_rabbits_pages` FOREIGN KEY (`rabbit_id`) REFERENCES `rabbits` (`id`)
)
ENGINE=InnoDB;

ALTER TABLE `rabbits`
    ADD CONSTRAINT `fk_rabbits_main_page` FOREIGN KEY (`main_page_id`) REFERENCES `rabbit_pages` (`id`);

第一次运行良好,但如果我再次运行它,它会在最后一行出现“写入或更新时复制键”失败。

有没有办法我可以做ADD CONSTRAINT IF NOT EXISTS 或类似的事情?就像我可以使用 CREATE TABLE 查询一样?

【问题讨论】:

    标签: mysql constraints


    【解决方案1】:

    对于非 MariaDB,这对我有用:

    SET @dbname = DATABASE();
    SET @tablename = "my_table";
    SET @constraintname = "my_constraint_name";
    SET @columnname = "my_column";
    SET @othertablename = "other_table";
    SET @othercolumnname = "other_column_name";
    SET @deleteaction = "CASCADE";
    SET @updateaction = "RESTRICT";
    SET @preparedStatement = (SELECT IF(
      (
        SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
        WHERE
          (table_name = @tablename)
          AND (table_schema = @dbname)
          AND (constraint_name = @constraintname)
      ) > 0,
      "SELECT 1",
      CONCAT("ALTER TABLE ",@tablename,
        " ADD CONSTRAINT ",@constraintname,
        " FOREIGN KEY(",@columnname,")
          REFERENCES ",@othertablename,"(",@othercolumnname,")
          ON DELETE ",@deleteaction,
        " ON UPDATE ",@updateaction)));
    PREPARE alterIfNotExists FROM @preparedStatement;
    EXECUTE alterIfNotExists;
    DEALLOCATE PREPARE alterIfNotExists;
    

    这是一个解决方案,由类似的问题制成:https://stackoverflow.com/a/31989541/3589448

    根据需要添加参数。 @deleteaction 和 @updateaction 可以有:“RESTRICT”、“CASCADE”、“SET NULL”或“NO ACTION”。

    【讨论】:

      【解决方案2】:

      FOREIGN_KEY_CHECKS 是一个很棒的工具,但如果您需要知道如何在不删除和重新创建表的情况下执行此操作。您可以使用SELECT 语句 ON information_schema.TABLE_CONSTRAINTS 来确定外键是否存在:

      IF NOT EXISTS (
          SELECT NULL 
          FROM information_schema.TABLE_CONSTRAINTS
          WHERE
              CONSTRAINT_SCHEMA = DATABASE() AND
              CONSTRAINT_NAME   = 'fk_rabbits_main_page' AND
              CONSTRAINT_TYPE   = 'FOREIGN KEY'
      )
      THEN
          ALTER TABLE `rabbits`
          ADD CONSTRAINT `fk_rabbits_main_page`
          FOREIGN KEY (`main_page_id`)
          REFERENCES `rabbit_pages` (`id`);
      END IF
      

      【讨论】:

      • 这个很有用,防御性很强,我喜欢
      • 此解决方案看起来可行,但会产生以下错误(如果您在 OP 发布的原始 SQL 语句中使用它代替 ALTER TABLE):[ERROR in query 3] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IF NOT EXISTS (SELECT NULL FROM information_schema.TABLE_CONSTRAINTS WHERE ' at line 1 [ERROR in query 4] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'END IF' at line 1
      • @jsdalton 我认为这种语法只适用于存储过程。
      【解决方案3】:

      MariaDB 在10.0.2 or later 中支持这种语法:

      ALTER TABLE `rabbits`
      ADD CONSTRAINT `fk_rabbits_main_page` FOREIGN KEY IF NOT EXISTS
      (`main_page_id`) REFERENCES `rabbit_pages` (`id`);
      

      【讨论】:

      • 该死,只有 mariadb。
      【解决方案4】:

      有趣的问题。您可能希望在调用 CREATE TABLE 语句之前禁用外键,然后再启用它们。这将允许您直接在 CREATE TABLE DDL 中定义外键:

      例子:

      SET FOREIGN_KEY_CHECKS = 0;
      Query OK, 0 rows affected (0.00 sec)
      
      CREATE TABLE IF NOT EXISTS `rabbits` (
          `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
          `name` VARCHAR(255) NOT NULL,
          `main_page_id` INT UNSIGNED COMMENT 'What page is the main one',
          PRIMARY KEY (`id`),
          KEY `main_page_id` (`main_page_id`),
          CONSTRAINT `fk_rabbits_main_page` FOREIGN KEY (`main_page_id`) REFERENCES `rabbit_pages` (`id`)
      ) ENGINE=InnoDB;
      Query OK, 0 rows affected (0.04 sec)
      
      CREATE TABLE IF NOT EXISTS `rabbit_pages` (
          `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
          `rabbit_id` INT UNSIGNED NOT NULL,
          `title` VARCHAR(255) NOT NULL,
          `content` TEXT NOT NULL,
          PRIMARY KEY (`id`),
          KEY `rabbit_id` (`rabbit_id`),
          CONSTRAINT `fk_rabbits_pages` FOREIGN KEY (`rabbit_id`) REFERENCES `rabbits` (`id`)
      ) ENGINE=InnoDB;
      Query OK, 0 rows affected (0.16 sec)
      
      SET FOREIGN_KEY_CHECKS = 1;
      Query OK, 0 rows affected (0.00 sec)
      

      测试用例:

      INSERT INTO rabbits (name, main_page_id) VALUES ('bobby', NULL);
      Query OK, 1 row affected (0.02 sec)
      
      INSERT INTO rabbit_pages (rabbit_id, title, content) VALUES (1, 'My Main Page', 'Hello');
      Query OK, 1 row affected (0.00 sec)
      
      SELECT * FROM rabbits;
      +----+-------+--------------+
      | id | name  | main_page_id |
      +----+-------+--------------+
      |  1 | bobby | NULL         |
      +----+-------+--------------+
      1 row in set (0.00 sec)
      
      SELECT * FROM rabbit_pages;
      +----+-----------+--------------+---------+
      | id | rabbit_id | title        | content |
      +----+-----------+--------------+---------+
      |  1 |         1 | My Main Page | Hello   |
      +----+-----------+--------------+---------+
      1 row in set (0.00 sec)
      
      UPDATE rabbits SET main_page_id = 2 WHERE id = 1;
      ERROR 1452 (23000): A foreign key constraint fails
      
      UPDATE rabbits SET main_page_id = 1 WHERE id = 1;
      Query OK, 1 row affected (0.00 sec)
      Rows matched: 1  Changed: 1  Warnings: 0
      
      UPDATE rabbit_pages SET rabbit_id = 2 WHERE id = 1;
      ERROR 1452 (23000): A foreign key constraint fails
      

      【讨论】:

      • 嗯,我以为我必须创建表 A,使用 B->A 创建表 B,然后添加 A->B,因为如果表不存在,约束将失败...奇怪...将尽快尝试:p
      • @Svish:会的,除非您在顶部有 SET FOREIGN_KEY_CHECKS = 0;。这就是诀窍。然后我们在最后将其设置回1
      • 我也遇到了这个问题,我会尝试更新我的脚本以使用 FOREIGN_KEY_CHECKS。如果我使用无效的外键运行SET FOREIGN_KEY_CHECKS = 1;,如果出现严重错误会发生什么?
      • 老实说,我可能会创建一个存储过程来检查信息模式中是否存在 FK,并仅在必要时添加约束。然后删除存储过程。
      猜你喜欢
      • 2014-07-17
      • 1970-01-01
      • 2016-06-21
      • 1970-01-01
      • 2014-06-01
      • 2016-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多