Забелязах, че много малко хора разбират как работят индексите в SQL Server, особено включени колони. Независимо от това, индексите са чудесният начин за оптимизиране на заявките. Отначало също не разбрах представата за включените колони, но експериментите ми показаха, че са много полезни.
Да предположим, че имаме следната таблица и заявка:
CREATE TABLE Person ( PersonID int, FirstName varchar(100), LastName varchar(100), Age int, … … ) SELECT FirstName, LastName, Age FROM Person WHERE FirstName = 'John' and LastName = 'Smith'
Ясно е, че PersonID е първичен ключ. Да предположим, че имаме индекс по собствено и фамилно име, нека го наречем IX_Person_FirstNameLastName. Планът за изпълнение на такава заявка ще изглежда по следния начин:
- Намиране на всички редове с посочените собствени и фамилни имена с помощта на индексното дърво IX_Person_FirstNameLastName
- Откриване на действителното местоположение на реда на диска в индексните листа, преминаване към действителното местоположение и отчитане на възрастта.
Сега нека помислим, че тази заявка се изпълнява доста често. Всеки път трябва да изпълняваме 2 стъпки. Може ли да се оптимизира? В случай на MS SQL Server това не е проблем – можете да включите стойности направо в индекса с помощта на опцията INCLUDE.
CREATE INDEX IX_PERSON ON Person ( FirstName, LastName ) INCLUDE(Age)
Сега това поле не се използва по време на индексирането, но е включено в индекса. Какви проблеми можем да срещнем в това отношение? Когато индексираме таблица по определено поле, сървърът на базата данни трябва да изгради индексно дърво по това поле. Това означава, че трябва да променим индексното дърво при промяна на стойността. Когато стойностите се променят интензивно, това се превръща в проблематична и трудна задача за сървъра. Когато актуализирането стане твърде масивно, понякога е по-лесно да изпуснете индекса. Индексът оптимизира значително търсенето, но влияе негативно на операциите за вмъкване, изтриване и актуализиране.
Ако поле е просто включено в индекс, то не се използва по време на изграждането на индексно дърво и не го засяга, а стойността може лесно да се намери на листа на това дърво. Когато се извърши търсене по фамилни и собствени имена, сървърът търси всички собствени и фамилни имена от дървото и когато достигне листа (намира необходимата стойност на индекса), тогава в допълнение към показалеца към физическото местоположение от стойностите на редовете, съдържа и стойности на полета, включени в индекса. Това означава, че няма нужда да правите втората стъпка за превключване към физическото местоположение на линията и да я четете от там.
Тъй като не е необходимо да променяте дървото, когато променяте данните за възрастта, всички тези неща не засягат много операциите за промяна на данните. Не е нужно да променяме индекса, просто трябва да променим стойностите на листа на дървото. Ето защо дори масивна промяна в полето Age няма да има голямо влияние върху производителността. Със сигурност ще се отрази, но не толкова.
Доколкото знам, стойностите на клъстерирания индекс се включват автоматично в нивото на листа, но това трябва да се провери със спецификацията.
И така, кога използването на включените полета е от полза? Когато те се използват често в резултатите от заявката, но се променят от време на време. Пример е таблица с банкови транзакции. Такава таблица може да се състои от следните полета:номер на сметката, вид на транзакцията, дата, сума. Няма смисъл да индексираме по сумата, но можем да я включим в индекса и това значително ще ускори заявката.
За да извлечете реалния ефект от индексирането, заявките не трябва да избират всички полета, т.е. трябва да забравим за таблицата SELECT * FROM. Винаги преизчислявайте само полетата, от които наистина се нуждаете. И ако стойностите им попаднат в индекса, скоростта на изпълнение може да е доста висока.
Полезен инструмент:
dbForge Index Manager – удобна добавка за SSMS за анализиране на състоянието на SQL индексите и отстраняване на проблеми с фрагментацията на индекса.