【问题标题】:How to exclude all rows in left table if a condition meets in right table without using a subquery oracle如果条件在右表中满足而不使用子查询oracle,如何排除左表中的所有行
【发布时间】:2021-04-04 03:55:41
【问题描述】:

我有两个表,BATCH_MESSAGES,BATCH_INSTRUCTIONS,如果其中任何指令的状态为暂停/拒绝,我想排除所有批次。

这里左边是 BATCH_MESSAGES,右边是 BATCH_INSTRUCTIONS。

这是 BATCH_MESSAGES 的 DDL

这是针对:BATCH_INSTRUCTIONS

仅应输出“CDC-SCBP-862411139626”,因为它是唯一一个所有指令状态为 NOT IN 暂停/拒绝。

我的查询产生相同的结果但它使用子查询复杂且效率低下。有没有更好的方法?

SELECT DISTINCT BM.ID,
         BM.BATCH_MSG_TYPE,
         BM.PARENT_BATCH_ID
FROM iris_activity.BATCH_MESSAGES BM 
WHERE BM.INSTRUCTIONS_COUNT = BM.PROCESSED_INSTRUCTIONS
AND BM.STAGE NOT IN ('COMPLETED')
AND BM.BATCH_MSG_TYPE NOT LIKE 'pacs.004%'
AND BM.RECEIVING_MODE = 'Stream' 
AND BM.ID NOT IN (SELECT DISTINCT BATCH_ID 
                  FROM iris_activity.BATCH_MESSAGES bm 
                  INNER JOIN Iris_activity.BATCH_INSTRUCTIONS bi  
                         on (bm.id = bi.BATCH_ID 
                            and bm.BATCH_MSG_TYPE = bi.BATCH_TYPE)
                   WHERE STATUS IN ('Suspended','Rejected') 
                   GROUP BY BATCH_ID, STATUS)
AND BM.RETRIES              <=5 ;

这样可以更高效、更简单吗?

【问题讨论】:

  • 请包含 DDL(您只共享了几行中的几个列值)。这些表中有多少行?您的查询实际需要多长时间?请分享执行计划,以便我们了解您正在处理的内容。 idbatch_messages 的主键吗?有人会期望如此,但您已将distinct 添加到查询中,这表明会有相同id 的行。使用子查询不会使查询效率低下。在子查询中使用group by batch_id, statusdistinct batch_id 是多余的,not in 无论如何都会隐式删除重复数据。

标签: sql oracle performance subquery query-optimization


【解决方案1】:

您可以在查询中使用分析函数COUNT,只需进行少量重写,如下所示:

Select DISTINCT BM.ID,
       BM.BATCH_MSG_TYPE,
       BM.PARENT_BATCH_ID
  From
    (SELECT BM.*,
            COUNT(CASE WHEN BI.STATUS IN ('Suspended','Rejected') THEN 1 END) 
                  Over(partition by BM.ID) AS CNT -- analytical function
       FROM iris_activity.BATCH_MESSAGES BM 
       LEFT JOIN Iris_activity.BATCH_INSTRUCTIONS bi
         ON bm.id = bi.BATCH_ID and bm.BATCH_MSG_TYPE = bi.BATCH_TYPE
      WHERE BM.INSTRUCTIONS_COUNT = BM.PROCESSED_INSTRUCTIONS
        AND BM.STAGE NOT IN ('COMPLETED')
        AND BM.BATCH_MSG_TYPE NOT LIKE 'pacs.004%'
        AND BM.RECEIVING_MODE = 'Stream' 
        AND BM.RETRIES <= 5            
    ) BM
 WHERE BM.CNT = 0 -- this condition

【讨论】:

    【解决方案2】:

    您可以将 NOT IN 子查询转换为左连接的内联视图,并添加一个谓词以确保内联视图中的值为 NULL。

    SELECT DISTINCT BM.ID,
             BM.BATCH_MSG_TYPE,
             BM.PARENT_BATCH_ID
    FROM iris_activity.BATCH_MESSAGES BM 
    LEFT JOIN
    (
        SELECT DISTINCT BATCH_ID 
        FROM iris_activity.BATCH_MESSAGES bm 
        INNER JOIN Iris_activity.BATCH_INSTRUCTIONS bi  
                on (bm.id = bi.BATCH_ID 
               and bm.BATCH_MSG_TYPE = bi.BATCH_TYPE)
        WHERE STATUS IN ('Suspended','Rejected') 
        GROUP BY BATCH_ID, STATUS
    ) suspended_or_rejected
      on  BM.BM_ID = suspended_or_rejected_batches.BATCH_ID
    WHERE BM.INSTRUCTIONS_COUNT = BM.PROCESSED_INSTRUCTIONS
    AND BM.STAGE NOT IN ('COMPLETED')
    AND BM.BATCH_MSG_TYPE NOT LIKE 'pacs.004%'
    AND BM.RECEIVING_MODE = 'Stream' 
    AND BM.RETRIES              <=5
    --New predicate:
    AND suspended_or_rejected.BATCH_ID IS NULL;
    

    虽然此更改实际上比您的原始示例复杂,但当您连接两个表中的大部分行时,这种方法通常会明显更快。 LEFT JOIN 更有可能使用哈希连接,这可能比嵌套循环和索引查找快得多。

    但您还应该检查原始查询的执行计划。子查询不一定会使查询效率低下,因为 Oracle 通常能够重写查询并将子查询合并到主查询中。

    【讨论】:

    • 真的需要GROUP BY BATCH_ID, STATUS吗?
    • @Popeye 没错,其中一些元素可以删除。我试图专注于左连接/为空技巧,而不是进行任何其他更改。您的方法可能是运行此查询的更快方法,但我不确定您是否可以将某些相同的条件应用于包含集和排除集。
    【解决方案3】:

    如果Not in,您可以使用not exists 代替

    SELECT DISTINCT BM.ID,
             BM.BATCH_MSG_TYPE,
             BM.PARENT_BATCH_ID
    FROM iris_activity.BATCH_MESSAGES BM 
    WHERE BM.INSTRUCTIONS_COUNT = BM.PROCESSED_INSTRUCTIONS
    AND BM.STAGE NOT IN ('COMPLETED')
    AND BM.BATCH_MSG_TYPE NOT LIKE 'pacs.004%'
    AND BM.RECEIVING_MODE = 'Stream' 
    AND NOT EXISTS (SELECT 1
                      FROM Iris_activity.BATCH_INSTRUCTIONS bi  
                             WHERE  bm.id = bi.BATCH_ID 
                                and bm.BATCH_MSG_TYPE = bi.BATCH_TYPE
                                and STATUS IN ('Suspended','Rejected') 
                                )
    AND BM.RETRIES              <=5 ;
    

    【讨论】:

    • 它仍然没有消除子查询。
    猜你喜欢
    • 2017-11-18
    • 2021-12-26
    • 1970-01-01
    • 1970-01-01
    • 2021-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-13
    相关资源
    最近更新 更多