Может ли использование isnull в операторе where вызвать проблемы с использованием индексов? - PullRequest
2 голосов
/ 23 декабря 2009

У меня есть запрос, подобный следующему:

SELECT t1.v3, t2.v2
FROM t1
INNER JOIN t2
ON t1.v1 = t2.v1
WHERE ISNULL(t1.DeleteFlag,'N') = 'N'

У меня есть индекс, который, я думаю, должен привести к поиску индекса для части = 'N', но вместо этого я вижу очень дорогое сканирование индекса. Возможно ли, что индекс нарушает правильное использование индексов? Имеет ли смысл даже иметь индекс для столбца, который будет иметь только несколько возможных значений (например, DeleteFlag will)?

Ответы [ 3 ]

8 голосов
/ 23 декабря 2009

Да, любые вызовы функций в вашем предложении WHERE, вероятно, сделают индекс бесполезным. Попробуйте переписать его, чтобы можно было использовать индекс:

SELECT t1.v3, t2.v2
FROM t1
INNER JOIN t2
ON t1.v1 = t2.v1
WHERE NOT t1.DeleteFlag = 'Y'

Индекс имеет смысл, если число результатов, которые вы ожидаете от запроса, намного меньше, чем общее количество строк в таблице.

6 голосов
/ 23 декабря 2009

1) Превращает ли использование ISNULL поиск в сканирование? Да. Применение функции к столбцу в целом делает выражение не пригодным для SARG (недоступно для поиска). Чтобы индекс рассматривался для операции поиска, движок должен знать, какое значение искать, в виде необработанного двоичного значения. Как только вы применяете функцию к столбцу, вы запрашиваете поиск результата функции, поэтому он должен оценивать функцию в каждой строке, чтобы увидеть, удовлетворяет ли результат условию.

2) Имеет ли смысл иметь индекс для столбца с очень низкой селективностью (2-3 значения)? Да, но никогда как автономное индексное выражение. Индекс переломный момент сделает автономный индекс для столбца с низкой избирательностью просто пустой тратой пространства. Но столбцы с очень низкой селективностью, такие как биты и флаги, очень полезны в качестве крайних левых ключей в индексе, если они составлены с большим количеством ключей. В вашем случае, учитывая, что это удаленный флаг, имеет смысл быть первым ключом кластеризованного индекса, поскольку ожидается, что каждый запрос будет указывать условие 'IsDeleted'.

Я также добавил бы, что у вас, вероятно, не должно быть NULL на флаге «удалено».

5 голосов
/ 29 декабря 2012

Ответ Марк Байерс не работает, и это связано с очень тонким способом, которым SQL Server обрабатывает нули. В выражении WHERE "t1.DeleteFlag = 'Y'", если t1.DeleteFlag имеет значение NULL, выражение возвращает значение NULL. Таким образом, NOT (NULL) также возвращает NULL, и это не выполняется условие WHERE. Попробуйте сделать этот тест:

DECLARE @myvar VARCHAR(1)
SET @myvar = NULL

SELECT 'OK' WHERE ISNULL(@myvar, 'N') = 'N' -- Baseline statement. Returns OK
SELECT 'OK' WHERE NOT (@myvar = 'Y')        -- Equivalent to answer above. Fails
SELECT 'OK' WHERE @myvar = 'N' OR @myvar IS NULL -- This is another way to do it. Also returns OK

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

Итак, вот правильный ответ на вопрос:

SELECT t1.v3, t2.v2
FROM t1
INNER JOIN t2
ON t1.v1 = t2.v1
WHERE (t1.DeleteFlag = 'N' OR t1.DeleteFlag IS NULL)

Альтернативой этому, которая, вероятно, привела бы к номинально лучшим результатам производительности, было бы определение поля DeleteFlag как "NOT NULL" и присвоение ему значения DEFAULT из '' (пустая строка). Тогда запрос может быть просто написан без забот о NULL:

SELECT t1.v3, t2.v2
FROM t1
INNER JOIN t2
ON t1.v1 = t2.v1
WHERE t1.DeleteFlag = 'N'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...