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/

Similar Messages

  • Generally when does optimizer use nested loop and Hash joins  ?

    Version: 11.2.0.3, 10.2
    Lets say I have a table called ORDER and ORDER_DETAIL.
    ORDER_DETAIL is the child table of ORDERS .
    This is what I understand about Nested Loop:
    When we join ORDER AND ORDER_DETAIL tables oracle will form a 'nested loop' in which for each order_ID in ORDER table (outer loop), oracle will look for corresponding multiple ORDER_IDs in the ORDER_DETAIL table.
    Is nested loop used when the driving table (ORDER in this case) is smaller than the child table (ORDER_DETAIL) ?
    Is nested loop more likely to use Indexes in general ?
    How will these two tables be joined using Hash joins ?
    When is it ideal to use hash joins  ?

    Your description of a nested loop is correct.
    The overall rule is that Oracle will use the plan that it calculates to be, in general, fastest. That mostly means fewest I/O's, but there are various factors that adjust its calculation - e.g. it expects index blocks to be cached, multiple reads entries in an index may reference the same block, full scans get multiple blocks per I/O. It also has CPU cost calculations, but they generally only become significant with function / package calls or domain indexes (spatial, text, ...).
    Nested loop with an index will require one indexed read of the child table per outer table row, so its I/O cost is roughly twice the number of rows expected to match the where clause conditions on the outer table.
    A hash join reads the of the smaller table into a hash table then matches the rows from the larger table against the hash table, so its I/O cost is the cost of a full scan of each table (unless the smaller table is too big to fit in a single in-memory hash table). Hash joins generally don't use indexes - it doesn't use the index to look up each result. It can use an index as a data source, as a narrow version of the table or a way to identify the rows satisfying the other where clause conditions.
    If you are processing the whole of both tables, Oracle is likely to use a hash join, and be very fast and efficient about it.
    If your where clause restricts it to a just few rows from the parent table and a few corresponding rows from the child table, and you have an index Oracle is likely to use a nested loops solution.
    If the tables are very small, either plan is efficient - you may be surprised by its choice.
    Don't be worry about plans with full scans and hash joins - they are usually very fast and efficient. Often bad performance comes from having to do nested loop lookups for lots of rows.

  • Nested loop vs Hash Join

    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

  • 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

  • Message-Mapping: nested Loops over Elements

    Hi Experts,
    I have problems with my Message-Mapping in the IR. I have a source and a target structure. In the following I will give you easy examples of these structures:
    <u>source structure:</u>
       <E1EDP01>
          <E1EDP19>
             <QUALF> ... </QUALF>
             <IDTNR> ... </IDTNR>
          </E1EDP19>
          <E1EDP19>
             <QUALF> ... </QUALF>
             <IDTNR> ... </IDTNR>
          </E1EDP19>
       </E1EDP01>
       <E1EDP01>
          <E1EDP19>
             <QUALF> ... </QUALF>
             <IDTNR> ... </IDTNR>
          </E1EDP19>
          <E1EDP19>
             <QUALF> ... </QUALF>
             <IDTNR> ... </IDTNR>
          </E1EDP19>
       </E1EDP01>
    <u>target structure:</u>
    <LineItem>
       <IDTNR></IDTNR>
    </LineItem>
    <LineItem>
       <IDTNR></IDTNR>
    </LineItem>
    That means:
    For every <E1EDP01> in the source structure I create one <LineItem> in the target structure. One E1EDP01-Element can contain more than one E1EDP19-Elements. I have to loop over these E1EDP19-Elements, because I have to locate the Element <QUALF> with a given (fixed) value. The Mapping should put the value from the Element <IDTNR> from the source structure - where the QUALF-Element has this given value - in the IDTNR-Element of the target structure.
    I tried it with a UDF, but only the first <IDTNR> in the target structure got filled.
    Thanks for your help
    Christopher

    Thank you,
    but how I can set the Elements IDTNR and QUALF to the context E1EDP01?
    In the splitByValue-Function do I need "each value"?
    best regards
    Christopher

  • Merge joins turned into expensive hash joins!?

    Hello.....
    working with two tables....
    Table A is where I am bringing data into.....
    Table B is where the data resides.
    Table B has 32million rows...
    I was using just a quick.....select b.whatever from table.a, table.b where primary key and b.table=a.table.....
    I could grab a few rows at a time and it would use a merge join and take 2-3 seconds.
    Now....(after NO changes) it hangs. In production it is still fast and uses merge joins, but I compared the activity to that of test and it's using hash joins! The hidden parameter _use merge join will force a merge, but then none of my other hash joins (where I want them) will work.
    Optimizer dorked up? Whats the scoop?

    hmm, well your replies are just as ingenius. . .here's a cookie.
    10.2.0.4.0 and i put the relevent sql in there enough to ask why over a dblink (not that it matters) why this behavior would all of the sudden change.
    Again, simple matter of creating a nested loop over a dblink to grab a few rows from table b to insert them into table a. It used to merge join, now it's hash joining. Just wondering :-)

  • Nested Loop and Driving Table.

    In the documentation I read these (/B19306_01/server.102/b14211/optimops.htm#i82080).
    The optimizer uses nested loop joins when joining small number of rows, with a good driving condition between the two tables. You drive from the outer loop to the inner loop, so the order of tables in the execution plan is important.
    The outer loop is the driving row source. It produces a set of rows for driving the join condition. The row source can be a table accessed using an index scan or a full table scan. Also, the rows can be produced from any other operation. For example, the output from a nested loop join can be used as a row source for another nested loop join.>
    I need some help to understand the bold line, i.e. so the order of tables in the execution plan is important.
    There are various conflicting opinion about the driving table (some says smaller and some says larger table is good option) and unfortunately I did not understand any of those logic.
    I read these threads/blogs also.
    CBO (optimizer) nest-loop join question
    http://hoopercharles.wordpress.com/2011/03/21/nested-loops-join-the-smaller-table-is-the-driving-table-the-larger-table-is-the-driving-table/
    In practice, I have seen explain plan for select e.ename,d.dname
      2  from emp e, dept d
      3  where e.deptno=d.deptno; usages emp as driving table (I only understood part of Aman's logic that dept table access would be faster when there would be an index available over it and hence it is in the inner loop)
    Whereas, SQL> explain plan for
      2  select e.ename,d.dname
      3  from emp e, dept d
      4  where e.deptno=d.deptno
      5  and e.deptno=20; usages dept as driving table.
    I have use USE_NL_WITH_INDEX with LEADING hint to change it, but it is giving some adverse effect (sometimes with optimizer_mode ALL_ROWS it is ignoring the hint completely).
    so the order of tables in the execution plan is important. How can I determine it ? I have read Tom's effective oracle by design but there is also no solution.

    Not sure I quite understand the question.
    Both threads contain lots of useful information about the broad considerations in play and debunking any myths.
    I need some help to understand the bold line, i.e.
    "so the order of tables in the execution plan is important"I read this as meaning just what it says, i.e. read the execution plan carefully and that
    NESTED LOOP
      A
      Bis not the same as
    NESTED LOOP
      B
      AA 10053 trace would normally be quite verbose about it's considerations as to join order.

  • HASH JOIN or NESTED LOOP

    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

  • CBO (optimizer) nest-loop join question

    OS: Red Hat Linux
    DB: 11gR1
    I have gotten two conflicting answers while reading books by Don Burleson and Dan Hotka. It has to do with the CBO and nested-joins:
    One says the CBO will choose the 'smaller' table as the driving table, the other states that the 'larger' table will be the driving table. And both stick by this philosophy as the preferred goal of any SQL Tuning -- that is, one states that the 'smaller' table should be the driving table. The other says the 'larger' table should be the driving table.
    I had always thought that the 'smaller' table should be the driving table. That in a nested loop the driving will not likely use an index even. Who is correct? (I am not going to say who said what, btw). :-)
    But I got to let one of them know they got a 'typo' ... :-)
    Thx.

    user601798 wrote:
    It is an over-simplistic scenario but, as I mentioned, if all other things are 'equal' -- which would include 'access time/work', then I think the small table as the driving table has the advantage.
    It is not possible for +"*all* other things to be equal"+. (my emphasis).
    If by +'access time/work'+ you mean the total is the same then it doesn't matter which table is first, the time/work is the same either way round.
    If you want to say that the +'access time/work'+ for acquiring the first rowsource is the same for both paths, and the +'access time/work'+ for acquiring related rows from the second table is the same FOR EACH DRIVING ROW, then the total +'access time/work'+ will be difference, and it would be better to start with the smaller table. (The example by Salman Qureshi above: Re: CBO (optimizer) nest-loop join question would apply.)
    On the other hand, and ignoring any idea of "all other things being equal", smaller tables tend to have smaller indexes, so if your smaller rowsource comes from a smaller table then acquiring those rows may be cheaper than acquiring rows from a larger table - which leads to the observation that (even with perfectly precise indexing):
    <ul>
    smaller number of rows * larger unit cost to find related rows
    </ul>
    may produce a larger value than
    <ul>
    larger number of rows * smaller unit cost to find related rows
    </ul>
    Regards
    Jonathan Lewis
    http://jonathanlewis.wordpress.com
    http://www.jlcomp.demon.co.uk
    A general reminder about "Forum Etiquette / Reward Points": http://forums.oracle.com/forums/ann.jspa?annID=718
    If you never mark your questions as answered people will eventually decide that it's not worth trying to answer you because they will never know whether or not their answer has been of any use, or whether you even bothered to read it.
    It is also important to mark answers that you thought helpful - again it lets other people know that you appreciate their help, but it also acts as a pointer for other people when they are researching the same question, moreover it means that when you mark a bad or wrong answer as helpful someone may be prompted to tell you (and the rest of the forum) what's so bad or wrong about the answer you found helpful.

  • Hash join vs nested loop

    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 plz

    Is 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]

  • Nested loop join v/s Sort merge

    I have seen that nested loops are better if the inner table is being indexed, because for each outer table row, we are looking for a match in the inner table. But is there any case when optimizer still goes for a nested loop even if there is no index on the inner table. That is my first question ?
    My second question := When doing a sort merge join oracle has to sort both result sets and then merge them. Oracle says that if both the row sets, if already sorted is definately better for performance. Ya thats obvious. But back to my upper question, when there is no index on the inner table, is it the situation when oracle goes for a sort merge join ?

    My response should really have examples but since I do not have any handy I will just say about your first question. If there is no index available from table A to table B yes it is possible a nested loop join may still be used and table B read via full table scan within a nested loop. If table B is very small and consists of only a block or two this may be relatively efficient plan. It is more likely you sould see table B full scanned and the result feed into a hash join, but I have seen the plan you mention.
    Back before hash joins were introduced with 7.3 (if my memory is correct) you would see sort/merge joins used more often than you do now. Generally speaking no index on the join conditions would exist for this option to be chosen.
    If you really want to know why and sometimes what the optimizer is going to do buy Jonathan Lewis's book Cost-Based Oracle Fundamentals. If explains the optimizer in more depth than any other source I know of.
    HTH -- Mark D Powell --

  • Hash Join over NL

    My query reads like:
    create table XYZ nologging as
    select [] from
    driving_table 1800K rows,
    lkp1 235K
    LKP2 160K
    lkp3 80K
    lkp4 4K
    lkp5 4K
    lkp6 4K
    where [Driving_Table joins with each of the Lookups to Get lookup Keys]..as many rows in result set as in LArge_Driving_Table..all inner joins
    And I keep getting NEsted Loops all over.
    Optimizer settigs:
    -CHOOSE
    -hash_area_enabled=true
    -hash_area_size =31M (I tried increasing it to 250 M i.e. greater than size of driving table), but does not help
    - I have tried to increase optimize_increase_cost_adj to 10000, does not help
    -Have tried using USE_HASH. Dones not work , but maybe I am not setting it correctly
    AM I hitting bug 2502657 ? Any pointers on using USE_HASH correctly ?

    - Version 9.2.0.3
    - All tables have been analyzed
    CTAS Query:
    create table temp_delete as
    select CSU.CUSTOMER_SITE_USE_CODE CUST_SITE_CUSTOMER_SITE__1,
    CSU.CUSTOMER_SITE_USE_KEY CUST_SITE_CUSTOMER_SITE_,
    7 DAILY_LOAD_FL,
    ENTR_DT.DAY_KEY ENTR_DT_DAY_KEY,
    ACT_DT.DAY_KEY ACT_DT_DAY_KEY,
    INV.ED_QTY INVOICE_ED_QTY,
    INV.ED_INVOICE_NO INVOICE_ED_INVOICE_NO,
    nvl( ITM.ITM_KEY ,-1) ITM_KEY,
    -- to_number( INV.ED_SO_LINE ) ITM_LN,
    1 PRCS_DT_KEY,
    -1 RELS_DT_KEY,
    PROM_DT.DAY_KEY PROM_DT_DAY_KEY,
    -1 SHIP_DT_KEY,
    CSU.CUSTOMER_SALES_CODE_NUMBER CUST_SITE_CUSTOMER_SALES_1,
    CSU.CUSTOMER_SALES_CODE_KEY CUST_SITE_CUSTOMER_SALES,
    INV.ED_VALUE INVOICE_ED_VALUE,
    ORD.SLSORD_GK SLSORD_WH_SLSORD_GK,
    1 SLSORD_STAT_KEY,
    INV.ED_TOTAL_COST INVOICE_ED_TOTAL_COST,
    case
    when ACT_DT.OFFSET_DAY = 0
    then 'Y'
    else NULL
    end TODAY_FLG,
    INV.ED_HED_CD INVOICE_ED_HED_CD
    FROM
    INVOICE_EU_ST INV, 1800 K
    SLSORD_WH ORD, 235 K,
    CUST_SITE_USE_WH CSU, 83 K
    ITM_WH ITM, 155 K
    TIME_WH ACT_DT, 4K
    TIME_WH ENTR_DT, 4K
    TIME_WH PROM_DT 4K
    WHERE
    INV.SRC_ID =7 and
    INV.ED_ACTIVITY_DATE = ACT_DT.DAY_CHAR and
    INV.ED_LINE_ENTRY_DATE = ENTR_DT.DAY_CHAR and
    INV.ED_LINE_PROMISE_DATE= PROM_DT.DAY_CHAR and
    INV.ED_SALES_ORDER =ORD.SLSORD_ID and
    ORD.SRC_ID = 7 and
    INV.ED_CUST_ID=CSU.CUSTOMER_SITE_USE_CODE and
    CSU.SOURCE_ID=7 and
    INV.ORGANIZATION_ID=ITM.ORGANIZATION_ID and
    INV.ITM_CD =ITM.ITM_CD and
    ITM.SRC_ID=7
    PLAN_TABLE_OUTPUT
    | Id | Operation | Name | Rows
    | 0 | CREATE TABLE STATEMENT | | 4
    | 1 | LOAD AS SELECT | |
    | 2 | NESTED LOOPS | | 4
    | 3 | NESTED LOOPS | | 4
    | 4 | NESTED LOOPS | | 4
    | 5 | NESTED LOOPS | | 4
    | 6 | NESTED LOOPS | | 4
    | 7 | NESTED LOOPS | | 8
    |* 8 | TABLE ACCESS FULL | SLSORD_WH | 1
    |* 9 | TABLE ACCESS BY INDEX ROWID | INVOICE_EU_ST | 8
    |* 10 | INDEX RANGE SCAN | I2 | 8
    | 11 | TABLE ACCESS BY GLOBAL INDEX ROWID| ITM_WH | 1
    |* 12 | INDEX UNIQUE SCAN | ITM_WH_UK | 2
    | 13 | TABLE ACCESS BY GLOBAL INDEX ROWID | CUST_SITE_USE_WH | 1
    |* 14 | INDEX UNIQUE SCAN | CUST_SITE_USE_WH_UK | 3
    | 15 | TABLE ACCESS BY INDEX ROWID | TIME_WH | 1
    |* 16 | INDEX UNIQUE SCAN | TIME_CAL_UK | 1
    | 17 | TABLE ACCESS BY INDEX ROWID | TIME_WH | 1
    |* 18 | INDEX UNIQUE SCAN | TIME_CAL_UK | 1
    | 19 | TABLE ACCESS BY INDEX ROWID | TIME_WH | 1
    |* 20 | INDEX UNIQUE SCAN | TIME_CAL_UK | 1
    Predicate Information (identified by operation id):
    8 - filter("ORD"."SRC_ID"=7)
    9 - filter("INV"."SRC_ID"=7)
    10 - access("INV"."ED_SALES_ORDER"="ORD"."SLSORD_ID")
    12 - access("ITM"."SRC_ID"=7 AND "INV"."ORGANIZATION_ID"="ITM"."ORGANIZATION_I
    14 - access("INV"."ED_CUST_ID"="CSU"."CUSTOMER_SITE_USE_CODE" AND "CSU"."SOURC
    16 - access("INV"."ED_LINE_PROMISE_DATE"="PROM_DT"."DAY_CHAR")
    18 - access("INV"."ED_LINE_ENTRY_DATE"="ENTR_DT"."DAY_CHAR")
    20 - access("INV"."ED_ACTIVITY_DATE"="ACT_DT"."DAY_CHAR")
    Note: cpu costing is off
    40 rows selected

  • Optimizer choosing hash joins even when slower

    We have several queries where joins are being evaluated by full scans / hash joins even when forcing index use results in an execution time about a quarter the duration of the hash join plan. It still happens if I run DMS_STATS.GATHER_TABLE_STATS with FOR ALL COLUMNS.
    Is there a stats gathering option which is more likely to result in an indexd join without having to get developers to put optimizer hints in their queries?
    11g on SuSE 10.
    Many thanks.

    user10400178 wrote:
    That would require me to post a large amount of schema information as well to be of any added value.
    Surely there are some general recommendations one could make as to how to allow the optimizer to realise that joining through an index is going to be quicker than doing a full scan and hash join to a table.If you don't want to post the plans, then as a first step you basically need to verify yourself if the cardinality estimates returned by the execution plan correspond roughly to the actual cardinalities.
    E.g. in your execution plan there are steps like "FULL TABLE SCAN" and these operations likely have a corresponding "FILTER" predicate in the "Predicate Information" section below the plan.
    As first step you should run simple count queries ("select count(*) from ... where <FILTER/ACCESS predicates>") on the tables involved using the "FILTER" and "ACCESS" predicates mentioned to compare if the returned number of rows is in the same ballpark than the estimates mentioned in the plan.
    If these estimates are already way off then you know that for some reason the optimizer makes wrong assumptions and that's probably the reason why the suboptimal access pattern is preferred.
    One potential reason could be correlated column values, but since you're already on 11g you could make use of extended column statistics. See here for more details:
    http://download.oracle.com/docs/cd/B28359_01/server.111/b28274/stats.htm#BEIEEIJA
    Another reason might simply that you're choosing a too low "estimate" sample size for the statistics collection. In 11g you should always use the DBMS_STATS.AUTO_SAMPLE_SIZE for the "estimate_percent" parameter of the DBMS_STATS.GATHER__STATS procedures. It should generate accurate statistics without the need to analyze all of the data. See here in Greg Rahn's blog for an example:
    http://structureddata.org/2007/09/17/oracle-11g-enhancements-to-dbms_stats/
    Regarding the histograms: Oracle 11g by default generates histograms if it deems them to be beneficial. It is controlled by the parameter "METHOD_OPT" which has the default value of "FOR ALL COLUMNS SIZE AUTO". The "SIZE" keyword determines the generation of histograms. You could use "SIZE 1" to prevent histogram generation, "SIZE <n>" to control the number of buckets to use for the histogram or "SIZE AUTO" to let Oracle decide itself when and how to generate histograms.
    Regarding the stored outlines: You could have so called "stored outlines" that force the optimizer to stick to a certain plan. That features was introduced a long time ago and is sometimes also referred to as "plan stability", its main purpose was an attempt to smooth the transition from the rule based optimizer (RBO) to the cost based optimizer (CBO) introduced in Oracle 7 (although you can use it for other purposes, too, of course). Oracle 11g offers now the new "SQL plan management" feature that is supposed to supersede the "plan stability" feature. For more information, look here:
    http://download.oracle.com/docs/cd/B28359_01/server.111/b28274/outlines.htm#PFGRF707
    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/
    Edited by: Randolf Geist on Oct 16, 2008 4:20 PM
    Sample size note added
    Edited by: Randolf Geist on Oct 16, 2008 6:54 PM
    Outline info added

  • Help tuning NESTED LOOPS OUTER joins

    Hello,
    I have inherited this nasty query (below) that is taking an awful time to complete (more than 2 hrs a day)
    The worst bit is that I need to outer join my fact table so many times as I need bit’s and pieces from other tables/mviews.
    When I look at the explain plan I see that this situation means that the cbo is doing several NESTED LOOPS OUTER join operations. I understand that these nested loops mean going through every row in my primary table to see if there is a match in the secondary table (much smaller) which makes it extremely inefficient, is this right?
    The stats on the tables are all refreshed daily.
    Any ideas on how I can improve the performance here?
    Thanks in advance!
    The query:
    explain plan for
    SELECT x.user_id AS user_id,
    x.login_name AS login_name,
    c.date_of_birth AS date_of_birth,
    x.registration_site AS registration_site,
    x.organisation AS organisation,
    c.user_title AS user_title,
    c.first_name AS first_name,
    c.last_name AS last_name,
    x.email_address AS email_address,
    x.user_status AS user_status,
    x.user_privilege AS user_access_privilege,
    x.date_registration AS date_registration,
    x.affiliate_id AS affiliate_id,
    x.mobile_number AS mobile_number,
    x.optional_parameter AS vt_number,
    gud.display_name AS chat_name,
    REPLACE (s4.address_line_1, ',', '') AS address_line_1,
    REPLACE (s4.address_line_2, ',', '') AS address_line_2,
    REPLACE (s4.town, ',', '') AS town,
    REPLACE (s4.county, ',', '') AS county,
    REPLACE (s4.postcode, ',', '') AS postcode,
    s4.country AS country,
    s3.last_login AS last_login_date,
    x.email_send_newsletter AS email_send_newsletter,
    x.email_give_details_thirdparty AS email_give_details_thirdparty,
    NVL (ia.cash_balance, 0) AS current_cash_balance,
    NVL (ia.bonus_balance, 0) AS current_bonus_balance,
    x.external_affiliate_id AS external_affiliate_id,
    r.currency_code AS currency,
    NVL (ia.points_balance, 0) AS current_loyalty_points_balance,
    p.status AS buyer_status,
    NVL (ia.bi_bonus_balance, 0) AS current_bi_bonus_balance,
    NVL (ia.pending_balance, 0) AS current_pending_balance,
    l.level_name AS current_loyalty_level,
    l.date_level_achieved AS date_level_achieved,
    NVL (l.current_period_loyalty_points, 0) AS current_period_loyalty_points,
    r.region AS user_region,
    x.registration_platform AS registration_platform,
    x.external_user_name AS external_user_name,
    c.home_number AS home_number,
    pr.code AS reg_promo_code,
    g.date_first_buy AS date_first_buy
    FROM gl_user_registrations x,
    gl_region r,
    MVW_USER_BALANCES ia,
    gl_customers c,
    gl_user_display_names gud,
    gl_user_last_login s3,
    (SELECT z.user_id AS user_id,
    z.address_line_1 AS address_line_1,
    z.address_line_2 AS address_line_2,
    z.town AS town,
    z.county AS county,
    z.postcode AS postcode,
    z.country AS country
    FROM gl_user_addresses z
    WHERE z.is_current = 1) s4,
    gl_user_buyer_mapping upm,
    gl_buyer p,
    mvw_user_loyalty_points l,
    MVW_USER_PROMO_CODE_REG pr,
    MVW_USER_FIRST_BUY_DATE g
    WHERE x.base_region = r.region
    AND x.user_id = ia.user_id (+)
    AND x.customer_id = c.customer_id(+)
    AND x.user_id = gud.user_id (+)
    AND x.user_id = s4.user_id (+)
    AND x.user_id = s3.user_id (+)
    AND x.user_id = upm.user_id (+)
    AND upm.buyer_id = p.buyer_id
    AND x.user_id = l.user_id (+)
    AND x.user_id = pr.user_id (+)
    AND x.user_id = g.user_id (+);
    select * from table(dbms_xplan.display);
    Plan hash value: 2158171613
    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
    | 0 | SELECT STATEMENT | | 100 | 63100 | 135 (1)| 00:00:01 |
    | 1 | NESTED LOOPS OUTER | | 100 | 63100 | 135 (1)| 00:00:01 |
    | 2 | NESTED LOOPS OUTER | | 100 | 60600 | 120 (1)| 00:00:01 |
    | 3 | NESTED LOOPS OUTER | | 100 | 57100 | 105 (1)| 00:00:01 |
    | 4 | NESTED LOOPS OUTER | | 100 | 55400 | 90 (2)| 00:00:01 |
    | 5 | NESTED LOOPS OUTER | | 100 | 53600 | 70 (2)| 00:00:01 |
    |* 6 | HASH JOIN | | 100 | 47000 | 55 (2)| 00:00:01 |
    | 7 | TABLE ACCESS FULL | GL_REGION | 18 | 252 | 2 (0)| 00:00:01 |
    | 8 | NESTED LOOPS OUTER | | 100 | 22800 | 52 (0)| 00:00:01 |
    | 9 | NESTED LOOPS OUTER | | 100 | 19700 | 47 (0)| 00:00:01 |
    | 10 | NESTED LOOPS OUTER | | 100 | 17600 | 37 (0)| 00:00:01 |
    | 11 | NESTED LOOPS | | 100 | 15800 | 27 (0)| 00:00:01 |
    | 12 | NESTED LOOPS | | 102 | 2754 | 17 (0)| 00:00:01 |
    | 13 | TABLE ACCESS FULL | GL_BUYER | 6143K| 64M| 2 (0)| 00:00:01 |
    | 14 | TABLE ACCESS BY INDEX ROWID| GL_USER_BUYER_MAPPING | 1 | 16 | 1 (0)| 00:00:01 |
    |* 15 | INDEX RANGE SCAN | GL_USER_BUYER_MAPPPING_IX | 1 | | 1 (0)| 00:00:01 |
    | 16 | TABLE ACCESS BY INDEX ROWID | GL_USER_REGISTRATIONS | 1 | 131 | 1 (0)| 00:00:01 |
    |* 17 | INDEX UNIQUE SCAN | PK_GL_USER_REGISTRATIONS | 1 | | 1 (0)| 00:00:01 |
    | 18 | TABLE ACCESS BY INDEX ROWID | GL_USER_LAST_LOGIN | 1 | 18 | 1 (0)| 00:00:01 |
    |* 19 | INDEX UNIQUE SCAN | GL_USER_LAST_LOGIN_PK | 1 | | 1 (0)| 00:00:01 |
    | 20 | TABLE ACCESS BY INDEX ROWID | GL_USER_DISPLAY_NAMES | 1 | 21 | 1 (0)| 00:00:01 |
    |* 21 | INDEX UNIQUE SCAN | PK_GL_USER_DISPLAY_NAMES | 1 | | 1 (0)| 00:00:01 |
    | 22 | TABLE ACCESS BY INDEX ROWID | GL_CUSTOMERS | 1 | 31 | 1 (0)| 00:00:01 |
    |* 23 | INDEX UNIQUE SCAN | PK_GL_CUSTOMERS | 1 | | 1 (0)| 00:00:01 |
    |* 24 | TABLE ACCESS BY INDEX ROWID | GL_USER_ADDRESSES | 1 | 66 | 1 (0)| 00:00:01 |
    |* 25 | INDEX RANGE SCAN | IX_GL_USER_ADDRESSES1 | 1 | | 1 (0)| 00:00:01 |
    | 26 | MAT_VIEW ACCESS BY INDEX ROWID | MVW_USER_FIRST_BUY_DATE | 1 | 18 | 1 (0)| 00:00:01 |
    |* 27 | INDEX RANGE SCAN | MVW_USER_FS_DATE_IDX | 1 | | 1 (0)| 00:00:01 |
    | 28 | MAT_VIEW ACCESS BY INDEX ROWID | MVW_USER_PROMO_CODE_REG | 1 | 17 | 1 (0)| 00:00:01 |
    |* 29 | INDEX RANGE SCAN | MVW_USER_PROMO_CODE_IDX | 1 | | 1 (0)| 00:00:01 |
    | 30 | MAT_VIEW ACCESS BY INDEX ROWID | MVW_USER_LOYALTY_POINTS | 1 | 35 | 1 (0)| 00:00:01 |
    |* 31 | INDEX RANGE SCAN | MVW_USER_LYP_IDX | 1 | | 1 (0)| 00:00:01 |
    | 32 | MAT_VIEW ACCESS BY INDEX ROWID | MVW_USER_BALANCES | 1 | 25 | 1 (0)| 00:00:01 |
    |* 33 | INDEX RANGE SCAN | MVW_USER_BALANCES_IDX | 1 | | 1 (0)| 00:00:01 |
    Predicate Information (identified by operation id):
    6 - access("X"."BASE_REGION"="R"."REGION")
    15 - access("UPM"."BUYER_ID"="P"."BUYER_ID")
    17 - access("X"."USER_ID"="UPM"."USER_ID")
    19 - access("X"."USER_ID"="S3"."USER_ID"(+))
    21 - access("X"."USER_ID"="GUD"."USER_ID"(+))
    23 - access("X"."CUSTOMER_ID"="C"."CUSTOMER_ID"(+))
    24 - filter("Z"."IS_CURRENT"(+)=1)
    25 - access("X"."USER_ID"="Z"."USER_ID"(+))
    27 - access("X"."USER_ID"="G"."USER_ID"(+))
    29 - access("X"."USER_ID"="PR"."USER_ID"(+))
    31 - access("X"."USER_ID"="L"."USER_ID"(+))
    33 - access("X"."USER_ID"="IA"."USER_ID"(+))

    Hi,
    1) What you are saying about nested loops is true about any join (except, of course, cartesian joins): you are taking rows from rowsource A and find matching rows from rowsource B. This doesn't make a join method efficient or inefficient.
    2) The plan you posted does not indicate any performance problem whatsoever. I know you have one, but it's not possible to address it without having any information about it. Trace it, get dbms_xplan.display_cursor dump with rowsource stats, or real-time SQL monitoring report (if your version and license allow it) and post the results here, then we'd be able to help
    3) One efficient way to perform queries of your type (big fact table joined to a bunch of small dimension tables) is star transformation, but there are certain pre-requisites for that (like bitmap indexes on FK constraints) -- please read the documentation on star queries/transformations and see if that is an option for you
    Best regards,
    Nikolay

  • When does oracle use a complete nested loop join?

    Hi!
    Does Oracle Database use a complete nested loop join? I mean, imagine 2 tables without any indexes.. is there any case where for each row in the outer table Oracle does a complete scan in the inner table? I know that this is the original algorithm for the nested loop join, but some data bases prefer to make a temp table to autoindex the inner table and never makes the complete scan in the inner table..
    thanks!!

    user12040235 wrote:
    If the table do not have indexes.. some data bases prefer to scan one time the inner table, to index all values, and than, for every row in the outter loop table, it will do a index search.
    I just like to know oracle does the same thing, or it does the complete scan..If you have two tables without indexes, Oracle may consider scanning one table, extracting the smallest data set it can get away with, and then building a hash table of that data set (rather than creating an in-memory copy with index). At this point Oracle can then do a nested loop join into the in-memory hash table.
    However, this is called a hash join, and the order of tables will appear to be reversed, viz:
    nested loop
        table scan full ABC
        table scan full XYZ
    {code]
    becomeshash join
    table scan full XYZ
    table scan full ABC
    See: http://jonathanlewis.wordpress.com/2010/08/02/joins/ as a starting point if you want to read more on this topic.
    Regards
    Jonathan Lewis                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       

Maybe you are looking for

  • Add Data Channel Headings to Output File

    I have seen several threads discussing how to get names on the column headers when outputting dynamic data to a file and thought I would give a simple example of how to do this. The example generates a Y=2X as 2 data flows and sends them to an X-Y gr

  • Changing Header

    In swing application , there is a cofee symbol in the header , I want to hide that symbol or replace it with another my own symbol , can anybody tell me how can I do this ????? Thanks

  • Can't Publish With Comments

    I recently updated to version 1.1.1, which has allowed me to publish, which I was having problems with before(Go figure!), but no matter how I try, I can publish my site, including my blog and my podcast(Which is full of h264 videos, no audio), but I

  • Displaying photo's vertically from slideshow to appleTV

    When I start a slideshow from photo's on my iphone and airplay to my apple TV, the pictures are mostly sideways, but if I just swipe through photo's they seem to be all displaying just fine. Do I need to configure something on my iphone?

  • API to update or insert records in BPA

    Hi All, We do a regular activity fo updating the BPA lines or inserting in our projects via dataloader. It is very cumbersome as sometimes we need to modify or insert 5k lines. We get the records in a flat file(xls or sometimes in txt format). Can an