我通常在陷入绝望状态时会联系 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) );