【问题标题】:Trying to refactor the recursive query in an Oracle CTE?试图重构 Oracle CTE 中的递归查询?
【发布时间】:2018-09-30 08:22:04
【问题描述】:

我一直在研究一个非常适合 MS-SQL 的 CTE。我正在将它翻译成 Oracle 并且它可以工作。但是它非常慢,当我查看解释计划时,我在运行查询时看到“合并加入笛卡尔”。我正在针对 Oracle 12.2 运行。如果我删除

“或”(...)

部分查询速度显着提高。然而,“合并加入笛卡尔”仍然出现在解释计划中。我已经确定了查询中的问题所在。

“和”(....)“或”(...)

在递归查询中是缓慢的根源。我需要这两个部分,因为有不同的路径,所以摆脱一个或另一个并不是一个真正的选择。我试图重写 CTE 的那部分,但我运气不佳。我的第一次尝试是在递归部分中包含第二个查询。然而,Oracle 只允许在 CTE 的递归部分进行一个查询。我也试图把它分解成更小的部分,但我的尝试失败了。对于重写此 CTE 中的递归部分的任何建议,我将不胜感激。

AND
    (    
        myCTE3.SubWorkflowBaseId <>  '0000000000000000' 
        and myCTE3.SubWorkflowBaseId is not null 
        and myCTE3.JoinColumn = wfb.WorkflowBaseId
        and wfb.RevOfRcdId    = wf.WorkflowId
        and wf.workflowid     = wfs.WorkflowId
    )       
OR
    (
        myCTE3.SubWorkflowId <>  '0000000000000000'
        and myCTE3.SubWorkflowId is not null 
        and myCTE3.JoinColumn =  wf.workflowid 
        --and wf.workflowid     = wfs.SubWorkflowId
        and wf.workflowid     = wfs.WorkflowId
  and wf.WorkflowBaseId = wfb.WorkflowBaseId     
    ) 

我已经在 SQLFiddle 上发布了我的 CTE 和示例数据。我忘记在最初的帖子中发布预期的结果。记录应返回的顺序如下。工作流程中有一些步骤指向子工作流程,但这些步骤应从结果中排除。

步骤 1a -> 步骤 2a -> 步骤 3a -> 步骤 3b -> 步骤 1c。

Sample Data and CTE

Plan hash value: 3402776882

------------------------------------------------------------------------------------------------------------
| Id  | Operation                                  | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                           |               |     2 |  4074 |    76   (2)| 00:00:01 |
|   1 |  SORT ORDER BY                             |               |     2 |  4074 |    76   (2)| 00:00:01 |
|*  2 |   VIEW                                     |               |     2 |  4074 |    75   (0)| 00:00:01 |
|   3 |    UNION ALL (RECURSIVE WITH) BREADTH FIRST|               |       |       |            |          |
|*  4 |     HASH JOIN                              |               |     1 |   119 |     6   (0)| 00:00:01 |
|*  5 |      TABLE ACCESS FULL                     | WORKFLOWSTEP2 |     1 |   102 |     3   (0)| 00:00:01 |
|*  6 |      TABLE ACCESS FULL                     | WORKFLOW2     |     1 |    17 |     3   (0)| 00:00:01 |
|   7 |     NESTED LOOPS                           |               |     1 |  2239 |    69   (0)| 00:00:01 |
|   8 |      BUFFER SORT (REUSE)                   |               |       |       |            |          |
|   9 |       MERGE JOIN CARTESIAN                 |               |     1 |   170 |     9   (0)| 00:00:01 |
|  10 |        MERGE JOIN CARTESIAN                |               |     1 |   136 |     6   (0)| 00:00:01 |
|  11 |         TABLE ACCESS FULL                  | WORKFLOWSTEP2 |     1 |   102 |     3   (0)| 00:00:01 |
|  12 |         BUFFER SORT                        |               |     3 |   102 |     3   (0)| 00:00:01 |
|  13 |          TABLE ACCESS FULL                 | WORKFLOWBASE2 |     3 |   102 |     3   (0)| 00:00:01 |
|  14 |        BUFFER SORT                         |               |     3 |   102 |     6   (0)| 00:00:01 |
|  15 |         TABLE ACCESS FULL                  | WORKFLOW2     |     3 |   102 |     3   (0)| 00:00:01 |
|* 16 |      RECURSIVE WITH PUMP                   |               |       |       |            |          |
------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("MYCTE3"."STEPTYPE"<>2)
   4 - access("WF"."WORKFLOWID"="WFS"."WORKFLOWID")
   5 - filter("WFS"."WORKFLOWID"='1100000000000000')
   6 - filter("WF"."WORKFLOWID"='1100000000000000')
  16 - filter("MYCTE3"."JOINCOLUMN" IS NOT NULL AND 
              "MYCTE3"."SUBWORKFLOWBASEID"<>'0000000000000000' AND "MYCTE3"."SUBWORKFLOWBASEID" IS NOT NULL AND 
              "MYCTE3"."JOINCOLUMN"="WFB"."WORKFLOWBASEID" AND "WFB"."REVOFRCDID"="WF"."WORKFLOWID" AND 
              "WF"."WORKFLOWID"="WFS"."WORKFLOWID" OR "MYCTE3"."SUBWORKFLOWID"<>'0000000000000000' AND 
              "MYCTE3"."SUBWORKFLOWID" IS NOT NULL AND "MYCTE3"."JOINCOLUMN"="WF"."WORKFLOWID" AND 
              "WF"."WORKFLOWID"="WFS"."WORKFLOWID" AND "WF"."WORKFLOWBASEID"="WFB"."WORKFLOWBASEID")

