Partition pruning, Nested loops
Hi,
I am having a problem with getting partition pruning in a query. I've managed to dumb down the problem to two tables and a minimal query (see below).
Basically, I have a partitioned table "Yearly Facts", and a helper table "Current Year" that always contain 1 row. The sole purpose of this one row is to tell what the curent year is, what the previous year was and what the next year will be. (In the real problem, there are non-standard timeperiods, so one cannot just calculate previous and next by adding/subtracting 1 as would be possible in this example).
The following query is executed the way I want.
It performs a scan on current_year, and then nested loop into the facts. And partition pruning was happening.
select sum(decode(a.year_key, b.curr_year, some_measure)) as curr_year_measure
from yearly_fact_t a
,current_year b
where a.year_key = b.curr_year;
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
| 0 | SELECT STATEMENT | | 1 | 39 | 4 (0)| 00:00:01 | | |
| 1 | SORT AGGREGATE | | 1 | 39 | | | | |
| 2 | NESTED LOOPS | | 1 | 39 | 4 (0)| 00:00:01 | | |
| 3 | INDEX FAST FULL SCAN | SYS_C00247890 | 1 | 13 | 2 (0)| 00:00:01 | | |
| 4 | PARTITION RANGE ITERATOR| | 1 | 26 | 2 (0)| 00:00:01 | KEY | KEY |
|* 5 | TABLE ACCESS FULL | YEARLY_FACT_T | 1 | 26 | 2 (0)| 00:00:01 | KEY | KEY |
Predicate Information (identified by operation id):
5 - filter("A"."YEAR_KEY"="B"."CURR_YEAR")The following query is where I have my problem.
The basic xplan is the same, but for some reason the cbo gave up and decide to scan all partitions, which was not very scalable on production data :)
I would have thought that the plan would be the same.
select sum(decode(a.year_key, b.curr_year, some_measure)) as curr_year_measure
,sum(decode(a.year_key, b.prev_year, some_measure)) as prev_year_measure
from yearly_fact_t a
,current_year b
where a.year_key = b.curr_year
or a.year_key = b.prev_year;
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
| 0 | SELECT STATEMENT | | 1 | 52 | 13 (0)| 00:00:01 | | |
| 1 | SORT AGGREGATE | | 1 | 52 | | | | |
| 2 | NESTED LOOPS | | 4 | 208 | 13 (0)| 00:00:01 | | |
| 3 | TABLE ACCESS FULL | CURRENT_YEAR | 1 | 26 | 4 (0)| 00:00:01 | | |
| 4 | PARTITION RANGE ALL| | 4 | 104 | 9 (0)| 00:00:01 | 1 | 6 |
|* 5 | TABLE ACCESS FULL | YEARLY_FACT_T | 4 | 104 | 9 (0)| 00:00:01 | 1 | 6 |
Predicate Information (identified by operation id):
5 - filter("A"."YEAR_KEY"="B"."CURR_YEAR" OR "A"."YEAR_KEY"="B"."PREV_YEAR")
-- drop table yearly_fact_t purge;
-- drop table current_year purge;
create table current_year(
curr_year number(4) not null
,prev_year number(4) not null
,next_year number(4) not null
,primary key(curr_year)
,unique(prev_year)
,unique(next_year)
insert into current_year(curr_year, prev_year, next_year) values(2010, 2009, 2011);
commit;
create table yearly_fact_t(
year_key number(4) not null
,some_dim_key number not null
,some_measure number not null
partition by range(year_key)(
partition p2007 values less than(2008)
,partition p2008 values less than(2009)
,partition p2009 values less than(2010)
,partition p2010 values less than(2011)
,partition p2011 values less than(2012)
,partition pmax values less than(maxvalue)
insert into yearly_fact_t(year_key, some_dim_key, some_measure) values(2007,1, 10);
insert into yearly_fact_t(year_key, some_dim_key, some_measure) values(2008,1, 20);
insert into yearly_fact_t(year_key, some_dim_key, some_measure) values(2009,1, 30);
insert into yearly_fact_t(year_key, some_dim_key, some_measure) values(2010,1, 40);
commit; What can I do to get partition pruning to happen in the second query?
Or even better, what is it in my query that prevents it from happening?
We're running Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit.
Best regards
Ronnie
Hi Ronnie,
Check whether your statistics are up to date.
SQL> set autotrace traceonly exp
SQL> select sum(decode(a.year_key, b.curr_year, some_measure)) as curr_year_measure
2 ,sum(decode(a.year_key, b.prev_year, some_measure)) as prev_year_measure
3 from yearly_fact_t a
4 ,current_year b
5 where a.year_key = b.curr_year
6 or a.year_key = b.prev_year;
Execution Plan
Plan hash value: 229094315
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
| 0 | SELECT STATEMENT | | 1 | 52 | 10 (0)| 00:00:01 | | |
| 1 | SORT AGGREGATE | | 1 | 52 | | | | |
| 2 | NESTED LOOPS | | 4 | 208 | 10 (0)| 00:00:01 | | |
| 3 | TABLE ACCESS FULL | CURRENT_YEAR | 1 | 26 | 3 (0)| 00:00:01 | | |
| 4 | PARTITION RANGE ALL| | 4 | 104 | 7 (0)| 00:00:01 | 1 | 6 |
|* 5 | TABLE ACCESS FULL | YEARLY_FACT_T | 4 | 104 | 7 (0)| 00:00:01 | 1 | 6 |
Predicate Information (identified by operation id):
5 - filter("A"."YEAR_KEY"="B"."CURR_YEAR" OR "A"."YEAR_KEY"="B"."PREV_YEAR")
Note
- dynamic sampling used for this statement
SQL> set autotrace off
SQL> exec dbms_stats.gather_table_stats(user, 'current_year', estimate_percent=>100, cascade=>true, method_opt
=>'for all columns size 1');
PL/SQL procedure successfully completed.
SQL>
SQL> exec dbms_stats.gather_table_stats(user, 'yearly_fact_t', estimate_percent=>100, cascade=>true, method_op
t=>'for all columns size 1');
PL/SQL procedure successfully completed.
SQL> set autotrace traceonly exp
SQL> select sum(decode(a.year_key, b.curr_year, some_measure)) as curr_year_measure
2 ,sum(decode(a.year_key, b.prev_year, some_measure)) as prev_year_measure
3 from yearly_fact_t a
4 ,current_year b
5 where a.year_key = b.curr_year
6 or a.year_key = b.prev_year;
Execution Plan
Plan hash value: 2253546831
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
| 0 | SELECT STATEMENT | | 1 | 15 | 8 (0)| 00:00:01 | | |
| 1 | SORT AGGREGATE | | 1 | 15 | | | | |
| 2 | CONCATENATION | | | | | | | |
| 3 | NESTED LOOPS | | 1 | 15 | 4 (0)| 00:00:01 | | |
| 4 | TABLE ACCESS FULL | CURRENT_YEAR | 1 | 8 | 3 (0)| 00:00:01 | | |
| 5 | PARTITION RANGE ITERATOR| | 1 | 7 | 1 (0)| 00:00:01 | KEY | KEY |
|* 6 | TABLE ACCESS FULL | YEARLY_FACT_T | 1 | 7 | 1 (0)| 00:00:01 | KEY | KEY |
| 7 | NESTED LOOPS | | 1 | 15 | 4 (0)| 00:00:01 | | |
| 8 | TABLE ACCESS FULL | CURRENT_YEAR | 1 | 8 | 3 (0)| 00:00:01 | | |
| 9 | PARTITION RANGE ITERATOR| | 1 | 7 | 1 (0)| 00:00:01 | KEY | KEY |
|* 10 | TABLE ACCESS FULL | YEARLY_FACT_T | 1 | 7 | 1 (0)| 00:00:01 | KEY | KEY |
Predicate Information (identified by operation id):
6 - filter("A"."YEAR_KEY"="B"."PREV_YEAR")
10 - filter("A"."YEAR_KEY"="B"."CURR_YEAR" AND LNNVL("A"."YEAR_KEY"="B"."PREV_YEAR"))
SQL> set autotrace off
SQL>Asif Momen
http://momendba.blogspot.com
Similar Messages
-
Reference partitioning and partition pruning
Hi All,
I am on v 11.2.0.3.
I have a pair of typical parent-child tables. Child table is growing like hell, hence we want to partition it, which will be used for deleting/dropping old data later on.
There is no partitioning key in the child table which I can use for relating the data to the time when data was created. So, I thought I can use the timestamp from parent table for partitioning the parent table and reference partition the child table.
I am more concerned about the child table (or the queries running on the child table) in terms of performance. ITEM_LIST_ID from the child table is extensively used in queries to access data from child table.
How will partition pruning work when the child table is queried on the foreign key? will it every time go to the parent table, find out the partition and then resolve the partition for child table?
The setup is given in the scripts below, will it cause lot of locking (to resolve partitions)? or am I worrying for nothing?
Here are the scripts
CREATE TABLE ITEM_LISTS /* Parent table, tens of thousands of records */
ITEM_LIST_ID NUMBER(10) NOT NULL PRIMARY KEY, /* Global index on partitioned table !!! */
LIST_NAME VARCHAR2(500) NOT NULL,
FIRST_INSERTED TIMESTAMP(6) NOT NULL
PARTITION BY RANGE ( FIRST_INSERTED )
partition p0 values less than ( to_date('20130101','YYYYMMDD') ),
partition p201301 values less than ( to_date('20130201','YYYYMMDD') ),
partition p201302 values less than ( to_date('20130301','YYYYMMDD') ),
partition p201303 values less than ( to_date('20130401','YYYYMMDD') ),
partition p201304 values less than ( to_date('20130501','YYYYMMDD') ),
partition p201305 values less than ( to_date('20130601','YYYYMMDD') )
CREATE INDEX ITEM_LISTS_IDX1 ON ITEM_LISTS ( LIST_NAME ) LOCAL ;
CREATE TABLE ITEM_LIST_DETAILS /* Child table, millions of records */
ITEM_ID NUMBER(10) NOT NULL,
ITEM_LIST_ID NUMBER(10) NOT NULL, /* Always used in WHERE clause by lots of big queries */
CODE VARCHAR2(30) NOT NULL,
ALT_CODE VARCHAR2(30) NOT NULL,
CONSTRAINT ITEM_LIST_DETAILS_FK
FOREIGN KEY ( ITEM_LIST_ID ) REFERENCES ITEM_LISTS
PARTITION BY REFERENCE ( ITEM_LIST_DETAILS_FK )
CREATE INDEX ITEM_LIST_DETAILS_IDX1 ON ITEM_LIST_DETAILS (ITEM_ID) LOCAL;
CREATE INDEX ITEM_LIST_DETAILS_IDX2 ON ITEM_LIST_DETAILS (ITEM_LIST_ID, CODE) LOCAL;Any thoughts / opinions / corrections ?
Thanks in advanceTo check how partition pruning works here, I inserted some data in these tables. Inserted data in ITEM_LISTS (parent) from DBA_OBJECTS ( object_id => item_list_id, object_name => list_name, first_inserted => manually created). Also created corresponding child data in ITEM_LIST_DETAILS, so that, for every item_list_id in parent about 5000 records go in child table.
Looking at the queries and plan below, my question is, what exactly does the operations "PARTITION REFERENCE SINGLE" and "PARTITION REFERENCE ITERATOR" imply ??
I gave a search on "PARTITION REFERENCE ITERATOR" in Oracle 11.2 documentation, it says "No exact match found" !!
/* Direct query on child table */
SQL> select count(*) from item_list_details where item_list_id = 6323 ;
COUNT(*)
5000
1 row selected.
Execution Plan
Plan hash value: 2798904155
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
| 0 | SELECT STATEMENT | | 1 | 5 | 22 (0)| 00:00:01 | | |
| 1 | SORT AGGREGATE | | 1 | 5 | | | | |
| 2 | PARTITION REFERENCE SINGLE| | 5000 | 25000 | 22 (0)| 00:00:01 | KEY | KEY |
|* 3 | INDEX RANGE SCAN | ITEM_LIST_DETAILS_IDX2 | 5000 | 25000 | 22 (0)| 00:00:01 | KEY | KEY |
Predicate Information (identified by operation id):
3 - access("ITEM_LIST_ID"=6323)
SQL> select * from temp1; /* Dummy table to try out some joins */
OBJECT_ID OBJECT_NAME
6598 WRH$_INTERCONNECT_PINGS
1 row selected.
/* Query on child table, joining with some other table */
SQL> select count(*)
2 from temp1 d, ITEM_LIST_DETAILS i1
3 where d.object_id = i1.item_list_id
4 and d.object_name = 'WRH$_INTERCONNECT_PINGS';
COUNT(*)
5000
1 row selected.
Execution Plan
Plan hash value: 2288153583
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
| 0 | SELECT STATEMENT | | 1 | 70 | 24 (0)| 00:00:01 | | |
| 1 | SORT AGGREGATE | | 1 | 70 | | | | |
| 2 | NESTED LOOPS | | 5000 | 341K| 24 (0)| 00:00:01 | | |
|* 3 | TABLE ACCESS FULL | TEMP1 | 1 | 65 | 3 (0)| 00:00:01 | | |
| 4 | PARTITION REFERENCE ITERATOR| | 5000 | 25000 | 21 (0)| 00:00:01 | KEY | KEY |
|* 5 | INDEX RANGE SCAN | ITEM_LIST_DETAILS_IDX2 | 5000 | 25000 | 21 (0)| 00:00:01 | KEY | KEY |
Predicate Information (identified by operation id):
3 - filter("D"."OBJECT_NAME"='WRH$_INTERCONNECT_PINGS')
5 - access("D"."OBJECT_ID"="I1"."ITEM_LIST_ID") -
Partition Pruning Aint Happening
I have a situation where Oracle pruning is not happening and this situation is getting worse when the search criteria is close to current date.
Table has a date based partition. SQL Statement has 2 partitioned tables. Date range is applied on a relatively smaller daily partitioned table and further joined to the next table (Its also date based partition). First table has partition column in the where clause and it joins to the next table using the partitioned column.
SQL does partition pruning even for a month range search if the date range is in August. It will do partition pruning only for 25 days for september where clause. Likewise the where clause date range ability for partition pruning goes down and It will only do partition pruning for where clause with 5 days. If the where clause has more than 6 days (Mar 01 Thru Mar 06) in March 2009, Oracle does All partitions.
Query is examined and it contains partition column in the where clause and we have also gathered 100% statistics of the table which is not partition pruning. Data growth is happening relatively each month.
Sample: -
Select 1
From TAB1, TAB2
Where TAB1.SELECT_DATE >= '01JAN2009
And TAB1.SELECT_DATE <= '31JAN2009
And TAB2.DATE_SELECT = TAB1.DATE_SELECT
TAB1 Is a daily partition with SELECT_DATE column. Oracle is able to successfully do partition pruning on this table. But TAB2 partition pruning is not happening.
How I can overcome this situation? Thanks for your help.
Here is the explain plan for the SQL statement| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
| 0 | SELECT STATEMENT | | 1224 | 96696 | | 978K (4)| 01:21:35 | | |
| 1 | HASH GROUP BY | | 1224 | 96696 | | 978K (4)| 01:21:35 | | |
|* 2 | HASH JOIN | | 1122K| 84M| 49M| 978K (4)| 01:21:34 | | |
|* 3 | HASH JOIN | | 1122K| 36M| | 5218 (4)| 00:00:27 | | |
| 4 | COLLECTION ITERATOR PICKLER FETCH| TABLE FUNC | | | | | | | |
| 5 | PARTITION RANGE ITERATOR | | 729K| 22M| | 5145 (3)| 00:00:26 | 413 | 422 |
| 6 | PARTITION LIST ALL | | 729K| 22M| | 5145 (3)| 00:00:26 | 1 | 2 |
|* 7 | MAT_VIEW ACCESS FULL | TAB_01 | 729K| 22M| | 5145 (3)| 00:00:26 | 825 | 844 |
| 8 | PARTITION RANGE ALL | | 84M| 3623M| | 575K (5)| 00:47:59 | 1 | 970 |
| 9 | TABLE ACCESS FULL | TAB_02 | 84M| 3623M| | 575K (5)| 00:47:59 | 1 | 970 |
Predicate Information (identified by operation id):
2 - access("TAB_02"."SELECT_DATE"="TAB_01"."SELECT_DATE" AND "TAB_02"."SELECT_NUMBER"="TAB_01"."SELECT_NUMBER")
3 - access("TAB_01"."TAG_ID"=SYS_OP_ATG(VALUE(KOKBF$),16,17,2))
7 - filter("TAB_01"."SELECT_DATE"<=TO_DATE('2009-02-10 00:00:00', 'yyyy-mm-dd hh24:mi:ss'))Following is the explain paln where partition elimination happening for a shorter date range search
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
| 0 | SELECT STATEMENT | | 714 | 56406 | 851K (1)| 01:10:56 | | |
| 1 | HASH GROUP BY | | 714 | 56406 | 851K (1)| 01:10:56 | | |
| 2 | NESTED LOOPS | | 621K| 46M| 850K (1)| 01:10:55 | | |
|* 3 | HASH JOIN | | 621K| 20M| 3210 (3)| 00:00:17 | | |
| 4 | COLLECTION ITERATOR PICKLER FETCH| TABLE FUNC | | | | | | |
| 5 | PARTITION RANGE ITERATOR | | 403K| 12M| 3152 (3)| 00:00:16 | 413 | 418 |
| 6 | PARTITION LIST ALL | | 403K| 12M| 3152 (3)| 00:00:16 | 1 | 2 |
|* 7 | MAT_VIEW ACCESS FULL | TAB_01 | 403K| 12M| 3152 (3)| 00:00:16 | 825 | 836 |
| 8 | PARTITION RANGE ITERATOR | | 1 | 45 | 2 (0)| 00:00:01 | KEY | KEY |
| 9 | TABLE ACCESS BY LOCAL INDEX ROWID| TAB_02 | 1 | 45 | 2 (0)| 00:00:01 | KEY | KEY |
|* 10 | INDEX RANGE SCAN | TAB_02_PK | 1 | | 1 (0)| 00:00:01 | KEY | KEY |
Predicate Information (identified by operation id):
3 - access("TAB_01"."TAG_ID"=SYS_OP_ATG(VALUE(KOKBF$),16,17,2))
7 - filter("TAB_01"."SELECT_DATE"<=TO_DATE('2009-02-06 00:00:00', 'yyyy-mm-dd hh24:mi:ss'))
10 - access("TAB_02"."SELECT_DATE"="TAB_01"."SELECT_DATE" AND "TAB_02"."SELECT_NUMBER"="TAB_01"."SELECT_NUMBER")Query where partition elimination not happening on the second table is having date range of 7 days or more. SO the first explain plan is for Feb-01 Thru Feb 10 search
Query where partition elimination happening on the second table is having date range of 7 days or less. The second explain plan is for Feb-01 Thru Feb-06 search
This is great.
Edited by: user10929035 on Mar 24, 2009 8:18 AM -
We have tables that are interval range partitioned on a DATE column, with a partition for each day - all very standard and straight out of Oracle doc.
A 3rd party application queries the tables to find number of rows based on date range that is on the column used for the partition key.
This application uses date range specified relative to current date - i.e. for last two days would be "..startdate > SYSDATE -2 " - but partition pruning does not take place and the explain plan shows that every partition is included.
By presenting the query using the date in a variable partition pruning does table place, and query obviously performs much better.
DB is 11.2.0.3 on RHEL6, and default parameters set - i.e. nothing changed that would influence optimizer behavior to something unusual.
I can't work out why this would be so. It very easy to reproduce with simple test case below.
I'd be very interested to hear any thoughts on why it is this way and whether anything can be done to permit the partition pruning to work with a query including SYSDATE as it would be difficult to get the application code changed.
Furthermore to make a case to change the code I would need an explanation of why querying using SYSDATE is not good practice, and I don't know of any such information.
1) Create simple partitioned table
CREATETABLE part_test
(id NUMBER NOT NULL,
starttime DATE NOT NULL,
CONSTRAINT pk_part_test PRIMARY KEY (id))
PARTITION BY RANGE (starttime) INTERVAL (NUMTODSINTERVAL(1,'day')) (PARTITION p0 VALUES LESS THAN (TO_DATE('01-01-2013','DD-MM-YYYY')));
2) Populate table 1million rows spread between 10 partitions
BEGIN
FOR i IN 1..1000000
LOOP
INSERT INTO part_test (id, starttime) VALUES (i, SYSDATE - DBMS_RANDOM.value(low => 1, high => 10));
END LOOP;
END;
EXEC dbms_stats.gather_table_stats('SUPER_CONF','PART_TEST');
3) Query the Table for data from last 2 days using SYSDATE in clause
EXPLAIN PLAN FOR
SELECT count(*)
FROM part_test
WHERE starttime >= SYSDATE - 2;
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
| 0 | SELECT STATEMENT | | 1 | 8 | 7895 (1)| 00:00:01 | | |
| 1 | SORT AGGREGATE | | 1 | 8 | | | | |
| 2 | PARTITION RANGE ITERATOR| | 111K| 867K| 7895 (1)| 00:00:01 | KEY |1048575|
|* 3 | TABLE ACCESS FULL | PART_TEST | 111K| 867K| 7895 (1)| 00:00:01 | KEY |1048575|
Predicate Information (identified by operation id):
3 - filter("STARTTIME">=SYSDATE@!-2)
4) Now do the same query but with SYSDATE - 2 presented as a literal value.
This query returns the same answer but very different cost.
EXPLAIN PLAN FOR
SELECT count(*)
FROM part_test
WHERE starttime >= (to_date('23122013:0950','DDMMYYYY:HH24MI'))-2;
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
| 0 | SELECT STATEMENT | | 1 | 8 | 131 (0)| 00:00:01 | | |
| 1 | SORT AGGREGATE | | 1 | 8 | | | | |
| 2 | PARTITION RANGE ITERATOR| | 111K| 867K| 131 (0)| 00:00:01 | 356 |1048575|
|* 3 | TABLE ACCESS FULL | PART_TEST | 111K| 867K| 131 (0)| 00:00:01 | 356 |1048575|
Predicate Information (identified by operation id):
3 - filter("STARTTIME">=TO_DATE(' 2013-12-21 09:50:00', 'syyyy-mm-dd hh24:mi:ss'))
thanks in anticipation
JimAs Jonathan has already pointed out there are situations where the CBO knows that partition pruning will occur but is unable to identify those partitions at parse time. The CBO will then use a dynamic pruning which means determine the partitions to eliminate dynamically at run time. This is why you see the KEY information instead of a known partition number. This is to occur mainly when you compare a function to your partition key i.e. where partition_key = function. And SYSDATE is a function. For the other bizarre PSTOP number (1048575) see this blog
http://hourim.wordpress.com/2013/11/08/interval-partitioning-and-pstop-in-execution-plan/
Best regards
Mohamed Houri -
Partition Pruning - Dimension and FACT tables..
Hi
I have a DWH environment where we have partitioned the FACT table by a date column. This is RANGE partition. The TIME dimension table joins to the FACT table based on this date. However the end user queries will typically be fired using a different column in the time dimension that will hold more VIEWABLE date values (e.g.) in format MON-YYYY or YYYY-MM etc..
The query is autogenerated by the viewer tool. The SQL has something like
select sum(balance), MONTH from fact a, dim_time b
where a.date = b.date and <-- this the partitioned key in fact
b.month_year_col = 'Apr-2006' <-- Dimension filter.
In the above case, Oracle is not doing PARTITION PRUNING. I have 24 period data and in the explain plan i can see it goes to the entire 24 periods. However if i change the query to
select sum(balance), MONTH from fact a, dim_time b
where a.date = b.date and <-- this the partitioned key in fact
b.date = '31-Apr-2006' <-- Dimension filter.
it does partition pruning. The explain plan shows that i goes to only one partition.
Any help on this please. I would need the first query to use PARTITION PRUNING.
Thanks
balaHi All
Got it to work with these 3 parameters
alter system set "_subquery_pruning_enabled" = true
alter session set "_subquery_pruning_cost_factor"=1;
alter session set "_subquery_pruning_reduction"=100;
Thanks for all those who had a look into my question.
Regards
bala -
Building Tree hierarchy Using nested loops and class cl_gui_column_tree
Hello gurus,
I want to create a tree report using custom container and class cl_gui_column_tree. I have read and understood the standard demo report which SAP has provided i.e. SAPCOLUMN_TREE_CONTROL_DEMO. But in this report all the levels nodes are created as constants and hardcoded. I want to create hierarchy using nested loops. For this i took one example of a hierarchy of VBAK-VBELN->VBAP-POSNR Like One sales order has many line items and each line item can have number of line items in billing plan.
I have done some coding for it.
FORM build_tree USING node_table TYPE treev_ntab
item_table TYPE zitem_table. " i created the zitem_table table type of mtreeitm in SE11.
DATA: node TYPE treev_node,
item TYPE mtreeitm.
node-node_key = root.
CLEAR node-relatkey.
CLEAR node-relatship.
node-hidden = ' '.
node-disabled = ' '.
CLEAR node-n_image.
CLEAR node-exp_image.
node-isfolder = 'X'.
node-expander = 'X'.
APPEND node TO node_table.
item-node_key = root.
item-item_name = colm1.
item-class = cl_gui_column_tree=>item_class_text.
item-text = 'Root'.
APPEND item TO item_table.
item-node_key = root.
item-item_name = colm2.
item-class = cl_gui_column_tree=>item_class_text.
item-text = 'Amount'.
APPEND item TO item_table.
LOOP AT it_vbeln INTO wa_vbeln.
node-node_key = wa_vbeln-vbeln.
node-relatkey = root.
node-relatship = cl_gui_column_tree=>relat_last_child.
node-hidden = ' '.
node-disabled = ' '.
CLEAR node-n_image.
CLEAR node-exp_image.
node-isfolder = 'X'.
node-expander = 'X'.
APPEND node TO node_table.
item-node_key = wa_vbeln-vbeln.
item-item_name = colm1.
item-class = cl_gui_column_tree=>item_class_text.
item-text = wa_vbeln-vbeln.
APPEND item TO item_table.
item-node_key = wa_vbeln-vbeln.
item-item_name = colm2.
item-class = cl_gui_column_tree=>item_class_text.
item-text = wa_vbeln-netwr.
APPEND item TO item_table.
LOOP AT it_posnr INTO wa_posnr.
node-node_key = wa_posnr-posnr.
node-relatkey = wa_vbeln-vbeln.
node-relatship = cl_gui_column_tree=>relat_last_child.
node-hidden = ' '.
node-disabled = ' '.
CLEAR node-n_image.
CLEAR node-exp_image.
node-isfolder = ' '.
node-expander = ' '.
APPEND node TO node_table.
item-node_key = wa_posnr-posnr.
item-item_name = colm1.
item-class = cl_gui_column_tree=>item_class_text.
item-text = wa_posnr-posnr.
APPEND item TO item_table.
item-node_key = wa_posnr-posnr.
item-item_name = colm2.
item-class = cl_gui_column_tree=>item_class_text.
item-text = wa_posnr-netpr.
APPEND item TO item_table.
ENDLOOP.
ENDLOOP.
ENDFORM.
Now this program compiles fine and runs till there is only one level. That is root->vbeln. But when i add one more loop of it_posnr it gives me runtime error of message type 'X'. The problem i found was uniqueness of item-item_name as all the sales order have posnr = 0010. What could be done? I tried giving item_name unique hierarchy level using counters just like stufe field in prps eg. 10.10.10, 10.10.20,10.20.10,10.20.20,20.10.10 etc.. etc.. but still i am getting runtime error when i add one more hierarchy using nested loop. Plz guide.
Edited by: Yayati6260 on Jul 14, 2011 7:25 AMHello all,
Thanks the issue is solved. The node key was not getting a unique identification as nodekey. I resolved the issue by generating unique identification for each level. Thanks all,
Regards
Yayati Ekbote -
Problem with Nested loop in Fox-Formula
Dear Experts,
Let s share the scenario :
MaterialGroups with following Keys for Example AAAA, BBBB, CCCC..., Materialgroups are selected in the 1st input ready query, which is assigned to DataProvider DP_1 in a webtemplate.
every Materialgroup has several Materials, for instance:
Materialgroup AAAA has following Materials: AAAA10, AAAA11, AAAA12, AAAA13...
Materials are selected in a second input ready Query, which is assigned to a second DataProvider DP_2 in the Same Webtemplate as the query 1.
Both Materialgroup and Material are based on the same Aggreagtion level and same real time cube.
I want to copy the input values for every MaterialGroup ( 1st query, DP_1) only to it s own Materials (2cond Query, DP_2).
To resolve this Issue i wrote the following Fox Formula code with a nested loop, however it does not work properly. when I m debugging the code, i could release that the second Loop was ignored.but wehn i replace the second loop (nested loop) with a fixed value it s seems to work properly
DATA MG1 TYPE MATG.<------ MaterialGroup
DATA MT1 TYPE MAT.<----
Material
DATA S1 TYPE STRING.
DATA S2 TYPE STRING.
DATA S3 TYPE STRING
DATA K TYPE F.
DATA Z TYPE F.
FOREACH MG1.
To check Materialgroup in debugger
S1= MG1.
BREAK-POINT.
K = {KEYfIG, #, MG1}.
BREAK-POINT.
FOREACH MT1. <----- if i set MT1 to a fixed value like AAAA11 then S3 get the wished value namely AAAA
S2 = MT1.
BREAK-POINT.
S3 = SUBSTR (MT1, 0, 4).
BREAK-POINT.
IF S1 = S3.
{KEYFIG, MT1, #} = K.
following Statement is only used To check in debugger if Material has become the same Materialgroup value
Z = {KEYFIG, MT1, #}.
BREAK-POINT.
ENDIF.
ENDFOR.
ENDFOR.
Thakns for any help
Frank
Edited by: FRYYYBM on Mar 17, 2011 10:54 PM
Edited by: FRYYYBM on Mar 17, 2011 11:06 PMHi,
Please try this way.
DATA MG1 TYPE MATG.<------ MaterialGroup
DATA MT1 TYPE MAT.<----
Material
DATA S1 TYPE STRING.
DATA S2 TYPE STRING.
DATA S3 TYPE STRING
DATA K TYPE F.
DATA Z TYPE F.
FOREACH MT1.
FOREACH MG1.
To check Materialgroup in debugger
S1= MG1.
BREAK-POINT.
K = {KEYfIG, #, MG1}.
BREAK-POINT.
FOREACH MT1. <----- if i set MT1 to a fixed value like AAAA11 then S3 get the wished value namely AAAA
S2 = MT1.
BREAK-POINT.
S3 = SUBSTR (MT1, 0, 4).
BREAK-POINT.
IF S1 = S3.
{KEYFIG, MT1, #} = K.
following Statement is only used To check in debugger if Material has become the same Materialgroup value
Z = {KEYFIG, MT1, #}.
BREAK-POINT.
ENDIF.
ENDFOR.
ENDFOR.
ENDFOR.
Thanks.
With regards,
Anand Kumar -
Hi!
I would like to list all Combinations of a list A and a list B (each containing 5 integers) in a third list C in order to get 25 combinations. I am intending to use nested loops so that on the first loop the second loop starts and then the result lets say list A + list B are listed in the list C. Is there any other way of doing this with Labview or I am on the right track?
Best Regards,
HamidYou can use the polymorphic array math to accomplish this. Inside of a single for loop, index Array A and add that to the entire Array B in one shot. At this point, there are numerous ways to take the output. Probably the simpliest is to reshape the output 2D array into a 1D array. For better memory allocation, you can preinitialize a 1D array and replace elements in the for loop but in the end it accomplishes the same task. See attached example (LV7.1.1). Of course everything is dependant on what you are going to do with this, what operations, when and where the numbers come from.
Paul
Paul <--Always Learning!!!
sense and simplicity.
Browse my sample VIs?
Attachments:
arrayMath.vi 33 KB -
hi all.
I´ve just completed a little test for making a tree
component with custom
icons / bransch.
However, i can only get my first branch to show custom
icons.(links -
document) I figure I need to make a nested loop to Iterate
over
nextSibling?. I tried ALOT, but i guess I´m doing
something completely
wrong.
here is code for my tree so far:
my_xml = new XML();
my_xml.ignoreWhite = true;
my_xml.load("tree.xml");
my_xml.onLoad = function(){
myTree.dataProvider = this.firstChild;
var folders = my_xml.firstChild.firstChild;
var docs = folders.childNodes;
for (var i=0; i < docs.length; i++){
currDoc = docs
trace(docs);
var docIcon = currDoc.attributes.pic;
switch(docIcon){
case "pdf":
myTree.setIcon(currDoc, "pdfIcon");
break;
case "word":
myTree.setIcon(currDoc, "wordIcon");
break;
case "excel":
myTree.setIcon(currDoc, "excelIcon");
break;
case "ie":
myTree.setIcon(currDoc, "ieIcon");
break;
}//switch
} //for
};//onLoad
And here is the XML I used:
<node label="» Dokument typer">
<node label="» links - document">
<node label="test.url" url="
http://www." pic="ie" info="test text" />
<node label="test.doc" url="test.doc" pic="word"
info="test text" />
<node label="test.excel" url="test.xls" pic="excel"
info="test text" />
<node label="test.pdf" url="test.pdf" pic="pdf"
info="test text." />
</node>
<node label="» Links - document">
<node label="test URL" url="
http://www." pic="ie" info="test text."
/>
<node label="test URL" url="
http://www." pic="ie" info="test text."
/>
</node>
</node>Solved it ..works nicely :D
ty anyways.
//cleaner
"cLeAnEr" <[email protected]> skrev i meddelandet
news:ekm1vc$r8h$[email protected]..
> hi all.
>
> I´ve just completed a little test for making a tree
component with custom
> icons / bransch.
> However, i can only get my first branch to show custom
icons.(links -
> document) I figure I need to make a nested loop to
Iterate over
> nextSibling?. I tried ALOT, but i guess I´m doing
something completely
> wrong.
>
>
> here is code for my tree so far:
>
> my_xml = new XML();
> my_xml.ignoreWhite = true;
> my_xml.load("tree.xml");
>
> my_xml.onLoad = function(){
> myTree.dataProvider = this.firstChild;
>
>
> var folders = my_xml.firstChild.firstChild;
> var docs = folders.childNodes;
>
> for (var i=0; i < docs.length; i++){
> currDoc = docs
> trace(docs);
>
> var docIcon = currDoc.attributes.pic;
>
> switch(docIcon){
> case "pdf":
> myTree.setIcon(currDoc, "pdfIcon");
> break;
> case "word":
> myTree.setIcon(currDoc, "wordIcon");
> break;
> case "excel":
> myTree.setIcon(currDoc, "excelIcon");
> break;
> case "ie":
> myTree.setIcon(currDoc, "ieIcon");
> break;
> }//switch
> } //for
> };//onLoad
>
>
> And here is the XML I used:
>
> <node label="» Dokument typer">
> <node label="» links - document">
> <node label="test.url" url="
http://www." pic="ie" info="test text" />
> <node label="test.doc" url="test.doc" pic="word"
info="test text" />
> <node label="test.excel" url="test.xls" pic="excel"
info="test text" />
> <node label="test.pdf" url="test.pdf" pic="pdf"
info="test text." />
> </node>
> <node label="» Links - document">
> <node label="test URL" url="
http://www." pic="ie" info="test text."
/>
> <node label="test URL" url="
http://www." pic="ie" info="test text."
/>
> </node>
> </node>
> -
Too many nested loops in execution plan?
Hi,
i wonder about execution plan not indicating that access to some tables (for join) is in parallel.
Please see this example:
------------------------ snip ------------------------------------
drop table test_a1;
drop table test_a2;
drop table test_b;
drop table test_c;
drop table test_d;
create table test_a1 (
x number,
y number,
z number);
create unique index testa1_pk on test_a1 (x);
create table test_a2 (
x number,
y number,
z number);
create unique index testa2_pk on test_a2 (x);
create table test_b (
x number,
y number,
z number);
create unique index testb_pk on test_b (y);
create table test_c (
x number,
y number,
z number);
create unique index testc_pk on test_b (z);
create table test_d (
x number,
y number,
z number);
create unique index testd_pk on test_d (y);
select
a1.x a1_x,
a1.y a1_y,
a1.z a1_z,
a2.x a2_x,
a2.y a2_y,
a2.z a2_z,
b.x b_x,
b.y b_y,
b.z b_z,
c.x c_x,
c.y c_y,
c.z c_z,
d.x d_x,
d.y d_y,
d.z d_z
from test_a1 a1, test_a2 a2, test_b b, test_c c, test_d d
where a1.x = 100
and a2.x = 200
and b.y = a1.y
and c.z = b.z
and d.y = a1.y;
------------------------ snap ------------------------------------
The execution plan goes like this:
Select Stmt
nested loops
nested loops
nested loops
nested loops
table access
index
access predicate
a2.x = 200
table access
index
access predicate
a1.x = 100
table access
index
access predicate
d.y = a1.y
table access
index
access predicate
b.y = a1.y
table acess
index
acess predicate
c.z = b.z
Access to tables a1 and a2 is on the same level (in parallel - i guess).
However, why isn't access to table d and b on the same level?
Both depend on a1. So no need to execute one after the other (no inter-dependency).
Maybe i have just wrong expectation to the output of the execution plan(?!)
- many thanks!
best regards,
FrankPreservation of identation and spacing is invaluable when it comes to reading an explain plan.
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 195 | 2 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 195 | 2 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 1 | 156 | 0 (0)| 00:00:01 |
| 3 | NESTED LOOPS | | 1 | 117 | 0 (0)| 00:00:01 |
| 4 | NESTED LOOPS | | 1 | 78 | 0 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| TEST_A2 | 1 | 39 | 0 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | TESTA2_PK | 1 | | 0 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| TEST_A1 | 1 | 39 | 0 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | TESTA1_PK | 1 | | 0 (0)| 00:00:01 |
| 9 | TABLE ACCESS BY INDEX ROWID | TEST_D | 82 | 3198 | 0 (0)| 00:00:01 |
|* 10 | INDEX UNIQUE SCAN | TESTD_PK | 1 | | 0 (0)| 00:00:01 |
| 11 | TABLE ACCESS BY INDEX ROWID | TEST_B | 82 | 3198 | 0 (0)| 00:00:01 |
|* 12 | INDEX UNIQUE SCAN | TESTB_PK | 1 | | 0 (0)| 00:00:01 |
|* 13 | TABLE ACCESS FULL | TEST_C | 1 | 39 | 2 (0)| 00:00:01 |
Predicate Information (identified by operation id):
6 - access("A2"."X"=200)
8 - access("A1"."X"=100)
10 - access("D"."Y"="A1"."Y")
12 - access("B"."Y"="A1"."Y")
13 - filter("C"."Z"="B"."Z")
Access to tables a1 and a2 is on the same level (in parallel - i guess).
Maybe i have just wrong expectation to the output of the execution plan(?!)You guess wrong, there's nothing parallel going on here.
Execution plan is a tree of parent-child operations.
For example, the NESTED LOOP at operation 4 has two children @ 5 and 7.
Both of these operations- 5 & 7 - have a single child operation.
The execution tree starts with operation 6, using the TESTA2_PK index to identify rows where A2.X=100.
From this list of rowids, we go to the table TEST_A2 operation 5.
The rows from operation five feed into the NESTED LOOP - operation 4.
For each of these rows, we go to TEST_A1 via the index TEST_A1_PK for rows where A1.X=100.
This is really a cartesian join because there's no join condition between the two tables.
etc, etc, etc
Three things in particular to point out.
Firstly, that nothing joins to A2. So there will be a cartesian product - i.e. for every row in the result set between the joined tables A1, B, C and D, these will be multiplied by the number of rows returned by the the A2 rowsource.
Secondly, when everything has got one or zero rows (or the optimizer thinks that it's one or zero rows), you can get very different plans from when there are known/thought to be more rows.
Both depend on a1. So no need to execute one after the other (no inter-dependency).Thirdly, in terms of isolated join operations (ignoring A2 and C for the moment), A1 cannot join to B and D at the same time, you can either join A1 to B and then join the result of that to D, or join A1 to D then B, which is what you've got in your plan (well, actually we have A2 joined to A1 then the result of that joined to D and then the result of that to B).
Edited by: Dom Brooks on Jul 6, 2011 4:07 PM
Corrected typo -
Why optimizer prefers nested loop over hash join?
What do I look for if I want to find out why the server prefers a nested loop over hash join?
The server is 10.2.0.4.0.
The query is:
SELECT p.*
FROM t1 p, t2 d
WHERE d.emplid = p.id_psoft
AND p.flag_processed = 'N'
AND p.desc_pool = :b1
AND NOT d.name LIKE '%DUPLICATE%'
AND ROWNUM < 2tkprof output is:
Production
call count cpu elapsed disk query current rows
Parse 1 0.01 0.00 0 0 4 0
Execute 1 0.00 0.01 0 4 0 0
Fetch 1 228.83 223.48 0 4264533 0 1
total 3 228.84 223.50 0 4264537 4 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 108 (SANJEEV)
Rows Row Source Operation
1 COUNT STOPKEY (cr=4264533 pr=0 pw=0 time=223484076 us)
1 NESTED LOOPS (cr=4264533 pr=0 pw=0 time=223484031 us)
10401 TABLE ACCESS FULL T1 (cr=192 pr=0 pw=0 time=228969 us)
1 TABLE ACCESS FULL T2 (cr=4264341 pr=0 pw=0 time=223182508 us)Development
call count cpu elapsed disk query current rows
Parse 1 0.01 0.00 0 0 0 0
Execute 1 0.00 0.01 0 4 0 0
Fetch 1 0.05 0.03 0 512 0 1
total 3 0.06 0.06 0 516 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 113 (SANJEEV)
Rows Row Source Operation
1 COUNT STOPKEY (cr=512 pr=0 pw=0 time=38876 us)
1 HASH JOIN (cr=512 pr=0 pw=0 time=38846 us)
51 TABLE ACCESS FULL T2 (cr=492 pr=0 pw=0 time=30230 us)
861 TABLE ACCESS FULL T1 (cr=20 pr=0 pw=0 time=2746 us)sanjeevchauhan wrote:
What do I look for if I want to find out why the server prefers a nested loop over hash join?
The server is 10.2.0.4.0.
The query is:
SELECT p.*
FROM t1 p, t2 d
WHERE d.emplid = p.id_psoft
AND p.flag_processed = 'N'
AND p.desc_pool = :b1
AND NOT d.name LIKE '%DUPLICATE%'
AND ROWNUM < 2
You've got already some suggestions, but the most straightforward way is to run the unhinted statement in both environments and then force the join and access methods you would like to see using hints, in your case probably "USE_HASH(P D)" in your production environment and "FULL(P) FULL(D) USE_NL(P D)" in your development environment should be sufficient to see the costs and estimates returned by the optimizer when using the alternate access and join patterns.
This give you a first indication why the optimizer thinks that the chosen access path seems to be cheaper than the obviously less efficient plan selected in production.
As already mentioned by Hemant using bind variables complicates things a bit since EXPLAIN PLAN is not reliable due to bind variable peeking performed when executing the statement, but not when explaining.
Since you're already on 10g you can get the actual execution plan used for all four variants using DBMS_XPLAN.DISPLAY_CURSOR which tells you more than the TKPROF output in the "Row Source Operation" section regarding the estimates and costs assigned.
Of course the result of your whole exercise might be highly dependent on the actual bind variable value used.
By the way, your statement is questionable in principle since you're querying for the first row of an indeterministic result set. It's not deterministic since you've defined no particular order so depending on the way Oracle executes the statement and the physical storage of your data this query might return different results on different runs.
This is either an indication of a bad design (If the query is supposed to return exactly one row then you don't need the ROWNUM restriction) or an incorrect attempt of a Top 1 query which requires you to specify somehow an order, either by adding a ORDER BY to the statement and wrapping it into an inline view, or e.g. using some analytic functions that allow you specify a RANK by a defined ORDER.
This is an example of how a deterministic Top N query could look like:
SELECT
FROM
SELECT p.*
FROM t1 p, t2 d
WHERE d.emplid = p.id_psoft
AND p.flag_processed = 'N'
AND p.desc_pool = :b1
AND NOT d.name LIKE '%DUPLICATE%'
ORDER BY <order_criteria>
WHERE ROWNUM <= 1;Regards,
Randolf
Oracle related stuff blog:
http://oracle-randolf.blogspot.com/
SQLTools++ for Oracle (Open source Oracle GUI for Windows):
http://www.sqltools-plusplus.org:7676/
http://sourceforge.net/projects/sqlt-pp/ -
Hi,
Both the querys are returning same results, but in my first query hash join and second query nested loop . How ? PLs explain
select *
from emp a,dept b
where a.deptno=b.deptno and b.deptno>20;
6 rows
Plan hash value: 4102772462
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 6 | 348 | 6 (17)| 00:00:01 |
|* 1 | HASH JOIN | | 6 | 348 | 6 (17)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 3 | 60 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | PK_DEPT | 3 | | 1 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL | EMP | 7 | 266 | 3 (0)| 00:00:01 |
Predicate Information (identified by operation id):
1 - access("A"."DEPTNO"="B"."DEPTNO")
3 - access("B"."DEPTNO">20)
4 - filter("A"."DEPTNO">20)
select *
from emp a,dept b
where a.deptno=b.deptno and b.deptno=30;
6 rows
Plan hash value: 568005898
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 5 | 290 | 4 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 5 | 290 | 4 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 20 | 1 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL | EMP | 5 | 190 | 3 (0)| 00:00:01 |
Predicate Information (identified by operation id):
3 - access("B"."DEPTNO"=30)
4 - filter("A"."DEPTNO"=30)Hi,
Unless specifically requested, Oracle picks the best execution plan based on estimates of table sizes, column selectivity and many other variables. Even though Oracle does its best to have the estimates as accurate as possible, they are frequently different, and in some cases quite different, from the actual values.
In the first query, Oracle estimated that the predicate “ b.deptno>20” would limit the number of records to 6, and based on that it decided the use Hash Join.
In the second query, Oracle estimated that the predicate “b.deptno=30” would limit the number of records to 5, and based on that it decided the use Nested Loops Join.
The fact that the actual number of records is the same is irrelevant because Oracle used the estimate, rather the actual number of records to pick the best plan.
HTH,
Iordan
Iotzov -
DECLARE @tableA table (Productid varchar(20),Product varchar(20),RateID int)
insert into @tableA values('1','Mobile',2);
insert into @tableA values('2','Chargers',4);
insert into @tableA values('3','Stand',6);
insert into @tableA values('4','Adapter',8);
insert into @tableA values('5','Cover',10);
insert into @tableA values('6','Protector',12);
--SELECT * FROM @tableA
DECLARE @tableB table (id varchar(20),RateID int,Rate int)
insert into @tableB values('1',2,200);
insert into @tableB values('2',4,40);
insert into @tableB values('3',6,60);
insert into @tableB values('4',8,80);
insert into @tableB values('5',10,10);
insert into @tableB values('6',12,15);
--SELECT * FROM @tableB
SELECT Product,Rate
FROM @tableA a
JOIN @tableB b ON a.RateID = b.RateID
Above is the sample query, where in execution plan it shows the Hash Match (inner Join). Now how do I change it to Nested Loop with out changing the query? help plzIs Hash Match(inner join) or Nested loop is better to have in the query?
That depends on the size of the tables, available indexes etc. The optimizer will (hopefully) make the best choice.
Above is the sample query, where in execution plan it shows the Hash Match (inner Join). Now how do I change it to Nested Loop with out changing the query?
The answer that you should leave that to the optimizer in most cases.
I see that the logical read for nested loop is higher than Hash Match.
But Hash Match tends to need more CPU. The best way to two compare two queries or plans is wallclock time.
On a big tables, how do we reduce the logical read?
Make sure that there are usable indexes.
Erland Sommarskog, SQL Server MVP, [email protected] -
I've been asked to check if HASH JOIN is more suitable than NESTED LOOP(which CBO chose by default) for the following query.
SELECT CM_DETAILS.TASK_ID FROM GEN_TYPE, CM_DETAILS WHERE ( ( ( ( ( CM_DETAILS.STAT_CODE < 8 ) AND ( GEN_TYPE.TASK_ID = CM_DETAILS.TASK_ID ) ) AND ( GEN_TYPE.DEST_LOCN_ID = 5 ) ) AND ( GEN_TYPE.COM_ID = 7 ) ) AND ( ( ( CM_DETAILS.CASE_NO = 1 ) OR ( CM_DETAILS.CASE_NO = 3 ) ) OR ( CM_DETAILS.CASE_NO = 9 ) ) )
Both GEN_TYPE and CM_DETAILS tables have over 330,000 rows.
Version: 10g R2
Any thoughts?As gintsp gave you very nice tip but there is initialization parameter " OPTIMIZER_INDEX_COST_ADJ" which cause what path to be chose for CBO,but usually expert says for changing init paramter setting should be at last resort.
It has default value of 100 which indicates to the CBO that indexed access is 100% as costly (i.e., equally costly) as FULL table scan access.
SQL> column plan_plus_exp format a100
SQL> set linesize 1000
SQL> SET AUTOTRACE TRACEONLY
SQL> SELECT e.ename,d.dname
2 FROM emp e,dept d
3 WHERE e.deptno=d.deptno
4 /
14 rows selected.
Execution Plan
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=7 Card=10 Bytes=320)
1 0 HASH JOIN (Cost=7 Card=10 Bytes=320)
2 1 TABLE ACCESS (FULL) OF 'DEPT' (TABLE) (Cost=3 Card=5 Bytes=90)
3 1 TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=3 Card=14 Bytes=196)
Statistics
672 recursive calls
0 db block gets
151 consistent gets
27 physical reads
0 redo size
793 bytes sent via SQL*Net to client
508 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
15 sorts (memory)
0 sorts (disk)
14 rows processed
SQL> /
14 rows selected.
Execution Plan
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=7 Card=10 Bytes=320)
1 0 HASH JOIN (Cost=7 Card=10 Bytes=320)
2 1 TABLE ACCESS (FULL) OF 'DEPT' (TABLE) (Cost=3 Card=5 Bytes=90)
3 1 TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=3 Card=14 Bytes=196)
Statistics
0 recursive calls
0 db block gets
15 consistent gets
0 physical reads
0 redo size
793 bytes sent via SQL*Net to client
508 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
14 rows processed
SQL> SHOW PARAMETER optimizer
NAME TYPE VALUE
optimizer_dynamic_sampling integer 2
optimizer_features_enable string 10.1.0
optimizer_index_caching integer 0
optimizer_index_cost_adj integer 100<--------
optimizer_mode string ALL_ROWS
SQL> ALTER SESSION SET optimizer_index_cost_adj=35
2 /
Session altered.
SQL> SELECT e.ename,d.dname
2 FROM emp e,dept d
3 WHERE e.deptno=d.deptno
4 /
14 rows selected.
Execution Plan
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=5 Card=10 Bytes=320)
1 0 MERGE JOIN (Cost=5 Card=10 Bytes=320)
2 1 TABLE ACCESS (BY INDEX ROWID) OF 'DEPT' (TABLE) (Cost=1 Card=5 Bytes=90)
3 2 INDEX (FULL SCAN) OF 'DEPT_PRIMARY_KEY' (INDEX (UNIQUE)) (Cost=1 Card=5)
4 1 SORT (JOIN) (Cost=4 Card=14 Bytes=196)
5 4 TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=3 Card=14 Bytes=196)
Statistics
1 recursive calls
0 db block gets
11 consistent gets
1 physical reads
0 redo size
733 bytes sent via SQL*Net to client
508 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
14 rows processed
SQL> /
14 rows selected.
Execution Plan
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=5 Card=10 Bytes=320)
1 0 MERGE JOIN (Cost=5 Card=10 Bytes=320)
2 1 TABLE ACCESS (BY INDEX ROWID) OF 'DEPT' (TABLE) (Cost=1 Card=5 Bytes=90)
3 2 INDEX (FULL SCAN) OF 'DEPT_PRIMARY_KEY' (INDEX (UNIQUE)) (Cost=1 Card=5)
4 1 SORT (JOIN) (Cost=4 Card=14 Bytes=196)
5 4 TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=3 Card=14 Bytes=196)
Statistics
0 recursive calls
0 db block gets
11 consistent gets
0 physical reads
0 redo size
733 bytes sent via SQL*Net to client
508 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
14 rows processedKhurram -
Oracle 11g - Nested loops on outer joins
Hello,
I have a select query that was working with no problems. The results are used to insert data into a temp table.
Recently, it would not complete executing. The explain plan shows a cartesian. But, there could be problems with using nested loops on the outer join. Interestingly, when I copy production code and rename the temp table and rename the view, it works.
Can someone take a look at the code and help. Maybe offer a suggestion on tuning too? Thanks.
CREATE TABLE "CT"
( "TN" VARCHAR2(30) NOT NULL ENABLE,
"COL_NAME" VARCHAR2(30) NOT NULL ENABLE,
"CDE" VARCHAR2(5) NOT NULL ENABLE,
"CDE_DESC" VARCHAR2(80) NOT NULL ENABLE,
"CDE_STAT" CHAR(1));
insert into CT (TN, COL_NAME, CDE, CDE_DESC, CDE_STAT)
values ('INDSD', 'STCD', 'U', 'RF', 'A');
insert into CT (TN, COL_NAME, CDE, CDE_DESC, CDE_STAT)
values ('AT', 'TCD', '001', 'RL', 'A');
insert into CT (TN, COL_NAME, CDE, CDE_DESC, CDE_STAT)
values ('AT', 'TCD', '033', 'PFR', 'A');
CREATE TABLE "IPP"
( "IND_ID" NUMBER(9,0) NOT NULL ENABLE,
"PLCD" VARCHAR2(5) NOT NULL ENABLE,
"CBCD" VARCHAR2(5));
insert into IPP (IND_ID, PLCD, CBCD)
values (2007, 'AS', '04');
insert into IPP (IND_ID, PLCD, CBCD)
values (797098, 'AS', '34');
insert into IPP (IND_ID, PLCD, CBCD)
values (797191, 'AS','04');
CREATE TABLE "INDS"
( "OPCD" VARCHAR2(5) NOT NULL ENABLE,
"IND_ID" NUMBER(9,0) NOT NULL ENABLE,
"IND_CID" NUMBER(*,0),
"GFLG" VARCHAR2(1),
"HHID" NUMBER(9,0),
"DOB" DATE,
"DOB_FLAG" VARCHAR2(1),
"VCD" VARCHAR2(5),
"VTDTE" DATE,
"VPPCD" VARCHAR2(4),
"VRCDTE" DATE NOT NULL ENABLE,
"VDSID" NUMBER(9,0),
"VTRANSID" NUMBER(12,0),
"VOWNCD" VARCHAR2(5),
"RCDTE" DATE,
"LRDTE" DATE
insert into INDS (OPCD, IND_ID, IND_CID, GFLG, HHID, DOB, DOB_FLAG, VCD, VTDTE, VPPCD, VRCDTE, VDSID, VTRANSID, VOWNCD, RCDTE, LRDTE)
values ('USST', 2007, 114522319, '', 304087673, to_date('01-01-1980', 'dd-mm-yyyy'), 'F', '2', to_date('06-04-2011 09:21:37', 'dd-mm-yyyy hh24:mi:ss'), '', to_date('06-04-2011 09:21:37', 'dd-mm-yyyy hh24:mi:ss'), 1500016, null, 'USST', to_date('06-04-2011 09:21:37', 'dd-mm-yyyy hh24:mi:ss'), to_date('18-07-2012 21:52:53', 'dd-mm-yyyy hh24:mi:ss'));
insert into INDS (OPCD, IND_ID, IND_CID, GFLG, HHID, DOB, DOB_FLAG, VCD, VTDTE, VPPCD, VRCDTE, VDSID, VTRANSID, VOWNCD, RCDTE, LRDTE)
values ('USST', 304087678, 115242519, '', 304087678, to_date('01-01-1984', 'dd-mm-yyyy'), 'F', '2', to_date('06-04-2011 09:21:39', 'dd-mm-yyyy hh24:mi:ss'), '', to_date('06-04-2011 09:21:39', 'dd-mm-yyyy hh24:mi:ss'), 1500016, null, 'USST', to_date('06-04-2011 09:21:39', 'dd-mm-yyyy hh24:mi:ss'), to_date('18-07-2012 21:52:53', 'dd-mm-yyyy hh24:mi:ss'));
CREATE TABLE "INDS_TYPE"
( "IND_ID" NUMBER(9,0) NOT NULL ENABLE,
"STCD" VARCHAR2(5) NOT NULL ENABLE);
insert into INDS_type (IND_ID, STCD)
values (2007, 'U');
insert into INDS_type (IND_ID, STCD)
values (313250322, 'U');
insert into INDS_type (IND_ID, STCD)
values (480058122, 'U');
CREATE TABLE "PLOP"
( "OPCD" VARCHAR2(5) NOT NULL ENABLE,
"PLCD" VARCHAR2(5) NOT NULL ENABLE,
"PPLF" VARCHAR2(1));
insert into PLOP (OPCD, PLCD, PPLF)
values ('USST', 'SP', 'Y');
insert into PLOP (OPCD, PLCD, PPLF)
values ('PMUSA', 'ST', '');
insert into PLOP (OPCD, PLCD, PPLF)
values ('USST', 'RC', '');
CREATE TABLE "IND_T"
( "OPCD" VARCHAR2(5) NOT NULL ENABLE,
"CID" NUMBER(9,0) NOT NULL ENABLE,
"CBCD" VARCHAR2(5),
"PF" VARCHAR2(1) NOT NULL ENABLE,
"DOB" DATE,
"VCD" VARCHAR2(5),
"VOCD" VARCHAR2(5),
"IND_CID" NUMBER,
"RCDTE" DATE NOT NULL ENABLE
insert into IND_T (OPCD, CID, CBCD,PF, DOB, VCD, VOCD, IND_CID, RCDTE)
values ('JMC', 2007, '04', 'F',to_date('11-10-1933', 'dd-mm-yyyy'), '2', 'PMUSA', 363004880, to_date('30-09-2009 04:31:34', 'dd-mm-yyyy hh24:mi:ss'));
insert into IND_T (OPCD, CID, CBCD,PF, DOB, VCD, VOCD, IND_CID, RCDTE)
values ('JMC', 2008, '04', 'N',to_date('01-01-1980', 'dd-mm-yyyy'), '2', 'PMUSA', 712606335, to_date('05-04-2013 19:36:05', 'dd-mm-yyyy hh24:mi:ss'));
CREATE TABLE "IC"
( "CID" NUMBER(9,0) NOT NULL ENABLE,
"CF" CHAR(1));
insert into IC (CID, CF)
values (2007, 'N');
insert into IC (CID, CF)
values (100, 'N');
insert into IC (CID, CF)
values (200, 'N');
CREATE OR REPLACE FORCE VIEW "INDSS_V" ("OPCD", "IND_ID", "IND_CID", "GFLG", "HHID", "DOB", "DOB_FLAG", "VCD", "VTDTE", "VPPCD", "VRCDTE", "VDSID", "VTRANSID", "VOWNCD", "RCDTE", "LRDTE") AS
SELECT DISTINCT a.OPCD, a.IND_ID, a.IND_CID, a.GFLG, a.HHID,
a.DOB, a.DOB_flag, a.VCD, a.VTDTE,
a.VPPCD, a.VRCDTE, a.VDSID, a.VTRANSID,
a.VOWNCD, a.RCDTE, a.LRDTE
FROM INDS a, INDS_type b
WHERE a.IND_ID = b.IND_ID
AND b.STCD in (select CDE
from CT --database link
where TN = 'INDSD'
and COL_NAME = 'STCD'
and CDE_STAT = 'A') ;
--insert /*+ parallel(IND_T,2) */ into IND_T
select /*+ parallel(a,4) */
a.OPCD as OPCD
, a.IND_ID as CID
, b.CBCD as CBCD
, NULL as BFCD
, 'N' as PF
, a.DOB as DOB
, a.VCD as VCD
, a.VOWNCD as VOCD
, a.IND_CID as IND_CID
, a.RCDTE as RCDTE
from INDSS_V a
, (select /*+ parallel(IPP,4) */ * from IPP IPP , PLOP PLO
where plo.PLCD = ipp.PLCD
and PPLF='Y') b
, IC c
where a.IND_ID = b.IND_ID (+)
and a.OPCD = b.OPCD (+)
and a.IND_ID = c.CID
and c.CF = 'N';Please consult
HOW TO: Post a SQL statement tuning request - template posting
Also format your code and post it using the [ code ] and [ /code ] tags. (Leave out the extra space after [ and before ])
Sybrand Bakker
Senior Oracle DBA
Edited by: sybrand_b on 10-apr-2013 17:57
Maybe you are looking for
-
Setting Destination URI of a Pie Chart Graph Programatically-OA Framework
Hi All, I have created a OF Page with the following Regions one under the other: 1. PageLayoutRN 2. HideShowHdr1 - Hide/Show Header 3. TblLayout1RN - Table Layout 4. Tbl1RowLayout1RN - Table Row Layout 5. Row1CellFormat1RN - Cell Format 6. Row2CellFo
-
Syncing logic express 9 with akai mpk61
hey guys, i need some advice syncing akai mpk 61 or 49 midi controller with logics timecode, any suggestions or pdf's u can send me, cheers, peter
-
Hi, we are currently with sky for everything but are looking to change. Looking for any advice that can sway our decision. Something to take into consideration is that we have a 9yr old who watches kids programmes. I have 'chatted' to BT but am confu
-
IBooks screen dim only works on 3/4 of screen
I Just upgraded to ipad os 813 and now in iBooks whe reading a book, if I want to set brightness to its lowest setting it applies this to only 3/4 of the screen. See attached screen shot. I would appreciate any help on how to fix or if this is a big
-
How to get two date difference in Day/hour/min/secs pl
First of all, Hello to All, i am new to Oracle DB, but i have little experience in Other Database such as Sybase, sql server etc please kindly help me in DoctorChart table i have DocName,Id#,Citcotype,Medtype,AdmitDate,DischargeDate(both admitdate &