2012-07-15 135 views
5

此处想要删除具有重复列值(Product)的行,然后将其用作主键删除没有主键的重复项

该列的类型为nvarchar,我们不希望对一个产品有2行。 数据库是一个大型的数据库,大约有数千行我们需要删除。

在查询所有重复项的过程中,我们希望保留第一项并删除第二项作为重复项。

目前还没有主键,我们希望在删除重复项之后进行。 然后Product columm可能是我们的主要关键。

数据库是SQL Server CE。

我试了几种方法,而且大多得到类似的错误:

有解析查询时出现错误。 [令牌行号= 2,令牌行偏移= 1,令牌在错误= FROM]

其中我尝试的方法:

DELETE FROM TblProducts 
FROM TblProducts w 
    INNER JOIN (
      SELECT Product 
      FROM TblProducts 
      GROUP BY Product 
      HAVING COUNT(*) > 1 
      )Dup ON w.Product = Dup.Product 

的首选方法试图学习和调整我的代码用类似的东西 (它尚未纠正):

SELECT Product, COUNT(*) TotalCount 
FROM TblProducts 
GROUP BY Product 
HAVING COUNT(*) > 1 
ORDER BY COUNT(*) DESC 

-- 
;WITH cte -- These 3 lines are the lines I have more doubt on them 
    AS (SELECT ROW_NUMBER() OVER (PARTITION BY Product 
             ORDER BY (SELECT 0)) RN 
     FROM Word) 
DELETE FROM cte 
WHERE RN > 1 
+0

数据库有多大。我们在这里谈论数百万行吗?十亿? – 2012-07-15 11:44:10

+0

大约有200,000条记录,其中有3000个重复,但并不多:D – Sypress 2012-07-15 11:44:54

+0

当您有两条记录的产品数据相同但其他列中的数据不同时,您如何知道哪一条是正确的? – 2012-07-15 11:45:37

回答

4

如果您有两个具有相同产品列的不同记录,那么您可以使用某些标准选择不想要的记录,例如,

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。所有其他 - 噗。

再次让我的状态,如果一个记录有没有有效记录,但重复,然后所有副本该记录的会被杀死 - 不会有幸存者

为了避免这种情况,可以先将表示这种失败模式的行放入另一个临时表中,然后在删除后将它们重新插入到主表中(在上面的查询中,检查“哪个不应该返回”)使用交易可能是有序的)。

+0

将尝试并尽快给出一个反馈,谢谢 – Sypress 2012-07-15 12:00:46

+0

朋友,我试图根据你的方法,如果可能的话,请提供样本3-5行代码基于你思考和总结。将不胜感激。 – Sypress 2012-07-15 14:55:57

+1

可以。我会包括一个小例子,以确定我理解你的问题。删除大量的数据总是让我感到紧张:-) – LSerni 2012-07-15 15:38:02

-2

试试这个:

DELETE FROM TblProducts  
WHERE Product IN 
     (
    SELECT Product 
    FROM TblProducts 
    GROUP BY Product 
    HAVING COUNT(*) > 1) 

这有缺陷,它会删除所有记录与一个重复的产品。你可能想要做的只是删除给定产品的每一组记录中的一个。将所有重复项首先复制到单独的表中,然后以某种方式从该表中删除重复项,然后应用上述内容,然后将剩余的产品复制回原始表可能是值得的。

+0

将尝试请尽快给我一个反馈,谢谢 – Sypress 2012-07-15 12:00:40

+0

这个执行速度真的很慢!它差不多半个小时了...... – Sypress 2012-07-15 12:34:21

+2

Walter,如果你知道它会把表中的每一个产品都打开,你甚至会发布代码有重复(包括该操作需要保留的重复)?希望Sypress或者在执行OR之前阅读代码下面的段落,或者有最近的完整备份... – brian 2012-07-15 19:39:39

1

通过编写旧表并重命名来创建新表。还要将旧表中的所有对象(索引等)编写为新的。将保持器插入新表中。如果您的数据库处于批量记录或简单恢复模式,则该操作将被最小化记录。放下旧桌子,然后将新桌子重新命名为旧名称。

这比删除的好处是插入可以被最小化记录。删除做双重工作,因为不仅数据被删除,而且删除必须写入事务日志。对于大表,插入最少的插入将比删除快得多。

1

如果它不是那么大,你有一些宕机时间,并且你有Sql服务器管理工​​作室,你可以使用GUI在桌子上放一个标识字段。现在你有像CTE这样的情况,除了行本身是真正独特的。所以现在你可以做到以下几点

SELECT MIN(table_a.MyTempIDField) 
FROM 
table_a lhs 
join table_1 rhs 
on lhs.field1 = rhs.field1 
and lhs.field2 = rhs.field2 [etc] 
WHERE 
table_a.MyTempIDField <> table_b.MyTempIDField 
GROUP BY 
lhs.field1, rhs.field2 etc 

这给你所有'好'重复。现在你可以用DELETE FROM查询来包装这个查询。

DELETE FROM lhs 
FROM table_a lhs 
join table_b rhs 
on lhs.field1 = rhs.field1 
and lhs.field2 = rhs.field2 [etc] 
WHERE 
lhs.MyTempIDField <> rhs.MyTempIDField 
and lhs.MyTempIDField not in (

SELECT MIN(lhs.MyTempIDField) 
FROM 
table_a lhs 
join table_a rhs 
on lhs.field1 = rhs.field1 
and lhs.field2 = rhs.field2 [etc] 
WHERE 
lhs.MyTempIDField <> rhs.MyTempIDField 
GROUP BY 
    lhs.field1, lhs.field2 etc 
) 
+0

嗨,谢谢你会试试这个,你认为这是Compact版吗? – Sypress 2012-07-15 19:36:19

+0

在语言方面应该不重要,如果需要,可以通过脚本非常轻松地添加标识行。 – 2012-07-15 23:24:35