【问题标题】:PL/SQL Performance Tuning for LIKE '%...%' Wildcard QueriesLIKE '%...%' 通配符查询的 PL/SQL 性能调优
【发布时间】:2011-09-07 22:29:37
【问题描述】:

我们使用的是 Oracle 11g 数据库。
您可能知道也可能不知道,如果您在字符串前面使用带有“%”的通配符查询,则列 index is not being used 并且 full table scan 是正在发生。

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

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

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

@All 建议 CONTEX 索引,请注意我的数据每天都会在一天中的任何时间更新,并且该索引需要重新同步,因此它不是一个好的选择对于 150 万行的表,抱歉。​​

附:我会为每一个答案投票,所以请继续关注。

【问题讨论】:

标签: sql oracle indexing query-optimization


【解决方案1】:

使用 Oracle 文本,但使用稍新的 CTXCAT 变体 - 此域索引作为插入/更新相关行的事务的一部分进行更新,因此始终是最新的 - 有关详细信息,请参阅 Oracle 自己的 Oracle 文本文档。

【讨论】:

    【解决方案2】:

    UPPER() 正在杀死您的索引,请考虑使用正则表达式。初始% 可能会避免正常的索引扫描,但并不总是会导致全表扫描,而是会导致比 FTS 更快的全索引扫描。

    我想'ABC' 是可变的。如果没有,函数索引是可行的方法。

    【讨论】:

    • 是的,它是一个变量,我将编辑 q 以注意这一点,谢谢。 UPPER(CUSTOMERNAME) 上有一个索引,但没有按照解释计划使用。
    • 对于快速全扫描,查询中的每一列都应该被索引覆盖。您可以尝试“SELECT UPPER(CustomerName) FROM myTable WHERE UPPER(CustomerName) like '%ABC%'” 看看结果是否进入 FFS?
    • 对不起,我猜它确实使用了索引:SELECT STATEMENT, GOAL = ALL_ROWS Cost=1930 Cardinality=78206 Bytes=1173090 INDEX FULL SCAN Object owner=POSOWN Object name=XIF20CR_PROPOSALSEARCH Cost=1930 Cardinality=78206字节=1173090
    • 真实数据的性能是否可以接受?如果是这样,您可以探索此选项,使用覆盖这三列的索引,或三个索引和三个查询。如果您需要除这三列之外的更多列,我认为您可以检索 rowid 并进行后续查找,例如:select * from mytable where rowid in (select rowid from mytable where upper(customer) like '%abc%')。 .. 可能一个提示也可以做到这一点。
    • 恐怕会进行全表扫描:(我试过这个:'select * from cr_proposalsearch where rowid in ( select rowid from cr_proposalsearch ps where UPPER(ps.CustomerName) like '% ABC%')
    【解决方案3】:

    如前所述,您可以将 ctx 上下文索引添加到名称列。

    假设少量记录得到更新,一种选择是每天刷新您的索引。 (并记录发生的时间)

    然后将 lastupdate 日期列和索引添加到正在搜索的表中。

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

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

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

    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
    

    【讨论】:

      【解决方案4】:

      有时这种查询是不可避免的——从 URL 中提取域,或者从带有前缀和后缀的单词中提取词根。

      您可以使用带或不带自定义标记器的全文索引。

      或者,如果您要搜索的字符串数量有限且事先已知(例如,您正在处理需要从 URL 中提取的一组有限的域名),您可以使用确定性函数,该函数可以被索引。

      http://www.akadia.com/services/ora_function_based_index_2.html

      【讨论】:

        【解决方案5】:

        唯一的优化是不使用该类型的查询,而是使用数据库平台的本机功能:

        查看 Oracle 文本:http://www.oracle.com/technetwork/database/enterprise-edition/index-098492.html

        SQL Server 相关问题的常见答案是全文搜索。很高兴看到 Oracle 有同样好的或更好的东西。

        【讨论】:

          猜你喜欢
          • 2012-02-22
          • 1970-01-01
          • 2016-06-12
          • 1970-01-01
          • 1970-01-01
          • 2017-06-28
          • 1970-01-01
          • 1970-01-01
          • 2016-09-19
          相关资源
          最近更新 更多