Wydajność SQL 'jak' vs '='


82

To pytanie omija to, nad czym się zastanawiam, ale odpowiedzi nie dotyczą go dokładnie.

Wydawałoby się, że generalnie „=” jest szybsze niż „like”, gdy używa się symboli wieloznacznych. Wydaje się, że jest to powszechna mądrość. Załóżmy jednak, że mam kolumnę zawierającą ograniczoną liczbę różnych stałych, zakodowanych na stałe identyfikatorów varchar i chcę wybrać wszystkie wiersze pasujące do jednego z nich:

select * from table where value like 'abc%'

i

select * from table where value = 'abcdefghijklmn'

„Like” powinno wymagać tylko przetestowania pierwszych trzech znaków, aby znaleźć dopasowanie, podczas gdy „=” musi porównać cały ciąg. W tym przypadku wydaje mi się, że „lubienie” miałoby przewagę, przy czym wszystkie inne rzeczy byłyby równe.

Jest to ogólne, akademickie pytanie, więc nie powinno mieć znaczenia, która baza danych, ale powstało przy użyciu SQL Server 2005.


23
Jedną z głównych rzeczy, które pominąłeś, jest to, czy valuejest indeksowany. Jeśli tak, =jest to proste wyszukiwanie bez konieczności skanowania tabeli i usunie spodnie z każdego LIKEstwierdzenia, które do niego rzucisz.
Daniel DiPaolo

7
@Daniel Myślę, że to niepoprawne. A LIKEz symbolem wieloznacznym na końcu jest SARGable i dlatego będzie wykonywać przeszukiwanie zakresu na indeksie, bez widocznego skanowania tabeli. To szukanie zakresu może dość dobrze konkurować z =instrukcją, aw wielu przypadkach (np. Jeśli wszystkie spełniające wymagania wiersze znajdują się na jednej stronie, warunek nie jest nieprawdopodobny) może mieć dokładnie taką samą wydajność, pociągając za sobą taką samą liczbę odczytów.
ErikE

Moje „wszystkie inne rzeczy są równe” miały na celu omówienie kwestii „zindeksowanej czy nie”, ale wydaje się, że są przynajmniej pewne kontrowersje co do tego, jak wiele to zmieni, na podstawie moich komentarzy do innych odpowiedzi.
MickeyfAgain_BeforeExitOfSO

Zobacz moją odpowiedź. Początkowo testowałem bez indeksowania i wydajność jest identyczna (oba skany tabeli były dokładnie takie same). W moim scenariuszu testowym założyłem, że będzie indeksowany, w przeciwnym razie po co miałbyś się przejmować wydajnością?
JNK

5
Cała mowa o „polubieniu” w tym pytaniu i odpowiedziach sprawiają, że brzmimy jak grupa licealistów. Całkowicie.
JulianR

Odpowiedzi:


64

Zobacz https://web.archive.org/web/20150209022016/http://myitforum.com/cs2/blogs/jnelson/archive/2007/11/16/108354.aspx

Cytat z tego miejsca:

zasady używania indeksu z LIKE są luźno takie:

  • Jeśli kryteria filtru używają równości =, a pole jest indeksowane, najprawdopodobniej użyje INDEX / CLUSTERED INDEX SEEK

  • Jeśli kryteria filtru używają LIKE, bez symboli wieloznacznych (np. Gdybyś miał parametr w raporcie sieciowym, który MOŻE mieć%, ale zamiast tego użyjesz pełnego ciągu), użycie indeksu jest prawie tak samo prawdopodobne, jak # 1. Zwiększony koszt to prawie nic.

  • Jeśli kryteria filtru używają LIKE, ale z symbolem wieloznacznym na początku (jak w Name0 LIKE '% UTER'), prawdopodobieństwo użycia indeksu jest znacznie mniejsze, ale nadal może przynajmniej wykonać SKANOWANIE INDEKSU na pełnym lub częściowym zakresie indeks.

  • JEDNAK, jeśli twoje kryteria filtru używają LIKE, ale zaczyna się od STRING FIRST i zawiera symbole wieloznaczne gdzieś po tym (jak w Name0 LIKE 'COMP% ER'), to SQL może po prostu użyć INDEX SEEK, aby szybko znaleźć wiersze, które mają to samo pierwsze początkowe znaki, a następnie przejrzyj te wiersze, aby znaleźć dokładne dopasowanie.

