Запрос на удаление дубликатов SQL с миллионами строк для повышения производительности

StackOverflow https://stackoverflow.com/questions/162325

Вопрос

Это было приключение. Я начал с зацикленного повторяющегося запроса, расположенного в моем предыдущем вопросе , но каждый цикл проходил бы по всем 17 миллионам записей , означает, что на это уйдут недели (простой запуск * select count * from MyTable * занимает у моего сервера 4:30 минуты с использованием MSSQL 2005). Я получил информацию с этого сайта и с этой записи .

И пришли по запросу ниже. Вопрос в том, является ли этот тип запроса для 17 миллионов записей для любого типа производительности? Если нет, то что?

SQL QUERY:

DELETE tl_acxiomimport.dbo.tblacxiomlistings
WHERE RecordID in 
(SELECT RecordID
    FROM tl_acxiomimport.dbo.tblacxiomlistings
    EXCEPT
    SELECT RecordID
    FROM (
        SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude,           Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
    FROM tl_acxiomimport.dbo.tblacxiomlistings
    ) al WHERE Rank = 1)
Это было полезно?

Решение

Просмотр QueryPlan поможет.

Это возможно?

SELECT m.*
into #temp
FROM tl_acxiomimport.dbo.tblacxiomlistings m 
inner join (SELECT RecordID, 
                   Rank() over (Partition BY BusinessName, 
                                             latitude,  
                                             longitude,            
                                             Phone  
                                ORDER BY webaddress DESC,  
                                         caption1 DESC,  
                                         caption2 DESC ) AS Rank
              FROM tl_acxiomimport.dbo.tblacxiomlistings
           ) al on (al.RecordID = m.RecordID and al.Rank = 1)

truncate table tl_acxiomimport.dbo.tblacxiomlistings

insert into tl_acxiomimport.dbo.tblacxiomlistings
     select * from #temp

Другие советы

Что-то не так с вашей БД, сервером, хранилищем или какой-то их комбинацией. 4:30 для выбранного количества * кажется ОЧЕНЬ высоким.

Запустите DBCC_SHOWCONTIG, чтобы увидеть, насколько фрагментирована ваша таблица, это может привести к значительному снижению производительности таблицы такого размера.

Кроме того, чтобы добавить комментарий RyanKeeter, запустите план показа и, если есть какие-либо просмотры таблицы, создайте индекс для поля PK в этой таблице.

Разве это не было бы проще сделать

DELETE tl_acxiomimport.dbo.tblacxiomlistings
WHERE RecordID in 
(SELECT RecordID
   FROM (
        SELECT RecordID,
            Rank() over (Partition BY BusinessName,
                                  latitude,
                                  longitude,
                                  Phone
                         ORDER BY webaddress DESC,
                                  caption1 DESC,
                                  caption2 DESC) AS Rank
        FROM tl_acxiomimport.dbo.tblacxiomlistings
        )
  WHERE Rank > 1
  )

Запустите это в анализаторе запросов:

SET SHOWPLAN_TEXT ON

Затем попросите анализатор запросов выполнить ваш запрос. Вместо выполнения запроса SQL Server сгенерирует план запроса и поместит его в набор результатов.

Покажите нам план запроса.

17 миллионов записей - ничто. Если для выбора счетчика (*) требуется 4:30, тогда возникает серьезная проблема, возможно связанная с нехваткой памяти на сервере или действительно старым процессором.

Для повышения производительности почините машину. Накачайте его до 2 ГБ. В наши дни оперативная память настолько дешева, что ее стоимость намного меньше вашего времени.

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

То есть вы удаляете все записи, которые не заняли первое место? Возможно, стоило бы сравнить объединение с первым подзапросом (с которым также можно работать в 2000 году, так как рейтинг только 2005 и выше)

Вам нужно удалить все дубликаты за одну операцию? Я предполагаю, что вы выполняете какое-то домашнее задание, возможно, вы сможете выполнять его по частям.

В основном создайте курсор, который зацикливает все записи (грязное чтение) и удаляет дубликаты для каждой. В целом это будет намного медленнее, но каждая операция будет относительно минимальной. Тогда ваша домашняя уборка станет постоянной фоновой задачей, а не ночной партией.

Предложенное выше предложение сначала выбрать временную таблицу - ваш лучший выбор. Вы также можете использовать что-то вроде:

set rowcount 1000

перед запуском удаления. Он остановится после удаления 1000 строк. Затем запускайте его снова и снова, пока не удалите 0 записей.

если я правильно понял, ваш запрос такой же, как

DELETE tl_acxiomimport.dbo.tblacxiomlistings
FROM
    tl_acxiomimport.dbo.tblacxiomlistings allRecords
    LEFT JOIN (   
        SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
        FROM tl_acxiomimport.dbo.tblacxiomlistings
        WHERE Rank = 1) myExceptions
    ON allRecords.RecordID = myExceptions.RecordID
WHERE
    myExceptions.RecordID IS NULL

Я думаю, что это должно работать быстрее, я стараюсь не использовать " IN " пункт в пользу СОЕДИНЕНИЯ, где это возможно.

На самом деле вы можете безопасно проверить скорость и результаты, просто вызвав SELECT * или SELECT COUNT (*) в части FROM, например,

SELECT *
FROM
    tl_acxiomimport.dbo.tblacxiomlistings allRecords
    LEFT JOIN (   
        SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
        FROM tl_acxiomimport.dbo.tblacxiomlistings
        WHERE Rank = 1) myExceptions
    ON allRecords.RecordID = myExceptions.RecordID
WHERE
    myExceptions.RecordID IS NULL

Это еще одна причина, по которой я бы предпочел подход JOIN Я надеюсь, что это помогает

Это выглядит хорошо, но вы можете подумать о том, чтобы выбрать свои данные во временную таблицу и использовать их в своем операторе удаления. Я заметил огромный прирост производительности, выполняя это вместо того, чтобы делать все это в одном запросе.

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

Кроме использования усечения, как было предложено, мне больше всего повезло, используя этот шаблон для удаления большого количества строк из таблицы. Я не помню, но использование транзакции помогло предотвратить рост файла журнала, хотя, возможно, это была еще одна причина, но я не уверен. И я обычно переключаю метод ведения журнала транзакций на простой, прежде чем делать что-то вроде этого:

SET ROWCOUNT 5000
WHILE 1 = 1
BEGIN
    begin tran
            DELETE FROM ??? WHERE ???
            IF @@rowcount = 0
            BEGIN
               COMMIT
               BREAK
            END
    COMMIT
END
SET ROWCOUNT 0
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top