11

我想知道与根本没有 where 子句相比,使用 LIKE 关键字和通配符作为值的查询性能如何。

考虑一个 where 子句,例如“WHERE a LIKE '%'”。这将匹配列“a”的所有可能值。这与根本没有 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 ?

显然,只有一个查询是最简单和最容易维护的。如果性能仍然很好,我宁愿只使用一个查询。

4

11 回答 11

13

SQL Server 一般会看到

WHERE City LIKE 'A%'

并将其视为

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

...并在适当的情况下愉快地使用索引搜索。我说“一般”,因为我已经看到它在某些情况下无法进行这种简化。

如果有人试图这样做:

WHERE City LIKE '%ville'

...那么索引搜索基本上是不可能的。

但是一些简单的事情:

WHERE City LIKE '%'

将被视为等同于:

WHERE City IS NOT NULL
于 2009-10-22T02:35:37.570 回答
4

您可以使用 DBMS 提供的任何查询分析(例如EXPLAIN,对于 MySQL、SET SHOWPLAN_ALL ON对于 MS SQL(或使用其他方法之一)、EXPLAIN PLAN FOR对于 Oracle)来查看查询将如何执行。

于 2009-10-22T02:42:14.863 回答
4

我希望对此有一个教科书的答案,但听起来它会因不同的数据库类型而有很大差异。大多数回复表明我应该进行测试,这正是我所做的。

我的应用程序主要针对 Derby、MS SQL 和 Oracle 数据库。由于 derby 可以嵌入运行并且易于设置,因此我首先测试了它的性能。结果令人惊讶。我在一张相当大的桌子上测试了最坏的情况。我运行了 1000 次测试并将结果取平均值。

查询一:

SELECT * FROM TableName

查询 2(具有 a="%" 和 b="%" 的值):

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

查询1平均时间:178ms

查询2平均时间:181ms

因此,两个查询之间的 derby 性能几乎相同。

于 2009-10-26T16:35:51.527 回答
2

Derby 还提供了用于检查所使用的实际查询计划的工具,因此您可以使用 Derby 运行实验并查看 Derby 选择的查询计划。您可以使用 -Dderby.language.logQueryPlan=true 运行 Derby,Derby 会将查询计划写入 derby.log,或者您可以使用 RUNTIMESTATISTICS 工具,如下所述:http: //db.apache.org/derby/ docs/10.5/tuning/ctundepth853133.html

我不确定 Derby 是否会提前去掉 A LIKE '%',但我也不认为该子句的存在会大大降低执行速度。

我很想看看你在你的环境中得到的实际查询计划输出,不管有没有 A LIKE '%' 子句。

于 2009-10-22T04:34:48.677 回答
2

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 行上的基数(行)

于 2009-10-22T06:47:33.900 回答
2

任何称职的 DBMS 都会LIKE '%'在尝试运行查询之前删除子句。我相当肯定我已经看到 DB2/z 在其执行计划中这样做了。

准备好的语句不应该有什么不同,因为它应该在到达执行引擎之前变成真正的SQL。

但是,与所有优化问题一样,衡量,不要猜测!DBA 的存在是因为他们不断根据实际数据(随时间变化)调整 DBMS。至少,您应该使用合适的静态数据为所有变体计时(并获取执行计划),看看是否存在差异。

我知道这样的查询:

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

经过优化以在执行之前删除整个 where 子句(无论如何在 DB2 上,并且在您询问之前,该构造在您需要删除 where 子句的影响但仍保留参数占位符的情况下很有用(使用 BIRT 和 Javascript 来修改查询)对于通配符))。

于 2009-10-22T02:24:59.073 回答
1

我认为讨论中缺少的一个方面是 OP 希望使用准备好的语句。在准备语句时,数据库/优化器将无法计算出其他人提到的简化,因此无法优化,a like '%'因为在准备时将不知道实际值。

所以:

  • 使用准备好的语句时,有四种不同的可用语句(0、只有 a、只有 b、两者)并在需要时使用适当的语句
  • 看看当你只坚持一个语句时不使用准备好的语句时你是否会获得更好的性能(尽管这样很容易不包括“空”条件)
于 2009-10-22T10:13:18.587 回答
1

根据 LIKE 谓词的结构以及您正在测试的字段,您可能需要进行全表扫描。从语义上讲,'%' 可能意味着全表扫描,但 Sql Server 在内部对查询进行各种优化。所以问题就变成了:Sql Server 是否优化了由 '%' 形成的 LIKE 谓词并将其从 WHERE 子句中抛出?

于 2009-10-22T02:28:11.127 回答
0

如果一列有一个非空的空白值怎么办?您的查询可能会匹配它。

如果这是对现实世界应用程序的查询,请尝试使用大多数现代 sql 数据库的自由文本索引功能。性能问题将变得微不足道。

一个简单的 if 语句 if (AB) search ab else (A) search a else B search b else 告诉用户他们没有指定任何内容

维护起来很简单,并且变得更容易理解,而不是对 LIKE 运算符做出假设。当您显示结果“您对 A 的搜索找到 x”或“您对 AB 的搜索找到...”时,您可能无论如何都会在 UI 中执行此操作。

于 2009-10-22T02:46:00.500 回答
0

我不确定将准备好的语句与您描述的参数类型一起使用的价值。原因是您可能会欺骗查询优化器来准备一个完全错误的执行计划,具体取决于哪个参数是“%”。

例如,如果语句是使用列 A 上的索引准备的执行计划,但列 A 的参数结果是 '%',您可能会遇到性能不佳的情况。

于 2009-10-22T02:49:55.220 回答
-2

以“like '%'”为唯一谓词的 where 子句的行为与没有 where 子句的行为完全相同。

于 2009-10-22T03:14:14.767 回答