29

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

我怎样才能优雅地组合ALTERCREATE

我在想一定有类似“添加......如果......重复”

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

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

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

我该怎么做?谢谢 :-)

4

4 回答 4

28

MySQLINFORMATION_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;

更多信息:

更新:

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

  1. 创建临时表,即现有表的精确副本
  2. 使用旧表中的数据填充临时表
  3. 丢弃旧表
  4. 使用新架构创建新表
  5. 使用临时表中的信息填充新表
  6. 删除临时表。

因此,在 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;

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

更多信息:

于 2013-05-30T13:24:49.087 回答
2

好吧,如果您使用的是编码语言,请执行以下操作:

SHOW TABLES LIKE 'myTable'

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

于 2013-05-30T13:15:53.700 回答
0

我将给出两个例子来说明如何使用 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 运行此代码。

于 2021-12-15T02:06:57.117 回答
0

虽然这个话题已经有几年了,但我一直在寻找类似问题的解决方案。关于@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();
于 2021-07-14T12:39:34.320 回答