目标是在比较 MySQL 5.5 (引擎MyISAM)中两个表的模式时添加缺少的列。参数p_table1
是模型表名称,p_table2
将从中进行比较,并且"同步"。
当它被调用时,没有任何反应,没有错误,没有任何错误。我试图记录一些变量,但它也没有。
代码有什么问题?
CREATE PROCEDURE synchronize_tables(p_table1 VARCHAR(64), p_table2 VARCHAR(64), p_schema_name VARCHAR(64))
BEGIN
DECLARE v_done INT default false;
DECLARE v_actual_column_name VARCHAR(64);
DECLARE v_does_columns_exist INT default true;
DECLARE v_column_type LONGTEXT;
DECLARE v_column_default LONGTEXT;
DECLARE v_is_nullable VARCHAR(3);
DECLARE v_cur CURSOR FOR
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = p_table1
AND table_schema = p_schema_name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = TRUE;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
OPEN v_cur;
read_loop: LOOP
FETCH v_cur INTO v_actual_column_name;
IF v_done THEN
LEAVE read_loop;
END IF;
SELECT count(*) INTO v_does_columns_exist
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = p_table2
AND table_schema = p_schema_name
AND column_name = v_actual_column_name;
IF NOT v_does_columns_exist THEN
SELECT column_type, COLUMN_DEFAULT, IS_NULLABLE
INTO v_column_type, v_column_default, v_is_nullable
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = p_table1
AND table_schema = p_schema_name
AND column_name = v_actual_column_name;
SET @stmt_text = CONCAT('ALTER TABLE ', p_schema_name, '.', p_table2,
' ADD COLUMN ', v_actual_column_name, ' ', v_column_type, ' ', IF(upper(v_is_nullable) = 'NO', 'NOT NULL', ''),
' DEFAULT ', v_column_default);
prepare v_stmt FROM @stmt_text;
execute v_stmt;
deallocate prepare v_stmt;
END IF;
END LOOP;
CLOSE v_cur;
END;
END
答案 0 :(得分:1)
我发现了一些问题。
首先,您的大多数游标代码都在EXIT HANDLER FOR SQLEXCEPTION中。仅在发生错误时才运行此块。所以通常这个块永远不会运行。
其次,您使用CONCAT()列来形成ALTER TABLE语句,但是一个或多个列可以为NULL。当您CONCAT()任何具有null的字符串时,整个concat操作的结果为NULL。所以你必须确保NULL默认为非NULL。
在我的测试中,列默认值通常为NULL。我们希望这成为关键字" NULL"在ALTER TABLE语句中。此外,如果默认值不是NULL,您可能想引用它,因为普通的默认值可能是字符串或日期,并且您不能引用它。解决方案:QUOTE()是一个内置函数,可以正确引用字符串,甚至可以将NULL转换为关键字" NULL"。
这就是我的工作:
CREATE PROCEDURE synchronize_tables(p_table1 VARCHAR(64),
p_table2 VARCHAR(64), p_schema_name VARCHAR(64))
BEGIN
DECLARE v_done INT default false;
DECLARE v_actual_column_name VARCHAR(64);
DECLARE v_does_columns_exist INT default true;
DECLARE v_column_type LONGTEXT;
DECLARE v_column_default LONGTEXT;
DECLARE v_is_nullable VARCHAR(3);
DECLARE v_cur CURSOR FOR
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = p_table1
AND table_schema = p_schema_name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = TRUE;
OPEN v_cur;
read_loop: LOOP
FETCH v_cur INTO v_actual_column_name;
IF v_done THEN
LEAVE read_loop;
END IF;
SELECT count(*) INTO v_does_columns_exist
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = p_table2
AND table_schema = p_schema_name
AND column_name = v_actual_column_name;
IF NOT v_does_columns_exist THEN
SELECT column_type, COLUMN_DEFAULT, IS_NULLABLE
INTO v_column_type, v_column_default, v_is_nullable
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = p_table1
AND table_schema = p_schema_name
AND column_name = v_actual_column_name;
SET @stmt_text = CONCAT('ALTER TABLE ',
p_schema_name, '.', p_table2,
' ADD COLUMN ', v_actual_column_name, ' ',
v_column_type, ' ',
IF(upper(v_is_nullable) = 'NO', 'NOT NULL', ''),
' DEFAULT ', QUOTE(v_column_default));
PREPARE v_stmt FROM @stmt_text;
EXECUTE v_stmt;
DEALLOCATE prepare v_stmt;
END IF;
END LOOP;
CLOSE v_cur;
END
这种方法还有其他问题:
但是,无论如何,我不会用游标做到这一点。有一种更简单的方法:
CREATE PROCEDURE synchronize_tables(p_table1 VARCHAR(64),
p_table2 VARCHAR(64), p_schema_name VARCHAR(64))
BEGIN
SELECT CONCAT(
'ALTER TABLE `', C1.TABLE_SCHEMA, '`.`', p_table2, '` ',
GROUP_CONCAT(CONCAT(
'ADD COLUMN `', C1.COLUMN_NAME, '` ', C1.COLUMN_TYPE,
IF(C1.IS_NULLABLE='NO', ' NOT NULL ', ''),
IF(C1.COLUMN_DEFAULT IS NULL, '',
CONCAT(' DEFAULT ', QUOTE(C1.COLUMN_DEFAULT)))
) SEPARATOR ', '
)
) INTO @stmt_text
FROM INFORMATION_SCHEMA.COLUMNS AS C1
LEFT OUTER JOIN INFORMATION_SCHEMA.COLUMNS AS C2
ON C1.TABLE_SCHEMA=C2.TABLE_SCHEMA
AND C2.TABLE_NAME=p_table2
AND C1.COLUMN_NAME=C2.COLUMN_NAME
WHERE C1.TABLE_SCHEMA=p_schema_name AND C1.TABLE_NAME=p_table1
AND C2.TABLE_SCHEMA IS NULL;
PREPARE v_stmt FROM @stmt_text;
EXECUTE v_stmt;
DEALLOCATE prepare v_stmt;
END
这使一个 ALTER TABLE添加所有列。它划分标识符。它更好地处理NOT NULL。
但即便是我的解决方案仍有问题:
完全同步数据库结构是一项非常复杂的任务。我建议尝试使用MySQL的存储过程语言更难以完成一项艰巨的任务。
MySQL对存储过程的实现很糟糕。
我经常向习惯使用Oracle或Microsoft SQL Server程序的开发人员推荐远离MySQL存储过程。