Chris Pollett >
Old Classes
> |
HW3 Solutions Page5.3.8 6.4.3 The equation to estimate the number of I/Os for a block nested loop join is: I/Os = B(S) + [B(S)/(M-1)]*B(R). The square brackets mean we round up to the next larger integer. We are given B(S)=B(R)= 10,000. (a) If we want to join S, R in no more than 100,000 I/Os, then we need that 90,000 > [10,000/(M-1)]*10,000. Solving for M and accounting for the fact that we need to round up gives M = 1113 blocks of memory. (b) If we want to join S, R in no more that 25,000 I/Os, then we need that 15,000 > [10,000/(M-1)]*10,000. Dividing by 10,000, notice this time we get 3/2 > [10,000/(M-1)] and the expression on the right should be an integer. So it must be 1, which means 10,000 = M-1, so M = 10,001. Thus, we end up doing essentially the one pass algorithm. (c) Let's consider what happens if we want to join S and R in 15,000 I/Os. Immediately, we should see we are being asked the impossible as R and S together are stored in 20,000 blocks. So to compute the join we need to read at least this many blocks. 7.1.2 (a) OR and NOT rules OR rule: <Condition> ::= <Condition> OR <Condition> NOT rule: <Condition> ::= NOT <Condition> (b) Other Comparisons: LESS THAN <Condition> ::= <Attibute> < <Attibute> LESS THAN EQUAL <Condition> ::= <Attibute> <= <Attibute> GREATER THAN <Condition> ::= <Attibute> > <Attibute> GREATER THAN EQUAL <Condition> ::= <Attibute> >= <Attibute> (c) Parenthesized Conditions: <Condition> ::= (<Condition>) (d) EXISTS Conditions: <Condition> ::= EXISTS (<Condition>) DataGenerator.javaimport java.util.*; import java.io.*; /** This class consists of a bunch of public static methods used to generate row data for the CS157B HW3. Typically one would run this program from the command line with a line like: java DataGenerator start_value num wrap_number txtfile here start_value is the initial value for the A column, num is the number of rows to be generated, wrap_number is the number of rows before which the B column cycles back to 1, and finally, txtfile is the file to write the data to. @author Chris Pollett @version 4.26.2005 */ public class DataGenerator { /** Generates and writes the data file to be used in CS157B HW3. This data consists of numRows many rows each consisting of two columns of int's the first column is a counter starting with value startNum, the second column starts at value 1 and counts mod wrapNumber @param startNum -- initial value for the columnA counter @param numRows -- number of rows of data to create @param wrapNumber -- number of rows of before the columnB value repeats @param file -- file to write data to */ public static void createData( int startNum, int numRows, int wrapNumber, String file) { try { PrintStream outFile = new PrintStream( new FileOutputStream(file)); int columnB = 1; int endNum = startNum + numRows; String row; for(int columnA = startNum; columnA < endNum; columnA++) { row = "" + columnA + " " + columnB; outFile.println(row); columnB = (columnB >= wrapNumber) ? 1 : columnB + 1; } outFile.close(); } catch(IOException ie) { ie.printStackTrace(); } } /** Calls createData with the command line arguments to generate data for Hw3 data @param args - array of command line arguments. */ public static void main (String [] args) { if(args.length < 4) { System.out.println("To use:"); System.out.println("java DataGenerator start_value num wrap_number txtfile"); System.out.println("Here start_value is the initial value for the A column,"); System.out.println("num is how many rows you'd like generated,"); System.out.println("wrap_number is the number of rows before which the B column" + "cycles back to 1,,"); System.out.println("and txtfile is the file you'd like data written to"); } else createData(Integer.parseInt(args[0]), Integer.parseInt(args[1]), Integer.parseInt(args[2]), args[3]); } } Here is a spool of me generating my data: |sigma:hw3:96>java DataGenerator 0 1000 100 R1data.txt |sigma:hw3:97*gt;java DataGenerator 1000 1000 100 R2data.txt |sigma:hw3:98>java DataGenerator 2000 1000 100 R3data.txt |sigma:hw3:99>java DataGenerator 3000 1000 100 R4data.txt |sigma:hw3:100>java DataGenerator 4000 1000 100 R5data.txt CreateTables.sqlThis was a short script I had to create my tables: create table R1 ( A INTEGER, B INTEGER ); create table R2 ( A INTEGER, B INTEGER ); create table R3 ( A INTEGER, B INTEGER ); create table R4 ( A INTEGER, B INTEGER ); create table R5 ( A INTEGER, B INTEGER ); Here is what the session looked like where I ran this: SQL*Plus: Release 10.1.0.3.0 - Production on Tue Apr 26 23:46:53 2005 Copyright (c) 1982, 2004, Oracle. All rights reserved. Enter password: Connected to: Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production SQL> @CreateTables.sql Table created. Table created. Table created. Table created. Table created. SQL> Control FilesHere are the control files I used to bulk load my data: LoadDataR1.ctl LOAD DATA INFILE R1data.txt INTO TABLE R1 FIELDS TERMINATED BY ' ' ( A, B) LoadDataR2.ctl LOAD DATA INFILE R2data.txt INTO TABLE R2 FIELDS TERMINATED BY ' ' ( A, B) LoadDataR3.ctl LOAD DATA INFILE R3data.txt INTO TABLE R3 FIELDS TERMINATED BY ' ' ( A, B) LoadDataR4.ctl LOAD DATA INFILE R4data.txt INTO TABLE R4 FIELDS TERMINATED BY ' ' ( A, B) LoadDataR5.ctl LOAD DATA INFILE R5data.txt INTO TABLE R5 FIELDS TERMINATED BY ' ' ( A, B) Here is a snippet from the session where I did the bulk loading: |sigma:hw3:107>sqlldr pollett_ora control=LoadDataR1.ctl log=LoadR1.log Password: SQL*Loader: Release 8.1.7.0.0 - Production on Wed Apr 27 00:07:06 2005 (c) Copyright 2000 Oracle Corporation. All rights reserved. Commit point reached - logical record count 64 Commit point reached - logical record count 128 Commit point reached - logical record count 192 Commit point reached - logical record count 256 Commit point reached - logical record count 320 Commit point reached - logical record count 384 Commit point reached - logical record count 448 Commit point reached - logical record count 512 Commit point reached - logical record count 576 Commit point reached - logical record count 640 Commit point reached - logical record count 704 Commit point reached - logical record count 768 Commit point reached - logical record count 832 Commit point reached - logical record count 896 Commit point reached - logical record count 960 Commit point reached - logical record count 1000 |sigma:hw3:108>sqlldr pollett_ora control=LoadDataR2.ctl log=LoadR2.log Password: SQL*Loader: Release 8.1.7.0.0 - Production on Wed Apr 27 00:10:21 2005 (c) Copyright 2000 Oracle Corporation. All rights reserved. Commit point reached - logical record count 64 Commit point reached - logical record count 128 Commit point reached - logical record count 192 Commit point reached - logical record count 256 Commit point reached - logical record count 320 Commit point reached - logical record count 384 Commit point reached - logical record count 448 Commit point reached - logical record count 512 Commit point reached - logical record count 576 Commit point reached - logical record count 640 Commit point reached - logical record count 704 Commit point reached - logical record count 768 Commit point reached - logical record count 832 Commit point reached - logical record count 896 Commit point reached - logical record count 960 Commit point reached - logical record count 1000 |sigma:hw3:109>sqlldr pollett_ora control=LoadDataR3.ctl log=LoadR3.log Password: SQL*Loader: Release 8.1.7.0.0 - Production on Wed Apr 27 00:10:32 2005 (c) Copyright 2000 Oracle Corporation. All rights reserved. Commit point reached - logical record count 64 Commit point reached - logical record count 128 Commit point reached - logical record count 192 Commit point reached - logical record count 256 Commit point reached - logical record count 320 Commit point reached - logical record count 384 Commit point reached - logical record count 448 Commit point reached - logical record count 512 Commit point reached - logical record count 576 Commit point reached - logical record count 640 Commit point reached - logical record count 704 Commit point reached - logical record count 768 Commit point reached - logical record count 832 Commit point reached - logical record count 896 Commit point reached - logical record count 960 Commit point reached - logical record count 1000 |sigma:hw3:110>sqlldr pollett_ora control=LoadDataR4.ctl log=LoadR4.log Password: SQL*Loader: Release 8.1.7.0.0 - Production on Wed Apr 27 00:10:44 2005 (c) Copyright 2000 Oracle Corporation. All rights reserved. Commit point reached - logical record count 64 Commit point reached - logical record count 128 Commit point reached - logical record count 192 Commit point reached - logical record count 256 Commit point reached - logical record count 320 Commit point reached - logical record count 384 Commit point reached - logical record count 448 Commit point reached - logical record count 512 Commit point reached - logical record count 576 Commit point reached - logical record count 640 Commit point reached - logical record count 704 Commit point reached - logical record count 768 Commit point reached - logical record count 832 Commit point reached - logical record count 896 Commit point reached - logical record count 960 Commit point reached - logical record count 1000 |sigma:hw3:111>sqlldr pollett_ora control=LoadDataR5.ctl log=LoadR5.log Password: SQL*Loader: Release 8.1.7.0.0 - Production on Wed Apr 27 00:10:54 2005 (c) Copyright 2000 Oracle Corporation. All rights reserved. Commit point reached - logical record count 64 Commit point reached - logical record count 128 Commit point reached - logical record count 192 Commit point reached - logical record count 256 Commit point reached - logical record count 320 Commit point reached - logical record count 384 Commit point reached - logical record count 448 Commit point reached - logical record count 512 Commit point reached - logical record count 576 Commit point reached - logical record count 640 Commit point reached - logical record count 704 Commit point reached - logical record count 768 Commit point reached - logical record count 832 Commit point reached - logical record count 896 Commit point reached - logical record count 960 Commit point reached - logical record count 1000 |sigma:hw3:112> Bulk Load Log FilesLoadR1.log SQL*Loader: Release 8.1.7.0.0 - Production on Wed Apr 27 00:07:06 2005 (c) Copyright 2000 Oracle Corporation. All rights reserved. Control File: LoadDataR1.ctl Data File: R1data.txt Bad File: R1data.bad Discard File: none specified (Allow all discards) Number to load: ALL Number to skip: 0 Errors allowed: 50 Bind array: 64 rows, maximum of 65536 bytes Continuation: none specified Path used: Conventional Table R1, loaded from every logical record. Insert option in effect for this table: INSERT Column Name Position Len Term Encl Datatype ------------------------------ ---------- ----- ---- ---- --------------------- A FIRST * WHT CHARACTER B NEXT * WHT CHARACTER Table R1: 1000 Rows successfully loaded. 0 Rows not loaded due to data errors. 0 Rows not loaded because all WHEN clauses were failed. 0 Rows not loaded because all fields were null. Space allocated for bind array: 33024 bytes(64 rows) Space allocated for memory besides bind array: 0 bytes Total logical records skipped: 0 Total logical records read: 1000 Total logical records rejected: 0 Total logical records discarded: 0 Run began on Wed Apr 27 00:07:06 2005 Run ended on Wed Apr 27 00:07:10 2005 Elapsed time was: 00:00:04.01 CPU time was: 00:00:00.05 LoadR2.log SQL*Loader: Release 8.1.7.0.0 - Production on Wed Apr 27 00:10:21 2005 (c) Copyright 2000 Oracle Corporation. All rights reserved. Control File: LoadDataR2.ctl Data File: R2data.txt Bad File: R2data.bad Discard File: none specified (Allow all discards) Number to load: ALL Number to skip: 0 Errors allowed: 50 Bind array: 64 rows, maximum of 65536 bytes Continuation: none specified Path used: Conventional Table R2, loaded from every logical record. Insert option in effect for this table: INSERT Column Name Position Len Term Encl Datatype ------------------------------ ---------- ----- ---- ---- --------------------- A FIRST * WHT CHARACTER B NEXT * WHT CHARACTER Table R2: 1000 Rows successfully loaded. 0 Rows not loaded due to data errors. 0 Rows not loaded because all WHEN clauses were failed. 0 Rows not loaded because all fields were null. Space allocated for bind array: 33024 bytes(64 rows) Space allocated for memory besides bind array: 0 bytes Total logical records skipped: 0 Total logical records read: 1000 Total logical records rejected: 0 Total logical records discarded: 0 Run began on Wed Apr 27 00:10:21 2005 Run ended on Wed Apr 27 00:10:24 2005 Elapsed time was: 00:00:02.88 CPU time was: 00:00:00.04 LoadR3.log SQL*Loader: Release 8.1.7.0.0 - Production on Wed Apr 27 00:10:32 2005 (c) Copyright 2000 Oracle Corporation. All rights reserved. Control File: LoadDataR3.ctl Data File: R3data.txt Bad File: R3data.bad Discard File: none specified (Allow all discards) Number to load: ALL Number to skip: 0 Errors allowed: 50 Bind array: 64 rows, maximum of 65536 bytes Continuation: none specified Path used: Conventional Table R3, loaded from every logical record. Insert option in effect for this table: INSERT Column Name Position Len Term Encl Datatype ------------------------------ ---------- ----- ---- ---- --------------------- A FIRST * WHT CHARACTER B NEXT * WHT CHARACTER Table R3: 1000 Rows successfully loaded. 0 Rows not loaded due to data errors. 0 Rows not loaded because all WHEN clauses were failed. 0 Rows not loaded because all fields were null. Space allocated for bind array: 33024 bytes(64 rows) Space allocated for memory besides bind array: 0 bytes Total logical records skipped: 0 Total logical records read: 1000 Total logical records rejected: 0 Total logical records discarded: 0 Run began on Wed Apr 27 00:10:32 2005 Run ended on Wed Apr 27 00:10:36 2005 Elapsed time was: 00:00:03.90 CPU time was: 00:00:00.08 LoadR4.log SQL*Loader: Release 8.1.7.0.0 - Production on Wed Apr 27 00:10:44 2005 (c) Copyright 2000 Oracle Corporation. All rights reserved. Control File: LoadDataR4.ctl Data File: R4data.txt Bad File: R4data.bad Discard File: none specified (Allow all discards) Number to load: ALL Number to skip: 0 Errors allowed: 50 Bind array: 64 rows, maximum of 65536 bytes Continuation: none specified Path used: Conventional Table R4, loaded from every logical record. Insert option in effect for this table: INSERT Column Name Position Len Term Encl Datatype ------------------------------ ---------- ----- ---- ---- --------------------- A FIRST * WHT CHARACTER B NEXT * WHT CHARACTER Table R4: 1000 Rows successfully loaded. 0 Rows not loaded due to data errors. 0 Rows not loaded because all WHEN clauses were failed. 0 Rows not loaded because all fields were null. Space allocated for bind array: 33024 bytes(64 rows) Space allocated for memory besides bind array: 0 bytes Total logical records skipped: 0 Total logical records read: 1000 Total logical records rejected: 0 Total logical records discarded: 0 Run began on Wed Apr 27 00:10:44 2005 Run ended on Wed Apr 27 00:10:46 2005 Elapsed time was: 00:00:02.65 CPU time was: 00:00:00.09 LoadR5.log SQL*Loader: Release 8.1.7.0.0 - Production on Wed Apr 27 00:10:54 2005 (c) Copyright 2000 Oracle Corporation. All rights reserved. Control File: LoadDataR5.ctl Data File: R5data.txt Bad File: R5data.bad Discard File: none specified (Allow all discards) Number to load: ALL Number to skip: 0 Errors allowed: 50 Bind array: 64 rows, maximum of 65536 bytes Continuation: none specified Path used: Conventional Table R5, loaded from every logical record. Insert option in effect for this table: INSERT Column Name Position Len Term Encl Datatype ------------------------------ ---------- ----- ---- ---- --------------------- A FIRST * WHT CHARACTER B NEXT * WHT CHARACTER Table R5: 1000 Rows successfully loaded. 0 Rows not loaded due to data errors. 0 Rows not loaded because all WHEN clauses were failed. 0 Rows not loaded because all fields were null. Space allocated for bind array: 33024 bytes(64 rows) Space allocated for memory besides bind array: 0 bytes Total logical records skipped: 0 Total logical records read: 1000 Total logical records rejected: 0 Total logical records discarded: 0 Run began on Wed Apr 27 00:10:54 2005 Run ended on Wed Apr 27 00:10:57 2005 Elapsed time was: 00:00:02.76 CPU time was: 00:00:00.08 Working with EXPLAIN PLANI next used the following script, CreatePlan.sql, to create my plan_table: CREATE TABLE plan_table ( statement_id VARCHAR2(30), timestamp DATE, remarks VARCHAR2(80), operation VARCHAR2(30), options VARCHAR2(30), object_node VARCHAR2(128), object_owner VARCHAR2(30), object_name VARCHAR2(30), object_instance NUMBER, object_type VARCHAR2(30), optimizer VARCHAR2(255), search_columns NUMBER, id NUMBER, parent_id NUMBER, position NUMBER, other LONG ); I ran it like this: SQL> @CreatePlan Table created. SQL> Plan's TestedThe following script, ExplainPlan.sql, contains the plans I tested: /* Get rid of the query buffer */ clear buffer /*Delete old stuff */ delete from plan_table; commit; COL operation FORMAT A20 COL options FORMAT A15 COL object_name FORMAT A20 COL id FORMAT 999999999 COL parent_id FORMAT 999999999 set echo on /* Explaining the Plan1 This plan is roughly what one gets by reading off the HW problem. I tried this next query with and without the NO_CACHE hint (what a hint is, is explained in the comment before Plan 4) and it took about the same time */ EXPLAIN PLAN set statement_id = 'PLAN1' for select * from R1, R2, R3, R4, R5 where R1.B=51 and R1.B=R2.B and R2.B=R3.B and R3.B=R4.B and R4.B=R5.B; select operation, options, object_name, id, parent_id from plan_table where statement_id = 'PLAN1' ; /* Explaining the Plan2. This is essentially a similar plan to plan 1 but table order changed. If all tables are the same size, the order in the from clause seems to be what Oracle uses. */ EXPLAIN PLAN set statement_id = 'PLAN2' for select * from R5, R1, R2, R3, R4 where R1.B=51 and R1.B=R2.B and R2.B=R3.B and R3.B=R4.B and R4.B=R5.B ; select operation, options, object_name, id, parent_id from plan_table where statement_id = 'PLAN2' ; /* Explaining the Plan3 What happens when you use OR's and NOT's in the where clause? Seems Oracle gives you same plan. So it looks like Oracle is swallowing selection into the join. Problem the sort step makes checking the condition fast. */ EXPLAIN PLAN set statement_id = 'PLAN3' for select * from R1,R2, R3,R4, R5 where R1.B=R2.B and R2.B=R3.B and R3.B=R4.B and R4.B=R5.B and not ( R1.B < 51 or R5.B > 51); select operation, options, object_name, id, parent_id from plan_table where statement_id = 'PLAN3' ; /* Explaining the Plan4. In this query views are used to try to force the order of operation Notice I am using the hint to the Oracle Optimizer not to use merge joins (so will get nested loop joins). The comment with the + NO_MERGE is not a comment but an optimizer hint. */ create or replace view T1 as select A, B from R1 where B=51; create or replace view T2 as select A, B from R2 where B=51; create or replace view T3 as select A, B from R3 where B=51; create or replace view T4 as select A, B from R4 where B=51; create or replace view T5 as select A, B from R5 where B=51; EXPLAIN PLAN set statement_id = 'PLAN3' for select /*+ NO_MERGE */ * from T1, T2, T3, T4, T5; select operation, options, object_name, id, parent_id from plan_table where statement_id = 'PLAN4' ; /* Explaining the Plan5 Here's what NO_MERGE looks like on the original query */ EXPLAIN PLAN set statement_id = 'PLAN5' for select /*+NO_MERGE */ * from R1, R2, R3, R4, R5 where R1.B=51 and R1.B=R2.B and R2.B=R3.B and R3.B=R4.B and R4.B=R5.B; select operation, options, object_name, id, parent_id from plan_table where statement_id = 'PLAN5' ; /* Explaining the Plan6 Here's a plan attempting using hash joins. When I did explain plan on it seemed Oracle Left it as a sort-merge join */ EXPLAIN PLAN set statement_id = 'PLAN6' for select /*+USE_HASH */ * from R1,R2, R3,R4, R5 where R1.B=51 and R1.B=R2.B and R2.B=R3.B and R3.B=R4.B and R4.B=R5.B ; select operation, options, object_name, id, parent_id from plan_table where statement_id = 'PLAN6' ; /* Explaining the Plan7 Here's what NO_REWRITE looks like on the view based query */ create or replace view S1 as select R1.A A1, R1.B B1 , R2.A A2, R2.b B2 from R1, R2 where R1.B=R2.B; create or replace view S2 as select /*+NO_MERGE USE_NL */ S1.*, R3.A A3, R3.B B3 from S1, R3 where S1.B1=R3.B; create or replace view S3 as select /*+NO_MERGE USE_NL */ S2.*, R4.A A4, R4.B B4 from S2, R4 where S2.B1=R4.B; create or replace view S4 as select /*+NO_MERGE USE_NL */ S3.*, R5.A A5, R5.B B5 from S3, R5 where S3.B1=R5.B; EXPLAIN PLAN set statement_id = 'PLAN7' for select * from S4 where B5=51; select operation, options, object_name, id, parent_id from plan_table where statement_id = 'PLAN7' ; /* Miscellaneous Plans Testing what explain plan lists */ /* Explaining the Plan8 Here' s a plan to see what the select statement in explain plan means. I have nothing in the where clause, but there is still a select statement. This and further testing seemed to indicate select statement is probably just used to indicate the root of the query. */ EXPLAIN PLAN set statement_id = 'PLAN8' for select * from R1; select operation, options, object_name, id, parent_id from plan_table where statement_id = 'PLAN8' ; /* Explaining the Plan9 Here' s another plan that seems to illustrate what I was saying under Plan 8. It returns only on select statement as in Plan 8. Another thing it shows is that things in the where class do not really seem to show up in the plan. They seemed to be swallowed up in the TABLE ACCESS lines. */ EXPLAIN PLAN set statement_id = 'PLAN9' for select T.A from (select A,B from R1 where B=51) T, (select A,B from R2 where B=51) S; select operation, options, object_name, id, parent_id from plan_table where statement_id = 'PLAN9' ; set echo off Script resultsHere is the result of running my script: SQL> SQL> set echo off SQL> @ExplainPLan SP2-0103: Nothing in SQL buffer to run. 107 rows deleted. Commit complete. SQL> SQL> /* Explaining the Plan1 SQL> SQL> This plan is roughly what one gets by reading SQL> off the HW problem. I tried this next query SQL> with and without the NO_CACHE hint (what a SQL> hint is, is explained in the comment before Plan 4) SQL> and it took about the same time SQL> */ SQL> EXPLAIN PLAN set statement_id = 'PLAN1' for 2 select * 3 from R1, R2, R3, R4, R5 4 where 5 R1.B=51 and 6 R1.B=R2.B and R2.B=R3.B and 7 R3.B=R4.B and R4.B=R5.B; Explained. SQL> SQL> select operation, options, object_name, id, parent_id 2 from plan_table 3 where statement_id = 'PLAN1' ; OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- MERGE JOIN 1 0 MERGE JOIN 2 1 MERGE JOIN 3 2 MERGE JOIN 4 3 SORT JOIN 5 4 TABLE ACCESS FULL R5 6 5 SORT JOIN 7 4 TABLE ACCESS FULL R4 8 7 SORT JOIN 9 3 TABLE ACCESS FULL R3 10 9 SORT JOIN 11 2 OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- TABLE ACCESS FULL R2 12 11 SORT JOIN 13 1 TABLE ACCESS FULL R1 14 13 SELECT STATEMENT 0 15 rows selected. SQL> SQL> SQL> /* Explaining the Plan2. SQL> This is essentially a similar plan to plan 1 but table order changed. SQL> If all tables are the same size, the order in the from clause seems SQL> to be what Oracle uses. SQL> */ SQL> SQL> EXPLAIN PLAN set statement_id = 'PLAN2' for 2 select * 3 from R5, R1, R2, R3, R4 4 where 5 R1.B=51 and R1.B=R2.B and R2.B=R3.B and 6 R3.B=R4.B and R4.B=R5.B ; Explained. SQL> SQL> select operation, options, object_name, id, parent_id 2 from plan_table 3 where statement_id = 'PLAN2' ; OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- SELECT STATEMENT 0 MERGE JOIN 1 0 MERGE JOIN 2 1 MERGE JOIN 3 2 MERGE JOIN 4 3 SORT JOIN 5 4 TABLE ACCESS FULL R4 6 5 SORT JOIN 7 4 TABLE ACCESS FULL R3 8 7 SORT JOIN 9 3 TABLE ACCESS FULL R2 10 9 OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- SORT JOIN 11 2 TABLE ACCESS FULL R1 12 11 SORT JOIN 13 1 TABLE ACCESS FULL R5 14 13 15 rows selected. SQL> SQL> /* Explaining the Plan3 SQL> SQL> What happens when you use OR's and NOT's in the where clause? SQL> Seems Oracle gives you same plan. So it looks like Oracle is swallowing SQL> selection into the join. Problem the sort step makes checking the condition fast. SQL> */ SQL> SQL> SQL> EXPLAIN PLAN set statement_id = 'PLAN3' for 2 select * 3 from R1,R2, R3,R4, R5 4 where 5 R1.B=R2.B and R2.B=R3.B and 6 R3.B=R4.B and R4.B=R5.B and not ( R1.B < 51 or R5.B > 51); Explained. SQL> SQL> select operation, options, object_name, id, parent_id 2 from plan_table 3 where statement_id = 'PLAN3' ; OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- SELECT STATEMENT 0 MERGE JOIN 1 0 MERGE JOIN 2 1 MERGE JOIN 3 2 MERGE JOIN 4 3 SORT JOIN 5 4 TABLE ACCESS FULL R5 6 5 SORT JOIN 7 4 TABLE ACCESS FULL R4 8 7 SORT JOIN 9 3 TABLE ACCESS FULL R3 10 9 OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- SORT JOIN 11 2 TABLE ACCESS FULL R2 12 11 SORT JOIN 13 1 TABLE ACCESS FULL R1 14 13 15 rows selected. SQL> SQL> SQL> /* Explaining the Plan4. SQL> In this query views are used to try to force the order of operation SQL> Notice I am using the hint to the Oracle Optimizer not to use merge SQL> joins (so will get nested loop joins). The comment with the + NO_MERGE SQL> is not a comment but an optimizer hint. SQL> */ SQL> SQL> create or replace view T1 as 2 select A, B from R1 where B=51; View created. SQL> SQL> create or replace view T2 as 2 select A, B from R2 where B=51; View created. SQL> SQL> create or replace view T3 as 2 select A, B from R3 where B=51; View created. SQL> SQL> create or replace view T4 as 2 select A, B from R4 where B=51; View created. SQL> SQL> create or replace view T5 as 2 select A, B from R5 where B=51; View created. SQL> SQL> SQL> EXPLAIN PLAN set statement_id = 'PLAN3' for 2 select /*+ NO_MERGE */ * 3 from 4 T1, T2, T3, T4, T5; Explained. SQL> SQL> select operation, options, object_name, id, parent_id 2 from plan_table 3 where statement_id = 'PLAN4' ; no rows selected SQL> SQL> /* Explaining the Plan5 SQL> SQL> Here's what NO_MERGE looks like on the original query SQL> */ SQL> EXPLAIN PLAN set statement_id = 'PLAN5' for 2 select /*+NO_MERGE */ * 3 from R1, R2, R3, R4, R5 4 where 5 R1.B=51 and 6 R1.B=R2.B and R2.B=R3.B and 7 R3.B=R4.B and R4.B=R5.B; Explained. SQL> SQL> select operation, options, object_name, id, parent_id 2 from plan_table 3 where statement_id = 'PLAN5' ; OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- TABLE ACCESS FULL R1 5 4 SORT JOIN 6 4 TABLE ACCESS FULL R2 7 6 SORT JOIN 8 3 TABLE ACCESS FULL R3 9 8 SORT JOIN 10 2 TABLE ACCESS FULL R4 11 10 SORT JOIN 12 1 TABLE ACCESS FULL R5 13 12 SELECT STATEMENT 0 MERGE JOIN CARTESIAN 1 0 OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- MERGE JOIN CARTESIAN 2 1 MERGE JOIN CARTESIAN 3 2 MERGE JOIN CARTESIAN 4 3 14 rows selected. SQL> SQL> /* Explaining the Plan6 SQL> SQL> Here's a plan attempting using hash joins. SQL> When I did explain plan on it seemed Oracle SQL> Left it as a sort-merge join SQL> */ SQL> SQL> SQL> EXPLAIN PLAN set statement_id = 'PLAN6' for 2 select /*+USE_HASH */ * 3 from R1,R2, R3,R4, R5 4 where 5 R1.B=51 and R1.B=R2.B and R2.B=R3.B and 6 R3.B=R4.B and R4.B=R5.B ; Explained. SQL> SQL> select operation, options, object_name, id, parent_id 2 from plan_table 3 where statement_id = 'PLAN6' ; OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- SELECT STATEMENT 0 MERGE JOIN 1 0 MERGE JOIN 2 1 MERGE JOIN 3 2 MERGE JOIN 4 3 SORT JOIN 5 4 TABLE ACCESS FULL R5 6 5 SORT JOIN 7 4 TABLE ACCESS FULL R4 8 7 SORT JOIN 9 3 TABLE ACCESS FULL R3 10 9 OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- SORT JOIN 11 2 TABLE ACCESS FULL R2 12 11 SORT JOIN 13 1 TABLE ACCESS FULL R1 14 13 15 rows selected. SQL> SQL> /* Explaining the Plan7 SQL> SQL> Here's what NO_REWRITE looks like on the view SQL> based query SQL> */ SQL> create or replace view S1 as 2 select R1.A A1, R1.B B1 , R2.A A2, R2.b B2 from R1, R2 where R1.B=R2.B; View created. SQL> SQL> create or replace view S2 as 2 select /*+NO_MERGE USE_NL */ S1.*, R3.A A3, R3.B B3 from S1, R3 where S1.B1=R3.B; View created. SQL> SQL> create or replace view S3 as 2 select /*+NO_MERGE USE_NL */ S2.*, R4.A A4, R4.B B4 from S2, R4 where S2.B1=R4.B; View created. SQL> SQL> create or replace view S4 as 2 select /*+NO_MERGE USE_NL */ S3.*, R5.A A5, R5.B B5 from S3, R5 where S3.B1=R5.B; View created. SQL> SQL> EXPLAIN PLAN set statement_id = 'PLAN7' for 2 select * from S4 where B5=51; Explained. SQL> SQL> select operation, options, object_name, id, parent_id 2 from plan_table 3 where statement_id = 'PLAN7' ; OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- SELECT STATEMENT 0 VIEW S4 1 0 MERGE JOIN CARTESIAN 2 1 TABLE ACCESS FULL R5 3 2 SORT JOIN 4 2 VIEW S3 5 4 MERGE JOIN CARTESIAN 6 5 TABLE ACCESS FULL R4 7 6 SORT JOIN 8 6 VIEW S2 9 8 MERGE JOIN CARTESIAN 10 9 OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- MERGE JOIN CARTESIAN 11 10 TABLE ACCESS FULL R1 12 11 SORT JOIN 13 11 TABLE ACCESS FULL R2 14 13 SORT JOIN 15 10 TABLE ACCESS FULL R3 16 15 17 rows selected. SQL> SQL> SQL> SQL> /* SQL> Miscellaneous Plans Testing what explain plan lists SQL> */ SQL> SQL> /* Explaining the Plan8 SQL> SQL> Here' s a plan to see what the select statement in explain plan means. SQL> I have nothing in the where clause, but there is still a select statement. SQL> This and further testing seemed to indicate SQL> select statement is probably just used to indicate the root of the query. SQL> */ SQL> EXPLAIN PLAN set statement_id = 'PLAN8' for 2 select * 3 from R1; Explained. SQL> SQL> select operation, options, object_name, id, parent_id 2 from plan_table 3 where statement_id = 'PLAN8' ; OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- SELECT STATEMENT 0 TABLE ACCESS FULL R1 1 0 SQL> SQL> SQL> /* Explaining the Plan8 SQL> SQL> Here' s a plan to see what the select statement in explain plan means. SQL> I have nothing in the where clause, but there is still a select statement. SQL> So select_statement is probably just used to indicate projections. SQL> */ SQL> EXPLAIN PLAN set statement_id = 'PLAN9' for 2 select T.A 3 from 4 (select A,B 5 from R1 6 where B=51) T, 7 (select A,B 8 from R2 9 where B=51) S; Explained. SQL> SQL> select operation, options, object_name, id, parent_id 2 from plan_table 3 where statement_id = 'PLAN9' ; OPERATION OPTIONS OBJECT_NAME ID PARENT_ID -------------------- --------------- -------------------- ---------- ---------- SELECT STATEMENT 0 NESTED LOOPS 1 0 TABLE ACCESS FULL R2 2 1 TABLE ACCESS FULL R1 3 1 SQL> SQL> set echo off SQL> Further Experiments and ConclusionExperiments 1,2,4,5,7 all yield distinct plans according to Oracle so I do have five distinct plans for the query. To figure out what the tree is for each plan look at a given explain plan result table, make a tree whose vertices are all the distinct id's (labelled also with the operation are the start of the row) and have edges between id's if one is the parent_id of the other. In addition to the experiments in my script, I tried executing the actual queries listed above from the SQL prompt. That is, I ran commands like: SQL> edit Wrote file afiedt.buf 1 select * 2 from R1, R2, R3, R4, R5 3 where 4 R1.B=51 and 5 R1.B=R2.B and R2.B=R3.B and 6* R3.B=R4.B and R4.B=R5.B SQL> / When I did this for each of experiments 1-7, I got 100,000 rows returned. Oracle took in each case about twenty minutes. So which was the better query? My guess is that the actual time was dominated by the time to display the results to the screen. I noticed for each query a lag of 2-3 seconds before the first result was returned. Measuring this more accurately would tell us which plan was better. Our accounts on Oracle 8 don't allow us to use TKPROF or even do: alter system set timed_statistics=true; so it is a little hard to time things from the SQLPLUS prompt. Another way to do this is I suppose to write a little Java program and time that. This, however, was beyond the scope of what I intended for the homework. In any event, the main interesting thing about all these experiments, is that Oracle seems to implicitly swallow selection operations into the table accesses at the leaves. This is what experiments 8 and 9 illustrate. The optimizer is not easily fooled in doing this as Experiment 3 illustrates. Finally, Oracle does seem to provide good facilities for controlling join order and and join type and these can be controlled by the use of hints as done above in Experiment 4 and 5. |