<?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>Oracle Italia by Massimo Ruocchio</title>
	<atom:link href="http://oracleitalia.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://oracleitalia.wordpress.com</link>
	<description>Il mondo Oracle in italiano...</description>
	<lastBuildDate>Fri, 09 Dec 2011 09:51:58 +0000</lastBuildDate>
	<language>it</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='oracleitalia.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Oracle Italia by Massimo Ruocchio</title>
		<link>http://oracleitalia.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://oracleitalia.wordpress.com/osd.xml" title="Oracle Italia by Massimo Ruocchio" />
	<atom:link rel='hub' href='http://oracleitalia.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Master mind (parte seconda)</title>
		<link>http://oracleitalia.wordpress.com/2011/06/27/master-mind-parte-seconda/</link>
		<comments>http://oracleitalia.wordpress.com/2011/06/27/master-mind-parte-seconda/#comments</comments>
		<pubDate>Sun, 26 Jun 2011 23:14:28 +0000</pubDate>
		<dc:creator>massimoruocchio</dc:creator>
				<category><![CDATA[Giochi]]></category>
		<category><![CDATA[PL/SQL]]></category>

		<guid isPermaLink="false">http://oracleitalia.wordpress.com/?p=376</guid>
		<description><![CDATA[Nell&#8217;articolo precedente ho mostrato un package PL/SQL che funge da &#8220;codemaker&#8221; e consente di giocare a Master Mind contro il db Oracle. A questo punto mi sono anche posto il problema di scrivere un programma che potesse automaticamente giocare una partita nel modo migliore possibile. Ho scritto quindi il package mmp (scaricatelo qui). Ecco la [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=376&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Nell&#8217;articolo precedente ho mostrato un package PL/SQL che funge da &#8220;codemaker&#8221; e consente di giocare a Master Mind contro il db Oracle.<br />
A questo punto mi sono anche posto il problema di scrivere un programma che potesse automaticamente giocare una partita nel modo migliore possibile.<br />
Ho scritto quindi il package mmp (scaricatelo <a href="http://oracleitalia.files.wordpress.com/2011/06/mmp.doc">qui</a>).</p>
<p>Ecco la spec:</p>
<p><pre class="brush: sql; wrap-lines: false;">

create or replace package mmp is
function goAlone return number;
end;
/

</pre></p>
<p>Molto semplice, la funzione goAlone avvia una nuova partita e la gioca, restituendo il numero di tentativi che sono stati necessari a trovare la soluzione.</p>
<p>Vediamo un esempio:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; select mmp.goAlone from dual;

   GOALONE
----------
         6
#######################################
01 C6 C2 C1 C5:BW
#######################################
TRY AGAIN.
After this guess there are still 660 possible solutions
#######################################
01 C6 C2 C1 C5:BW
02 C3 C2 C8 C1:W
#######################################
TRY AGAIN.
After this guess there are still 147 possible solutions
#######################################
01 C6 C2 C1 C5:BW
02 C3 C2 C8 C1:W
03 C5 C1 C4 C5:BW
#######################################
TRY AGAIN.
After this guess there are still 23 possible solutions
#######################################
01 C6 C2 C1 C5:BW
02 C3 C2 C8 C1:W
03 C5 C1 C4 C5:BW
04 C4 C6 C3 C5:B
#######################################
TRY AGAIN.
After this guess there are still 6 possible solutions
#######################################
01 C6 C2 C1 C5:BW
02 C3 C2 C8 C1:W
03 C5 C1 C4 C5:BW
04 C4 C6 C3 C5:B
05 C7 C5 C2 C5:BBW
#######################################
TRY AGAIN.
After this guess there are still 1 possible solutions
#######################################
01 C6 C2 C1 C5:BW
02 C3 C2 C8 C1:W
03 C5 C1 C4 C5:BW
04 C4 C6 C3 C5:B
05 C7 C5 C2 C5:BBW
06 C2 C5 C5 C5:BBBB
#######################################
YOU GOT IT! WELL DONE!!!
C2 C5 C5 C5
</pre></p>
<p>Qualche dettaglio sull&#8217;algoritmo:<br />
All&#8217;inizio il package memorizza in un array associativo tutte le 4096 possibili combinazioni in ordine casuale.</p>
<p><pre class="brush: sql; wrap-lines: false;">

procedure fillSolTab is
begin

  if solTab.count &gt; 0 then
     solTab.delete(solTab.first,solTab.last);
  end if;

  with t as (
    select mm.c1 col from dual union all
    select mm.c2 from dual union all
    select mm.c3 from dual union all
    select mm.c4 from dual union all
    select mm.c5 from dual union all
    select mm.c6 from dual union all
    select mm.c7 from dual union all
    select mm.c8 from dual)
  select t1.col, t2.col, t3.col, t4.col
    bulk collect into solTab
    from t t1, t t2, t t3, t t4
  order by dbms_random.value;

end;

</pre></p>
<p>Poi prende la prima e la prova.</p>
<p><pre class="brush: sql; wrap-lines: false;">

function nextTry return number is
res mm.t_res;
sol t_sol := solTab(solTab.first);
begin
  res := mm.tryThis(sol.s1,sol.s2,sol.s3,sol.s4);

  if res.game_over = 0 then
     cleanSolTab(res.r1||res.r2||res.r3||res.r4);
  end if;

  return res.game_over;
end;

</pre></p>
<p>Ovviamente ottiene dal package mm un feedback che viene utilizzato per eliminare dalla tabella di tutte le combinazioni quelle che non possono essere più valide.</p>
<p><pre class="brush: sql; wrap-lines: false;">

procedure cleanSolTab(p_res in varchar2) is
i number;
currSol t_sol;
markForDelete number;
begin
  i := solTab.first;
  currSol := solTab(i);
  solTab.delete(i);
  i := solTab.first;
  loop
    if nvl(checkSol(solTab(i),currSol),'x') != nvl(p_res,'x') then
       markForDelete := i;
    else
       markForDelete := 0;
    end if;
    exit when i=solTab.last;
    i := solTab.next(i);
    if markForDelete&gt;0 then
       solTab.delete(markForDelete);
    end if;
  end loop;

  if markForDelete&gt;0 then
     solTab.delete(markForDelete);
  end if;

  dbms_output.put_line('After this guess there are still '||solTab.count||' possible solutions');
end;

</pre></p>
<p>Dopodiché continua a provare in questo modo finché non trova la combinazione giusta. </p>
<p><pre class="brush: sql; wrap-lines: false;">

function goAlone return number is
num number:=0;
begin
  startGame;
  loop
    num := num+1;
  exit when nextTry != 0;
  end loop;

  return num;

end;

</pre></p>
<p>Se si gioca un numero molto alto di partite (non vi dimenticate di spegnere il serverout!!) si vede che il mio metodo ottiene la soluzione con un numero medio di circa 5,5 tentativi:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; select min(g), max(g), avg(g), stddev(g)
  2  from (select mmp.goalone g from dual connect by level&lt;=100);

    MIN(G)     MAX(G)     AVG(G)  STDDEV(G)
---------- ---------- ---------- ----------
         3          8       5.47 .968754277

</pre></p>
<p>Se qualcuno trova un algoritmo che consente di ottenere una media più bassa mi faccia sapere&#8230;</p>
<p>Massimo</p>
<br />Filed under: <a href='http://oracleitalia.wordpress.com/category/giochi/'>Giochi</a>, <a href='http://oracleitalia.wordpress.com/category/plsql/'>PL/SQL</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/oracleitalia.wordpress.com/376/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/oracleitalia.wordpress.com/376/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/oracleitalia.wordpress.com/376/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/oracleitalia.wordpress.com/376/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/oracleitalia.wordpress.com/376/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/oracleitalia.wordpress.com/376/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/oracleitalia.wordpress.com/376/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/oracleitalia.wordpress.com/376/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/oracleitalia.wordpress.com/376/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/oracleitalia.wordpress.com/376/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/oracleitalia.wordpress.com/376/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/oracleitalia.wordpress.com/376/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/oracleitalia.wordpress.com/376/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/oracleitalia.wordpress.com/376/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=376&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://oracleitalia.wordpress.com/2011/06/27/master-mind-parte-seconda/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fbabbf605a3a90e522d71f6a0117055f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">massimoruocchio</media:title>
		</media:content>
	</item>
		<item>
		<title>Master Mind</title>
		<link>http://oracleitalia.wordpress.com/2011/06/26/master-mind/</link>
		<comments>http://oracleitalia.wordpress.com/2011/06/26/master-mind/#comments</comments>
		<pubDate>Sun, 26 Jun 2011 22:59:46 +0000</pubDate>
		<dc:creator>massimoruocchio</dc:creator>
				<category><![CDATA[Giochi]]></category>
		<category><![CDATA[PL/SQL]]></category>

		<guid isPermaLink="false">http://oracleitalia.wordpress.com/?p=371</guid>
		<description><![CDATA[Da ragazzino mi piaceva moltissimo giocare a Master Mind. La notte scorsa, turbato dai primi caldi estivi, ho scritto un package pl/sql che mi consente di giocare a MAster Mind contro il mio database Oracle. Il package mm include tutte le funzionalità per generare una chiave composta di quattro colori (su otto) e dare il [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=371&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Da ragazzino mi piaceva moltissimo giocare a Master Mind. </p>
<p>La notte scorsa, turbato dai primi caldi estivi, ho scritto un package pl/sql che mi consente di giocare a MAster Mind contro il mio database Oracle.</p>
<p>Il package mm include tutte le funzionalità per generare una chiave composta di quattro colori (su otto) e dare il feedback sui tentativi del giocatore.</p>
<p>Ecco la spec del package:</p>
<p><pre class="brush: sql; wrap-lines: false;">

create or replace package mm is

c1 constant char(2) := 'C1';
c2 constant char(2) := 'C2';
c3 constant char(2) := 'C3';
c4 constant char(2) := 'C4';
c5 constant char(2) := 'C5';
c6 constant char(2) := 'C6';
c7 constant char(2) := 'C7';
c8 constant char(2) := 'C8';

type t_res is record (r1 char(1), r2 char(1), r3 char(1), r4 char(1), game_over number(1));

procedure startGame(p_maxAttempts in number default 12);

procedure tryThis(s1 in char, s2 in char, s3 in char, s4 in char);

function tryThis(p_s1 in char, p_s2 in char, p_s3 in char, p_s4 in char) return t_res;

end;
/
</pre></p>
<p>Gli otto colori si chiamano c1, c2&#8230;c8.<br />
La procedura startGame avvia la partita selezionando la combinazione in maniera casuale. Il giocatore può indicare il numero massimo di tentativi che devono essere consentiti (il default è 12).<br />
La procedura tryThis si utilizza per fare un tentativo con una combinazione, l&#8217;esito del tentativo è stampato a video con DBMS_OUTPUT.</p>
<p>Vediamo un esempio:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; set feed off
SQL&gt; set serverout on
SQL&gt; exec mm.startGame
SQL&gt; exec mm.tryThis(mm.c1,mm.c2,mm.c3,mm.c4)
#######################################
01 C1 C2 C3 C4:W
#######################################
TRY AGAIN.
</pre></p>
<p>Ogni volta che chiamo tryThis vedo tutti i tentativi precedenti ed i relativi feedback (una lettera B per ogni colore esatto nel posto esatto, una lettera W per ogni colore esatto ma nel posto sbagliato).</p>
<p>In questo caso ho beccato la soluzione con sei tentativi:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; exec mm.tryThis(mm.c5,mm.c6,mm.c7,mm.c8)
#######################################
01 C1 C2 C3 C4:W
02 C5 C6 C7 C8:BW
#######################################
TRY AGAIN.
SQL&gt; exec mm.tryThis(mm.c5,mm.c7,mm.c1,mm.c1)
#######################################
01 C1 C2 C3 C4:W
02 C5 C6 C7 C8:BW
03 C5 C7 C1 C1:W
#######################################
TRY AGAIN.
SQL&gt; exec mm.tryThis(mm.c2,mm.c6,mm.c2,mm.c7)
#######################################
01 C1 C2 C3 C4:W
02 C5 C6 C7 C8:BW
03 C5 C7 C1 C1:W
04 C2 C6 C2 C7:WW
#######################################
TRY AGAIN.
SQL&gt; exec mm.tryThis(mm.c3,mm.c3,mm.c7,mm.c6)
#######################################
01 C1 C2 C3 C4:W
02 C5 C6 C7 C8:BW
03 C5 C7 C1 C1:W
04 C2 C6 C2 C7:WW
05 C3 C3 C7 C6:BBWW
#######################################
TRY AGAIN.
SQL&gt; exec mm.tryThis(mm.c6,mm.c3,mm.c7,mm.c3)
#######################################
01 C1 C2 C3 C4:W
02 C5 C6 C7 C8:BW
03 C5 C7 C1 C1:W
04 C2 C6 C2 C7:WW
05 C3 C3 C7 C6:BBWW
06 C6 C3 C7 C3:BBBB
#######################################
YOU GOT IT! WELL DONE!!!
C6 C3 C7 C3
SQL&gt;

</pre></p>
<p>Il codice completo del package può essere scaricato <a href="http://oracleitalia.files.wordpress.com/2011/06/mm.doc">qui</a>, è un file di testo, anche se l&#8217;estensione è .doc.</p>
<p>Il continuo nell&#8217;articolo successivo.</p>
<p>Massimo</p>
<br />Filed under: <a href='http://oracleitalia.wordpress.com/category/giochi/'>Giochi</a>, <a href='http://oracleitalia.wordpress.com/category/plsql/'>PL/SQL</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/oracleitalia.wordpress.com/371/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/oracleitalia.wordpress.com/371/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/oracleitalia.wordpress.com/371/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/oracleitalia.wordpress.com/371/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/oracleitalia.wordpress.com/371/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/oracleitalia.wordpress.com/371/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/oracleitalia.wordpress.com/371/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/oracleitalia.wordpress.com/371/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/oracleitalia.wordpress.com/371/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/oracleitalia.wordpress.com/371/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/oracleitalia.wordpress.com/371/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/oracleitalia.wordpress.com/371/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/oracleitalia.wordpress.com/371/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/oracleitalia.wordpress.com/371/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=371&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://oracleitalia.wordpress.com/2011/06/26/master-mind/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fbabbf605a3a90e522d71f6a0117055f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">massimoruocchio</media:title>
		</media:content>
	</item>
		<item>
		<title>Manuale &#8220;Welcome to Oracle&#8221;</title>
		<link>http://oracleitalia.wordpress.com/2011/06/06/manuale-welcome-to-oracle/</link>
		<comments>http://oracleitalia.wordpress.com/2011/06/06/manuale-welcome-to-oracle/#comments</comments>
		<pubDate>Mon, 06 Jun 2011 08:18:43 +0000</pubDate>
		<dc:creator>massimoruocchio</dc:creator>
				<category><![CDATA[PL/SQL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://oracleitalia.wordpress.com/?p=366</guid>
		<description><![CDATA[Ho pubblicato sul blog il manuale &#8220;Welcome to Oracle&#8221;. Tutti i dettagli alla pagina dedicata Massimo Filed under: PL/SQL, SQL, XML<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=366&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Ho pubblicato sul blog il manuale &#8220;Welcome to Oracle&#8221;. Tutti i dettagli alla <a href="http://oracleitalia.wordpress.com/welcome_to_oracle">pagina dedicata</a></p>
<p>Massimo</p>
<br />Filed under: <a href='http://oracleitalia.wordpress.com/category/plsql/'>PL/SQL</a>, <a href='http://oracleitalia.wordpress.com/category/sql/'>SQL</a>, <a href='http://oracleitalia.wordpress.com/category/xml/'>XML</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/oracleitalia.wordpress.com/366/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/oracleitalia.wordpress.com/366/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/oracleitalia.wordpress.com/366/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/oracleitalia.wordpress.com/366/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/oracleitalia.wordpress.com/366/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/oracleitalia.wordpress.com/366/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/oracleitalia.wordpress.com/366/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/oracleitalia.wordpress.com/366/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/oracleitalia.wordpress.com/366/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/oracleitalia.wordpress.com/366/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/oracleitalia.wordpress.com/366/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/oracleitalia.wordpress.com/366/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/oracleitalia.wordpress.com/366/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/oracleitalia.wordpress.com/366/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=366&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://oracleitalia.wordpress.com/2011/06/06/manuale-welcome-to-oracle/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fbabbf605a3a90e522d71f6a0117055f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">massimoruocchio</media:title>
		</media:content>
	</item>
		<item>
		<title>PL/SQL Profiling con DBMS_PROFILER e DBMS_HPROF</title>
		<link>http://oracleitalia.wordpress.com/2010/04/03/plsql-profiling-con-dbms_profiler-e-dbms_hprof/</link>
		<comments>http://oracleitalia.wordpress.com/2010/04/03/plsql-profiling-con-dbms_profiler-e-dbms_hprof/#comments</comments>
		<pubDate>Fri, 02 Apr 2010 23:58:10 +0000</pubDate>
		<dc:creator>massimoruocchio</dc:creator>
				<category><![CDATA[11g]]></category>
		<category><![CDATA[PL/SQL]]></category>
		<category><![CDATA[DBMS_HPROF]]></category>
		<category><![CDATA[DBMS_PROFILER]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[profiling]]></category>

		<guid isPermaLink="false">http://oracleitalia.wordpress.com/?p=334</guid>
		<description><![CDATA[Mi riallaccio al tema dell&#8217;ultimo articolo, dove ho trattato la tracciatura di programmi pl/sql. Un altro tema fondamentale per il controllo dei programmi a runtime è il profiling, cioè l&#8217;attività di monitoraggio dei tempi d&#8217;esecuzione dei singoli step di programma. Quest&#8217;attività può essere eseguita con l&#8217;ausilio del package DBMS_PROFILER fino a Oracle10g e con il [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=334&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Mi riallaccio al tema dell&#8217;<a href="http://oracleitalia.wordpress.com/2010/03/27/dbms_trace-plsql-sotto-controllo/">ultimo articolo</a>, dove ho trattato la tracciatura di programmi pl/sql.<br />
Un altro tema fondamentale per il controllo dei programmi a runtime è il profiling, cioè l&#8217;attività di monitoraggio dei tempi d&#8217;esecuzione dei singoli step di programma.<br />
Quest&#8217;attività può essere eseguita con l&#8217;ausilio del package DBMS_PROFILER fino a Oracle10g e con il nuovo package DBMS_HPROF in Oracle11g.</p>
<p>DBMS_PROFILER scrive i risultati del profiling in tabelle di database che devono essere create lanciando lo script proftab.sql (si trova nella directory RDBMS/ADMIN sotto la Oracle Home del server).<br />
Una volta lanciato lo script saranno presenti nel DB le seguenti tre tabelle:</p>
<p>Sessioni di profiling attivate:<br />
<pre class="brush: sql; wrap-lines: false;">
SQL&gt; desc plsql_profiler_runs
 Name                    Null?    Type
 ----------------------- -------- ----------------
 RUNID                   NOT NULL NUMBER
 RELATED_RUN                      NUMBER
 RUN_OWNER                        VARCHAR2(32)
 RUN_DATE                         DATE
 RUN_COMMENT                      VARCHAR2(2047)
 RUN_TOTAL_TIME                   NUMBER
 RUN_SYSTEM_INFO                  VARCHAR2(2047)
 RUN_COMMENT1                     VARCHAR2(2047)
 SPARE1                           VARCHAR2(256)

</pre></p>
<p>Unità di codice controllate:<br />
<pre class="brush: sql; wrap-lines: false;">
SQL&gt; desc plsql_profiler_units
 Name                    Null?    Type
 ----------------------- -------- ----------------
 RUNID                   NOT NULL NUMBER
 UNIT_NUMBER             NOT NULL NUMBER
 UNIT_TYPE                        VARCHAR2(32)
 UNIT_OWNER                       VARCHAR2(32)
 UNIT_NAME                        VARCHAR2(32)
 UNIT_TIMESTAMP                   DATE
 TOTAL_TIME              NOT NULL NUMBER
 SPARE1                           NUMBER
 SPARE2                           NUMBER

</pre></p>
<p>Tempi di esecuzione per i singoli elementi delle unità di codice:<br />
<pre class="brush: sql; wrap-lines: false;">
SQL&gt; desc plsql_profiler_data
 Name                    Null?    Type
 ----------------------- -------- ----------------
 RUNID                   NOT NULL NUMBER
 UNIT_NUMBER             NOT NULL NUMBER
 LINE#                   NOT NULL NUMBER
 TOTAL_OCCUR                      NUMBER
 TOTAL_TIME                       NUMBER
 MIN_TIME                         NUMBER
 MAX_TIME                         NUMBER
 SPARE1                           NUMBER
 SPARE2                           NUMBER
 SPARE3                           NUMBER
 SPARE4                           NUMBER

</pre></p>
<p>Si tenga presente che i campi SPARE non sono utilizzati quindi saranno omessi anche nelle query che seguono.</p>
<p>Prima di tutto attiviamo una sessione di profiling. Per fare questo esistono due funzioni e due prcedure del package che rispondono tutte al nome di START_PROFILER.</p>
<p><pre class="brush: sql; wrap-lines: false;">

FUNCTION START_PROFILER RETURNS BINARY_INTEGER
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 RUN_COMMENT                    VARCHAR2                IN     DEFAULT
 RUN_COMMENT1                   VARCHAR2                IN     DEFAULT
 RUN_NUMBER                     BINARY_INTEGER          OUT
PROCEDURE START_PROFILER
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 RUN_COMMENT                    VARCHAR2                IN     DEFAULT
 RUN_COMMENT1                   VARCHAR2                IN     DEFAULT
 RUN_NUMBER                     BINARY_INTEGER          OUT
FUNCTION START_PROFILER RETURNS BINARY_INTEGER
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 RUN_COMMENT                    VARCHAR2                IN     DEFAULT
 RUN_COMMENT1                   VARCHAR2                IN     DEFAULT
PROCEDURE START_PROFILER
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 RUN_COMMENT                    VARCHAR2                IN     DEFAULT
 RUN_COMMENT1                   VARCHAR2                IN     DEFAULT

</pre></p>
<p>Come si vede i vari metodi sono più o meno equivalenti e consentono di passare un paio di commenti in input tornando l&#8217;identificativo della sessione di profiling.<br />
Attiviamo la sessione usando una funzione:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select dbms_profiler.start_profiler from dual;

START_PROFILER
--------------
             0

</pre></p>
<p>E poi lanciamo la procedura P1 definita per l&#8217;articolo precedente:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; exec p1

PL/SQL procedure successfully completed.

</pre></p>
<p>Per arrestare definitivamente la sessione di profiling possiamo usare:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; exec DBMS_PROFILER.STOP_PROFILER

PL/SQL procedure successfully completed.

</pre></p>
<p>Mentre se avessimo voluto interromperla solo momentaneamente avremmo usato:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; exec DBMS_PROFILER.PAUSE_PROFILER

PL/SQL procedure successfully completed.

</pre></p>
<p>Ed in seguito avremmo potuto riprenderla con:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; exec DBMS_PROFILER.RESUME_PROFILER

PL/SQL procedure successfully completed.

</pre></p>
<p>In ogni caso, giunti al termine dell&#8217;attività di raccolta dei dati, per scaricarli in tabella dobbiamo usare:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt;  exec DBMS_PROFILER.FLUSH_DATA

PL/SQL procedure successfully completed.

</pre></p>
<p>Vediamo adesso cosa abbiamo raccolto.<br />
Nella tabella delle sessioni di profiling abbiamo ovviamente un solo record:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; select RUNID, RUN_OWNER, RUN_DATE, RUN_TOTAL_TIME
  2  from plsql_profiler_runs;

     RUNID RUN_OWNER                        RUN_DATE  RUN_TOTAL_TIME
---------- -------------------------------- --------- --------------
         1 MAXR                             03-APR-10     1.4904E+13

</pre></p>
<p>Dove RUN_TOTAL_TIME e tutti i tempi delle tabelle seguenti sono espressi in nanosecondi.</p>
<p>Ci sono anche le unità di codice esaminate:<br />
<pre class="brush: sql; wrap-lines: false;">

SQL&gt; select RUNID, UNIT_NUMBER, UNIT_TYPE, UNIT_OWNER, UNIT_NAME
  2  from plsql_profiler_units;

     RUNID UNIT_NUMBER UNIT_TYPE                        UNIT_OWNER                       UNIT_NAME
---------- ----------- -------------------------------- -------------------------------- ------------------
         1           1 ANONYMOUS BLOCK                  &lt;anonymous&gt;                      &lt;anonymous&gt;
         1           2 ANONYMOUS BLOCK                  &lt;anonymous&gt;                      &lt;anonymous&gt;
         1           3 PROCEDURE                        MAXR                             P1
         1           4 FUNCTION                         MAXR                             F1
         1           5 ANONYMOUS BLOCK                  &lt;anonymous&gt;                      &lt;anonymous&gt;
         1           6 ANONYMOUS BLOCK                  &lt;anonymous&gt;                      &lt;anonymous&gt;
         1           7 ANONYMOUS BLOCK                  &lt;anonymous&gt;                      &lt;anonymous&gt;
         1           8 ANONYMOUS BLOCK                  &lt;anonymous&gt;                      &lt;anonymous&gt;
         1           9 ANONYMOUS BLOCK                  &lt;anonymous&gt;                      &lt;anonymous&gt;

9 rows selected.

</pre></p>
<p>E le singole linee eseguite delle unità di codice suddette:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; select RUNID, UNIT_NUMBER, LINE#, TOTAL_OCCUR, TOTAL_TIME, MIN_TIME, MAX_TIME
  2  from plsql_profiler_data;

     RUNID UNIT_NUMBER      LINE# TOTAL_OCCUR TOTAL_TIME   MIN_TIME   MAX_TIME
---------- ----------- ---------- ----------- ---------- ---------- ----------
         1           1          1           2   50444120    3074971   21215545
         1           2          1           3   14592916     633600   10932115
         1           3          1           0    1302400    1302400    1302400
         1           3          4           1    5114057    5114057    5114057
         1           3          5           1     854857     854857     854857
         1           4          1           4    4837486     155885    1226971
         1           4          4           4 8847138952   41797491 8690106703
         1           4          5           3    3665829     125714    1840457
         1           4          7           1          0          0          0
         1           4          8           1    1272228    1272228    1272228
         1           4          9           4    2740571     251428    1322514
         1           5          1           2   81628810    5199543   37538290
         1           6          1           2   88246411    5576686   38242290
         1           7          1           2   86234982    5456000   37372347
         1           8          1           2   89101268    5637029   38383090
         1           9          1           2   18485030    1015771   15467887

16 rows selected.

</pre></p>
<p>In Oracle11g è stato aggiunto un nuovo package per il profiling, DBMS_HPROF.</p>
<p>Anche questo ha bisogno di sue tabelle che possono essere create lanciando lo script dbmshptab.sql. Eccole:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; desc dbmshp_runs
 Name                    Null?    Type
 ----------------------- -------- ----------------
 RUNID                   NOT NULL NUMBER
 RUN_TIMESTAMP                    TIMESTAMP(6)
 TOTAL_ELAPSED_TIME               NUMBER(38)
 RUN_COMMENT                      VARCHAR2(2047)

SQL&gt; desc dbmshp_function_info
 Name                    Null?    Type
 ----------------------- -------- ----------------
 RUNID                   NOT NULL NUMBER
 SYMBOLID                NOT NULL NUMBER
 OWNER                            VARCHAR2(32)
 MODULE                           VARCHAR2(32)
 TYPE                             VARCHAR2(32)
 FUNCTION                         VARCHAR2(4000)
 LINE#                            NUMBER
 HASH                             RAW(32)
 NAMESPACE                        VARCHAR2(32)
 SUBTREE_ELAPSED_TIME             NUMBER(38)
 FUNCTION_ELAPSED_TIME            NUMBER(38)
 CALLS                            NUMBER(38)

SQL&gt; desc dbmshp_parent_child_info
 Name                    Null?    Type
 ----------------------- -------- ----------------
 RUNID                            NUMBER
 PARENTSYMID                      NUMBER
 CHILDSYMID                       NUMBER
 SUBTREE_ELAPSED_TIME             NUMBER(38)
 FUNCTION_ELAPSED_TIME            NUMBER(38)
 CALLS                            NUMBER(38)

</pre></p>
<p>Ricalcano più o meno quelle viste prima.<br />
La sessione di profiling si avvia così:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; exec DBMS_HPROF.START_PROFILING('TEST_DIR','profile_test.txt')

PL/SQL procedure successfully completed.

</pre></p>
<p>Assegnando una directory ed un file in cui scrivere i dati raccolti.</p>
<p>Poi si esegue il pl/sql che ci interessa:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; exec p1

PL/SQL procedure successfully completed.

</pre></p>
<p>E si chiude la sessione in questo modo:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; exec DBMS_HPROF.STOP_PROFILING

PL/SQL procedure successfully completed.

</pre></p>
<p>Per scrivere i dati in una forma leggibile nelle tabelle di DB, si usa la seguente funzione:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; var id number;
SQL&gt; exec :id := DBMS_HPROF.ANALYZE('TEST_DIR','profile_test.txt');

PL/SQL procedure successfully completed.

</pre></p>
<p>Che ritorna l&#8217;id della sessione di profiling.</p>
<p>Ecco infine i dati raccolti.</p>
<p>Sessioni di profiling:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; select RUNID, RUN_TIMESTAMP, TOTAL_ELAPSED_TIME
  2* from dbmshp_runs

     RUNID RUN_TIMESTAMP             TOTAL_ELAPSED_TIME
---------- ------------------------- ------------------
         1 03-APR-10 01:11:01.296000              18620


</pre></p>
<p>Blocchi di codice analizzati:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; select RUNID, SYMBOLID, OWNER, MODULE, TYPE,
  2  FUNCTION, LINE#, SUBTREE_ELAPSED_TIME, FUNCTION_ELAPSED_TIME, CALLS
  3* from dbmshp_function_info

 RUNID   SYMBOLID OWNER MODULE      TYPE         FUNCTION                 LINE# SUBTREE_ELAPSED_TIME FUNCTION_ELAPSED_TIME CALLS
------ ---------- ----- ----------- ------------ ----------------------- ------ -------------------- --------------------- -----
     1          1                                __anonymous_block            0                18564                   420     4
     1          2                                __plsql_vm                   0                18620                    56     4
     1          3 MAXR  F1          FUNCTION     F1                           1                18020                    84     1
     1          4 MAXR  F1          FUNCTION     F1@1                         1                17702                    24     1
     1          5 MAXR  F1          FUNCTION     F1@2                         1                17590                    22     1
     1          6 MAXR  F1          FUNCTION     F1@3                         1                17484                    77     1
     1          7 MAXR  P1          PROCEDURE    P1                           1                18043                    23     1
     1          8 SYS   DBMS_HPROF  PACKAGE BODY STOP_PROFILING              53                    0                     0     1
     1          9 SYS   DBMS_OUTPUT PACKAGE BODY GET_LINE                   129                   13                    13     2
     1         10 SYS   DBMS_OUTPUT PACKAGE BODY GET_LINES                  180                  101                    88     2
     1         11 MAXR  F1          FUNCTION     __static_sql_exec_line4      4                17813                 17813     4

11 rows selected.

</pre></p>
<p>E singole chiamate (PARENTSYMID è il codice del chiamante, CHILDSYMID è il codice del chiamato). I tempi qui sono tutti in microsecondi.</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; select * from dbmshp_parent_child_info;

 RUNID PARENTSYMID CHILDSYMID SUBTREE_ELAPSED_TIME FUNCTION_ELAPSED_TIME      CALLS
------ ----------- ---------- -------------------- --------------------- ----------
     1           1          7                18043                    23          1
     1           1         10                  101                    88          2
     1           1          8                    0                     0          1
     1           2          1                18564                   420          4
     1           3          4                17702                    24          1
     1           3         11                  234                   234          1
     1           4          5                17590                    22          1
     1           4         11                   88                    88          1
     1           5          6                17484                    77          1
     1           5         11                   84                    84          1
     1           6         11                17407                 17407          1
     1           7          3                18020                    84          1
     1          10          9                   13                    13          2

13 rows selected.
</pre></p>
<p>Abbiamo visto come tenere sotto osservazione i tempi d&#8217;esecuzione dei singoli step all&#8217;interno dei nostri programmi pl/sql.<br />
Queste funzionalità ci consentono di individuare gli eventuali colli di bottiglia da rimuovere.</p>
<p>Alla prossima,<br />
Massimo</p>
<br />Filed under: <a href='http://oracleitalia.wordpress.com/category/11g/'>11g</a>, <a href='http://oracleitalia.wordpress.com/category/plsql/'>PL/SQL</a> Tagged: <a href='http://oracleitalia.wordpress.com/tag/dbms_hprof/'>DBMS_HPROF</a>, <a href='http://oracleitalia.wordpress.com/tag/dbms_profiler/'>DBMS_PROFILER</a>, <a href='http://oracleitalia.wordpress.com/tag/oracle/'>oracle</a>, <a href='http://oracleitalia.wordpress.com/tag/profiling/'>profiling</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/oracleitalia.wordpress.com/334/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/oracleitalia.wordpress.com/334/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/oracleitalia.wordpress.com/334/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/oracleitalia.wordpress.com/334/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/oracleitalia.wordpress.com/334/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/oracleitalia.wordpress.com/334/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/oracleitalia.wordpress.com/334/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/oracleitalia.wordpress.com/334/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/oracleitalia.wordpress.com/334/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/oracleitalia.wordpress.com/334/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/oracleitalia.wordpress.com/334/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/oracleitalia.wordpress.com/334/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/oracleitalia.wordpress.com/334/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/oracleitalia.wordpress.com/334/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=334&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://oracleitalia.wordpress.com/2010/04/03/plsql-profiling-con-dbms_profiler-e-dbms_hprof/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fbabbf605a3a90e522d71f6a0117055f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">massimoruocchio</media:title>
		</media:content>
	</item>
		<item>
		<title>DBMS_TRACE: PL/SQL sotto controllo.</title>
		<link>http://oracleitalia.wordpress.com/2010/03/27/dbms_trace-plsql-sotto-controllo/</link>
		<comments>http://oracleitalia.wordpress.com/2010/03/27/dbms_trace-plsql-sotto-controllo/#comments</comments>
		<pubDate>Sat, 27 Mar 2010 00:47:08 +0000</pubDate>
		<dc:creator>massimoruocchio</dc:creator>
				<category><![CDATA[PL/SQL]]></category>
		<category><![CDATA[DBMS_TRACE]]></category>

		<guid isPermaLink="false">http://oracleitalia.wordpress.com/?p=327</guid>
		<description><![CDATA[Fin da oracle8i è disponibile una preziosa utility che consente di mettere sotto trace una sessione di lavoro PL/SQL: DBMS_TRACE. Il package offre le seguenti funzionalità: CLEAR_PLSQL_TRACE Procedura che consente di stoppare una sessione di tracing GET_PLSQL_TRACE_LEVEL Funzione che restituisce il livello di tracing corrente PLSQL_TRACE_VERSION Procedura che restituisce la versione del package DBMS_TRACE SET_PLSQL_TRACE [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=327&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Fin da oracle8i è disponibile una preziosa utility che consente di mettere sotto trace una sessione di lavoro PL/SQL: DBMS_TRACE.</p>
<p>Il package offre le seguenti funzionalità:</p>
<p><b>CLEAR_PLSQL_TRACE</b><br />
Procedura che consente di stoppare una sessione di tracing</p>
<p><b>GET_PLSQL_TRACE_LEVEL</b><br />
Funzione che restituisce il livello di tracing corrente</p>
<p><b>PLSQL_TRACE_VERSION</b><br />
Procedura che restituisce la versione del package DBMS_TRACE</p>
<p><b>SET_PLSQL_TRACE</b><br />
Procedura che avvia una sessione di tracing impostando il livello di tracing. </p>
<p>Per fare un semplice test creiamo una semplice funzione ricorsiva ed una procedura che la chiama:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; create or replace function f1(p number) return number is
  2  a number;
  3  begin
  4    select 1/p into a from dual;
  5    return f1(p-1);
  6  exception
  7    when others then
  8       return 0;
  9  end;
 10  /

Function created.

SQL&gt; create or replace procedure p1 is
  2  a number;
  3  begin
  4    a := f1(3);
  5  end;
  6  /

Procedure created.

</pre></p>
<p>La funzione richiama se stessa decrementando il parametro in input finché otterrà un errore (divisione per zero) gestito ritornando zero (e dunque fermando il ciclo).<br />
La procedura innesca la funzione passando il valore 3 come parametro.</p>
<p>Innanzi tutto dobbiamo dire che il package DBMS_TRACE scrive in un paio di tabelle del database che devono essere create lanciando (da SYS) lo script tracetab.sql che si trova nella directory RDBMS/ADMIN sotto la Oracle Home del server.</p>
<p>Una volta lanciato lo script e create le tabelle </p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; desc sys.plsql_trace_runs
 Nome                    Nullo?   Tipo
 ----------------------- -------- ----------------
 RUNID                   NOT NULL NUMBER
 RUN_DATE                         DATE
 RUN_OWNER                        VARCHAR2(31)
 RUN_COMMENT                      VARCHAR2(2047)
 RUN_COMMENT1                     VARCHAR2(2047)
 RUN_END                          DATE
 RUN_FLAGS                        VARCHAR2(2047)
 RELATED_RUN                      NUMBER
 RUN_SYSTEM_INFO                  VARCHAR2(2047)
 SPARE1                           VARCHAR2(256)

SQL&gt; desc sys.plsql_trace_events
 Nome                    Nullo?   Tipo
 ----------------------- -------- ----------------
 RUNID                   NOT NULL NUMBER
 EVENT_SEQ               NOT NULL NUMBER
 EVENT_TIME                       DATE
 RELATED_EVENT                    NUMBER
 EVENT_KIND                       NUMBER
 EVENT_UNIT_DBLINK                VARCHAR2(4000)
 EVENT_UNIT_OWNER                 VARCHAR2(31)
 EVENT_UNIT                       VARCHAR2(31)
 EVENT_UNIT_KIND                  VARCHAR2(31)
 EVENT_LINE                       NUMBER
 EVENT_PROC_NAME                  VARCHAR2(31)
 STACK_DEPTH                      NUMBER
 PROC_NAME                        VARCHAR2(31)
 PROC_DBLINK                      VARCHAR2(4000)
 PROC_OWNER                       VARCHAR2(31)
 PROC_UNIT                        VARCHAR2(31)
 PROC_UNIT_KIND                   VARCHAR2(31)
 PROC_LINE                        NUMBER
 PROC_PARAMS                      VARCHAR2(2047)
 ICD_INDEX                        NUMBER
 USER_EXCP                        NUMBER
 EXCP                             NUMBER
 EVENT_COMMENT                    VARCHAR2(2047)
 MODULE                           VARCHAR2(4000)
 ACTION                           VARCHAR2(4000)
 CLIENT_INFO                      VARCHAR2(4000)
 CLIENT_ID                        VARCHAR2(4000)
 ECID_ID                          VARCHAR2(4000)
 ECID_SEQ                         NUMBER
 CALLSTACK                        CLOB
 ERRORSTACK                       CLOB
</pre></p>
<p>Possiamo attivare la trace:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; begin
  2  DBMS_TRACE.SET_PLSQL_TRACE (
  3      DBMS_TRACE.TRACE_ALL_CALLS+
  4      DBMS_TRACE.TRACE_ALL_EXCEPTIONS+
  5      DBMS_TRACE.TRACE_ALL_SQL);
  6  end;
  7  /

PL/SQL procedure successfully completed.
</pre></p>
<p>Nell&#8217;attivazione della trace dobbiamo impostare il livello di tracing, cioè definire quali eventi monitorare.<br />
Ciò si fa sommando le costanti relative agli eventi che interessano, le costanti di maggiore interesse sono:</p>
<p><pre class="brush: sql; wrap-lines: false;">
TRACE_ALL_CALLS             1
TRACE_ENABLED_CALLS         2
TRACE_ALL_EXCEPTIONS        4
TRACE_ENABLED_EXCEPTIONS    8
TRACE_LIMIT                 16
TRACE_ALL_SQL               32
TRACE_ENABLED_SQL           64
TRACE_ALL_LINES             128
TRACE_ENABLED_LINES         256
NO_TRACE_ADMINISTRATIVE     32768
NO_TRACE_HANDLED_EXCEPTIONS 65536
</pre></p>
<p>Ad ogni costante è associato come valore numerico una potenza di due e dunque è possibile scomporre ogni possibile livello di tracing nelle singole costanti che lo compogono.<br />
Per ottenere il livello di tracing si può fare:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select dbms_trace.GET_PLSQL_TRACE_LEVEL from dual;

GET_PLSQL_TRACE_LEVEL
---------------------
                   37
</pre></p>
<p>Per ottenere invece la versione del package:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; var v1 number;
SQL&gt; var v2 number;
SQL&gt; exec dbms_trace.PLSQL_TRACE_VERSION(:v1,:v2)

Procedura PL/SQL completata correttamente.

SQL&gt; print v1

        V1
----------
         1

SQL&gt; print v2

        V2
----------
         0
</pre></p>
<p>Tornando al nostro esempio, siamo pronti per eseguire p1 con la trace attiva:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; exec p1

Procedura PL/SQL completata correttamente.

SQL&gt; select runid, count(0) from sys.plsql_trace_events
  2  group by runid;

     RUNID   COUNT(0)
---------- ----------
         1          7
         2          7
         3         52
         4          7
         5         24
</pre></p>
<p>Come si vede ci sono vari runid in tabella dovuti a diverse prove che ho fatto. L&#8217;ultimo è quello che ci interessa.<br />
La tabella plsql_trace_events contiene molte informazioni utili, per ragioni di formattazione della pagina io sono costretto a mostrarne solo poche:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select EVENT_SEQ, event_kind, event_unit, event_comment
  2  from sys.plsql_trace_events
  3  where runid=5;

 EVENT_SEQ EVENT_KIND EVENT_UNIT
---------- ---------- -------------------------------
EVENT_COMMENT
----------------------------------------------------------------------
         1         38
PL/SQL Trace Tool started

         2         40
Trace flags changed

         3         50 DBMS_TRACE
Return from procedure call

         4         50 DBMS_TRACE
Return from procedure call

         5         50 DBMS_TRACE
Return from procedure call

         6         44
PL/SQL Virtual Machine stopped

         7         43 &lt;anonymous&gt;
PL/SQL Virtual Machine started

         8         45 &lt;anonymous&gt;
Procedure Call

         9         45 P1
Procedure Call

        10         54 F1
SELECT 1/:B1 FROM DUAL

        11         45 F1
Procedure Call

        12         54 F1
SELECT 1/:B1 FROM DUAL

        13         45 F1
Procedure Call

        14         54 F1
SELECT 1/:B1 FROM DUAL

        15         45 F1
Procedure Call

        16         54 F1
SELECT 1/:B1 FROM DUAL

        17         52 F1
Exception raised

        18         53 F1
Exception handled

        19         50 F1
Return from procedure call

        20         50 F1
Return from procedure call

        21         50 F1
Return from procedure call

        22         50 F1
Return from procedure call

        23         50 P1
Return from procedure call

        24         44
PL/SQL Virtual Machine stopped


Selezionate 24 righe.
</pre></p>
<p>Come si vede ci sono alcuni eventi di sistema (la cui tracciatura può essere soppressa utilizzando la costante NO_TRACE_ADMINISTRATIVE) e molti eventi applicativi che avevo richiesto (le query eseguite, le eccezioni sollevate e gestite, le chiamate tra programmi).</p>
<p>Un altro utile campo è il CALLSTACK, valorizzato solo su alcuni eventi particolari:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select event_seq, CALLSTACK
  2  from sys.plsql_trace_events
  3  where runid=5;

 EVENT_SEQ CALLSTACK
---------- ----------------------------------------------------------
         1
         2 ----- PL/SQL Call Stack -----
             object      line  object
             handle    number  name
           1CDCDB88        21  package body SYS.DBMS_TRACE
           1CDCDB88        75  package body SYS.DBMS_TRACE
           1CDCDB88        80  package body SYS.DBMS_TRACE
           1CDCFD7C         2  anonymous block

         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17 ----- PL/SQL Call Stack -----
             object      line  object
             handle    number  name
           1CF6A894         4  function MAXR.F1
           1CF6A894         5  function MAXR.F1
           1CF6A894         5  function MAXR.F1
           1CF6A894         5  function MAXR.F1
           1CDB5A58         4  procedure MAXR.P1
           1CDC5888         1  anonymous block

        18 ----- PL/SQL Call Stack -----
             object      line  object
             handle    number  name
           1CF6A894         8  function MAXR.F1
           1CF6A894         5  function MAXR.F1
           1CF6A894         5  function MAXR.F1
           1CF6A894         5  function MAXR.F1
           1CDB5A58         4  procedure MAXR.P1
           1CDC5888         1  anonymous block

        19
        20
        21
        22
        23
        24

Selezionate 24 righe.
</pre></p>
<p>Che visualizza proprio la pila di chiamate tra programmi.</p>
<p>Una volta raccolte le informazioni necessarie ricordiamoci di disattivare la trace usando:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; exec DBMS_TRACE.CLEAR_PLSQL_TRACE

Procedura PL/SQL completata correttamente.
</pre></p>
<p>Altrimenti la tabella degli eventi si riempe molto velocemente&#8230;</p>
<p>Alla prossima,<br />
Massimo</p>
<br />Filed under: <a href='http://oracleitalia.wordpress.com/category/plsql/'>PL/SQL</a> Tagged: <a href='http://oracleitalia.wordpress.com/tag/dbms_trace/'>DBMS_TRACE</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/oracleitalia.wordpress.com/327/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/oracleitalia.wordpress.com/327/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/oracleitalia.wordpress.com/327/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/oracleitalia.wordpress.com/327/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/oracleitalia.wordpress.com/327/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/oracleitalia.wordpress.com/327/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/oracleitalia.wordpress.com/327/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/oracleitalia.wordpress.com/327/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/oracleitalia.wordpress.com/327/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/oracleitalia.wordpress.com/327/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/oracleitalia.wordpress.com/327/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/oracleitalia.wordpress.com/327/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/oracleitalia.wordpress.com/327/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/oracleitalia.wordpress.com/327/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=327&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://oracleitalia.wordpress.com/2010/03/27/dbms_trace-plsql-sotto-controllo/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fbabbf605a3a90e522d71f6a0117055f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">massimoruocchio</media:title>
		</media:content>
	</item>
		<item>
		<title>Altre nuove funzioni in XMLDB</title>
		<link>http://oracleitalia.wordpress.com/2010/03/21/altre-nuove-funzioni-in-xmldb/</link>
		<comments>http://oracleitalia.wordpress.com/2010/03/21/altre-nuove-funzioni-in-xmldb/#comments</comments>
		<pubDate>Sat, 20 Mar 2010 23:19:45 +0000</pubDate>
		<dc:creator>massimoruocchio</dc:creator>
				<category><![CDATA[11g]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[XML]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[xmlcast]]></category>
		<category><![CDATA[xmldiff]]></category>
		<category><![CDATA[xmlexists]]></category>
		<category><![CDATA[xmlpatch]]></category>

		<guid isPermaLink="false">http://oracleitalia.wordpress.com/?p=323</guid>
		<description><![CDATA[In quest&#8217;articolo presento velocemente quattro nuove funzioni introdotte in XMLDB a partire dalla versione 11g di oracle. Cominciamo dalla funzione XMLCAST, creata al fine di consentire il cast esplicito del contenuto di un nodo XML ad un tipo di dato scalare SQL. Ipotizziamo ad esempio di avere il semplicissimo documento Contenente soltanto un elemento valorizzato [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=323&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>In quest&#8217;articolo presento velocemente quattro nuove funzioni introdotte in XMLDB a partire dalla versione 11g di oracle.</p>
<p>Cominciamo dalla funzione <b>XMLCAST</b>, creata al fine di consentire il cast esplicito del contenuto di un nodo XML ad un tipo di dato scalare SQL.</p>
<p>Ipotizziamo ad esempio di avere il semplicissimo documento</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select extract(xmltype('&lt;a&gt;2010-03-20&lt;/a&gt;'),'//a') from dual;

EXTRACT(XMLTYPE('&lt;A&gt;2010-03-20&lt;/A&gt;'),'//A')
--------------------------------------------------------------------------------------------------
&lt;a&gt;2010-03-20&lt;/a&gt;
</pre></p>
<p>Contenente soltanto un elemento valorizzato con una data.</p>
<p>Per estrarre il contenuto dell&#8217;elemento a in forma di data facciamo:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select xmlcast(extract(xmltype('&lt;a&gt;2010-03-20&lt;/a&gt;'),'//a') as date) from dual;

XMLCAST(E
---------
20-MAR-10
</pre></p>
<p>Dove il formato ottenuto è il default corrente (il valore del parametro NLS_DATE_FORMAT). Proviamo a cambiarlo per ottenere un formato diverso:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; alter session set nls_date_format='dd/mm/yyyy hh24:mi:ss';

Session altered.

SQL&gt; select xmlcast(extract(xmltype('&lt;a&gt;2010-03-20&lt;/a&gt;'),'//a') as date) from dual;

XMLCAST(EXTRACT(XML
-------------------
20/03/2010 00:00:00
</pre></p>
<p>Ovviamente se avessimo effettuato il cast a VARCHAR2 anziché a data avremmo ottenuto:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select xmlcast(extract(xmltype('&lt;a&gt;2010-03-20&lt;/a&gt;'),'//a') as varchar2(20)) from dual;

XMLCAST(EXTRACT(XMLT
--------------------
2010-03-20
</pre></p>
<p>Si noti che la funzione XMLCAST vuole in input un elemento XML, se forniamo invece un dato scalare otteniamo un errore:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select xmlcast('2010-03-20' as date) from dual;
select xmlcast('2010-03-20' as date) from dual
*
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected - got -
</pre></p>
<p>La funzione <b>XMLEXISTS</b> è una funzione booleana che indica se una query XQuery ritorna un risultato non nullo.<br />
La possiamo considerare un&#8217;estensione di EXISTSNODE visto che quest&#8217;ultima prende in input una query XPath che è un sottoinsieme di XQuery.</p>
<p>Per fare un esempio utilizziamo il documento seguente:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select xmlserialize(document x) from myxml;

XMLSERIALIZE(DOCUMENTX)
----------------------------------------------------------
&lt;emps&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7369&lt;/EMPNO&gt;
    &lt;ENAME&gt;SMITH&lt;/ENAME&gt;
    &lt;JOB&gt;CLERK&lt;/JOB&gt;
    &lt;SAL&gt;800&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7499&lt;/EMPNO&gt;
    &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1600&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7521&lt;/EMPNO&gt;
    &lt;ENAME&gt;WARD&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1250&lt;/SAL&gt;
  &lt;/employee&gt;
&lt;/emps&gt;
</pre></p>
<p>Presente nella tabella MYXML.<br />
Una semplice query potrebbe essere:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select XMLQuery(
  2          'for $i in //employee
  3           where $i/SAL &gt; 1000
  4           return $i/ENAME' passing x returning content) NAMES
  5    FROM myxml;

NAMES
--------------------------------------------------------------------
&lt;ENAME&gt;ALLEN&lt;/ENAME&gt;&lt;ENAME&gt;WARD&lt;/ENAME&gt;
</pre></p>
<p>Che ritorna gli ENAME dei dipendenti che guadagnano più di 1000. Applicando XMLEXISTS:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select 1 from myxml
  2  where xmlexists(
  3   'for $i in //employee
  4    where $i/SAL &gt; 1000
  5    return $i/ENAME' passing x);

         1
----------
         1

SQL&gt; select 1 from myxml
  2  where xmlexists(
  3   'for $i in //employee
  4    where $i/SAL &gt; 10000
  5    return $i/ENAME' passing x);

no rows selected
</pre></p>
<p>Se utilizziamo XMLEXISTS nella condizione di WHERE, oppure</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select case when xmlexists(
  2   'for $i in //employee
  3    where $i/SAL &gt; 1000
  4    return $i/ENAME' passing x) then 'TRUE' else 'FALSE' end &quot;Exists?&quot;
  5* from myxml

Exists?
--------------------
TRUE

SQL&gt; select case when xmlexists(
  2   'for $i in //employee
  3    where $i/SAL &gt; 10000
  4    return $i/ENAME' passing x) then 'TRUE' else 'FALSE' end &quot;Exists?&quot;
  5  from myxml;

Exists?
--------------------
FALSE
</pre></p>
<p>Se invece la mettiamo in una CASE. </p>
<p>Come detto XPath è un sottoinsieme di XQuery, quindi XMLEXISTS avrebbe accettato anche le seguenti:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; select case when xmlexists('//ENAME[../SAL&gt;1000]' passing x)
  2         then 'TRUE' else 'FALSE' end &quot;Exists?&quot;
  3  from myxml;

Exists?
--------------------
TRUE

SQL&gt; select case when xmlexists('//ENAME[../SAL&gt;10000]' passing x)
  2         then 'TRUE' else 'FALSE' end &quot;Exists?&quot;
  3  from myxml;

Exists?
--------------------
FALSE
</pre></p>
<p>La funzione <b>XMLDIFF</b> fornisce la differenza tra due documenti XML.<br />
Per provarla partiamo da una versione leggermente modificata della tabella MYXML:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select xmlserialize(document x) x,
  2         xmlserialize(document y) y
  3  from myxml;

X                              Y
------------------------------ ------------------------------
&lt;emps&gt;                         &lt;emps&gt;
  &lt;employee&gt;                     &lt;employee&gt;
    &lt;EMPNO&gt;7369&lt;/EMPNO&gt;            &lt;EMPNO&gt;7369&lt;/EMPNO&gt;
    &lt;ENAME&gt;SMITH&lt;/ENAME&gt;           &lt;ENAME&gt;TOM&lt;/ENAME&gt;
    &lt;JOB&gt;CLERK&lt;/JOB&gt;               &lt;JOB&gt;CLERK&lt;/JOB&gt;
    &lt;SAL&gt;800&lt;/SAL&gt;                 &lt;SAL&gt;800&lt;/SAL&gt;
  &lt;/employee&gt;                    &lt;/employee&gt;
  &lt;employee&gt;                     &lt;employee&gt;
    &lt;EMPNO&gt;7499&lt;/EMPNO&gt;            &lt;EMPNO&gt;7499&lt;/EMPNO&gt;
    &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;           &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;            &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1600&lt;/SAL&gt;                &lt;SAL&gt;3000&lt;/SAL&gt;
  &lt;/employee&gt;                    &lt;/employee&gt;
  &lt;employee&gt;                     &lt;employee&gt;
    &lt;EMPNO&gt;7521&lt;/EMPNO&gt;            &lt;EMPNO&gt;7521&lt;/EMPNO&gt;
    &lt;ENAME&gt;WARD&lt;/ENAME&gt;            &lt;ENAME&gt;WARD&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;            &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1250&lt;/SAL&gt;                &lt;SAL&gt;1250&lt;/SAL&gt;
  &lt;/employee&gt;                    &lt;/employee&gt;
&lt;/emps&gt;                        &lt;/emps&gt;
</pre></p>
<p>Come si vede i due documenti differiscono per due valori, un ENAME ed un SAL.</p>
<p>Eseguendo la XMLDIFF:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select xmldiff(x,y) differenze
  2  from myxml;

DIFFERENZE
---------------------------------------------------------
&lt;xd:xdiff xsi:schemaLocation=
          &quot;http://xmlns.oracle.com/xdb/xdiff.xsd 
           http://xmlns.oracle.com/xdb/xdiff.xsd&quot; 
          xmlns:xd=&quot;http://xmlns.oracle.com/xdb/xdiff.xsd&quot; 
          xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&gt;
  &lt;?oracle-xmldiff operations-in-docorder=&quot;true&quot; 
                   output-model=&quot;snapshot&quot; 
                   diff-algorithm=&quot;global&quot;?&gt;
  &lt;xd:update-node xd:node-type=&quot;text&quot; 
                  xd:xpath=&quot;/emps[1]/employee[1]/ENAME[1]/text()[1]&quot;&gt;
    &lt;xd:content&gt;TOM&lt;/xd:content&gt;
  &lt;/xd:update-node&gt;
  &lt;xd:update-node xd:node-type=&quot;text&quot; 
                  xd:xpath=&quot;/emps[1]/employee[2]/SAL[1]/text()[1]&quot;&gt;
    &lt;xd:content&gt;3000&lt;/xd:content&gt;
  &lt;/xd:update-node&gt;
&lt;/xd:xdiff&gt;

</pre></p>
<p>(Il risultato è stato formattato manulamente per una migliore visualizzazione nel blog&#8230;) </p>
<p>Si ottengono le differnze cercate. La funzione XMLDIFF è un&#8217;iterfaccia SQL verso l&#8217;<a href="http://download.oracle.com/docs/cd/E11882_01/appdev.112/e10770/xmldiff.htm">API C XmlDiff</a></p>
<p>Esattamente allo stesso modo la funzione <b>XMLPATCH</b> è un&#8217;interfaccia SQL per l&#8217;<a href="http://download.oracle.com/docs/cd/E15523_01/appdev.1111/b28394/adx_c_diff.htm#BABEGJJD">API C XmlPatch</a>.</p>
<p>Questa consente di modificare un documento XML applicando una &#8220;patch&#8221; espressa esattamente nel formato XDIFF che abbiamo visto come risultato della XMLDIFF.<br />
Praticamente se applichiamo alla colonna X il risultato della XMLDIFF precedente mediante XMLPATCH otteniamo la colonna Y:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select xmlserialize(document XMLPATCH(X,XMLDIFF(X,Y)))
  2  from myxml;

XMLSERIALIZE(DOCUMENTXMLPATCH(X,XMLDIFF(X,Y)))
-------------------------------------------------------------
&lt;emps&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7369&lt;/EMPNO&gt;
    &lt;ENAME&gt;TOM&lt;/ENAME&gt;
    &lt;JOB&gt;CLERK&lt;/JOB&gt;
    &lt;SAL&gt;800&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7499&lt;/EMPNO&gt;
    &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;3000&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7521&lt;/EMPNO&gt;
    &lt;ENAME&gt;WARD&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1250&lt;/SAL&gt;
  &lt;/employee&gt;
&lt;/emps&gt;
</pre></p>
<p>Ricordate sempre che XMLSERIALIZE è lì solo per ottenere una visualizzazione più leggibile&#8230;</p>
<p>Questo è tutto per stasera.</p>
<p>Alla prossima,<br />
Massimo</p>
<br />Filed under: <a href='http://oracleitalia.wordpress.com/category/11g/'>11g</a>, <a href='http://oracleitalia.wordpress.com/category/sql/'>SQL</a>, <a href='http://oracleitalia.wordpress.com/category/xml/'>XML</a> Tagged: <a href='http://oracleitalia.wordpress.com/tag/oracle/'>oracle</a>, <a href='http://oracleitalia.wordpress.com/tag/xmlcast/'>xmlcast</a>, <a href='http://oracleitalia.wordpress.com/tag/xmldiff/'>xmldiff</a>, <a href='http://oracleitalia.wordpress.com/tag/xmlexists/'>xmlexists</a>, <a href='http://oracleitalia.wordpress.com/tag/xmlpatch/'>xmlpatch</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/oracleitalia.wordpress.com/323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/oracleitalia.wordpress.com/323/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/oracleitalia.wordpress.com/323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/oracleitalia.wordpress.com/323/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/oracleitalia.wordpress.com/323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/oracleitalia.wordpress.com/323/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/oracleitalia.wordpress.com/323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/oracleitalia.wordpress.com/323/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/oracleitalia.wordpress.com/323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/oracleitalia.wordpress.com/323/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/oracleitalia.wordpress.com/323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/oracleitalia.wordpress.com/323/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/oracleitalia.wordpress.com/323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/oracleitalia.wordpress.com/323/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=323&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://oracleitalia.wordpress.com/2010/03/21/altre-nuove-funzioni-in-xmldb/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fbabbf605a3a90e522d71f6a0117055f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">massimoruocchio</media:title>
		</media:content>
	</item>
		<item>
		<title>Le funzioni per l&#8217;aggiornamento dei docmenti XML</title>
		<link>http://oracleitalia.wordpress.com/2010/03/14/le-funzioni-per-laggiornamento-dei-docmenti-xml/</link>
		<comments>http://oracleitalia.wordpress.com/2010/03/14/le-funzioni-per-laggiornamento-dei-docmenti-xml/#comments</comments>
		<pubDate>Sun, 14 Mar 2010 00:41:23 +0000</pubDate>
		<dc:creator>massimoruocchio</dc:creator>
				<category><![CDATA[SQL]]></category>
		<category><![CDATA[XML]]></category>
		<category><![CDATA[appendchildxml]]></category>
		<category><![CDATA[deletexml]]></category>
		<category><![CDATA[insertxml]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[updatexml]]></category>

		<guid isPermaLink="false">http://oracleitalia.wordpress.com/?p=320</guid>
		<description><![CDATA[Abbiamo già visto in precedenti articoli varie funzionalità di XMLDB, ad esempio il supporto di XML Schema e le query xml. In quest&#8217;articolo voglio invece soffermarmi sull&#8217;aggiornamento di documenti xml già presenti nel DB all&#8217;interno di colonne XMLType. Come documento di partenza utilizzeremo il seguente xml presente nella tabella myxml: Cominciamo dalla funzione ultima arrivata, [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=320&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Abbiamo già visto in precedenti articoli varie funzionalità di XMLDB, ad esempio il <a href="http://oracleitalia.wordpress.com/2010/01/17/supporto-di-xml-schema-in-oracle-xmldb/">supporto di XML Schema</a> e le <a href="http://oracleitalia.wordpress.com/tag/xml-query/">query xml</a>.<br />
In quest&#8217;articolo voglio invece soffermarmi sull&#8217;aggiornamento di documenti xml già presenti nel DB all&#8217;interno di colonne XMLType.</p>
<p>Come documento di partenza utilizzeremo il seguente xml presente nella tabella myxml:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select xmlserialize(document x) from myxml;

XMLSERIALIZE(DOCUMENTX)
-------------------------------------------------
&lt;emps&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7369&lt;/EMPNO&gt;
    &lt;ENAME&gt;SMITH&lt;/ENAME&gt;
    &lt;JOB&gt;CLERK&lt;/JOB&gt;
    &lt;SAL&gt;800&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7499&lt;/EMPNO&gt;
    &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1600&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7521&lt;/EMPNO&gt;
    &lt;ENAME&gt;WARD&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1250&lt;/SAL&gt;
  &lt;/employee&gt;
&lt;/emps&gt;
</pre></p>
<p>Cominciamo dalla funzione ultima arrivata, INSERTXMLAFTER.<br />
Questa funzione, novità di Oracle 11gR1, serve ad aggiungere un frammento XML subito dopo un elemento del documento individuato mediante un&#8217;espressione XPATH.<br />
Vediamo un esempio:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; update myxml set
  2  x=INSERTXMLAFTER(x,
  3                    '//EMPNO[text()=7521]',
  4                    xmlelement(&quot;newel&quot;,'newval')
  5                   );

1 row updated.

SQL&gt; select xmlserialize(document x) from myxml;

XMLSERIALIZE(DOCUMENTX)
--------------------------------------------------------
&lt;emps&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7369&lt;/EMPNO&gt;
    &lt;ENAME&gt;SMITH&lt;/ENAME&gt;
    &lt;JOB&gt;CLERK&lt;/JOB&gt;
    &lt;SAL&gt;800&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7499&lt;/EMPNO&gt;
    &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1600&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7521&lt;/EMPNO&gt;
    &lt;newel&gt;newval&lt;/newel&gt;
    &lt;ENAME&gt;WARD&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1250&lt;/SAL&gt;
  &lt;/employee&gt;
&lt;/emps&gt;

</pre></p>
<p>INSERTXMLAFTER riceve in input il doc xml di partenza, l&#8217;espressione xpath che indica l&#8217;elemento dopo il quale bisogna inserire il nuovo elemento ed appunto il nuovo elemento.<br />
In questo caso abbiamo deciso di inserire l&#8217;elemento &lt;newel&gt;newval&lt;/newel&gt; subito dopo l&#8217;elemento EMPNO avente valore 7521.<br />
Utilizzando la funzione INSERTXMLBEFORE avremmo potuto invece inserire il nuovo elemento subito prima del riferimento:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; update myxml set
  2  x=INSERTXMLBEFORE(x,
  3                    '//EMPNO[text()=7521]',
  4                    xmlelement(&quot;newel&quot;,'newval')
  5                   );

1 row updated.

SQL&gt; select xmlserialize(document x) from myxml;

XMLSERIALIZE(DOCUMENTX)
--------------------------------------------------------
&lt;emps&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7369&lt;/EMPNO&gt;
    &lt;ENAME&gt;SMITH&lt;/ENAME&gt;
    &lt;JOB&gt;CLERK&lt;/JOB&gt;
    &lt;SAL&gt;800&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7499&lt;/EMPNO&gt;
    &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1600&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;newel&gt;newval&lt;/newel&gt;
    &lt;EMPNO&gt;7521&lt;/EMPNO&gt;
    &lt;ENAME&gt;WARD&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1250&lt;/SAL&gt;
  &lt;/employee&gt;
&lt;/emps&gt;

</pre></p>
<p>Attenzione! sebbene INSERTXMLAFTER sia disponibile in teoria in tutte le versioni della 11gR1, in realtà a causa di un bug funziona solo a partire dalla versione 11.1.0.7.0</p>
<p>Simile, ma leggermente differente, è la funzione APPENDCHILDXML. Questa, infatti, inserisce il nuovo elemento come figlio (e dunque ad un livello inferiore nell&#8217;albero gerarchico) dell&#8217;elemento a cui punta l&#8217;espressione XPATH:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; update myxml set
  2  x=appendchildxml(x,
  3                    '//EMPNO[text()=7521]',
  4                    xmlelement(&quot;newel&quot;,'newval')
  5                   );

1 row updated.

SQL&gt; select xmlserialize(document x) from myxml;

XMLSERIALIZE(DOCUMENTX)
-----------------------------------------------------------
&lt;emps&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7369&lt;/EMPNO&gt;
    &lt;ENAME&gt;SMITH&lt;/ENAME&gt;
    &lt;JOB&gt;CLERK&lt;/JOB&gt;
    &lt;SAL&gt;800&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7499&lt;/EMPNO&gt;
    &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1600&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7521&lt;newel&gt;newval&lt;/newel&gt;
    &lt;/EMPNO&gt;
    &lt;ENAME&gt;WARD&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1250&lt;/SAL&gt;
  &lt;/employee&gt;
&lt;/emps&gt;
</pre></p>
<p>Come si vede &#8220;newel&#8221; è finito dentro EMPNO, a pari livello con il contenuto testuale di quest&#8217;ultimo.</p>
<p>Si ottiene lo stesso risultato anche utilizzando INSERTCHILDXML, questa funzione, però, non garantisce la posizione dell&#8217;elemento inserito, ma solo che questo sarà figlio del riferimento dato.<br />
APPENDCHILDXML, invece, garantisce che il nuovo elemento inserito sia proprio l&#8217;ultimo figlio del riferimento.</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; update myxml set
  2  x=insertchildxml(x,
  3                    '//EMPNO[text()=7521]',
  4                    'newel',
  5                    xmlelement(&quot;newel&quot;,'newval')
  6                   );

1 row updated.

SQL&gt; select xmlserialize(document x) from myxml;

XMLSERIALIZE(DOCUMENTX)
---------------------------------------------------------
&lt;emps&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7369&lt;/EMPNO&gt;
    &lt;ENAME&gt;SMITH&lt;/ENAME&gt;
    &lt;JOB&gt;CLERK&lt;/JOB&gt;
    &lt;SAL&gt;800&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7499&lt;/EMPNO&gt;
    &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1600&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7521&lt;newel&gt;newval&lt;/newel&gt;
    &lt;/EMPNO&gt;
    &lt;ENAME&gt;WARD&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;1250&lt;/SAL&gt;
  &lt;/employee&gt;
&lt;/emps&gt;

</pre></p>
<p>Viste le funzioni per aggiungere frammenti XML passiamo a quelle che consentono di modificare ed eliminare gli elementi esistenti.</p>
<p>UPDATEXML riceve in input il documento di partenza ed un insieme di coppie (espressione XPATH, valore) che indicano le modifiche da apportare.<br />
Se ad esempio vogliamo portare a 2000 lo stipendio di ALLEN e contestualmente cambiare il mestiere di WARD facciamo:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; update myxml set
  2  x=updatexml(x,
  3              '//employee[EMPNO=7499]/SAL/text()',
  4              2000,
  5              '//employee[EMPNO=7521]/JOB/text()',
  6              'disoccupato'
  7             );

1 row updated.

SQL&gt; select xmlserialize(document x) from myxml;

XMLSERIALIZE(DOCUMENTX)
-----------------------------------------------------------------
&lt;emps&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7369&lt;/EMPNO&gt;
    &lt;ENAME&gt;SMITH&lt;/ENAME&gt;
    &lt;JOB&gt;CLERK&lt;/JOB&gt;
    &lt;SAL&gt;800&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7499&lt;/EMPNO&gt;
    &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;2000&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7521&lt;/EMPNO&gt;
    &lt;ENAME&gt;WARD&lt;/ENAME&gt;
    &lt;JOB&gt;disoccupato&lt;/JOB&gt;
    &lt;SAL&gt;1250&lt;/SAL&gt;
  &lt;/employee&gt;
&lt;/emps&gt;
</pre></p>
<p>Per finire la funzione che consente di eliminare frammenti xml da un documento. Una volta che abbiamo dichiarato che WARD è disoccupato tanto vale eliminarlo del tutto dal documento:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; update myxml set
  2  x=deletexml(x,
  3              '//employee[EMPNO=7521]'
  4             );

1 row updated.

SQL&gt; select xmlserialize(document x) from myxml;

XMLSERIALIZE(DOCUMENTX)
--------------------------------------------------------
&lt;emps&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7369&lt;/EMPNO&gt;
    &lt;ENAME&gt;SMITH&lt;/ENAME&gt;
    &lt;JOB&gt;CLERK&lt;/JOB&gt;
    &lt;SAL&gt;800&lt;/SAL&gt;
  &lt;/employee&gt;
  &lt;employee&gt;
    &lt;EMPNO&gt;7499&lt;/EMPNO&gt;
    &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;
    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;
    &lt;SAL&gt;2000&lt;/SAL&gt;
  &lt;/employee&gt;
&lt;/emps&gt;

</pre></p>
<p>La funzione riceve in input il documento di partenza ed un riferimento XPATH, eliminando l&#8217;elemento (o gli elementi) che si trova al riferimento.</p>
<p>Questo è tutto per gli aggiornamenti dei documenti XML, alla prossima<br />
Massimo.</p>
<br />Filed under: <a href='http://oracleitalia.wordpress.com/category/sql/'>SQL</a>, <a href='http://oracleitalia.wordpress.com/category/xml/'>XML</a> Tagged: <a href='http://oracleitalia.wordpress.com/tag/appendchildxml/'>appendchildxml</a>, <a href='http://oracleitalia.wordpress.com/tag/deletexml/'>deletexml</a>, <a href='http://oracleitalia.wordpress.com/tag/insertxml/'>insertxml</a>, <a href='http://oracleitalia.wordpress.com/tag/oracle/'>oracle</a>, <a href='http://oracleitalia.wordpress.com/tag/updatexml/'>updatexml</a>, <a href='http://oracleitalia.wordpress.com/tag/xml/'>XML</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/oracleitalia.wordpress.com/320/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/oracleitalia.wordpress.com/320/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/oracleitalia.wordpress.com/320/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/oracleitalia.wordpress.com/320/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/oracleitalia.wordpress.com/320/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/oracleitalia.wordpress.com/320/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/oracleitalia.wordpress.com/320/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/oracleitalia.wordpress.com/320/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/oracleitalia.wordpress.com/320/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/oracleitalia.wordpress.com/320/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/oracleitalia.wordpress.com/320/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/oracleitalia.wordpress.com/320/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/oracleitalia.wordpress.com/320/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/oracleitalia.wordpress.com/320/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=320&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://oracleitalia.wordpress.com/2010/03/14/le-funzioni-per-laggiornamento-dei-docmenti-xml/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fbabbf605a3a90e522d71f6a0117055f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">massimoruocchio</media:title>
		</media:content>
	</item>
		<item>
		<title>Tributo ad E. F. Codd</title>
		<link>http://oracleitalia.wordpress.com/2010/03/07/tributo-ad-e-f-codd/</link>
		<comments>http://oracleitalia.wordpress.com/2010/03/07/tributo-ad-e-f-codd/#comments</comments>
		<pubDate>Sun, 07 Mar 2010 22:25:20 +0000</pubDate>
		<dc:creator>massimoruocchio</dc:creator>
				<category><![CDATA[Generici]]></category>
		<category><![CDATA[Codd]]></category>
		<category><![CDATA[relazionale]]></category>

		<guid isPermaLink="false">http://oracleitalia.wordpress.com/?p=318</guid>
		<description><![CDATA[Nel giugno del 1970 è stato pubblicato l&#8217;articolo di Codd &#8220;A Relational Model of Data for Large Shared Data Banks&#8221; che costituisce una vera e propria Bibbia dei database relazionali. A quarant&#8217;anni dalla pubblicazione ho ripreso l&#8217;articolo, ancora attualissimo, e l&#8217;ho tradotto poiché ritengo giusto diffondere la conoscenza sulle origini di questa importante ed oggi [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=318&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Nel giugno del 1970 è stato pubblicato l&#8217;articolo di Codd &#8220;A Relational Model of Data for Large Shared Data Banks&#8221; che costituisce una vera e propria Bibbia dei database relazionali. A quarant&#8217;anni dalla pubblicazione ho ripreso l&#8217;articolo, ancora attualissimo, e l&#8217;ho tradotto poiché ritengo giusto diffondere la conoscenza sulle origini di questa importante ed oggi utilizzatissima tecnologia.</p>
<p>L&#8217;articolo sarà sempre disponibile in questo blog alla pagina <a href='http://oracleitalia.wordpress.com/codd/'>Codd</a></p>
<p>Massimo</p>
<br />Filed under: <a href='http://oracleitalia.wordpress.com/category/generici/'>Generici</a> Tagged: <a href='http://oracleitalia.wordpress.com/tag/codd/'>Codd</a>, <a href='http://oracleitalia.wordpress.com/tag/relazionale/'>relazionale</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/oracleitalia.wordpress.com/318/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/oracleitalia.wordpress.com/318/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/oracleitalia.wordpress.com/318/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/oracleitalia.wordpress.com/318/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/oracleitalia.wordpress.com/318/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/oracleitalia.wordpress.com/318/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/oracleitalia.wordpress.com/318/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/oracleitalia.wordpress.com/318/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/oracleitalia.wordpress.com/318/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/oracleitalia.wordpress.com/318/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/oracleitalia.wordpress.com/318/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/oracleitalia.wordpress.com/318/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/oracleitalia.wordpress.com/318/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/oracleitalia.wordpress.com/318/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=318&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://oracleitalia.wordpress.com/2010/03/07/tributo-ad-e-f-codd/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fbabbf605a3a90e522d71f6a0117055f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">massimoruocchio</media:title>
		</media:content>
	</item>
		<item>
		<title>La clausola UNPIVOT</title>
		<link>http://oracleitalia.wordpress.com/2010/03/05/la-clausola-unpivot/</link>
		<comments>http://oracleitalia.wordpress.com/2010/03/05/la-clausola-unpivot/#comments</comments>
		<pubDate>Fri, 05 Mar 2010 18:34:07 +0000</pubDate>
		<dc:creator>massimoruocchio</dc:creator>
				<category><![CDATA[11g]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[UNPIVOT]]></category>

		<guid isPermaLink="false">http://oracleitalia.wordpress.com/?p=311</guid>
		<description><![CDATA[Ho già parlato in precedenti articoli di due nuove funzionalità presenti in SQL nella versione 11g di Oracle. Si tratta della clausola PIVOT e della funzione REGEXP_COUNT. In questo articolo mi propongo di mostrare un&#8217;altra novità, la clausola UNPIVOT. Della clausola PIVOT abbiamo detto che serve a trasformare record in colonne. Ad esempio la somma [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=311&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Ho già parlato in precedenti articoli di due nuove funzionalità presenti in SQL nella versione 11g di Oracle.<br />
Si tratta della <a href="http://oracleitalia.wordpress.com/2009/12/01/trasformare-righe-in-colonne-la-clausola-pivot/">clausola PIVOT</a> e della funzione <a href="http://oracleitalia.wordpress.com/2009/12/04/espressioni-regolari-in-oracle-sql-e-plsql/">REGEXP_COUNT</a>.</p>
<p>In questo articolo mi propongo di mostrare un&#8217;altra novità, la clausola UNPIVOT.</p>
<p>Della clausola PIVOT abbiamo detto che serve a trasformare record in colonne. </p>
<p>Ad esempio la somma degli stipendi per dipartimento (sulle colonne) e per impiego (sulle righe):</p>
<p><pre class="brush: sql; wrap-lines: false;">
select job, sum(d10),sum(d20),sum(d30) from emp
pivot (sum(sal) for deptno 
         in (10 as D10, 20 as d20, 30 as d30))
group by job;


JOB         SUM(D10)   SUM(D20)   SUM(D30)
--------- ---------- ---------- ----------
SALESMAN                              5600
CLERK           1300       1900        950
PRESIDENT       5000
MANAGER         2450       2975       2850
ANALYST                    6000

</pre></p>
<p>La clausola UNPIVOT serve a fare esattamente il contrario, cioè a leggere in diversi record i dati che in partenza abbiamo su diverse colonne.</p>
<p>Ipotizziamo ad esempio di avere la tabella VENDITE:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select * from vendite;

    DEPTNO  ANNO_2008  ANNO_2009  ANNO_2010
---------- ---------- ---------- ----------
        10     350000     400000     500000
        20     185000     220000     330000
        30     400000     500000     600000
</pre></p>
<p>e di voler ottenere nove record, uno per ogni dipartimento e per ogni anno.</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; select *
  2  from vendite
  3  UNPIVOT
  4  (importo for anno in (
  5           anno_2008 as '2008',
  6           anno_2009 as '2009',
  7           anno_2010 as '2010')
  8  );

    DEPTNO ANNO    IMPORTO
---------- ---- ----------
        10 2008     350000
        10 2009     400000
        10 2010     500000
        20 2008     185000
        20 2009     220000
        20 2010     330000
        30 2008     400000
        30 2009     500000
        30 2010     600000

9 rows selected.

</pre></p>
<p>Guardiamo la clausola UNPIVOT. Ho chiesto un campo importo per anno indicando nella clausola IN come trascodificare l&#8217;header della colonna in un valore.<br />
Facciamo una piccola modifica ai dati:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; update vendite set anno_2010 = null where deptno=20;

1 row updated.

SQL&gt; select * from vendite;

    DEPTNO  ANNO_2008  ANNO_2009  ANNO_2010
---------- ---------- ---------- ----------
        10     350000     400000     500000
        20     185000     220000
        30     400000     500000     600000
</pre></p>
<p>e ripetiamo la stessa query di prima:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; select *
  2  from vendite
  3  UNPIVOT
  4  (importo for anno in (
  5           anno_2008 as 2008,
  6           anno_2009 as 2009,
  7           anno_2010 as 2010)
  8  );

    DEPTNO ANNO    IMPORTO
---------- ---- ----------
        10 2008     350000
        10 2009     400000
        10 2010     500000
        20 2008     185000
        20 2009     220000
        30 2008     400000
        30 2009     500000
        30 2010     600000

8 rows selected.

</pre></p>
<p>Le righe selezionate sono solo otto perché il valore nullo è stato ignorato.<br />
Se vogliamo che i valori nulli siano invece presi in considerazione basta chiederlo con l&#8217;aggiunta dell&#8217;opzione &#8220;INCLUDE NULLS&#8221;:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; select *
  2  from vendite
  3  UNPIVOT INCLUDE NULLS
  4  (importo for anno in (
  5           anno_2008 as 2008,
  6           anno_2009 as 2009,
  7           anno_2010 as 2010)
  8  );

    DEPTNO ANNO    IMPORTO
---------- ---- ----------
        10 2008     350000
        10 2009     400000
        10 2010     500000
        20 2008     185000
        20 2009     220000
        20 2010
        30 2008     400000
        30 2009     500000
        30 2010     600000

9 rows selected.

</pre></p>
<p>Ovviamente questi dati possono essere successivamente manipolati a piacere, ad esempio possiamo calcolare al volo la media annua delle vendite:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; select deptno, min(anno), max(anno), avg(importo)
  2  from vendite
  3  UNPIVOT INCLUDE NULLS
  4  (importo for anno in (
  5           anno_2008 as 2008,
  6           anno_2009 as 2009,
  7           anno_2010 as 2010)
  8  )
  9  group by deptno;

    DEPTNO  MIN(ANNO)  MAX(ANNO) AVG(IMPORTO)
---------- ---------- ---------- ------------
        30       2008       2010       500000
        20       2008       2010       202500
        10       2008       2010   416666.667

</pre></p>
<p>Alla prossima,<br />
Massimo</p>
<br />Filed under: <a href='http://oracleitalia.wordpress.com/category/11g/'>11g</a>, <a href='http://oracleitalia.wordpress.com/category/sql/'>SQL</a> Tagged: <a href='http://oracleitalia.wordpress.com/tag/oracle/'>oracle</a>, <a href='http://oracleitalia.wordpress.com/tag/sql/'>SQL</a>, <a href='http://oracleitalia.wordpress.com/tag/unpivot/'>UNPIVOT</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/oracleitalia.wordpress.com/311/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/oracleitalia.wordpress.com/311/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/oracleitalia.wordpress.com/311/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/oracleitalia.wordpress.com/311/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/oracleitalia.wordpress.com/311/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/oracleitalia.wordpress.com/311/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/oracleitalia.wordpress.com/311/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/oracleitalia.wordpress.com/311/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/oracleitalia.wordpress.com/311/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/oracleitalia.wordpress.com/311/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/oracleitalia.wordpress.com/311/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/oracleitalia.wordpress.com/311/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/oracleitalia.wordpress.com/311/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/oracleitalia.wordpress.com/311/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=311&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://oracleitalia.wordpress.com/2010/03/05/la-clausola-unpivot/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fbabbf605a3a90e522d71f6a0117055f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">massimoruocchio</media:title>
		</media:content>
	</item>
		<item>
		<title>Collezionare statistiche con DBMS_STATS</title>
		<link>http://oracleitalia.wordpress.com/2010/02/28/collezionare-statistiche-con-dbms_stats/</link>
		<comments>http://oracleitalia.wordpress.com/2010/02/28/collezionare-statistiche-con-dbms_stats/#comments</comments>
		<pubDate>Sun, 28 Feb 2010 21:18:30 +0000</pubDate>
		<dc:creator>massimoruocchio</dc:creator>
				<category><![CDATA[Ottimizzazione]]></category>
		<category><![CDATA[CBO]]></category>
		<category><![CDATA[DBMS_STATS]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[Ottimizzatore]]></category>

		<guid isPermaLink="false">http://oracleitalia.wordpress.com/?p=307</guid>
		<description><![CDATA[Quando Oracle ha introdotto l&#8217;ottimizzatore statistico il comando per raccogliere le statistiche era Già in Oracle8, però, è stato introdotto il package DBMS_STATS che offre molte utili funzionalità per la raccolta e la gestione delle statistiche, in quest&#8217;articolo le vediamo velocemente. Le funzioni presenti in DBMS_STATS consentono di Raccogliere Statistiche Le seguenti procedure consentono di [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=307&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Quando Oracle ha introdotto l&#8217;ottimizzatore statistico il comando per raccogliere le statistiche era</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; Analyze table EMP compute statistics;

Analizzata tabella.
</pre></p>
<p>Già in Oracle8, però, è stato introdotto il package DBMS_STATS che offre molte utili funzionalità per la raccolta e la gestione delle statistiche, in quest&#8217;articolo le vediamo velocemente.</p>
<p>Le funzioni presenti in DBMS_STATS consentono di </p>
<p><b>Raccogliere Statistiche</b></p>
<p>Le seguenti procedure consentono di raccogliere statistiche:</p>
<p>GATHER_DATABASE_STATS<br />
GATHER_DICTIONARY_STATS<br />
GATHER_FIXED_OBJECTS_STATS<br />
GATHER_INDEX_STATS<br />
GATHER_SCHEMA_STATS<br />
GATHER_SYSTEM_STATS<br />
GATHER_TABLE_STATS </p>
<p>Si passa dalla raccolta delle statistiche per l&#8217;intero DB a quelle per il singolo indice o tabella.<br />
Tutte le procedure raccolgono statistiche relative agli oggetti di DB ad eccezione della GATHER_SYSTEM_STATS che raccoglie statistiche sulle capacità dell&#8217;hardware del sistema (CPU e dischi).<br />
Le statistiche possono essere scritte nel dizionario dati (ed utilizzate dall&#8217;ottimizzatore), oppure in una tabella appositamente creata, vedremo come più avanti.</p>
<p><b>Gestire Statistiche</b></p>
<p>Le procedure seguenti:</p>
<p>PREPARE_COLUMN_VALUES<br />
PREPARE_COLUMN_VALUES_NVARCHAR2<br />
PREPARE_COLUMN_VALUES_ROWID<br />
SET_COLUMN_STATS<br />
SET_INDEX_STATS<br />
SET_SYSTEM_STATS<br />
SET_TABLE_STATS<br />
GET_COLUMN_STATS<br />
GET_INDEX_STATS<br />
GET_SYSTEM_STATS<br />
GET_TABLE_STATS </p>
<p>Consentono di gestire le statistiche settando o leggendo specifici valori con le procedure SET* e GET*.<br />
Le procedure PREPARE* servono a convertire un insieme di valori di una colonna al formato interno oracle per poi settarli utilizzando SET_COLUMN_VALUE.</p>
<p><b>Cancellare Statistiche</b></p>
<p>Alcune procedure consentono la cancellazione delle statistiche:</p>
<p>DELETE_COLUMN_STATS<br />
DELETE_DATABASE_STATS<br />
DELETE_DICTIONARY_STATS<br />
DELETE_FIXED_OBJECTS_STATS<br />
DELETE_INDEX_STATS<br />
DELETE_SCHEMA_STATS<br />
DELETE_SYSTEM_STATS<br />
DELETE_TABLE_STATS  </p>
<p><b>Trasferimento di Statistiche</b></p>
<p>Come detto le statistiche possono essere registrate nel dizionario oppure in una tabella appositamente creata.<br />
Le statistiche collezionate in tabella non vengono utilizzate dall&#8217;ottimizzatore.<br />
Per creare una tabella adatta a tale scopo si può utilizzare la procedura</p>
<p>CREATE_STAT_TABLE </p>
<p>Che può essere poi eliminata mediante la procedura </p>
<p>DROP_STAT_TABLE </p>
<p>Ci sono poi alcune procedure che servono a spostare le statistiche dal dizionario alla tabella:</p>
<p>EXPORT_COLUMN_STATS<br />
EXPORT_DATABASE_STATS<br />
EXPORT_DICTIONARY_STATS<br />
EXPORT_FIXED_OBJECTS_STATS<br />
EXPORT_INDEX_STATS<br />
EXPORT_SCHEMA_STATS<br />
EXPORT_SYSTEM_STATS<br />
EXPORT_TABLE_STATS </p>
<p>oppure dalla tabella al dizionario:</p>
<p>IMPORT_COLUMN_STATS<br />
IMPORT_DATABASE_STATS<br />
IMPORT_DICTIONARY_STATS<br />
IMPORT_FIXED_OBJECTS_STATS<br />
IMPORT_INDEX_STATS<br />
IMPORT_SCHEMA_STATS<br />
IMPORT_SYSTEM_STATS<br />
IMPORT_TABLE_STATS  </p>
<p><b>Bloccare e sbloccare Statistiche</b></p>
<p>Se vogliamo congelare le statistiche di un oggetto è possibile utilizzare le procedure<br />
LOCK_SCHEMA_STATS<br />
LOCK_TABLE_STATS </p>
<p>Il procedimento inverso può essere ottenuto mediante le procedure<br />
UNLOCK_SCHEMA_STATS<br />
UNLOCK_TABLE_STATS </p>
<p>Questa funzionalità è particolarmente utile quando si vuole che per una tabella non vengano più raccolte le statistiche.<br />
E&#8217; sufficiente infatti cancellare le statistiche e poi chiamare la procedura LOCK_SCHEMA_STATS:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; BEGIN
  2  DBMS_STATS.DELETE_TABLE_STATS('MAXR','EMP');
  3  DBMS_STATS.LOCK_TABLE_STATS('MAXR','EMP');
  4  END;
  5  /

Procedura PL/SQL completata correttamente.

</pre></p>
<p>In questo modo le statistiche sono e resteranno nulle.</p>
<p><b>Ripristino e svecchiamento delle Statistiche</b></p>
<p>La procedura PURGE_STATS elimina tutte le statistiche cancellate prima di un certo timestamp fornito in input.</p>
<p>Se invece si vogliono ripristinare le statistiche come erano ad un certo timestamp si possono utilizzare le procedure:</p>
<p>RESTORE_DICTIONARY_STATS<br />
RESTORE_FIXED_OBJECTS_STATS<br />
RESTORE_SCHEMA_STATS<br />
RESTORE_SYSTEM_STATS<br />
RESTORE_TABLE_STATS </p>
<p><b>Pending Statistics</b></p>
<p>A volte può essere utile verificare l&#8217;impatto della raccolta delle statistiche sui piani d&#8217;esecuzione prima di renderle attive.<br />
Per questo motivo esistono le &#8220;Pending Statistics&#8221;.<br />
Per fare in modo che le statistiche raccolte siano considerate pending, e quindi momentaneamente non utilizzate dall&#8217;ottimizzatore, bisogna impostare a FALSE il parametro PUBLISH utilizzando una delle seguenti procedure:</p>
<p>SET_DATABASE_PREFS<br />
SET_GLOBAL_PREFS<br />
SET_SCHEMA_PREFS<br />
SET_TABLE_PREFS </p>
<p>Ad esempio, per definire pending le statistiche sulla tabella EMP:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; begin
  2  DBMS_STATS.SET_TABLE_PREFS ('MAXR', 'EMP', 'PUBLISH', 'FALSE');
  3  end;
  4  /

Procedura PL/SQL completata correttamente.

</pre></p>
<p>Una volta poi raccolte le statistiche e valutato l&#8217;impatto sui piani d&#8217;esecuzione si può decidere di cancellarle:<br />
DELETE_PENDING_STATS </p>
<p>Esportarle in una tabella utente:<br />
EXPORT_PENDING_STATS </p>
<p>Pubblicarle, cioè renderle valide ed utilizzabili dall&#8217;ottimizzatore:<br />
PUBLISH_PENDING_STATS </p>
<p><b>Comparing Statistics</b></p>
<p>E&#8217; possibile ottenere un report delle differenze tra due versioni delle statistiche mediante le funzioni<br />
DIFF_TABLE_STATS_IN_HISTORY<br />
DIFF_TABLE_STATS_IN_PENDING<br />
DIFF_TABLE_STATS_IN_STATTAB </p>
<p>Si tratta di funzioni pipelined che possono essere utilizzate nella clausola FROM di una query.<br />
Vediamo un esempio.</p>
<p>Innanzitutto creiamo una tabella di test:<br />
<pre class="brush: sql; wrap-lines: false;">

SQL&gt; create table testtab as select * from emp;

Tabella creata.

</pre></p>
<p>E&#8217; identica alla EMP, anche come contenuto.<br />
Raccogliamone le statistiche:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; exec DBMS_STATS.GATHER_TABLE_STATS('MAXR','TESTTAB')

Procedura PL/SQL completata correttamente.

SQL&gt; select systimestamp  from dual;

SYSTIMESTAMP
---------------------------------------------------------------------------
28-FEB-10 21:44:41,859000 +01:00

</pre></p>
<p>Adesso inseriamo un po&#8217; di record aggiuntivi:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; insert into testtab
  2  select * from testtab;

Create 14 righe.

SQL&gt; r
  1  insert into testtab
  2* select * from testtab

Create 28 righe.

SQL&gt; r
  1  insert into testtab
  2* select * from testtab

Create 56 righe.

SQL&gt; r
  1  insert into testtab
  2* select * from testtab

Create 112 righe.

SQL&gt; r
  1  insert into testtab
  2* select * from testtab

Create 224 righe.

SQL&gt; r
  1  insert into testtab
  2* select * from testtab

Create 448 righe.

SQL&gt; r
  1  insert into testtab
  2* select * from testtab

Create 896 righe.

SQL&gt; r
  1  insert into testtab
  2* select * from testtab

Create 1792 righe.

SQL&gt; r
  1  insert into testtab
  2* select * from testtab

Create 3584 righe.

SQL&gt; r
  1  insert into testtab
  2* select * from testtab

Create 7168 righe.

SQL&gt; r
  1  insert into testtab
  2* select * from testtab

Create 14336 righe.

SQL&gt; r
  1  insert into testtab
  2* select * from testtab

Create 28672 righe.

SQL&gt; r
  1  insert into testtab
  2* select * from testtab

Create 57344 righe.

SQL&gt; commit;

Commit completato.

</pre></p>
<p>E raccogliamo di nuovo le statistiche:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; exec DBMS_STATS.GATHER_TABLE_STATS('MAXR','TESTTAB');

Procedura PL/SQL completata correttamente.

</pre></p>
<p>A questo punto siamo pronti per vedere il report delle differenze:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; set long 5000
SQL&gt; select * from
  2* table(DBMS_STATS.DIFF_TABLE_STATS_IN_HISTORY(
           'MAXR',
           'TESTTAB',
           to_timestamp('20100228214442','yyyymmddhh24miss'),
           systimestamp,
           0)
           );

REPORT                                                                           MAXDIFFPCT
-------------------------------------------------------------------------------- ----------
###############################################################################      819100

STATISTICS DIFFERENCE REPORT FOR:
.................................

TABLE         : TESTTAB
OWNER         : MAXR
SOURCE A      : Statistics as of 28-FEB-10 21:44:42,000000 +01:00
SOURCE B      : Statistics as of 28-FEB-10 21:50:16,359000 +01:00
PCTTHRESHOLD  : 0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

REPORT                                                                           MAXDIFFPCT
-------------------------------------------------------------------------------- ----------

TABLE / (SUB)PARTITION STATISTICS DIFFERENCE:
.............................................

OBJECTNAME                  TYP SRC ROWS       BLOCKS     ROWLEN     SAMPSIZE
...............................................................................

TESTTAB                     T   A   14         4          37         14
                                B   114688     751        37         114688
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


REPORT                                                                           MAXDIFFPCT
-------------------------------------------------------------------------------- ----------
COLUMN STATISTICS DIFFERENCE:
.............................

COLUMN_NAME     SRC NDV     DENSITY    HIST NULLS   LEN  MIN   MAX   SAMPSIZ
...............................................................................

COMM            A   4       ,25        NO   10      2    80    C20F  4
                B   4       ,25        NO   81920   2    80    C20F  32768
MGR             A   6       ,166666666 NO   1       4    C24C4 C2500 13
                B   6       ,166666666 NO   8192    4    C24C4 C2500 106496
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

REPORT                                                                           MAXDIFFPCT
-------------------------------------------------------------------------------- ----------


NO DIFFERENCE IN INDEX / (SUB)PARTITION STATISTICS
###############################################################################

</pre></p>
<p><b>Statistiche Estese</b></p>
<p>Il package DBMS_STATS ci dà anche la possibilità di calcolare statistiche su gruppi di colonne o espressioni. Queste vengono dette &#8220;Statistiche Estese&#8221;.<br />
Le statistiche su gruppi di colonne sono utilizzare dall&#8217;ottimizzatore quando in una query ci sono condizioni su diverse colonne. Una eventuale correlazione tra le colonne (valutata appunto leggendo le statistiche estese) può far cambiare il piano d&#8217;esecuzione.</p>
<p>Per creare le statistiche estese utilizziamo la funzione<br />
CREATE_EXTENDED_STATS </p>
<p>Per cancellarle:<br />
DROP_EXTENDED_STATS </p>
<p>E per vedere i nomi delle estensioni la funzione:<br />
SHOW_EXTENDED_STATS_NAME </p>
<p>Ad esempio per creare una statistica estesa sulla correlazione tra EMPNO ed ENAME facciamo:</p>
<p><pre class="brush: sql; wrap-lines: false;">

SQL&gt; exec dbms_output.put_line(DBMS_STATS.CREATE_EXTENDED_STATS('MAXR','TESTTAB','(EMPNO,ENAME)'))
SYS_STU7CJ_LJ#28ARDED17#AUWYJZ

Procedura PL/SQL completata correttamente.

</pre></p>
<p>Ho stampato il valore di ritorno che è il nome dell&#8217;estesione creata.</p>
<p>Se mi perdo questo nome posso ricavarlo mediante la funzione SHOW_EXTENDED_STATS_NAME:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt; select dbms_stats.SHOW_EXTENDED_STATS_NAME('MAXR','TESTTAB','(ENAME, EMPNO)')
  2  from dual;

DBMS_STATS.SHOW_EXTENDED_STATS_NAME('MAXR','TESTTAB','(ENAME,EMPNO)')
---------------------------------------------------------------------------------------
SYS_STU7CJ_LJ#28ARDED17#AUWYJZ

SQL&gt; select dbms_stats.SHOW_EXTENDED_STATS_NAME('MAXR','TESTTAB','(DEPTNO, ENAME)')
  2  from dual;
select dbms_stats.SHOW_EXTENDED_STATS_NAME('MAXR','TESTTAB','(DEPTNO, ENAME)')
       *
ERRORE alla riga 1:
ORA-20000: extension &quot;(DEPTNO, ENAME)&quot; does not exist
ORA-06512: a &quot;SYS.DBMS_STATS&quot;, line 25416

</pre></p>
<p>Che come si vede nell&#8217;ultimo esempio torna un errore se cerchiamo di leggere il nome di un&#8217;estensione che non è stata definita.</p>
<p>Un&#8217;altro tipo di estensione che si può creare è su un&#8217;espressione, ad esempio:</p>
<p><pre class="brush: sql; wrap-lines: false;">
SQL&gt;  exec dbms_output.put_line(DBMS_STATS.CREATE_EXTENDED_STATS('MAXR','TESTTAB','(TO_CHAR(EMPNO))'))
SYS_STUM9HJB83_TSA6P91FJMZ84U9

Procedura PL/SQL completata correttamente.
</pre></p>
<p>Questo è tutto per quanto riguarda il package DBMS_STATS.</p>
<p>Alla prossima,<br />
Massimo</p>
<br />Filed under: <a href='http://oracleitalia.wordpress.com/category/ottimizzazione/'>Ottimizzazione</a> Tagged: <a href='http://oracleitalia.wordpress.com/tag/cbo/'>CBO</a>, <a href='http://oracleitalia.wordpress.com/tag/dbms_stats/'>DBMS_STATS</a>, <a href='http://oracleitalia.wordpress.com/tag/oracle/'>oracle</a>, <a href='http://oracleitalia.wordpress.com/tag/ottimizzatore/'>Ottimizzatore</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/oracleitalia.wordpress.com/307/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/oracleitalia.wordpress.com/307/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/oracleitalia.wordpress.com/307/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/oracleitalia.wordpress.com/307/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/oracleitalia.wordpress.com/307/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/oracleitalia.wordpress.com/307/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/oracleitalia.wordpress.com/307/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/oracleitalia.wordpress.com/307/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/oracleitalia.wordpress.com/307/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/oracleitalia.wordpress.com/307/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/oracleitalia.wordpress.com/307/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/oracleitalia.wordpress.com/307/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/oracleitalia.wordpress.com/307/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/oracleitalia.wordpress.com/307/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=oracleitalia.wordpress.com&amp;blog=10730896&amp;post=307&amp;subd=oracleitalia&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://oracleitalia.wordpress.com/2010/02/28/collezionare-statistiche-con-dbms_stats/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fbabbf605a3a90e522d71f6a0117055f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">massimoruocchio</media:title>
		</media:content>
	</item>
	</channel>
</rss>
