LIKE '%...%' 通配符查询的 PL/SQL 性能调优

2021-12-21 00:00:00 indexing sql oracle query-optimization

我们使用的是 Oracle 11g 数据库.
您可能知道也可能不知道,如果您在字符串前面使用带有%"的通配符查询,则不会使用列索引,并且全表扫描发生.

We're using Oracle 11g database.
As you may or may not know, if you use wildcard query with "%" in front of the string, the column index is not being used and a full table scan is happening.

似乎没有关于如何改进此类查询的明确建议,但也许您可以分享一些关于如何优化以下查询的经验的宝贵信息:

It looks like there isn't a definitive suggestion on how to improve this kind of query, but perhaps you could share some valuable information from your experience on how to optimize the following query:

SELECT * 
  FROM myTable 
 WHERE UPPER(CustomerName) like '%ABC%' 
    OR UPPER(IndemnifierOneName) like '%ABC%' 
    OR UPPER(IndemnifierTwoName) like '%ABC%';

...其中所有 3 列都是 varchar2(100) 类型,ABC 是变量输入参数的值.

...where all 3 columns are of type varchar2(100) and ABC is a value of variable input parameter.

@All 建议 CONTEX 索引,请注意我的数据每天随时更新,此索引需要重新同步,因此它不是一个好的选择对于150 万行 的表,抱歉.​​

@All suggesting CONTEX index, please note my data gets updated any time of the day every day and this index requires re-syncing, hence it's not a good option for a table of 1.5 million rows, sorry.

附言我会为每个答案点赞,所以请让他们继续.

P.S. I'll upvote every answer, so please do keep them coming.

推荐答案

如前所述,您可以向名称列添加 ctx 上下文索引.

As already mentioned you could add a ctx context index to the name columns.

假设更新了少量记录,一个选项是每天刷新您的索引.(并记录发生的时间)

assuming a small number of records get updated, 1 option is to refresh your index daily. (and record when it happened)

然后添加一个 lastupdate 日期列 &正在搜索的表的索引.

then add a lastupdate date column & index to your table being searched.

应该可以扫描您的 ctx 索引以获取大部分未更改的旧数据并使用传统的 LIKE 从更新数据的一小部分中选择例如:

It should be possible to scan your ctx index for the majority of the old unchanged data and select from the small percentage of updated data using the traditonal LIKE e.g:

WHERE (lastupdated<lastrefresh AND contains(name,'%ABC%')) 
   OR (lastupdated>lastrefresh AND name like '%ABC%')

注意:在这种情况下,您可能会发现您的查询计划有点脑残(大量位图转换为行 ID),在这种情况下将 OR 的 2 部分拆分为 UNION ALL 查询.例如

NOTE: you may find your query plan goes a little mental (lots of bitmap conversions to row ids) in that case split the 2 parts of the OR into a UNION ALL query. e.g

SELECT id FROM mytable   
    WHERE 
    (lastupdate>lastrefresh and name LIKE '%ABC%')
    UNION ALL
    SELECT id FROM mytable   
    WHERE lastupdate<lastrefresh and CONTAINS(name, '%ABC%', 1) > 0

相关文章