简体   繁体   中英

How to use trigger in MySql to make foreign key

I want to use trigger to make foreign key in MySql. I have the following tables: 1) 'content' table: teacher_id varchar(20) sub_id varchar(20) path varchar(100) file_name varchar(100)

2) 'teacher' table: teacher_id varchar(20) teacher_name varchar(45)

and I am using the following code for trigger(delimiter //):

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//

The problem is, when am trying to insert in content table for a teacher_id which is not present in teacher table, I get the following error:

1172 - Result consists of more than one row

What can I do to make it work fine, or any other way i can use trigger to make foreign keys? Thank you in advance!

While it is not clear what exactly you intend with the statement "use trigger to make foreign key", your current issue is that SELECT INTO cannot be used in queries that return more than one result.

SELECT 1 INTO has_row FROM teacher INNER JOIN content ON content.teacher_id=teacher.teacher_id; returns EVERY match between the two tables.

If you were trying to check if teacher contains the teacher_id value being used in the new content record, you should just be able to drop the JOIN clause completely and just query like so:

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

While this is an oldish question I would like to provide some insight for future searchers on how one might deal with such issue.

In a recent project I was unable to use InnoDB but had to use the MyISAM engine (in reality it was MariaDB's Aria engine) for a database transfer which contained foreign keys.

I opted for implementing foreign keys using triggers as described here.

A great intro into the subject is provided here: https://dev.mysql.com/tech-resources/articles/mysql-enforcing-foreign-keys.html

However, I will outline my solution as I found some thing not fully workable for me in the above. Eg Any update to a parent table was completely prohibited in their "restrict" example when a foreign child key existed even though the child was not affected.

For demonstration I use the following table definitions and test data:

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

Prevent inserts into a child table when no corresponding linked parent entry exists:

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 ;

Prevent updates to a child table where it would unlink a child record:

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 ;

Cascading updates to the child table when the parent is updated:

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 ;

Cascade deletes to the child table when a parent is deleted:

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 ;

Sometime you don't want to cascade but restrict. In this case use the following instead:

Restrict parent updates to the child table:

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 ;

Restrict parent deletes from the child table:

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 ;

Hope this helps someone.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM