Страницы

Поиск по вопросам

среда, 5 февраля 2020 г.

Может ли сравнение строк быть быстрее сравнения чисел?

#sql #sql_server


Прочитал вот эту статью и был озадачен советом #9: Cast for quicker search.

Пишут, что вот этот запрос:

SELECT * FROM HumanResources.EmployeeSick
WHERE CAST(SickLeaveHours AS char(3)) <> 0


может отрабатывать быстрее, чем запрос без каста. Не всегда, но может. SickLeaveHours
имеет тип int.

Как такое может быть? Ведь здравый смысл подсказывает, что на каст будет тратиться время.
    


Ответы

Ответ 1



Как такое может быть? Могу предположить, что данный трюк может работать, из-за особенностей использования индекса. Если запрос должен обработать больше половины записей таблицы, то полный перебор эффективнее индексного чтения. В Firebord того же эффекта добиваются добавлением нуля: WHERE SickLeaveHours+0 <> 0 на каст будет тратиться время. Время на такой cast исчезающе мало по сравнению со временем чтения с диска. Уменьшение I/O за счет отсутствия необходимости читать индекс (в дополнение к данным) перекроет расход на cast.

Ответ 2



Это подавление использования индекса по полю SickLeaveHours. Предположим, что есть некий индекс: CREATE INDEX idx_EmployeeSick_SickLeaveHours ON [HumanResources].[EmployeeSick] ( SickLeaveHours ASC ) Тогда возможен выбор оптимизатором такого плана запроса: Сканирование индекса idx_EmployeeSick_SickLeaveHours (не поиск из-за операции <>) KeyLookup-ы по полученным записям из кластерного индекса/таблицы, так как у нас запрос SELECT *. В случае, если по первому пункту вернется достаточно много записей, вторая операция будет очень дорогой по чтениям, но оптимизатор может ошибиться с оценкой количества записей по ряду причин (устаревшая либо сэмплированная статистика, например) и выбрать план с лукапами вместо сканирования кластерного индекса/таблицы. При использовании предиката CAST(SickLeaveHours AS char(3)) <> 0 из-за преобразований по столбцу SickLeaveHours, а их там будет на самом деле два - явное к char(3), а затем неявное к int, индекс по полю SickLeaveHours использоваться не может, поэтому будет построен план со сканированием кластерного индекса и таким предикатом: CONVERT_IMPLICIT(int,CONVERT(char(3), HumanResources.EmployeeSick.[SickLeaveHours],0),0)<>(0) Как справедливо заметили в комментариях, такого же результата можно добиться хинтами, явно указав какой индекс использовать.

Комментариев нет:

Отправить комментарий