20-часовой запрос из ада - PullRequest
       1

20-часовой запрос из ада

1 голос
/ 29 декабря 2010

Я выполняю этот запрос для таблицы, которая содержит полмиллиона записей с примерно 7 полями:

delete from qvalues where rid not in  
(
select min(rid) from qvalues
group by name,compound,rowid
having COUNT(*)>1)

and rid not in  (select min(rid) from qvalues

group by name,compound,rowid
having COUNT(*)=1);

почему он занимает так много времени?

что я могу сделать, чтобы оптимизироватьэто?

я использую SQL Server 2008

Ответы [ 6 ]

2 голосов
/ 29 декабря 2010

Лучше всего посмотреть на план выполнения и посмотреть, что займет больше всего времени. Я бы начал с сокращения двух not in запросов до одного:

delete 
from qvalues 
where rid not in
(
    select min(rid)
    from qvalues
    group by name, compound, rowid
    having count(1) >= 1
)

Возможно, вы захотите добавить индекс на name, compound и rowid

.
1 голос
/ 29 декабря 2010

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

;WITH cte As
(
SELECT ROW_NUMBER() OVER (PARTITION BY name, compound, rowid ORDER BY rid) AS RN
FROM qvalues
)
DELETE FROM cte WHERE RN > 1

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

1 голос
/ 29 декабря 2010

1 - Используйте пакетирование. Это позволяет вам возобновить работу и дает представление о прогрессе. Как пример:

DECLARE @MSG Varchar(max)

WHILE 1=1
    BEGIN
        DELETE TOP (100000) qvalues
        FROM qvalues WITH (TABLOCKX)
        <logic here>
        IF @@ROWCOUNT < 100000 BREAK        
        SET @Msg = 'Deleted another 10 Million'
        SET @Msg = @Msg + ' ' +CONVERT(varchar(20),GETDATE(),101)+' '+CONVERT(varchar(20),GETDATE(),108) 
        RAISERROR(@Msg, 0, 1) WITH NOWAIT
    END

Обратите внимание, что я также добавил подсказку WITH (TABLOCKX), которая устанавливает блокировку таблицы и устраняет блокировку на уровне строк. Это вызовет проблемы с одновременным чтением, но, надеюсь, у вас больше не будет запросов к этой таблице во время удаления.

2 - Исправьте свою логику Это невозможно написать для вас без лучшего представления о структуре вашей таблицы, но некоторые варианты: - материализуйте таблицу со значениями, с которыми вы хотите сравнить, и выполните объединение. Если удаление достаточно велико, вы можете создать кластерный индекс для временной таблицы в поле соединения. Я использовал это много с большим успехом. - Если вы планируете удалить большую часть записей, SELECT INTO новую таблицу и отбросьте старую. Это минимально регистрируемая операция, которая выполняется на SQL Server 2008 очень быстро по сравнению с удалением, для которого необходимо записать значения для каждой строки. - Отбросьте все свои индексы, кроме того, что вы используете для выбора и вашего кластерного индекса. Сохранение кластеризованного индекса обычно нормально для удаления этого типа, если это релевантный кластер для запроса.

1 голос
/ 29 декабря 2010

Не зная реальных данных, я могу дать несколько общих советов: запускать каждый из подзапросов по отдельности.

Кроме того, я неправильно читаю или вы удаляете из этой таблицы все записи, кроме 2(если рид уникален)?

0 голосов
/ 31 декабря 2010

Как настроены ваши машины, достаточно ли у вас памяти, где вы видите наибольшее использование при выполнении запроса (CPU, Memory, Disk IO)?

0 голосов
/ 29 декабря 2010

первая мысль:

delete from qvalues where rid not in  
(
select min(rid) from qvalues
group by name,compound,rowid
having COUNT(*)>1

UNION

select min(rid) from qvalues
group by name,compound,rowid
having COUNT(*)=1);

Возможно, также неплохо бы убедиться, что sql-сервер знает, что вы делаете «некоррелированный подвыбор» (потому что «коррелированные подвыборы» занимают намного больше времени):

delete from qvalues a where a.rid not in  
(
select min(b.rid) from qvalues b
group by b.name,b.compound,b.rowid
having COUNT(*)>1

UNION

select min(c.rid) from qvalues c
group by c.name,c.compound,c.rowid
having COUNT(*)=1);

и, конечно, вы должны рассмотреть возможность использования индексов (особенно для rid, но также для имени, составного, rowid)

Мои SQL не проверены - надеюсь, вы поймете, что я пытался показать.

PS: ваш sql требует много вычислений (особенно предложений HAVING), не могли бы вы попытаться найти другое решение для вашей проблемы?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...