------------INCLUDE------------------------------ -- Naredbom INCLUDE se u indeks dodaju kolone iz tabele koje nisu deo kljuca pretrage. -- Ove kolone su pridruzene indeksu kao sto je pridruzena npr. kolona klasterovanog kljuca. -- Od vrednosti pridruzenih kolona ne zavisi raspored zapisa u indeksu. -- Kreiranje indeksa nad kolonom FirstName sa pridruzenom kolonom LastName: create nonclustered index idx_first_incl on person(firstname) include (lastname) -- Neklasterovanom indeksu se moze pridruziti i vise kolona create nonclustered index idx_incl_sve on person(firstname) include (lastname, middlename, suffix, ModifiedDate, PersonType, Title) -- Nema potrebe pridruzivati i primarni kljuc jer je on vec pridruzen automatski. -- Kakva je razlika izmedju indeksa sa include idx_first_incl i sledeceg kompozitnog indeksa: create nonclustered index idx_first_last on person(firstname, lastname) -- I jedan i drugi indeks sadrze kolone firstname i lastname. -- Razlika je u tome sto su kod kompozitnih svi zapisi sortirani po celom kljucu pretrage, -- po FirstName, pa po LastName. -- Kod idx_first_incl su zapisi sortirani samo po FirstName, po LastName nisu, -- LastName kolona je samo pridruzena. -- Zasto bismo onda uopste koristili indekse sa include? -- 1. razlog - SQL server dopusta duzinu kljuca od 16 kolona ili 900 bajtova. -- Medjutim, sa include mozemo da dodamo proizvoljan broj non-key kolona u indeks. -- 2. razlog - Include-ovane kolone se cuvaju samo u listovima, ne koriste se za pretragu, -- nisu sortirane, pa ih nema u cvorovima stabla. Samim tim, zauzimaju manje memorije -- nego dodatna kolona u kljucu pretrage, koja se nalazi i u listovima i u stablu. alter table person add constraint PK_id primary key (BusinessEntityID) create nonclustered index idx_first on person(firstname) drop index person.idx_first create nonclustered index idx_first_last on person(firstname, lastname) drop index person.idx_first_last create nonclustered index idx_first_incl on person(firstname) include (lastname) drop index person.idx_first_incl exec sp_spaceused person select firstname, lastname from person where firstname like 'Martin' --samp PK 0.17, data 1600KB, index 16KB --sa idx_first 0.063, data 1600KB, index 544KB --sa idx_first_last 0.0033, data 1600KB, index 808KB --sa idx_first_incl 0.0033, data 1600KB, index 808KB -- Kompozitni indeks i indeks sa include su dali isto vreme izvrsavanja. -- Razlika u memoriji je trenutno zanemarljiva, nije cak ni na nivou KB. -- Ova razlika ce doci do izrazaja kod vecih indeksa. create nonclustered index idx_sve on person(firstname, lastname, middlename, suffix, ModifiedDate, PersonType, Title) drop index person.idx_sve create nonclustered index idx_sve_incl on person(firstname) include (lastname, middlename, suffix, ModifiedDate, PersonType, Title) drop index person.idx_sve_incl exec sp_spaceused person select * from person where firstname like 'Martin' -- sa idx_sve 0.0033, data 1600KB, index 1600KB -- sa idx_sve_incl 0.0033, data 1600KB, index 1576KB -- Vreme izvrsavanja je i dalje isto, -- ali je sada razlika u memoriji dosla do izrazaja -- ZAKLJUCAK: Kolone koje se koriste za pretragu, grupisanje ili sortiranje -- treba staviti u kompozitni indeks, -- a one koje se nalaze samo u SELECT delu treba pridruziti indeksu -- jer nam za pretragu nisu potrebne, pa ne moraju biti deo kljuca pretrage, -- a ovako mogu da ustede memoriju. ---------KLASTEROVANI INDEKSI------------------- -- Klasterovani indeksi ne moraju da budu samo primarni kljucevi. -- Npr. moguce je napraviti klasterovani indeks nad kolonom LastName. -- Ali prvo treba ukloniti postojeci klasterovani indeks. -- Jedna tabela moze imati samo jedan kl. indeks koji fizicki sortira podatke. alter table person drop constraint PK_id -- Sada moze da se napravi novi klasterovani indeks create clustered index cl_last on person(lastname) exec sp_spaceused person --sa PK_id, data 1600KB, index 16KB --sa cl_last, data 1736KB, index 16KB -- Sama tabela zauzima vise memorije sa cl_last nego sa primarnim kljucem. -- Razlog - Kada je klasterovani kljuc PK, i uz to jos auto-increment, -- memorijske stranice tabele se popunjavaju bez ostavljanja slobodnog prostora, -- jer se racuna da nece biti INSERT operacija koje ubacuju vrste u sredinu tabele. -- Kada klasterovani kljuc nije PK, a nije ni UNIQUE, postoji realna mogucnost -- da se cesto ubacuju nove vrste u svaki deo tabele. Zato se u svakoj memorijskoj stranici -- ostavlja dodatni prazan prostor za buduce nove vrste. Samim tim, cela tabela zauzima -- vise memorijskih stranica. select * from person where lastname like 'Adams' -- sa cl_last 0.0033 -- nema potrebe za include-ovanjem svih select-ovanih kolona, -- jer se sve vec nalaze u klasterovanom indeksu, -- jer je klasterovani indeks zapravo sama tabela, sortirana po klasterovanom kljucu. -- Poprilicna usteda memorije u odnosu na resenje sa neklasteerovanim indeksom. -- Koriscenje INCLUDE naredbe sa klasterovanim indeksom nema smisla, -- sve kolone van kljuca pretrage su vec pridruzene indeksu. -- Sa druge strane, moguce je napraviti klasterovani kompozitni indeks: drop index person.cl_last create clustered index cl_last_first on person(lastname, firstname) -- data 1664KB, index 32KB -- data je manji nego kod cl_last jer ima manje ponavljanja vrednosti kljuca, -- pa je ostavljeno manje slobodnog prostora u mmorijskim stranicama -- indeks zauzima duplo vise nego kod cl_last zbog duplo veceg kljuca. select * from person order by lastname, firstname -- bez indeksa 1.49 -- sa cl_last_first 0.178 select lastname, firstname, count(*) from person group by lastname, firstname -- bez indeksa 1.5 -- sa cl_last_first 0.2 -- Ako zelimo da klasterovani indeks ostane nad kolonom LastName, -- a da ipak napravimo primarni kljuc nad ID kolonom, -- moguce je napraviti neklasteerovani primarni kljuc: alter table person add constraint PK_id primary key nonclustered (BusinessEntityID) ------------------------------------------------------------------------- alter table person drop constraint PK_id drop index person.cl_last_first -- Indeksi mogu i da uspore izvrsavanje nekih upita. -- Takav ja slucaj sa INSERT, UPDATE i DELETE naredbama. -------------INSERT------------------------------- insert into person(BusinessEntityID, PersonType, FirstName, LastName, ModifiedDate) values (30000,'EM','Pera','Peric','2015-12-3') -- bez indeksa 0.01 -- table insert 100% - 0.01, ubacivanje nove vrste u tabelu -- assert 0% - provera da li nova vrsta narusava ogranicenja nad tabelom -- sa PK_id 0.01 -- clustered index insert - isto sto i table insert, 100% - 0.01 -- assert 0% -- klasterovani indeks ne usporava INSERT naredbu -- sa 1 ncl (idx_last), 0.02 -- clustered index insert 0.02, duplo vise nego bez neklasterovanog -- razlog: nova vrsta, osim u samu tabelu, sada mora da se ubaci i u ncl indeks -- sa 2 ncl (idx_last i idx_first), 0.03 -- jos jedan ncl indeks, i u njega mora da se ubaci nova vrsta alter table person add constraint PK_id primary key (BusinessEntityID) create nonclustered index idx_last on person(lastname) create nonclustered index idx_first on person(firstname) drop index person.idx_last drop index person.idx_first -- Neklasterovani indeksi usporavaju naredbe modifikacije, -- jer svaka promena tabele mora da se odrazi i u indeksima. -------------UPDATE----------------------------------------- update person set lastname = 'Jovanovic' where firstname = 'Pera' -- samo klasterovani nad id 0.1862 -- clustered index scan 93%, 0.1726 -- compute scalar 0.003, racunanje izraza pod set (pretpostavlja da moze postojati neki izraz) -- clustered index update 0.01, promena vrednosti u jednoj vrsti tabele create nonclustered index idx_last on person(lastname) -- sa idx_last 0.2 -- clustered index update 10% - 0.02 -- update traje duplo duze jer treba azurirati vrstu i u tabeli i u neklasterovanom indeksu -- Za razliku od INSERT naredbe, UPDATE naredbu je moguce i ubrzati indeksom, jer ima WHERE deo drop index person.idx_last create nonclustered index idx_first on person(firstname) -- sa idx_first 0.013 -- index seek 25%-0.003, pretraga kroz indeks umesto citanja cele tabele, znatno ubrzanje -- clustered index update 75%-0.01 -- mora da azurira tabelu, ali ne mora i neklasterovani indeks -- jer se azurira samo kolona LastName, a ona nije deo indeksa idx_first -- DELETE ima slicno ponasanje kao UPDATE