Why does this SELECT statement take more than two hours? - sql

I have two tables A & B. A has 6,760,636 records and B has 452,175,960 records. Here is the SELECT Statement I'm using:
SELECT /*+ parallel (T,1) */
T.*
FROM TABLE_A T,
TABLE_B P
WHERE T.DESTINATION = P.DESTINATION
AND T.SAIL_DATE = P.SAIL_DATE
AND T.PACKAGE_TYPE = P.PACKAGE_TYPE
AND T.CABIN_CATEGORY = P.CABIN_CATEGORY
AND T.BOOKING_SOURCE = P.BOOKING_SOURCE
AND T.FARE_TYPE = P.FARE_TYPE
AND T.POST_DATE = P.POST_DATE;
I tried creating the index on TABLE_A, but still it is not considering the INDEX and doing a FULL TABLE SCAN.
The EXPLAIN PLAN for above is
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| TQ |IN-OUT| PQ Distrib |
----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6760K| 1805M| 747K (1)| | | |
| 1 | SORT AGGREGATE | | 1 | 48 | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| CL_PRICING_CONTROLS | 1 | 48 | 2 (0)| | | |
| 3 | INDEX RANGE SCAN | CL_PRICING_CONTROLS_IX3 | 1 | | 1 (0)| | | |
| 4 | PX COORDINATOR | | | | | | | |
| 5 | PX SEND QC (RANDOM) | :TQ10002 | 6760K| 1805M| 747K (1)| Q1,02 | P->S | QC (RAND) |
| 6 | HASH JOIN | | 6760K| 1805M| 747K (1)| Q1,02 | PCWP | |
| 7 | PX RECEIVE | | 6760K| 1437M| 1443 (1)| Q1,02 | PCWP | |
| 8 | PX SEND HASH | :TQ10001 | 6760K| 1437M| 1443 (1)| Q1,01 | P->P | HASH |
| 9 | PX BLOCK ITERATOR | | 6760K| 1437M| 1443 (1)| Q1,01 | PCWC | |
| 10 | TABLE ACCESS FULL | TMP_RES_PRICE_CONTROL_111 | 6760K| 1437M| 1443 (1)| Q1,01 | PCWP | |
| 11 | BUFFER SORT | | | | | Q1,02 | PCWC | |
| 12 | PX RECEIVE | | 450M| 23G| 746K (1)| Q1,02 | PCWP | |
| 13 | PX SEND HASH | :TQ10000 | 450M| 23G| 746K (1)| | S->P | HASH |
| 14 | INDEX FULL SCAN | CL_PRICING_CONTROLS_IX1 | 450M| 23G| 746K (1)| | | |
I tried parallel query on both tables as below
SELECT /*+ PARALLEL(T, 32) PARALLEL(P, 32)*/
T.*
FROM TABLE_A T,
TABLE_B P
WHERE T.DESTINATION = P.DESTINATION
AND T.SAIL_DATE = P.SAIL_DATE
AND T.PACKAGE_TYPE = P.PACKAGE_TYPE
AND T.CABIN_CATEGORY = P.CABIN_CATEGORY
AND T.BOOKING_SOURCE = P.BOOKING_SOURCE
AND T.FARE_TYPE = P.FARE_TYPE
AND T.POST_DATE = P.POST_DATE;
But this results in the EXPLAIN PLAN as below, where I see CPU with lower Cost, but still it is taking two hours.
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| TQ |IN-OUT| PQ Distrib |
----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6760K| 1805M| 59345 (1)| | | |
| 1 | SORT AGGREGATE | | 1 | 48 | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| CL_PRICING_CONTROLS | 1 | 48 | 2 (0)| | | |
| 3 | INDEX RANGE SCAN | CL_PRICING_CONTROLS_IX3 | 1 | | 1 (0)| | | |
| 4 | PX COORDINATOR | | | | | | | |
| 5 | PX SEND QC (RANDOM) | :TQ10002 | 6760K| 1805M| 59345 (1)| Q1,02 | P->S | QC (RAND) |
| 6 | HASH JOIN BUFFERED | | 6760K| 1805M| 59345 (1)| Q1,02 | PCWP | |
| 7 | PX RECEIVE | | 6760K| 1437M| 1443 (1)| Q1,02 | PCWP | |
| 8 | PX SEND HASH | :TQ10000 | 6760K| 1437M| 1443 (1)| Q1,00 | P->P | HASH |
| 9 | PX BLOCK ITERATOR | | 6760K| 1437M| 1443 (1)| Q1,00 | PCWC | |
| 10 | TABLE ACCESS FULL | TMP_RES_PRICE_CONTROL_111 | 6760K| 1437M| 1443 (1)| Q1,00 | PCWP | |
| 11 | PX RECEIVE | | 450M| 23G| 57858 (1)| Q1,02 | PCWP | |
| 12 | PX SEND HASH | :TQ10001 | 450M| 23G| 57858 (1)| Q1,01 | P->P | HASH |
| 13 | PX BLOCK ITERATOR | | 450M| 23G| 57858 (1)| Q1,01 | PCWC | |
| 14 | TABLE ACCESS FULL | CL_PRICING_CONTROLS | 450M| 23G| 57858 (1)| Q1,01 | PCWP | |

Please look at your query, specifically at its WHERE clause. It consists entirely of join conditions. It could be rewritten using the ANSI-92 syntax to this:
SELECT /*+ parallel (T,1) */
T.*
FROM TABLE_A T
inner join TABLE_B P
on T.DESTINATION = P.DESTINATION
AND T.SAIL_DATE = P.SAIL_DATE
AND T.PACKAGE_TYPE = P.PACKAGE_TYPE
AND T.CABIN_CATEGORY = P.CABIN_CATEGORY
AND T.BOOKING_SOURCE = P.BOOKING_SOURCE
AND T.FARE_TYPE = P.FARE_TYPE
AND T.POST_DATE = P.POST_DATE;
So every row in each table has to be considered. Clearly full table scans are the only plausible access path.
Building a composite index on all columns in the JOIN clause is unlikely to change this. You are selecting all the columns from TABLE_A, so the database needs to visit the table anyway.
Unless the number of records in the intersection between the two tables is incredibly small it will be more efficient to read the tables in multi-block reads rather than index scans with table row look-ups. As it is, you're selecting almost one-in-six of all the rows in a half-billion row table. How would an index make that faster?
Incidentally, how did you decide on the degree of parallelism? How many CPUs does your server have? What other processes are running concurrently? What is the value of MAX_PARALLEL_SERVERS? Find out more.

Related

Is there a way to fine-tune my Oracle SQL code?

Is there a way to fine-tune my Oracle SQL code below? All tables are residing in the same schema.
MERGE INTO W_PURCH_COST_F TGT USING
(SELECT
/*+ PARALLEL(8) */
cost.INTEGRATION_ID,
cost.X_RECEIVED_ON_DT,
COALESCE(gaap.ROW_WID,0) X_GAAP_EXCH_RATE_WID
FROM W_Purch_Cost_F_3955 cost
JOIN W_DAY_D wday
ON TRUNC(cost.X_RECEIVED_ON_DT)=TRUNC(wday.CALENDAR_DATE)
LEFT OUTER JOIN WC_GAAP_EXCH_RATE_G gaap
ON gaap.PERIOD =wday.PER_NAME_ENT_PERIOD
) SRC ON (TGT.INTEGRATION_ID = SRC.INTEGRATION_ID AND TGT.DATASOURCE_NUM_ID = 310)
WHEN MATCHED THEN
UPDATE SET TGT.X_GAAP_EXCH_RATE_WID = SRC.X_GAAP_EXCH_RATE_WID;
TGT is the target table and SRC is the source table, all residing in a staging environment and the same schema.
Plan hash value: 679420733
-----------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
-----------------------------------------------------------------------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | 18G| 256G| | 186K (5)| 00:00:15 | | | |
| 1 | MERGE | W_PURCH_COST_F | | | | | | | | |
| 2 | PX COORDINATOR | | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10002 | 18G| 10T| | 186K (5)| 00:00:15 | Q1,02 | P->S | QC (RAND) |
| 4 | VIEW | | | | | | | Q1,02 | PCWP | |
|* 5 | HASH JOIN RIGHT OUTER BUFFERED| | 18G| 10T| | 186K (5)| 00:00:15 | Q1,02 | PCWP | |
| 6 | TABLE ACCESS FULL | WC_GAAP_EXCH_RATE_G | 16261 | 269K| | 5 (0)| 00:00:01 | Q1,02 | PCWP | |
|* 7 | HASH JOIN | | 398M| 228G| 767M| 179K (1)| 00:00:15 | Q1,02 | PCWP | |
| 8 | PX RECEIVE | | 10M| 6014M| | 18997 (1)| 00:00:02 | Q1,02 | PCWP | |
| 9 | PX SEND HASH | :TQ10000 | 10M| 6014M| | 18997 (1)| 00:00:02 | Q1,00 | P->P | HASH |
| 10 | PX BLOCK ITERATOR | | 10M| 6014M| | 18997 (1)| 00:00:02 | Q1,00 | PCWC | |
|* 11 | TABLE ACCESS FULL | W_PURCH_COST_F | 10M| 6014M| | 18997 (1)| 00:00:02 | Q1,00 | PCWP | |
| 12 | PX RECEIVE | | 398M| 13G| | 28134 (2)| 00:00:03 | Q1,02 | PCWP | |
| 13 | PX SEND HASH | :TQ10001 | 398M| 13G| | 28134 (2)| 00:00:03 | Q1,01 | P->P | HASH |
|* 14 | HASH JOIN | | 398M| 13G| | 28134 (2)| 00:00:03 | Q1,01 | PCWP | |
| 15 | TABLE ACCESS FULL | W_DAY_D | 10790 | 210K| | 29 (0)| 00:00:01 | Q1,01 | PCWP | |
| 16 | PX BLOCK ITERATOR | | 383M| 6219M| | 27963 (2)| 00:00:03 | Q1,01 | PCWC | |
| 17 | TABLE ACCESS FULL | W_PURCH_COST_F_3955 | 383M| 6219M| | 27963 (2)| 00:00:03 | Q1,01 | PCWP | |
-----------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("GAAP"."PERIOD"(+)="WDAY"."PER_NAME_ENT_PERIOD")
7 - access("TGT"."INTEGRATION_ID"="COST"."INTEGRATION_ID")
11 - filter("TGT"."DATASOURCE_NUM_ID"=310)
14 - access(TRUNC(INTERNAL_FUNCTION("COST"."X_RECEIVED_ON_DT"))=TRUNC(INTERNAL_FUNCTION("WDAY"."CALENDAR_DATE")))
Note
-----
- dynamic statistics used: dynamic sampling (level=AUTO)
- Degree of Parallelism is 8 because of hint
- PDML is disabled in current session

can I use different hints in one clause of union in oracle

I have a merge query of below format -
merge into dest
using (select /*+ use_hash(t1,t2) parallel (4)*/ t1_name ,count(*) from table1 , table2 on t1.col=t2.col
group by t1_name) src
on (values)
when matched then update dest.col
now I have to do union in this query
merge into dest
using (select count(*),t1_name from (select /*+ use_hash(t1,t2) parallel (4)*/ t1_name ,count(*) from table1 , table2 on t1.col=t2.col
group by t1_name
union all
select t1_name,count(*) from table t1 group by t1_name from table t1 group by t1_name ) group by t1_name) src
on (values)
when matched then update dest.col
My question is do I have to give parallel hint in second union clause also . Also use_hash hint which was running good earlier would not have impact on adding union clause in the query
The hints apply to each select independently. First, you need to understand how parallel execution works in terms of session level and/or hints applied.
In this you have three elements
The merge statement itself which will update some rows and will not run in parallel
The first data set recover with the first select will run in parallel
The second will run in parallel because of the hints applied to the first statement
I am assuming the tables are configured with noparallel and that you are not enabling parallel dml. I won't even try to replicate the query, because I don't know exactly what you want to do with it.
Test case
SQL> create table t1 ( c1 number, c2 number ) ;
Table created.
SQL> create table t2 ( c1 number, c3 number ) ;
Table created.
SQL> ed
Wrote file afiedt.buf
1 declare
2 begin
3 for i in 1 .. 10000
4 loop
5 insert into t1 values ( i , dbms_random.value );
6 insert into t2 values ( i , dbms_random.value );
7 end loop;
8* end;
SQL> /
PL/SQL procedure successfully completed.
SQL> ed
Wrote file afiedt.buf
1 declare
2 begin
3 for i in 1 .. 10000
4 loop
5 insert into t1 values ( i , dbms_random.value );
6 end loop;
7* end;
SQL> /
PL/SQL procedure successfully completed.
SQL> select count(*) from t1 ;
COUNT(*)
----------
20000
SQL> select count(*) from t2 ;
COUNT(*)
----------
10000
Now I am going to build a merge statement to update the table t2 using a source based on a union select. Forget in this case the meaning of the query, which has no sense at all, but the resulting execution plan
SQL> merge into t dest
2 using ( select distinct t1 from (
3 select /*+ use_hash(t2,t1) parallel (4)*/ t1.c1 as t1 from t1 inner join t2 on ( t1.c1=t2.c1)
4 union all
5 select t2.c1 as t1 from t1 inner join t2 on ( t1.c1=t2.c1 )
6 )) src on ( src.t1 = dest.c1 )
7* when matched then update set dest.c2 = dbms_random.value
10000 rows merged.
Execution Plan
----------------------------------------------------------
Plan hash value: 889987475
-----------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
-----------------------------------------------------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | 10000 | 253K| 43 (3)| 00:00:01 | | | |
| 1 | MERGE | T | | | | | | | |
| 2 | PX COORDINATOR | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10006 | 10000 | 498K| 43 (3)| 00:00:01 | Q1,06 | P->S | QC (RAND) |
| 4 | VIEW | | | | | | Q1,06 | PCWP | |
|* 5 | HASH JOIN | | 10000 | 498K| 43 (3)| 00:00:01 | Q1,06 | PCWP | |
| 6 | PX RECEIVE | | 10000 | 126K| 17 (6)| 00:00:01 | Q1,06 | PCWP | |
| 7 | PX SEND BROADCAST | :TQ10005 | 10000 | 126K| 17 (6)| 00:00:01 | Q1,05 | P->P | BROADCAST |
| 8 | VIEW | | 10000 | 126K| 17 (6)| 00:00:01 | Q1,05 | PCWP | |
| 9 | HASH UNIQUE | | 10000 | 126K| 17 (6)| 00:00:01 | Q1,05 | PCWP | |
| 10 | PX RECEIVE | | 10000 | 126K| 17 (6)| 00:00:01 | Q1,05 | PCWP | |
| 11 | PX SEND HASH | :TQ10004 | 10000 | 126K| 17 (6)| 00:00:01 | Q1,04 | P->P | HASH |
| 12 | HASH UNIQUE | | 10000 | 126K| 17 (6)| 00:00:01 | Q1,04 | PCWP | |
| 13 | VIEW | | 40000 | 507K| 16 (0)| 00:00:01 | Q1,04 | PCWP | |
| 14 | UNION-ALL | | | | | | Q1,04 | PCWP | |
|* 15 | HASH JOIN | | 20000 | 507K| 8 (0)| 00:00:01 | Q1,04 | PCWP | |
| 16 | PX RECEIVE | | 10000 | 126K| 3 (0)| 00:00:01 | Q1,04 | PCWP | |
| 17 | PX SEND HASH | :TQ10000 | 10000 | 126K| 3 (0)| 00:00:01 | Q1,00 | P->P | HASH |
| 18 | PX BLOCK ITERATOR | | 10000 | 126K| 3 (0)| 00:00:01 | Q1,00 | PCWC | |
| 19 | TABLE ACCESS FULL| T2 | 10000 | 126K| 3 (0)| 00:00:01 | Q1,00 | PCWP | |
| 20 | PX RECEIVE | | 20000 | 253K| 5 (0)| 00:00:01 | Q1,04 | PCWP | |
| 21 | PX SEND HASH | :TQ10001 | 20000 | 253K| 5 (0)| 00:00:01 | Q1,01 | P->P | HASH |
| 22 | PX BLOCK ITERATOR | | 20000 | 253K| 5 (0)| 00:00:01 | Q1,01 | PCWC | |
| 23 | TABLE ACCESS FULL| T1 | 20000 | 253K| 5 (0)| 00:00:01 | Q1,01 | PCWP | |
|* 24 | HASH JOIN | | 20000 | 507K| 8 (0)| 00:00:01 | Q1,04 | PCWP | |
| 25 | PX RECEIVE | | 10000 | 126K| 3 (0)| 00:00:01 | Q1,04 | PCWP | |
| 26 | PX SEND HASH | :TQ10002 | 10000 | 126K| 3 (0)| 00:00:01 | Q1,02 | P->P | HASH |
| 27 | PX BLOCK ITERATOR | | 10000 | 126K| 3 (0)| 00:00:01 | Q1,02 | PCWC | |
| 28 | TABLE ACCESS FULL| T2 | 10000 | 126K| 3 (0)| 00:00:01 | Q1,02 | PCWP | |
| 29 | PX RECEIVE | | 20000 | 253K| 5 (0)| 00:00:01 | Q1,04 | PCWP | |
| 30 | PX SEND HASH | :TQ10003 | 20000 | 253K| 5 (0)| 00:00:01 | Q1,03 | P->P | HASH |
| 31 | PX BLOCK ITERATOR | | 20000 | 253K| 5 (0)| 00:00:01 | Q1,03 | PCWC | |
| 32 | TABLE ACCESS FULL| T1 | 20000 | 253K| 5 (0)| 00:00:01 | Q1,03 | PCWP | |
| 33 | PX BLOCK ITERATOR | | 94911 | 3522K| 26 (0)| 00:00:01 | Q1,06 | PCWC | |
| 34 | TABLE ACCESS FULL | T | 94911 | 3522K| 26 (0)| 00:00:01 | Q1,06 | PCWP | |
-----------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("SRC"."T1"="DEST"."C1")
15 - access("T1"."C1"="T2"."C1")
24 - access("T1"."C1"="T2"."C1")
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
- Degree of Parallelism is 4 because of hint
- PDML is disabled in current session
The most important parts of the execution plan below:
As the tables has no statistics, Oracle has used dynamic sampling.
The degree used in 4 due to the hing.
Parallel DML is not enabled at session level, therefore the update resulting of the merge runs in noparallel.
Oracle builds an VIEW dynamically to join the two datasets of the union, as a result of one of them is running in parallel, the CBO runs in parallel the second one even though it has no hints.
In a USE_HASH hint use always as driving table the smaller table. Normally the CBO will always do that, so I would recommend to carefully use the USE_HASH hint, because if the smaller table grows to a point that is even greater than the second one, your hint will produce a lot of performance degradation.
Hope it clarifies.

