2017-04-23 68 views
1

我试过以下内容,但由于某种原因@check参数设置为我试图检查的约束的值,即使约束不存在。如何检测是否在mysql存储过程中添加了外键?

任何想法为什么?

CREATE PROCEDURE AddForeignKey(
    IN constraint_name varchar(64), 
    IN foreign_key_column_name varchar(64), 
    IN table_name varchar(64), 
    IN database_name varchar(64), 
    IN foreign_table_key_name varchar(64), 
    IN foreign_table_name varchar(64)) 

BEGIN 
    set @sql = CONCAT('ALTER TABLE ', database_name, '.', table_name, 
      ' ADD CONSTRAINT ', constraint_name, 
       ' FOREIGN KEY (', foreign_key_column_name, ')' 
       ' REFERENCES ', foreign_table_name, ' (', foreign_table_key_name, ');') ; 

    set @dbname = database_name; 

    set @fkname = constraint_name; 

    set @check = ''; 

    SELECT 
     CONSTRAINT_NAME 
    INTO 
     @check 
    FROM 
     INFORMATION_SCHEMA.TABLE_CONSTRAINTS 
    WHERE 
     CONSTRAINT_SCHEMA = @dbname 
    AND 
     CONSTRAINT_NAME = @fkname 
    AND 
     CONSTRAINT_TYPE = 'FOREIGN KEY' 
    LIMIT 1; 

    IF @check != @fkname 
    THEN 
     prepare stmt from @sql; 
     execute stmt; 
    ELSE 
     insert into errors(message) values (CONCAT ('Check: ', @check, ' DB:', @dbname, ' FK:', @fkname, ' Could not execute statement: ', @sql)); 
    END IF; 
END 
+0

我无法重现该问题。请参阅[db-fiddle](https://www.db-fiddle.com/f/huwJ99DrjLbaLPqKCvcBhB/0)。 – wchiquito

+0

我没有使用MySQL作为root用户。我也使用MySQL 5.6。 – rstackhouse

回答

1

你只是比较列CONSTRAINT_NAME是不足以防止外键重复创作。

您必须添加REFERENTIAL_CONSTRAINTS表并添加更多列进行比较。

我也改变了变量名,以避免不必要的变量名:

CREATE PROCEDURE AddForeignKey(
    IN p_constraint_name varchar(64), 
    IN foreign_key_column_name varchar(64), 
    IN p_table_name varchar(64), 
    IN p_database_name varchar(64), 
    IN foreign_table_key_name varchar(64), 
    IN foreign_table_name varchar(64)) 

BEGIN 
    set @sql = CONCAT('ALTER TABLE `', p_database_name, '`.`', p_table_name, 
     '` ADD CONSTRAINT `', p_constraint_name, 
      '` FOREIGN KEY (`', foreign_key_column_name, '`)' 
      ' REFERENCES `', foreign_table_name, '` (`', foreign_table_key_name, '`);') ; 

    set @check = ''; 

    SELECT 
     TABLE_CONSTRAINTS.CONSTRAINT_NAME 
     INTO @check 
    FROM 
    INFORMATION_SCHEMA.TABLE_CONSTRAINTS 
      INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS 
      ON TABLE_CONSTRAINTS.CONSTRAINT_SCHEMA = REFERENTIAL_CONSTRAINTS.CONSTRAINT_SCHEMA 
       AND TABLE_CONSTRAINTS.CONSTRAINT_NAME = REFERENTIAL_CONSTRAINTS.CONSTRAINT_NAME 
       AND TABLE_CONSTRAINTS.TABLE_NAME = REFERENTIAL_CONSTRAINTS.TABLE_NAME 

    WHERE 
    TABLE_CONSTRAINTS.CONSTRAINT_SCHEMA = p_database_name 
     AND TABLE_CONSTRAINTS.CONSTRAINT_NAME = p_constraint_name 
     AND TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'FOREIGN KEY' 
    AND TABLE_CONSTRAINTS.TABLE_NAME = p_table_name 
     and REFERENTIAL_CONSTRAINTS.REFERENCED_TABLE_NAME = foreign_table_name 
    LIMIT 1; 

    IF @check <> p_constraint_name 
    THEN 
     prepare stmt from @sql; 
     execute stmt; 
    ELSE 
     insert into errors(message) values (CONCAT ('Check: ', @check, ' DB:', @dbname, ' FK:', 
     @fkname, ' Could not execute statement: ', @sql)); 
    END IF; 

END 
+0

我不相信你已经回答了我的问题。我会做出你所建议的改变,看看是否有什么影响,但我怀疑它。你对terser SQL和更彻底的检查有公平的观点,但这些并不是我遇到的问题。另外,在ELSE条件下插入将失败。 – rstackhouse

+0

告诉我如果在测试我的答案后解决您的问题 –

+0

不太确定如何,但这似乎已修复它。 – rstackhouse