如果您有两个具有相同产品列的不同记录,那么您可以使用某些标准选择不想要的记录,例如,
CREATE TABLE victims AS
SELECT MAX(entryDate) AS date, Product, COUNT(*) AS dups FROM ProductsTable WHERE ...
GROUP BY Product HAVING dups > 1;
然后,您可以在ProductTable和受害者之间执行DELETE JOIN。
或者您也可以选择仅产品,然后对其他JOIN条件执行DELETE,例如具有无效的CustomerId或EntryDate NULL或其他任何其他条件。如果你的知道有一个且只有一个有效的产品副本,并且所有其他产品都可以通过无效数据识别,那么这将起作用。
假设你改为拥有IDENTICAL记录(或者你有两个完全相同或不相同的产品,或者对于某些产品你可能有几个产品,你不知道是哪一个产品)。您运行完全相同的查询。然后,在ProductsTable和SELECT DISTINCT上运行SELECT查询,所有匹配要被重复删除的产品代码的产品,按产品分组,以及为所有字段选择合适的聚合函数(如果相同,则聚合应该执行;否则,我通常尝试使用MAX或MIN)。这将为每个产品“精确保存”一行。
此时您运行DELETE JOIN并杀死所有重复的产品。然后,只需将保存的和重复的子集重新导入主表。
当然,在DELETE JOIN和INSERT SELECT之间,您将使DB处于不稳定状态,并且至少有一个重复的所有产品都会消失。
另一种方式应在MySQL的工作:
-- Create an empty table
CREATE TABLE deduped AS SELECT * FROM ProductsTable WHERE false;
CREATE UNIQUE INDEX deduped_ndx ON deduped(Product);
-- DROP duplicate rows, Joe the Butcher's way
INSERT IGNORE INTO deduped SELECT * FROM ProductsTable;
ALTER TABLE ProductsTable RENAME TO ProductsBackup;
ALTER TABLE deduped RENAME TO ProductsTable;
-- TODO: Copy all indexes from ProductsTable on deduped.
注意:以上的方式不起作用如果要区分“好记”和“无效的重复”。它只适用于你有冗余DUPLICATE记录,或者如果你不在乎哪你保留的行和你丢弃!
编辑: 你说“重复”有无效的字段。在这种情况下,你可以修改上面分拣招:
SELECT * FROM ProductsTable ORDER BY Product, FieldWhichShouldNotBeNULL IS NULL;
那么,如果你只有一排产品,一切都很好,它会被选中。如果你有更多的,那么(FieldWhichShouldNeverBeNull IS NULL)是FALSE的那个(也就是FieldWhichShouldNeverBeNull实际上不应该为null的那个实际上不应该是null)将被首先选择并被插入。所有其他人都会因IGNORE条款而无声地反弹产品的独特性。不是一个非常好的方式来做到这一点(并检查我没有在我的条款中混淆真假),但它应该工作。
编辑
其实更多的新的答案
的这是一个简单的表来说明问题
CREATE TABLE ProductTable (Product varchar(10), Description varchar(10));
INSERT INTO ProductTable VALUES ('CBPD10', 'C-Beam Prj');
INSERT INTO ProductTable VALUES ('CBPD11', 'C Proj Mk2');
INSERT INTO ProductTable VALUES ('CBPD12', 'C Proj Mk3');
没有指数着呢,没有主键。我们仍然可以将产品声明为主键。
但是发生了一些不好的事情。两个新记录进入,并且都有NULL描述。
然而,第二个是有效的产品,因为在此之前我们对CBPD14一无所知,因此我们不希望完全失去此记录。我们做虽然想摆脱虚假的CBPD10。
INSERT INTO ProductTable VALUES ('CBPD10', NULL);
INSERT INTO ProductTable VALUES ('CBPD14', NULL);
粗鲁DELETE从ProductTable WHERE描述IS NULL是不可能的,它会杀死CBPD14这是不重复的。
所以我们这样做。首先得到重复列表:
SELECT Product, COUNT(*) AS Dups FROM ProductTable GROUP BY Product HAVING Dups > 1;
我们假设:“每组坏记录至少有一个好记录”。
我们通过设定相反的方向并查询它来检查这个假设。如果所有都是协同的,我们期望这个查询不返回任何内容。
SELECT Dups.Product FROM ProductTable
RIGHT JOIN (SELECT Product, COUNT(*) AS Dups FROM ProductTable GROUP BY Product HAVING Dups > 1) AS Dups
ON (ProductTable.Product = Dups.Product
AND ProductTable.Description IS NOT NULL)
WHERE ProductTable.Description IS NULL;
为了进一步验证,我插入了代表这种失败模式的两条记录;现在我确实希望上面的查询返回新的代码。
INSERT INTO ProductTable VALUES ("AC5", NULL), ("AC5", NULL);
现在的 “检查” 查询确实返回,
AC5
所以,复本的产生看起来不错。
我现在着手删除所有不是有效的重复记录。如果有重复的有效记录,除非发现一些条件,否则它们将保持复制状态,在它们之间区分一个“良好”记录并声明所有其他记录“无效”(可能重复该过程的字段与描述不同)。
但是,有一个问题。 目前,您无法从表中删除并从子查询(http://dev.mysql.com/doc/refman/5.0/en/delete.html)中的同一表中进行选择。因此,需要一点点的解决方法:
CREATE TEMPORARY TABLE Dups AS
SELECT Product, COUNT(*) AS Duplicates
FROM ProductTable GROUP BY Product HAVING Duplicates > 1;
DELETE ProductTable FROM ProductTable JOIN Dups USING (Product)
WHERE Description IS NULL;
现在这将删除所有无效的记录,只要他们出现在复本表。
因此,我们的CBPD14记录将保持不变,因为它不会出现在那里。 CBPD10的“良好”记录将保持不变,因为它的描述不是NULL。所有其他 - 噗。
再次让我的状态,如果一个记录有没有有效记录,但是重复,然后所有副本该记录的会被杀死 - 不会有幸存者。
为了避免这种情况,可以先将表示这种失败模式的行放入另一个临时表中,然后在删除后将它们重新插入到主表中(在上面的查询中,检查“哪个不应该返回”)使用交易可能是有序的)。
数据库有多大。我们在这里谈论数百万行吗?十亿? – 2012-07-15 11:44:10
大约有200,000条记录,其中有3000个重复,但并不多:D – Sypress 2012-07-15 11:44:54
当您有两条记录的产品数据相同但其他列中的数据不同时,您如何知道哪一条是正确的? – 2012-07-15 11:45:37