SQL LIKE Производительность только с подстановочным знаком (%) в качестве значения - PullRequest
11 голосов
/ 22 октября 2009

Мне интересно, какова будет производительность запроса при использовании ключевого слова LIKE и подстановочного знака в качестве значения по сравнению с отсутствием предложения where.

Рассмотрим условие where, такое как "WHERE a LIKE '%'". Это будет соответствовать всем возможным значениям столбца «а». Как это можно сравнить с отсутствием предложения where.

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

SELECT * FROM TableName WHERE a LIKE ? AND b LIKE ?

Можно указать значения «%» и «%», чтобы соответствовать всем возможным значениям для a и или b. Это удобно, поскольку для этого я могу использовать один именованный запрос в своем приложении. Интересно, каковы соображения производительности для этого. Уменьшает ли оптимизатор запросов значение LIKE '%', чтобы оно просто соответствовало всем? Я понимаю, что, поскольку я использую именованный запрос (подготовленное утверждение), это также может повлиять на ответ. Я понимаю, что ответ, скорее всего, зависит от конкретной базы данных. В частности, как это будет работать в Oracle, MS SQL Server и Derby.

Альтернативный подход к этому состоит в использовании 3 отдельных запросов, основанных на вводе пользователем символа подстановки.

A - шаблонный запрос:

SELECT * FROM TableName WHERE b LIKE ?

B - шаблонный запрос:

SELECT * FROM TableName WHERE a LIKE ?

A и B являются подстановочными знаками:

SELECT * FROM TableName

Нет подстановочных знаков:

SELECT * FROM TableName WHERE a LIKE ? AND b LIKE ?

Очевидно, что один запрос является самым простым и простым в обслуживании. Я бы предпочел использовать только один запрос, если производительность все еще будет хорошей.

Ответы [ 11 ]

12 голосов
/ 22 октября 2009

SQL Server будет обычно видеть

WHERE City LIKE 'A%'

и относитесь к нему как

WHERE City >= 'A' AND City < 'B'

... и с радостью использовать индексный поиск, если это уместно. Я говорю «в общем», потому что я видел, что в некоторых случаях это не удается сделать это упрощение.

Если кто-то пытается это сделать:

WHERE City LIKE '%ville'

... тогда поиск по индексу будет практически невозможен.

Но так просто:

WHERE City LIKE '%'

будет считаться эквивалентным:

WHERE City IS NOT NULL
4 голосов
/ 26 октября 2009

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

Мое приложение в основном предназначено для баз данных Derby, MS SQL и Oracle. Поскольку derby может быть встроенным и его легко настроить, я сначала проверил его производительность. Результаты были удивительными. Я проверил худший вариант на довольно большом столе. Я провел тест 1000 раз и усреднил результаты.

Запрос 1:

SELECT * FROM TableName

Запрос 2 (со значениями a = "%" и b = "%"):

SELECT * FROM TableName WHERE a LIKE ? AND b LIKE ?

Среднее время запроса 1: 178 мс

Среднее время запроса 2: 181 мс

Таким образом, производительность в дерби почти одинакова между двумя запросами.

4 голосов
/ 22 октября 2009

Вы можете использовать любой анализ запросов, который предлагает СУБД (например, EXPLAIN для MySQL, SET SHOWPLAN_ALL ON для MS SQL (или использовать один из других методов ), EXPLAIN PLAN FOR для Oracle), чтобы увидеть, как будет выполняться запрос.

2 голосов
/ 22 октября 2009

Oracle 10gR2, по-видимому, не выполняет специальной оптимизации для этой ситуации, но признает, что LIKE '%' исключает нули.

create table like_test (col1)
as select cast(dbms_random.string('U',10) as varchar2(10))
from dual
connect by level <= 1000
/
insert into like_test values (null)
/
commit
/

exec dbms_stats.gather_table_stats(user,'like_test')

explain plan for
select count(*)
from   like_test
/
select plan_table_output from table(dbms_xplan.display)
/
explain plan for
select count(*)
from   like_test
where  col1 like '%'
/
select plan_table_output from table(dbms_xplan.display)
/
explain plan for
select count(*)
from   like_test
where  col1 is not null
/
select plan_table_output from table(dbms_xplan.display)
/

... давая ...

Plan hash value: 3733279756

------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Cost (%CPU)| Time     |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |           |     1 |            |          |
|   2 |   TABLE ACCESS FULL| LIKE_TEST |  1001 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------

