如果存在则更改表,如果不存在则创建

Posted

技术标签:

【中文标题】如果存在则更改表,如果不存在则创建【英文标题】:Alter table if exists or create if doesn't 【发布时间】:2013-05-26 02:03:11 【问题描述】:

我需要运行一个安装程序,它也可以是一个更新程序。 安装程序需要能够最终获得 mysql 数据库的特定方案/结构,无论某些表是否存在、遗漏了几列或不需要更改,因为它们的结构是最新的。

如何优雅地组合ALTERCREATE

我在想肯定有“添加...如果...重复”之类的东西

假设我有表 A。在一个客户端中,该表具有一列 -A1,而另一个客户端具有相同的表,但具有 A1 列和 A2 列。

我希望我的 sql 命令使两个客户端的表 A 都包含三列:A1、A2 和 A3。

同样,我的脚本是一个我转储到 mysql 的 sql 文件。

我该怎么做?谢谢:-)

【问题讨论】:

【参考方案1】:

MySQL INFORMATION_SCHEMA 数据库救援:

-- First check if the table exists
IF EXISTS(SELECT table_name 
            FROM INFORMATION_SCHEMA.TABLES
           WHERE table_schema = 'db_name'
             AND table_name LIKE 'wild')

-- If exists, retreive columns information from that table
THEN
   SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT
     FROM INFORMATION_SCHEMA.COLUMNS
    WHERE table_name = 'tbl_name'
      AND table_schema = 'db_name';

   -- do some action, i.e. ALTER TABLE if some columns are missing 
   ALTER TABLE ...

-- Table does not exist, create a new table
ELSE
   CREATE TABLE ....

END IF;

更多信息:

MySQL 参考手册:Chapter 19. INFORMATION_SCHEMA Tables MySQL 参考手册:The INFORMATION_SCHEMA TABLES Table MySQL 参考手册:The INFORMATION_SCHEMA COLUMNS Table

更新:

另一个可能更简单的选择是删除现有表并使用新架构重新创建它。为此,您需要:

    创建临时表,即现有表的精确副本 用旧表中的数据填充临时表 放下旧桌子 使用新架构创建新表 用临时表中的信息填充新表 删除临时表。

所以,在 SQL 代码中:

CREATE TABLE old_table_copy LIKE old_table;

INSERT INTO old_table_copy
SELECT * FROM old_table;

DROP TABLE old_table;

CREATE TABLE new_table (...new values...);

INSERT INTO new_table ([... column names from old table ...])
SELECT [...column names from old table ...] 
FROM old_table_copy;

DROP TABLE old_table_copy;

其实最后一步,“删除临时表。”,你可以跳过一会儿。以防万一,您可能希望对旧表进行某种备份,“以防万一”。

更多信息:

MySQL 参考手册:CREATE TABLE Syntax MySQL 参考手册:INSERT Syntax MySQL 参考手册:INSERT ... SELECT Syntax

【讨论】:

我在哪里运行这样的脚本——我是一个有 mysql 功能的新手。您可以将其作为查询运行吗? 我对您的“安装程序”一无所知,因为您没有提供任何信息,但基本上您有两个选择:(1)您在一个 SQL 查询中执行此操作,使用STORED PROCEDURE,或者 (2) 你使用其他一些外部编程语言并在那里执行 IF / ELSE 条件。 If Exists 语句似乎不再起作用?尝试使用 MySQL 8 执行此操作时出现语法错误,是否已在以后的版本中删除?【参考方案2】:

如果你使用的是编码语言,那么:

SHOW TABLES LIKE 'myTable'

如果它返回一个值调用alter else调用create

【讨论】:

我不使用编码语言。我正在转储一个 sql 文件。 好的然后创建一个mysql函数来计算SHOW TABLES LIKE 'myTable'的结果,如果0创建其他改变?有很多关于mysql函数的文档。【参考方案3】:

虽然这个话题已经有几年的历史了,但我一直在寻找类似问题的解决方案。关于@GregD 的答案,我找到了一个适合我的解决方案。 SQL 脚本创建表,但是,如果数据库中已有表,它应该应用更改。在此版本中,它仅在有附加列时才有效。修改的(剩余的)列不起作用。

-- We create a procedure that is dropped if it exists already.
DROP PROCEDURE IF EXISTS changeFunction;
-- We have to change the delimiter for the procedure.
DELIMITER $$
CREATE PROCEDURE changeFunction()
    BEGIN
-- Check if table already exists
IF EXISTS(SELECT table_name 
            FROM INFORMATION_SCHEMA.TABLES
           WHERE table_schema = 'your_database_name'
             AND table_name LIKE 'your_table_name')

-- It exists, so create a new table and copy the data.
THEN
    -- Copy the data into a copy of the table.
    CREATE TABLE `your_table_name_copy` LIKE `your_table_name`;
    INSERT INTO `your_table_name_copy` SELECT * FROM `your_table_name`;
    -- Remove old table
    DROP TABLE `your_table_name`;
    -- Create the new table
    CREATE TABLE IF NOT EXISTS `your_table_name` (
        -- Your columns
    );
    -- Copy values, it determines the old column names by itself
    SET @v1 := (SELECT GROUP_CONCAT(COLUMN_NAME)
      FROM INFORMATION_SCHEMA.COLUMNS
      WHERE TABLE_SCHEMA = 'your_database_name' AND TABLE_NAME = 'your_table_name_copy');
    -- Since the columns are detected automatically, we have to execute the 
    -- statement as prepared statement.
    SET @q1 := CONCAT('INSERT INTO `your_table_name` (', @v1, ') ',
                      'SELECT ', @v1, ' FROM `your_table_name_copy`');
    PREPARE stmt FROM @q1;
    EXECUTE stmt;
    -- Remove copy
    DROP TABLE `your_table_name_copy`;
