Самый быстрый способ удаления дубликатов данных - PullRequest
7 голосов
/ 18 августа 2010

После поиска на stackoverflow.com я обнаружил несколько вопросов, спрашивающих, как удалить дубликаты, но ни один из них не касался скорости.

В моем случае у меня есть таблица с 10 столбцами, которая содержит 5 миллионов точных дубликатов строк.Кроме того, у меня есть как минимум миллион других строк с дубликатами в 9 из 10 столбцов.Моя текущая техника занимает (пока) 3 часа , чтобы удалить эти 5 миллионов строк.Вот мой процесс:

-- 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 Ponies, и он закончился через 40 минут.Я попробовал свой шаг 2 с пакетным удалением Andomar, оно прошло 9 часов, прежде чем я остановил его.ОБНОВЛЕНИЕ: Выполнил аналогичный запрос с одним полем меньше, чтобы избавиться от другого набора дубликатов, и запрос выполнялся всего 4 минуты (8000 строк), используя метод OMG Ponies.

Я попробую метод cteОднако, при следующей возможности, я подозреваю, что метод OMG Ponies будет непростым.

Ответы [ 6 ]

4 голосов
/ 18 августа 2010

Можете ли вы позволить себе иметь оригинальный стол недоступным в течение короткого времени?

Я думаю, что самое быстрое решение - создать новую таблицу без дубликатов. В основном подход, который вы используете с временной таблицей, но вместо этого создаете «обычную» таблицу.

Затем отбросьте исходную таблицу и переименуйте промежуточную таблицу, чтобы иметь то же имя, что и старая таблица.

4 голосов
/ 18 августа 2010

А как насчет СУЩЕСТВУЮЩИХ:

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)
3 голосов
/ 18 августа 2010

Узким местом в массовом удалении строк обычно является транзакция, которую должен создать SQL Server. Вы можете значительно ускорить его, разделив удаление на более мелкие транзакции. Например, чтобы удалить 100 строк одновременно:

while 1=1
    begin

    delete top 100
    from sourceTable 
    ...

    if @@rowcount = 0
        break
    end
1 голос
/ 18 августа 2010

... на основании комментария 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
0 голосов
/ 18 августа 2010

Вот версия, в которой вы можете объединить оба шага в один шаг.

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

Кстати, есть ли у вас индексы, которые можно временно удалить?

0 голосов
/ 18 августа 2010

Ну, много разных вещей. Сначала нужно что-то вроде этой работы (сделайте выбор, чтобы убедиться, возможно, даже поместите в собственную временную таблицу, #recordsToDelete):

delete  
from sourceTable 
left join #dupTemp on   
       sourceTable.PriKey = #dupTemp.MaxPriKey   
where #dupTemp.MaxPriKey  is null

Далее вы можете индексировать временные таблицы, ставить индекс на prikey

Если у вас есть записи во временной таблице тех, которые вы хотите удалить, вы можете удалять пакетами, что часто быстрее, чем блокировка всей таблицы с помощью удаления.

...