Очень медленный запрос DELETE - программирование
Подтвердить что ты не робот

Очень медленный запрос DELETE

У меня проблемы с производительностью SQL. По внезапной причине следующие запросы очень медленные:

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

DECLARE @IdList1 TABLE(Id INT)
DECLARE @IdList2 TABLE(Id INT)

-- Approach 1
DELETE list1
FROM @IdList1 list1
INNER JOIN @IdList2 list2 ON list1.Id = list2.Id

-- Approach 2
DELETE FROM @IdList1
WHERE Id IN (SELECT Id FROM @IdList2)

Возможно, что в двух списках содержится более 10.000 записей. В этом случае оба запроса выполняются каждые 20 секунд.

План выполнения также показал то, что я не понимаю. Может быть, это объясняет, почему это так медленно: Queryplan of both queries

Я заполнил оба списка с 10.000 последовательными целыми числами, поэтому оба списка содержали значение 1-10 000 в качестве отправной точки.

Как вы можете видеть, оба запроса отображаются для @IdList2. Фактическое количество строк - 50.005.000!!. @IdList1 является правильным (фактическое количество строк - 10.000)

Я знаю, что есть другие способы решения этой проблемы. Как заполнение третьего списка, установленного для удаления из первого списка. Но мой вопрос:

Почему эти запросы на удаление так медленно и почему я вижу эти странные планы запросов?

4b9b3361

Ответ 1

Добавьте первичный ключ к переменным таблицы и посмотрите их крик

DECLARE @IdList1 TABLE(Id INT primary Key not null)
DECLARE @IdList2 TABLE(Id INT primary Key not null)

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

Ответ 2

SQL Server компилирует план, когда переменная таблицы пуста и не перекомпилирует ее при добавлении строк. Попробуйте

DELETE FROM @IdList1
WHERE Id IN (SELECT Id FROM @IdList2)
OPTION (RECOMPILE)

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

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

Ответ 3

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

DECLARE @IdList1 TABLE(Id INT PRIMARY KEY)
DECLARE @IdList2 TABLE(Id INT PRIMARY KEY)

Ответ 4

Возможные решения:

1) Попробуйте создать индексы, таким образом

1.1) Если столбец List {1 | 2}.Id имеет уникальные значения, тогда вы можете определить уникальный кластерный индекс, используя ограничение PK следующим образом:

DECLARE @IdList1 TABLE(Id INT PRIMARY KEY);
DECLARE @IdList2 TABLE(Id INT PRIMARY KEY);

1.2) Если столбец List {1 | 2}.Id может иметь повторяющиеся значения, тогда вы можете определить уникальный кластерный индекс, используя ограничение PK, используя столбец IDENTITY, как это:

DECLARE @IdList1 TABLE(Id INT, DummyID INT IDENTITY, PRIMARY KEY (ID, DummyID) );
DECLARE @IdList2 TABLE(Id INT, DummyID INT IDENTITY, PRIMARY KEY (ID, DummyID) );

2) Попробуйте добавить подсказку HASH JOIN так:

DELETE list1
FROM @IdList1 list1
INNER JOIN @IdList2 list2 ON list1.Id = list2.Id
OPTION (HASH JOIN);

Ответ 5

Вы используете Table Variables, либо добавьте первичный ключ в таблицу, либо измените их на Temporary Tables и добавьте INDEX. Это приведет к значительно большей производительности. Как правило, если таблица мала, используйте Table Variables, однако, если таблица расширяется и содержит много данных, либо используйте временную таблицу.

Ответ 6

У меня возникнет соблазн попробовать

DECLARE @IdList3 TABLE(Id INT);

INSERT @IdList3
SELECT Id FROM @IDList1 ORDER BY Id
EXCEPT
SELECT Id FROM @IDList2 ORDER BY Id

Не требуется удаление.

Ответ 7

Попробуйте этот альтернативный синтаксис:

DELETE deleteAlias
FROM @IdList1 deleteAlias
WHERE EXISTS (
        SELECT NULL
        FROM @IdList2 innerList2Alias
        WHERE innerList2Alias.id=deleteAlias.id
    )

ИЗМЕНИТЬ.....................

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

Вот общий пример, где "DepartmentKey" - это PK и FK.

IF OBJECT_ID('tempdb..#Department') IS NOT NULL
begin
        drop table #Department
end


CREATE TABLE #Department 
( 
    DepartmentKey int , 
    DepartmentName  varchar(12)
)



CREATE INDEX IX_TEMPTABLE_Department_DepartmentKey ON #Department (DepartmentKey)




IF OBJECT_ID('tempdb..#Employee') IS NOT NULL
begin
        drop table #Employee
end


CREATE TABLE #Employee 
( 
    EmployeeKey int , 
    DepartmentKey int ,
    SSN  varchar(11)
)



CREATE INDEX IX_TEMPTABLE_Employee_DepartmentKey ON #Employee (DepartmentKey)


Delete deleteAlias 
from #Department deleteAlias
where exists ( select null from #Employee innerE where innerE.DepartmentKey = deleteAlias.DepartmentKey )





IF OBJECT_ID('tempdb..#Employee') IS NOT NULL
begin
        drop table #Employee
end

IF OBJECT_ID('tempdb..#Department') IS NOT NULL
begin
        drop table #Department
end