Плохая производительность, когда не выбран конкретный столбец в представлении - PullRequest
0 голосов
/ 04 сентября 2018

Использование SQL Server 2016 SP1. У меня есть представление Users, которое выглядит как

SELECT
    ROW_NUMBER() OVER (ORDER BY ID) AS DataModelID, *
FROM            
    (Some query) AS tbl

Я тогда выбираю из него

SELECT 
    U1.ID UserId, U1.IdentityNumber IdentityNumber,
    U1.ArabicFirstName, U1.ArabicSecondName
FROM
    USERS U1
LEFT JOIN 
    USERS U2 ON U1.IdentityNumber = U2.IdentityNumber
             AND U1.ID <> U2.ID
             AND U1.RoleId = 2
WHERE 
    U2.ID IS NOT NULL 
    AND U1.IdentityNumber <> '' 
    AND PATINDEX('[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]', U1.IdentityNumber) = 1

Дело в том, что с указанным выше запросом при выборе * или включении столбца DataModelID он запускается за 3 секунды, но при выборе столбцов без этого он выполняется более 2 минут.

Почему это происходит, работает быстрее при включении столбца?

Я перепробовал все, чтобы очистить его и запустить несколько раз, и он дает те же результаты

1 Ответ

0 голосов
/ 04 сентября 2018

Не видя план выполнения actual, нельзя сказать наверняка, но, как упомянул @mvisser, - вероятная причина в том, что оптимизатор выбирает лучший индекс, когда вы делаете SELECT * или включить столбец DataModelID, чем когда вы этого не сделаете. Здесь есть ряд решений, одним из которых было бы посмотреть план выполнения для запросов, которые выполняются в течение 3 секунд, отметить, какой индекс используется, и использовать индексную подсказку (см. Раздел G) для вынудите оптимизатор использовать этот индекс в ваших запросах, которые не ссылаются на эти столбцы. Я бы не советовал, хотя - слишком много неотвеченных переменных, чтобы считать этот вариант приемлемым.

Вот что я рекомендую:

Во-первых, как упомянул @Lukasz Szozda, это не SARGable:

AND PATINDEX( '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]',U1.IdentityNumber) = 1

Но это:

U1.IdentityNumber LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'

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

ВАРИАНТ № 2

Вы можете создать индексированное представление таблицы USERS, которая выглядит примерно так:

CREATE VIEW dbo.vwUSERS_clean
WITH SCHEMABINDING AS
SELECT U1.ID, UserId, U1.IdentityNumber IdentityNumber,
       U1.ArabicFirstName, U1.ArabicSecondName, DataModelID, U2.IdentityNumber
FROM USERS U1
WHERE U2.ID IS NOT NULL 
AND U1.IdentityNumber <> '' 
AND U1.IdentityNumber LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]';
GO

Затем создайте уникальный кластерный индекс для него. Затем вы измените отправленный запрос, чтобы он ссылался на индексированное представление (, например, измените обе ссылки на USERS на dbo.vwUSERS_clean WITH (NOEXPAND)).

Обратите внимание, что ROW_NUMBER недопустим в индексированных представлениях, но если вы сделаете ID своим кластеризованным индексом (или первым столбцом в составном кластерном индексе), то добавление ROW_NUMBER() OVER ORDER BY ID к запросам, которые ссылаются на это индексированное представление, не будет стоить ,

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