How to debug an Oracle query given an explain plan?

IMAGE OF TOAD: query waits
WITH
NJ AS (SELECT CNATJUR FROM RE_CD.CDTD005_NATUR_JURIDICA WHERE DESCRICAO = 'Particulares' AND DTEND_VER = '9999-12-31 00:00' ),
PAISES AS (SELECT PAIS_BST FROM RE_CD.CDTD006_PAISES WHERE GRUPO_PAIS = 'RESTO UNIAO EUROPEIA' AND DTEND_VER = '9999-12-31 00:00' ),
GAR AS (SELECT /*+PARALLEL(4)*/ CCONTA, CREFERENCIA_IMP FROM RE_DM.DMTD002_GAR_CONTRATO WHERE TIPO_GAR = 'GAR REAL' AND TIPO_GAR2 = 'GAR DINERARIA' AND FLAG_PRIORIDADE = 1 AND IDVERSAO = (SELECT MAX(IDVERSAO) FROM RE_DM.DMTD001_VERSOES WHERE TABELA = 'RE_DM.DMTD002_GAR_CONTRATO' AND DTEND_VER = '9999-12-31 00:00' ) )
SELECT /*+PARALLEL(4)*/ MOROS.COD_PERIMETRO, MOROS.SOCIEDADE
FROM RE_DM.DMTF020_MOROS MOROS
INNER JOIN GAR ON MOROS.CCONTA = GAR.CCONTA AND MOROS.CREFERENCIA = GAR.CREFERENCIA_IMP
INNER JOIN NJ ON MOROS.COD_NATJUR = NJ.CNATJUR
INNER JOIN PAISES ON MOROS.COD_PAIS = PAISES.PAIS_BST
WHERE MOROS.IDVERSAO = 167
AND MOROS.SEGMENTO <> 'PC' AND MOROS.SEGMENTO <> 'LO' AND MOROS.SEGMENTO <> 'GA'
AND MOROS.SEGMENTO <> 'G1' AND MOROS.SEGMENTO <> 'G2' AND MOROS.SEGMENTO <> 'G3'
AND MOROS.INPUT = 'IMP'
AND MOROS.FLAG_MOROSIDADE = 0;
Plan hash value: 441958758
----------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
----------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 76752 | 16M| 22115 (1)| 00:01:07 | | | |
| 1 | PX COORDINATOR | | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ20002 | 76752 | 16M| 22113 (1)| 00:01:07 | Q2,02 | P->S | QC (RAND) |
|* 3 | HASH JOIN | | 76752 | 16M| 22113 (1)| 00:01:07 | Q2,02 | PCWP | |
| 4 | PX RECEIVE | | 42 | 1512 | 2 (0)| 00:00:01 | Q2,02 | PCWP | |
| 5 | PX SEND BROADCAST | :TQ20000 | 42 | 1512 | 2 (0)| 00:00:01 | Q2,00 | P->P | BROADCAST |
| 6 | PX BLOCK ITERATOR | | 42 | 1512 | 2 (0)| 00:00:01 | Q2,00 | PCWC | |
|* 7 | TABLE ACCESS FULL | CDTD006_PAISES | 42 | 1512 | 2 (0)| 00:00:01 | Q2,00 | PCWP | |
|* 8 | HASH JOIN | | 237K| 41M| 22110 (1)| 00:01:07 | Q2,02 | PCWP | |
| 9 | PX RECEIVE | | 23 | 1150 | 2 (0)| 00:00:01 | Q2,02 | PCWP | |
| 10 | PX SEND BROADCAST | :TQ20001 | 23 | 1150 | 2 (0)| 00:00:01 | Q2,01 | P->P | BROADCAST |
| 11 | PX BLOCK ITERATOR | | 23 | 1150 | 2 (0)| 00:00:01 | Q2,01 | PCWC | |
|* 12 | TABLE ACCESS FULL | CDTD005_NATUR_JURIDICA | 23 | 1150 | 2 (0)| 00:00:01 | Q2,01 | PCWP | |
| 13 | NESTED LOOPS | | | | | | Q2,02 | PCWP | |
| 14 | NESTED LOOPS | | 795K| 100M| 22105 (1)| 00:01:07 | Q2,02 | PCWP | |
| 15 | PX BLOCK ITERATOR | | | | | | Q2,02 | PCWC | |
|* 16 | TABLE ACCESS FULL | DMTD002_GAR_CONTRATO | 6357 | 341K| 4423 (1)| 00:00:14 | Q2,02 | PCWP | |
| 17 | SORT AGGREGATE | | 1 | 42 | | | Q2,02 | PCWP | |
| 18 | PX COORDINATOR | | | | | | | | |
| 19 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 42 | | | Q1,00 | P->S | QC (RAND) |
| 20 | SORT AGGREGATE | | 1 | 42 | | | Q1,00 | PCWP | |
| 21 | PX BLOCK ITERATOR | | 1 | 42 | 2 (0)| 00:00:01 | Q1,00 | PCWC | |
|* 22 | TABLE ACCESS FULL | DMTD001_VERSOES | 1 | 42 | 2 (0)| 00:00:01 | Q1,00 | PCWP | |
|* 23 | INDEX RANGE SCAN | DMTF020_MOROS_INDEX2 | 21 | | 1 (0)| 00:00:01 | Q2,02 | PCWP | |
|* 24 | TABLE ACCESS BY INDEX ROWID| DMTF020_MOROS | 125 | 9750 | 3 (0)| 00:00:01 | Q2,02 | PCWP | |
----------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("MOROS"."COD_PAIS"="PAIS_BST")
7 - filter("GRUPO_PAIS"='RESTO UNIAO EUROPEIA' AND "DTEND_VER"='9999-12-31 00:00')
8 - access("MOROS"."COD_NATJUR"="CNATJUR")
12 - filter("DESCRICAO"='Particulares' AND "DTEND_VER"='9999-12-31 00:00')
16 - filter("TIPO_GAR2"='GAR DINERARIA' AND "TIPO_GAR"='GAR REAL' AND "FLAG_PRIORIDADE"=1 AND "IDVERSAO"= (SELECT
MAX(SYS_OP_CSR(SYS_OP_MSR(MAX("IDVERSAO")),0)) FROM "RE_DM"."DMTD001_VERSOES" "DMTD001_VERSOES" WHERE
"TABELA"='RE_DM.DMTD002_GAR_CONTRATO' AND "DTEND_VER"='9999-12-31 00:00'))
22 - filter("TABELA"='RE_DM.DMTD002_GAR_CONTRATO' AND "DTEND_VER"='9999-12-31 00:00')
23 - access("MOROS"."CREFERENCIA"="CREFERENCIA_IMP")
filter("MOROS"."CREFERENCIA" IS NOT NULL)
24 - filter("MOROS"."IDVERSAO"=167 AND "MOROS"."CCONTA" IS NOT NULL AND TO_NUMBER("MOROS"."FLAG_MOROSIDADE")=0 AND
"MOROS"."INPUT"='IMP' AND "MOROS"."SEGMENTO"<>'GA' AND "MOROS"."SEGMENTO"<>'G1' AND "MOROS"."SEGMENTO"<>'PC' AND
"MOROS"."SEGMENTO"<>'G2' AND "MOROS"."SEGMENTO"<>'G3' AND "MOROS"."SEGMENTO"<>'LO' AND "MOROS"."CCONTA"="CCONTA")
Note
-----
- dynamic sampling used for this statement (level=5)
- Degree of Parallelism is 4 because of hint
Does anyone see something fishy with this "explain plan"? I've added indexes, I ran statistics, rebuild indexes... the server is a laptop with 8gb ram, SSD hard drive and i7 cpu.
Any ideas?
Obviously, look for your full table scans, but they aren't always the culprit. The problem appears to be in the nested loops.
I would focus on DMTD002_GAR_CONTRATO, and perhaps place an index to eliminate the
full table scan; however, I suspect that the main problem may be in the subquery in the where clause that creates table alias "GAR".
It would be worth experimenting with a hardcoded value rather than a subquery to see if it improves performance.
(Posted on behalf of OP).
Our temp tablespace was super tiny. After increasing the size all things start to go faster and smoother. Thanks to all that helped.

