使用大量".. OR COLUMN LIKE 'STRING%' OR ..."优化查询



我通常在陷入绝望状态时会联系 StackOverflow......所以。。。任何关于优化的想法或见解将不胜感激......

问题:我有一些查询,在 where 子句中,我有这样的东西:

    WHERE VERSION = 462
        AND (CSB_CART_MAN LIKE '12010%'
        OR CSB_CART_MAN LIKE '12011%'
        OR CSB_CART_MAN LIKE '12013%'
    .
    .
    . )
and around a thousound conditions like the ones above.
  • CSB_CART_MAN是一个VARCHAR2。
  • 数据大小 - 160 行,51 列。

正如预期的那样,这是超慢的...

关于如何优化这样的情况的任何想法?(一千个"或像'XXX%'")

数据示例:

CSB_CART_MAN - 270090
CSB_CART_MAN - 2700910
CSB_CART_MAN - 13911
CSB_CART_MAN - 13912
CSB_CART_MAN - 139130

这些数字是多少?- 这些数字代表国际会计准则,也称为"国际会计估计"

也许它适用于使用regexp_like而不是标准?您上面的示例可以写成:

... WHERE regexp_like(CSB_CART_MAN, '^1201[0,1,3]') 

顺便说一句:我最喜欢的形成正则表达式模式并测试它的地方是 https://regex101.com/

。以及您新提供的数据样本:

 WITH d AS (
   SELECT 270090 CSB_CART_MAN FROM dual UNION ALL
   SELECT 2700910 CSB_CART_MAN FROM dual UNION ALL
   SELECT 13911 CSB_CART_MAN FROM dual UNION ALL
   SELECT 13912 CSB_CART_MAN FROM dual UNION ALL
   SELECT 139130 CSB_CART_MAN FROM dual 
   )
SELECT * 
  FROM d
 WHERE regexp_like(d.csb_cart_man, '^(2700|1391)d{1,3}$')

这意味着,值必须以"2700"或"1391"开头 (^),后跟 1 到 3 位数字,然后到达末尾 ($)

我会考虑将搜索字符串放入表(可能是临时表)中并JOIN

SELECT
    ...
FROM
    My_Table MT
INNER JOIN Search_Criteria SC ON MT.CSB_CART_MAN LIKE SC.string_pattern
WHERE
    version = 462

你想要性能明智的查询,那么你必须过滤前 4 位数字到数据并将这个表连接到主表,然后再次过滤你想要的任何内容

喜欢这个

SELECT
    MT.*
FROM
    My_Table MT
INNER JOIN(
         select * from my_table 
         where  version = 462
         cSB_CART_MAN LIKE '1201%')a
 ON a.id=mt.id
WHERE
(a.CSB_CART_MAN LIKE '12010%'
        OR a.CSB_CART_MAN LIKE '12011%'
        OR a.CSB_CART_MAN LIKE '12013%'
    .
    .
    . )

在大约一千OR条件下,DBMS 使用索引没有多大意义。必须逐条记录读取该表并与列表进行比较。所以我必须快速进行比较。

您正在使用模式匹配运算符LIKE。你给它一个模式,例如'12010%',必须解析为通配符('%'和'_')。可以寻找像"1_2%345%"这样复杂的东西,所以它必须有一个相当复杂的算法来做到这一点。因此,在没有 wildchars 的情况下进行显式比较可能会好得多:

substr(csb_cart_man, 1, 5)  = '12010'

我被告知,在列上使用诸如 substr 之类的函数会使优化器无法使用索引,而它可能在 substr(col, 1, n)' 上使用索引like 'xxx%'. That sounds kind of strange to me. If the optimizer is able to examine 'xxx%' on whether it starts with non-wildcard characters, why can't it see the 1 in?但无论如何,如前所述,无论如何在查询中使用索引是没有意义的,所以没问题。

我会这样写查询:

select * 
from mytable 
where version = 462
and substr(csb_cart_man, 1, 5) in ('12010', '12011', '12013', ...);

因此,对于多个长度:

select * 
from mytable 
where version = 462
and 
(
  substr(csb_cart_man, 1, 5) in ('12010', '12011', '12013', ...)
  or
  substr(csb_cart_man, 1, 6) in ('120444', '120555', '120777', ...)
);
使用

一个固定长度,您可以尝试使用函数索引,但如前所述,我认为它不会被使用:

create index idx_fivechars on mytable( version , substr(csb_cart_man, 1, 5) );

相关内容

最新更新