【问题标题】:how to rewrite this Oracle query to use efficient CTE如何重写此 Oracle 查询以使用高效的 CTE
【发布时间】:2020-12-25 23:06:59
【问题描述】:

我有一个查询,我想通过使用with 子句来改进它。我尝试了下面的变体,但我只能降低一点成本。如何使用 with 子句重写连接的主要部分只完成一次的查询,记住对于第二个子集,我有这个额外的过滤器 AND WS.CODE ='S_WLM_WL_013'

任何想法都将不胜感激。

查询是

SELECT
CASE
WHEN ( SELECT COUNT(*)
      FROM EXIM1B.GER_CUSTOMER_ONBOARDING GCO
        INNER JOIN EXIM1B.ALERT_HEADER AH
      ON AH.AT_ID=GCO.WLM_TRANSACTION_ID
  INNER JOIN EXIM1B.WORKFLOW_STATUSES WS
  ON AH.WW_STATUS_CODE    =WS.CODE
  WHERE GCO.MESSAGE_ID    =:1
  AND GCO.IDENTIFICATOR_ID=
    (SELECT MIN(IDENTIFICATOR_ID)
    FROM EXIM1B.GER_CUSTOMER_ONBOARDING
    WHERE MESSAGE_ID=:1
    )
    )  =
  (SELECT COUNT(*)
  FROM EXIM1B.GER_CUSTOMER_ONBOARDING GCO
  INNER JOIN EXIM1B.ALERT_HEADER AH
  ON AH.AT_ID=GCO.WLM_TRANSACTION_ID
  INNER JOIN EXIM1B.WORKFLOW_STATUSES WS
  ON AH.WW_STATUS_CODE    =WS.CODE
  AND WS.CODE             ='S_WLM_WL_013'
  WHERE GCO.MESSAGE_ID    =:1
  AND GCO.IDENTIFICATOR_ID=
    (SELECT MIN(IDENTIFICATOR_ID)
    FROM EXIM1B.GER_CUSTOMER_ONBOARDING
    WHERE MESSAGE_ID=:1
    )
  )
THEN 'OK'
ELSE null  END AS StatusAlert FROM DUAL

我试过这个

with t as ( SELECT MIN(IDENTIFICATOR_ID) as minval , message_id FROM EXIM1B.GER_CUSTOMER_ONBOARDING WHERE MESSAGE_ID=:1 group by message_id ) 
SELECT
  CASE
    WHEN ( SELECT COUNT(*) 
           FROM EXIM1B.GER_CUSTOMER_ONBOARDING GCO inner join t on t.message_id = gco.message_id 
            INNER JOIN EXIM1B.ALERT_HEADER AH
           ON AH.AT_ID=GCO.WLM_TRANSACTION_ID
            INNER JOIN EXIM1B.WORKFLOW_STATUSES WS
           ON AH.WW_STATUS_CODE    =WS.CODE
          WHERE GCO.MESSAGE_ID    =:1
          AND GCO.IDENTIFICATOR_ID = t.minval 
         )  =
       (SELECT COUNT(*)
        FROM EXIM1B.GER_CUSTOMER_ONBOARDING GCO inner join t on t.message_id = gco.message_id 
       INNER JOIN EXIM1B.ALERT_HEADER AH
       ON AH.AT_ID=GCO.WLM_TRANSACTION_ID
       INNER JOIN EXIM1B.WORKFLOW_STATUSES WS
       ON AH.WW_STATUS_CODE    =WS.CODE
       AND WS.CODE             ='S_WLM_WL_013'
       WHERE GCO.MESSAGE_ID    =:1
       AND GCO.IDENTIFICATOR_ID = t.minval
      )
    THEN 'OK'
    ELSE null  END AS StatusAlert FROM DUAL

【问题讨论】:

  • 您是否已经尝试过 /*+ materialize */ 提示?
  • @Sujitmohanty30,我试过了,但没用
  • 请分享您的索引。您是否有权创建索引?

标签: sql oracle oracle12c with-statement


【解决方案1】:

您可以减少在同一查询中计算过滤和未过滤状态的成本:

