Обчислений індекс стовпця не використовується


14

Я хочу здійснити швидкий пошук на основі, якщо два стовпці рівні. Я намагався використовувати обчислену колонку з індексом, але SQL Server, схоже, не використовує її. Якщо я просто використовую статично заселений бітовий стовпчик з індексом, я отримую очікуваний пошук індексу.

Здається, є деякі інші подібні питання, але жодне не фокусувалося на тому, чому індекс не буде використаний.

Тестова таблиця:

CREATE TABLE dbo.Diffs
    (
    Id int NOT NULL IDENTITY (1, 1),
    DataA int NULL,
    DataB int NULL,
    DiffPersisted  AS isnull(convert(bit, case when [DataA] is null and [DataB] is not null then 1 when [DataA] <> [DataB] then 1 else 0 end), 0) PERSISTED ,
    DiffComp  AS isnull(convert(bit, case when [DataA] is null and [DataB] is not null then 1 when [DataA] <> [DataB] then 1 else 0 end), 0),
    DiffStatic bit not null,
    Primary Key (Id)
    )

create index ix_DiffPersisted on Diffs (DiffPersisted)
create index ix_DiffComp on Diffs (DiffComp)
create index ix_DiffStatic on Diffs (DiffStatic)

І Запит:

select Id from Diffs where DiffPersisted = 1
select Id from Diffs where DiffComp = 1
select Id from Diffs where DiffStatic = 1

І отримані плани виконання: План виконання

Відповіді:


10

Спробуйте COALESCEзамість цього ISNULL. З ISNULL, SQL Server, здається, не здатний підштовхувати предикат до вужчого індексу, і тому він повинен сканувати кластер, щоб знайти інформацію.

CREATE TABLE dbo.Diffs
    (
    Id int NOT NULL IDENTITY (1, 1),
    DataA int NULL,
    DataB int NULL,
    DiffPersisted  AS COALESCE(convert(bit, case when [DataA] is null 
      and [DataB] is not null then 1 when [DataA] <> [DataB] 
      then 1 else 0 end), 0) PERSISTED ,
    DiffComp  AS COALESCE(convert(bit, case when [DataA] is null 
      and [DataB] is not null then 1 when [DataA] <> [DataB] 
      then 1 else 0 end), 0),
    DiffStatic bit not null,
    Primary Key (Id)
    );

Це означає, що якщо ви дотримуєтесь статичного стовпця, відфільтрований індекс може мати більше сенсу і матиме менші витрати на введення / виведення (все залежно від того, скільки рядків зазвичай відповідає предикату фільтра), наприклад:

CREATE INDEX ix_DiffStaticFiltered 
  ON dbo.Diffs(DiffStatic)
  WHERE DiffStatic = 1;

Дуже цікаво, я б не подумав про це. Здається, ви можете просто позбутися COALESCEцього моменту; Я вважаю, що CASEзаява вже гарантовано повертається 0або 1, але воно ISNULLбуло присутнє лише для того, щоб SQL Server дав нульове значення BITдля обчисленого стовпця. Однак COALESCEвсе одно вийде нульовий стовпчик. Отже, одним із наслідків цієї зміни, з або без COALESCE, є те, що обчислена колонка тепер є нульовою, але пошук індексу може бути використаний.
Джефф Паттерсон

@Geoff Так, це правда. Але в цьому випадку , так як ми знаємо , по розрахунковому визначенню стовпчика NULL справді не можливий вихід, це тільки дійсно має значення , якщо ми використовуємо цю таблицю в якості джерела SELECT INTO.
Аарон Бертран

Це дивовижна інформація - дякую! Моя кінцева мета полягає в тому, щоб стовпці DataA і DataB використовувались як "брудні" uuids для забезпечення асинхронного оновлення денормалізованих стовпців у записі, тому не повинно бути занадто багато, де прапор Diff становить 1. Якщо я використовую статичний поле, тоді я думав додати тригер для моніторингу двох уідів та оновлення поля.
Девід Файвр

Крім того, як зазначав @GeoffPatterson, чи не можу я використовувати COALESCE? Чому я б його зберігав?
Девід Файвр

@David Ви, ймовірно, можете скинути COALESCE. Я намагався підтримувати зовнішній вигляд і наміри вашого оригінального коду, і не тестував без нього, щоб тестування було на вас. (Я не можу пояснити, чому ви ISNULLтам були в першу чергу.)
Аарон Бертран

5

Це специфічне обмеження логіки відповідності обчислених стовпців SQL Server, коли використовується найбільш зовнішня ISNULL, і тип даних стовпця є bit.

Повідомлення про помилку

Щоб уникнути проблеми, може бути використаний будь-який із наведених нижче способів:

  1. Не використовуйте зовнішній ISNULL(єдиний спосіб скласти обчислений стовпчик NOT NULL).
  2. Не використовуйте bitтип даних як кінцевий тип обчисленого стовпця.
  3. Складіть обчислений стовпець PERSISTEDі включіть прапор сліду 174 .

Деталі

Суть проблеми полягає в тому, що без прапора сліду 174 всі обчислювані посилання стовпців у запиті (навіть зберігаються) завжди розгортаються в базове визначення дуже рано на початку складання запитів.

Ідея розширення полягає в тому, що вона могла б забезпечити спрощення та перезаписи, які можуть працювати лише на визначення, а не лише на ім'я стовпця. Наприклад, в запиті можуть бути предикати, на які посилається той обчислений стовпець, який може зробити частину обчислення надмірною або іншим чином більш обмеженим.

Після того, як будуть розглянуті ранні спрощення та перезаписи, компіляція запитів намагається співставити вирази в запиті з обчисленими стовпцями (усі обчислювані стовпці, не тільки ті, які спочатку були знайдені в тексті запиту).

Незмінені вирази обчислених стовпців відповідають більшості випадків вихідному обчисленому стовпцю без проблем. Здається, помилка, коли характерна для відповідності виразу bitтипу, із зовнішнім ISNULL. Відповідність є невдалою в цьому конкретному випадку, навіть якщо детальне вивчення внутрішніх справ показує, що воно повинно досягти успіху.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.