(Należy również pamiętać, że silnik SQL może nadal nie używać indeksu w oczekiwany sposób, w zależności od tego, co jeszcze dzieje się w zapytaniu i do jakich tabel się przyłączasz. Silnik SQL zastrzega sobie prawo do przepisania zapytaj trochę, aby uzyskać dane w sposób, który według nich jest najbardziej wydajny i który może obejmować SKANOWANIE INDEKSU zamiast SZUKANIA INDEKSU)


1
ten link jest martwy
baxx,

2
@baxx kopia linku jest dostępna w maszynie zwrotnej. web.archive.org/web/20150209022016/http://myitforum.com/cs2/…
alphabet5 5

45

To wymierna różnica.

Uruchom następujące:

Create Table #TempTester (id int, col1 varchar(20), value varchar(20))
go

INSERT INTO #TempTester (id, col1, value)
VALUES
(1, 'this is #1', 'abcdefghij')
GO

INSERT INTO #TempTester (id, col1, value)
VALUES
(2, 'this is #2', 'foob'),
(3, 'this is #3', 'abdefghic'),
(4, 'this is #4', 'other'),
(5, 'this is #5', 'zyx'),
(6, 'this is #6', 'zyx'),
(7, 'this is #7', 'zyx'),
(8, 'this is #8', 'klm'),
(9, 'this is #9', 'klm'),
(10, 'this is #10', 'zyx')
GO 10000

CREATE CLUSTERED INDEX ixId ON #TempTester(id)CREATE CLUSTERED INDEX ixId ON #TempTester(id)

CREATE NONCLUSTERED INDEX ixTesting ON #TempTester(value)

Następnie:

SET SHOWPLAN_XML ON

Następnie:

SELECT * FROM #TempTester WHERE value LIKE 'abc%'

SELECT * FROM #TempTester WHERE value = 'abcdefghij'

Wynikowy plan wykonania pokazuje, że koszt pierwszej operacji, LIKEporównania, jest około 10 razy droższy niż =porównanie.

Jeśli możesz użyć =porównania, zrób to.


2
+1 za faktyczne przetestowanie. Jednak samo spojrzenie na plan pokazu może nie opowiedzieć całej historii. Zrobię kilka własnych testów i dam znać wszystkim, jeśli znajdę coś nieoczekiwanego.
Tom H

1
Tom - to prawda, ale dało mi to wystarczającą wskazówkę, że te dwie rzeczy NIE zostały przetworzone tak samo za kulisami.
JNK

1
Koszty przedstawione w planie wykonania są błędne. Nie odzwierciedlają rzeczywistej wydajności. W pierwszym planie są one oparte na szacunkowej liczbie wierszy, czyli 19.95kosztach SQL Server w dodatkowych 19 kluczach wyszukiwania, które nigdy nie pojawiają się w rzeczywistości (nawet w rzeczywistym planie wykonania pokazane koszty są oparte na szacunkowym koszcie poddrzewa)
Martin Smith

Właśnie zrobiłem Twój test, a także jeden z około 1 mln wierszy, aw obu przypadkach wydajność i plany zapytań były identyczne. To jest w SQL 2008, ponieważ nie mam 2005 na tym komputerze.
Tom H

1
@JNK - właśnie wypróbowałem - różnica jest znikoma, jednak różnica jest taka sama. 327 ms dla LIKE, 203 ms dla =. Spodziewam się, że gdybym przeprowadził więcej testów i wziął dokładne średnie, nie byłoby prawdziwej różnicy między #temp a prawdziwym stołem.
Will A

