Запрос на удаление дубликатов SQL с миллионами строк для повышения производительности - PullRequest
2 голосов
/ 02 октября 2008

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

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

SQL-запрос:

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)

Ответы [ 11 ]

6 голосов
/ 02 октября 2008

Просмотр 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
2 голосов
/ 02 октября 2008

Не было бы проще сделать:

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
  )
2 голосов
/ 02 октября 2008

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

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

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

1 голос
/ 02 октября 2008

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

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" в пользу JOIN, где это возможно.

Вы действительно можете безопасно проверить скорость и результаты, просто позвонив по номеру SELECT * или SELECT COUNT(*) ОТ, например,

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 Я надеюсь, что это помогает

1 голос
/ 02 октября 2008

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

set rowcount 1000

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

1 голос
/ 02 октября 2008

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

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

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

1 голос
/ 02 октября 2008

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

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

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

1 голос
/ 02 октября 2008

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

SET SHOWPLAN_TEXT ON

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

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

0 голосов
/ 24 июля 2009

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

0 голосов
/ 02 октября 2008

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

SET ROWCOUNT 5000
WHILE 1 = 1
BEGIN
    begin tran
            DELETE FROM ??? WHERE ???
            IF @@rowcount = 0
            BEGIN
               COMMIT
               BREAK
            END
    COMMIT
END
SET ROWCOUNT 0
...