Производительность SQL 'like' vs '='

82

Этот вопрос охватывает то, что мне интересно, но ответы на него не совсем точны.

Может показаться, что в целом «=» быстрее, чем «нравится» при использовании подстановочных знаков. Похоже, это общепринятое мнение. Однако давайте предположим, что у меня есть столбец, содержащий ограниченное количество различных фиксированных, жестко запрограммированных идентификаторов varchar, и я хочу выбрать все строки, соответствующие одному из них:

select * from table where value like 'abc%'

и

select * from table where value = 'abcdefghijklmn'

'Like' должно проверять только первые три символа, чтобы найти совпадение, тогда как '=' должен сравнивать всю строку. В этом случае мне кажется, что подобное имело бы преимущество при прочих равных условиях.

Это общий академический вопрос, поэтому он не имеет значения, какая БД возникла при использовании SQL Server 2005.

MickeyfAgain_BeforeExitOfSO
источник
23
Одна важная вещь, которую вы упустили, value- индексируется или нет . Если это так, то =это простой поиск без необходимости сканирования таблицы, и он избавит вас от любого LIKEутверждения, которое вы ему бросите.
Даниэль ДиПаоло
7
@ Дэниел, я думаю, это неправильно. A LIKEс подстановочным знаком в конце является SARGable и, таким образом, будет выполнять поиск диапазона по индексу, без просмотра таблицы. Этот поиск диапазона может довольно легко конкурировать с =оператором, и во многих случаях (например, если все удовлетворяющие строки находятся на одной странице, что немаловажно) может иметь точно такую ​​же производительность, что влечет за собой одинаковое количество чтений.
ErikE
Мое «при прочих равных условиях» было предназначено для того, чтобы охватить проблему «проиндексировано или нет», но, похоже, есть по крайней мере некоторые разногласия по поводу того, какая разница, согласно моим комментариям к другим ответам.
MickeyfAgain_BeforeExitOfSO
Смотрите мой ответ. Первоначально я тестировал неиндексированный, и производительность была идентична (оба сканирования таблиц были абсолютно одинаковыми). В своем тестовом сценарии я предполагал, что он будет проиндексирован, иначе зачем вам вообще заботиться о производительности?
JNK
5
Все разговоры о «лайках» в этом вопросе и ответах заставляют нас походить на кучку старшеклассниц. Полностью.
JulianR

Ответы:

64

См. Https://web.archive.org/web/20150209022016/http://myitforum.com/cs2/blogs/jnelson/archive/2007/11/16/108354.aspx

Цитата оттуда:

правила использования индекса с LIKE примерно такие:

  • Если в ваших критериях фильтрации используется equals = и поле проиндексировано, то, скорее всего, будет использоваться INDEX / CLUSTERED INDEX SEEK.

  • Если в ваших критериях фильтрации используется LIKE без подстановочных знаков (например, если у вас есть параметр в веб-отчете, который МОЖЕТ иметь%, но вместо этого вы используете полную строку), то вероятность использования индекса примерно такая же, как # 1. Повышенная стоимость почти ничего.

  • Если в ваших критериях фильтрации используется LIKE, но с подстановочным знаком в начале (как в Name0 LIKE '% UTER'), гораздо меньше шансов использовать индекс, но он все равно может, по крайней мере, выполнить сканирование индекса для полного или частичного диапазона индекс.

  • ОДНАКО, если в ваших критериях фильтра используется LIKE, но он начинается с STRING FIRST и имеет подстановочные знаки где-то ПОСЛЕ этого (как в Name0 LIKE 'COMP% ER'), тогда SQL может просто использовать INDEX SEEK, чтобы быстро найти строки с одинаковыми первыми начальные символы, а затем просмотрите эти строки для точного совпадения.

(Также имейте в виду, что механизм SQL по-прежнему может не использовать индекс так, как вы ожидаете, в зависимости от того, что еще происходит в вашем запросе и к каким таблицам вы присоединяетесь. Механизм SQL оставляет за собой право переписать ваш выполнить небольшой запрос, чтобы получить данные наиболее эффективным способом, который может включать сканирование по индексу вместо поиска по индексу)

BonyT
источник
1
эта ссылка мертва
baxx
2
@baxx копия ссылки доступна на машине возврата. web.archive.org/web/20150209022016/http://myitforum.com/cs2/...
alphabet5
45

Это ощутимая разница.

Выполните следующее:

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)

Потом:

SET SHOWPLAN_XML ON

Потом:

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

SELECT * FROM #TempTester WHERE value = 'abcdefghij'

Полученный план выполнения показывает, что стоимость первой операции LIKEсравнения примерно в 10 раз дороже, чем =сравнение.

Если вы можете использовать =сравнение, сделайте это.

JNK
источник
2
+1 за собственное тестирование. Однако простой взгляд на выставочный план может не рассказать всей истории. Я собираюсь провести собственное тестирование и сообщу всем, если найду что-нибудь неожиданное.
Tom H
1
Том - правда, но это дало мне достаточно указаний на то, что эти двое НЕ обрабатывались одинаково за кулисами.
JNK
1
Затраты, указанные в плане выполнения, неверны. Они не отражают фактическую производительность. В первом плане они основаны на оценочном количестве строк, 19.95поэтому SQL Server требует дополнительных 19 ключевых поисков, которые никогда не материализуются в действительности (даже в фактическом плане выполнения показанные затраты основаны на оценочной стоимости поддерева)
Мартин Смит
Я только что провел ваш тест, а также тест с примерно 1 млн строк, и в обоих случаях производительность и планы запросов были идентичны. Это на SQL 2008, поскольку у меня нет 2005 на этой машине.
Tom H
1
@JNK - просто попробовал - разница незначительная, но разница такая же. 327 мс для LIKE, 203 мс для =. Я ожидаю, что если я проведу больше тестов и получу точные средние значения, реальной разницы между #temp и реальной таблицей не будет.
Will A
13

Вы также должны иметь в виду, что при использовании likeнекоторые разновидности sql будут игнорировать индексы, и это снизит производительность. Это особенно верно, если вы не используете шаблон «начинается с», как в вашем примере.

Вам действительно стоит взглянуть на план выполнения запроса и посмотреть, что он делает, стараясь угадывать как можно меньше.

При этом шаблон «начинается с» может быть оптимизирован в sql server. Он будет использовать индекс таблицы. EF 4.0 перешел likeна именно StartsWithпо этой причине.

Слепой
источник
2
Ни одна достойная реляционная база данных не будет игнорировать индекс, если аналогичный шаблон является частью запроса, а подстановочный знак является завершающим. Это может быть другая история, если вы привязываете значение, а база данных поддерживает привязку отдельно от подготовки запроса.
Дэйв В. Смит
Это то, что мне подсказывает моя интуиция, но у меня есть только практический опыт работы с sql server в этом отношении, поэтому я сосредоточился именно на нем.
Blindy
7

Если valueнеиндексировано, оба результата приводят к сканированию таблицы. Разница в производительности в этом сценарии будет незначительной.

Если valueон проиндексирован, как указывает Даниэль в своем комментарии, =результатом будет поиск индекса, который имеет производительность O (log N). КАК будет (скорее всего - в зависимости от того, как оно селективного) в результате частичного сканирования индекса >= 'abc'и < 'abd'который потребует больше усилий , чем =.

Обратите внимание, что я говорю здесь о SQL Server - не всем СУБД понравится LIKE.

Будет А
источник
Я не думаю, что вы знаете, как работает двоичный поиск. И =case, и like '...%'case ведут себя одинаково, если sql распознает шаблон (и это так), потому что в обоих случаях поддеревья выбираются на основе отношений сравнения.
Blindy
О, я знаю. LIKE, скорее всего, будет вести себя хуже, хотя он все равно будет O (log N), если избирательность достаточно высока - O (log N), чтобы узнать, с чего начать частичное сканирование, затем несколько прямых чтений через индекс до тех пор, пока конечная точка 'abd'достигнута.
Will A
Да, но пример OP предполагает, что в этом диапазоне есть только одно значение, поэтому с учетом этого сравнения будут идентичными.
Blindy
Верный момент - не совсем понятно, что именно об этом говорил ОП, но я думаю, что это скорее так, чем нет. В этом случае производительность будет практически одинаковой.
Will A
Поиск диапазона LIKE, вероятно, будет довольно легко конкурировать с оператором =, и во многих случаях (например, если все удовлетворяющие строки находятся на одной странице, что немаловажно), может быть точно такая же производительность, что влечет за собой такое же количество чтений . Я считаю, что фраза «потребует больше усилий» - ошибочное заявление.
ErikE
6

Вы задаете неправильный вопрос. В базах данных имеет значение не производительность оператора, это всегда SARGability выражения и покрываемость всего запроса. Производительность самого оператора во многом не имеет значения.

Итак, как сделать LIKEи =сравнить с точки зрения SARGability? LIKE, при использовании с выражением, которое не начинается с константы (например, при использовании LIKE '%something'), по определению не относится к SARGabale. Но делает ли это возможным =или LIKE 'something%'надежным? Нет. Как и на любой вопрос о производительности SQL, ответ заключается не в запросе текста, а в развернутой схеме. Эти выражения могут быть SARGable, если для них существует индекс.

Так что, по правде говоря, есть небольшие различия между =и LIKE. Но спросить, является ли тот или иной оператор «быстрее» в SQL, все равно что спросить: «Что идет быстрее, красная машина или синяя машина?». Вам следует задавать вопросы о размере двигателя и весе автомобиля, а не о цвете ... Чтобы подойти к вопросам об оптимизации реляционных таблиц, вам следует искать свои индексы и выражения в предложении WHERE (и других предложениях, но обычно это начинается с ГДЕ).

Ремус Русану
источник
5

Личный пример с использованием mysql 5.5: у меня было внутреннее соединение между двумя таблицами, одной из 3 миллионов строк и одной из 10 тысяч строк.

При использовании лайка для индекса, как показано ниже (без подстановочных знаков), это заняло около 30 секунд:

where login like '12345678'

используя "объяснить", я получаю:

введите описание изображения здесь

При использовании '=' в том же запросе потребовалось около 0,1 секунды:

where login ='600009'

Используя «объяснить», я получаю:

введите описание изображения здесь

Как видите, likeпоиск по индексу полностью отменен, поэтому запрос занял в 300 раз больше времени.

Арис
источник
Вы также можете просто посмотреть на план казни, чтобы подтвердить это
LittleBobbyTables - Au Revoir
спасибо @LittleBobbyTables. Посмотрим на это.
Арис,
Я не знаю, связано ли это с моей последней версией (5.7), но LIKE не нарушает мой уникальный индекс здесь.
Себас
0

Возможно, вы ищете полнотекстовый поиск .

В отличие от полнотекстового поиска, предикат LIKE Transact-SQL работает только с шаблонами символов. Кроме того, вы не можете использовать предикат LIKE для запроса отформатированных двоичных данных. Более того, запрос LIKE к большому количеству неструктурированных текстовых данных выполняется намного медленнее, чем эквивалентный полнотекстовый запрос к тем же данным . Запрос LIKE для миллионов строк текстовых данных может занять несколько минут; тогда как полнотекстовый запрос может занять несколько секунд или меньше для тех же данных, в зависимости от количества возвращаемых строк.


источник
-1

Перво-наперво,

они не всегда равны

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

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

когда вещи не всегда равны, говорить об их производительности не так актуально.

Если вы работаете со строками и только с символьными переменными, можно говорить о производительности. Но не используйте подобные и "=" как взаимозаменяемые.

Как вы могли видеть во многих сообщениях (выше и в других вопросах), в случаях, когда они равны, производительность лайка ниже из-за сопоставления с образцом (сопоставления)

user5190021
источник
Если 'Hello 'это VARCHAR(по умолчанию), вы правы, но если это CHAR- нет. Преобразуйте его в a, CHAR(7)и оба вернут true. Кроме того, что, черт возьми, вы делаете, когда не используете TRIMсвои варчары? (примечание: это, по крайней мере, так в SQL Server 2008r2)
abluejelly 07