Like ‘[A-Z]QLServer’ vs. like ‘_QLServer’

by Marcin Goł on June 1st, 2008

ostatnio na forum wss.pl pojawił się wątek o przeszukiwaniu ciągów znakowych, punktem wyjścia dla niniejszego researchu był post Pawła Potasińskiego (aka brejk):

————————————————————————————-

Akodo_Shado:
select name from slowa
where name LIKE ‘kote[a-z]%’.
Efekt był piorunujący! 140 wyników w mig.

brejk:
I nic dziwnego. Przecież w tych 3,5 mln słów ile mogło się zaczynać od “kote” :-) Naturalne, że przy tak dobrej selektywności serwer może użyć indeksu na polu name (jakiegokolwiek indeksu, warto dodać).

Akodo_Shado:
Konsekwencja tego problemu jest też taka, że
o ile ‘kote[a-z]%’ robi się błyskawicznie o tyle
‘[a-z]%otek’ już nie koniecznie.

brejk:
Co w tym dziwnego? W drugim przypadku serwer musi przecież przeszukać praktycznie całą tabelę (od a do z).

————————————————————————————-

Paweł miał rację, to jasne … ale ale … od czego jest kombinacja CTRL + L :)

research rozpoczął się od zrobienia tabeli:

 create table test2 ( id int, ciag char(36),ciag_rev char(36));
 go
 declare @i int
 set @i = 0
 while (@i < 1000000)
 begin
 insert into test2 (id, ciag) values (@i,convert(char(36), newid()))
 set @i = @i + 1
 end

Pierwszy pomysł jaki mi wpadł do głowy był taki żeby zrobić drugą kolumnę z:

 update test2 set ciag_rev = reverse(ciag)
 go

mamy tabele bez indeksów, robię z niej dwa zapytania jak poniżej:
(mam w kolumnie ciag wartosc: ‘00006BB4-215F-424E-8B99-9A69A5C94A97′)

 select ciag,id from test2 where ciag like '[0-9]0006BB4-215F-424E-8B99-9A69A5C94A97'
 select ciag_rev,id from test2 where ciag_rev like '79A49C5A96A9-99B8-E424-F512-4BB6000[0-9]'

wybieram CTRL+L i widze:

p1

następnie robię 1x klastrowany + 2x nieklastrowany:

 create clustered index idx_id on test2 (id)
 create nonclustered index idx_ciag on test2 (ciag)
 create nonclustered index idx_ciag_rev on test2 (ciag_rev)

wykonuje te same 2 zapytania:

p2

zmienia się plan wykonania (nie mamy już table scan, mamy index seek … ale tak czy siak nie ma znaczenia czy nieznany znak znajduje się jako pierwszy czy jako ostatni! Sprawdzam dalej podmieniając aż do: ‘[0-9][0-9][0-9][0-9][0-9]BB4-215F-424E-8B99-9A69A5C94A97′ i dalej nic …. nie ma znaczenia czy nieznane znaki są na końcu czy na początku…
dopiero kiedy wykonam:

 select ciag,id from test2 where ciag like '%0006BB4-215F-424E-8B99-9A69A5C94A97'
 select ciag_rev,id from test2 where ciag_rev like '79A49C5A96A9-99B8-E424-F512-4BB6000%'

sprawa się wyjaśnia …

p3

Wniosek:
wskazywać SQL Serverowi czego może sie spodziewać a wówczas nawet jeśli nie będziemy znali pierwszych znaków to można próbować się ograniczać dzięki czemu:

- seek bedzie jeśli użyjemy:
[0-9]CiagZnakow
[a-zA-Z0-9]CiagZnakow

- scan będzie jeśli:
_CiagZnakow
%CiagZnakow

Jeśli mimo wszystko ktoś musi specyfikować ciągi znaków przy pomocy _ lub % to wówczas można spróbować dołożyć kolumnę z odwróconą kolejnością liter ?

From → SQL Server 2005

No comments yet

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS