问题
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