重写查询替换不在

Posted

技术标签:

【中文标题】重写查询替换不在【英文标题】:rewrite query replacing not in 【发布时间】:2018-11-20 07:42:44 【问题描述】:

我想以删除NOT IN 的方式重写以下查询,这可能吗?

select * from TRX_T TT, TRX_SUB TS
where TT.CODE=TS.CODE
and TT.SUBID= TS.ID
and TS.VALUE=1
and TS.CODE=1
AND TS.ID=17
AND TT.STATUS NOT IN('T','R','C')

如果状态是IN,我会使用union all

我想重写的原因是因为下面的 oracle 推荐。

谓词 "TT"."STATUS"'C' 在执行的第 5 行使用 plan 包含索引列“STATUS”上的表达式。这 表达式阻止优化器有效地使用索引 表 TT。

不同值的数量

    T       264
    C   5489709
    D      2987
    J       924
    L    529430
    R     39382
    S      5449

TRX_T上的索引是这样的:(CODE,SUBID,TYPE,STATUS,NO_SL)

【问题讨论】:

您是否发现查询的性能问题...有理由相信它应该能够运行得比 Oracle 推荐的更快吗?只有当它提供的替代方案(TRX_T 表上的索引访问)可以提高性能时,该建议才有意义——它很可能不会。 解释计划是怎么说的?它使用索引吗?毕竟查询很简单。由于TS.ID = 17,您选择了一个或零个trx_sub 行。这将为您提供 ID 为 17 的一行,但如果 codevalue 不等于 1,则将其关闭。对于这条trx_sub 记录,您将获得相关的trx_t 行,并且您在相关列codesubid 上有一个索引。看起来不错。 trx_t 表中有多少行,您的查询通常检索多少行? @ThorstenKettner 都在 600 万左右,解释计划显示所有列的读取索引 所以trx_t 中有六百万行。您的查询检索到其中的多少?你说索引正在被使用,所以它显然只是一小部分。那么问题出在哪里? Oracle 似乎认为AND TT.STATUS IN('D','J','L','S')(CODE,SUBID,STATUS) 上的索引会更可取,但是这样可以获得查询多快?我预计速度不会有太大变化。 附注:逗号分隔的连接在 1992 年的 SQL 标准中变得多余。Oracle 花了 9 年时间才采用它,但现在仍然是 17 年前。请使用正确的连接 (from trx_t tt inner join trx_sub ts on ...)。 【参考方案1】:

如果您想调整查询,您需要了解您的数据模型和数据。优化器说它不能有效地使用TRX_T 上的索引。让我们看看那个复合索引:

CODE :用于连接条件 SUBID :用于连接条件 TYPE:没用过 STATUS : 可以用作过滤器吗? NO_SL:没用过

您的查询使用了五个索引列中的三个。但是因为您在STATUS 上有一个 NOT IN 表达式,所以优化器不会使用索引来评估过滤器。因此,它读取TRX_T 中与TRX_SUB 中的记录匹配的每条记录,并评估表上的过滤器。

也许如果您将条件肯定地表达为TT.STATUS IN ('D','J','L', 'S'),那么优化器可能能够使用 SKIP SCAN 来评估索引上的过滤器。

但是,如果将 TRX_T.TYPE 用作过滤器(或者将索引列的顺序重新排列为在 TYPE 之前具有 STATUS 但不要这样做,则索引使用会更有效)它可能会破坏其他查询的稳定性)。

另一种选择是将表达式重写为 NOT IN 子查询(如果 (TRX_T.CODE, TRX_T.SUBID) 中有 no 空值,否则为 NOT EXISTS 子查询):

select * from TRX_T TT, TRX_SUB TS
where TT.CODE=TS.CODE
and TT.SUBID= TS.ID
and TS.VALUE=1
and TS.CODE=1
AND TS.ID=17
AND (TT.CODE, TT.SUBID) NOT IN
     (select x.CODE, x.SUBID
      from trx_t x
      where x.status in ('T','R','C')
     )

但是,在该列表中具有 STATUS 值的 TRX_T 记录的数量非常大 - 它们是您的表的大部分 - 因此评估该子查询可能比您目前拥有的更昂贵。