SELECT CASE WHEN COUNT(*) = SUM(CASE WHEN WS.CODE='S_WLM_WL_013' THEN 1 ELSE 0 END) THEN 'OK' ELSE NULL END AS StatusAlert 
        FROM EXIM1B.GER_CUSTOMER_ONBOARDING GCO
        INNER JOIN EXIM1B.ALERT_HEADER AH ON AH.AT_ID = GCO.WLM_TRANSACTION_ID
        INNER JOIN EXIM1B.WORKFLOW_STATUSES WS ON AH.WW_STATUS_CODE = WS.CODE
        WHERE GCO.MESSAGE_ID = :1
            AND GCO.IDENTIFICATOR_ID = (
                SELECT MIN(IDENTIFICATOR_ID)
                FROM EXIM1B.GER_CUSTOMER_ONBOARDING
                WHERE MESSAGE_ID = :1
            )
FROM DUAL

编辑

您可以进一步降低查询成本,不包括WORKFLOW_STATUSES 表上的JOIN 和直接过滤EXIM1B.ALERT_HEADER.WW_STATUS_CODE

SELECT CASE WHEN COUNT(*) = SUM(CASE WHEN /* WS.CODE */WW_STATUS_CODE='S_WLM_WL_013' THEN 1 ELSE 0 END) THEN 'OK' ELSE NULL END AS StatusAlert 
        FROM EXIM1B.GER_CUSTOMER_ONBOARDING GCO
        INNER JOIN EXIM1B.ALERT_HEADER AH ON AH.AT_ID = GCO.WLM_TRANSACTION_ID
        /* EXIM1B.WORKFLOW_STATUSES NOT NEEDED */
        /* INNER JOIN EXIM1B.WORKFLOW_STATUSES WS ON AH.WW_STATUS_CODE = WS.CODE */
        WHERE GCO.MESSAGE_ID = :1
            AND GCO.IDENTIFICATOR_ID = (
                SELECT MIN(IDENTIFICATOR_ID)
                FROM EXIM1B.GER_CUSTOMER_ONBOARDING
                WHERE MESSAGE_ID = :1
            )
FROM DUAL

【讨论】:

  • 感谢您的建议,但该查询不起作用。 ORA-00933: 第 12 行的 SQL 命令未正确结束。我想我必须删除答案中的最后一行,然后才能正常工作。
  • 你是对的,我已经更正了查询,现在试试。没有测试数据很难
  • 谢谢。我虽然这是问题所在,所以实际上我测试了它并且它有效。另一个答案也有效,但我认为你的答案更优雅,估算成本也更低。
  • @Roberto Hernandez,加入始终是首选解决方案,但我只要求我提供知识。当您说估算成本时,我可以知道估算的具体变化在哪一部分吗?
【解决方案2】:

你可以试试下面,

WITH customer_onboarding 
AS
(SELECT /*+ materialize */ 
       gco.wlm_transaction_id
  FROM exim1b.ger_customer_onboarding gco
WHERE  gco.message_id = :1
  AND    gco.identificator_id = (SELECT MIN(identificator_id) 
                                   FROM exim1b.ger_customer_onboarding 
                                  WHERE message_id = :1)),
wrk_flw_not_filtered
AS
(SELECT /*+ materialize */
       ws.code,count(*) over () as cnt_not_filtered
  FROM customer_onboarding gco
  JOIN exim1b.alert_header ah
    ON ah.at_id = gco.wlm_transaction_id
  JOIN exim1b.workflow_statuses ws
    ON ah.ww_status_code = ws.code)
SELECT CASE 
         WHEN max(cnt_not_filtered) = count(*) 
         THEN 'OK'
        END statusalert
   FROM wrk_flw_not_filtered flt
  WHERE flt.code = 'S_WLM_WL_013';

【讨论】:

  • 谢谢你的尝试,但它不起作用:ORA-00937:不是单组群功能
  • @Roberto Hernandez,我更新了我的答案并使用了max,因为它没有任何过滤器并删除else 的一部分case statement,因为它无论如何都会给出 null 以防计数不' t 匹配,你能检查一下作为最后一次尝试吗?
  • 现在它起作用了,它实现了结果。不过,我需要检查输出是否与原始查询中的相同。我赞成你的回答。谢谢
  • 很高兴听到这个消息。祝你事后找到好结果。 :)
猜你喜欢
  • 2013-02-20
  • 2021-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-25
  • 1970-01-01
  • 1970-01-01
  • 2019-08-27
相关资源
最近更新 更多