SQL ЛЮБИТ производительность только с подстановочным знаком (%) в качестве значения

StackOverflow https://stackoverflow.com/questions/1604707

Вопрос

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

Рассмотрим предложение where, такое как "WHERE a НРАВИТСЯ '%'".Это будет соответствовать всем возможным значениям столбца "a".Как это соотносится с отсутствием предложения where вообще?

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

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

Значения '%' и '%' могут быть предоставлены для соответствия всем возможным значениям для a и или b.Это удобно, так как я могу использовать для этого один именованный запрос в своем приложении.Интересно, каковы для этого соображения по производительности.Уменьшает ли оптимизатор запросов значение '%', чтобы просто сопоставить все?Я понимаю, что, поскольку я использую именованный запрос (подготовленный оператор), это также может повлиять на ответ.Я понимаю, что ответ, скорее всего, зависит от конкретной базы данных.Итак, конкретно, как это будет работать в 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 ?

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

Это было полезно?

Решение 3

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

Мое приложение в основном предназначено для баз данных 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 мс

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

Другие советы

SQL Server, как правило, будет видеть

WHERE City LIKE 'A%'

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

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

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

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

WHERE City LIKE '%ville'

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

Но что-то простое, как:

WHERE City LIKE '%'

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

WHERE City IS NOT NULL

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

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

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

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

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

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

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

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 '%' и без него.

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

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

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

Следовательно ,:

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

Что если столбец имеет ненулевое пустое значение? Ваш запрос, вероятно, будет соответствовать этому.

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

Простое утверждение if если (A B)  искать б еще (A)  искать еще Б  поиск б еще  сообщить пользователю, что он ничего не указал

поддерживать тривиально и становится гораздо проще понять, чем делать предположения об операторе LIKE. Вероятно, вы все равно будете делать это в пользовательском интерфейсе, когда будете отображать результаты " Ваш поиск A found x " или " Ваш поиск A B найден ... "

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

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

предложение where с " как "%" " поскольку единственный предикат будет вести себя точно так же, как предложение no where вообще.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top