PerformanceTune#Huge Table Size

Looking for a help in PT.
Attaching the psuedo query..
There are 3 tables used below all are having huge chunk of records
t2 ~ 10 million,
t1 ~ 15 million ,
t3 ~ 8 million
Used Parallel Hints . But not much help in cost.. Cost is shooting to more than 200k
(SELECT /*+ parallel (a,8) parallel (b,8)*/
DISTINCT B.ACCNT_NUM
FROM s1.t1 a, s2.t2 b
WHERE a.accnt_num = b.accnt_num
AND product_key IN (SELECT product_key FROM s3.t3 WHERE prod_type_cd = 'S')
AND a.active_flg = 'Y'
AND a.deleted_flg = 'N'
GROUP BY B.ACCNT_NUM, a.product_key
HAVING COUNT (*) > 1)
Plan:
| 0 | SELECT STATEMENT
| 1 | 54 | 204K (1)| 00:47:39 | | | | | | | 1 | PX COORDINATOR | | | | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10004 | 1 | 54 | 204K (1)| 00:47:39 | | | Q1,04 | P->S | QC (RAND) |
| 3 | HASH UNIQUE | | 1 | 54 | 204K (1)| 00:47:39 | | | Q1,04 | PCWP | |
| 4 | PX RECEIVE | | 1 | 54 | 204K (1)| 00:47:39 | | | Q1,04 | PCWP | |
| 5 | PX SEND HASH | :TQ10003 | 1 | 54 | 204K (1)| 00:47:39 | | | Q1,03 | P->P | HASH |
|* 6 | FILTER | | | | | | | | Q1,03 | PCWC | |
| 7 | HASH GROUP BY | | 1 | 54 | 204K (1)| 00:47:39 | | | Q1,03 | PCWP | |
| 8 | PX RECEIVE | | 1 | 54 | 204K (1)| 00:47:39 | | | Q1,03 | PCWP | |
| 9 | PX SEND HASH | :TQ10002 | 1 | 54 | 204K (1)| 00:47:39 | | | Q1,02 | P->P | HASH |
| 10 | HASH GROUP BY | | 1 | 54 | 204K (1)| 00:47:39 | | | Q1,02 | PCWP | |
|* 11 | HASH JOIN | | 90476 | 4771K| 204K (1)| 00:47:39 | | | Q1,02 | PCWP | |
| 12 | PX RECEIVE | | 22572 | 462K| 66147 (1)| 00:15:27 | | | Q1,02 | PCWP | |
| 13 | PX SEND BROADCAST | :TQ10001 | 22572 | 462K| 66147 (1)| 00:15:27 | | | Q1,01 | P->P | BROADCAST |
|* 14 | TABLE ACCESS BY LOCAL INDEX ROWID| table 1 | 22572 | 462K| 66147 (1)| 00:15:27 | | | Q1,01 | PCWP | |
| 15 | NESTED LOOPS | | 135K| 4893K| 198K (1)| 00:46:19 | | | Q1,01 | PCWP | |
| 16 | BUFFER SORT | | | | | | | | Q1,01 | PCWC | |
| 17 | PX RECEIVE | | | | | | | | Q1,01 | PCWP | |
| 18 | PX SEND BROADCAST | :TQ10000 | | | | | | | | S->P | BROADCAST |
| 19 | SORT UNIQUE | | 6 | 96 | 3 (0)| 00:00:01 | | | | | |
|* 20 | TABLE ACCESS FULL | table 3 | 6 | 96 | 3 (0)| 00:00:01 | | | | | |
| 21 | PX PARTITION HASH ALL | | 65923 | | 207 (1)| 00:00:03 | 1 | 32 | Q1,01 | PCWC | |
|* 22 | INDEX RANGE SCAN | I_table1 | 65923 | | 207 (1)| 00:00:03 | 33 | 64 | Q1,01 | PCWP | |
| 23 | PX BLOCK ITERATOR | | 12M| 201M| 5738 (2)| 00:01:21 | | | Q1,02 | PCWC | |
| 24 | INDEX FAST FULL SCAN | I_table 2 | 12M| 201M| 5738 (2)| 00:01:21 | | | Q1,02 | PCWP | |
Regards
So the eye is drawn to this line of the Explain Plan:
|* 20 | TABLE ACCESS FULL | table 3 | 6 | 96 | 3 (0)| 00:00:01 | | | | | |
The optimizer on the other hand thinks it has six rows, and so regards it as a very cheap operation. The OP says that t3 has ~ 8 million rows. A Full Table Scan of that will be pretty expensive.
The optimizer is clever but relies on accurate table and index statistics. It seems a good place to start would be a review of the pertinent statistics, and refreshing the ones which are inaccurate. Find out more.