【问题讨论】:

    标签: sql oracle common-table-expression


    【解决方案1】:

    我会尝试重写为:

    with MyCTE3 (WorkflowStepName, Depth, WorkflowId, WorkflowStepId, SubWorkflowBaseId, SubWorkflowId, Sequence, StepType, JoinColumn, DisplayOrder) as 
    (
        SELECT wfs.WorkflowStepName
               ,1 as Depth
               , wfs.WorkflowId
               , wfs.WorkflowStepId
               , wfs.SubWorkflowBaseId
               , wfs.SubWorkflowId
               , wfs.Sequence
               , wfs.StepType
               , case 
                    when wfs.SubWorkflowBaseId = '0000000000000000' then wfs.SubworkflowId
                    --when wfs.SubWorkflowBaseId is null then wfs.SubworkflowId
                    when wfs.SubWorkflowId = '0000000000000000' then wfs.SubWorkflowBaseId
                    --when wfs.SubWorkflowId is null then wfs.SubWorkflowBaseId
                 end as JoinColumn
              ,1 || '.' || CAST(wfs.Sequence AS VARCHAR(255))  AS DisplayOrder
          --, to_clob(wfs.WorkflowId) as ProcessedWorkflow
          FROM WorkflowStep2 wfs, Workflow2  wf 
         WHERE wfs.WorkflowId = '1100000000000000' 
         and wf.WorkflowId = wfs.WorkflowId
    
        union all
        SELECT wfs.WorkflowStepName
                ,  Depth+1
                , wfs.WorkflowId
                , wfs.WorkflowStepId
                , wfs.SubWorkflowBaseId
                , wfs.SubWorkflowId
                , wfs.Sequence
                , wfs.StepType
                , case 
                    when wfs.SubWorkflowBaseId = '0000000000000000' then wfs.SubworkflowId
                    --when wfs.SubWorkflowBaseId is null then wfs.SubworkflowId
                    when wfs.SubWorkflowId = '0000000000000000' then wfs.SubWorkflowBaseId
                    --when wfs.SubWorkflowId is null then wfs.SubWorkflowBaseId
                    end as JoinColumn
            ,DisplayOrder || '.' || CAST(Depth+1 AS VARCHAR(255)) || CAST(wfs.Sequence AS VARCHAR(255))  as DisplayOrder
            --,to_clob(myCTE3.ProcessedWorkflow) || ',' || to_clob(wfs.WorkflowId) as ProcessedWorkflow
            FROM workflowbase2 wfb, workflowstep2 wfs, workflow2 wf, MyCTE3     
        where  myCTE3.JoinColumn is not null
        -- and (','+to_clob(myCTE3.ProcessedWorkflow) +',' not like '%,'+to_clob(wfs.WorkflowId)+',%')    
        -- and (dbms_lob.instr(myCTE3.ProcessedWorkflow,wfs.WorkflowId) = 0)  
            AND
            (    
                myCTE3.SubWorkflowBaseId <>  '0000000000000000' 
                and myCTE3.SubWorkflowBaseId is not null 
                and myCTE3.JoinColumn = wfb.WorkflowBaseId
                and wfb.RevOfRcdId    = wf.WorkflowId
                and wf.workflowid     = wfs.WorkflowId
            )   
    ), mcte2(WorkflowStepName, Depth, WorkflowId, WorkflowStepId, SubWorkflowBaseId, SubWorkflowId, Sequence, StepType, JoinColumn, DisplayOrder) AS (
      SELECT wfs.WorkflowStepName
               ,1 as Depth
               , wfs.WorkflowId
               , wfs.WorkflowStepId
               , wfs.SubWorkflowBaseId
               , wfs.SubWorkflowId
               , wfs.Sequence
               , wfs.StepType
               , case 
                    when wfs.SubWorkflowBaseId = '0000000000000000' then wfs.SubworkflowId
                    --when wfs.SubWorkflowBaseId is null then wfs.SubworkflowId
                    when wfs.SubWorkflowId = '0000000000000000' then wfs.SubWorkflowBaseId
                    --when wfs.SubWorkflowId is null then wfs.SubWorkflowBaseId
                 end as JoinColumn
              ,1 || '.' || CAST(wfs.Sequence AS VARCHAR(255))  AS DisplayOrder
          --, to_clob(wfs.WorkflowId) as ProcessedWorkflow
          FROM WorkflowStep2 wfs, Workflow2  wf 
         WHERE wfs.WorkflowId = '1100000000000000' 
         and wf.WorkflowId = wfs.WorkflowId
    
        union all
    
    
      SELECT wfs.WorkflowStepName
                ,  Depth+1
                , wfs.WorkflowId
                , wfs.WorkflowStepId
                , wfs.SubWorkflowBaseId
                , wfs.SubWorkflowId
                , wfs.Sequence
                , wfs.StepType
                , case 
                    when wfs.SubWorkflowBaseId = '0000000000000000' then wfs.SubworkflowId
                    --when wfs.SubWorkflowBaseId is null then wfs.SubworkflowId
                    when wfs.SubWorkflowId = '0000000000000000' then wfs.SubWorkflowBaseId
                    --when wfs.SubWorkflowId is null then wfs.SubWorkflowBaseId
                    end as JoinColumn
            ,DisplayOrder || '.' || CAST(Depth+1 AS VARCHAR(255)) || CAST(wfs.Sequence AS VARCHAR(255))  as DisplayOrder
            --,to_clob(myCTE3.ProcessedWorkflow) || ',' || to_clob(wfs.WorkflowId) as ProcessedWorkflow
            FROM workflowbase2 wfb, workflowstep2 wfs, workflow2 wf, MyCTE3     
        where  myCTE3.JoinColumn is not null
           AND
            (
                myCTE3.SubWorkflowId <>  '0000000000000000'
                and myCTE3.SubWorkflowId is not null 
                and myCTE3.JoinColumn =  wf.workflowid 
                --and wf.workflowid     = wfs.SubWorkflowId
                and wf.workflowid     = wfs.WorkflowId
          and wf.WorkflowBaseId = wfb.WorkflowBaseId     
            ) 
    )
    select   WorkFlowStepName, DisplayOrder --, DisplayOrder, ProcessedWorkflow
      from MyCTE3
    where MYCTE3.StepType <> 2
    UNION ALL
    select   WorkFlowStepName, DisplayOrder --, DisplayOrder, ProcessedWorkflow
      from MyCTE3
    where MYCTE3.StepType <> 2
    order by DisplayOrder ;
    

    SQLFiddle Demo

    我用过OR expansion

    OR 扩展是一种可用于优化析取查询(包含 OR 子句的查询)的转换。 OR 扩展的基本思想是将包含析取的查询转换为两个或多个分支的 UNION ALL 查询的形式。这是通过将析取拆分为其组件并将每个组件与 UNION ALL 查询的分支相关联来完成的。

    【讨论】:

    • 感谢有关 OR 扩展的指针。我会调查一下,看看是否有帮助。现在,结果集包括重复项,并且似乎没有深入到结果集中。这是我的错,我忘记在我的帖子中发布预期的结果。我用这些信息更新了帖子。我会看看你的例子,看看我能不能做点什么。我会发回我发现的。再次感谢您的努力和想法。
    • 我想我明白你在做什么。 “And (...) Or(...)” 是链接的。换句话说,在递归查询中,要么执行“And (...)”,要么执行“OR (...)”,而不是两者都执行。但是,在查询查看结果集中的步骤之前,我不会知道执行哪个路径。 WorkflowStep 有两列,SubWorkflowBaseId 和 SubWorkflowId。随着查询的深入,它将选择“And (...) aka SubWorkflowBaseId(如果它具有有效值)或“OR (...)”SubWorkflowId(如果它具有有效值)并将继续循环直到没有更多记录找到匹配项。
    猜你喜欢
    • 2014-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-29
    • 1970-01-01
    • 2016-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多