<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Engin ÖZER&#039;s Blog</title>
	<atom:link href="http://enginozer.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://enginozer.wordpress.com</link>
	<description>Knowledge = Power</description>
	<lastBuildDate>Wed, 07 Sep 2011 07:35:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='enginozer.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Engin ÖZER&#039;s Blog</title>
		<link>http://enginozer.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://enginozer.wordpress.com/osd.xml" title="Engin ÖZER&#039;s Blog" />
	<atom:link rel='hub' href='http://enginozer.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Deneme</title>
		<link>http://enginozer.wordpress.com/2011/07/13/online-consultancy/</link>
		<comments>http://enginozer.wordpress.com/2011/07/13/online-consultancy/#comments</comments>
		<pubDate>Wed, 13 Jul 2011 19:26:39 +0000</pubDate>
		<dc:creator>Engin ÖZER</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://enginozer.wordpress.com/?p=108</guid>
		<description><![CDATA[Deneme<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=108&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Deneme</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/enginozer.wordpress.com/108/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/enginozer.wordpress.com/108/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/enginozer.wordpress.com/108/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/enginozer.wordpress.com/108/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/enginozer.wordpress.com/108/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/enginozer.wordpress.com/108/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/enginozer.wordpress.com/108/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/enginozer.wordpress.com/108/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/enginozer.wordpress.com/108/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/enginozer.wordpress.com/108/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/enginozer.wordpress.com/108/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/enginozer.wordpress.com/108/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/enginozer.wordpress.com/108/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/enginozer.wordpress.com/108/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=108&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://enginozer.wordpress.com/2011/07/13/online-consultancy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/d115faa5b1011c2837cb2de5b694342f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">enginozer</media:title>
		</media:content>
	</item>
		<item>
		<title>Oracle Cursor / Sql Processing</title>
		<link>http://enginozer.wordpress.com/2011/04/01/oracle-cursor-sql-processing/</link>
		<comments>http://enginozer.wordpress.com/2011/04/01/oracle-cursor-sql-processing/#comments</comments>
		<pubDate>Fri, 01 Apr 2011 07:50:34 +0000</pubDate>
		<dc:creator>Engin ÖZER</dc:creator>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://enginozer.wordpress.com/?p=103</guid>
		<description><![CDATA[Cursor/SQL Processing Here are some scripts related to Cursor/SQL Processing . Disk Intensive SQL SQL WITH MOST DISK READ NOTES: Username &#8211; Name of the user Disk Reads &#8211; Total number of disk reads for this statement Executions &#8211; Total number of times this statement has been executed Reads/Execs &#8211; Number of reads per execution [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=103&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<h2>Cursor/SQL Processing</h2>
<p>Here are some scripts related to Cursor/SQL Processing .</p>
<h2>Disk Intensive SQL</h2>
<p><strong>SQL WITH MOST DISK READ NOTES:</strong></p>
<div>
<li><strong>Username</strong> &#8211; Name of the user</li>
<li><strong>Disk Reads</strong> &#8211; Total number of disk reads for this statement</li>
<li><strong>Executions</strong> &#8211; Total number of times this statement has been executed</li>
<li><strong>Reads/Execs</strong> &#8211; Number of reads per execution</li>
<li><strong>SQL Text</strong> &#8211; Text of the SQL statement requiring the cursor, or the PL/SQL anonymous code<br />
<blockquote>
<pre>select 	a.USERNAME,
	DISK_READS,
	EXECUTIONS,
	round(DISK_READS / decode(EXECUTIONS, 0, 1, EXECUTIONS)) "Reads/Execs",
	SQL_TEXT
from 	dba_users a, v$session, v$sqlarea
where 	PARSING_USER_ID=USER_ID
and 	ADDRESS=SQL_ADDRESS(+)
and 	DISK_READS &gt; 10000
order  	by DISK_READS desc, EXECUTIONS desc</pre>
</blockquote>
<h2>Buffer Intensive SQL</h2>
<p><strong>SQL WITH MOST BUFFER SCAN NOTES:</strong></li>
<li><strong>Username</strong> &#8211; Name of the user</li>
<li><strong>Buffer Gets</strong> &#8211; Total number of buffer gets for this statement</li>
<li><strong>Executions</strong> &#8211; Total number of times this statment has been executed</li>
<li><strong>Gets/Execs</strong> &#8211; Number of buffer gets per execution</li>
<li><strong>SQL Text</strong> &#8211; Text of the SQL statement requiring the cursor, or the PL/SQL anonymous code<br />
<blockquote>
<pre>select	EXECUTIONS,
	BUFFER_GETS,
	round(DISK_READS / decode(EXECUTIONS, 0, 1, EXECUTIONS) / 400,2) "Gets/Execs",
	SQL_TEXT
from 	v$sqlarea
where  	BUFFER_GETS / decode(EXECUTIONS,0,1, EXECUTIONS) / 400 &gt; 10
order  	by EXECUTIONS desc</pre>
</blockquote>
<h2>Buffer SQL w/ Most Loads</h2>
<p><strong>SQL WITH MOST LOAD NOTES:</strong></li>
<li><strong>Loads</strong> &#8211; Number of times the cursor has been loaded after the body of the cursor has been aged out of the cache while the text of the SQL statement remained in it, or after the cursor is invalidated</li>
<li><strong>First Load Time</strong> &#8211; Time at which the cursor was first loaded into the SGA</li>
<li><strong>Sorts</strong> &#8211; Number of sorts performed by the SQL statement</li>
<li><strong>SQL Text</strong> &#8211; Text of the SQL statement requiring the cursor, or the PL/SQL anonymous code<br />
<blockquote>
<pre>select 	LOADS,
	FIRST_LOAD_TIME,
	SORTS,
	SQL_TEXT
from 	v$sqlarea
where  	LOADS &gt; 50
order  	by EXECUTIONS desc</pre>
</blockquote>
<h2>Open Cursors By User</h2>
<p><strong>OPEN CURSORS BY USER NOTES:</strong></li>
<li><strong>Username</strong> &#8211; Name of user</li>
<li><strong>SQL Text</strong> &#8211; Text of the SQL statement requiring the cursor, or the PL/SQL anonymous code<br />
<blockquote>
<pre>select	nvl(USERNAME,'ORACLE PROC')||'('||s.SID||')' username,
	SQL_TEXT
from 	v$open_cursor oc,
	v$session s
where 	s.SADDR = oc.SADDR
order 	by 1</pre>
</blockquote>
<h2>Running Cursors By User</h2>
<p><strong>RUNNING CURSORS BY USER NOTES:</strong></li>
<li><strong>Username</strong> &#8211; Name of user</li>
<li><strong>SQL Text</strong> &#8211; Text of the SQL statement requiring the cursor, or the PL/SQL anonymous code<br />
<blockquote>
<pre>select	nvl(USERNAME,'ORACLE PROC')||'('||s.SID||')' username,
	SQL_TEXT
from 	v$open_cursor oc, v$session s
where 	s.SQL_ADDRESS = oc.ADDRESS
and 	s.SQL_HASH_VALUE = oc.HASH_VALUE
order 	by 1</pre>
</blockquote>
<h2>LR Open Cursors</h2>
<p><strong>OPEN CURSORS WITH LOW HIT RATIO NOTES:</strong></li>
<li><strong>Username</strong> &#8211; Name of user</li>
<li><strong>SQL Text</strong> &#8211; Text of the SQL statement requiring the cursor, or the PL/SQL anonymous code<br />
<blockquote>
<pre>select	nvl(se0.USERNAME,'ORACLE PROC')||'('||se0.SID||')' username,
	SQL_TEXT
from	v$open_cursor oc0, v$session se0
where 	se0.SADDR = oc0.SADDR
and	se0.USERNAME != 'SYS'
and    	60 &lt; (
		select 	"Hit Ratio"
		from 	(
select nvl(se.USERNAME,'ORACLE PROC')||'('|| se.SID||')' "User Session",
	sum(decode(NAME, 'consistent gets',value, 0))  "Consistent Gets",
	sum(decode(NAME, 'db block gets',value, 0))  "DB Block Gets",
	sum(decode(NAME, 'physical reads',value, 0))  "Physical Reads",
	(
	(sum(decode(NAME, 'consistent gets',value, 0)) +
	 sum(decode(NAME, 'db block gets',value, 0)) -
	 sum(decode(NAME, 'physical reads',value, 0)))
	/
	(sum(decode(NAME, 'consistent gets',value, 0)) +
	 sum(decode(NAME, 'db block gets',value, 0))) * 100)
	 "Hit Ratio"
from 	v$sesstat ss, v$statname sn, v$session se
where 	ss.SID = se.SID
and	sn.STATISTIC# = ss.STATISTIC#
and 	VALUE != 0
and 	sn.NAME in ('db block gets', 'consistent gets', 'physical reads')
group 	by se.USERNAME, se.SID
) XX
		where 	nvl(se0.USERNAME,'ORACLE PROC')||'('||se0.SID||')' = "User Session")
order 	by nvl(se0.USERNAME,'ORACLE'), se0.SID</pre>
</blockquote>
<h2>LR Running Cursors</h2>
<p><strong>RUNNING CURSORS WITH LOW HIT RATIO NOTES:</strong></li>
<li><strong>Username</strong> &#8211; Name of user</li>
<li><strong>SQL Text</strong> &#8211; Text of the SQL statement requiring the cursor, or the PL/SQL anonymous code<br />
<blockquote>
<pre>select	nvl(se0.USERNAME,'ORACLE PROC')||'('|| se0.SID||'),
	SQL_TEXT
from 	v$open_cursor oc0, v$session se0
where  	se0.SQL_ADDRESS = oc0.ADDRESS
and 	se0.SQL_HASH_VALUE = oc0.HASH_VALUE
and	se0.username != 'SYS'
and    	60 &gt; (
	select 	"Hit Ratio"
	from (
select nvl(se.USERNAME,'ORACLE PROC')||'('|| se.SID||')' "User Session",
	sum(decode(NAME, 'consistent gets',value, 0))  "Consistent Gets",
	sum(decode(NAME, 'db block gets',value, 0))  "DB Block Gets",
	sum(decode(NAME, 'physical reads',value, 0))  "Physical Reads",
		(
		(sum(decode(NAME, 'consistent gets',value, 0)) +
		 sum(decode(NAME, 'db block gets',value, 0)) -
		 sum(decode(NAME, 'physical reads',value, 0)))
		/
		(sum(decode(NAME, 'consistent gets',value, 0)) +
		 sum(decode(NAME, 'db block gets',value, 0))) * 100) "Hit Ratio"
from 	v$sesstat ss, v$statname sn, v$session se
where 	ss.SID = se.SID
and	sn.STATISTIC# = ss.STATISTIC#
and 	VALUE != 0
and 	sn.NAME in ('db block gets', 'consistent gets', 'physical reads')
group 	by se.USERNAME, se.SID
)
        where 	nvl(se0.username,'ORACLE PROC')||'('||se0.sid||')' = "User Session")
order 	by nvl(se0.username,'ORACLE'), se0.sid</pre>
</blockquote>
<h2>LR Objects Access</h2>
<p><strong>OBJECTS BEING USED BY USERS WITH LOW HIT RATIO NOTES:</strong></li>
<li><strong>Username</strong> &#8211; Name of the user</li>
<li><strong>Object Owner</strong> &#8211; Owner of the object</li>
<li><strong>Object</strong> &#8211; Name of the object<br />
<blockquote>
<pre>select	nvl(se0.USERNAME,'ORACLE PROC')||'('|| se0.SID||')' username,
	OWNER,
	OBJECT
from 	v$access ac, v$session se0
where 	ac.SID    = se0.SID
and  	ac.TYPE   = 'TABLE'
and 	60 &lt; (
	select 	"Hit Ratio"
	from
(
select nvl(se.USERNAME,'ORACLE PROC')||'('|| se.SID||')' "User Session",
	sum(decode(NAME, 'consistent gets',value, 0))  "Consistent Gets",
	sum(decode(NAME, 'db block gets',value, 0))  "DB Block Gets",
	sum(decode(NAME, 'physical reads',value, 0))  "Physical Reads",
	(
		(sum(decode(NAME, 'consistent gets',value, 0)) +
		 sum(decode(NAME, 'db block gets',value, 0)) -
		 sum(decode(NAME, 'physical reads',value, 0)))
		/
		(sum(decode(NAME, 'consistent gets',value, 0)) +
		 sum(decode(NAME, 'db block gets',value, 0))) * 100) "Hit Ratio"
from 	v$sesstat ss,
	v$statname sn,
	v$session se
where 	ss.SID = se.SID
and	sn.STATISTIC# = ss.STATISTIC#
and 	VALUE != 0
and 	sn.NAME in ('db block gets', 'consistent gets', 'physical reads')
group 	by se.USERNAME, se.SID
)
	where 	nvl(se0.USERNAME,'ORACLE PROC')||'('|| se0.SID||')' = "User Session")
order 	by USERNAME,se0.SID,OWNER</pre>
</blockquote>
</li>
</div>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/enginozer.wordpress.com/103/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/enginozer.wordpress.com/103/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/enginozer.wordpress.com/103/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/enginozer.wordpress.com/103/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/enginozer.wordpress.com/103/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/enginozer.wordpress.com/103/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/enginozer.wordpress.com/103/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/enginozer.wordpress.com/103/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/enginozer.wordpress.com/103/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/enginozer.wordpress.com/103/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/enginozer.wordpress.com/103/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/enginozer.wordpress.com/103/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/enginozer.wordpress.com/103/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/enginozer.wordpress.com/103/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=103&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://enginozer.wordpress.com/2011/04/01/oracle-cursor-sql-processing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/d115faa5b1011c2837cb2de5b694342f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">enginozer</media:title>
		</media:content>
	</item>
		<item>
		<title>QP Price List Import</title>
		<link>http://enginozer.wordpress.com/2011/01/11/qp-price-list-import/</link>
		<comments>http://enginozer.wordpress.com/2011/01/11/qp-price-list-import/#comments</comments>
		<pubDate>Tue, 11 Jan 2011 12:45:09 +0000</pubDate>
		<dc:creator>Engin ÖZER</dc:creator>
				<category><![CDATA[EBS Example Scripts]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://enginozer.wordpress.com/?p=98</guid>
		<description><![CDATA[Here is an example code of QP price list import. procedure price_list_import(errbuf out varchar2, retcode out varchar2, p_price_list_name in varchar2, p_price_list_desc in varchar2, p_line_type_code in varchar2 default &#8216;PRL&#8217;, p_currency_code in varchar2 default &#8216;USD&#8217;, p_aritmetic_operator in varchar2 default &#8216;UNIT_PRICE&#8217;, p_qualifier_context in varchar2 default &#8216;PO_SUPPLIER&#8217;, p_qualifier_attribute in varchar2 default &#8216;QUALIFIER_ATTRIBUTE1&#8242;, p_comparison_operator_code in varchar2 default &#8216;=&#8217;, p_qualifier_precedence in [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=98&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Here is an example code of QP price list import.</p>
<p>procedure price_list_import(errbuf out varchar2,<br />
retcode out varchar2,<br />
p_price_list_name in varchar2,<br />
p_price_list_desc in varchar2,<br />
p_line_type_code in varchar2 default &#8216;PRL&#8217;,<br />
p_currency_code in varchar2 default &#8216;USD&#8217;,<br />
p_aritmetic_operator in varchar2 default &#8216;UNIT_PRICE&#8217;,<br />
p_qualifier_context in varchar2 default &#8216;PO_SUPPLIER&#8217;,<br />
p_qualifier_attribute in varchar2 default &#8216;QUALIFIER_ATTRIBUTE1&#8242;,<br />
p_comparison_operator_code in varchar2 default &#8216;=&#8217;,<br />
p_qualifier_precedence in number default 3<br />
)<br />
is<br />
gpr_return_status             varchar2 (1) := null;<br />
gpr_msg_count                 number := 0;<br />
gpr_msg_data                  varchar2 (2000);<br />
gpr_price_list_rec            qp_price_list_pub.price_list_rec_type;<br />
gpr_price_list_val_rec        qp_price_list_pub.price_list_val_rec_type;<br />
gpr_price_list_line_tbl       qp_price_list_pub.price_list_line_tbl_type;<br />
gpr_price_list_line_val_tbl   qp_price_list_pub.price_list_line_val_tbl_type;<br />
gpr_qualifiers_tbl            qp_qualifier_rules_pub.qualifiers_tbl_type;<br />
gpr_qualifiers_val_tbl        qp_qualifier_rules_pub.qualifiers_val_tbl_type;<br />
gpr_pricing_attr_tbl          qp_price_list_pub.pricing_attr_tbl_type;<br />
gpr_pricing_attr_val_tbl      qp_price_list_pub.pricing_attr_val_tbl_type;<br />
ppr_price_list_rec            qp_price_list_pub.price_list_rec_type;<br />
ppr_price_list_val_rec        qp_price_list_pub.price_list_val_rec_type;<br />
ppr_price_list_line_tbl       qp_price_list_pub.price_list_line_tbl_type;<br />
ppr_price_list_line_val_tbl   qp_price_list_pub.price_list_line_val_tbl_type;<br />
ppr_qualifiers_tbl            qp_qualifier_rules_pub.qualifiers_tbl_type;<br />
ppr_qualifiers_val_tbl        qp_qualifier_rules_pub.qualifiers_val_tbl_type;<br />
ppr_pricing_attr_tbl          qp_price_list_pub.pricing_attr_tbl_type;<br />
ppr_pricing_attr_val_tbl      qp_price_list_pub.pricing_attr_val_tbl_type;<br />
k number := 1;<br />
j number := 1;<br />
l_current_inventory_item_id number;<br />
l_current_uom_code           mtl_system_items_b.primary_uom_code%type;<br />
l_current_vendor_id            number;<br />
l_qual_group_num              number := 1;<br />
l_qp_line_id                       number;<br />
l_line_index                       number := 1;</p>
<p>cursor c_lines<br />
is</p>
<p>select xeql.*<br />
from xxtg_eam_qp_lines_tmp xeql<br />
where list_line_id = -1<br />
&#8211;and inventory_item_id is null<br />
and nvl(success_flag,&#8217;N') = &#8216;N&#8217;<br />
and segment1 in (select segment1<br />
from mtl_system_items_b);</p>
<p>cursor c_qualifier<br />
is</p>
<p>select xeql.*<br />
from xxtg_eam_qp_qualifier_vals_tmp xeql<br />
where list_line_id is null<br />
and nvl(success_flag,&#8217;N') = &#8216;N&#8217;<br />
&#8211;and qualifier_value is null<br />
and qualifier in (select vendor_name<br />
from ap_suppliers);</p>
<p>begin<br />
&#8211;dbms_output.put_line(&#8216;after get price list &#8216;);<br />
/* set the list_header_id to g_miss_num */<br />
gpr_price_list_rec.operation := qp_globals.g_opr_create;<br />
gpr_price_list_rec.list_header_id := fnd_api.g_miss_num;<br />
gpr_price_list_rec.name := p_price_list_name;<br />
gpr_price_list_rec.list_type_code := p_line_type_code;<br />
gpr_price_list_rec.description := p_price_list_desc;<br />
gpr_price_list_rec.currency_code := p_currency_code;</p>
<p>&#8211;looping at price list lines. for each line an attribute is created.<br />
for r_lines in c_lines<br />
loop</p>
<p>begin<br />
select inventory_item_id,<br />
primary_uom_code<br />
into l_current_inventory_item_id,<br />
l_current_uom_code<br />
from mtl_system_items_b msib<br />
where msib.segment1 = r_lines.segment1<br />
and rownum = 1;<br />
exception<br />
when no_data_found then<br />
l_current_inventory_item_id := null;<br />
continue;<br />
end;<br />
&#8211;QP_LIST_LINES_s</p>
<p>select qp_list_lines_s.nextval<br />
into l_qp_line_id<br />
from dual;</p>
<p>gpr_price_list_line_tbl (k).list_line_id := l_qp_line_id;<br />
gpr_price_list_line_tbl (k).operation := qp_globals.g_opr_create;<br />
gpr_price_list_line_tbl(k).list_line_id := fnd_api.g_miss_num;<br />
gpr_price_list_line_tbl (k).list_line_type_code := &#8216;PLL&#8217;;<br />
gpr_price_list_line_tbl(k).inventory_item_id := l_current_inventory_item_id;<br />
gpr_price_list_line_tbl (k).operand := r_lines.price;<br />
gpr_price_list_line_tbl (k).arithmetic_operator := p_aritmetic_operator;</p>
<p>dbms_output.put_line(&#8216;attribute &#8216;);<br />
gpr_pricing_attr_tbl (k).list_line_id := l_qp_line_id;<br />
gpr_pricing_attr_tbl (k).operation := qp_globals.g_opr_create;<br />
gpr_pricing_attr_tbl (k).pricing_attribute_id := fnd_api.g_miss_num;<br />
gpr_pricing_attr_tbl (k).list_line_id := fnd_api.g_miss_num;<br />
gpr_pricing_attr_tbl (k).product_attribute_context := &#8216;ITEM&#8217;;<br />
gpr_pricing_attr_tbl (k).product_attribute := &#8216;PRICING_ATTRIBUTE1&#8242;;<br />
gpr_pricing_attr_tbl (k).excluder_flag := &#8216;N&#8217;;<br />
gpr_pricing_attr_tbl (k).price_list_line_index := k;<br />
gpr_pricing_attr_tbl (k).product_attr_value := l_current_inventory_item_id;<br />
gpr_pricing_attr_tbl (k).product_uom_code := l_current_uom_code;<br />
dbms_output.put_line(&#8216;attribute done&#8217;);</p>
<p>l_line_index := l_line_index + 1;</p>
<p>update xxtg.xxtg_eam_qp_lines_tmp tmp<br />
set tmp.inventory_item_id = l_current_inventory_item_id<br />
where tmp.segment1 = r_lines.segment1;</p>
<p>dbms_output.put_line(&#8216;l_current_inventory_item_id: &#8216; || l_current_inventory_item_id || &#8216; r_lines.segment1: &#8216; || r_lines.segment1);<br />
fnd_file.put_line(FND_FILE.LOG,&#8217;l_current_inventory_item_id: &#8216; || l_current_inventory_item_id || &#8216; r_lines.segment1: &#8216; || r_lines.segment1);</p>
<p>k := k + 1;<br />
end loop;</p>
<p>/*<br />
product_attr_value stores inventory item id &#8211; product_attribute for Item Number<br />
is Pricing_Attribute1 product_attribute_context is ITEM. Each line can have one<br />
or more pricing attributes. PRICE_LIST_LINE_INDEX is used to link the child<br />
(pricing attributes) to the parent(line).<br />
When you have pricing attributes like color, length, width etc, populate the<br />
fields pricing_attribute_context, pricing_attribute, pricing_attr_value_from,<br />
pricing_attr_value_to and comparison_operator_code ( &#8216;=&#8217; or &#8216;between&#8217;) and<br />
repeat the product_attr_value and its attribute and context for each record.<br />
*/</p>
<p>&#8211;looping at qualifier. Here we define vendors that can use this price list.<br />
for r_qualifier in c_qualifier<br />
loop<br />
begin<br />
select vendor_id<br />
into l_current_vendor_id<br />
from ap_suppliers aps<br />
where aps.vendor_name = r_qualifier.qualifier;<br />
exception<br />
when no_data_found then<br />
l_current_vendor_id := null;<br />
continue;<br />
end;</p>
<p>&#8211;gpr_qualifiers_tbl(j).list_line_id := fnd_api.g_miss_num;<br />
gpr_qualifiers_tbl (j).operation := qp_globals.g_opr_create;<br />
gpr_qualifiers_tbl (j).qualifier_attr_value := l_current_vendor_id;<br />
gpr_qualifiers_tbl (j).qualifier_context := p_qualifier_context;<br />
gpr_qualifiers_tbl (j).qualifier_attribute := p_qualifier_attribute;<br />
gpr_qualifiers_tbl (j).comparison_operator_code := p_comparison_operator_code;<br />
gpr_qualifiers_tbl (j).qualifier_precedence := p_qualifier_precedence;<br />
gpr_qualifiers_tbl (j).qualifier_grouping_no := l_qual_group_num;</p>
<p>l_qual_group_num := l_qual_group_num + 1;</p>
<p>update xxtg.xxtg_eam_qp_qualifier_vals_tmp<br />
set QUALIFIER_VALUE = l_current_vendor_id<br />
where qualifier = r_qualifier.qualifier;</p>
<p>dbms_output.put_line(&#8216;l_current_vendor_id: &#8216; || l_current_vendor_id || &#8216; r_lines.segment1: &#8216; || r_qualifier.qualifier);<br />
fnd_file.put_line(FND_FILE.LOG,&#8217;l_current_vendor_id: &#8216; || l_current_vendor_id || &#8216; r_lines.segment1: &#8216; || r_qualifier.qualifier);</p>
<p>j := j + 1;<br />
end loop;</p>
<p>&#8211;dbms_output.put_line(&#8216;before process price list &#8216;);<br />
qp_price_list_pub.process_price_list (<br />
p_api_version_number        =&gt; 1,<br />
p_init_msg_list             =&gt; fnd_api.g_true,<br />
p_return_values             =&gt; fnd_api.g_true,<br />
p_commit                    =&gt; fnd_api.g_false,<br />
x_return_status             =&gt; gpr_return_status,<br />
x_msg_count                 =&gt; gpr_msg_count,<br />
x_msg_data                  =&gt; gpr_msg_data,<br />
p_price_list_rec            =&gt; gpr_price_list_rec,<br />
p_price_list_line_tbl       =&gt; gpr_price_list_line_tbl,<br />
p_pricing_attr_tbl          =&gt; gpr_pricing_attr_tbl,<br />
p_qualifiers_tbl            =&gt;  gpr_qualifiers_tbl,<br />
p_qualifiers_val_tbl        =&gt; gpr_qualifiers_val_tbl,<br />
x_price_list_rec            =&gt; ppr_price_list_rec,<br />
x_price_list_val_rec        =&gt; ppr_price_list_val_rec,<br />
x_price_list_line_tbl       =&gt; ppr_price_list_line_tbl,<br />
x_price_list_line_val_tbl   =&gt; ppr_price_list_line_val_tbl,<br />
x_qualifiers_tbl            =&gt; ppr_qualifiers_tbl,<br />
x_qualifiers_val_tbl        =&gt; ppr_qualifiers_val_tbl,<br />
x_pricing_attr_tbl          =&gt; ppr_pricing_attr_tbl,<br />
x_pricing_attr_val_tbl      =&gt; ppr_pricing_attr_val_tbl);</p>
<p>if gpr_return_status &lt;&gt; fnd_api.g_ret_sts_success<br />
then<br />
dbms_output.put_line(&#8216;unsuccessfull:&#8217; || gpr_msg_count || &#8216;-&#8217; || gpr_msg_data);</p>
<p>fnd_file.put_line(FND_FILE.LOG,&#8217;unsuccessfull:&#8217; || gpr_msg_data);<br />
rollback;</p>
<p>for m in 1 .. gpr_msg_count<br />
loop<br />
gpr_msg_data := oe_msg_pub.get (p_msg_index =&gt; m, p_encoded =&gt; &#8216;F&#8217;);</p>
<p>fnd_file.put_line(FND_FILE.LOG,&#8217;exception &#8211; &#8216; || gpr_msg_data);<br />
dbms_output.put_line(&#8216;exception -  &#8216; || gpr_msg_data);<br />
end loop;</p>
<p>raise fnd_api.g_exc_unexpected_error;<br />
end if;</p>
<p>if gpr_return_status = fnd_api.g_ret_sts_success<br />
then<br />
dbms_output.put_line(&#8216;success&#8217;);<br />
fnd_file.put_line(FND_FILE.LOG,&#8217;success&#8217;);</p>
<p>update xxtg_eam_qp_lines_tmp<br />
set success_flag = &#8216;Y&#8217;;</p>
<p>update xxtg_eam_qp_qualifier_vals_tmp<br />
set success_flag = &#8216;Y&#8217;;<br />
end if;<br />
exception<br />
when fnd_api.g_exc_error<br />
then<br />
gpr_return_status := fnd_api.g_ret_sts_error;<br />
fnd_file.put_line(FND_FILE.LOG,&#8217;exception:&#8217; || gpr_return_status);</p>
<p>rollback;<br />
when fnd_api.g_exc_unexpected_error<br />
then<br />
gpr_return_status := fnd_api.g_ret_sts_unexp_error;<br />
fnd_file.put_line(FND_FILE.LOG,&#8217;exception:&#8217; || gpr_return_status);</p>
<p>for k in 1 .. gpr_msg_count<br />
loop<br />
gpr_msg_data := oe_msg_pub.get (p_msg_index =&gt; k, p_encoded =&gt; &#8216;F&#8217;);</p>
<p>fnd_file.put_line(FND_FILE.LOG,&#8217;exception &#8211; &#8216; || gpr_msg_data);<br />
end loop;</p>
<p>rollback;<br />
when others<br />
then<br />
gpr_return_status := fnd_api.g_ret_sts_unexp_error;<br />
fnd_file.put_line(FND_FILE.LOG,&#8217;exception &#8211; others:&#8217; || gpr_return_status);<br />
rollback;<br />
end price_list_import;</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/enginozer.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/enginozer.wordpress.com/98/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/enginozer.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/enginozer.wordpress.com/98/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/enginozer.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/enginozer.wordpress.com/98/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/enginozer.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/enginozer.wordpress.com/98/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/enginozer.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/enginozer.wordpress.com/98/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/enginozer.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/enginozer.wordpress.com/98/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/enginozer.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/enginozer.wordpress.com/98/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=98&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://enginozer.wordpress.com/2011/01/11/qp-price-list-import/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/d115faa5b1011c2837cb2de5b694342f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">enginozer</media:title>
		</media:content>
	</item>
		<item>
		<title>Creating Invoice By AP_INVOICES_INTERFACE with match option</title>
		<link>http://enginozer.wordpress.com/2010/12/16/creating-invoice-by-ap_invoices_interface-with-match-option/</link>
		<comments>http://enginozer.wordpress.com/2010/12/16/creating-invoice-by-ap_invoices_interface-with-match-option/#comments</comments>
		<pubDate>Thu, 16 Dec 2010 15:49:55 +0000</pubDate>
		<dc:creator>Engin ÖZER</dc:creator>
				<category><![CDATA[EBS Example Scripts]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://enginozer.wordpress.com/?p=93</guid>
		<description><![CDATA[At this article i am giving an example of creating invoice with AP_INVOICES_INTERFACE. You only need to be careful about your purchase order&#8217;s match option must be &#8216;R&#8217; (match option at po_line_locations_all at R12). This script inserts into ap invoice interfaces so that you can execute your Open Interfaces Import to create an invoice. You [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=93&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>At this article i am giving an example of creating invoice with AP_INVOICES_INTERFACE. You only need to be careful about your purchase order&#8217;s match option must be &#8216;R&#8217; (match option at po_line_locations_all at R12).</p>
<p>This script inserts into ap invoice interfaces so that you can execute your Open Interfaces Import to create an invoice. You can also call this concurrent from this code sample.</p>
<p>procedure insert_into_interface (errbuf    out varchar2,<br />
retcode   out varchar2,<br />
p_allowance_id in number,<br />
p_invoice_num in varchar2,<br />
p_group_id      in varchar2)<br />
is<br />
cursor c_allowance_lines(cp_allowance_id number)<br />
is</p>
<p>select xeaa.vendor_id,<br />
xeaa.vendor_site_id,<br />
plla.line_location_id,<br />
plla.match_option,<br />
pha.segment1,<br />
pha.po_header_id,<br />
pla.po_line_id,<br />
pda.po_distribution_id,<br />
plla.org_id,<br />
rsl.item_id,<br />
plla.shipment_num,<br />
apss.vendor_site_code,<br />
apss.payment_method_lookup_code,<br />
aps.accts_pay_code_combination_id,<br />
rsh.receipt_num,<br />
rsl.line_num,<br />
xeal.*<br />
from xxxt_allowances_all        xeaa,<br />
xxxt_allowance_lines_all xeal,<br />
ap_suppliers                         aps,<br />
ap_supplier_sites_all             apss,<br />
rcv_transactions                    rt,<br />
rcv_shipment_headers        rsh,<br />
rcv_shipment_lines rsl,<br />
po_line_locations_all           plla,<br />
po_lines_all                        pla,<br />
po_headers_all                   pha,<br />
po_distributions_all             pda<br />
where xeal.allowance_id = xeaa.allowance_id<br />
and pla.po_line_id = plla.po_line_id<br />
and pha.po_header_id = pla.po_header_id<br />
and pda.line_location_id = rt.po_line_location_id<br />
and aps.vendor_id = xeaa.vendor_id<br />
and apss.vendor_id = xeaa.vendor_id<br />
and apss.vendor_site_id = xeaa.vendor_site_id<br />
and xeaa.allowance_id = cp_allowance_id<br />
and rt.transaction_id = xeal.rcv_transaction_id<br />
and rsl.shipment_line_id = rt.shipment_line_id<br />
and rsh.shipment_header_id = rsl.shipment_header_id<br />
and plla.line_location_id = rt.po_line_location_id;</p>
<p>l_line_number number;<br />
l_line_id         number;<br />
l_total_invoice_amount number;<br />
l_invoice_id    number;<br />
l_invoice_count number;<br />
l_allowance_status xxxt_allowances_all.status%type;<br />
l_invoice_num ap_invoices_all.invoice_num%type;</p>
<p>begin<br />
select count(1)<br />
into l_invoice_count<br />
from ap_invoices_all<br />
where invoice_num = p_invoice_num;</p>
<p>begin<br />
select status<br />
into l_allowance_status<br />
from xxxt_allowances_all<br />
where allowance_id = p_allowance_id;</p>
<p>if(l_allowance_status &lt;&gt; &#8216;ONAYLANDI&#8217;) then<br />
fnd_file.put_line (fnd_file.OUTPUT, &#8216; ALLOWANCE_ID: &#8216; || p_allowance_id || &#8216; hakedişinin statüsü ONAYLANDI değildir.&#8217;);<br />
&#8211;return;<br />
end if;<br />
exception<br />
when no_data_found then<br />
fnd_file.put_line (fnd_file.OUTPUT, &#8216; ALLOWANCE_ID: &#8216; || p_allowance_id || &#8216; böyle bir hakediş bulunmamaktadır.&#8217;);<br />
return;<br />
end;</p>
<p>if(l_invoice_count &gt; 0) then<br />
fnd_file.put_line (fnd_file.OUTPUT, &#8216; INVOICE_NUM: &#8216; || p_invoice_num || &#8216; fatura numarası kullanımdadır. Lütfen kullanılmayan bir fatura numarası giriniz.&#8217;);<br />
return;<br />
end if;</p>
<p>select ap_invoices_s.nextval<br />
into l_invoice_id<br />
from dual;</p>
<p>fnd_file.put_line (fnd_file.OUTPUT, &#8216;INVOICE_ID: &#8216; || l_invoice_id ||&#8217; INVOICE_NUM: &#8216; || p_invoice_num || &#8216; ALLOWANCE_ID:&#8217; || p_allowance_id || &#8216; Faturası girilecek.&#8217;);</p>
<p>&#8211;inserting into ap_invoices_interface<br />
insert into ap_invoices_interface<br />
(<br />
invoice_id,<br />
invoice_num,<br />
invoice_type_lookup_code,<br />
invoice_date,<br />
exchange_date,<br />
exchange_rate_type,<br />
vendor_id,<br />
vendor_site_id,<br />
payment_method_lookup_code,<br />
invoice_amount,<br />
invoice_currency_code,<br />
terms_name,<br />
last_update_date,<br />
last_updated_by,<br />
last_update_login,<br />
creation_date,<br />
created_by,<br />
source,<br />
group_id,<br />
accts_pay_code_combination_id,<br />
org_id,<br />
attribute15<br />
)<br />
&#8211;values<br />
(<br />
select l_invoice_id,<br />
p_invoice_num,<br />
&#8216;STANDARD&#8217;,<br />
sysdate,<br />
null,&#8211;exchange_date<br />
null,<br />
xeaa.vendor_id,<br />
xeaa.vendor_site_id,<br />
nvl(apss.payment_method_lookup_code,&#8217;EFT&#8217;),<br />
(select nvl(sum(round(price_override * quantity_override,2)),0)<br />
from xxxt_allowance_lines_all<br />
where allowance_id = xeaa.allowance_id),<br />
&#8216;TRY&#8217;,<br />
&#8217;30 GÜN&#8217;,<br />
sysdate,<br />
fnd_profile.value(&#8216;USER_ID&#8217;),<br />
fnd_profile.value(&#8216;LOGIN_ID&#8217;),<br />
sysdate,<br />
fnd_profile.value(&#8216;USER_ID&#8217;),<br />
&#8216;HAKEDIS&#8217;,<br />
p_group_id,<br />
aps.accts_pay_code_combination_id,<br />
fnd_profile.value(&#8216;ORG_ID&#8217;),<br />
apss.vendor_site_code<br />
from xxxt_allowances_all xeaa,<br />
ap_suppliers aps,<br />
ap_supplier_sites_all apss<br />
where aps.vendor_id = xeaa.vendor_id<br />
and apss.vendor_id = xeaa.vendor_id<br />
and apss.vendor_site_id = xeaa.vendor_site_id<br />
and xeaa.allowance_id = p_allowance_id);</p>
<p>&#8211;looping at allowance lines<br />
for r_allowance_lines in c_allowance_lines(p_allowance_id)<br />
loop<br />
begin<br />
select ap_invoice_lines_interface_s.nextval<br />
into l_line_id<br />
from dual;</p>
<p>select nvl (max (line_number),0) + 1<br />
into l_line_number<br />
from ap_invoice_lines_interface<br />
where invoice_id = l_invoice_id;</p>
<p>fnd_file.put_line (fnd_file.OUTPUT, &#8221;);<br />
fnd_file.put_line (fnd_file.OUTPUT, &#8216;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8217;);<br />
fnd_file.put_line (fnd_file.OUTPUT, &#8221;);<br />
fnd_file.put_line (fnd_file.OUTPUT, &#8216;INVOICE_LINE_ID: &#8216; || l_line_id ||&#8217; LINE_NUMBER: &#8216; || l_line_number || &#8216; AMOUNT: &#8216; || r_allowance_lines.price_override);<br />
fnd_file.put_line (fnd_file.OUTPUT, &#8216;RECEIPT_NUM: &#8216; || r_allowance_lines.receipt_num ||&#8217; RCV_TRANSACTION_ID: &#8216; || r_allowance_lines.rcv_transaction_id || &#8216; QUANTITY: &#8216; || r_allowance_lines.quantity_override || &#8216; ORG_ID: &#8216; || r_allowance_lines.org_id);</p>
<p>&#8211;inserting into ap_invoice_lines_interface<br />
insert into ap_invoice_lines_interface<br />
(invoice_id,<br />
invoice_line_id,<br />
line_number,<br />
line_type_lookup_code,<br />
match_option,<br />
amount,<br />
inventory_item_id,<br />
last_update_date,<br />
last_updated_by,<br />
last_update_login,<br />
creation_date,<br />
created_by,<br />
rcv_transaction_id,<br />
quantity_invoiced,<br />
po_distribution_id,<br />
org_id<br />
)<br />
values<br />
(<br />
l_invoice_id,&#8211;p_invoice_id,<br />
l_line_id,<br />
l_line_number,<br />
&#8216;ITEM&#8217;,<br />
&#8216;R&#8217;,&#8211;match option<br />
round(r_allowance_lines.price_override * r_allowance_lines.quantity_override,2),&#8211;amount,<br />
r_allowance_lines.item_id,<br />
sysdate,<br />
fnd_profile.value(&#8216;USER_ID&#8217;),<br />
fnd_profile.value(&#8216;LOGIN_ID&#8217;),<br />
sysdate,<br />
fnd_profile.value(&#8216;USER_ID&#8217;),<br />
r_allowance_lines.rcv_transaction_id,<br />
r_allowance_lines.quantity_override,<br />
r_allowance_lines.po_distribution_id,<br />
r_allowance_lines.org_id<br />
);</p>
<p>commit;<br />
exception<br />
when no_data_found<br />
then<br />
xxxt_error_package.raise_error(sqlerrm);<br />
end;<br />
end loop;<br />
end insert_into_interface;</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/enginozer.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/enginozer.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/enginozer.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/enginozer.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/enginozer.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/enginozer.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/enginozer.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/enginozer.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/enginozer.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/enginozer.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/enginozer.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/enginozer.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/enginozer.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/enginozer.wordpress.com/93/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=93&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://enginozer.wordpress.com/2010/12/16/creating-invoice-by-ap_invoices_interface-with-match-option/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/d115faa5b1011c2837cb2de5b694342f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">enginozer</media:title>
		</media:content>
	</item>
		<item>
		<title>Custom Workflow Using Oracle Approvals Manager(AME)</title>
		<link>http://enginozer.wordpress.com/2010/12/04/custom-workflow-using-oracle-approvals-managerame/</link>
		<comments>http://enginozer.wordpress.com/2010/12/04/custom-workflow-using-oracle-approvals-managerame/#comments</comments>
		<pubDate>Sat, 04 Dec 2010 14:28:09 +0000</pubDate>
		<dc:creator>Engin ÖZER</dc:creator>
				<category><![CDATA[Oracle Workflow]]></category>

		<guid isPermaLink="false">http://enginozer.wordpress.com/?p=78</guid>
		<description><![CDATA[In most of Oracle standard workflows Oracle Approvals Manager(AME) is used to define approvers. At one of my projects, there was so many workflows that approvers of workflow could be changed. So, my functional consultant advised me to use AME for defining approval groups and rules on which user to send the notification. I developed [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=78&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>In most of Oracle standard workflows Oracle Approvals Manager(AME) is used to define approvers. At one of my projects, there was so many workflows that approvers of workflow could be changed. So, my functional consultant advised me to use AME for defining approval groups and rules on which user to send the notification.</p>
<p>I developed a custom method for my workflows that look fine at last.</p>
<p><img src="http://enginozer.files.wordpress.com/2010/12/main_approval_process1.jpg?w=450" alt="" /></p>
<p>Here we can see firstly we set the startup values and the first step approvals at set_startup_values.  I got a variable as current_notif_done which i keep for mentioning whether next approver exists or not.</p>
<p>If you are careful enough you can see i make a loop at approval_ntf. Here workflow loops at approval_ntf each time next approval exists. When there is no next approver i set current_notif_done attribute1 to break the loop.</p>
<p>I also make two more loops which does not need approval(FYI notifications at approval and reject).</p>
<p>I settled this structure in two main steps.</p>
<p>1- Getting approvers for current step</p>
<p>Here i use this code for asking AME which approver is the next</p>
<p>ame_api2.getNextApprovers4(applicationIdIn=&gt;your_ame_setups_application_id,<br />
transactionTypeIn=&gt;your_ame_setups_transaction_type,<br />
transactionIdIn=&gt;your_ame_setups_transaction_id,&#8211;generally itemkey<br />
flagApproversAsNotifiedIn =&gt; ame_util.booleanFalse,<br />
approvalProcessCompleteYNOut =&gt; l_complete,&#8211;mentions if approval is complete<br />
nextApproversOut=&gt;l_next_approvers);&#8211;next approval table for current step</p>
<p>2- Telling approval status to AME</p>
<p>ame_api2.updateApprovalStatus2(applicationIdIn     =&gt; your_ame_setups_application_id,<br />
transactionIdIn     =&gt; your_ame_setups_transaction_id,&#8211;generally itemkey<br />
approvalStatusIn  =&gt; AME_UTIL.approvedStatus,&#8211;approved_status_or_rejected_status<br />
approverNameIn   =&gt; approver_or_rejecters_user_name,<br />
transactionTypeIn =&gt;  your_ame_setups_transaction_type);</p>
<p>&nbsp;</p>
<p>There are some more keypoints about this. First of all, this generic workflow can be implemented for any needs. So, creating a new workflow time would decrease so much by the help of this generic workflow.</p>
<p>Two AME transaction type is needed to implement both approval and FYI at AME. Because, if it is not done like this FYI notifications could not be sent.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/enginozer.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/enginozer.wordpress.com/78/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/enginozer.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/enginozer.wordpress.com/78/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/enginozer.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/enginozer.wordpress.com/78/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/enginozer.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/enginozer.wordpress.com/78/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/enginozer.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/enginozer.wordpress.com/78/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/enginozer.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/enginozer.wordpress.com/78/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/enginozer.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/enginozer.wordpress.com/78/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=78&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://enginozer.wordpress.com/2010/12/04/custom-workflow-using-oracle-approvals-managerame/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/d115faa5b1011c2837cb2de5b694342f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">enginozer</media:title>
		</media:content>

		<media:content url="http://enginozer.files.wordpress.com/2010/12/main_approval_process1.jpg" medium="image" />
	</item>
		<item>
		<title>Gathering Scripts For Migration</title>
		<link>http://enginozer.wordpress.com/2010/04/08/gathering-scripts-for-migration/</link>
		<comments>http://enginozer.wordpress.com/2010/04/08/gathering-scripts-for-migration/#comments</comments>
		<pubDate>Thu, 08 Apr 2010 14:47:22 +0000</pubDate>
		<dc:creator>Engin ÖZER</dc:creator>
				<category><![CDATA[Oracle]]></category>

		<guid isPermaLink="false">http://enginozer.wordpress.com/?p=74</guid>
		<description><![CDATA[  At this article i will try to show you an easier way of migrating scripts (including table, view, procedure, function, package creation scripts) to another system. Here are the steps for doing this; 1 - Create a table for keeping data temptorarily. 2- create a table. I will name my table as xxtg_kts_engin_deneme    create [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=74&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p> </p>
<p>At this article i will try to show you an easier way of migrating scripts (including table, view, procedure, function, package creation scripts) to another system. Here are the steps for doing this;</p>
<p><strong>1 -</strong> Create a table for keeping data temptorarily.</p>
<p><strong>2-</strong> create a table. I will name my table as xxtg_kts_engin_deneme</p>
<p>   create table xxtg.xxtg_kts_engin_deneme<br />
   (<br />
       text clob;<br />
   );</p>
<p><strong>3 -</strong> Choose the prefixes of the tables(or whatever object you want) you desire.</p>
<p><strong>4 -</strong> Choose the prefixes of tables you want to exclude.</p>
<p><strong>5 -</strong> modify the cursor c_table below upon your needs</p>
<p>cursor c_table is</p>
<p>        SELECT *<br />
          FROM dba_objects<br />
         WHERE object_type = &#8216;TABLE&#8217;<br />
           AND object_name LIKE &#8216;XX%&#8217; <br />
           AND object_name NOT LIKE &#8216;XXTG%&#8217;</p>
<p><strong>6 -</strong> Run the script below to insert your table creation scripts into xxtg_kts_engin_deneme table. After that you can copy your scripts by just opening the table data.</p>
<p>declare<br />
    cursor c_table is<br />
        SELECT *<br />
          FROM dba_objects<br />
         WHERE object_type = &#8216;TABLE&#8217;<br />
           AND object_name LIKE &#8216;XX%&#8217;<br />
           and object_name not like &#8216;XXXT_PO%&#8217;<br />
           and object_name not like &#8216;XXXT_INV%&#8217;<br />
           and object_name not like &#8216;XXXT_AP%&#8217;<br />
           AND owner NOT IN (&#8216;XXTEPE&#8217;, &#8216;XXBR&#8217;, &#8216;XXPYXIS&#8217;);<br />
    r_table c_table%rowtype;<br />
    <br />
    one_table clob;<br />
    tmp_table clob;<br />
    tmp_table2 clob;<br />
    pos number;<br />
begin<br />
    open c_table;<br />
    loop<br />
        fetch c_table into r_table;<br />
        exit when c_table%notfound;<br />
        <br />
        tmp_table2 := &#8216;drop table &#8216; || r_table.table_name;<br />
        <br />
        select DBMS_METADATA.GET_DDL (<br />
                                &#8217;TABLE&#8217;,<br />
                                r_table.table_name,<br />
                                &#8217;XXTG&#8217;<br />
                                ) X<br />
        into tmp_table<br />
        from dual;<br />
        <br />
        tmp_table := &#8216;drop table xxtg.&#8217; || r_table.table_name || chr(10) || tmp_table;<br />
        <br />
        <br />
        begin<br />
        select &#8216;drop synonym &#8216; || r_table.table_name || chr(10) || DBMS_METADATA.GET_DDL (<br />
                        &#8217;SYNONYM&#8217;,<br />
                        r_table.table_name,<br />
                        &#8217;APPS&#8217;<br />
                        ) X<br />
        into tmp_table2<br />
        from dual;<br />
        exception when others then<br />
            tmp_table2 := &#8221;;<br />
        end;<br />
        <br />
        tmp_table := tmp_table || tmp_table2;<br />
        <br />
        one_table := one_table || tmp_table;<br />
        dbms_output.put_line(r_table.table_name);<br />
        one_table := one_table || chr(10) || &#8216;/&#8217; || chr(10);<br />
        <br />
        <br />
        &#8211;one_table := one_table || tmp_table;<br />
    end loop;<br />
    close c_table;</p>
<p>    insert into xxtg.xxtg_kts_engin_deneme<br />
    (text)<br />
    values(one_table);<br />
end;</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/enginozer.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/enginozer.wordpress.com/74/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/enginozer.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/enginozer.wordpress.com/74/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/enginozer.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/enginozer.wordpress.com/74/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/enginozer.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/enginozer.wordpress.com/74/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/enginozer.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/enginozer.wordpress.com/74/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/enginozer.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/enginozer.wordpress.com/74/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/enginozer.wordpress.com/74/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/enginozer.wordpress.com/74/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=74&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://enginozer.wordpress.com/2010/04/08/gathering-scripts-for-migration/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/d115faa5b1011c2837cb2de5b694342f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">enginozer</media:title>
		</media:content>
	</item>
		<item>
		<title>Developing On E-Business Suite</title>
		<link>http://enginozer.wordpress.com/2010/03/01/developing-on-e-business-suite/</link>
		<comments>http://enginozer.wordpress.com/2010/03/01/developing-on-e-business-suite/#comments</comments>
		<pubDate>Mon, 01 Mar 2010 12:05:24 +0000</pubDate>
		<dc:creator>Engin ÖZER</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://enginozer.wordpress.com/?p=72</guid>
		<description><![CDATA[fnd_date.canonical_to_date(p_date_from);<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=72&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>fnd_date.canonical_to_date(p_date_from);</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/enginozer.wordpress.com/72/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/enginozer.wordpress.com/72/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/enginozer.wordpress.com/72/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/enginozer.wordpress.com/72/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/enginozer.wordpress.com/72/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/enginozer.wordpress.com/72/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/enginozer.wordpress.com/72/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/enginozer.wordpress.com/72/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/enginozer.wordpress.com/72/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/enginozer.wordpress.com/72/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/enginozer.wordpress.com/72/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/enginozer.wordpress.com/72/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/enginozer.wordpress.com/72/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/enginozer.wordpress.com/72/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=72&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://enginozer.wordpress.com/2010/03/01/developing-on-e-business-suite/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/d115faa5b1011c2837cb2de5b694342f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">enginozer</media:title>
		</media:content>
	</item>
		<item>
		<title>Oracle Forms Triggers</title>
		<link>http://enginozer.wordpress.com/2009/11/13/oracle-forms-triggers/</link>
		<comments>http://enginozer.wordpress.com/2009/11/13/oracle-forms-triggers/#comments</comments>
		<pubDate>Fri, 13 Nov 2009 15:44:02 +0000</pubDate>
		<dc:creator>Engin ÖZER</dc:creator>
				<category><![CDATA[Oracle Forms]]></category>

		<guid isPermaLink="false">http://enginozer.wordpress.com/?p=66</guid>
		<description><![CDATA[At this article i will try to mention about Forms&#8217; special triggers on various types. We will examine them in three sections; Form level triggers, Data block level triggers and Item level triggers. Form Level Triggers; When-New-Form-Instance: This trigger is called just after form is called. So you can do everything you need to do [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=66&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>At this article i will try to mention about Forms&#8217; special triggers on various types. We will examine them in three sections; Form level triggers, Data block level triggers and Item level triggers.</p>
<p><strong>Form Level Triggers;</strong></p>
<p><strong>When-New-Form-Instance:</strong> This trigger is called just after form is called. So you can do everything you need to do before the form starts. Be careful that this trigger is called only when the form is opened.</p>
<p><strong>When-Form-Navigate:</strong> This trigger is helpful when you need to open a new form(as new program) from a from. This trigger is called when navigating between those forms.</p>
<p><strong>When-New-Block-Instance:</strong> This trigger is called when any of the blocks the form contains is entered.</p>
<p><strong>When-New-Record-Instance:</strong> This trigger is called when any record changes in any blocks.</p>
<p><strong>When-New-Item-Instance:</strong> This trigger is called when any of the items in the forms is entered.</p>
<p>These are most commonly used triggers at form level. There are some other triggers like WHEN-MOUSE-CLICK, WHEN-MOUSE-DOUBLECLICK, Key-Commit, Key-Listval &#8230; But there these kind of triggers are more commonly used at lower levels that will be explained later.</p>
<p><strong>Block Level Triggers;</strong></p>
<p><strong>When-New-Block-Instance:</strong> This trigger is called when one specific block is entered.</p>
<p><strong>When-New-Record-Instance:</strong> This trigger is called when cursor enters to a new record in specified block. This trigger may be useful when it is needed to set calculated value to an item at every record.</p>
<p><strong>When-New-Item-Instance:</strong> This trigger is called when any of the items in the forms is entered. This trigger can be used at block level upon needs. But, it is better idea to use this trigger at item level.</p>
<p><strong>WHEN-VALIDATE-RECORD: </strong>This trigger is called when cursor is relocated out of current record of specified block to another record. This trigger can be very useful while trying to insert or update a record under some costraints. If the conditions are checked at this trigger and form_trigger_failure will be raised under the situation of failing validation, record will not be saved. So, everything works just fine.</p>
<p><strong>POST-QUERY</strong><strong>:</strong> This trigger is called when the form user searches a record or searches all records at form. POST-QUERY trigger is triggered for each record the search fetches. So, it is very useful when it is needed to set some item&#8217;s values upon other database items&#8217; values.</p>
<p><strong>PRE-INSERT: </strong>This trigger is called when the form user tries to insert a new record to table. This trigger is generally used to give sequence number to table&#8217;s &#8220;id&#8221; column. You can also make some validation of record that is specific for only insert statements.</p>
<p><strong>PRE-UPDATE</strong><strong>: </strong>This trigger is called when the form user tries to update one record.  You can make some validation of record that is specific for only update statements.</p>
<p><strong>ON-POPULATE-DETAILS</strong><strong>: </strong>This trigger populates the detail blocks that has relation with the record we select. Well, before telling about this trigger i should tell about relations of blocks. Blocks can be joined on one or more item&#8217;s values and there must be a master-detail relationship between that blocks. For example, let&#8217;s assume that customers is our master table, customer_accounts is our detail table and these tables can be joines on customer_id. These table data on forms can be showed with two data blocks. One of them is Customers and other block is Customer_Accounts. At this structure, customers block will be given a relation with customer_accounts on customer_id. Then after, this trigger is created automatically.</p>
<p>It is not a good idea to edit automatically created code on the concern of maintenance.</p>
<p><strong>ON-CHECK-DELETE-MASTER:</strong> This trigger is also created automatically upon relation. This trigger is created upon the Delete Record Behaviour choice at relation. Again it is not a good idea to edit this trigger.</p>
<p>These are most commonly used triggers at block level. There is much more. Others&#8217; usage is on your need.</p>
<p><strong>Item Level Triggers:</strong> At this level most of the common triggers exits but the ones following are the most commonly used ones.</p>
<p><strong>WHEN-VALIDATE-ITEM</strong><strong>:</strong> This trigger is called when the cursor exits from specified item. This trigger is used for validation for only one field.</p>
<p><strong>POST-CHANGE:</strong> This trigger is called when any change of an item occurs. You can use this trigger for calculations. For example, you calculate %20 tax upon cost of service. If cost of service changes then the tax changes. If you write this trigger on cost of service item for recalculation of tax each time cost changes, it will work just fine.</p>
<p><strong>KEY-LISTVAL:</strong> This trigger is called when the form user tries to open LOV from an item. This trigger is used to calculate some other values at the execution of LOV. And also, this trigger is called to show calendar on LOV at text items with date data type.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/enginozer.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/enginozer.wordpress.com/66/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/enginozer.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/enginozer.wordpress.com/66/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/enginozer.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/enginozer.wordpress.com/66/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/enginozer.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/enginozer.wordpress.com/66/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/enginozer.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/enginozer.wordpress.com/66/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/enginozer.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/enginozer.wordpress.com/66/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/enginozer.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/enginozer.wordpress.com/66/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=66&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://enginozer.wordpress.com/2009/11/13/oracle-forms-triggers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/d115faa5b1011c2837cb2de5b694342f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">enginozer</media:title>
		</media:content>
	</item>
		<item>
		<title>Oracle Explain Plan &#8211; 2</title>
		<link>http://enginozer.wordpress.com/2009/09/25/oracle-explain-plan-2-2/</link>
		<comments>http://enginozer.wordpress.com/2009/09/25/oracle-explain-plan-2-2/#comments</comments>
		<pubDate>Fri, 25 Sep 2009 07:20:19 +0000</pubDate>
		<dc:creator>Engin ÖZER</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://enginozer.wordpress.com/?p=64</guid>
		<description><![CDATA[  Interpreting Explain Plan  What&#8217;s an explain plan? An explain plan is a representation of the access path that is taken when a query is executed within Oracle. Query processing can be divided into 7 phases: [1] Syntactic Checks the syntax of the query [2] Semantic Checks that all objects exist and are accessible [3] [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=64&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<table border="0" cellspacing="0" cellpadding="4" width="570">
<tbody>
<tr>
<td width="100" height="35" valign="top">
<p align="left"><a href="history.back()"><img src="http://enginozer.wordpress.com/img/zurueck.gif" border="0" alt="Zurück" width="76" height="19" /></a></p>
</td>
<th height="35" valign="top">
<p align="left"><img src="http://enginozer.wordpress.com/img/bullet-gruen.gif" alt="" width="15" height="10" />  Interpreting Explain Plan  <img src="http://enginozer.wordpress.com/img/bullet-gruen.gif" alt="" width="15" height="10" /></p>
</th>
</tr>
<tr>
<td colspan="2" valign="top">What&#8217;s an explain plan?</p>
<p>An explain plan is a representation of the access path that is taken when a query is executed within Oracle.</p>
<p>Query processing can be divided into 7 phases:</p>
<table border="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td width="25%" valign="top">[1] Syntactic</td>
<td width="75%" valign="top">Checks the syntax of the query</td>
</tr>
<tr>
<td width="25%" valign="top">[2] Semantic</td>
<td width="75%" valign="top">Checks that all objects exist and are accessible</td>
</tr>
<tr>
<td width="25%" valign="top">[3] View Merging</td>
<td width="75%" valign="top">Rewrites query as join on base tables as opposed to using views</td>
</tr>
<tr>
<td width="25%" valign="top">[4] Statement<br />
     Transformation</td>
<td width="75%" valign="top">Rewrites query transforming some complex constructs into simpler ones where appropriate (e.g. subquery merging, in/or transformation)</td>
</tr>
<tr>
<td width="25%" valign="top">[5] Optimization</td>
<td width="75%" valign="top">Determines the optimal access path for the query to take. With the Rule Based Optimizer (RBO) it uses a set of heuristics to determine access path. With the Cost Based Optimizer (CBO) we use statistics to analyze the relative costs of accessing objects.</td>
</tr>
<tr>
<td width="25%" valign="top">[6] QEP Generation</td>
<td width="75%" valign="top">QEP = Query Evaluation Plan</td>
</tr>
<tr>
<td width="25%" valign="top">[7] QEP Execution</td>
<td width="75%" valign="top">QEP = Query Evaluation Plan</td>
</tr>
</tbody>
</table>
<p>Steps [1]-[6] are handled by the parser. Step [7] is the execution of the statement.</p>
<p>The explain plan is produced by the parser. Once the access path has been decided upon it is stored in the library cache together with the statement itself. We store queries in the library cache based upon a hashed representation  of that query. When looking for a statement in the library cache, we first apply a hashing algorithm to the statement and then we look for this hash value in the library cache. This access path will be used until the query is reparsed.</p>
<p>Terminology</p>
<table border="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td width="25%">Row Source</td>
<td width="75%">A set of rows used in a query may be a select from a base object or the result set returned by joining 2 earlier row sources</td>
</tr>
<tr>
<td width="25%">Predicate</td>
<td width="75%">where clause of a query</td>
</tr>
<tr>
<td width="25%">Tuples</td>
<td width="75%">rows</td>
</tr>
<tr>
<td width="25%">Driving Table</td>
<td width="75%">This is the row source that we use to seed the query. If this returns a lot of rows then this can have a negative affect on all subsequent operations</td>
</tr>
<tr>
<td width="25%">Probed Table</td>
<td width="75%">This is the object we lookup data in after we have retrieved relevant key data from the driving table.</td>
</tr>
</tbody>
</table>
<p>How does Oracle access data?</p>
<p>At the physical level Oracle reads blocks of data. The smallest amount of data read is a single Oracle block, the largest is constrained by operating system limits (and multiblock i/o). Logically Oracle finds the data to read by using the following methods:</p>
<ul>
<li>Full Table Scan (FTS)</li>
<li>Index Lookup (unique &amp; non-unique)</li>
<li>Rowid</li>
</ul>
<p>Explain plan Hierarchy</p>
<p>Simple explain plan:</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
SELECT STATEMENT     [CHOOSE] Cost=1234<br />
  TABLE ACCESS FULL LARGE [:Q65001] [ANALYZED]</p>
<p>The rightmost uppermost operation of an explain plan is the first thing that the explain plan will execute. In this case TABLE ACCESS FULL LARGE is the first operation. This statement means we are doing a full table scan of table LARGE. When this operation completes then the resultant row source is passed up to the<br />
next level of the query for processing. In this case it is the SELECT STATEMENT which is the top of the query.</p>
<p>[CHOOSE] is an indication of the optimizer_goal for the query. This DOES NOT necessarily indicate that plan has actually used this goal. The only way to confirm this is to check the<br />
cost= part of the explain plan as well. For example the following query indicates that the CBO has been used because there is a cost in the cost field:</p>
<p>SELECT STATEMENT     [CHOOSE] Cost=1234</p>
<p>However the explain plan below indicates the use of the RBO because the cost field is blank:</p>
<p>SELECT STATEMENT     [CHOOSE] Cost=</p>
<p>The cost field is a comparative cost that is used internally to determine the best cost for particular plans. The costs of different statements are not really directly comparable.</p>
<p>[:Q65001] indicates that this particular part of the query is being executed in parallel. This number indicates that the operation will be processed by a parallel query slave as opposed to being executed serially.</p>
<p>[ANALYZED] indicates that the object in question has been analyzed and there are currently statistics available for the CBO to use. There is no indication of the &#8216;level&#8217; of analysis done.</p>
<p>Access Methods in detail</p>
<p>Full Table Scan (FTS)</p>
<p>In a FTS operation, the whole table is read up to the high water mark (HWM). The HWM marks the last block in the table that has ever had data written to it. If you have deleted all the rows then you will still read up to the HWM. Truncate resets the HWM back to the start of the table. FTS uses multiblock i/o to read the blocks from disk. Multiblock i/o is controlled by the parameter &lt;PARAM:db_block_multi_block_read_count&gt;.</p>
<p>This defaults to:</p>
<p>db_block_buffers / ( (PROCESSES+3) / 4 )</p>
<p>Maximum values are OS dependant</p>
<p>Buffers from FTS operations are placed on the Least Recently Used (LRU) end of the buffer cache so will be quickly aged out. FTS is not recommended for large tables unless you are reading &gt;5-10% of it (or so) or you intend to run in parallel.</p>
<p>Example FTS explain plan:</p>
<p>SQL&gt; explain plan for select * from dual;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
SELECT STATEMENT     [CHOOSE] Cost=<br />
  TABLE ACCESS FULL DUAL</p>
<p>Index lookup</p>
<p>Data is accessed by looking up key values in an index and returning rowids. A rowid uniquely identifies an individual row in a particular data block. This block is read via single block i/o.</p>
<p>In this example an index is used to find the relevant row(s) and then the table is accessed to lookup the ename column (which is not included in the index):</p>
<p>SQL&gt; explain plan for<br />
select empno,ename from emp where empno=10;<br />
Query Plan</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
TABLE ACCESS BY ROWID EMP [ANALYZED]<br />
    INDEX UNIQUE SCAN EMP_I1</p>
<p>Notice the &#8216;TABLE ACCESS BY ROWID&#8217; section. This indicates that the table data is not being accessed via a FTS operation but rather by a rowid lookup. In this case the rowid has been produced by looking up values in the index first. The index is being accessed by an &#8216;INDEX UNIQUE SCAN&#8217; operation. This is explained below. The index name in this case is EMP_I1. If all the required data resides in the index then a table lookup may be unnecessary and all you will see is an index access with no table access.</p>
<p>In the following example all the columns (empno) are in the index. Notice that no table access takes place:</p>
<p>SQL&gt; explain plan for<br />
select empno from emp where empno=10;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
  INDEX UNIQUE SCAN EMP_I1</p>
<p>Indexes are presorted so sorting may be unecessary if the sort order required is the same as the index.</p>
<p>SQL&gt; explain plan for select empno,ename from emp<br />
where empno &gt; 7876 order by empno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT   [CHOOSE] Cost=1<br />
TABLE ACCESS BY ROWID EMP [ANALYZED]<br />
  INDEX RANGE SCAN EMP_I1 [ANALYZED]</p>
<p>In this case the index is sorted so ther rows will be returned in the order of the index hence a sort is unecessary.</p>
<p>SQL&gt; explain plan for<br />
select /*+ Full(emp) */ empno,ename from emp<br />
where empno&gt; 7876 order by empno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT   [CHOOSE] Cost=9<br />
  SORT ORDER BY<br />
    TABLE ACCESS FULL EMP [ANALYZED]  Cost=1 Card=2 Bytes=66</p>
<p>Because we have forced a FTS the data is unsorted and so we must sort the data<br />
after it has been retrieved.</p>
<p>There are 4 methods of index lookup:</p>
<ul>
<li>index unique scan</li>
<li>index range scan</li>
<li>index full scan</li>
<li>index fast full scan</li>
</ul>
<p>Index unique scan</p>
<p>Method for looking up a single key value via a unique index. Always returns a single value You must supply AT LEAST the leading column of the index to access data via the index, However this may return &gt; 1 row as the uniqueness will not be guaranteed.</p>
<p>SQL&gt; explain plan for<br />
select empno,ename from emp where empno=10;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
TABLE ACCESS BY ROWID EMP [ANALYZED]<br />
    INDEX UNIQUE SCAN EMP_I1</p>
<p>Index range scan</p>
<p>Method for accessing multiple column values You must supply AT LEAST the leading column of the index to access data via the index Can be used for range operations (e.g. &gt; &lt; &lt;&gt; &gt;= &lt;= between)</p>
<p>SQL&gt; explain plan for select empno,ename from emp<br />
where empno &gt; 7876 order by empno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT   [CHOOSE] Cost=1<br />
TABLE ACCESS BY ROWID EMP [ANALYZED]<br />
  INDEX RANGE SCAN EMP_I1 [ANALYZED]</p>
<p>A non-unique index may return multiple values for the predicate col1 = 5 and will use an index range scan</p>
<p>SQL&gt; explain plan for select mgr from emp where mgr = 5</p>
<p>Query plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
  INDEX RANGE SCAN EMP_I2 [ANALYZED]</p>
<p>Index Full Scan</p>
<p>In certain circumstances it is possible for the whole index to be scanned as opposed to a range scan (i.e. where no constraining predicates are provided for a table). Full index scans are  only available in the CBO as otherwise we are unable to determine whether a full scan would be a good idea or not. We choose an index Full Scan when we have statistics that indicate that it is going to be more efficient than a Full table scan and a sort.</p>
<p>For example we may do a Full index scan when we do an unbounded scan of an index and want the data to be ordered in the index order. The optimizer may decide that selecting all the information from the index and not sorting is more efficient than doing a FTS or a Fast Full Index Scan and then sorting.</p>
<p>An Index full scan will perform single block i/o&#8217;s and so it may prove to be inefficient. Index BE_IX is a concatenated index on big_emp (empno,ename)</p>
<p>SQL&gt; explain plan for select empno,ename<br />
     from big_emp order by empno,ename;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT   [CHOOSE] Cost=26<br />
  INDEX FULL SCAN BE_IX [ANALYZED]</p>
<p>Index Fast Full Scan</p>
<p>Scans all the block in the index Rows are not returned in sorted order Introduced in 7.3 and requires V733_PLANS_ENABLED=TRUE and CBO may be hinted using INDEX_FFS hint uses multiblock i/o can be executed in parallel can be used to access second column of concatenated indexes. This is because we are selecting all of the index.</p>
<p>Note that INDEX FAST FULL SCAN is the mechinism behind fast index create and recreate. Index BE_IX is a concatenated index on big_emp (empno,ename)</p>
<p>SQL&gt; explain plan for select empno,ename from big_emp;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT   [CHOOSE] Cost=1<br />
  INDEX FAST FULL SCAN BE_IX [ANALYZED]</p>
<p>Selecting the 2nd column of concatenated index:</p>
<p>SQL&gt; explain plan for select ename from big_emp;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT   [CHOOSE] Cost=1<br />
  INDEX FAST FULL SCAN BE_IX [ANALYZED]</p>
<p>Rowid</p>
<p>This is the quickest access method available Oracle simply retrieves the block specified and extracts the rows it is interested in. Most frequently seen in explain plans as Table access by Rowid</p>
<p>SQL&gt; explain plan for select * from dept where rowid = &#8216;:x&#8217;;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
TABLE ACCESS BY ROWID DEPT [ANALYZED]</p>
<p>Table is accessed by rowid following index lookup:</p>
<p>SQL&gt; explain plan for<br />
select empno,ename from emp where empno=10;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
TABLE ACCESS BY ROWID EMP [ANALYZED]<br />
    INDEX UNIQUE SCAN EMP_I1</p>
<p>Joins</p>
<p>A Join is a predicate that attempts to combine 2 row sources We only ever join 2 row sources together Join steps are always performed serially even though underlying row sources may have been accessed in parallel. Join order &#8211; order in which joins are performed</p>
<p>The join order makes a significant difference to the way in which the query is executed. By accessing particular row sources first, certain predicates may be satisfied that are not satisfied by with other join orders. This may prevent certain access paths from being taken.</p>
<p>Suppose there is a concatenated index on A(a.col1,a.col2). Note that a.col1 is the leading column. Consider the following query:</p>
<p>select A.col4<br />
from   A,B,C<br />
where  B.col3 = 10<br />
and    A.col1 = B.col1<br />
and    A.col2 = C.col2<br />
and    C.col3 = 5</p>
<p>We could represent the joins present in the query using the following schematic:</p>
<p>  B     &lt;&#8212;&gt; A &lt;&#8212;&gt;    C<br />
col3=10                col3=5</p>
<p>There are really only 2 ways we can drive the query: via B.col3 or C.col3. We would have to do a Full scan of A to be able to drive off it. This is unlikely to be efficient with large tables;</p>
<p>If we drive off table B, using predicate B.col3=10 (as a filter or lookup key) then we will retrieve the value for B.col1 and join to A.col1. Because we have now filled the leading column of the concatenated index on table A we can use this index to give us values for A.col2 and join to A.</p>
<p>However if we drive of table c, then we only get a value for a.col2 and since this is a trailing column of a concatenated index and the leading column has not been supplied at this point, we cannot use the index on a to lookup the data.</p>
<p>So it is likely that the best join order will be B A C. The CBO will obviously use costs to establish whether the individual access paths are a good idea or not.</p>
<p>If the CBO does not choose this join order then we can hint it by changing the from<br />
clause to read:</p>
<p>from B,A,C</p>
<p>and using the /*+ ordered */ hint. The resultant query would be:</p>
<p>select /*+ ordered */ A.col4<br />
from   B,A,C<br />
where  B.col3 = 10<br />
and    A.col1 = B.col1<br />
and    A.col2 = C.col2<br />
and    C.col3 = 5</p>
<p>Join Types</p>
<ul>
<li>Sort Merge Join (SMJ)</li>
<li>Nested Loops (NL)</li>
<li>Hash Join</li>
</ul>
<p>Sort Merge Join</p>
<p>Rows are produced by Row Source 1 and are then sorted Rows from Row Source 2 are then produced and sorted by the same sort key as Row Source 1. Row Source 1 and 2 are NOT accessed concurrently Sorted rows from both sides are then merged together (joined)</p>
<p>                   MERGE<br />
                 /      \<br />
            SORT        SORT<br />
             |             |<br />
        Row Source 1  Row Source 2</p>
<p>If the row sources are already (known to be) sorted then the sort operation is unecessary as long as both &#8216;sides&#8217; are sorted using the same key. Presorted row sources include indexed columns and row sources that have already been sorted in earlier steps. Although the merge of the 2 row sources is handled serially, the row sources could be accessed in parallel.</p>
<p>SQL&gt; explain plan for<br />
select /*+ ordered */ e.deptno,d.deptno<br />
from emp e,dept d<br />
where e.deptno = d.deptno<br />
order by e.deptno,d.deptno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT [CHOOSE] Cost=17<br />
  MERGE JOIN<br />
    SORT JOIN<br />
      TABLE ACCESS FULL EMP [ANALYZED]<br />
    SORT JOIN<br />
      TABLE ACCESS FULL DEPT [ANALYZED]</p>
<p>Sorting is an expensive operation, especially with large tables. Because of this, SMJ is often not a particularly efficient join method.</p>
<p>Nested Loops</p>
<p>First we return all the rows from row source 1 Then we probe row source 2 once for each row returned from row source 1</p>
<p>Row source 1<br />
~~~~~~~~~~~~<br />
Row 1 &#8212;&#8212;&#8212;&#8212;&#8211;       &#8212; Probe -&gt;       Row source 2<br />
Row 2 &#8212;&#8212;&#8212;&#8212;&#8211;       &#8212; Probe -&gt;       Row source 2<br />
Row 3 &#8212;&#8212;&#8212;&#8212;&#8211;       &#8212; Probe -&gt;       Row source 2</p>
<p>Row source 1 is known as the outer table<br />
Row source 2 is known as the inner table</p>
<p>Accessing row source 2 is known a probing the inner table For nested loops to be efficient it is important that the first row source returns as few rows as possible as this directly controls the number of probes of the second row source. Also it helps if the access method for row source 2 is efficient as this operation is being repeated once for every row returned by row source 1.</p>
<p>SQL&gt; explain plan for<br />
select a.dname,b.sql<br />
from dept a,emp b<br />
where a.deptno = b.deptno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT [CHOOSE] Cost=5<br />
  NESTED LOOPS<br />
    TABLE ACCESS FULL DEPT [ANALYZED]<br />
    TABLE ACCESS FULL EMP [ANALYZED]</p>
<p>Hash Join</p>
<p>New join type introduced in 7.3 More efficient in theory than NL &amp; SMJ Only accessible via the CBO Smallest row source is chosen and used to build a hash table and a bitmap The second row source is hashed and checked against the hash table looking for joins. The bitmap is used as a quick lookup to check if rows are in the hash table and are especially useful when the hash table is too large to fit in memory.</p>
<p>SQL&gt; explain plan for<br />
select /*+ use_hash(emp) */ empno<br />
from emp,dept<br />
where emp.deptno = dept.deptno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT  [CHOOSE] Cost=3<br />
  HASH JOIN<br />
    TABLE ACCESS FULL DEPT<br />
    TABLE ACCESS FULL EMP</p>
<p>Hash joins are enabled by the parameter HASH_JOIN_ENABLED=TRUE in the init.ora or session. TRUE is the default in 7.3</p>
<p>Cartesian Product</p>
<p>A Cartesian Product is done where they are no join conditions between 2 row sources and there is no alternative method of accessing the data Not really a join as such as there is no join! Typically this is caused by a coding mistake where a join has been left out. It can be useful in some circumstances &#8211; Star joins uses cartesian products.</p>
<p>Notice that there is no join between the 2 tables:</p>
<p>SQL&gt; explain plan for<br />
select emp.deptno,dept,deptno<br />
from emp,dept</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SLECT STATEMENT [CHOOSE] Cost=5<br />
  MERGE JOIN CARTESIAN<br />
    TABLE ACCESS FULL DEPT<br />
    SORT JOIN<br />
      TABLE ACCESS FULL EMP</p>
<p>The CARTESIAN keyword indicate that we are doing a cartesian product.</p>
<p>Operations</p>
<p>Operations that show up in explain plans</p>
<ul>
<li>sort</li>
<li>filter</li>
<li>view</li>
</ul>
<p>Sorts</p>
<p>There are a number of different operations that promote sorts</p>
<ul>
<li>order by clauses</li>
<li>group by</li>
<li>sort merge join</li>
</ul>
<p>Note that if the row source is already appropriately sorted then no sorting is required. This is now indicated in 7.3:</p>
<p>SORT GROUP BY NOSORT<br />
     INDEX FULL SCAN &#8230;..</p>
<p>In this case the group by operation simply groups the rows it does not do the sort operation as this has already been completed.</p>
<p>Sorts are expensive operations especially on large tables where the rows do not fit in memory and spill to disk. By default sort blocks are placed into the buffer cache. This may result in aging out of other blocks that may be reread by other processes. To avoid this you can use the parameter &lt;Parameter:SORT_DIRECT_WRITES&gt; which does not place sort blocks into the buffer cache.</p>
<p>Filter</p>
<p>Has a number of different meanings used to indicate partition elimination may also indicate an actual filter step where one row source is filtering another functions such as min may introduce filter steps into query plans</p>
<p>In this example there are 2 filter steps. The first is effectively like a NL except that it stops when it gets something that it doesn&#8217;t like (i.e. a bounded NL). This is there because of the not in. The second is filtering out the min value:</p>
<p>SQL&gt; explain plan for select * from emp<br />
     where empno not in (select min(empno)<br />
     from big_emp group by empno);</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE]  Cost=1<br />
  FILTER     **** This is like a bounded nested loops<br />
    TABLE ACCESS FULL EMP [ANALYZED]<br />
     FILTER   **** This filter is introduced by the min<br />
        SORT GROUP BY NOSORT<br />
          INDEX FULL SCAN BE_IX</p>
<p>This example is also interesting in that it has a NOSORT function. The group by does not need to sort because the index row source is already pre sorted.</p>
<p>Views</p>
<p>When a view cannot be merged into the main query you will often see a projection view operation. This indicates that the &#8216;view&#8217; will be selected from directly as opposed to being broken down into joins on the base tables. A number of constructs make a view non mergeable. Inline views are also non mergeable.</p>
<p>In the following example the select contains an inline view which cannot be merged:</p>
<p>SQL&gt; explain plan for<br />
select ename,tot<br />
from emp,<br />
    (select empno,sum(empno) tot from big_emp group by empno) tmp<br />
where emp.empno = tmp.empno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE]<br />
  HASH JOIN<br />
    TABLE ACCESS FULL EMP [ANALYZED]<br />
    VIEW<br />
      SORT GROUP BY<br />
        INDEX FULL SCAN BE_IX</p>
<p>In this case the inline view tmp which contains an aggregate function cannot be merged into the main query. The explain plan shows this as a view step.</p>
<p>Partition Views</p>
<p>Allows a large table to be broken up into a number of smaller partitions which can be queried much more quickly than the table as a whole a union all view is built over the top to provide the original functionality Check constraints or where clauses provide partition elimination capabilities</p>
<p>SQL&gt; explain plan for<br />
select /*+ use_nl(p1,kbwyv1) ordered */  sum(prc_pd)<br />
from parent1 p1,  kbwyv1<br />
where p1.class = 22<br />
and   kbwyv1.bitm_numb = p1.bitm_numb<br />
and   kbwyv1.year = 1997<br />
and   kbwyv1.week between 32 and 33 ;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
SELECT STATEMENT   [FIRST_ROWS] Cost=1780<br />
  SORT AGGREGATE<br />
    NESTED LOOPS   [:Q65001] Ct=1780 Cd=40 Bt=3120<br />
      TABLE ACCESS FULL PARENT1 [:Q65000] [AN] Ct=20 Cd=40 Bt=1040<br />
      VIEW  KBWYV1 [:Q65001]<br />
        UNION-ALL PARTITION  [:Q65001]<br />
          FILTER   [:Q64000]<br />
            TABLE ACCESS FULL KBWYT1 [AN] Ct=11 Cd=2000 Bt=104000<br />
          TABLE ACCESS FULL KBWYT2 [AN] Ct=11 Cd=2000 Bt=104000<br />
          TABLE ACCESS FULL KBWYT3 [AN] Ct=11 Cd=2000 Bt=104000<br />
          FILTER   [:Q61000]<br />
            TABLE ACCESS FULL KBWYT4 [AN] Ct=11 Cd=2000 Bt=104000</p>
<p>KBWYV1 is a view on 4 tables KBWYT1-4. KBWYT1-4 contain rows for week 31-34 respectively and are maintained by check constraints. This query should only return rows from partions 2 &amp; 3. The filter operation indicates this. Partitions 1 &amp; 4 are eliminated at execution time. The view line indicates that the view is not merged. The union-all partion information indicates that we have recognised this as a partition view. Note that the tables can be accessed in parallel.</p>
<p>Remote Queries</p>
<p>Only shows remote in the OPERATION column OTHER column shows query executed on remote node OTHER_NODE shows where it is executed Different operational characteristics for RBO &amp; CBO</p>
<p>RBO &#8211; Drags everything across the link and joins locally<br />
CBO &#8211; Uses cost estimates to determine whether to execute remotely or locally</p>
<p>SQL&gt;  explain plan for<br />
select *<br />
from dept@loop_link;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT REMOTE  [CHOOSE] Cost=1<br />
  TABLE ACCESS FULL DEPT [SJD.WORLD] [ANALYZED]</p>
<p>In this case the whole query has been sent to the remote site. The other column shows nothing.</p>
<p>SQL&gt; explain plan for<br />
select a.dname,avg(b.sal),max(b.sal)<br />
from dept@loop_link a, emp b<br />
where a.deptno=b.deptno<br />
group by a.dname<br />
order by max(b.sal),avg(b.sal) desc;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
SELECT STATEMENT   [CHOOSE] Cost=20<br />
  SORT ORDER BY  [:Q137003] [PARALLEL_TO_SERIAL]<br />
    SORT GROUP BY  [:Q137002] [PARALLEL_TO_PARALLEL]<br />
      NESTED LOOPS   [:Q137001] [PARALLEL_TO_PARALLEL]<br />
        REMOTE   [:Q137000] [PARALLEL_FROM_SERIAL]<br />
        TABLE ACCESS FULL EMP [:Q137001] [ANALYZED]<br />
        [PARALLEL_COMBINED_WITH_PARENT]</p>
<p>Bind Variables</p>
<p>Bind variables are recommended in most cases because they promote sharing of sql code<br />
At parse time the parser has NO IDEA what the bind variable contains. With RBO this makes no difference but with CBO, which relies on accurate statistics to produce plans, this can be a problem.</p>
<p>Defining bind variables in sqlplus:</p>
<p>variable x varchar2(18);<br />
assigning values:<br />
begin<br />
 <img src='http://s2.wp.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' />  := &#8216;hello&#8217;;<br />
end;<br />
/</p>
<p>SQL&gt; explain plan for<br />
select *<br />
from dept<br />
where rowid = &#8216;:x&#8217;;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
  TABLE ACCESS BY ROWID DEPT [ANALYZED]</p>
<p>Parallel Query</p>
<p>Main indicators that a query is using PQO:</p>
<ul>
<li>[:Q1000004] entries in the explain plan</li>
<li>Checkout the other column for details of what the slaves are executing</li>
<li>v$pq_slave will show any parallel activity</li>
</ul>
<p>Columns to look in for information</p>
<ul>
<li>other &#8211; contains the query passed to the slaves</li>
<li>other_tag &#8211; describes the contents of other</li>
<li>object_node &#8211; indicates order of pqo slaves</li>
</ul>
<p>Parallel Query operates on a producer/consumer basis. When you specify parallel degree 4 oracle tries to allocate 4 producer slaves and 4 consumer slaves. The producers can feed any of the consumers. If there are only 2 slaves available then we use these. If there is only 1 slave available then we go serial If there are none available then we use serial. If parallel_min_percent is set then we error ora 12827 instead of using a lower number of slaves or going serial</p>
<p>Consumer processes typically perform a sorting function. If there is no requirement for the data to be sorted then the consumer slaves are not produced and we end up with the number of slaves used matching the degree of parallelism as opposed to being 2x the degree.</p>
<p>Parallel Terms</p>
<table border="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td width="50%" valign="top">PARALLEL_FROM_SERIAL</td>
<td width="50%" valign="top">This means that source of the data is serial but it is passed to a parallel consumer</td>
</tr>
<tr>
<td width="50%" valign="top">PARALLEL_TO_PARALLEL</td>
<td width="50%" valign="top">Both the consumer and the producer are  parallel</td>
</tr>
<tr>
<td width="50%" valign="top">PARALLEL_COMBINED_WITH_PARENT</td>
<td width="50%" valign="top">This operation has been combined with the parent operator. For example in a sort merge join the sort operations would be shown as PARALLEL_COMBINED_WITH_PARENT because the sort and the merge are handled as 1 operation.</td>
</tr>
<tr>
<td width="50%" valign="top">PARALELL_TO_SERIAL</td>
<td width="50%" valign="top">The source of the data is parallel but it is passed to a serial consumer. This typically will happen at the top of the explain plan but could occur anywhere</td>
</tr>
</tbody>
</table>
<p>Examples of parallel queries</p>
<p>Assumptions</p>
<p>OPTIMIZER_MODE = CHOOSE<br />
DEPT is small compared to EMP<br />
DEPT has an index (DEPT_INDX) on deptno column</p>
<p>Three examples are presented</p>
<p>Query #1:  Serial<br />
Query #2:  Parallel<br />
Query #3:  Parallel, with forced optimization to RULE and forced usage of DEPT_INDX</p>
<p>Sample Query #1 (Serial)</p>
<p>select A.dname, avg(B.sal), max(B.sal)<br />
from  dept A, emp B<br />
where A.deptno = B.deptno<br />
group by A.dname<br />
order by max(B.sal), avg(B.sal) desc;</p>
<p>Execution Plan #1 (Serial)</p>
<p>OBJECT_NAME                      OBJECT_NODE OTHER<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-  &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;-<br />
SELECT STATEMENT<br />
 SORT ORDER BY<br />
   SORT GROUP BY<br />
     MERGE JOIN<br />
       SORT JOIN<br />
         TABLE ACCESS FULL emp<br />
       SORT JOIN<br />
         TABLE ACCESS FULL dept</p>
<p>Notice that the object_node and other columns are empty</p>
<p>Sample Query #2 (Query #1 with parallel hints)</p>
<p>select <strong>/*+ parallel(B,4) parallel(A,4) */</strong><br />
A.dname, avg(B.sal), max(B.sal)<br />
from  dept A, emp B<br />
where A.deptno = B.deptno<br />
group by A.dname<br />
order by max(B.sal), avg(B.sal) desc;</p>
<p>Execution Plan #2  (Parallel)</p>
<p>OBJECT_NAME                      OBJECT_NODE OTHER<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-  &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;-<br />
SELECT STATEMENT      Cost = ??<br />
 SORT ORDER BY                   :Q55004     **[7]**<br />
   SORT GROUP BY                 :Q55003     **[6]**<br />
     MERGE JOIN                  :Q55002     **[5]**<br />
       SORT JOIN                 :Q55002     **[4]**<br />
         TABLE ACCESS FULL emp   :Q55001     **[2]**<br />
       SORT JOIN                 :Q55002     **[3]**<br />
         TABLE ACCESS FULL dept  :Q55000     **[1]**</p>
<p>Execution Plan #2  &#8212; OTHER column</p>
<p>**[1]**  (:Q55000) &#8220;PARALLEL_FROM_SERIAL&#8221;</p>
<p>Serial execution of SELECT DEPTNO, DNAME FROM DEPT</p>
<p>**[2]**  (:Q55001) &#8220;PARALLEL_TO_PARALLEL&#8221;</p>
<p>        SELECT /*+ ROWID(A1)*/<br />
        A1.&#8221;DEPTNO&#8221; C0, A1.&#8221;SAL&#8221; C1<br />
        FROM &#8220;EMP&#8221; A1<br />
        WHERE ROWID BETWEEN :1 AND :2</p>
<p>**[3]**  (:Q55002) &#8220;PARALLEL_COMBINED_WITH_PARENT&#8221;<br />
**[4]**  (:Q55002) &#8220;PARALLEL_COMBINED_WITH_PARENT&#8221;<br />
**[5]**  (:Q55002) &#8220;PARALLEL_TO_PARALLEL&#8221;</p>
<p>        SELECT /*+ ORDERED USE_MERGE(A2)*/<br />
        A2.C1 C0, A1.C1 C1<br />
        FROM :Q55001 A1,:Q55000 A2<br />
        WHERE A1.C0=A2.C0</p>
<p>**[6]**  (:Q55003) &#8220;PARALLEL_TO_PARALLEL&#8221;</p>
<p>        SELECT MAX(A1.C1) C0, AVG(A1.C1) C1, A1.C0 C2<br />
        FROM :Q55002 A1<br />
        GROUP BY A1.C0</p>
<p>**[7]**  (:Q55004) &#8220;PARALLEL_FROM_SERIAL&#8221;</p>
<p>        SELECT A1.C0 C0, A1.C1 C1, A1.C2 C2<br />
        FROM :Q55003 A1<br />
        ORDER BY A1.CO, A1.C1 DESC</p>
<p>Sample Query #3 (Query #2 with fudged hints)</p>
<p>select /*+ index(A dept_indx) <strong>parallel(B,4) parallel(A,4)</strong> */<br />
      A.dname, avg(B.sal), max(B.sal)<br />
from  dept A, emp B<br />
where A.deptno = B.deptno<br />
group by A.dname<br />
order by max(B.sal), avg(B.sal) desc;</p>
<p>Execution Plan #3  (Parallel)</p>
<p>OBJECT_NAME                         OBJECT_NODE OTHER<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;-<br />
SELECT STATEMENT          Cost = ??<br />
 SORT ORDER BY                      :Q58002     **[6]**<br />
   SORT GROUP BY                    :Q58001     **[5]**<br />
     NESTED LOOPS JOIN              :Q58000     **[4]**<br />
       TABLE ACCESS FULL emp        :Q58000     **[3]**<br />
       TABLE ACCESS BY ROWID dept   :Q58000     **[2]**<br />
         INDEX RANGE SCAN dept_indx :Q58000     **[1]**</p>
<p>Execution Plan #3  &#8212; OTHER column</p>
<p>**[1]**  (:Q58000) &#8220;PARALLEL_COMBINED_WITH_PARENT&#8221;<br />
**[2]**  (:Q58000) &#8220;PARALLEL_COMBINED_WITH_PARENT&#8221;<br />
**[3]**  (:Q58000) &#8220;PARALLEL_COMBINED_WITH_PARENT&#8221;<br />
**[4]**  (:Q58000) &#8220;PARALLEL_TO_PARALLEL&#8221;</p>
<p>        SELECT /*+ ORDERED USE_NL(A2) INDEX(A2) */<br />
        A2.&#8221;DNAME&#8221; C0, A1.C0 C1<br />
        FROM<br />
          (SELECT /*+ ROWID(A3) */<br />
           A3.&#8221;SAL&#8221; CO, A3.&#8221;DEPTNO&#8221; C1<br />
           FROM &#8220;EMP&#8221; A3<br />
           WHERE ROWID BETWEEN :1 AND :2) A1,<br />
          &#8220;DEPT&#8221; A2<br />
        WHERE A2.&#8221;DEPTNO&#8221; = A1.C1</p>
<p>**[5]**  (:Q58001) &#8220;PARALLEL_TO_PARALLEL&#8221;</p>
<p>        SELECT MAX(A1.C1) C0, AVG(A1.C1) C1, A1.C0 C2<br />
        FROM :Q58000 A1<br />
        GROUP BY A1.C0</p>
<p>**[6]**  (:Q58002) &#8220;PARALLEL_TO_SERIAL&#8221;</p>
<p>        SELECT A1.C0 C0, A1.C1 C1, A1.C2 C2<br />
        FROM :Q58001 A1<br />
        ORDER BY A1.C0, A1.C1 DESC</p>
<p>How to obtain explain plans</p>
<p>Explain plan for</p>
<p>Main advantage is that it does not actually run the query &#8211; just parses the sql. This means that it executes quickly. In the early stages of tuning explain plan gives you an idea of the potential performance of your query without actually running it. You can then make a judgement as to any modifications you may choose to make.</p>
<p>Autotrace</p>
<p>Autotrace can be configured to run the sql &amp; gives a plan  and statistics afterwards or just give you an explain plan without execu</p>
<table border="0" cellspacing="0" cellpadding="4" width="570">
<tbody>
<tr>
<td width="100" height="35" valign="top">
<p align="left"><a href="history.back()"><img src="http://enginozer.wordpress.com/img/zurueck.gif" border="0" alt="Zurück" width="76" height="19" /></a></p>
</td>
<th height="35" valign="top">
<p align="left"><img src="http://enginozer.wordpress.com/img/bullet-gruen.gif" alt="" width="15" height="10" />  Interpreting Explain Plan  <img src="http://enginozer.wordpress.com/img/bullet-gruen.gif" alt="" width="15" height="10" /></p>
</th>
</tr>
<tr>
<td colspan="2" valign="top">What&#8217;s an explain plan?</p>
<p>An explain plan is a representation of the access path that is taken when a query is executed within Oracle.</p>
<p>Query processing can be divided into 7 phases:</p>
<table border="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td width="25%" valign="top">[1] Syntactic</td>
<td width="75%" valign="top">Checks the syntax of the query</td>
</tr>
<tr>
<td width="25%" valign="top">[2] Semantic</td>
<td width="75%" valign="top">Checks that all objects exist and are accessible</td>
</tr>
<tr>
<td width="25%" valign="top">[3] View Merging</td>
<td width="75%" valign="top">Rewrites query as join on base tables as opposed to using views</td>
</tr>
<tr>
<td width="25%" valign="top">[4] Statement<br />
     Transformation</td>
<td width="75%" valign="top">Rewrites query transforming some complex constructs into simpler ones where appropriate (e.g. subquery merging, in/or transformation)</td>
</tr>
<tr>
<td width="25%" valign="top">[5] Optimization</td>
<td width="75%" valign="top">Determines the optimal access path for the query to take. With the Rule Based Optimizer (RBO) it uses a set of heuristics to determine access path. With the Cost Based Optimizer (CBO) we use statistics to analyze the relative costs of accessing objects.</td>
</tr>
<tr>
<td width="25%" valign="top">[6] QEP Generation</td>
<td width="75%" valign="top">QEP = Query Evaluation Plan</td>
</tr>
<tr>
<td width="25%" valign="top">[7] QEP Execution</td>
<td width="75%" valign="top">QEP = Query Evaluation Plan</td>
</tr>
</tbody>
</table>
<p>Steps [1]-[6] are handled by the parser. Step [7] is the execution of the statement.</p>
<p>The explain plan is produced by the parser. Once the access path has been decided upon it is stored in the library cache together with the statement itself. We store queries in the library cache based upon a hashed representation  of that query. When looking for a statement in the library cache, we first apply a hashing algorithm to the statement and then we look for this hash value in the library cache. This access path will be used until the query is reparsed.</p>
<p>Terminology</p>
<table border="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td width="25%">Row Source</td>
<td width="75%">A set of rows used in a query may be a select from a base object or the result set returned by joining 2 earlier row sources</td>
</tr>
<tr>
<td width="25%">Predicate</td>
<td width="75%">where clause of a query</td>
</tr>
<tr>
<td width="25%">Tuples</td>
<td width="75%">rows</td>
</tr>
<tr>
<td width="25%">Driving Table</td>
<td width="75%">This is the row source that we use to seed the query. If this returns a lot of rows then this can have a negative affect on all subsequent operations</td>
</tr>
<tr>
<td width="25%">Probed Table</td>
<td width="75%">This is the object we lookup data in after we have retrieved relevant key data from the driving table.</td>
</tr>
</tbody>
</table>
<p>How does Oracle access data?</p>
<p>At the physical level Oracle reads blocks of data. The smallest amount of data read is a single Oracle block, the largest is constrained by operating system limits (and multiblock i/o). Logically Oracle finds the data to read by using the following methods:</p>
<ul>
<li>Full Table Scan (FTS)</li>
<li>Index Lookup (unique &amp; non-unique)</li>
<li>Rowid</li>
</ul>
<p>Explain plan Hierarchy</p>
<p>Simple explain plan:</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
SELECT STATEMENT     [CHOOSE] Cost=1234<br />
  TABLE ACCESS FULL LARGE [:Q65001] [ANALYZED]</p>
<p>The rightmost uppermost operation of an explain plan is the first thing that the explain plan will execute. In this case TABLE ACCESS FULL LARGE is the first operation. This statement means we are doing a full table scan of table LARGE. When this operation completes then the resultant row source is passed up to the<br />
next level of the query for processing. In this case it is the SELECT STATEMENT which is the top of the query.</p>
<p>[CHOOSE] is an indication of the optimizer_goal for the query. This DOES NOT necessarily indicate that plan has actually used this goal. The only way to confirm this is to check the<br />
cost= part of the explain plan as well. For example the following query indicates that the CBO has been used because there is a cost in the cost field:</p>
<p>SELECT STATEMENT     [CHOOSE] Cost=1234</p>
<p>However the explain plan below indicates the use of the RBO because the cost field is blank:</p>
<p>SELECT STATEMENT     [CHOOSE] Cost=</p>
<p>The cost field is a comparative cost that is used internally to determine the best cost for particular plans. The costs of different statements are not really directly comparable.</p>
<p>[:Q65001] indicates that this particular part of the query is being executed in parallel. This number indicates that the operation will be processed by a parallel query slave as opposed to being executed serially.</p>
<p>[ANALYZED] indicates that the object in question has been analyzed and there are currently statistics available for the CBO to use. There is no indication of the &#8216;level&#8217; of analysis done.</p>
<p>Access Methods in detail</p>
<p>Full Table Scan (FTS)</p>
<p>In a FTS operation, the whole table is read up to the high water mark (HWM). The HWM marks the last block in the table that has ever had data written to it. If you have deleted all the rows then you will still read up to the HWM. Truncate resets the HWM back to the start of the table. FTS uses multiblock i/o to read the blocks from disk. Multiblock i/o is controlled by the parameter &lt;PARAM:db_block_multi_block_read_count&gt;.</p>
<p>This defaults to:</p>
<p>db_block_buffers / ( (PROCESSES+3) / 4 )</p>
<p>Maximum values are OS dependant</p>
<p>Buffers from FTS operations are placed on the Least Recently Used (LRU) end of the buffer cache so will be quickly aged out. FTS is not recommended for large tables unless you are reading &gt;5-10% of it (or so) or you intend to run in parallel.</p>
<p>Example FTS explain plan:</p>
<p>SQL&gt; explain plan for select * from dual;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
SELECT STATEMENT     [CHOOSE] Cost=<br />
  TABLE ACCESS FULL DUAL</p>
<p>Index lookup</p>
<p>Data is accessed by looking up key values in an index and returning rowids. A rowid uniquely identifies an individual row in a particular data block. This block is read via single block i/o.</p>
<p>In this example an index is used to find the relevant row(s) and then the table is accessed to lookup the ename column (which is not included in the index):</p>
<p>SQL&gt; explain plan for<br />
select empno,ename from emp where empno=10;<br />
Query Plan</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
TABLE ACCESS BY ROWID EMP [ANALYZED]<br />
    INDEX UNIQUE SCAN EMP_I1</p>
<p>Notice the &#8216;TABLE ACCESS BY ROWID&#8217; section. This indicates that the table data is not being accessed via a FTS operation but rather by a rowid lookup. In this case the rowid has been produced by looking up values in the index first. The index is being accessed by an &#8216;INDEX UNIQUE SCAN&#8217; operation. This is explained below. The index name in this case is EMP_I1. If all the required data resides in the index then a table lookup may be unnecessary and all you will see is an index access with no table access.</p>
<p>In the following example all the columns (empno) are in the index. Notice that no table access takes place:</p>
<p>SQL&gt; explain plan for<br />
select empno from emp where empno=10;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
  INDEX UNIQUE SCAN EMP_I1</p>
<p>Indexes are presorted so sorting may be unecessary if the sort order required is the same as the index.</p>
<p>SQL&gt; explain plan for select empno,ename from emp<br />
where empno &gt; 7876 order by empno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT   [CHOOSE] Cost=1<br />
TABLE ACCESS BY ROWID EMP [ANALYZED]<br />
  INDEX RANGE SCAN EMP_I1 [ANALYZED]</p>
<p>In this case the index is sorted so ther rows will be returned in the order of the index hence a sort is unecessary.</p>
<p>SQL&gt; explain plan for<br />
select /*+ Full(emp) */ empno,ename from emp<br />
where empno&gt; 7876 order by empno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT   [CHOOSE] Cost=9<br />
  SORT ORDER BY<br />
    TABLE ACCESS FULL EMP [ANALYZED]  Cost=1 Card=2 Bytes=66</p>
<p>Because we have forced a FTS the data is unsorted and so we must sort the data<br />
after it has been retrieved.</p>
<p>There are 4 methods of index lookup:</p>
<ul>
<li>index unique scan</li>
<li>index range scan</li>
<li>index full scan</li>
<li>index fast full scan</li>
</ul>
<p>Index unique scan</p>
<p>Method for looking up a single key value via a unique index. Always returns a single value You must supply AT LEAST the leading column of the index to access data via the index, However this may return &gt; 1 row as the uniqueness will not be guaranteed.</p>
<p>SQL&gt; explain plan for<br />
select empno,ename from emp where empno=10;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
TABLE ACCESS BY ROWID EMP [ANALYZED]<br />
    INDEX UNIQUE SCAN EMP_I1</p>
<p>Index range scan</p>
<p>Method for accessing multiple column values You must supply AT LEAST the leading column of the index to access data via the index Can be used for range operations (e.g. &gt; &lt; &lt;&gt; &gt;= &lt;= between)</p>
<p>SQL&gt; explain plan for select empno,ename from emp<br />
where empno &gt; 7876 order by empno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT   [CHOOSE] Cost=1<br />
TABLE ACCESS BY ROWID EMP [ANALYZED]<br />
  INDEX RANGE SCAN EMP_I1 [ANALYZED]</p>
<p>A non-unique index may return multiple values for the predicate col1 = 5 and will use an index range scan</p>
<p>SQL&gt; explain plan for select mgr from emp where mgr = 5</p>
<p>Query plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
  INDEX RANGE SCAN EMP_I2 [ANALYZED]</p>
<p>Index Full Scan</p>
<p>In certain circumstances it is possible for the whole index to be scanned as opposed to a range scan (i.e. where no constraining predicates are provided for a table). Full index scans are  only available in the CBO as otherwise we are unable to determine whether a full scan would be a good idea or not. We choose an index Full Scan when we have statistics that indicate that it is going to be more efficient than a Full table scan and a sort.</p>
<p>For example we may do a Full index scan when we do an unbounded scan of an index and want the data to be ordered in the index order. The optimizer may decide that selecting all the information from the index and not sorting is more efficient than doing a FTS or a Fast Full Index Scan and then sorting.</p>
<p>An Index full scan will perform single block i/o&#8217;s and so it may prove to be inefficient. Index BE_IX is a concatenated index on big_emp (empno,ename)</p>
<p>SQL&gt; explain plan for select empno,ename<br />
     from big_emp order by empno,ename;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT   [CHOOSE] Cost=26<br />
  INDEX FULL SCAN BE_IX [ANALYZED]</p>
<p>Index Fast Full Scan</p>
<p>Scans all the block in the index Rows are not returned in sorted order Introduced in 7.3 and requires V733_PLANS_ENABLED=TRUE and CBO may be hinted using INDEX_FFS hint uses multiblock i/o can be executed in parallel can be used to access second column of concatenated indexes. This is because we are selecting all of the index.</p>
<p>Note that INDEX FAST FULL SCAN is the mechinism behind fast index create and recreate. Index BE_IX is a concatenated index on big_emp (empno,ename)</p>
<p>SQL&gt; explain plan for select empno,ename from big_emp;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT   [CHOOSE] Cost=1<br />
  INDEX FAST FULL SCAN BE_IX [ANALYZED]</p>
<p>Selecting the 2nd column of concatenated index:</p>
<p>SQL&gt; explain plan for select ename from big_emp;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT   [CHOOSE] Cost=1<br />
  INDEX FAST FULL SCAN BE_IX [ANALYZED]</p>
<p>Rowid</p>
<p>This is the quickest access method available Oracle simply retrieves the block specified and extracts the rows it is interested in. Most frequently seen in explain plans as Table access by Rowid</p>
<p>SQL&gt; explain plan for select * from dept where rowid = &#8216;:x&#8217;;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
TABLE ACCESS BY ROWID DEPT [ANALYZED]</p>
<p>Table is accessed by rowid following index lookup:</p>
<p>SQL&gt; explain plan for<br />
select empno,ename from emp where empno=10;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
TABLE ACCESS BY ROWID EMP [ANALYZED]<br />
    INDEX UNIQUE SCAN EMP_I1</p>
<p>Joins</p>
<p>A Join is a predicate that attempts to combine 2 row sources We only ever join 2 row sources together Join steps are always performed serially even though underlying row sources may have been accessed in parallel. Join order &#8211; order in which joins are performed</p>
<p>The join order makes a significant difference to the way in which the query is executed. By accessing particular row sources first, certain predicates may be satisfied that are not satisfied by with other join orders. This may prevent certain access paths from being taken.</p>
<p>Suppose there is a concatenated index on A(a.col1,a.col2). Note that a.col1 is the leading column. Consider the following query:</p>
<p>select A.col4<br />
from   A,B,C<br />
where  B.col3 = 10<br />
and    A.col1 = B.col1<br />
and    A.col2 = C.col2<br />
and    C.col3 = 5</p>
<p>We could represent the joins present in the query using the following schematic:</p>
<p>  B     &lt;&#8212;&gt; A &lt;&#8212;&gt;    C<br />
col3=10                col3=5</p>
<p>There are really only 2 ways we can drive the query: via B.col3 or C.col3. We would have to do a Full scan of A to be able to drive off it. This is unlikely to be efficient with large tables;</p>
<p>If we drive off table B, using predicate B.col3=10 (as a filter or lookup key) then we will retrieve the value for B.col1 and join to A.col1. Because we have now filled the leading column of the concatenated index on table A we can use this index to give us values for A.col2 and join to A.</p>
<p>However if we drive of table c, then we only get a value for a.col2 and since this is a trailing column of a concatenated index and the leading column has not been supplied at this point, we cannot use the index on a to lookup the data.</p>
<p>So it is likely that the best join order will be B A C. The CBO will obviously use costs to establish whether the individual access paths are a good idea or not.</p>
<p>If the CBO does not choose this join order then we can hint it by changing the from<br />
clause to read:</p>
<p>from B,A,C</p>
<p>and using the /*+ ordered */ hint. The resultant query would be:</p>
<p>select /*+ ordered */ A.col4<br />
from   B,A,C<br />
where  B.col3 = 10<br />
and    A.col1 = B.col1<br />
and    A.col2 = C.col2<br />
and    C.col3 = 5</p>
<p>Join Types</p>
<ul>
<li>Sort Merge Join (SMJ)</li>
<li>Nested Loops (NL)</li>
<li>Hash Join</li>
</ul>
<p>Sort Merge Join</p>
<p>Rows are produced by Row Source 1 and are then sorted Rows from Row Source 2 are then produced and sorted by the same sort key as Row Source 1. Row Source 1 and 2 are NOT accessed concurrently Sorted rows from both sides are then merged together (joined)</p>
<p>                   MERGE<br />
                 /      \<br />
            SORT        SORT<br />
             |             |<br />
        Row Source 1  Row Source 2</p>
<p>If the row sources are already (known to be) sorted then the sort operation is unecessary as long as both &#8216;sides&#8217; are sorted using the same key. Presorted row sources include indexed columns and row sources that have already been sorted in earlier steps. Although the merge of the 2 row sources is handled serially, the row sources could be accessed in parallel.</p>
<p>SQL&gt; explain plan for<br />
select /*+ ordered */ e.deptno,d.deptno<br />
from emp e,dept d<br />
where e.deptno = d.deptno<br />
order by e.deptno,d.deptno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT [CHOOSE] Cost=17<br />
  MERGE JOIN<br />
    SORT JOIN<br />
      TABLE ACCESS FULL EMP [ANALYZED]<br />
    SORT JOIN<br />
      TABLE ACCESS FULL DEPT [ANALYZED]</p>
<p>Sorting is an expensive operation, especially with large tables. Because of this, SMJ is often not a particularly efficient join method.</p>
<p>Nested Loops</p>
<p>First we return all the rows from row source 1 Then we probe row source 2 once for each row returned from row source 1</p>
<p>Row source 1<br />
~~~~~~~~~~~~<br />
Row 1 &#8212;&#8212;&#8212;&#8212;&#8211;       &#8212; Probe -&gt;       Row source 2<br />
Row 2 &#8212;&#8212;&#8212;&#8212;&#8211;       &#8212; Probe -&gt;       Row source 2<br />
Row 3 &#8212;&#8212;&#8212;&#8212;&#8211;       &#8212; Probe -&gt;       Row source 2</p>
<p>Row source 1 is known as the outer table<br />
Row source 2 is known as the inner table</p>
<p>Accessing row source 2 is known a probing the inner table For nested loops to be efficient it is important that the first row source returns as few rows as possible as this directly controls the number of probes of the second row source. Also it helps if the access method for row source 2 is efficient as this operation is being repeated once for every row returned by row source 1.</p>
<p>SQL&gt; explain plan for<br />
select a.dname,b.sql<br />
from dept a,emp b<br />
where a.deptno = b.deptno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT [CHOOSE] Cost=5<br />
  NESTED LOOPS<br />
    TABLE ACCESS FULL DEPT [ANALYZED]<br />
    TABLE ACCESS FULL EMP [ANALYZED]</p>
<p>Hash Join</p>
<p>New join type introduced in 7.3 More efficient in theory than NL &amp; SMJ Only accessible via the CBO Smallest row source is chosen and used to build a hash table and a bitmap The second row source is hashed and checked against the hash table looking for joins. The bitmap is used as a quick lookup to check if rows are in the hash table and are especially useful when the hash table is too large to fit in memory.</p>
<p>SQL&gt; explain plan for<br />
select /*+ use_hash(emp) */ empno<br />
from emp,dept<br />
where emp.deptno = dept.deptno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT  [CHOOSE] Cost=3<br />
  HASH JOIN<br />
    TABLE ACCESS FULL DEPT<br />
    TABLE ACCESS FULL EMP</p>
<p>Hash joins are enabled by the parameter HASH_JOIN_ENABLED=TRUE in the init.ora or session. TRUE is the default in 7.3</p>
<p>Cartesian Product</p>
<p>A Cartesian Product is done where they are no join conditions between 2 row sources and there is no alternative method of accessing the data Not really a join as such as there is no join! Typically this is caused by a coding mistake where a join has been left out. It can be useful in some circumstances &#8211; Star joins uses cartesian products.</p>
<p>Notice that there is no join between the 2 tables:</p>
<p>SQL&gt; explain plan for<br />
select emp.deptno,dept,deptno<br />
from emp,dept</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SLECT STATEMENT [CHOOSE] Cost=5<br />
  MERGE JOIN CARTESIAN<br />
    TABLE ACCESS FULL DEPT<br />
    SORT JOIN<br />
      TABLE ACCESS FULL EMP</p>
<p>The CARTESIAN keyword indicate that we are doing a cartesian product.</p>
<p>Operations</p>
<p>Operations that show up in explain plans</p>
<ul>
<li>sort</li>
<li>filter</li>
<li>view</li>
</ul>
<p>Sorts</p>
<p>There are a number of different operations that promote sorts</p>
<ul>
<li>order by clauses</li>
<li>group by</li>
<li>sort merge join</li>
</ul>
<p>Note that if the row source is already appropriately sorted then no sorting is required. This is now indicated in 7.3:</p>
<p>SORT GROUP BY NOSORT<br />
     INDEX FULL SCAN &#8230;..</p>
<p>In this case the group by operation simply groups the rows it does not do the sort operation as this has already been completed.</p>
<p>Sorts are expensive operations especially on large tables where the rows do not fit in memory and spill to disk. By default sort blocks are placed into the buffer cache. This may result in aging out of other blocks that may be reread by other processes. To avoid this you can use the parameter &lt;Parameter:SORT_DIRECT_WRITES&gt; which does not place sort blocks into the buffer cache.</p>
<p>Filter</p>
<p>Has a number of different meanings used to indicate partition elimination may also indicate an actual filter step where one row source is filtering another functions such as min may introduce filter steps into query plans</p>
<p>In this example there are 2 filter steps. The first is effectively like a NL except that it stops when it gets something that it doesn&#8217;t like (i.e. a bounded NL). This is there because of the not in. The second is filtering out the min value:</p>
<p>SQL&gt; explain plan for select * from emp<br />
     where empno not in (select min(empno)<br />
     from big_emp group by empno);</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE]  Cost=1<br />
  FILTER     **** This is like a bounded nested loops<br />
    TABLE ACCESS FULL EMP [ANALYZED]<br />
     FILTER   **** This filter is introduced by the min<br />
        SORT GROUP BY NOSORT<br />
          INDEX FULL SCAN BE_IX</p>
<p>This example is also interesting in that it has a NOSORT function. The group by does not need to sort because the index row source is already pre sorted.</p>
<p>Views</p>
<p>When a view cannot be merged into the main query you will often see a projection view operation. This indicates that the &#8216;view&#8217; will be selected from directly as opposed to being broken down into joins on the base tables. A number of constructs make a view non mergeable. Inline views are also non mergeable.</p>
<p>In the following example the select contains an inline view which cannot be merged:</p>
<p>SQL&gt; explain plan for<br />
select ename,tot<br />
from emp,<br />
    (select empno,sum(empno) tot from big_emp group by empno) tmp<br />
where emp.empno = tmp.empno;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE]<br />
  HASH JOIN<br />
    TABLE ACCESS FULL EMP [ANALYZED]<br />
    VIEW<br />
      SORT GROUP BY<br />
        INDEX FULL SCAN BE_IX</p>
<p>In this case the inline view tmp which contains an aggregate function cannot be merged into the main query. The explain plan shows this as a view step.</p>
<p>Partition Views</p>
<p>Allows a large table to be broken up into a number of smaller partitions which can be queried much more quickly than the table as a whole a union all view is built over the top to provide the original functionality Check constraints or where clauses provide partition elimination capabilities</p>
<p>SQL&gt; explain plan for<br />
select /*+ use_nl(p1,kbwyv1) ordered */  sum(prc_pd)<br />
from parent1 p1,  kbwyv1<br />
where p1.class = 22<br />
and   kbwyv1.bitm_numb = p1.bitm_numb<br />
and   kbwyv1.year = 1997<br />
and   kbwyv1.week between 32 and 33 ;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
SELECT STATEMENT   [FIRST_ROWS] Cost=1780<br />
  SORT AGGREGATE<br />
    NESTED LOOPS   [:Q65001] Ct=1780 Cd=40 Bt=3120<br />
      TABLE ACCESS FULL PARENT1 [:Q65000] [AN] Ct=20 Cd=40 Bt=1040<br />
      VIEW  KBWYV1 [:Q65001]<br />
        UNION-ALL PARTITION  [:Q65001]<br />
          FILTER   [:Q64000]<br />
            TABLE ACCESS FULL KBWYT1 [AN] Ct=11 Cd=2000 Bt=104000<br />
          TABLE ACCESS FULL KBWYT2 [AN] Ct=11 Cd=2000 Bt=104000<br />
          TABLE ACCESS FULL KBWYT3 [AN] Ct=11 Cd=2000 Bt=104000<br />
          FILTER   [:Q61000]<br />
            TABLE ACCESS FULL KBWYT4 [AN] Ct=11 Cd=2000 Bt=104000</p>
<p>KBWYV1 is a view on 4 tables KBWYT1-4. KBWYT1-4 contain rows for week 31-34 respectively and are maintained by check constraints. This query should only return rows from partions 2 &amp; 3. The filter operation indicates this. Partitions 1 &amp; 4 are eliminated at execution time. The view line indicates that the view is not merged. The union-all partion information indicates that we have recognised this as a partition view. Note that the tables can be accessed in parallel.</p>
<p>Remote Queries</p>
<p>Only shows remote in the OPERATION column OTHER column shows query executed on remote node OTHER_NODE shows where it is executed Different operational characteristics for RBO &amp; CBO</p>
<p>RBO &#8211; Drags everything across the link and joins locally<br />
CBO &#8211; Uses cost estimates to determine whether to execute remotely or locally</p>
<p>SQL&gt;  explain plan for<br />
select *<br />
from dept@loop_link;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
SELECT STATEMENT REMOTE  [CHOOSE] Cost=1<br />
  TABLE ACCESS FULL DEPT [SJD.WORLD] [ANALYZED]</p>
<p>In this case the whole query has been sent to the remote site. The other column shows nothing.</p>
<p>SQL&gt; explain plan for<br />
select a.dname,avg(b.sal),max(b.sal)<br />
from dept@loop_link a, emp b<br />
where a.deptno=b.deptno<br />
group by a.dname<br />
order by max(b.sal),avg(b.sal) desc;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
SELECT STATEMENT   [CHOOSE] Cost=20<br />
  SORT ORDER BY  [:Q137003] [PARALLEL_TO_SERIAL]<br />
    SORT GROUP BY  [:Q137002] [PARALLEL_TO_PARALLEL]<br />
      NESTED LOOPS   [:Q137001] [PARALLEL_TO_PARALLEL]<br />
        REMOTE   [:Q137000] [PARALLEL_FROM_SERIAL]<br />
        TABLE ACCESS FULL EMP [:Q137001] [ANALYZED]<br />
        [PARALLEL_COMBINED_WITH_PARENT]</p>
<p>Bind Variables</p>
<p>Bind variables are recommended in most cases because they promote sharing of sql code<br />
At parse time the parser has NO IDEA what the bind variable contains. With RBO this makes no difference but with CBO, which relies on accurate statistics to produce plans, this can be a problem.</p>
<p>Defining bind variables in sqlplus:</p>
<p>variable x varchar2(18);<br />
assigning values:<br />
begin<br />
 <img src='http://s2.wp.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' />  := &#8216;hello&#8217;;<br />
end;<br />
/</p>
<p>SQL&gt; explain plan for<br />
select *<br />
from dept<br />
where rowid = &#8216;:x&#8217;;</p>
<p>Query Plan<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
SELECT STATEMENT [CHOOSE] Cost=1<br />
  TABLE ACCESS BY ROWID DEPT [ANALYZED]</p>
<p>Parallel Query</p>
<p>Main indicators that a query is using PQO:</p>
<ul>
<li>[:Q1000004] entries in the explain plan</li>
<li>Checkout the other column for details of what the slaves are executing</li>
<li>v$pq_slave will show any parallel activity</li>
</ul>
<p>Columns to look in for information</p>
<ul>
<li>other &#8211; contains the query passed to the slaves</li>
<li>other_tag &#8211; describes the contents of other</li>
<li>object_node &#8211; indicates order of pqo slaves</li>
</ul>
<p>Parallel Query operates on a producer/consumer basis. When you specify parallel degree 4 oracle tries to allocate 4 producer slaves and 4 consumer slaves. The producers can feed any of the consumers. If there are only 2 slaves available then we use these. If there is only 1 slave available then we go serial If there are none available then we use serial. If parallel_min_percent is set then we error ora 12827 instead of using a lower number of slaves or going serial</p>
<p>Consumer processes typically perform a sorting function. If there is no requirement for the data to be sorted then the consumer slaves are not produced and we end up with the number of slaves used matching the degree of parallelism as opposed to being 2x the degree.</p>
<p>Parallel Terms</p>
<table border="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td width="50%" valign="top">PARALLEL_FROM_SERIAL</td>
<td width="50%" valign="top">This means that source of the data is serial but it is passed to a parallel consumer</td>
</tr>
<tr>
<td width="50%" valign="top">PARALLEL_TO_PARALLEL</td>
<td width="50%" valign="top">Both the consumer and the producer are  parallel</td>
</tr>
<tr>
<td width="50%" valign="top">PARALLEL_COMBINED_WITH_PARENT</td>
<td width="50%" valign="top">This operation has been combined with the parent operator. For example in a sort merge join the sort operations would be shown as PARALLEL_COMBINED_WITH_PARENT because the sort and the merge are handled as 1 operation.</td>
</tr>
<tr>
<td width="50%" valign="top">PARALELL_TO_SERIAL</td>
<td width="50%" valign="top">The source of the data is parallel but it is passed to a serial consumer. This typically will happen at the top of the explain plan but could occur anywhere</td>
</tr>
</tbody>
</table>
<p>Examples of parallel queries</p>
<p>Assumptions</p>
<p>OPTIMIZER_MODE = CHOOSE<br />
DEPT is small compared to EMP<br />
DEPT has an index (DEPT_INDX) on deptno column</p>
<p>Three examples are presented</p>
<p>Query #1:  Serial<br />
Query #2:  Parallel<br />
Query #3:  Parallel, with forced optimization to RULE and forced usage of DEPT_INDX</p>
<p>Sample Query #1 (Serial)</p>
<p>select A.dname, avg(B.sal), max(B.sal)<br />
from  dept A, emp B<br />
where A.deptno = B.deptno<br />
group by A.dname<br />
order by max(B.sal), avg(B.sal) desc;</p>
<p>Execution Plan #1 (Serial)</p>
<p>OBJECT_NAME                      OBJECT_NODE OTHER<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-  &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;-<br />
SELECT STATEMENT<br />
 SORT ORDER BY<br />
   SORT GROUP BY<br />
     MERGE JOIN<br />
       SORT JOIN<br />
         TABLE ACCESS FULL emp<br />
       SORT JOIN<br />
         TABLE ACCESS FULL dept</p>
<p>Notice that the object_node and other columns are empty</p>
<p>Sample Query #2 (Query #1 with parallel hints)</p>
<p>select <strong>/*+ parallel(B,4) parallel(A,4) */</strong><br />
A.dname, avg(B.sal), max(B.sal)<br />
from  dept A, emp B<br />
where A.deptno = B.deptno<br />
group by A.dname<br />
order by max(B.sal), avg(B.sal) desc;</p>
<p>Execution Plan #2  (Parallel)</p>
<p>OBJECT_NAME                      OBJECT_NODE OTHER<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-  &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;-<br />
SELECT STATEMENT      Cost = ??<br />
 SORT ORDER BY                   :Q55004     **[7]**<br />
   SORT GROUP BY                 :Q55003     **[6]**<br />
     MERGE JOIN                  :Q55002     **[5]**<br />
       SORT JOIN                 :Q55002     **[4]**<br />
         TABLE ACCESS FULL emp   :Q55001     **[2]**<br />
       SORT JOIN                 :Q55002     **[3]**<br />
         TABLE ACCESS FULL dept  :Q55000     **[1]**</p>
<p>Execution Plan #2  &#8212; OTHER column</p>
<p>**[1]**  (:Q55000) &#8220;PARALLEL_FROM_SERIAL&#8221;</p>
<p>Serial execution of SELECT DEPTNO, DNAME FROM DEPT</p>
<p>**[2]**  (:Q55001) &#8220;PARALLEL_TO_PARALLEL&#8221;</p>
<p>        SELECT /*+ ROWID(A1)*/<br />
        A1.&#8221;DEPTNO&#8221; C0, A1.&#8221;SAL&#8221; C1<br />
        FROM &#8220;EMP&#8221; A1<br />
        WHERE ROWID BETWEEN :1 AND :2</p>
<p>**[3]**  (:Q55002) &#8220;PARALLEL_COMBINED_WITH_PARENT&#8221;<br />
**[4]**  (:Q55002) &#8220;PARALLEL_COMBINED_WITH_PARENT&#8221;<br />
**[5]**  (:Q55002) &#8220;PARALLEL_TO_PARALLEL&#8221;</p>
<p>        SELECT /*+ ORDERED USE_MERGE(A2)*/<br />
        A2.C1 C0, A1.C1 C1<br />
        FROM :Q55001 A1,:Q55000 A2<br />
        WHERE A1.C0=A2.C0</p>
<p>**[6]**  (:Q55003) &#8220;PARALLEL_TO_PARALLEL&#8221;</p>
<p>        SELECT MAX(A1.C1) C0, AVG(A1.C1) C1, A1.C0 C2<br />
        FROM :Q55002 A1<br />
        GROUP BY A1.C0</p>
<p>**[7]**  (:Q55004) &#8220;PARALLEL_FROM_SERIAL&#8221;</p>
<p>        SELECT A1.C0 C0, A1.C1 C1, A1.C2 C2<br />
        FROM :Q55003 A1<br />
        ORDER BY A1.CO, A1.C1 DESC</p>
<p>Sample Query #3 (Query #2 with fudged hints)</p>
<p>select /*+ index(A dept_indx) <strong>parallel(B,4) parallel(A,4)</strong> */<br />
      A.dname, avg(B.sal), max(B.sal)<br />
from  dept A, emp B<br />
where A.deptno = B.deptno<br />
group by A.dname<br />
order by max(B.sal), avg(B.sal) desc;</p>
<p>Execution Plan #3  (Parallel)</p>
<p>OBJECT_NAME                         OBJECT_NODE OTHER<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;-<br />
SELECT STATEMENT          Cost = ??<br />
 SORT ORDER BY                      :Q58002     **[6]**<br />
   SORT GROUP BY                    :Q58001     **[5]**<br />
     NESTED LOOPS JOIN              :Q58000     **[4]**<br />
       TABLE ACCESS FULL emp        :Q58000     **[3]**<br />
       TABLE ACCESS BY ROWID dept   :Q58000     **[2]**<br />
         INDEX RANGE SCAN dept_indx :Q58000     **[1]**</p>
<p>Execution Plan #3  &#8212; OTHER column</p>
<p>**[1]**  (:Q58000) &#8220;PARALLEL_COMBINED_WITH_PARENT&#8221;<br />
**[2]**  (:Q58000) &#8220;PARALLEL_COMBINED_WITH_PARENT&#8221;<br />
**[3]**  (:Q58000) &#8220;PARALLEL_COMBINED_WITH_PARENT&#8221;<br />
**[4]**  (:Q58000) &#8220;PARALLEL_TO_PARALLEL&#8221;</p>
<p>        SELECT /*+ ORDERED USE_NL(A2) INDEX(A2) */<br />
        A2.&#8221;DNAME&#8221; C0, A1.C0 C1<br />
        FROM<br />
          (SELECT /*+ ROWID(A3) */<br />
           A3.&#8221;SAL&#8221; CO, A3.&#8221;DEPTNO&#8221; C1<br />
           FROM &#8220;EMP&#8221; A3<br />
           WHERE ROWID BETWEEN :1 AND :2) A1,<br />
          &#8220;DEPT&#8221; A2<br />
        WHERE A2.&#8221;DEPTNO&#8221; = A1.C1</p>
<p>**[5]**  (:Q58001) &#8220;PARALLEL_TO_PARALLEL&#8221;</p>
<p>        SELECT MAX(A1.C1) C0, AVG(A1.C1) C1, A1.C0 C2<br />
        FROM :Q58000 A1<br />
        GROUP BY A1.C0</p>
<p>**[6]**  (:Q58002) &#8220;PARALLEL_TO_SERIAL&#8221;</p>
<p>        SELECT A1.C0 C0, A1.C1 C1, A1.C2 C2<br />
        FROM :Q58001 A1<br />
        ORDER BY A1.C0, A1.C1 DESC</p>
<p>How to obtain explain plans</p>
<p>Explain plan for</p>
<p>Main advantage is that it does not actually run the query &#8211; just parses the sql. This means that it executes quickly. In the early stages of tuning explain plan gives you an idea of the potential performance of your query without actually running it. You can then make a judgement as to any modifications you may choose to make.</p>
<p>Autotrace</p>
<p>Autotrace can be configured to run the sql &amp; gives a plan  and statistics afterwards or just give you an explain plan without executing the query.</p>
<p>Tkprof</p>
<p>Analyzes trace file</td>
</tr>
</tbody>
</table>
<p>ting the query.</p>
<p>Tkprof</p>
<p> </p>
<p>This article is from ; <a href="http://www.akadia.com/services/ora_interpreting_explain_plan.html">http://www.akadia.com/services/ora_interpreting_explain_plan.html</a></p>
<p>Analyzes trace file</td>
</tr>
</tbody>
</table>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/enginozer.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/enginozer.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/enginozer.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/enginozer.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/enginozer.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/enginozer.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/enginozer.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/enginozer.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/enginozer.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/enginozer.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/enginozer.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/enginozer.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/enginozer.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/enginozer.wordpress.com/64/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=64&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://enginozer.wordpress.com/2009/09/25/oracle-explain-plan-2-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/d115faa5b1011c2837cb2de5b694342f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">enginozer</media:title>
		</media:content>

		<media:content url="http://enginozer.wordpress.com/img/zurueck.gif" medium="image">
			<media:title type="html">Zurück</media:title>
		</media:content>

		<media:content url="http://enginozer.wordpress.com/img/bullet-gruen.gif" medium="image" />

		<media:content url="http://enginozer.wordpress.com/img/bullet-gruen.gif" medium="image" />

		<media:content url="http://enginozer.wordpress.com/img/zurueck.gif" medium="image">
			<media:title type="html">Zurück</media:title>
		</media:content>

		<media:content url="http://enginozer.wordpress.com/img/bullet-gruen.gif" medium="image" />

		<media:content url="http://enginozer.wordpress.com/img/bullet-gruen.gif" medium="image" />
	</item>
		<item>
		<title>Oracle Explain Plan</title>
		<link>http://enginozer.wordpress.com/2009/09/25/oracle-explain-plan-2/</link>
		<comments>http://enginozer.wordpress.com/2009/09/25/oracle-explain-plan-2/#comments</comments>
		<pubDate>Fri, 25 Sep 2009 07:17:09 +0000</pubDate>
		<dc:creator>Engin ÖZER</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://enginozer.wordpress.com/?p=62</guid>
		<description><![CDATA[  Oracle Explain Plan Terms:   Table Access Full: Oracle reads every row in selected table. In terms of that there is no primary key or index defined with the columns that you use at your where clause Oracle makes full table access. OPERATION                      OPTIONS         OBJECT_NAME &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; SELECT STATEMENT TABLE ACCESS                   FULL            Students [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=62&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p> </p>
<p><strong>Oracle Explain Plan Terms:</strong></p>
<p> </p>
<p><strong>Table Access Full:</strong> Oracle reads every row in selected table. In terms of that there is no primary key or index defined with the columns that you use at your where clause Oracle makes full table access.</p>
<p>OPERATION                      OPTIONS         OBJECT_NAME</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>SELECT STATEMENT</p>
<p>TABLE ACCESS                   FULL            Students</p>
<p><strong>Selecting With Primary Keys: </strong> Primary key defines a row uniquely. By using table’s primary key at where clause Oracle makes <strong>Index RowID Unique Scan</strong>   in order to select unique row which reduces our total cost so much.</p>
<pre>OPERATION                      OPTIONS         OBJECT_NAME
------------------------------ --------------- --------------------
SELECT STATEMENT
TABLE ACCESS                   BY INDEX ROWID  Students
INDEX                          UNIQUE SCAN     Students _PK</pre>
<p> </p>
<p><strong>Selecting With Ununique Indexes: </strong>Ununique indexes reduces the cost of full table scan. But, of course if we have change to use primary keys we should use them. If we can’t we can use other non-unique indexes. The reducement of cost varies due to index’s definition. If the uniqueness of our index columns raises retrieval time will go down. Because of this reason, use the criteria that will catch the fastest index for that query.</p>
<p>OPERATION                      OPTIONS         OBJECT_NAME</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>SELECT STATEMENT</p>
<p>TABLE ACCESS                   BY INDEX ROWID  Students</p>
<p>INDEX                          RANGE SCAN      Students_IDX1</p>
<p> </p>
<p><strong>Efficiency of Join Statements:  </strong>We should be very careful at writing join statements. The column you choose the join plays very important role here. The most important thing we have to consider at creating joins is using indexed columns at where clause. Let’s assume that there is a table “StudentMarks” keeping marks of students for some particular lesson.For example phisics. The statement following retrievs  all the marks of the students having the name “ENGİN”. StudentMarks table does not have any foreign key at student column. İts only index column is mark.</p>
<p>select s.name,s.surname,m.mark<br />
  from StudentMarks m, Students s<br />
 where s.name = ‘ENGİN’</p>
<p>   and s.id = m.studentid</p>
<p>   and m.mark between 10000 and 30000</p>
<p> </p>
<p>OPERATION                      OPTIONS         OBJECT_NAME</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>SELECT STATEMENT</p>
<p>NESTED LOOPS</p>
<p>TABLE ACCESS                   BY INDEX ROWID  Students</p>
<p>INDEX                          RANGE SCAN      Student_IDX2</p>
<p>TABLE ACCESS                   BY INDEX ROWID  StudentMarks</p>
<p>INDEX                          RANGE SCAN      StudentMarks_IDX3</p>
<p> </p>
<p>All the columns are catched by <strong>range scan</strong>.</p>
<p> </p>
<p><strong>Choosing The Fastest Index: </strong>Indexes varies by the mean of cost. To query with minimum cost, we have to keep in mind the table’s count of rows. We should always give our priority to most sized table by the mean of choosing where clauses. Let’s take an example to examine this furter;<strong> </strong></p>
<p><strong> </strong></p>
<p>select s.name,s.surname,m.mark<br />
  from StudentMarks m, Students s<br />
 where s.name = ‘ENGİN’</p>
<p>   and s.id = m.studentid</p>
<p>   and m.mark between 10000 and 30000</p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>Will produce this plan:</p>
<p>OPERATION                      OPTIONS         OBJECT_NAME</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>SELECT STATEMENT</p>
<p>NESTED LOOPS</p>
<p>TABLE ACCESS                   BY INDEX ROWID  Students</p>
<p>INDEX                          RANGE SCAN      Students_IDX2</p>
<p>TABLE ACCESS                   BY INDEX ROWID  StudentMarks</p>
<p>INDEX                          RANGE SCAN      StudentMarks _IDX3</p>
<p>Oracle, tries to determine whether students or studentsmarks table is the best to index first. At the term oracle can not decide, Oracle starts indexing at the order of right to left at from clause. Be careful about this query. It is the same with the one above. But, at this query Oracle starts indexing with StudentMarks table.</p>
<p> select s.name,s.surname,m.mark<br />
  from Students s, StudentMarks m<br />
 where s.name = ‘ENGİN’</p>
<p>   and s.id = m.studentid</p>
<p>   and m.mark between 10000 and 30000</p>
<p> </p>
<p>This query produces this plan:</p>
<p>OPERATION                      OPTIONS         OBJECT_NAME</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>SELECT STATEMENT</p>
<p>NESTED LOOPS</p>
<p>TABLE ACCESS                   BY INDEX ROWID  StudentMarks</p>
<p>INDEX                          RANGE SCAN      StudentMarks _IDX3</p>
<p>TABLE ACCESS                   BY INDEX ROWID  Students</p>
<p>INDEX                          RANGE SCAN      Students_IDX2</p>
<p> </p>
<p>StudentMarks.mark is queried first because we told Oracle to do so.</p>
<p> </p>
<p> </p>
<p><strong> </strong></p>
<p><strong>Utilizing Multiple Column Indexes</strong></p>
<p><strong>Utilizing Multiple Column Indexes: </strong>Multiple Column indexes uses more than one column for indexing. In order to use that index we have to search for the first column before we search second column in the index. To get more efficient queries from that indexes we have to choose index criteria more uniquely. Unique columns are also indexes. It is the best way to use unique indexes to get one specific row.</p>
<p><strong>Oracle Explan Plan Terms</strong></p>
<p>Whenever you read or write data in Oracle, you do so by issuing an <a href="http://www.adp-gmbh.ch/ora/concepts/sql_stmt.html">SQL statement</a>. One of Oracle&#8217;s task when it receives such a statement is to build a <a href="http://www.adp-gmbh.ch/ora/sql/execution_plan.html">query execution plan</a>. An execution plan defines how Oracle finds or writes the data. For example, an important decision that Oracle has to take is if it uses indexes or not. And if there are more indexes, which of these is used. All this is contained in an execution plan.</p>
<p>If one wants to explore such an execution plan, Oracle provides the SQL statement <strong>EXPLAIN PLAN</strong> to determine this.</p>
<p>The general syntax of EXPLAIN PLAN is:</p>
<pre><a href="http://www.adp-gmbh.ch/ora/sql/explain_plan.html">explain plan</a> for your-precious-sql-statement;</pre>
<p> </p>
<p>If you do an EXPLAIN PLAN, Oracle will analyze the statment and fill a special table with the Execution plan for that statement. You can indicate which table has to be filled with the following SQL command:</p>
<pre><a href="http://www.adp-gmbh.ch/ora/sql/explain_plan.html">explain plan</a> into table_name for your-precious-sql-statement;</pre>
<p>If you omit the INTO TABLE_NAME clause, Oracle fills a table named PLAN_TABLE by default.</p>
<p> </p>
<p>Whenever you read or write data in Oracle, you do so by issuing an <a href="http://www.adp-gmbh.ch/ora/concepts/sql_stmt.html">SQL statement</a>. One of Oracle&#8217;s task when it receives such a statement is to build a <a href="http://www.adp-gmbh.ch/ora/sql/execution_plan.html">query execution plan</a>. An execution plan defines how Oracle finds or writes the data. For example, an important decision that Oracle has to take is if it uses indexes or not. And if there are more indexes, which of these is used. All this is contained in an execution plan.</p>
<p>If one wants to explore such an execution plan, Oracle provides the SQL statement <strong>EXPLAIN PLAN</strong> to determine this.</p>
<p>The general syntax of EXPLAIN PLAN is:</p>
<pre><a href="http://www.adp-gmbh.ch/ora/sql/explain_plan.html">explain plan</a> for your-precious-sql-statement;</pre>
<p>If you do an EXPLAIN PLAN, Oracle will analyze the statment and fill a special table with the Execution plan for that statement. You can indicate which table has to be filled with the following SQL command:</p>
<pre><a href="http://www.adp-gmbh.ch/ora/sql/explain_plan.html">explain plan</a> into table_name for your-precious-sql-statement;</pre>
<p>If you omit the INTO TABLE_NAME clause, Oracle fills a table named PLAN_TABLE by default.</p>
<h2>The Plan Table</h2>
<p>The plan table is the table that Oracle fills when you have it explain an execution plan for an SQL statement. You must make sure such a plan table exists. Oracle ships with the script UTLXPLAN.SQL which creates this table, named PLAN_TABLE (which is the default name used by EXPLAIN PLAN). If you like, however, you can choose any other name for the plan table, as long as you have been granted insert on it and it has all the fields as <a href="http://www.adp-gmbh.ch/ora/exp_plan/plan_table.html">here</a>.</p>
<h3>The fields (attributes) within the plan table</h3>
<p>Arguably, the <strong>most important fields</strong> within the plan table are <em>operation</em>, <em>option</em>, <em>object_name</em>, <em>id</em>, and <em>parent_id</em>. The pair operation and object_name define what operation would be done on (or with) object_name. If an operation has an id which other operations have as parent_id, it means the other operations feed their result to the parent.</p>
<p> </p>
<p>Possible <strong>values for operation</strong> are:</p>
<ul>
<li>DELETE STATEMENT</li>
<li>INSERT STATEMENT</li>
<li>SELECT STATEMENT</li>
<li>UPDATE STATEMENT</li>
<li>AND-EQUAL</li>
<li>CONNECT BY</li>
<li>CONCATENATION</li>
<li>COUNT</li>
<li>DOMAIN INDEX</li>
<li>FILTER</li>
<li>FIRST ROW</li>
<li>FOR UPDATE</li>
<li>HASH JOIN</li>
<li>INDEX</li>
<li>INLIST ITERATOR</li>
<li>INTERSECTION</li>
<li><a href="http://www.adp-gmbh.ch/ora/sql/join/join_methods.html#merge_join">MERGE JOIN</a></li>
<li>MINUS</li>
<li><a href="http://www.adp-gmbh.ch/ora/sql/join/join_methods.html#nested_loops">NESTED LOOPS</a></li>
<li>PARTITION,</li>
<li>REMOTE</li>
<li>SEQUENCE</li>
<li>SORT</li>
<li>TABLE ACCESS</li>
<li>UNION</li>
<li>VIEW</li>
</ul>
<p> </p>
<p> </p>
<p><strong>Option</strong> tells more about how an operation would be done. For example, the operation TABLE ACCESS can have the options: FULL or BY ROWID or many others. Full in this case means, that the entire table is accessed (takes a long time if table is huge) whereas BY ROWID means, Oracle knows where (from which block) the rows are to be retrieved, which makes the time to access the table shorter.</p>
<h3>dbms_xplan</h3>
<p>As of 9i, <a href="http://www.adp-gmbh.ch/ora/plsql/dbms_xplan.html">dbms_xplan</a> can be used to format the plan table.</p>
<h2>Operations</h2>
<p>The following table is used to demonstrate EXPLAIN PLAN:</p>
<p>create table test_for_ep (a number, b varchar2(100));</p>
<p>Now, let&#8217;s explain the plan for selecting everything on that table:</p>
<p>delete plan_table;</p>
<p>explain plan for select /*+ rule */ * from test_for_ep where a = 5;</p>
<p> </p>
<h2>Displaying the execution plan</h2>
<p>In order to view the explained plan, we have to query the plan table:</p>
<pre>select
  substr (lpad(' ', level-1) || operation || ' (' || options || ')',1,30 ) "Operation",
  object_name                                                              "Object"
from
  plan_table
<a href="http://www.adp-gmbh.ch/ora/sql/connect_by.html#start_with">start with</a> id = 0
<a href="http://www.adp-gmbh.ch/ora/sql/connect_by.html#connect_by_prior">connect by prior</a>;</pre>
<p>This statement is a simplified version of <strong>utlxpls.sql</strong>. utlxpls.sql is a script that Oracle ships.</p>
<p>Here&#8217;s the output of the explain plan:</p>
<pre>SELECT STATEMENT ()
 TABLE ACCESS (FULL)           TEST_FOR_EP</pre>
<p>First, take a look at the indention: TABLE ACCESS is indented right. In an explain plan output, the more indented an operation is, the earlier it is executed. And the result of this operation (or operations, if more than one have are equally indented AND have the same parent) is then feeded to the parent operation. In this case, TABLE ACCESS is made first, and its result feeded to SELECT STATEMENT (which is not an actual operation). Note the FULL in paranthesis in TABLE ACCESS: this means that the entire table is accessed.</p>
<p>Btw, <a href="http://www.adp-gmbh.ch/ora/sqlplus/index.html">sql*plus</a> automatically explains the plan for you if <a href="http://www.adp-gmbh.ch/ora/sqlplus/autotrace.html">autotrace</a> is enabled.</p>
<p>Now, let&#8217;s create an index on that table:</p>
<pre>create index test_for_ep_ix on test_for_ep (a);</pre>
<p>And do the same select statement again:</p>
<pre>delete plan_table;
explain plan for select /*+ rule */ * from test_for_ep where a = 5;</pre>
<p>The plan is now:</p>
<pre>SELECT STATEMENT ()
 TABLE ACCESS (BY INDEX <strong>ROWID</strong>) TEST_FOR_EP
  INDEX (RANGE SCAN)           TEST_FOR_EP_IX</pre>
<p>Obviously, the index (TEST_FOR_EP_IX) is used first (most indented) then used for a TABLE ACCESS, second most indented, then the result is returned. The table access is not done by a <a href="http://www.adp-gmbh.ch/ora/tuning/fulltablescan.html">full table scan</a> but rather by using the data&#8217;s rowid.</p>
<h3>INDEX</h3>
<p>In the last example, Oracle employed an INDEX (RANGE SCAN). The RANGE SCAN basically means, that the index was used, but that it can return more than one row. Now, we create a unique index to see how this alters the explain plan:</p>
<pre>create table test_for_ep (a number, b varchar2(100), constraint uq_tp unique(a));
 
delete plan_table;
explain plan for select /*+ rule */ * from test_for_ep where a = 5;</pre>
<p>The explained plan is:</p>
<pre>SELECT STATEMENT ()
 TABLE ACCESS (BY INDEX ROWID) TEST_FOR_EP
  INDEX (<strong>UNIQUE SCAN</strong>)          UQ_TP</pre>
<p>INDEX (UNIQUE SCAN) means, that this index is used, and it sort of guarantees that this index returnes exactly one <a href="http://www.adp-gmbh.ch/ora/concepts/rowid.html">rowid</a>. What happens, if we query the field not for equality but for greater than (a&gt;5)?</p>
<pre>explain plan for select /*+ rule */ * from test_for_ep where a &gt; 5;</pre>
<p>Here, we see that the index is used, but for a RANGE SCAN:</p>
<pre>SELECT STATEMENT ()
 TABLE ACCESS (BY INDEX ROWID) TEST_FOR_EP
  INDEX (<strong>RANGE SCAN</strong>)           UQ_TP</pre>
<p>If we only query fields of a table that are already in an index, Oracle doesn&#8217;t have to read the data blocks because it can get the relevant data from the index:</p>
<pre>create table test_for_ep (a number, b varchar2(100), constraint uq_tp unique(a));
 
delete plan_table;
explain plan for select /*+ rule */ a from test_for_ep where a &gt; 5 and a &lt; 50;</pre>
<p>Here&#8217;s the <a href="http://www.adp-gmbh.ch/ora/sql/execution_plan.html">query execution plan</a>execution plan. No table access anymore!</p>
<pre>SELECT STATEMENT ()
 INDEX (RANGE SCAN)            UQ_TP</pre>
<h3>MERGE JOIN</h3>
<p>See <a href="http://www.adp-gmbh.ch/ora/sql/join/join_methods.html#merge_join">here</a>. The first table&#8217;s join key is <strong>ba</strong> while the second table&#8217;s join key is <strong>aa</strong>.</p>
<pre>create table <strong>test_for_ep_a</strong> (aa number, ab varchar2(100));
create table <strong>test_for_ep_b</strong> (ba number, bb varchar2(100));</pre>
<p>Note, there are no indexes on both of the tables. Now, we join the tables on <strong>aa</strong> and <strong>ba</strong>:</p>
<pre>explain plan for
  select /*+ rule */ a.aa from test_for_ep_a a, test_for_ep_b b where
  a.aa=b.ba and a.aa &gt; 5;</pre>
<p>As there are no indexes, both tables must be TABLE ACCESSed (FULL). After these accesses, their results are sorted.</p>
<pre>SELECT STATEMENT ()
 MERGE JOIN ()
  SORT (JOIN)
   TABLE ACCESS (FULL)         TEST_FOR_EP_B
  SORT (JOIN)
   TABLE ACCESS (FULL)         TEST_FOR_EP_A</pre>
<p>Note MERGE JOINs can only be used for equi joins, as is demonstrated in NESTED LOOPS</p>
<p> </p>
<h3>NESTED LOOPS</h3>
<p>For each relevant row in the first table (driving table), find all matching rows in the other table (probed table).</p>
<p>See also <a href="http://www.adp-gmbh.ch/ora/sql/join/join_methods.html#nested_loops">here</a>.</p>
<pre>create table <strong>test_for_ep_a</strong> (aa number, ab varchar2(100));
create table <strong>test_for_ep_b</strong> (ba number, bb varchar2(100));
explain plan for
  select /*+ rule */ a.aa from test_for_ep_a a, test_for_ep_b b where
  a.aa &gt; b.ba and a.aa &gt; 5;</pre>
<p>Note, there is no equi join to join test_for_ep_a and test_for_ep_b, (a.aa &gt; b.ba)</p>
<pre>SELECT STATEMENT ()
 NESTED LOOPS ()
  TABLE ACCESS (FULL)          TEST_FOR_EP_B
  TABLE ACCESS (FULL)          TEST_FOR_EP_A</pre>
<p>Now, we put an index on TEST_FOR_EP_B and see how that influences our nested loop:</p>
<pre>create table test_for_ep_a (aa number, ab varchar2(100));
create table test_for_ep_b (ba number, bb varchar2(100), <strong>constraint uq_ba unique(ba)</strong>);
 
delete plan_table;
explain plan for
  select /*+ rule */ a.aa from test_for_ep_a a, test_for_ep_b b where
  a.aa &gt; b.ba;</pre>
<p>The plan is:</p>
<pre>SELECT STATEMENT ()
 NESTED LOOPS ()
  TABLE ACCESS (FULL)          TEST_FOR_EP_A
  INDEX (RANGE SCAN)           <strong>UQ_BA</strong></pre>
<p>Interpreted, this means: TEST_FOR_EP_A is fully accessed and for each row, TEST_FOR_EP_B (or more accurately, its index UQ_BA) is probed. Thinking about it, this makes sense, doing the costly TABLE ACCESS once and use the index for each row. Then again, thinking about it, if TEST_FOR_EP_A is very small nad TEST_FOR_EP_B is large, this doesn&#8217;t make sense anymore. This is when the Cost Based Optimizer comes into play.</p>
<h2>Sorts</h2>
<h3>Aggregate Sorts</h3>
<p>Whenever a result set must be sorted, the operation is <strong>sort</strong>. If this sort is used to return a single row (for example <strong>max</strong> or <strong>min</strong>) the options is <strong>AGGREGATE</strong>. Consider the following example:</p>
<pre>create table t_ep (
  w date,
  v number,
  x varchar2(40)
);
 
 
delete plan_table;
explain plan for select /*+ rule */ max(w) from t_ep where v=4;
SELECT STATEMENT ()
 <strong>SORT (AGGREGATE)</strong>
  TABLE ACCESS (FULL)          T_EP</pre>
<p>Now: creating an index:</p>
<pre>alter table t_ep add constraint uq_t_ep unique(v);
delete plan_table;
explain plan for select /*+ rule */ max(w) from t_ep where v=4;
SELECT STATEMENT ()
 SORT (AGGREGATE)
  TABLE ACCESS (BY INDEX ROWID) T_EP
   INDEX (UNIQUE SCAN)          UQ_T_EP</pre>
<h2>TKPROF</h2>
<p>If you want to know, how much time an SQL statement acutally used, use <a href="http://www.adp-gmbh.ch/ora/tuning/tkprof/index.html">TKPROF</a></p>
<p> </p>
<p><strong> </strong></p>
<p><strong>References</strong>Top of Form</p>
<p><strong> </strong></p>
<p>-          <a href="http://www.evolt.org/node/2986"><strong>http://www.evolt.org/node/2986</strong></a><strong> </strong></p>
<p><strong>Adam Patrick, </strong>Use Oracle&#8217;s Explain Plan to Tune Your Queries, <strong>August 24, 2000</strong></p>
<p>-          <a href="http://www.adp-gmbh.ch/ora/explainplan.html"><strong>http://www.adp-gmbh.ch/ora/explainplan.html</strong></a><strong></strong></p>
<p><strong> </strong></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/enginozer.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/enginozer.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/enginozer.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/enginozer.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/enginozer.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/enginozer.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/enginozer.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/enginozer.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/enginozer.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/enginozer.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/enginozer.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/enginozer.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/enginozer.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/enginozer.wordpress.com/62/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=enginozer.wordpress.com&amp;blog=2597612&amp;post=62&amp;subd=enginozer&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://enginozer.wordpress.com/2009/09/25/oracle-explain-plan-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/d115faa5b1011c2837cb2de5b694342f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">enginozer</media:title>
		</media:content>
	</item>
	</channel>
</rss>
