使用MySQL,我有这些表:
Person
----------
id (PK)
name
...
Person_Association
----------
id_Person1 (FK Person.id)
id_Person2 (FK Person.id)
我希望每个Person_Association是唯一的:如果(#1,#2)存在,则既不(#1,#2),也不(#2,#1)可以被插入。
要做到这一点,我添加了一个字段和触发Person_Association,就像这样:
Person_Association
----------
id_Person1 (FK Person.id)
id_Person2 (FK Person.id)
unique_id (PK)
CREATE TRIGGER `Person_Association_BINS` BEFORE INSERT ON `Person_Association` FOR EACH ROW
BEGIN
IF (new.id_Person1 < new.id_Person2) THEN
SET new.unique_id = CONCAT(new.id_Person1, '-', new.id_Person2);
ELSE
SET new.unique_id = CONCAT(new.id_Person2, '-', new.id_Person1);
END IF;
END
它的工作原理,但有一个更好的方式来做到这一点?
你最好扔掉触发,并有上唯一索引(id_Person1, id_Person2)
其实你也可以扔掉不必要的unique_id
,使(id_Person1, id_Person2)
主键。
然后插入这样的:
INSERT INTO your_table VALUES (LEAST($value1, $value2), GREATEST($value1, $value2));
要么
INSERT INTO your_table SELECT LEAST($value1, $value2), GREATEST($value1, $value2);
编辑:如果你真的坚持“SQL以确保(而不是应用层)”,我会写这样的(但仍然有你的UNIQUE_ID列删除,上唯一索引(id_Person1, id_Person2)
DELIMITER $$
CREATE TRIGGER `Person_Association_BINS` BEFORE INSERT ON `Person_Association` FOR EACH ROW
BEGIN
IF EXISTS(SELECT 1 FROM `Person_Association` WHERE id_Person1 = LEAST(NEW.id_Person1, NEW.id_Person2) AND id_Person2 = GREATEST(NEW.id_Person1, NEW.id_Person2)) THEN
SIGNAL SQLSTATE '23000' SET MESSAGE_TEXT='dup key error';
END IF;
END $$
DELIMITER ;
我仍然不想让餐桌上的触发器(从数据库管理员的观点,他们不仅经常造成混乱,但他们往往还碍事做管理员任务时)。 因此,要追求我的解决它的第一种方式,你也可以创建一个存储过程来为你做这个:
CREATE PROCEDURE insert_p_a(IN p_id1 INT, IN p_id2 INT)
INSERT INTO your_table SELECT LEAST(p_id1, p_id2), GREATEST(p_id1, p_id2);
并在应用层,你只是做
CALL insert_p_a($value1, $value2);
- 阅读更多关于信号在这里 。
- 这里就是为什么我选择23000作为SQLSTATE(但实际上它并不重要)