... и ...

Plan hash value: 3733279756

--------------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |    10 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |           |     1 |    10 |            |          |
|*  2 |   TABLE ACCESS FULL| LIKE_TEST |  1000 | 10000 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("COL1" LIKE '%')

... и ...

Plan hash value: 3733279756

--------------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |    10 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |           |     1 |    10 |            |          |
|*  2 |   TABLE ACCESS FULL| LIKE_TEST |  1000 | 10000 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("COL1" IS NOT NULL)

Обратите внимание на количество элементов (строк) в строке TABLE ACCESS FULL

2 голосов
/ 22 октября 2009

Derby также предлагает инструменты для проверки фактического плана запросов, который использовался, поэтому вы можете проводить эксперименты с использованием Derby и просматривать план запросов, выбранный Derby. Вы можете запустить Derby с -Dderby.language.logQueryPlan = true, и Derby запишет план запроса в derby.log, или вы можете использовать средство RUNTIMESTATISTICS, как описано здесь: http://db.apache.org/derby/docs/10.5/tuning/ctundepth853133.html

Я не уверен, что Дерби заберет A LIKE '%' раньше времени, но я также не думаю, что присутствие этого предложения приведет к значительному замедлению скорости выполнения.

Мне было бы весьма интересно увидеть фактические выходные данные плана запроса, которые вы получаете в своей среде, с предложением A LIKE '%' и без него.

2 голосов
/ 22 октября 2009

Любая достойная СУБД будет исключать предложения LIKE '%', даже не пытаясь выполнить запрос. Я почти уверен, что видел, как DB2 / z делает это в своих планах выполнения.

Подготовленный оператор не должен иметь значения, поскольку он должен быть превращен в real SQL, прежде чем он попадет в механизм исполнения.

Но, как и во всех вопросах оптимизации, измерьте, не догадайтесь ! Администраторы баз данных существуют, потому что они постоянно настраивают СУБД на основе фактических данных (которые меняются со временем). Как минимум, вам нужно время (и получить планы выполнения) для всех вариантов с подходящими статическими данными, чтобы увидеть, есть ли разница.

Я знаю, что запросы вроде:

select c from t where ((1 = 1) or (c = ?))

оптимизированы для для удаления всего предложения where перед выполнением (в любом случае в DB2, и, прежде чем вы спросите, конструкция полезна, когда вам нужно удалить эффект предложения where, но при этом сохранить заполнитель параметра (используя BIRT с Javascript для изменения запросов для подстановочных знаков)).

1 голос
/ 22 октября 2009

Один аспект, который, по моему мнению, отсутствует в обсуждении, заключается в том, что ФП хочет использовать подготовленное заявление. Во время подготовки заявления база данных / оптимизатор не сможет отработать упрощения, о которых упоминали другие, и поэтому не сможет оптимизировать значение a like '%', поскольку фактическое значение не будет известно во время подготовки.

Таким образом:

  • при использовании подготовленных операторов, имейте в наличии четыре различных оператора (0, только a, только b, оба) и используйте соответствующий при необходимости
  • посмотрите, получите ли вы лучшую производительность, если вы не используете подготовленный оператор, когда придерживаетесь только одного оператора (хотя тогда было бы довольно просто не включать «пустые» условия)
1 голос
/ 22 октября 2009

В зависимости от структуры предиката LIKE и от области, в которой вы тестируете, может потребоваться полное сканирование таблицы. Семантически «%» может подразумевать полное сканирование таблицы, но Sql Server выполняет всевозможную оптимизацию внутри себя по запросам. Таким образом, возникает вопрос: оптимизирует ли Sql Server использование предиката LIKE, сформированного с помощью «%», и выбрасывает его из предложения WHERE?

0 голосов
/ 22 октября 2009

предложение where с "like '%'" в качестве единственного предиката будет вести себя точно так же, как предложение no where.

0 голосов
/ 22 октября 2009

Я не уверен в ценности использования подготовленного оператора с параметрами, которые вы описываете. Причина в том, что вы могли бы заставить оптимизатор запросов подготовить план выполнения, который был бы совершенно неверным в зависимости от того, какой из параметров был «%».

Например, если оператор был подготовлен с планом выполнения с использованием индекса для столбца A, но параметр для столбца A оказался «%», вы можете столкнуться с низкой производительностью.

...