2016-01-26 2 views
2

У меня есть запрос SELECT в представлении, содержащем 500 000 строк. Давайте держать это просто:Неудовлетворительная производительность SQL-запроса с использованием переменной таблицы или пользовательского типа

SELECT * FROM dbo.Document WHERE MemberID = 578310 

Запрос выполняется быстро, ~0s

Давайте перепишем его для работы с набором значений, что отражает мои потребности больше:

SELECT * FROM dbo.Document WHERE MemberID IN (578310) 

Это же быстро , ~0s

Но теперь набор идентификаторов должен быть переменным; определим его как:

DECLARE @AuthorizedMembers TABLE 
    (
     MemberID BIGINT NOT NULL PRIMARY KEY, --primary key 
     UNIQUE NONCLUSTERED (MemberID) -- and index, as if it could help... 
    ); 

    INSERT INTO @AuthorizedMembers SELECT 578310 

Набор содержит одно и то же значение, но теперь переменная таблицы. Производительность такого запроса падает до 2s, а в более сложных - до 25s и более, а с фиксированным идентификатором - ~0s.

SELECT * 
FROM dbo.Document 
WHERE MemberID IN (SELECT MemberID FROM @AuthorizedMembers) 

это же плохо, как:

SELECT * 
FROM dbo.Document 
WHERE EXISTS (SELECT MemberID 
       FROM @AuthorizedMembers 
       WHERE [@AuthorizedMembers].MemberID = Document.MemberID) 

или же плохо, как это:

SELECT * 
FROM dbo.Document 
INNER JOIN @AuthorizedMembers AS AM ON AM.MemberID = Document.MemberID 

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

Динамический SQL с легкостью справляется, поэтому создание nvarchar, такого как (id1,id2,id3), и построение фиксированного запроса с ним сохраняет мое время запроса ~0s. Но я хотел бы как можно больше избегать использования Dynamic SQL, и если да, то я бы хотел, чтобы он всегда был одной и той же строкой, независимо от значений (используя параметры, которые выше метод не позволяет).

Любые идеи о том, как получить производительность переменной таблицы подобно фиксированному массиву значений или избежать создания другого динамического кода SQL для каждого запуска?

P.S. Я попытался выше с определенным типом пользователя с такими же результатами

Edit: Результаты с временной таблицы, определяемые как:

CREATE TABLE #AuthorizedMembers 
    (
     MemberID BIGINT NOT NULL PRIMARY KEY 
    ); 

    INSERT INTO #AuthorizedMembers SELECT 578310 

улучшили время выполнения до 3-х раз. (13 с -> 4 с). Который все еще значительно выше, чем динамический SQL < 1s.

+0

Попробуйте изменить переменную таблицы во временную таблицу 'CREATE TABLE # AuthorizedMembers' Она может значительно повысить производительность в некоторых ситуациях, в зависимости от объема данных. –

+1

'OPTION (RECOMPILE)' – Devart

+0

как насчет 'INNER JOIN', вместо' LEFT JOIN + IS NOT NULL'? – Kobi

ответ

2

Ваших вариантов:

  • Используйте временную таблицу вместо TABLE переменной
  • Если вы настаиваете на использование TABLE переменных, добавьте OPTION(RECOMPILE) в конце запроса

Пояснения:

Когда компилятор компилирует ваш оператор, переменная TABLE имеет n o строк в нем и, следовательно, не имеет достаточных мощностей. Это приводит к неэффективному плану выполнения. OPTION(RECOMPILE) заставляет команду перекомпилировать при ее запуске. В этот момент переменная TABLE имеет в ней строки, а компилятор имеет лучшие возможности для создания плана выполнения.

Общее правило заключается в использовании временных таблиц при работе с большими наборами данных и табличными переменными для небольших наборов данных с частыми обновлениями. Лично я редко использую переменные TABLE, потому что они обычно плохо работают.

Я могу порекомендовать this answer по вопросу «В чем разница между временными таблицами и табличными переменными в SQL Server?», если вы хотите провести углубленный анализ различий.

+0

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

+0

@mikus * добавить 'OPTION (RECOMPILE)' в конце вашего запроса * –

+0

@mikus Если это не улучшает время выполнения, переключитесь на временные таблицы. Предоставьте временную таблицу ИНДЕКСОМ, если ваш запрос выиграет от этого. Также возможно, что ваш запрос не имеет надлежащих индексов ... проверьте план выполнения, чтобы увидеть, отсутствуют ли в каких-либо таблицах правильные индексы для вашего запроса. –