删除没有主键的重复项
此处想要删除具有重复列值(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和受害者之间执行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。所有其他 - 噗。
再次让我的状态,如果一个记录有没有有效记录,但是重复,然后所有副本该记录的会被杀死 - 不会有幸存者。
为了避免这种情况,可以先将表示这种失败模式的行放入另一个临时表中,然后在删除后将它们重新插入到主表中(在上面的查询中,检查“哪个不应该返回”)使用交易可能是有序的)。
试试这个:
DELETE FROM TblProducts
WHERE Product IN
(
SELECT Product
FROM TblProducts
GROUP BY Product
HAVING COUNT(*) > 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
)
嗨,谢谢你会试试这个,你认为这是Compact版吗? – Sypress 2012-07-15 19:36:19
在语言方面应该不重要,如果需要,可以通过脚本非常轻松地添加标识行。 – 2012-07-15 23:24:35
数据库有多大。我们在这里谈论数百万行吗?十亿? – 2012-07-15 11:44:10
大约有200,000条记录,其中有3000个重复,但并不多:D – Sypress 2012-07-15 11:44:54
当您有两条记录的产品数据相同但其他列中的数据不同时,您如何知道哪一条是正确的? – 2012-07-15 11:45:37