13

Należy również pamiętać, że podczas używania likeniektóre odmiany sql będą ignorować indeksy, a to zabije wydajność. Jest to szczególnie ważne, jeśli nie używasz wzorca „zaczyna się od”, jak w przykładzie.

Naprawdę powinieneś spojrzeć na plan wykonania zapytania i zobaczyć, co robi, zgaduj jak najmniej.

Biorąc to pod uwagę, wzorzec „zaczyna się od” może i jest zoptymalizowany na serwerze sql. To będzie użyć indeksu tabeli. EF 4,0 przełącza się likedo StartsWithtego samego powodu.


2
Żadna relacyjna baza danych warta swojej soli nie zignoruje indeksu, gdy podobny wzorzec jest częścią zapytania, a symbol wieloznaczny jest na końcu. To może być inna historia, jeśli wiążesz wartość, a baza danych obsługuje powiązanie niezależnie od przygotowania zapytania.
Dave W. Smith

To samo mówi mi moje przeczucie, ale mam tylko praktyczne doświadczenie z serwerem sql w tym zakresie, więc skupiłem się na tym konkretnie.
Blindy

7

Jeśli nie valuejest indeksowany, oba powodują skanowanie tabeli. Różnica wydajności w tym scenariuszu będzie znikoma.

Jeśli valuejest indeksowane, jak Daniel wskazuje w swoim komentarzu, =spowoduje to przeszukanie indeksu, które jest wydajnością O (log N). LIKE będzie (najprawdopodobniej - w zależności od sposobu selektywnej jest) w wyniku częściowego skanowania indeksu >= 'abc'i < 'abd'co będzie wymagało więcej wysiłku niż =.

Zauważ, że mówię tutaj o SQL Server - nie wszystkie DBMS-y będą miłe z LIKE.


Myślę, że nie wiesz, jak działa wyszukiwanie binarne. Zarówno =przypadek, jak i like '...%'przypadek zachowują się tak samo, jeśli sql rozpoznaje wzorzec (i robi), ponieważ w obu przypadkach poddrzewa są wybierane na podstawie relacji porównania.
Blindy

Och, tak. LIKE najprawdopodobniej zachowa się gorzej, chociaż nadal będzie O (log N), jeśli selektywność jest wystarczająco wysoka - O (log N), aby dowiedzieć się, od czego należy rozpocząć częściowe skanowanie, a następnie liczba odczytów w przód przez indeks do osiągnięto punkt końcowy 'abd'.
Will A

Tak, ale przykład PO zakłada, że ​​w tym zakresie jest tylko jedna wartość, więc mając to na uwadze, porównania będą identyczne.
Blindy

Ważna uwaga - nie jest do końca jasne, że tak mówił OP, ale myślę, że jest to bardziej prawdopodobne niż nie. W takim przypadku wydajność będzie prawie identyczna.
Will A

Przeszukiwanie zakresu LIKE prawdopodobnie będzie dość dobrze konkurować z instrukcją =, aw wielu przypadkach (na przykład jeśli wszystkie spełniające wymagania wiersze znajdują się na jednej stronie, warunek nie jest nieprawdopodobny) może mieć dokładnie taką samą wydajność, pociągając za sobą taką samą liczbę odczytów . Myślę, że stwierdzenie „będzie wymagać więcej wysiłku” jest błędnym stwierdzeniem ogólnym.
ErikE

6

Zadajesz złe pytanie. W bazach danych nie liczy się wydajność operatora, zawsze liczy się SARGability wyrażenia i pokrycie całego zapytania. Wydajność samego operatora jest w dużej mierze nieistotna.

Jak więc zrobić LIKEi =porównać pod względem SARGability? LIKE, gdy jest używane z wyrażeniem, które nie zaczyna się od stałej (np. gdy jest używane LIKE '%something'), jest z definicji inne niż SARGabale. Ale czy to czyni =lub LIKE 'something%'SARGable? Nie. Podobnie jak w przypadku każdego pytania o wydajność SQL, odpowiedź nie leży w zapytaniu o tekst, ale o wdrożonym schemacie. Te wyrażenia mogą być SARGable, jeśli istnieje indeks, który je spełnia.

Tak więc, prawdę mówiąc, istnieją niewielkie różnice między =i LIKE. Ale pytanie, czy jeden operator lub inny operator jest „szybszy” w SQL, jest jak pytanie „Co jedzie szybciej, czerwony samochód czy niebieski samochód?”. Powinieneś przestać zadawać pytania o rozmiar silnika i masę pojazdu, a nie o kolor ... Aby podejść do pytań o optymalizację tabel relacyjnych, miejscem do spojrzenia jest twoje indeksy i twoje wyrażenia w klauzuli WHERE (i innych klauzulach, ale zwykle zaczyna się od GDZIE).


5

Osobisty przykład z użyciem mysql 5.5: miałem sprzężenie wewnętrzne między 2 tabelami, jednym z 3 milionów wierszy i jednym z 10 000 wierszy.

W przypadku użycia polubienia w indeksie jak poniżej (bez symboli wieloznacznych) zajęło to około 30 sekund:

where login like '12345678'

używając „wyjaśnienia” otrzymuję:

wprowadź opis obrazu tutaj

W przypadku użycia znaku „=” w tym samym zapytaniu zajęło to około 0,1 sekundy:

where login ='600009'

Używając opcji „wyjaśnij”, otrzymuję:

wprowadź opis obrazu tutaj

Jak widać, likecałkowicie anulowano przeszukiwanie indeksu, więc zapytanie zajęło 300 razy więcej czasu.


Możesz także po prostu spojrzeć na plan wykonania, aby to potwierdzić
LittleBobbyTables - Au Revoir

dzięki @LittleBobbyTables. Przyjrzę się temu.
Aris

Nie wiem, czy to z powodu mojej ostatniej wersji (5.7), ale LIKE nie łamie tutaj mojego unikalnego indeksu.
Sebas

0

Może szukasz informacji o wyszukiwaniu pełnotekstowym .

W przeciwieństwie do wyszukiwania pełnotekstowego predykat LIKE Transact-SQL działa tylko na wzorcach znakowych. Ponadto nie można używać predykatu LIKE do wykonywania zapytań dotyczących sformatowanych danych binarnych. Ponadto zapytanie LIKE dotyczące dużej ilości nieustrukturyzowanych danych tekstowych jest znacznie wolniejsze niż równoważne zapytanie pełnotekstowe dotyczące tych samych danych . Zapytanie LIKE dotyczące milionów wierszy danych tekstowych może zwrócić kilka minut; podczas gdy zapytanie pełnotekstowe może zająć tylko kilka sekund lub krócej dla tych samych danych, w zależności od liczby zwracanych wierszy.


-1

Po pierwsze,

nie zawsze są równi

    select 'Hello' from dual where 'Hello  ' like 'Hello';

    select 'Hello' from dual where 'Hello  ' =  'Hello';

kiedy sprawy nie zawsze są równe, mówienie o ich wynikach nie jest tak istotne.

Jeśli pracujesz na łańcuchach i tylko zmiennych typu char, możesz mówić o wydajności. Ale nie używaj like i "=" jako ogólnie wymiennych.

Jak można zobaczyć w wielu postach (powyższe i inne pytania), w przypadkach, gdy są one równe, działanie podobnego jest wolniejsze ze względu na dopasowanie wzorców (sortowanie)


Jeśli 'Hello 'jest VARCHAR(domyślnie), masz rację, ale jeśli to jest CHAR, nie masz. Rzuć to na a CHAR(7)i oba zwracają prawdę. Poza tym, co do cholery robisz, skoro nie TRIMrobisz swoich varcharów? (uwaga: tak jest przynajmniej w SQL Server 2008r2)
abluejelly
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.