ELSE
    -- It does not exist, simply create it
    CREATE TABLE IF NOT EXISTS `your_table_name` (
        -- Your columns
    );
END IF;
END $$
-- Reset the delimiter
DELIMITER ;
-- Call the function
CALL changeFunction();

【讨论】:

【参考方案4】:

我将给出两个示例来说明如何使用 MySQL 8.0+ 来执行此操作。首先,如果存在某些东西,alter 语句才能工作,它必须位于 存储过程 中。下面的示例显示了如何删除和添加约束(如果存在或不存在)。如果它不存在,它将简单地忽略 if 并创建它。如果它确实存在,它将删除它,然后再次创建它。 这对于调试很有用,因此您可以快速更改约束逻辑并对其进行测试。对于那些不熟悉 DELIMITER 的人来说,它从本质上改变了 ;在这种情况下为 $$。这是必要的,以便过程知道 ; 属于它,而不是完整的 SQL 查询正在结束。

    DROP PROCEDURE IF EXISTS trading.create_constraint;
    DELIMITER $$
    CREATE PROCEDURE trading.create_constraint()
        BEGIN
            IF EXISTS (SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS WHERE CONSTRAINT_NAME = 'assigned_equity_percent_not_over_100')
            THEN
                ALTER TABLE Strategies DROP CONSTRAINT assigned_equity_percent_not_over_100;
            END IF;
            ALTER TABLE Strategies ADD CONSTRAINT assigned_equity_percent_not_over_100 CHECK (assigned_equity_percent <= 100 AND assigned_equity_percent>=0);
        END$$
    DELIMITER ;
    CALL create_constraint();

下面的另一个示例是如何使用触发器创建更高级的过程,该触发器将计算所有值的总和,并通过 SIGNAL SQLSTATE '45000' 引发错误来确保在更新或插入时它们不超过 100错误。

DROP PROCEDURE IF EXISTS trading.check_sum_strategy_assigned_equity;
DELIMITER $$
CREATE PROCEDURE trading.check_sum_strategy_assigned_equity(IN new_equity_percentage DECIMAL(5,2),IN old_equity_percentage DECIMAL(5,2),IN updating BOOLEAN)
BEGIN
    IF (updating=false) THEN
        IF ((SELECT SUM(assigned_equity_percent) total FROM Strategies WHERE strategy_pack_id = strategy_pack_id)+new_equity_percentage)>100
        THEN
            SIGNAL SQLSTATE '45000' SET message_text = 'Can not INSERT a value of 100 for assigned equity percent with the given strategy_pack_id';
        END IF;
    ELSE
        IF ((SELECT SUM(assigned_equity_percent) total FROM Strategies WHERE strategy_pack_id = strategy_pack_id)-old_equity_percentage+new_equity_percentage)>100
        THEN
            SIGNAL SQLSTATE '45000' SET message_text = 'Can not UPDATE a value of 100 for assigned equity percent with the given strategy_pack_id';
        END IF;
    END IF;
END$$
DELIMITER ;
DROP TRIGGER IF EXISTS ins_sum_strat_ass_eq;
CREATE TRIGGER ins_sum_strat_ass_eq
BEFORE INSERT ON Strategies
FOR EACH ROW
CALL trading.check_sum_strategy_assigned_equity(NEW.assigned_equity_percent,0.00,FALSE);
DROP TRIGGER IF EXISTS up_sum_strat_ass_eq;
CREATE TRIGGER up_sum_strat_ass_eq
BEFORE UPDATE ON Strategies
FOR EACH ROW
CALL trading.check_sum_strategy_assigned_equity(NEW.assigned_equity_percent,OLD.assigned_equity_percent,TRUE);

需要注意的是,DELIMITER 只能通过 MySQL 客户端工作,而不是通过 API 工作,因此您会收到错误消息,指出它无法识别 DELIMITER。考虑到这一点,您可以运行类似于我在下面的 javascript 中所做的代码。

//@JA - This function sanitizes custom SQL so that any delimiter code is removed which only works via client or phpmyadmin.
var sanitize_for_mysql = function(sql) 
    sql = sql.replace(/([$][$])/gm, ";");
    sql = sql.replace(/DELIMITER ;/gm, "");
    return sql;
;

module.exports = sanitize_for_mysql;

通过这样做,您仍然可以使用像 Sequelize 这样的 ORM 来运行此代码。

【讨论】:

以上是关于如果存在则更改表,如果不存在则创建的主要内容,如果未能解决你的问题,请参考以下文章

Shell 脚本检查 dir 目录是不是存在然后更改路径,如果不存在则使用该名称创建 dir 并检查文件名不存在

从 Excel VBA-检查访问表是不是存在/如果不存在,则创建/复制

mysql怎样实现" 如果表存在则清空,不存在则创建表"

HiveQL 中的“如果存在 t1 则更改表重命名为 t2”?

如果 Oracle 中不存在表,则创建一个表(使用 Java)

PostgreSQL:如果不存在则创建表 AS