繁体   English   中英

删除没有主键的重复项

[英]Delete duplicates with no primary key

这里要删除具有重复列值( 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

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

 CREATE TABLE victims AS
     SELECT MAX(entryDate) AS date, Product, COUNT(*) AS dups FROM ProductsTable WHERE ...
     GROUP BY Product HAVING dups > 1;

然后你可以在ProductTable和Victims之间进行DELETE JOIN。

或者您也可以选择“仅产品”,然后对其他某些JOIN条件执行DELETE,例如,具有无效的CustomerId或EntryDate NULL或其他任何内容。 如果您知道只有一个有效的Product副本,而其他所有副本都可以被无效数据识别,则此方法有效。

假设你有IDENTICAL记录(或者你有相同和不相同的,或者你可能有几个产品的dupe,你不知道哪个)。 您运行完全相同的查询。 然后,在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实际上不是空的那个)将首先被选中并插入。 由于IGNORE条款的原因,所有其他人都会默默地反弹,而不是产品的唯一性。 这不是一个非常漂亮的方式(并且检查我在我的条款中没有将false与false混合!),但它应该有效。

编辑
实际上更多是一个新的答案

这是一个简单的表来说明问题

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

还没有索引,也没有主键。 我们仍然可以将Product声明为主键。

但是有些事情发生了。 两个新记录进入,并且都有NULL描述。

然而,第二个是有效的产品,因为我们之前对CBPD14一无所知,因此我们不想完全失去这个记录。 我们确实希望摆脱虚假的CBPD10。

INSERT INTO ProductTable VALUES ( 'CBPD10', NULL );
INSERT INTO ProductTable VALUES ( 'CBPD14', NULL );

一个粗鲁的DELETE FROM ProductTable WHERE描述IS NULL是不可能的,它会杀死CBPD14,这不是重复的。

所以我们这样做。 首先获取重复列表:

SELECT Product, COUNT(*) AS Dups FROM ProductTable GROUP BY Product HAVING Dups > 1;

我们假设:“每组坏记录至少有一条好的记录”。

我们通过设置相反的方式并查询它来检查这个假设。 如果所有都是copacetic,我们希望此查询不返回任何内容。

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

因此,Dups的产生看起来很好。

我现在开始删除所有重复记录无效的。 如果存在重复的有效记录,则它们将保持重复,除非可以找到某些条件,区分其中一个“好”记录并声明所有其他“无效”(可能用不同于字段的字段重复该过程)。

但是,有一个问题。 目前,您无法从表中删除并从子查询中的同一个表中进行选择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;

现在,这将删除所有无效记录,前提是它们出现在Dups表中。

因此,我们的CBPD14记录将保持不变,因为它不会出现在那里。 CBPD10的“好”记录将保持不变,因为它的描述为NULL并不正确。 所有其他人 - 噗。

让我再次声明,如果一条记录没有有效记录,但 它是重复的,那么该记录的所有副本都将被杀死 - 没有幸存者

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

通过编写旧表并重命名来创建新表。 还要将旧表中的所有对象(索引等..)编写为新表。 将守护者插入新表中。 如果您的数据库是批量记录或简单恢复模型,则此操作将被最小化记录。 删除旧表,然后将新表重命名为旧名称。

相比删除的优点是插入可以最小化记录。 删除会进行双重工作,因为不仅会删除数据,还必须将删除写入事务日志。 对于大表,最少记录的插入将比删除快得多。

如果它不是那么大并且你有一些停机时间,并且你有Sql Server Management工作室,你可以使用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
)

尝试这个:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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