在搜索了stackoverflow.com之后,我发现了几个问题,询问如何删除重复项,但没有一个能解决速度问题。
在我的情况下,我有一个包含10列的表,其中包含500万个确切的行重复项。另外,我在10列中的9列中至少有一百万行具有重复。我目前的技术是(到目前为止) 3小时来删除这500万行。这是我的过程:
-- Step 1: **This step took 13 minutes.** Insert only one of the n duplicate rows into a temp table
select
MAX(prikey) as MaxPriKey, -- identity(1, 1)
a,
b,
c,
d,
e,
f,
g,
h,
i
into #dupTemp
FROM sourceTable
group by
a,
b,
c,
d,
e,
f,
g,
h,
i
having COUNT(*) > 1
接下来,
-- Step 2: **This step is taking the 3+ hours**
-- delete the row when all the non-unique columns are the same (duplicates) and
-- have a smaller prikey not equal to the max prikey
delete
from sourceTable
from sourceTable
inner join #dupTemp on
sourceTable.a = #dupTemp.a and
sourceTable.b = #dupTemp.b and
sourceTable.c = #dupTemp.c and
sourceTable.d = #dupTemp.d and
sourceTable.e = #dupTemp.e and
sourceTable.f = #dupTemp.f and
sourceTable.g = #dupTemp.g and
sourceTable.h = #dupTemp.h and
sourceTable.i = #dupTemp.i and
sourceTable.PriKey != #dupTemp.MaxPriKey
有关如何加快速度或更快速的提示吗?请记住,对于不完全重复的行,我将不得不再次运行它。
非常感谢。
更新:
我不得不停止步骤2从9小时开始跑步。
我尝试了OMG小马的方法,它只用了40分钟就结束了。
我用Andomar的批量删除尝试了我的第2步,它在我停止之前运行了9个小时。
更新:
使用一个较少的字段进行类似的查询以删除不同的重复项集,并且使用OMG Ponies的方法查询仅运行4分钟(8000行)。
我会在下一次机会尝试cte技术,但是,我怀疑OMG小马的方法很难被击败。
答案 0 :(得分:4)
EXISTS怎么样:
DELETE FROM sourceTable
WHERE EXISTS(SELECT NULL
FROM #dupTemp dt
WHERE sourceTable.a = dt.a
AND sourceTable.b = dt.b
AND sourceTable.c = dt.c
AND sourceTable.d = dt.d
AND sourceTable.e = dt.e
AND sourceTable.f = dt.f
AND sourceTable.g = dt.g
AND sourceTable.h = dt.h
AND sourceTable.i = dt.i
AND sourceTable.PriKey < dt.MaxPriKey)
答案 1 :(得分:4)
您可以在短时间内让原始桌子无法使用吗?
我认为最快的解决方案是创建一个没有重复项的新表。基本上是您使用临时表的方法,而是创建一个“常规”表。
然后删除原始表并将中间表重命名为与旧表同名。
答案 2 :(得分:3)
批量行删除的瓶颈通常是SQL Server必须构建的事务。通过将删除拆分为较小的事务,您可以大大加快速度。例如,要一次删除100行:
while 1=1
begin
delete top 100
from sourceTable
...
if @@rowcount = 0
break
end
答案 3 :(得分:1)
...基于上面的OMG Ponies评论,CTE方法更加紧凑。这种方法可以在您(无论出于何种原因)没有主键的表格上创造奇迹 - 您可以在所有列上拥有相同的行。
;WITH cte AS (
SELECT ROW_NUMBER() OVER
(PARTITION BY a,b,c,d,e,f,g,h,i ORDER BY prikey DESC) AS sequence
FROM sourceTable
)
DELETE
FROM cte
WHERE sequence > 1
答案 4 :(得分:0)
很多不同的事情。首先会像这样的工作(做一个选择o确保,甚至可能放入它自己的临时表,#recordToDelete):
delete
from sourceTable
left join #dupTemp on
sourceTable.PriKey = #dupTemp.MaxPriKey
where #dupTemp.MaxPriKey is null
接下来,您可以索引临时表,在prikey上放置索引
如果您要删除的临时表中有记录,则可以批量删除,这通常比通过删除锁定整个表更快。
答案 5 :(得分:0)
这是一个可以将两个步骤合并为一个步骤的版本。
WITH cte AS
( SELECT prikey, ROW_NUMBER() OVER (PARTITION BY a,b,c,d,e,f,g,h,i ORDER BY
prikey DESC) AS sequence
FROM sourceTable
)
DELETE
FROM sourceTable
WHERE prikey IN
( SELECT prikey
FROM cte
WHERE sequence > 1
) ;
顺便问一下,你有没有可以暂时删除的索引吗?
答案 6 :(得分:0)
如果您使用的是Oracle数据库,我最近发现,从总持续时间以及CPU消耗的角度来看,以下语句的效果最佳。 我已经用循环从几十行到数千行的不同数据大小执行了几次测试。我使用TKProf工具分析结果。
与上面的ROW_NUMBER()解决方案相比,此方法花费了原始时间的2/3,并消耗了大约50%的CPU时间。它似乎表现出线性关系,也就是说,无论任何输入数据大小,它都应给出相似的结果。
请随时给我您的反馈。我想知道是否有更好的方法。
DELETE FROM sourceTable
WHERE
ROWID IN(
-- delete all
SELECT ROWID
FROM sourceTable t
MINUS
-- but keep every unique row
SELECT
rid
FROM
(
SELECT a,b,c,d,e,f,g,h,i, MAX(ROWID) KEEP (DENSE_RANK FIRST ORDER BY ROWID) AS RID
FROM sourceTable t
GROUP BY a,b,c,d,e,f,g,h,i
)
)
;