Trying to refactor the recursive query in an Oracle CTE?

孤街浪徒 提交于 2020-01-15 03:54:12

问题


I have been working on a CTE which works pretty good for MS-SQL. I am translating it to Oracle and it works. However it is very slow and when I look at the explain plan I see "Merge Join Cartesian" when I run the query. I am running against Oracle 12.2. If I remove the

"OR" (...)

part of the query the speed is dramatically improved. However the "Merge Join Cartesian" still shows up in the explain plan. I have determined where the problem is in the query. The

"and" (....) "or" (...)

in the recursive query is the source of the slowness. I need both sections because there are different paths so getting rid of one or the other is not really an option. I have tried to rewrite that part of CTE but I am not having much luck. My first try was to include a second query in the recursive section. However Oracle ONLY allows one query in the recursive section of the CTE. I have also tried to break this down into smaller pieces but my attempts have failed. I would appreciate any suggestions on rewriting the recursive section in this 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     
    ) 

I have posted my CTE and sample data on SQLFiddle. I forgot to post the expected results with my initial post. The order the records should come back is the following. There are steps in a workflow which point to subworkflow but those should be excluded from the results.

Step-1a -> Step-2a -> Step-3a -> Step-3b -> Step-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")

回答1:


I would try to rewrite is as:

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

I've used OR expansion

OR expansion is a transformation that can be used to optimize disjunctive queries (queries that contain OR clauses). The basic idea in OR expansion is to transform a query containing disjunctions into the form of a UNION ALL query of two or more branches. This is done by splitting the disjunction into its components and associating each component with a branch of a UNION ALL query.



来源:https://stackoverflow.com/questions/49927709/trying-to-refactor-the-recursive-query-in-an-oracle-cte

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!