请注意,通常的警告适用。在 *** 上调优查询是个小游戏。缺少太多信息(数据量、偏差、其他索引、解释计划等),我们只能做猜测之外的任何事情。

【讨论】:

是的,我同意调优查询在 SO 上有点棘手。这就是我要求重新编写查询的原因,我可以创建一个覆盖索引来覆盖它可能有帮助的所有列。根据TT.STATUS IN ('D','J','L', 'S'),有时会插入新状态,因此不会有帮助。 这就是杯子游戏的确切原因。我们所能做的就是为您建议不同的方法来重写您的查询,大多数情况下不会有任何改进,甚至会使事情变得更糟,或者由于某些其他原因(例如新状态)而不起作用。跨度> 【参考方案2】:

使用显式加入而不加入可以用下面的方式替换

   select * from TRX_T TT
   join  TRX_SUB TS
    on  TT.CODE=TS.CODE and TT.SUBID= TS.ID
    where TS.VALUE=1
    and TS.CODE=1
    AND TS.ID=17 
    and not exists
    ( select 1 from TRX_T t1 where t1.CODE=TT.code
     and (t1.STATUS ='T' OR t1.STATUS='R' or t1.STATUS='C')
    )

【讨论】:

这不是等效查询。您需要使用布尔 AND 而不是 OR。【参考方案3】:

您可以尝试使用left join 并过滤掉TT2.status is null,这样它就会为您提供not in ('T','R','C') 的记录

select * from TRX_T TT
   join  TRX_SUB TS on  TT.CODE=TS.CODE and TT.SUBID= TS.ID
   left join (select * from TRX_T where STATUS in ('T','R','C')) TT2 on 
   TT.CODE=TT2.CODE and TT.SUBID= TT2.SUBID
where TS.VALUE=1 and TS.CODE=1 AND TS.ID=17 and TT2.status is null

【讨论】:

【参考方案4】:

你可以使用minus设置运算符作为

select *
  from TRX_T TT
  join TRX_SUB TS
    on ( TT.CODE = TS.CODE
     and TT.SUBID = TS.ID )
 where TS.VALUE = 1
   and TS.CODE = 1
   and TS.ID = 17
minus
select *
  from TRX_T TT
  join TRX_SUB TS
    on ( TT.CODE = TS.CODE
     and TT.SUBID = TS.ID )
 where TS.VALUE = 1
   and TS.CODE = 1
   and TS.ID = 17
   and TT.STATUS in ('T', 'R', 'C');

附:是的,Not in 从性能的角度来看主要是有问题的,作为不使用nvl 功能的一方,在使用Not in 时不应该忘记。

【讨论】:

这是否更有效取决于评估 TRXT_T 和 TRX_SUB 上的连接的成本。【参考方案5】:

试试下面,你可以使用'except':

SELECT * 
FROM   trx_t TT, 
       trx_sub TS 
WHERE  TT.code = TS.code 
       AND TT.subid = TS.id 
       AND TS.value = 1 
       AND TS.code = 1 
       AND TS.id = 17 
EXCEPT 
SELECT * 
FROM   trx_t TT, 
       trx_sub TS 
WHERE  TT.status = 'T' 
        OR TT.status = 'R' 
        OR TT.status = 'C' 

【讨论】:

很高兴看到有人对此表示赞同。显然他们也不知道 EXCEPT 在 Oracle 中是无效的。它应该是 MINUS。 唉,没有证据表明这会导致更有效的执行计划。 ('T','R' and 'C') 的状态值占 TRX_T 中所有行的 90% 以上,因此组装较低子查询的结果集将是昂贵的。那是在我们考虑产品的灾难性影响之前,我们将从 TRX_T 和 TRX_SUB 的交叉连接中得到。 哦,对不起,我认为它的 sql 正如@APC 所说,这些都只是猜测。我建议获取 SQL Monitor 报告并提供事实。 你是比尔盖茨

以上是关于重写查询替换不在的主要内容,如果未能解决你的问题,请参考以下文章

Oracle用左连接替换不在

替换 Apache 重写规则中缺少的参数

如何替换 HIVE Join 中的 OR 条件

如何替换不在引号中的字符串

替换不在括号中的数字?

VBA - 替换CSV中的逗号不在引号内