Как стать автором
Обновить

Комментарии 8

Здесь какая то опечатка? Вы создаете вычисляемый столбец AboutMeHash

ALTER TABLE dbo.Users
    ADD AboutMeHash AS CHECKSUM(AboutMe); 

А в запросе его не используете

SELECT *
FROM dbo.Users
WHERE CHECKSUM(AboutMe) = CHECKSUM(@var) AND AboutMe = @var

Я имена вычисляемых столбцов вообще нигде по тексту не использую - только их определения.

MSSQLSERVER - умный, и в этом случае задействует индекс по вычисляемому полю.

Один из приемов ускорения запросов, в том случае, когда запрос изменить нельзя (например запрос летит из клиентского приложения, которое фиг поменяешь) - как раз и состоит в том, чтобы обнаружить подобные случаи, создать вычисляемые столбцы, аналогичные используемому выражению, и создание по ним индексов.

Засовывать varchar(max) в included-колонки индекса - так себе практика. Индекс распухнет и перестанет быстро сканироваться (понадобится много чтений страниц даже для бинарного поиска в нем) - так как included-колонки хранятся в нем «по значению» ;). Туда хорошо ложатся компактные FK для дальнейших связок и всякие скалярные признаки, по которым может идти поиск.

Computed колонки лучше делать persisted (по ним, кстати, тогда можно тоже построить индекс и даже ПК). В Вашем случае можно сделать computed persisted-колонку LEFT(x, 200) и индекс по ней - если реально данные не длиннее 200 символов. Не забывайте ещё, что varchar(max) хранятся в куче, а в таблице - только ссылки на них - поэтому выборка для поиска может быть ещё дольше

Засовывать varchar(max) в included-колонки индекса - так себе практика.

Согласен, вроде и не утверждал обратного, хотя иногда и приходится.

Computed колонки лучше делать persisted

Можете пояснить - зачем persisted? Зачем хранить и в кластерном индексе, и в самом индексе, если они мне особо и не нужны, а только для оптимизации запроса?

computed persisted-колонку LEFT(x, 200) и индекс по ней - если реально данные не длиннее 200 символов.

Зависит от того, какая средняя длина. Я предпочитаю объявлять больше, чтобы не было спилов в tempdb, если в выборке будут слишком длинные varchar'ы.

DROP INDEX ix_ON ON smth (field2_calculated);
GO
ALTER TABLE smth
    DROP COLUMN field2_calculated;
GO
ALTER TABLE smth
    ADD field2_calculated AS  LEFT(field2, 200) PERSISTED;
GO
CREATE INDEX ix_ON ON smth (field2_calculated);
GO

Msg 1919, Level 16, State 1, Line 18 Column 'field2_calculated' in table 'smth' is of a type that is invalid for use as a key column in an index.

Явно приводить тип всё равно нужно.

Не забывайте ещё, что varchar(max) хранятся в куче

зависит от реальной длины строки

Сорри, я прочитал статью на телефоне практически по диагонали - сейчас смотрю на компьютере и вижу, что вопросов у меня нет :)

Добавлю только, что я лично не уверен, что SQL Engine всегда будет понимать выражение с колонкой, и заменять его на computed, поэтому в своих конструкциях я полагаюсь на явное использование имени СС, при этом делаю ее PERSISTED, и если нужен индекс, включаю ее в селектор или в included. В примере с контрольной суммой CLOBа я бы сделал AboutMeHash AS BINARY_CHECKSUM(AboutMe) PERSISTED и построил бы индекс на AboutMeHash.

PS - Вы абсолютно правильно пишете про key lookup для широких таблиц - это очень затратная операция - этот факт нужно прописать болдом в мануале разработчиков - так как выборка по разреженным ключам при большой длине записи приводит к хаотичному чтению большого количества страниц, часто ради одного-двух ключей по которым таблицы связываются дальше - поэтому если в execution plan вы видите, что key lookup делается для вытаскивания компактного набора данных, и он относительно статичен (не апдейтится часто) - то добавление его в included индекса существенно ускоряет весь велосипед.

Согласен, вроде и не утверждал обратного, хотя иногда и приходится.

А вот зачем приходится, не скажите? Ведь картинка будет та же - сначала будет найден ключ, а потом сервер полезет на LOB страницу. Что в лоб, что по лбу. Разве что для фильтрованных индексов имеет смысл.

Можете пояснить - зачем persisted? Зачем хранить и в кластерном индексе, и в самом индексе, если они мне особо и не нужны, а только для оптимизации запроса?

Чтобы не тратить время на пересоздание значения поля при каждом подъеме данных в память.

Впрочем, если вы построите по такому полю индекс (когда то давно это можно было делать только по persisted) - сервер всё равно материализует значение и сохранит в индексе.

Благодарю за интересный материал.

В своей практике часто использую вычисляемое сохраняемое поле как хэшсумма по большому полю или группы полей. На это поле навешивается индекс. В самом запросе сначала явно пишется фильтр по этому полю (равенство) и только потом фильтр уточняющий по исходным полям (равенство).

В принципе, такая идея лежит в .NET для тех ссылочных типов, когда сначала сравнивается их хэш двух объектов и если они одинаковые, то только потом сравнение идёт непосредственно по указанным полям. Так достигается лучшая производительность, чем тотально проверять сразу все поля типа на идентичность.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории