Równa się (=) vs. PODOBNE

281

Czy podczas korzystania z SQL są jakieś korzyści wynikające z użycia =w WHEREklauzuli zamiast LIKE?

Bez specjalnych operatorów LIKEi czy =są tacy sami, prawda?

Travis
źródło
4
Może chcesz określić typ db ... mssql, mysql, oracle?
Allen Rice,
1
Twoje pytanie ma co najmniej 5głosy na tag operatora podobnego . Czy mogę prosić o zasugerowanie sql-like jako synonimu ?
Kermit
@FreshPrinceOfSO, zrobię to, gdy zdobędę wystarczającą reputację. Dzięki.
Travis,

Odpowiedzi:

271

Różni operatorzy

LIKEi =są różnymi operatorami. Większość odpowiedzi tutaj koncentruje się na obsłudze symboli wieloznacznych, co nie jest jedyną różnicą między tymi operatorami!

=to operator porównania, który działa na liczbach i ciągach. Podczas porównywania ciągów operator porównania porównuje całe ciągi .

LIKEjest operatorem łańcuchowym, który porównuje znak po znaku .

Aby skomplikować sprawę, obaj operatorzy stosują sortowanie, które może mieć istotny wpływ na wynik porównania.

Motywujący przykład

Najpierw zidentyfikujmy przykład, w którym operatorzy ci uzyskują oczywiście różne wyniki. Pozwól mi zacytować z podręcznika MySQL:

Zgodnie ze standardem SQL LIKE wykonuje dopasowanie dla poszczególnych znaków, dzięki czemu może generować wyniki inne niż operator =:

mysql> SELECT 'ä' LIKE 'ae' COLLATE latin1_german2_ci;
+-----------------------------------------+
| 'ä' LIKE 'ae' COLLATE latin1_german2_ci |
+-----------------------------------------+
|                                       0 |
+-----------------------------------------+
mysql> SELECT 'ä' = 'ae' COLLATE latin1_german2_ci;
+--------------------------------------+
| 'ä' = 'ae' COLLATE latin1_german2_ci |
+--------------------------------------+
|                                    1 |
+--------------------------------------+

Należy pamiętać, że ta strona podręcznika MySQL nazywa funkcji porównawczych String , a =nie jest omawiana, co oznacza, że =nie jest to ściśle funkcją porównania ciągów.

W jaki sposób =działa?

SQL Standardowe § 8.2 opisuje sposób =porównuje ciągi:

Porównanie dwóch ciągów znaków określa się w następujący sposób:

a) Jeśli długość w znakach X nie jest równa długości w znakach Y, wówczas dla celów porównania efektywnie krótszy ciąg zostaje zastąpiony kopią samego siebie, która została przedłużona do długości dłuższego ciągu przez konkatenację po prawej stronie jednego lub więcej znaków padu, gdzie znak padu jest wybierany na podstawie CS. Jeśli CS ma atrybut NO PAD, to znak pad jest znakiem zależnym od implementacji innym niż dowolny znak w zestawie znaków X i Y, który zestawia mniej niż dowolny ciąg znaków w CS. W przeciwnym razie znak padu to.

b) Wynik porównania X i Y jest podany przez kolejność zestawiania CS.

c) W zależności od sekwencji zestawiania dwa ciągi znaków mogą się równać, nawet jeśli mają różną długość lub zawierają różne ciągi znaków. Gdy operacje MAX, MIN, DISTINCT, odniesienia do kolumny grupującej oraz operatory UNION, EXCEPT i INTERSECT odnoszą się do ciągów znaków, konkretna wartość wybrana przez te operacje z zestawu takich równych wartości zależy od implementacji.

(Podkreślenie dodane.)

Co to znaczy? Oznacza to, że podczas porównywania ciągów =operator jest tylko cienkim owinięciem wokół bieżącego sortowania. Zestawienie to biblioteka, która ma różne reguły porównywania ciągów. Oto przykład sortowania binarnego z MySQL :

