如果存在则更改表,如果不存在则创建
Posted
技术标签:
【中文标题】如果存在则更改表,如果不存在则创建【英文标题】:Alter table if exists or create if doesn't 【发布时间】:2013-05-26 02:03:11 【问题描述】:我需要运行一个安装程序,它也可以是一个更新程序。 安装程序需要能够最终获得 mysql 数据库的特定方案/结构,无论某些表是否存在、遗漏了几列或不需要更改,因为它们的结构是最新的。
如何优雅地组合ALTER
和CREATE
?
我在想肯定有“添加...如果...重复”之类的东西
假设我有表 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-检查访问表是不是存在/如果不存在,则创建/复制
HiveQL 中的“如果存在 t1 则更改表重命名为 t2”?