Connect By Level In Subquery Slow

I am using a connect_by_level subquery in an Oracle database to supply dates to join with another table. The connect by level appears to be causing the query to run very slowly. Here is my slow query:
select t.code, d.month_end, count(*)
from device t,
(select add_months(trunc(sysdate, 'MM'), - (level - 1)) - 1 MONTH_END
from dual
connect by level <= 1) d
where (d.month_end between t.date and t.exp_date)
group by t.code, d.month_end
The slow query above took about 2 hours to run the other day. The query below which should be equivalent runs in less than 30 seconds:
select t.code, trunc(sysdate, 'MM') - 1 month_end, count(*)
from device t
where ((trunc(sysdate, 'MM') - 1) between t.date and t.exp_date)
group by t.code
Ultimately, I want the 1st query to return data for the past 24 months, but I need to figure out why it is running so slowly with only 1 month as the criteria. Any suggestions on what is causing the slow completion time with the connect by level query?
Edit to add explain plan output for slow query:
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 520 | 65520 | 134K (1)| 00:00:03 | | | | | |
| 1 | PX COORDINATOR | | | | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10006 | 520 | 65520 | 134K (1)| 00:00:03 | | | Q1,06 | P->S | QC (RAND) |
| 3 | HASH GROUP BY | | 520 | 65520 | 134K (1)| 00:00:03 | | | Q1,06 | PCWP | |
| 4 | PX RECEIVE | | 520 | 65520 | 134K (1)| 00:00:03 | | | Q1,06 | PCWP | |
| 5 | PX SEND HASH | :TQ10005 | 520 | 65520 | 134K (1)| 00:00:03 | | | Q1,05 | P->P | HASH |
| 6 | HASH GROUP BY | | 520 | 65520 | 134K (1)| 00:00:03 | | | Q1,05 | PCWP | |
|* 7 | HASH JOIN OUTER | | 520 | 65520 | 134K (1)| 00:00:03 | | | Q1,05 | PCWP | |
| 8 | PX RECEIVE | | 520 | 48880 | 134K (1)| 00:00:03 | | | Q1,05 | PCWP | |
| 9 | PX SEND HASH | :TQ10003 | 520 | 48880 | 134K (1)| 00:00:03 | | | Q1,03 | P->P | HASH |
|* 10 | HASH JOIN OUTER BUFFERED | | 520 | 48880 | 134K (1)| 00:00:03 | | | Q1,03 | PCWP | |
| 11 | PX RECEIVE | | | | | | | | Q1,03 | PCWP | |
| 12 | PX SEND HASH | :TQ10001 | | | | | | | Q1,01 | P->P | HASH |
| 13 | NESTED LOOPS | | | | | | | | Q1,01 | PCWP | |
| 14 | NESTED LOOPS | | 276 | 13800 | 50303 (0)| 00:00:01 | | | Q1,01 | PCWP | |
| 15 | BUFFER SORT | | | | | | | | Q1,01 | PCWC | |
| 16 | PX RECEIVE | | | | | | | | Q1,01 | PCWP | |
| 17 | PX SEND BROADCAST | :TQ10000 | | | | | | | | S->P | BROADCAST |
| 18 | VIEW | | 1 | 6 | 3 (0)| 00:00:01 | | | | | |
|* 19 | CONNECT BY WITHOUT FILTERING| | | | | | | | | | |
| 20 | FAST DUAL | | 1 | | 3 (0)| 00:00:01 | | | | | |
| 21 | PX PARTITION RANGE ALL | | 3898K| | 1656 (0)| 00:00:01 | 1 | 75 | Q1,01 | PCWC | |
|* 22 | INDEX RANGE SCAN | FTR_DT_IX1 | 3898K| | 1656 (0)| 00:00:01 | 1 | 75 | Q1,01 | PCWP | |
|* 23 | TABLE ACCESS BY LOCAL INDEX ROWID| FTR_DT | 276 | 12144 | 50300 (0)| 00:00:01 | 1 | 1 | Q1,01 | PCWP | |
| 24 | PX RECEIVE | | 7344K| 308M| 83875 (1)| 00:00:02 | | | Q1,03 | PCWP | |
| 25 | PX SEND HASH | :TQ10002 | 7344K| 308M| 83875 (1)| 00:00:02 | | | Q1,02 | P->P | HASH |
| 26 | PX BLOCK ITERATOR | | 7344K| 308M| 83875 (1)| 00:00:02 | | | Q1,02 | PCWC | |
|* 27 | TABLE ACCESS STORAGE FULL | DTL_HIST_DT | 7344K| 308M| 83875 (1)| 00:00:02 | | | Q1,02 | PCWP | |
| 28 | PX RECEIVE | | 108K| 3376K| 275 (0)| 00:00:01 | | | Q1,05 | PCWP | |
| 29 | PX SEND HASH | :TQ10004 | 108K| 3376K| 275 (0)| 00:00:01 | | | Q1,04 | P->P | HASH |
| 30 | PX BLOCK ITERATOR | | 108K| 3376K| 275 (0)| 00:00:01 | | | Q1,04 | PCWC | |
|* 31 | TABLE ACCESS STORAGE FULL | POINT_T | 108K| 3376K| 275 (0)| 00:00:01 | | | Q1,04 | PCWP | |
Predicate Information (identified by operation id):
---------------------------------------------------
7 - access("C"."CODE"="AP"."CODE"(+) AND "C"."POINT_ID"="AP"."POINT_ID"(+))
10 - access("CF"."P_ID"="C"."WTN"(+))
19 - filter(LEVEL<=1)
22 - access("CF"."DATE"<=INTERNAL_FUNCTION("D"."MONTH_END"))
23 - filter(UPPER("CF"."PLAN") LIKE '%DV%' AND ("CF"."FT_CODE"='7370' OR "CF"."FT_CODE"='7371' OR "CF"."FT_CODE"='7372' OR
"CF"."FT_CODE"='7373' OR "CF"."FT_CODE"='7374' OR "CF"."FT_CODE"='7380' OR "CF"."FT_CODE"='7380C' OR "CF"."FT_CODE"='7381' OR
"CF"."FT_CODE"='7381C' OR "CF"."FT_CODE"='7382' OR "CF"."FT_CODE"='7382C' OR "CF"."FT_CODE"='7383' OR "CF"."FT_CODE"='7384' OR
"CF"."FT_CODE"='7409' OR "CF"."FT_CODE"='7409C' OR "CF"."FT_CODE"='7410' OR "CF"."FT_CODE"='7410C' OR "CF"."FT_CODE"='TRKDV') AND
"CF"."EXP_DATE">=INTERNAL_FUNCTION("D"."MONTH_END"))
27 - storage("C"."EXP_DATE"(+)>=TRUNC(SYSDATE#!,'fmmm')-1 AND "C"."DATE"(+)<=TRUNC(SYSDATE#!,'fmmm')-1)
filter("C"."EXP_DATE"(+)>=TRUNC(SYSDATE#!,'fmmm')-1 AND "C"."DATE"(+)<=TRUNC(SYSDATE#!,'fmmm')-1)
31 - storage("AP"."EXP_DATE"(+)>=TRUNC(SYSDATE#!,'fmmm')-1 AND "AP"."DATE"(+)<=TRUNC(SYSDATE#!,'fmmm')-1)
filter("AP"."EXP_DATE"(+)>=TRUNC(SYSDATE#!,'fmmm')-1 AND "AP"."DATE"(+)<=TRUNC(SYSDATE#!,'fmmm')-1)
Note
-----
- dynamic sampling used for this statement (level=6)
- automatic DOP: Computed Degree of Parallelism is 8
Edit to add explain plan for faster query:
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
---------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 278 | 33360 | 622K (1)| 00:00:11 | | | | | |
| 1 | PX COORDINATOR | | | | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10005 | 278 | 33360 | 622K (1)| 00:00:11 | | | Q1,05 | P->S | QC (RAND) |
| 3 | HASH GROUP BY | | 278 | 33360 | 622K (1)| 00:00:11 | | | Q1,05 | PCWP | |
| 4 | PX RECEIVE | | 4034 | 472K| 622K (1)| 00:00:11 | | | Q1,05 | PCWP | |
| 5 | PX SEND HASH | :TQ10004 | 4034 | 472K| 622K (1)| 00:00:11 | | | Q1,04 | P->P | HASH |
|* 6 | HASH JOIN OUTER BUFFERED | | 4034 | 472K| 622K (1)| 00:00:11 | | | Q1,04 | PCWP | |
| 7 | PX RECEIVE | | 4034 | 346K| 621K (1)| 00:00:11 | | | Q1,04 | PCWP | |
| 8 | PX SEND HASH | :TQ10002 | 4034 | 346K| 621K (1)| 00:00:11 | | | Q1,02 | P->P | HASH |
|* 9 | HASH JOIN OUTER BUFFERED | | 4034 | 346K| 621K (1)| 00:00:11 | | | Q1,02 | PCWP | |
| 10 | PX RECEIVE | | 2140 | 94160 | 538K (1)| 00:00:09 | | | Q1,02 | PCWP | |
| 11 | PX SEND HASH | :TQ10000 | 2140 | 94160 | 538K (1)| 00:00:09 | | | Q1,00 | P->P | HASH |
| 12 | PX BLOCK ITERATOR | | 2140 | 94160 | 538K (1)| 00:00:09 | 1 | 75 | Q1,00 | PCWC | |
|* 13 | TABLE ACCESS STORAGE FULL| FTR_DT | 2140 | 94160 | 538K (1)| 00:00:09 | 1 | 75 | Q1,00 | PCWP | |
| 14 | PX RECEIVE | | 7344K| 308M| 83875 (1)| 00:00:02 | | | Q1,02 | PCWP | |
| 15 | PX SEND HASH | :TQ10001 | 7344K| 308M| 83875 (1)| 00:00:02 | | | Q1,01 | P->P | HASH |
| 16 | PX BLOCK ITERATOR | | 7344K| 308M| 83875 (1)| 00:00:02 | | | Q1,01 | PCWC | |
|* 17 | TABLE ACCESS STORAGE FULL| DTL_HIST_DT | 7344K| 308M| 83875 (1)| 00:00:02 | | | Q1,01 | PCWP | |
| 18 | PX RECEIVE | | 108K| 3376K| 275 (0)| 00:00:01 | | | Q1,04 | PCWP | |
| 19 | PX SEND HASH | :TQ10003 | 108K| 3376K| 275 (0)| 00:00:01 | | | Q1,03 | P->P | HASH |
| 20 | PX BLOCK ITERATOR | | 108K| 3376K| 275 (0)| 00:00:01 | | | Q1,03 | PCWC | |
|* 21 | TABLE ACCESS STORAGE FULL | POINT_T | 108K| 3376K| 275 (0)| 00:00:01 | | | Q1,03 | PCWP | |
Predicate Information (identified by operation id):
---------------------------------------------------
6 - access("C"."CODE"="AP"."CODE"(+) AND "C"."POINT_ID"="AP"."POINT_ID"(+))
9 - access("CF"."P_ID"="C"."WTN"(+))
13 - storage(UPPER("CF"."PLAN") LIKE '%DV%' AND ("CF"."FT_CODE"='7370' OR "CF"."FT_CODE"='7371' OR "CF"."FT_CODE"='7372' OR
"CF"."FT_CODE"='7373' OR "CF"."FT_CODE"='7374' OR "CF"."FT_CODE"='7380' OR "CF"."FT_CODE"='7380C' OR "CF"."FT_CODE"='7381' OR
"CF"."FT_CODE"='7381C' OR "CF"."FT_CODE"='7382' OR "CF"."FT_CODE"='7382C' OR "CF"."FT_CODE"='7383' OR "CF"."FT_CODE"='7384'
OR "CF"."FT_CODE"='7409' OR "CF"."FT_CODE"='7409C' OR "CF"."FT_CODE"='7410' OR "CF"."FT_CODE"='7410C' OR
"CF"."FT_CODE"='TRKDV') AND "CF"."EXP_DATE">=TRUNC(SYSDATE#!,'fmmm')-1 AND "CF"."DATE"<=TRUNC(SYSDATE#!,'fmmm')-1)
filter(UPPER("CF"."PLAN") LIKE '%DV%' AND ("CF"."FT_CODE"='7370' OR "CF"."FT_CODE"='7371' OR "CF"."FT_CODE"='7372' OR
"CF"."FT_CODE"='7373' OR "CF"."FT_CODE"='7374' OR "CF"."FT_CODE"='7380' OR "CF"."FT_CODE"='7380C' OR "CF"."FT_CODE"='7381' OR
"CF"."FT_CODE"='7381C' OR "CF"."FT_CODE"='7382' OR "CF"."FT_CODE"='7382C' OR "CF"."FT_CODE"='7383' OR "CF"."FT_CODE"='7384'
OR "CF"."FT_CODE"='7409' OR "CF"."FT_CODE"='7409C' OR "CF"."FT_CODE"='7410' OR "CF"."FT_CODE"='7410C' OR
"CF"."FT_CODE"='TRKDV') AND "CF"."EXP_DATE">=TRUNC(SYSDATE#!,'fmmm')-1 AND "CF"."DATE"<=TRUNC(SYSDATE#!,'fmmm')-1)
17 - storage("C"."EXP_DATE"(+)>=TRUNC(SYSDATE#!,'fmmm')-1 AND "C"."DATE"(+)<=TRUNC(SYSDATE#!,'fmmm')-1)
filter("C"."EXP_DATE"(+)>=TRUNC(SYSDATE#!,'fmmm')-1 AND "C"."DATE"(+)<=TRUNC(SYSDATE#!,'fmmm')-1)
21 - storage("AP"."EXP_DATE"(+)>=TRUNC(SYSDATE#!,'fmmm')-1 AND "AP"."DATE"(+)<=TRUNC(SYSDATE#!,'fmmm')-1)
filter("AP"."EXP_DATE"(+)>=TRUNC(SYSDATE#!,'fmmm')-1 AND "AP"."DATE"(+)<=TRUNC(SYSDATE#!,'fmmm')-1)
Note
-----
- dynamic sampling used for this statement (level=6)
- automatic DOP: Computed Degree of Parallelism is 8
In first query Oracle is doing an index range scan on a large table and retrieving huge amounts of data on the first pass with access("CF"."DATE"<=INTERNAL_FUNCTION("D"."MONTH_END")). You can work around it by rewriting your select to be more like your fast one. It would be something like this -
select t.code, trunc(t.exp_date, 'MM') - 1 month_end, count(*)
from device t
where t.date < :report_period_end
and t.exp_date > :report_period_start
group by t.code, trunc(t.exp_date,'MM') - 1;
Also note that there are several potential problems with your original slow query:
A row where t.date and t.exp_date is more than 2 months apart will be counted multiple times.
Rows where t.date and t.exp_date has the same month will not be counted at all
You are comparing your dates to e.g. 2013.10.30 00:00:00. Results may be incorrect if your dates contain time data.