static int my_strnncoll_binary(const CHARSET_INFO *cs __attribute__((unused)),
                               const uchar *s, size_t slen,
                               const uchar *t, size_t tlen,
                               my_bool t_is_prefix)
{
  size_t len= MY_MIN(slen,tlen);
  int cmp= memcmp(s,t,len);
  return cmp ? cmp : (int)((t_is_prefix ? len : slen) - tlen);
}

To konkretne zestawienie zdarza się porównywać bajt po bajcie (dlatego nazywa się to „binarne” - nie nadaje żadnego specjalnego znaczenia ciągom znaków). Inne zestawienia mogą zapewniać bardziej zaawansowane porównania.

Na przykład tutaj jest zestawienie UTF-8, które obsługuje porównania bez rozróżniania wielkości liter. Kod jest za długi, aby go wkleić, ale przejdź do tego linku i przeczytaj treść my_strnncollsp_utf8mb4(). To zestawianie może przetwarzać wiele bajtów jednocześnie i może stosować różne transformacje (takie jak porównanie bez rozróżniania wielkości liter). =Operator jest całkowicie oderwana od napotkane pęczka.

W jaki sposób LIKEdziała?

SQL Standardowe § 8.5 opisuje sposób LIKEporównuje ciągi:

<predicate>

M LIKE P

jest prawdą, jeśli istnieje podział M na podciągi, tak aby:

i) Podciąg M jest sekwencją 0 lub więcej ciągłych <reprezentacji znaków> M, a każda <reprezentacja znaków> M jest częścią dokładnie jednego podłańcucha.

ii) Jeśli i-ty specyfikator podciągu P jest dowolnym specyfikatorem znaków, i -ty ​​podciąg M jest dowolną <reprezentacją znaków>.

iii) Jeśli i-ty specyfikator podciągu P jest arbitralnym specyfikatorem ciągu, to i-ty podciąg M jest dowolną sekwencją 0 lub więcej <reprezentacji znaków>.

iv) Jeśli i-ty specyfikator podciągu P nie jest ani arbitralnym specyfikatorem znaku, ani arbitralnym specyfikatorem łańcucha, to i-ty podciąg M jest równy temu specyfikatorowi podłańcuchu zgodnie z sekwencją zestawiania <podobnego predykatu>, bez dodanie znaków <spacja> do M i ma taką samą długość jak ten specyfikator podłańcucha.

v) Liczba podciągów M jest równa liczbie specyfikatorów podciągów P.

(Podkreślenie dodane.)

To dość trudne, więc załamajmy się. Pozycje ii i iii odnoszą się odpowiednio do symboli wieloznacznych _i %. Jeśli Pnie zawiera żadnych symboli wieloznacznych, obowiązuje tylko pozycja iv. Jest to przypadek zainteresowania PO.

W tym przypadku porównuje każdy „podłańcuch” (poszczególne znaki) Mz każdym podciągiem przy Pużyciu bieżącego sortowania.

Wnioski

Najważniejsze jest to, że porównując ciągi, =porównuje cały ciąg, jednocześnie LIKEporównując jeden znak na raz. Oba porównania wykorzystują bieżące zestawienie. Różnica ta prowadzi do różnych wyników w niektórych przypadkach, o czym świadczy pierwszy przykład w tym poście.

Którego powinieneś użyć? Nikt nie może ci tego powiedzieć - musisz użyć tego, który jest odpowiedni dla twojego przypadku użycia. Nie przedwcześnie optymalizuj, przełączając operatory porównania.

Mark E. Haase
źródło
4
„EQUALS porównuje dwa bajty danych bajt po bajcie”: nadmiernie uproszczone i zbyt często nieprawdziwe, ponieważ zachowanie EQUALS (=) można modyfikować za pomocą polecenia COLLATE, co powoduje porównywanie klas znaków zamiast znaków. Np. Patrz dev.mysql.com/doc/refman/5.0/en/charset-collate.html (MySQL) lub sqlmag.com/blog/forcing-collation-where-clause-22-jun-2011 (SQL Server).
Peter B
11
To jest poprawna odpowiedź. Wiemy, co LIKErobi, ale ta odpowiedź niesamowicie wyjaśnia, że ​​używanie LIKEbez %lub z _prezentem wcale nie jest tym samym, co używanie =. Niech twoja odpowiedź otrzyma tysiąc głosów pozytywnych.
rinogo
1
@ Uważaj, że to nie może być prawda. Jeśli moje pole varchar zawiera wartość 'AbCdEfG', a ja tak WHERE MyCol = 'abcdefg', nadal otrzymuję ten wiersz z powrotem, mimo że wyraźnie nie są one równoważne bajt po bajcie
Kip
1
Zarówno PeterB, jak i @Kip podnoszą dobre punkty. Poprawiłem swoją odpowiedź, aby spróbować wyjaśnić, w jaki sposób sortowanie wpływa na tych operatorów.
Mark E. Haase
2
To już nie wydaje się prawdą: set charset latin1; SELECT 'ä' = 'ae' COLLATE latin1_german2_ci;daje 0, a także SELECT 'ä' LIKE 'ae' COLLATE latin1_german2_ci;daje 0.
joanq
170

Operator równości (=) to „operator porównania porównujący dwie wartości równości”. Innymi słowy, w instrukcji SQL nie zwróci wartości true, chyba że obie strony równania są równe. Na przykład:

SELECT * FROM Store WHERE Quantity = 200;

Operator LIKE „implementuje porównanie dopasowania wzorca”, który próbuje dopasować „wartość ciągu do ciągu wzorca zawierającego znaki wieloznaczne”. Na przykład:

SELECT * FROM Employees WHERE Name LIKE 'Chris%';

LIKE jest generalnie używane tylko z łańcuchami i równa się (jak sądzę) jest szybsza. Operator równości traktuje znaki wieloznaczne jako znaki dosłowne. Różnica w zwracanych wynikach jest następująca:

SELECT * FROM Employees WHERE Name = 'Chris';

I

SELECT * FROM Employees WHERE Name LIKE 'Chris';

Zwróciłby ten sam wynik, chociaż użycie LIKE zwykle trwa dłużej, ponieważ jest to dopasowanie wzorca. Jednak,

SELECT * FROM Employees WHERE Name = 'Chris%';

I

SELECT * FROM Employees WHERE Name LIKE 'Chris%';

Zwróciłoby różne wyniki, przy czym użycie „=” powoduje tylko wyniki ze zwróconym „Chris%”, a operator LIKE zwróci wszystko zaczynające się od „Chris”.

Mam nadzieję, że to pomaga. Kilka dobrych informacji można znaleźć tutaj .

achinda99
źródło
108
Mam wrażenie, że OP wie, kiedy używać LIKE, a kiedy =. Po prostu zastanawia się, czy istnieje różnica w wydajności, gdy nie ma symboli zastępczych. Ta odpowiedź krótko dotyczy tego, ale wydaje mi się, że 95% tej odpowiedzi nie jest tak naprawdę istotne.
Outlaw Programmer
1
Bardzo prawdziwe. Nie jestem pewien, czy pytanie było takie samo, kiedy na nie odpowiedziałem. Jeśli tak, to tęskniłem za częścią, która pytała o przedstawienie. Dzięki za obserwację.
achinda99
9
Ta odpowiedź jest okropna. LIKE i '=' są całkowicie odrębnymi operatorami, ale akurat zachowują się podobnie w niektórych małych podzbiorach. Ze względu na potomstwo przeczytaj resztę odpowiedzi tutaj lub przynajmniej google dla „mysql like”, zanim zapiszesz to w pamięci.
Mark E. Haase
3
Z drugiej strony, ta odpowiedź odpowiedziała na pytanie, na które miałem i szukałem google. Czasami jest tak samo dobrze, jeśli odpowiedź odpowiada na tytuł pytania, jak treść.
CorayThan,
Warto pamiętać, gdy używasz char i varchar2. Jeśli porównasz char z char. Przed porównaniem bazy danych najpierw przekonwertuj długość pierwszej „zmiennej” na taką samą jak druga. Jeśli porównasz char i varchar2, baza danych nic nie zrobi. docs.oracle.com/cd/A64702_01/doc/server.805/a58236/c_char.htm
xild
18

To jest kopia / wklej innej mojej odpowiedzi na pytanie SQL „jak” vs ”=„ wydajność :

Osobisty przykład z użyciem mysql 5.5: Miałem wewnętrzne połączenie między 2 tabelami, jednym z 3 milionów wierszy i jednym z 10 tysięcy wierszy.

Podczas korzystania z polubienia w indeksie jak poniżej (bez symboli wieloznacznych) zajęło to około 30 sekund:

where login like '12345678'

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

wprowadź opis zdjęcia tutaj

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

where login ='12345678'

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

wprowadź opis zdjęcia tutaj

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

Aris
źródło
17

LIKEi =są różne. LIKEto, czego byś użył w wyszukiwaniu. Umożliwia także stosowanie symboli wieloznacznych, takich jak _( symbol wieloznaczny prosty) i %(symbol wieloznaczny wieloznakowy).

= należy użyć, jeśli chcesz dokładnie dopasować i będzie to szybsze.

Ta strona wyjaśnia LIKE

WalterJ89
źródło
11

Jedną różnicą - oprócz możliwości używania symboli wieloznacznych z LIKE - są spacje końcowe: operator = ignoruje spację końcową, ale LIKE nie.

ISW
źródło
4
Chociaż dotyczy to MySQL i MS SQL, nie dotyczy to PostgreSQL.
Bruno,
10

Zależy od systemu baz danych.

Ogólnie bez znaków specjalnych, tak, = i LIKE są takie same.

Niektóre systemy baz danych mogą jednak traktować ustawienia sortowania inaczej w zależności od operatora.

Na przykład w porównaniach MySQL z ciągami = na ciągach domyślnie zawsze nie jest rozróżniana wielkość liter, więc LIKE bez znaków specjalnych jest takie samo. W niektórych innych RDBMS LIKE nie rozróżnia wielkości liter, podczas gdy = nie.

.ıu
źródło
Czy istnieje coś takiego jak przegląd tej osobliwości?
Gumbo,
9

W tym przykładzie przyjmujemy za pewnik, że varcharcol nie zawiera ''i nie ma pustej komórki względem tej kolumny

select * from some_table where varcharCol = ''
select * from some_table where varcharCol like ''

Pierwszy daje wynik w wierszu 0, a drugi pokazuje całą listę. = jest ściśle dopasowaną wielkością, podczas gdy podobnie działa jak filtr. jeśli filtr nie ma kryteriów, wszystkie dane są prawidłowe.

jak - ze względu na swój cel działa nieco wolniej i jest przeznaczony do użytku z varchar i podobnymi danymi.

Arnab
źródło
6

Jeśli szukasz dokładnego dopasowania, możesz użyć zarówno, = i JAK.

Użycie „=” jest w tym przypadku nieco szybsze (wyszukiwanie dokładnego dopasowania) - możesz to sprawdzić, wykonując dwukrotnie to samo zapytanie w SQL Server Management Studio, raz używając „=”, raz używając „LIKE” i następnie używając „Zapytania” / „Uwzględnij aktualny plan wykonania”.

Wykonaj dwa zapytania i powinieneś zobaczyć swoje wyniki dwa razy oraz dwa rzeczywiste plany wykonania. W moim przypadku zostały podzielone 50% w porównaniu do 50%, ale plan wykonania „=” ma mniejszy „szacunkowy koszt drzewa podrzędnego” (wyświetlany, gdy najedziesz myszką na najbardziej lewe pole „WYBIERZ”) - ale znowu, to naprawdę niezbyt duża różnica.

Ale kiedy zaczniesz wyszukiwać za pomocą symboli wieloznacznych w wyrażeniu LIKE, wydajność wyszukiwania będzie przygaszona. Wyszukiwanie „LIKE Mill%” może być nadal dość szybkie - SQL Server może użyć indeksu w tej kolumnie, jeśli taki istnieje. Wyszukiwanie „LIKE% expression%” jest strasznie wolne, ponieważ jedynym sposobem, w jaki SQL Server może spełnić to wyszukiwanie, jest wykonanie pełnego skanowania tabeli. Uważaj więc na swoje PODOBNE!

Marc

marc_s
źródło
-1 jak nie, nie zawsze jest to odrobinę szybsze. Jeśli kolumna jest indeksowana za pomocą% mystring%, jest o kilka rzędów wielkości wolniejsza. Rzeczywiście, wszelkie normy kodowe warte swojej soli będą miały rygorystyczne wytyczne dotyczące tego, kiedy i kiedy nie należy używać tak jak w przypadku bazy danych większej niż myszka micky.
Cruachan
1
Nigdy nie powiedziałem, że będzie nieco wolniejszy we wszystkich przypadkach - powiedziałem, że będzie nieco wolniejszy, jeśli szukasz DOKŁADNEGO dopasowania. Oczywiście wyszukiwanie za pomocą LIKE i używanie symboli wieloznacznych, zwłaszcza na początku i na końcu wyszukiwanego elementu, jest DUŻO wolniejsze, bez wątpienia.
marc_s
I tak, zgadzam się - należy mieć jasne wytyczne, kiedy używać LIKE, czy nie (tylko wtedy, gdy POTRZEBUJESZ wyszukiwać za pomocą symboli wieloznacznych). Ale z drugiej strony - w teorii nie ma różnicy między teorią a praktyką, ale w praktyce .......
marc_s
6

Użycie = pozwala uniknąć konfliktów ze znakami wieloznacznymi i znakami specjalnymi w łańcuchu podczas tworzenia zapytania w czasie wykonywania.

To sprawia, że ​​życie programisty jest łatwiejsze, ponieważ nie trzeba uciec od wszystkich specjalnych znaków wieloznacznych, które mogą wpaść w klauzulę LIKE i nie generować zamierzonego wyniku. W końcu = = scenariusz przypadków użycia 99%, ucieczka przed nimi za każdym razem byłaby uciążliwa.

przewraca oczami w latach 90

Podejrzewam też, że jest nieco wolniejszy, ale wątpię, czy jest znaczący, jeśli we wzorze nie ma symboli wieloznacznych.

Moneta
źródło
6

Aby odpowiedzieć na pierwotne pytanie dotyczące wydajności, sprowadza się to do wykorzystania indeksu . Kiedy następuje proste skanowanie tabeli, „LIKE” i „=” są identyczne . W przypadku indeksów zależy to od sposobu utworzenia klauzuli LIKE. Mówiąc dokładniej, jaka jest lokalizacja symboli wieloznacznych?


Rozważ następujące:

CREATE TABLE test(
    txt_col  varchar(10) NOT NULL
)
go

insert test (txt_col)
select CONVERT(varchar(10), row_number() over (order by (select 1))) r
  from master..spt_values a, master..spt_values b
go

CREATE INDEX IX_test_data 
    ON test (txt_col);
go 

--Turn on Show Execution Plan
set statistics io on

--A LIKE Clause with a wildcard at the beginning
DBCC DROPCLEANBUFFERS
SELECT txt_Col from test where txt_col like '%10000'
--Results in
--Table 'test'. Scan count 3, logical reads 15404, physical reads 2, read-ahead reads 15416, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Index SCAN is 85% of Query Cost

--A LIKE Clause with a wildcard in the middle
DBCC DROPCLEANBUFFERS
SELECT txt_Col from test where txt_col like '1%99'
--Results in
--Table 'test'. Scan count 1, logical reads 3023, physical reads 3, read-ahead reads 3018, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Index Seek is 100% of Query Cost for test data, but it may result in a Table Scan depending on table size/structure

--A LIKE Clause with no wildcards
DBCC DROPCLEANBUFFERS
SELECT txt_Col from test where txt_col like '10000'
--Results in
--Table 'test'. Scan count 1, logical reads 3, physical reads 2, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Index Seek is 100% of Query Cost
GO

--an "=" clause = does Index Seek same as above
DBCC DROPCLEANBUFFERS
SELECT txt_Col from test where txt_col = '10000'
--Results in
--Table 'test'. Scan count 1, logical reads 3, physical reads 2, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Index Seek is 100% of Query Cost
GO


DROP TABLE test

Może występować nieznaczna różnica w tworzeniu planu zapytań przy użyciu „=” vs „LIKE”.

Laramie
źródło
4

Oprócz symboli wieloznacznych różnica między =AND LIKEbędzie zależeć zarówno od rodzaju serwera SQL, jak i od typu kolumny.

Weź ten przykład:

CREATE TABLE testtable (
  varchar_name VARCHAR(10),
  char_name CHAR(10),
  val INTEGER
);

INSERT INTO testtable(varchar_name, char_name, val)
    VALUES ('A', 'A', 10), ('B', 'B', 20);

SELECT 'VarChar Eq Without Space', val FROM testtable WHERE varchar_name='A'
UNION ALL
SELECT 'VarChar Eq With Space', val FROM testtable WHERE varchar_name='A '
UNION ALL
SELECT 'VarChar Like Without Space', val FROM testtable WHERE varchar_name LIKE 'A'
UNION ALL
SELECT 'VarChar Like Space', val FROM testtable WHERE varchar_name LIKE 'A '
UNION ALL
SELECT 'Char Eq Without Space', val FROM testtable WHERE char_name='A'
UNION ALL
SELECT 'Char Eq With Space', val FROM testtable WHERE char_name='A '
UNION ALL
SELECT 'Char Like Without Space', val FROM testtable WHERE char_name LIKE 'A'
UNION ALL
SELECT 'Char Like With Space', val FROM testtable WHERE char_name LIKE 'A '
  • Używając MS SQL Server 2012 , końcowe spacje zostaną zignorowane w porównaniu, z wyjątkiem sytuacji, LIKEgdy typ kolumny to VARCHAR.

  • Używając MySQL 5.5 , końcowe spacje będą ignorowane dla =, ale nie dla LIKE, zarówno z jak CHARi VARCHAR.

  • Korzystając z PostgreSQL 9.1 , spacje są znaczące zarówno przy użyciu , jak =i LIKEprzy użyciu VARCHAR, ale nie przy CHAR(patrz dokumentacja ).

    Zachowanie się LIKErównież różni się CHAR.

    Korzystanie z tych samych danych, co powyżej, użycie wyraźnego CASTw nazwie kolumny również robi różnicę :

    SELECT 'CAST none', val FROM testtable WHERE char_name LIKE 'A'
    UNION ALL
    SELECT 'CAST both', val FROM testtable WHERE
        CAST(char_name AS CHAR) LIKE CAST('A' AS CHAR)
    UNION ALL
    SELECT 'CAST col', val FROM testtable WHERE CAST(char_name AS CHAR) LIKE 'A'
    UNION ALL
    SELECT 'CAST value', val FROM testtable WHERE char_name LIKE CAST('A' AS CHAR)

    Zwraca to tylko wiersze dla „CAST zarówno”, jak i „CAST col”.

Bruno
źródło
2

Słowo kluczowe LIKE niewątpliwie ma dołączony „znacznik ceny wykonania”. To powiedziawszy, jeśli masz pole wejściowe, które potencjalnie może zawierać znaki wieloznaczne do użycia w zapytaniu, zaleciłbym użycie LIKE tylko, jeśli dane wejściowe zawierają jedną z symboli wieloznacznych. W przeciwnym razie użyj standardu równego porównaniu.

Z poważaniem...

Josh Stodola
źródło
1

Naprawdę sprowadza się do tego, co chcesz zrobić zapytanie. Jeśli masz na myśli dokładne dopasowanie, użyj =. Jeśli masz na myśli bardziej fuzzier, użyj LIKE. Mówienie, co masz na myśli, jest zwykle dobrą polityką z kodem.

nie nie
źródło
1

W Oracle „podobne” bez symboli wieloznacznych zwróci ten sam wynik co „równa się”, ale może wymagać dodatkowego przetwarzania. Według Toma Kyte'a Oracle będzie traktować „jak” bez symboli wieloznacznych jako „równa się” przy użyciu literałów, ale nie przy użyciu zmiennych powiązania.

Chris B.
źródło
0

=i LIKEto nie to samo;

  1. = pasuje do dokładnego ciągu
  2. LIKE dopasowuje ciąg znaków, który może zawierać symbole wieloznaczne (%)
Baretta
źródło
2
Niewystarczająca odpowiedź
Można go używać bez symboli wieloznacznych. Pytanie dotyczyło różnicy dla tych samych przypadków.
M-Razavi