2010-12-29 3 views
1

я бегу этот запрос на таблицу, которая имеет полмиллиона записей около 7 полей:20 час запрос из ада

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

+0

Вы уверены, что его 20 часов? Перед выполнением запроса убедитесь, что в таблице нет блокировки ... –

+1

Есть ли указатель на таблице? Ключи? Схема? – jzd

+0

@madh как проверить, есть ли блокировка –

ответ

1

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

;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

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

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

+1

rid is unique yes –

+1

В таком случае, почему бы не просто скопировать данные, которые вы хотите сохранить, а затем удалить всю таблицу и повторно добавить данные, которые вы скопировали? – Jeff

+0

Мне нравится эта идея (+1), единственное, о чем следует опасаться, это триггеры, индексы и т. Д. В исходной таблице, которые нужно будет дублировать в новой таблице. –

2

Лучше всего посмотреть на план выполнения и посмотреть, что занимает самое длинное. Я хотел бы начать за счет снижения двух 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

Я также сделал эту ошибку, которую указал Джефф. Это не то же самое, что и исходный запрос, поскольку это исключает только один из них. – Jim

+0

@Jim - Это точно так же, как и исходный запрос. Это не только исключает одно избавление вообще. Исходный запрос удаляет дубликаты на основе 'name, compound, rowid', оставляя один неповрежденным с минимальным риском для этой группы. Вот что правильно делает запрос в этом ответе и [мой ответ] (http://stackoverflow.com/questions/4555623/twenty-hour-query-from-persephones-nightmare/4556062#4556062). –

0

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

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

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

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

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

1

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

, что я также добавил WITH (TABLOCKX) подсказку, что ставит блокировку таблицы, и исключает блокировку на уровне строк
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 

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

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

0

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

Смежные вопросы