【问题标题】:How do I add unique constraint for 2 columns without deleting the existing duplicated records?如何在不删除现有重复记录的情况下为 2 列添加唯一约束?
【发布时间】:2013-07-21 02:48:05
【问题描述】:

我有这张表,col1 col2 col3,col1是主键,现在我想给col2和col3添加唯一约束,我用alter table add constraint,但是好像表里已经有重复的记录了,所以我必须删除它们才能使其正常工作,但是,如果我需要保留所有现有记录,我如何确保新添加的记录在 col2 和 col3 中是唯一约束?

【问题讨论】:

  • 如果表仍然包含无效数据,那么约束有什么好处。修复数据,然后添加约束。
  • 但如果你保留现有记录,它就不是唯一的。这意味着使用旧记录的每个查询都不会执行您想要的操作。那么,你为什么要做这样的事情呢?
  • 您可以通过更新修复重复项(在 col2 和 col3 之后添加“-col1”以使其唯一。)通过对选定重复项进行更新,您只需对重复的行执行此操作.

标签: mysql


【解决方案1】:

如果我理解正确,您想保留当前的重复项,但要确保不添加任何新的重复项,对吗?我个人会遵循上面的 cmets,但如果这不能满足您的业务需求,我理解。

不幸的是,约束不能满足您描述的需求。您可能希望控制插入数据的软件中的重复逻辑,而不是在表级别。 例如,如果表中尚不存在 val3,则以下内容只会添加该行。说得通?

INSERT INTO MyTable (col1, col2, col3, col4)
VALUES (val1, val2, val3, val4) WHERE NOT col3 = val3;

或者您可以触发 INSTEAD OF 触发器。 SQL 有一个特殊的临时“插入”表,您可以在输入值之前与它进行比较。所以您说,“插入这些值,但等等!仅当临时“已插入”表中的行与 MyTable 中的现有行不匹配时才插入它们。”

CREATE TRIGGER no_duplicates_in_MyTable ON MyTable
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON
IF (NOT EXISTS (SELECT M.val3
      FROM MyTable M, inserted I
      WHERE M.val3 = I.val3))
   INSERT INTO MyTable
      SELECT val1, val2, val3, val4
      FROM inserted
END

或者您可以使用 AFTER INSERT 触发器在表级别处理逻辑,但这有点不稳定。在某些情况下,您可以删除现有的重复项。像这样的东西;

CREATE TRIGGER delete_duplicates_MyTable
AFTER INSERT ON MyTable
BEGIN
   DELETE FROM MyTable 
   WHERE val3 NOT IN (SELECT MIN(val3) 
                             FROM MyTable 
                             WHERE val1 = new. val1)
   AND val1 = new. val1;
END;

如果您有任何问题或这不符合您的业务需求,请告诉我。

编辑:@Rik 是正确的,这些是 INSERT 触发器,因此它们不适用于更新,允许任何人的代码将值更改为重复值。尝试更新触发器。您可以随意使用这些将 INSERT 更改为 UPDATE。

【讨论】:

  • 非常感谢,我尝试了临时表,不幸的是mysql没有默认的“插入”表,所以我改用NEW.F1 F2 F3,但仍然出现语法错误
  • CREATE TRIGGER no_duplicates_in_version BEFORE INSERT ON version FOR EACH BEGIN IF (NOT EXISTS (SELECT version.product_id FROM version WHERE version.produc_id = NEW.product_id)) INSERT INTO version (id, name, product_id)值(NEW.id、NEW.name、NEW.product_id)END
  • 我不知道您是否可以这样更改插入内容。这应该可以工作(如果 Mysql 是正确的版本)CREATE TRIGGER no_duplicates_in_version BEFORE INSERT ON test FOR EACH ROW BEGIN DECLARE num_rows INTEGER; DECLARE msg VARCHAR(255); SELECT COUNT(*) INTO num_rows FROM test WHERE product_id = NEW.product_id; IF (num_rows>0) THEN set msg = concat('Duplicate Error: Trying to insert a duplicate: ', cast(new.product_id as char)); signal sqlstate '45000' set message_text = msg; end if; END// 如果检测到重复,它会抛出异常。 (注意更改的 // 终止符)
  • B.T.W.这仅适用于插入。没有什么能阻止您将 product_id 更改为副本!第二:您可以在注释中插入代码,代码周围有反引号。 (不漂亮,但它有效)
  • 我花了一整天的时间在这个触发器上,然后我做了一些研究,然后按照 BClaydon 告诉我的第一个方法,在我的 PHP 代码中添加了一些代码,它成功了!再次感谢大家,我从你们那里学到了很多!谢谢 Rik,我会尝试并告诉你结果。
【解决方案2】:

就我个人而言,我会和其他人一起去。您应该真正修复数据,然后添加约束。

但是,为了解决这个问题,可能的解决方案是使用 MERGE 引擎。基本上,您会将您现在拥有的数据保存在一个表中,并创建一个几乎相同的第二个表。唯一不同的是,这次索引是唯一的。

看这个例子:

CREATE TABLE t1 (
   a INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
   message CHAR(20),
   key whatever (message)
   ) ENGINE=MyISAM;

CREATE TABLE t2 (
   a INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
   message CHAR(20),
   unique key whatever (message)
   ) ENGINE=MyISAM;

INSERT INTO t1 (message) VALUES ('Testing'),('Testing'),('t1');
INSERT INTO t2 (message) VALUES ('Testing'),('table'),('t2');

CREATE TABLE total (
   a INT NOT NULL AUTO_INCREMENT,
   message CHAR(20), INDEX(a))
   ENGINE=MERGE UNION=(t1,t2) INSERT_METHOD=LAST;

select * from total; 
/*Here you can see, that there are duplicate values*/

insert into total (message) values ('Testing');
/*but this results in a duplicate key error*/

insert into total (message) values ('it work\'s');
/*whereas this doesn't*/

阅读更多关于 MERGE 引擎的信息here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-26
    • 1970-01-01
    • 1970-01-01
    • 2017-02-23
    • 1970-01-01
    • 2018-02-07
    • 2023-03-24
    • 2013-05-11
    相关资源
    最近更新 更多