【问题标题】:How to use trigger in MySql to make foreign key如何在MySql中使用触发器来制作外键
【发布时间】:2015-11-30 17:11:21
【问题描述】:

我想在 MySql 中使用触发器来制作外键。我有以下表格: 1)“内容”表: 教师 ID varchar(20) sub_id varchar(20) 路径 varchar(100) 文件名 varchar(100)

2) “教师”表: 教师 ID varchar(20) 教师名 varchar(45)

我正在使用以下代码作为触发器(分隔符 //):

CREATE TRIGGER fk_content_teacher_temp BEFORE INSERT ON `content`
FOR EACH ROW
BEGIN
DECLARE has_row TINYINT;
SET has_row = 0;
SELECT 1 INTO has_row FROM `teacher` INNER JOIN `content` ON content.teacher_id=teacher.teacher_id;
IF has_row=0 THEN
INSERT error_msg VALUES ('Foreign Key Constraint Violated!');
END IF;
END//

问题是,当我尝试在内容表中插入教师表中不存在的教师 ID 时,我收到以下错误:

1172 - 结果包含多行

我该怎么做才能使它正常工作,或者我可以使用触发器制作外键的任何其他方式? 提前谢谢!

【问题讨论】:

  • 可能不是。因为这里的主要动机是使用触发器来实现外键,而不仅仅是像在您建议的问题@Drew 中那样使用“加入”的概念
  • 这就是@uu 下面所说的,你同意了。因为这您收到错误 1172 的原因。如果您认为这不是您收到 1172 的原因,请告诉我们您认为原因是什么。

标签: mysql database triggers foreign-key-relationship


【解决方案1】:

虽然不清楚您对“使用触发器生成外键”语句的确切意图,但您当前的问题是 SELECT INTO 不能用于返回多个结果的查询。

SELECT 1 INTO has_row FROM teacher INNER JOIN content ON content.teacher_id=teacher.teacher_id; 返回两个表之间的每个匹配项。

如果您尝试检查 teacher 是否包含在新的 content 记录中使用的 teacher_id 值,您应该能够完全删除 JOIN 子句并像这样查询:

SELECT 1 INTO has_row FROM `teacher` WHERE `teacher_id` = NEW.`teacher_id`;

【讨论】:

  • 非常感谢!那确实奏效了。我试图制作一个触发器,它允许我们添加与特定“teacher_id”相对应的“内容”,前提是该特定的“teacher_id”已经存在于“teacher”表中。很好的帮助!
  • @AnkitGupta 据我所知,触发器不能阻止插入不存在的teacher_id 值;但是,您可以使用它在进入内容表之前将暂定的teacher_id 值更改为预设的“无效教师”值。
【解决方案2】:

虽然这是一个古老的问题,但我想为未来的搜索者提供一些关于如何处理此类问题的见解。

在最近的一个项目中,我无法使用 InnoDB,但不得不使用 MyISAM 引擎(实际上是 MariaDB 的 Aria 引擎)进行包含外键的数据库传输。

我选择使用此处所述的触发器来实现外键。

此处提供了对该主题的精彩介绍:https://dev.mysql.com/tech-resources/articles/mysql-enforcing-foreign-keys.html

但是,我将概述我的解决方案,因为我在上面发现了一些对我来说并不完全可行的东西。例如。当外部子键存在时,即使子表没有受到影响,在他们的“限制”示例中也完全禁止对父表进行任何更新。

为了演示,我使用了下表定义和测试数据:

CREATE TABLE `__parent` (`id` int UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`)) ENGINE=`Aria`;
CREATE TABLE `__child` (`id` int UNSIGNED NOT NULL AUTO_INCREMENT,`parent_id` int UNSIGNED, PRIMARY KEY (`id`), INDEX `parent_id_idx` USING BTREE (`parent_id`) ) ENGINE=`Aria`;
INSERT INTO __parent VALUES (1), (2), (3);
INSERT INTO __child VALUES (1,1), (2,2), (3,1), (4,2), (5,3), (6,1);

防止在不存在对应的链接父条目时向子表中插入

DELIMITER //
CREATE TRIGGER __before_insert_child BEFORE INSERT ON __child FOR EACH ROW
BEGIN
IF (SELECT COUNT(*) FROM __parent WHERE __parent.id=new.parent_id) = 0 THEN
    SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO = 30001, MESSAGE_TEXT = 'Can\'t insert record. Foreign parent key does not exist!';
END IF;
END //
DELIMITER ;

阻止更新到子表,它会取消链接子记录:

DELIMITER //
CREATE TRIGGER __before_update_child BEFORE UPDATE ON __child FOR EACH ROW
BEGIN
IF (SELECT COUNT(*) FROM __parent WHERE __parent.id = new.parent_id) = 0 THEN
    SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO = 30001, MESSAGE_TEXT = 'Can\'t update record. Foreign parent key does not exist!';
END IF;
END //
DELIMITER ;

在更新父表时对子表进行级联更新

DELIMITER //
CREATE TRIGGER __after_update_parent AFTER UPDATE ON __parent FOR EACH ROW
BEGIN
    UPDATE __child SET __child.parent_id=new.id WHERE __child.parent_id=old.id;
END //
DELIMITER ;

级联删除在删除父表时对子表:

DELIMITER //
CREATE TRIGGER __after_delete_parent AFTER DELETE ON __parent FOR EACH ROW
BEGIN
    DELETE FROM __child WHERE __child.parent_id=old.id;
END;
END //
DELIMITER ;

有时您不想级联但要限制。在这种情况下,请改用以下内容:

限制父更新到子表:

DELIMITER //
CREATE TRIGGER __before_update_parent BEFORE UPDATE ON __parent FOR EACH ROW
BEGIN
IF ( old.id <> new.id AND (SELECT COUNT(*) FROM __child WHERE __child.parent_id = old.id) <> 0 ) THEN
    SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO = 30001, MESSAGE_TEXT = 'Can\'t update record. Foreign key updates to child table restricted!';
END IF;
END //
DELIMITER ;

限制父级删除子表:

DELIMITER //
CREATE TRIGGER __before_delete_parent BEFORE DELETE ON __parent FOR EACH ROW
BEGIN
IF ( SELECT COUNT(*) FROM __child WHERE __child.parent_id = old.id) <> 0 THEN
    SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO = 30001, MESSAGE_TEXT = 'Can\'t delete record. Foreign key exists in child table!';
END IF;
END //
DELIMITER ;

希望这对某人有所帮助。

【讨论】:

  • 如果我们想要 100% 准确的行为,外键约束消息的 SQLSTATE 值是 23000。45xxx 是一个通用的失败代码。
猜你喜欢
  • 2012-01-18
  • 2017-03-31
  • 2021-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-20
  • 1970-01-01
  • 2012-11-15
相关资源
最近更新 更多