<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10spanishfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DU8DRHoyfCp7ImA9WxJUEkg.&quot;"><id>tag:blogger.com,1999:blog-26801181</id><updated>2009-07-10T22:31:15.494+02:00</updated><title>Programación PL/SQL y bases de datos Oracle</title><subtitle type="html">Artículos acerca del leguaje de programación de bases de datos Oracle PLSQL.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.plsql.biz/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.plsql.biz/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>50</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" /><link rel="self" href="http://feeds.feedburner.com/PLSQL" type="application/atom+xml" /><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FPLSQL" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/PLSQL" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FPLSQL" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FPLSQL" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FPLSQL" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://add.my.yahoo.com/content?lg=es&amp;url=http%3A%2F%2Ffeeds.feedburner.com%2FPLSQL" src="http://eur.i1.yimg.com/eur.yimg.com/i/es/my/addto1.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.feedness.com/alta/http://feeds.feedburner.com/PLSQL" src="http://www.feedness.com/ayuda/wp-content/square_b_sh_feed.gif">Subscribe with Feedness</feedburner:feedFlare><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry gd:etag="W/&quot;CUQAQXg6fyp7ImA9WxJXFkg.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-2717845421088077638</id><published>2009-06-10T18:36:00.014+02:00</published><updated>2009-06-10T19:02:20.617+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-10T19:02:20.617+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Librerías estándar PLSQL" /><title>El paquete PL/SQL DBMS_SCHEDULER para programación de trabajos</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/C9TbQ1CHxfNUeCPsGUp4GNb6Ipw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/C9TbQ1CHxfNUeCPsGUp4GNb6Ipw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/C9TbQ1CHxfNUeCPsGUp4GNb6Ipw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/C9TbQ1CHxfNUeCPsGUp4GNb6Ipw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ObxKtfPuuSQ/Si_iKOujajI/AAAAAAAAEmA/aOmOysywNm4/s1600-h/DBMS-SCHEDULER-PLSQL-Clock-Reloj.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 200px;" src="http://1.bp.blogspot.com/_ObxKtfPuuSQ/Si_iKOujajI/AAAAAAAAEmA/aOmOysywNm4/s200/DBMS-SCHEDULER-PLSQL-Clock-Reloj.jpg" border="0" alt="DBMS_SCHEDULER programación de trabajos y procesos en PLSQL" id="BLOGGER_PHOTO_ID_5345739948091992626" /&gt;&lt;/a&gt;&lt;b&gt;DBMS_SCHEDULER&lt;/b&gt; es el paquete PLSQL que reemplazó en la versión de la base de datos Oracle 10g al paquete &lt;b&gt;DBMS_JOB&lt;/b&gt;. Aunque el paquete DBMS_JOB sigue existiendo por razones de compatibilidad, no debe utilizarse ya que es muy probable que deje de existir en futuras versiones de la base de datos Oracle. El paquete &lt;b&gt;DBMS_SCHEDULER&lt;/b&gt; permite programar la ejecución, en los instantes que deseemos, de &lt;a href="http://www.plsql.biz/2006/10/los-bloques-de-cdigo-plsql.html"&gt;bloques PLSQL&lt;/a&gt;, así como de &lt;a href="http://www.plsql.biz/2007/03/procedimientos-y-funciones-en-plsql.html"&gt;procedimientos y funciones PL/SQL&lt;/a&gt;. Por otro lado, también permite programar la ejecución de binarios y &lt;i&gt;shell-scripts&lt;/i&gt;.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;b&gt;Permisos necesarios&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Con permisos de DBA se tiene acceso a todas las funciones del paquete DBMS_SCHEDULER. Para administrar la programación de procesos se necesita tener la rol (&lt;i&gt;role&lt;/i&gt;) de SCHEDULER_ADMIN. Y finalmente, para crear y ejecutar procesos bajo tu propia identidad, se necesita tener el privilegio CREATE JOB. Por otro lado, aprovecho para mencionar que el paquete DBMS_JOB requería la inicialización de un parámetro del sistema, tras lo cual se lanzaba un proceso en &lt;i&gt;background&lt;/i&gt; encargado de coordinar la ejecución de los distintos procesos programados, pero esto ya no es necesario si utilizamos Oracle 10g y el paquete DBMS_SCHEDULER.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Programación rápida de ejecución de un bloque PL/SQL&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Para programar la ejecución de un bloque PLSQL podemos utilizar directamente el siguiente código. Sin duda es la forma más rápida y sencilla de programar la ejecución de un trabajo.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 95%;"&gt;&lt;pre&gt;BEGIN&lt;br /&gt;  DBMS_SCHEDULER.create_job(&lt;br /&gt;     job_name        =&gt; 'DEMO_BLOQUE_PROGRAMADO'&lt;br /&gt;    ,job_type        =&gt; 'PLSQL_BLOCK'&lt;br /&gt;    ,job_action      =&gt; &lt;br /&gt;      'begin &lt;br /&gt;       nom_paq.nom_proced(''valor_param'');&lt;br /&gt;       end;'&lt;br /&gt;    ,start_date      =&gt; '06/11/2009 02:00 AM'&lt;br /&gt;    ,repeat_interval =&gt; 'FREQ=DAILY'&lt;br /&gt;    ,enabled         =&gt; TRUE&lt;br /&gt;    ,comments        =&gt; 'Bloque PLSQL programado');&lt;br /&gt;  COMMIT;&lt;br /&gt;END;&lt;br /&gt;/&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En el código de arriba lo que hemos hecho es programar un bloque PL/SQL para que se ejecute diariamente a las dos de la mañana a partir del día 11 de junio de 2009.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ejemplo completo de programación de un trabajo&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Lo primero que debemos hacer el &lt;b&gt;definir el programa&lt;/b&gt; que se va a ejecutar.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 95%;"&gt;&lt;pre&gt;BEGIN&lt;br /&gt;  DBMS_SCHEDULER.create_program (&lt;br /&gt;     program_name        =&gt; 'DEMO_PROC_PROGRAMADO'&lt;br /&gt;    ,program_type        =&gt; 'STORED_PROCEDURE'&lt;br /&gt;    ,program_action      =&gt; 'nom_paq.nom_proced'&lt;br /&gt;    ,number_of_arguments =&gt; 1&lt;br /&gt;    ,enabled             =&gt; FALSE&lt;br /&gt;    ,comments            =&gt; 'Proc. PLSQL programado');&lt;br /&gt;  DBMS_SCHEDULER.define_program_argument (&lt;br /&gt;     program_name      =&gt; 'DEMO_PROC_PROGRAMADO'&lt;br /&gt;    ,argument_position =&gt; 1&lt;br /&gt;    ,argument_name     =&gt; 'valor1'&lt;br /&gt;    ,argument_type     =&gt; 'VARCHAR2'&lt;br /&gt;    ,default_value     =&gt; 'valor por defecto');&lt;br /&gt;  DBMS_SCHEDULER.enable (&lt;br /&gt;     name =&gt; 'DEMO_PROC_PROGRAMADO');&lt;br /&gt;  COMMIT;&lt;br /&gt;END;&lt;br /&gt;/&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;El parámetro &lt;i&gt;program_type&lt;/i&gt; puede tomar los siguiente valores: PLSQL_BLOCK, STORED_PROCEDURE o EXECUTABLE.&lt;br /&gt;&lt;br /&gt;A continuación debemos definir cuando y con que frecuencia queremos ejecutar nuestro programa, es decir, debemos &lt;b&gt;definir la &lt;i&gt;schedule&lt;/i&gt;&lt;/b&gt; propiamente dicha.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 95%;"&gt;&lt;pre&gt;BEGIN&lt;br /&gt;  DBMS_SCHEDULER.create_schedule(  &lt;br /&gt;     schedule_name   =&gt; 'DEMO_SCHEDULE'&lt;br /&gt;    ,start_date      =&gt;  '01/01/2009 01:00:00'&lt;br /&gt;    ,repeat_interval =&gt; 'FREQ=WEEKLY'&lt;br /&gt;    ,comments        =&gt; 'Semanalmente a las 01:00');&lt;br /&gt;  COMMIT;&lt;br /&gt;END;&lt;br /&gt;/&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Si queremos deshabilitar la &lt;i&gt;schedule&lt;/i&gt; anteriormente definida utilizaremos el siguiente código:&lt;br /&gt; &lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 95%;"&gt;&lt;pre&gt;BEGIN&lt;br /&gt;  DBMS_SCHEDULER.drop_schedule(&lt;br /&gt;     schedule_name =&gt; 'DEMO_SCHEDULE'&lt;br /&gt;    ,force         =&gt; TRUE );&lt;br /&gt;  COMMIT;&lt;br /&gt;END;&lt;br /&gt;/&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Para establecer la frecuencia (&lt;i&gt;repeat_interval&lt;/i&gt;) podemos utilizar las siguientes expresiones: YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY y SECONDELY.&lt;br /&gt;&lt;br /&gt;A continuación debemos &lt;b&gt;definir el proceso o trabajo&lt;/b&gt; que queremos programar. Esto se realiza mediante la asignación de un programa a una o más &lt;i&gt;schedules&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 95%;"&gt;&lt;pre&gt;BEGIN&lt;br /&gt;  DBMS_SCHEDULER.create_job(&lt;br /&gt;     job_name      =&gt; 'DEMO_TRABAJO_PLSQL'&lt;br /&gt;    ,program_name  =&gt;'DEMO_PROC_PROGRAMADO'&lt;br /&gt;    ,schedule_name =&gt;'DEMO_SCHEDULE'&lt;br /&gt;    ,enabled       =&gt; FALSE&lt;br /&gt;    ,comments      =&gt; 'Semanal a las 01:00');&lt;br /&gt;  DBMS_SCHEDULER.set_job_argument_value(&lt;br /&gt;     job_name          =&gt; 'DEMO_TRABAJO_PLSQL'&lt;br /&gt;    ,argument_position =&gt; 1&lt;br /&gt;    ,argument_value    =&gt; 'valorparametro1');&lt;br /&gt;  DBMS_SCHEDULER.enable('DEMO_TRABAJO_PLSQL');&lt;br /&gt;  COMMIT;&lt;br /&gt;END;&lt;br /&gt;/&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Por otro lado y según ya he comentado, con el paquete DBMS_SCHEDULER también podemos hacer correr directamente un ejecutable o una &lt;i&gt;shell script&lt;/i&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 95%;"&gt;&lt;pre&gt;BEGIN&lt;br /&gt;   DBMS_SCHEDULER.create_job&lt;br /&gt;   (&lt;br /&gt;      job_name      =&gt; 'RUN_SHELL_SCRIPT_DEMO',&lt;br /&gt;      schedule_name =&gt; 'DEMO_SCHEDULE',&lt;br /&gt;      job_type      =&gt; 'EXECUTABLE',&lt;br /&gt;      job_action    =&gt; '/home/shell/demo_script.sh',&lt;br /&gt;      enabled       =&gt; TRUE,&lt;br /&gt;      comments      =&gt; 'Ejecución de un shell script'&lt;br /&gt;   );&lt;br /&gt;  COMMIT;&lt;br /&gt;END;&lt;br /&gt;/&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;Otros paquetes estándares del PLSQL&lt;/b&gt;: &lt;a href="http://www.plsql.biz/2009/02/el-paquete-estandar-dbmslock-para.html"&gt;DBMS_LOCK&lt;/a&gt;, &lt;a href="http://www.plsql.biz/2008/08/paquete-dbmssql-para-utilizar-sql.html"&gt;DBMS_SQL&lt;/a&gt; y &lt;a href="http://www.plsql.biz/2006/10/como-mostrar-mensajes-de-texto-en.html"&gt;DBMS_OUTPUT&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-2717845421088077638?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/96Pgbulxg4E" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/2717845421088077638/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=2717845421088077638&amp;isPopup=true" title="1 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/2717845421088077638?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/2717845421088077638?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/96Pgbulxg4E/el-paquete-plsql-dbmsscheduler-para.html" title="El paquete PL/SQL DBMS_SCHEDULER para programación de trabajos" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_ObxKtfPuuSQ/Si_iKOujajI/AAAAAAAAEmA/aOmOysywNm4/s72-c/DBMS-SCHEDULER-PLSQL-Clock-Reloj.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://www.plsql.biz/2009/06/el-paquete-plsql-dbmsscheduler-para.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0QFSXs5eCp7ImA9WxJSFk4.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-6987664821462539475</id><published>2009-05-06T18:07:00.007+02:00</published><updated>2009-05-06T19:35:18.520+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-06T19:35:18.520+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><title>Como evitar el uso de constantes fijas (hard-coded) en PL/SQL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/X5VGrJep4BQOwOOTIyU5SQWewmc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/X5VGrJep4BQOwOOTIyU5SQWewmc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/X5VGrJep4BQOwOOTIyU5SQWewmc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/X5VGrJep4BQOwOOTIyU5SQWewmc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ObxKtfPuuSQ/SgG2LjVSCqI/AAAAAAAAEgI/Rn9o9oLcJEg/s1600-h/chiste-plsql-internet.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 168px; height: 200px;" src="http://4.bp.blogspot.com/_ObxKtfPuuSQ/SgG2LjVSCqI/AAAAAAAAEgI/Rn9o9oLcJEg/s200/chiste-plsql-internet.jpg" border="0" alt="Chiste de perros e Internet para amenizar el tema de las constantes fijas en PLSQL" id="BLOGGER_PHOTO_ID_5332743743362697890" /&gt;&lt;/a&gt;Esta claro que la mayoría de los &lt;b&gt;programas y aplicaciones PLSQL&lt;/b&gt; tienen su propio &lt;b&gt;conjunto de constantes&lt;/b&gt; que determinan las características de dicha aplicación. Por lo general, estos valores constantes tienen que ser utilizados en distintos lugares del código. En muchas ocasiones estos valores permanecerán invariables durante todo el ciclo de vida de la aplicación pero, en muchos otros casos, cambiarán de forma periódica (por ejemplo, una vez al año).&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Pongamos un ejemplo, supongamos que en un programa PL/SQL establecemos que el salario anual de un empleado no puede superar los 50.000 euros. Podemos codificar esta norma utilizando la siguiente subrutina:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;IF l_salario_anual &gt; 50000&lt;br /&gt;THEN&lt;br /&gt;RAISE_APPLICATION_ERROR (&lt;br /&gt;  -20001, &lt;br /&gt;  'El salario anual no puede ser superior&lt;br /&gt;  a 50000 euros');&lt;br /&gt;END IF;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;La mayoría de los programadores sabemos que referenciar un valor constante de esta manera no es muy recomendable. En inglés se dice que la constante está "&lt;i&gt;hard-coded&lt;/i&gt;" directamente en la rutina. Resulta obvio señalar que este tipo de prácticas suele derivar en &lt;a href="http://problemasdeajedrez.blogspot.com/" target="_blank"&gt;problemas&lt;/a&gt;, ya que el valor de 50.000 euros cambiará muy probablemente de año en año y, cuando esto ocurra, deberemos revisar el código PLSQL y cambiar el antiguo valor por el nuevo en cada uno de los sitios donde sea utilizado.&lt;br /&gt;&lt;br /&gt;En PL/SQL disponemos de &lt;b&gt;tres formas&lt;/b&gt; bastante evidentes de evitar tener que utilizar constantes directamente en el código:&lt;ul&gt;&lt;li&gt;Crear un paquete PLSQL que asigne el valor a esa constante&lt;/li&gt;&lt;li&gt;Crear un función PLSQL que devuelva dicho valor&lt;/li&gt;&lt;li&gt;Almacenar y gestionar el valor de dicha constante en una tabla de la base de datos Oracle&lt;/li&gt;&lt;/ul&gt;A continuación pondré un ejemplo de cada una de las opciones mencionadas.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Paquete PL/SQL de constantes&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE PACKAGE c_constantes IS&lt;br /&gt; c_salario_maximo CONSTANT NUMBER := 50000;&lt;br /&gt;END c_constantes;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Una vez definido el paquete ya sólo tendríamos que referirnos al mismo de la siguiente manera:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;IF l_salario_anual &gt; &lt;br /&gt;   c_constantes.c_salario_maximo&lt;br /&gt;THEN&lt;br /&gt;  RAISE_APPLICATION_ERROR (&lt;br /&gt;  -20001, &lt;br /&gt;  'El salario anual no puede ser superior a '&lt;br /&gt;  || c_constantes.c_salario_maximo&lt;br /&gt;  || ' euros');&lt;br /&gt;END IF;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;Función PL/SQL que devuelve el valor de la constante&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE PACKAGE f_constantes IS&lt;br /&gt;FUNCTION f_salario_maximo RETURN NUMBER;&lt;br /&gt;END f_constantes;&lt;br /&gt;&lt;br /&gt;CREATE PACKAGE BODY f_constantes IS&lt;br /&gt;FUNCTION f_salario_maximo RETURN NUMBER IS&lt;br /&gt;BEGIN&lt;br /&gt;  RETURN 50000;&lt;br /&gt;END;&lt;br /&gt;END f_constantes;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Y ahora para referirnos a la constante utilizaríamos el siguiente código:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;IF l_salario_anual &gt; &lt;br /&gt;   f_constantes.f_salario_maximo()&lt;br /&gt;THEN&lt;br /&gt;  RAISE_APPLICATION_ERROR (&lt;br /&gt;  -20001, &lt;br /&gt;  'El salario anual no puede ser superior a '&lt;br /&gt;  || f_constantes.f_salario_maximo()&lt;br /&gt;  || ' euros');&lt;br /&gt;END IF;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;Almacenar la constante en una tabla de la base de datos&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Si en nuestra aplicación debemos gestionar un buen número de constantes, la opción más recomendable sería crear una tabla con los campos, por ejemplo, "&lt;i&gt;nombre&lt;/i&gt;", "&lt;i&gt;descripción&lt;/i&gt;" y "&lt;i&gt;valor&lt;/i&gt;", y sacar de ella los valores constantes correspondientes. En nuestro caso, y suponiendo que el campo "&lt;i&gt;valor&lt;/i&gt;" es un VARCHAR2, podríamos escribir la siguiente función PLSQL:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE PACKAGE t_constantes IS&lt;br /&gt;FUNCTION t_numero &lt;br /&gt;  (t_nombre IN tabla_constantes.valor%TYPE) &lt;br /&gt;  RETURN NUMBER;&lt;br /&gt;END t_constantes;&lt;br /&gt;&lt;br /&gt;CREATE PACKAGE BODY t_constantes IS&lt;br /&gt;FUNCTION t_numero &lt;br /&gt;  (p_nombre IN tabla_constantes.valor%TYPE) &lt;br /&gt;  RETURN NUMBER IS&lt;br /&gt;BEGIN&lt;br /&gt;  v_valor tabla_constantes.valor%TYPE;&lt;br /&gt;  SELECT valor&lt;br /&gt;  INTO v_valor&lt;br /&gt;  FROM tabla_constantes&lt;br /&gt;  WHERE nombre = p_nombre;&lt;br /&gt;  RETURN TO_NUMBER(v_valor);&lt;br /&gt;END;&lt;br /&gt;END t_constantes;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Y para referirnos a la constante utilizaríamos algo semejante a:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;IF l_salario_anual &gt;&lt;br /&gt;   t_constantes.t_numero('salario maximo')&lt;br /&gt;THEN&lt;br /&gt;  RAISE_APPLICATION_ERROR (&lt;br /&gt;  -20001, &lt;br /&gt;  'El salario anual no puede ser superior a '&lt;br /&gt;  || t_constantes.t_numero('salario maximo')&lt;br /&gt;  || ' euros');&lt;br /&gt;END IF;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En este ejemplo he definido una función para devolver valores de tipo numérico (NUMBER), pero igualmente podríamos definir funciones semejantes que devuelvan valores de tipo VARCHAR2, DATE, etcétera.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Artículos relacionados&lt;/b&gt;: &lt;br /&gt;&lt;a href="http://www.plsql.biz/2006/10/tipos-de-datos-en-plsql.html"&gt;Tipos de datos en PLSQL&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://www.plsql.biz/2007/03/procedimientos-y-funciones-en-plsql.html"&gt;Procedimientos y funciones en PLSQL&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-6987664821462539475?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/C4uL4nDzRxU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/6987664821462539475/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=6987664821462539475&amp;isPopup=true" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6987664821462539475?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6987664821462539475?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/C4uL4nDzRxU/como-evitar-el-uso-de-constantes-fijas.html" title="Como evitar el uso de constantes fijas (hard-coded) en PL/SQL" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_ObxKtfPuuSQ/SgG2LjVSCqI/AAAAAAAAEgI/Rn9o9oLcJEg/s72-c/chiste-plsql-internet.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://www.plsql.biz/2009/05/como-evitar-el-uso-de-constantes-fijas.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkEER3g-eip7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-3207262870184619556</id><published>2009-03-30T18:16:00.008+02:00</published><updated>2009-04-03T12:30:06.652+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:30:06.652+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>La funcionalidad de muestreo dinámico o Dynamic Sampling</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/TV1p_xNUzOXVsFbNJdJXQiop_4Y/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/TV1p_xNUzOXVsFbNJdJXQiop_4Y/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/TV1p_xNUzOXVsFbNJdJXQiop_4Y/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/TV1p_xNUzOXVsFbNJdJXQiop_4Y/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ObxKtfPuuSQ/SdD0bR-h7-I/AAAAAAAAEXs/rOQsCcyxIY8/s1600-h/PLSQL-dynamic-sampling-tetris.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 208px; height: 174px;" src="http://1.bp.blogspot.com/_ObxKtfPuuSQ/SdD0bR-h7-I/AAAAAAAAEXs/rOQsCcyxIY8/s200/PLSQL-dynamic-sampling-tetris.jpg" border="0" alt="Muestreo dinámico en las bases de dato Oracle para SQL y PL/SQL" id="BLOGGER_PHOTO_ID_5319019909443874786" /&gt;&lt;/a&gt;La &lt;b&gt;funcionalidad de muestreo dinámico&lt;/b&gt; (&lt;i&gt;Dynamic Sampling&lt;/i&gt;) estuvo por primera vez disponible para la &lt;b&gt;release 2 de la bases de datos Oracle 9i&lt;/b&gt;. Esta funcionalidad posibilita que el &lt;b&gt;optimizador SQL y PL/SQL basado en costes&lt;/b&gt; (CBO) muestree las tablas que utiliza una consulta (&lt;i&gt;query&lt;/i&gt;) durante la fase de &lt;a href="http://www.plsql.biz/2007/04/fases-durante-el-procesamiento-de-una.html"&gt;&lt;i&gt;parsing duro&lt;/i&gt;&lt;/a&gt;, para mejorar los valores estadísticos que utiliza el optimizador al incluir en dichas estadísticas los segmentos no analizados con anterioridad. Como ya he indicado, este muestro sólo ocurre durante la fase de &lt;i&gt;parsing duro&lt;/i&gt; y se utiliza para mejorar las estadísticas utilizadas por el optimizador PLSQL, de ahí el nombre de muestro dinámico.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;El optimizador de las bases de datos Oracle usa una gran variedad de datos de entradas para generar los planes de ejecución. Utiliza todos los índices y restricciones (&lt;i&gt;constraints&lt;/i&gt;) definidos en las tablas, las estadísticas del sistema (velocidad de entrada/salida de los servidores, velocidad de CPU), y las estadísticas recogidas de los segmentos involucrados en la ejecución de la consulta. El optimizador utiliza estas estadísticas para &lt;b&gt;estimar el número de registros&lt;/b&gt; involucrados en cada uno de los pasos de los que consta un &lt;b&gt;plan de ejecución&lt;/b&gt; específico, siendo este volumen de registros la variable principal a la hora de calcular el coste de ejecución de una consulta. Cuando el optimizador calcula incorrectamente el volumen de registros, probablemente escogerá un plan de ejecución ineficiente causando problemas de rendimiento en la base de datos. De hecho, la principal razón por la que el optimizador Oracle puede decidirse por un plan de ejecución incorrecto, es precisamente por esta causa, es decir, una estimación incorrecta del volumen de registros a procesar en cada paso del plan de ejecución.&lt;br /&gt;&lt;br /&gt;En conclusión, el principal motivo que existe detrás de utilizar la funcionalidad del muestreo dinámico, es ayudar al optimizador a estimar correctamente el volumen de registros a procesar, alimentándole con mayor cantidad de datos y una información más exacta relativa a la ejecución de la consulta que se necesita ejecutar en ese mismo momento.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Formas de trabajar del muestreo dinámico o Dynamic Sampling&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;El tipo de muestreo dinámico puede establecerse de dos formas diferentes:&lt;ul&gt;&lt;li&gt;Con el parámetro &lt;b&gt;&lt;i&gt;OPTIMIZER_DYNAMIC_SAMPLING&lt;/i&gt;&lt;/b&gt; puede establecerse el tipo de muestreo dinámico, bien a nivel de instancia de la base de datos, bien a nivel de sesión mediante la utilización del comando &lt;i&gt;ALTER SESSION&lt;/i&gt;.&lt;br /&gt;&lt;li&gt;También se puede utilizar el &lt;i&gt;hint&lt;/i&gt; de SQL o PL/SQL denominado &lt;b&gt;&lt;i&gt;DYNAMIC_SAMPLING&lt;/i&gt;&lt;/b&gt; para establecer es tipo de muestreo a nivel de consulta&lt;/li&gt;&lt;/ul&gt;A continuación os mostraré un &lt;b&gt;ejemplo&lt;/b&gt; en el que podréis ver algunas diferencias entre un plan de ejecución generado por el optimizador Oracle sin utilizar muestreo dinámico, y otro plan en el que si que se utiliza dicha funcionalidad. En el ejemplo debemos considerar que la tabla &lt;i&gt;mitabla&lt;/i&gt; consta de 60.000 registros:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 85%;"&gt;&lt;pre&gt;SQL&gt; set autotrace traceonly explain&lt;br /&gt;SQL&gt; select /*+ dynamic_sampling(mit 0) */ * &lt;br /&gt;SQL&gt; from mitabla mit;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;------------------------------------------------------&lt;br /&gt;Plan hash value: 1601036213&lt;br /&gt;------------------------------------------------------&lt;br /&gt;|Id|Operation        |Name|Rows |Cost (%CPU)|Time    |&lt;br /&gt;------------------------------------------------------&lt;br /&gt;| 0|SELECT STATEMENT |    |15034| 52     (0)|00:00:01|&lt;br /&gt;| 1|TABLE ACCESS FULL|T   |15034| 52     (0)|00:00:01|&lt;br /&gt;------------------------------------------------------&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from mitabla mit;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;------------------------------------------------------&lt;br /&gt;Plan hash value: 1601036213&lt;br /&gt;------------------------------------------------------&lt;br /&gt;|Id|Operation        |Name|Rows |Cost (%CPU)|Time    |&lt;br /&gt;------------------------------------------------------&lt;br /&gt;| 0|SELECT STATEMENT |    |64222| 54     (2)|00:00:01|&lt;br /&gt;| 1|TABLE ACCESS FULL|T   |64222| 54     (2)|00:00:01|&lt;br /&gt;------------------------------------------------------&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En el ejemplo vemos un primer caso de plan de ejecución en el que hemos &lt;b&gt;deshabilitado el muestreo dinámico&lt;/b&gt; utilizando el &lt;i&gt;hint DYNAMIC_SAMPLING&lt;/i&gt;, mientras que el segundo plan de ejecución, puesto que por defecto dicha funcionalidad está habilitada y no hemos utilizado ningún &lt;i&gt;hint&lt;/i&gt; en la consulta, se ha generado utilizando el muestro dinámico. Como podemos ver, los planes de ejecución arrojan datos bastante dispares. Al deshabilitar el muestreo dinámico, el optimizador utiliza los &lt;b&gt;valores estadísticos por defecto&lt;/b&gt; que típicamente no son muy fiables, ya que el número de registros se estima en base a número de bloques de datos en la tabla y al tamaño medio de los registros de dicha tabla. Así, al deshabilitar el muestreo dinámico, el optimizador deduce que la tabla cuenta con tan sólo 15.034 registros frente a los 60.000 que tiene en realidad. Por otro lado, al activar el muestreo dinámico, el optimizador da un valor mucho más cercano a la realidad, 64.222 registros.&lt;br /&gt;&lt;br /&gt;La inexactitud se hace todavía más patente si borramos totalmente el contenido de la tabla &lt;i&gt;mitabla&lt;/i&gt;. En este caso, el plan de ejecución si deshabilitamos el muestreo dinámico seguirá devolviéndonos el valor de 15.034 registros, mientras que el plan de ejecución con el &lt;i&gt;dynamic sampling&lt;/i&gt; habilitado no dirá que el número de registros a procesar es tan sólo 1. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;El muestreo dinámico es más eficaz&lt;/b&gt; cuando en una consulta estamos utilizando una tabla que ha sido creada y poblada con datos, pero que todavía no ha sido analizada mediante el paquete estándar PLSQL &lt;i&gt;DBMS_STATS&lt;/i&gt;, paquete que se utiliza para generar los datos estadísticos de las tablas. Hay que considerar que desde la primera release de la base de datos &lt;b&gt;Oracle 10g&lt;/b&gt;, ya sólo esta soportado el optimizador basado en costes, y que dicho optimizador necesita datos estadísticos exactos para funcionar correctamente. Por lo tanto, si una tabla existe y sus estadísticas no han sido recogidas todavía, el muestreo dinámico ayuda sobremanera a que el optimizador genere planes de ejecución adecuados.&lt;br /&gt;&lt;br /&gt;Por último terminaré indicando que la funcionalidad de muestreo dinámico ofrece &lt;b&gt;once niveles de &lt;i&gt;dynamic sampling&lt;/i&gt;&lt;/b&gt;, de 0 (deshabilitado) a 10. En la release 2 de &lt;b&gt;Oracle 9i&lt;/b&gt; el valor por defecto es 1, mientras que en la release 2 de &lt;b&gt;Oracle 10g&lt;/b&gt; y superiores, el valor por defecto es 2. Si queréis informaros acerca del significado e implicaciones que supone utilizar un nivel de muestreo dinámico u otro, os emplazo a que visitéis este enlace: &lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14211/stats.htm#i43032" target="_blank"&gt;Dynamic Sampling Levels&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Artículos relacionados&lt;/b&gt;: &lt;br /&gt;&lt;a href="http://www.plsql.biz/2006/12/hints-en-plsql-para-el-modo-de.html"&gt;Hints PLSQL para el modo de optimización&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://www.plsql.biz/2007/05/cmo-obtener-el-plan-de-ejecucin-de-una.html"&gt;Planes de ejecución Oracle&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-3207262870184619556?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/RSWeJkgWBlw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/3207262870184619556/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=3207262870184619556&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3207262870184619556?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3207262870184619556?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/RSWeJkgWBlw/la-funcionalidad-de-muestreo-dinamico-o.html" title="La funcionalidad de muestreo dinámico o Dynamic Sampling" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_ObxKtfPuuSQ/SdD0bR-h7-I/AAAAAAAAEXs/rOQsCcyxIY8/s72-c/PLSQL-dynamic-sampling-tetris.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.plsql.biz/2009/03/la-funcionalidad-de-muestreo-dinamico-o.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMDR3YzfSp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-7635883838807495787</id><published>2009-02-24T18:54:00.014+01:00</published><updated>2009-04-03T12:27:56.885+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:27:56.885+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Librerías estándar PLSQL" /><title>El paquete estándar DBMS_LOCK para sincronizar procesos</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/KBAUpDhw9BATyKYvdA6JKUCLgCk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/KBAUpDhw9BATyKYvdA6JKUCLgCk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/KBAUpDhw9BATyKYvdA6JKUCLgCk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/KBAUpDhw9BATyKYvdA6JKUCLgCk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_ObxKtfPuuSQ/SaQ026zJI7I/AAAAAAAAEQo/bUTUKzmuiXE/s1600-h/DBMS_LOCK-PLSQL.jpg" target="_blank"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 187px; height: 200px;" src="http://3.bp.blogspot.com/_ObxKtfPuuSQ/SaQ026zJI7I/AAAAAAAAEQo/bUTUKzmuiXE/s200/DBMS_LOCK-PLSQL.jpg" border="0" alt="Paquete estandar Oracle PL/SQL DBMS_LOCK" id="BLOGGER_PHOTO_ID_5306424379050632114" /&gt;&lt;/a&gt;Hace unas semanas alguien me preguntó como se podía determinar si un procedimiento PL/SQL (&lt;i&gt;procedure PLSQL&lt;/i&gt;) estaba siendo ejecutado para evitar tener dos instancias del mismo proceso corriendo simultáneamente. Para manejar este tipo de situaciones, así como situaciones en las que queramos ejecutar procedimientos y funciones de forma secuencial y sincronizar diferentes procesos, Oracle dispone de el paquete de funciones y procedimientos &lt;b&gt;DBMS_LOCK&lt;/b&gt;.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Con el paquete &lt;b&gt;DBMS_LOCK&lt;/b&gt; podemos establecer bloqueos de usuario (&lt;i&gt;PL/SQL User Locks (UL)&lt;/i&gt;) con los que podremos parar la ejecución de un procedure si al ejecutarlo nos encontramos con que un determinado bloqueo o &lt;i&gt;lock&lt;/i&gt; está en proceso.&lt;br /&gt;&lt;br /&gt;A continuación os describo los tres procedimientos y funciones del paquete &lt;b&gt;DBMS_LOCK&lt;/b&gt; más importantes:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;DBMS_LOCK.allocate_unique (nombre_lock, id_lock, expira_seg)&lt;/b&gt;: Este procedimiento lo que hace es asignar un identificador único (&lt;i&gt;id_lock&lt;/i&gt;) para el nombre de bloqueo (&lt;i&gt;nombre_lock&lt;/i&gt;) que hemos utilizado como parámetro de entrada. Por lo tanto, &lt;i&gt;id_lock&lt;/i&gt; se trata de un parámetro de salida, y los valores que puede tomar están comprendidos en el rango de 1073741824 a 1999999999. Por otro lado, el parámetro &lt;i&gt;expira_seg&lt;/i&gt; es opcional e indica el número de segundos que se mantendrá el bloqueo una vez que haya sido solicitado, si no se especifica, el valor por defecto es de 864000 segundos (10 días).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;DBMS_LOCK.request (id_lock, tipo_lock, timeout, unlock_on)&lt;/b&gt;: Con esta función lo que hacemos es solicitar el establecimiento de un bloqueo de usuario.&lt;br /&gt;&lt;br /&gt;Veamos el uso de los distintos parámetros:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;&lt;i&gt;id_lock&lt;/i&gt;&lt;/b&gt;: Valor del identificador único generado anteriormente mediante la ejecución del procedimiento &lt;i&gt;DBMS_LOCK.allocate_unique&lt;/i&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;&lt;i&gt;tipo_lock&lt;/i&gt;&lt;/b&gt;: Permite definir el tipo de bloqueo que queremos establecer. Puede tomar los siguientes valores: 2 - Row Share (SS), 3 - Row Exclusive (SX), 4 - Share (S), 5 - Share Row Exclusive (SSX) y 6 - Exclusive (X). En la mayoría de los casos utilizaremos el valor de 6 para establecer un bloqueo exclusivo.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;&lt;i&gt;timeout&lt;/i&gt;&lt;/b&gt;: Número de segundos que queremos permanecer esperando a que se libere el bloqueo si nos encontramos con que otro idéntico ha sido solicitado anteriormente y está todavía en marcha. Si no especificamos ningún valor, la función permanecerá en espera hasta que el bloqueo se libere (el valor por defecto que toma la variable &lt;i&gt;timeout&lt;/i&gt; es muy alto).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;&lt;i&gt;unlock_on&lt;/i&gt;&lt;/b&gt;: Indica si el bloqueo se debe liberar al ejecutar los comandos &lt;i&gt;COMMIT&lt;/i&gt; o &lt;i&gt;ROLLBACK&lt;/i&gt; (valor &lt;i&gt;TRUE&lt;/i&gt;) o si debe mantenerse aunque ejecutemos alguno de los comandos mencionados (valor &lt;i&gt;FALSE&lt;/i&gt;). El valor por defecto es &lt;i&gt;FALSE&lt;/i&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Los valores que puede devolver esta función son los siguientes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;0&lt;/b&gt;: Bloqueo establecido satisfactoriamente.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;1&lt;/b&gt;: El bloqueo no se pudo establecer por superarse el tiempo de espera (&lt;i&gt;timeout&lt;/i&gt;).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;2&lt;/b&gt;: Bloqueo multiple (&lt;i&gt;deadlock&lt;/i&gt;). Aparece cuando se ha excedido el límite de peticiones de bloqueo permitidas sobre el mismo identificador.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;3&lt;/b&gt;: Parámetro erróneo o inválido.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;4&lt;/b&gt;: Aparece cuando en una misma sesión se solicita el mismo bloqueo por segunda vez sin haber liberado la primera petición.&lt;/li&gt; &lt;br /&gt;&lt;li&gt;&lt;b&gt;5&lt;/b&gt;: Identificador de &lt;i&gt;lock&lt;/i&gt; ilegal.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;DBMS_LOCK.release (id_lock)&lt;/b&gt;: Esta función se utiliza para liberar un bloqueo previamente establecido con la función &lt;i&gt;DBMS_LOCK.request&lt;/i&gt;. La función puede devolver los valores 0, 3, 4 ó 5. El significado de dichos valores es exactamente el mismo que en la función &lt;i&gt;DBMS_LOCK.request&lt;/i&gt; salvo el valor 4, que en este caso indica que no somos los dueños del &lt;i&gt;lock&lt;/i&gt; y no podemos liberarlo.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ejemplo práctico del uso del paquete DBMS_LOCK&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;En una primera sesión ejecutamos: &lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;DECLARE&lt;br /&gt;  v_lockhandle VARCHAR2(200);&lt;br /&gt;BEGIN&lt;br /&gt;  DBMS_LOCK.allocate_unique&lt;br /&gt;    ('Mi_Procedure', v_lockhandle);&lt;br /&gt;  DBMS_OUTPUT.put_line&lt;br /&gt;    ('id_lock = '||v_lockhandle);&lt;br /&gt;  DBMS_OUTPUT.put_line&lt;br /&gt;    ('request status = ' ||&lt;br /&gt;     DBMS_LOCK.request(v_lockhandle, 6));&lt;br /&gt;END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;La salida por pantalla tras la ejecución del processo sería:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;id_lock = 1074484814107448481486&lt;br /&gt;request status = 0&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;A continuación en la tabla &lt;i&gt;V$LOCK&lt;/i&gt; podemos identificar el bloqueo en curso de la siguiente manera (ojo, sólo hay que utilizar los diez primeros caracteres del &lt;i&gt;id_lock&lt;/i&gt;):&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT sid, type, id1, lmode, &lt;br /&gt;       request, ctime, block&lt;br /&gt;FROM   v$lock &lt;br /&gt;WHERE  id1 = '1074484814'&lt;br /&gt;&lt;br /&gt;SID TYPE ID1        LMODE REQUEST CTIME BLOCK&lt;br /&gt;117 UL   1074484814     6       0   201     0&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Si ejecutamos el mismo proceso sobre una segunda sesión, observaremos que dicho proceso se queda esperando a que el bloqueo establecido en la primera sesión se libere. En la tabla &lt;i&gt;V$LOCK&lt;/i&gt; veremos que aparece una nueva línea en espera de establecer un bloqueo exclusivo (lmode = 0, request = 6):&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SID TYPE ID1        LMODE REQUEST CTIME BLOCK&lt;br /&gt;117 UL   1074484814     6       0   790     1&lt;br /&gt; 97 UL   1074484814     0       6     9     0&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Ahora si abrimos una tercera sesión y ejecutamos: &lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;DECLARE&lt;br /&gt;  v_lockhandle VARCHAR2(200);&lt;br /&gt;BEGIN&lt;br /&gt;  DBMS_LOCK.allocate_unique&lt;br /&gt;    ('Mi_Procedure', v_lockhandle);&lt;br /&gt;  DBMS_OUTPUT.put_line&lt;br /&gt;    ('id_lock = '||v_lockhandle);&lt;br /&gt;  DBMS_OUTPUT.put_line&lt;br /&gt;    ('request status = ' ||&lt;br /&gt;    DBMS_LOCK.request(v_lockhandle, 6, 20));&lt;br /&gt;END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;El proceso se quedará en espera durante 20 segundos (observad que he utilizado el parámetro de &lt;i&gt;timeout&lt;/i&gt; con el valor de 20). Transcurrido este tiempo el proceso terminará y obtendremos la siguiente salida:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;id_lock = 1074484814107448481486&lt;br /&gt;request status = 1&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Es decir, el proceso &lt;i&gt;DBMS_LOCK.request&lt;/i&gt; devuelve el valor "1" indicando que el establecimiento del bloqueo falló por &lt;i&gt;timeout&lt;/i&gt;. Por otro lado, durante esos 20 segundos de espera, aparecerá una tercera línea en la tabla &lt;i&gt;V$LOCK&lt;/i&gt; (lmode = 0, request = 6), que no considero necesario mostrar.&lt;br /&gt;&lt;br /&gt;Ahora, si en la primera sesión liberamos el bloqueo ejecutando:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;DECLARE&lt;br /&gt;  v_lockhandle VARCHAR2(200) := '1074484814';&lt;br /&gt;BEGIN&lt;br /&gt;  DBMS_LOCK.release(v_lockhandle);&lt;br /&gt;END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Entonces observaremos que, al liberarse el bloqueo, la ejecución del proceso que estaba en espera en la segunda sesión termina. En la tabla &lt;i&gt;V$LOCK&lt;/i&gt; nos encontraremos con lo siguiente:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SID TYPE ID1        LMODE REQUEST CTIME BLOCK&lt;br /&gt; 97 UL   1074484814     6       0     3     0&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Es decir, ahora el bloqueo exclusivo (lmode = 6) está establecido por la segunda sesión.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Artículos relacionados&lt;/span&gt;: &lt;br /&gt;&lt;a href="http://www.plsql.biz/2008/08/paquete-dbmssql-para-utilizar-sql.html"&gt;El paquete estándar PLSQL DBMS_SQL&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://www.plsql.biz/2006/10/como-mostrar-mensajes-de-texto-en.html"&gt;El paquete estándar PLSQL DBMS_OUTPUT&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-7635883838807495787?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/SyQUY2AbGl4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/7635883838807495787/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=7635883838807495787&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7635883838807495787?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7635883838807495787?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/SyQUY2AbGl4/el-paquete-estandar-dbmslock-para.html" title="El paquete estándar DBMS_LOCK para sincronizar procesos" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_ObxKtfPuuSQ/SaQ026zJI7I/AAAAAAAAEQo/bUTUKzmuiXE/s72-c/DBMS_LOCK-PLSQL.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.plsql.biz/2009/02/el-paquete-estandar-dbmslock-para.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMCQng4fCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-7613208721789557809</id><published>2009-01-26T08:08:00.006+01:00</published><updated>2009-04-03T12:27:43.634+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:27:43.634+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Tablas Oracle: Claves naturales o claves sustitutivas</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Xb-vwutIKAXYIdsbx-Zxh26SgLg/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Xb-vwutIKAXYIdsbx-Zxh26SgLg/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Xb-vwutIKAXYIdsbx-Zxh26SgLg/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Xb-vwutIKAXYIdsbx-Zxh26SgLg/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ObxKtfPuuSQ/SX3zZ889dHI/AAAAAAAAEK8/IOiyBdzuLns/s1600-h/claves-tablas-oracle.JPG"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 197px; height: 195px;" src="http://4.bp.blogspot.com/_ObxKtfPuuSQ/SX3zZ889dHI/AAAAAAAAEK8/IOiyBdzuLns/s200/claves-tablas-oracle.JPG" border="0" alt="¿Usar claves naturales o sustitutivas con tablas Oracle?" id="BLOGGER_PHOTO_ID_5295656364041663602" /&gt;&lt;/a&gt;En algunas ocasiones me he encontrado con bases de datos en las que se aplica la norma de, a la hora de diseñar una tabla nueva, utilizar siempre una &lt;span style="font-weight: bold;"&gt;clave sustitutiva&lt;/span&gt; (&lt;i&gt;surrogate key&lt;/i&gt;) , incluso existiendo una &lt;span style="font-weight: bold;"&gt;clave natural&lt;/span&gt; perfectamente aplicable. Cuando he preguntado por el motivo de crear tales claves sustitutivas, la razón ha sido casi siempre la de aumentar la eficiencia de la base de datos eliminando la posibilidad de tener que enlazar dos tablas utilizando más de una columna.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Primero aclararé, para aquellos que no lo tengan claro, el &lt;span style="font-weight: bold;"&gt;concepto de clave sustitutiva o surrogate key&lt;/span&gt;. Una clave sustitutiva no es más que una clave interna, un identificador único, que no tiene significado para el negocio, y que identifica de forma única un registro de una tabla de la base de datos.&lt;br /&gt;&lt;br /&gt;En mi opinión, si al definir una tabla disponemos de una clave natural, que obviamente debe ser única e inmutable, debemos utilizarla en detrimento de la clave sustitutiva. El argumento de que la utilización de claves sustitutivas va a mejorar el rendimiento de la base de datos es erróneo, y las claves naturales hacen más sencilla la escritura de nuestras consultas SQL o del código PL/SQL.&lt;br /&gt;&lt;br /&gt;Por &lt;span style="font-weight: bold;"&gt;ejemplo&lt;/span&gt;, si disponemos de un par de tablas, la tabla &lt;i&gt;PRODUCTOS&lt;/i&gt; y la tabla &lt;span style="font-style:italic;"&gt;VERSION_PRODUCTOS&lt;/span&gt;, lo lógico sería utilizar para la primera tabla un campo &lt;i&gt;producto_id&lt;/i&gt; como clave primaria (&lt;i&gt;primary key&lt;/i&gt;) , campo que podría tratarse de una clave sustitutiva poblada por una secuencia, y usar como clave primaria de la segunda tabla una combinación de &lt;i&gt;producto_id&lt;/i&gt; y de &lt;i&gt;numero_version&lt;/i&gt;. Las tablas podrían definirse como sigue:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE TABLE productos&lt;br /&gt;( producto_id NUMBER PRIMARY KEY,&lt;br /&gt;  otros_datos... );&lt;br /&gt;&lt;br /&gt;CREATE TABLE version_productos&lt;br /&gt;( producto_id REFERENCES productos,&lt;br /&gt;  numero_version NUMBER,&lt;br /&gt;  otros_datos...&lt;br /&gt;  CONSTRAINT ver_pro_pk PRIMARY KEY &lt;br /&gt;  (producto_id, numero_version));&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En nuestro ejemplo, el uso de una clave sustitutiva añadiría un trabajo extra a cualquier operación de INSERT que debería generar la clave sustitutiva, además de hacer lo propio para la clave natural. Mi opinión es que si la &lt;span style="font-weight: bold;"&gt;clave natural&lt;/span&gt; es &lt;span style="font-weight: bold;"&gt;inmutable y razonable&lt;/span&gt;, entonces debemos utilizarla. En este caso concreto, con razonable quiero significar que la clave no necesite utilizar quince campos para ser generada, sino que sólo hagan falta de dos a cinco columnas.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-7613208721789557809?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/MhfVSwOvAcI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/7613208721789557809/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=7613208721789557809&amp;isPopup=true" title="1 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7613208721789557809?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7613208721789557809?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/MhfVSwOvAcI/tablas-oracle-claves-naturales-o-claves.html" title="Tablas Oracle: Claves naturales o claves sustitutivas" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_ObxKtfPuuSQ/SX3zZ889dHI/AAAAAAAAEK8/IOiyBdzuLns/s72-c/claves-tablas-oracle.JPG" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://www.plsql.biz/2009/01/tablas-oracle-claves-naturales-o-claves.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMAQnk4eip7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-3199546674015103071</id><published>2008-12-15T18:15:00.007+01:00</published><updated>2009-04-03T12:27:23.732+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:27:23.732+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Problemas con los triggers SQL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/0sQpcmVnit3SrYGsjNT5Wm3eHgw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/0sQpcmVnit3SrYGsjNT5Wm3eHgw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/0sQpcmVnit3SrYGsjNT5Wm3eHgw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/0sQpcmVnit3SrYGsjNT5Wm3eHgw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_ObxKtfPuuSQ/SUaZajXolqI/AAAAAAAAD9Y/8wcAVPN67XQ/s1600-h/programador-PL-SQL.gif" target="_blank"&gt;&lt;img style="float:right; margin:0 0 10px 10px; cursor:pointer; cursor:hand;width: 170px; height: 190px;" src="http://3.bp.blogspot.com/_ObxKtfPuuSQ/SUaZajXolqI/AAAAAAAAD9Y/8wcAVPN67XQ/s200/programador-PL-SQL.gif" border="0" alt="Programador PL/SQL en la cama" id="BLOGGER_PHOTO_ID_5280076294588503714" /&gt;&lt;/a&gt;Mucha gente piensa que los &lt;a href="http://www.plsql.biz/2007/02/triggers-en-plsql.html"&gt;&lt;span style="font-weight:bold;"&gt;triggers PL/SQL&lt;/span&gt;&lt;/a&gt; son una de las más potentes herramientas de las bases de datos Oracle. De hecho lo son, pero existen dos razones fundamentales por las que, personalmente, trato de evitar la utilización de triggers a la hora de implementar mis proyectos en PL/SQL.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;b&gt;Problemas de mantenimiento&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;A largo plazo, la utilización de triggers suele causar grandes &lt;a href="http://www.chungas.es/" target="_blank"&gt;dolores de cabeza&lt;/a&gt; a la hora de pensar en el mantenimiento. Al ser piezas del código que sólo ocurren como consecuencia de que se ha realizado otra operación, es muy frecuente que la gente se olvide de que los triggers están allí, y revisar el código pensando en todas las piezas de la base de datos que pueden afectarle, se hace poco menos que imposible.&lt;br /&gt;&lt;br /&gt;Este problema es muy sencillo de comprender. Una persona experta en PL/SQL que no conoce una aplicación realizada en código PL/SQL y SQL, puede comprender fácilmente lo que realiza un &lt;a href="http://www.plsql.biz/2007/03/procedimientos-y-funciones-en-plsql.html"&gt;procedimiento almacenado&lt;/a&gt;, pero si nuestro sistema utiliza triggers de manera rutinaria, entonces ese procedimiento puede desencadenar centenares de otro tipo de procesos y cambios en la base de datos que no son detectables a simple vista.&lt;br /&gt;&lt;br /&gt;En conclusión, &lt;b&gt;los triggers hacen que la comprensión de un sistema sea complicada&lt;/b&gt;, causan que su mantenimiento sea más difícil de lo normal y crean confusión al permanecer ocultos en el esquema la base de datos. Mucha gente considera que los triggers son como sentencias DDL y que, al igual que no hay necesidad de revisar una sentencia &lt;i&gt;CREATE TABLE&lt;/i&gt;, tampoco hay necesidad de revisar el código de un trigger PL/SQL. En realidad, los triggers son subrutinas que son llamadas una y otra vez, cuyo código debe revisarse de igual manera que se revisan paquetes y procedimientos almacenados.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Uso incorrecto de los triggers&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;En muchos casos de los que yo he analizado, los triggers PL/SQL se han implementado de forma incorrecta. El código de los triggers suele incluir &lt;b&gt;enormes errores&lt;/b&gt; que el programador no fue capaz de prever o anticipar. Veamos algunos ejemplos de este tipo de implementaciones incorrectas. &lt;br /&gt;&lt;br /&gt;En alguna ocasión me he encontrado con triggers que realizan operaciones como esta:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;:new.nombre_completo := &lt;br /&gt;:new.nombre||' '||:new.apellidos;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Lo primero que se me viene a la cabeza al ver una línea de código como la de arriba, es que la columna &lt;i&gt;nombre_completo&lt;/i&gt; debería ser una columna de una vista (&lt;i&gt;view&lt;/i&gt;) o, en Oracle 11g, una columna virtual. La columna &lt;i&gt;nombre_completo&lt;/i&gt; es el resultado de una función trivial, con casi ningún coste de procesamiento y que, de ser necesario, podría hasta ser indexada. &lt;b&gt;No existe ninguna razón para almacenar el resultado de la función en la tabla física&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Además, imaginemos que por alguna razón queremos cambiar el valor de la columna &lt;i&gt;nombre_completo&lt;/i&gt; para algunos registros, simplemente el trigger no nos dejará hacerlo y, si no conocemos la existencia del mismo, nos preguntaremos una y mil veces por qué el campo no se actualiza al ejecutar el comando &lt;i&gt;UPDATE&lt;/i&gt;. El trigger creará, cuando menos, confusión, y, con suerte, nos daremos cuenta de su existencia y de que debemos deshabilitarlo para poder realizar la deseada actualización del campo.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Otro de los usos incorrectos de los triggers es utilizarlos para enviar emails&lt;/b&gt; a los usuarios cuando se inserta o se actualiza un registro de una tabla. Veamos el siguiente trigger:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; CREATE TRIGGER enviar_correo&lt;br /&gt; 2   AFTER INSERT ON pedidos&lt;br /&gt; 3   FOR EACH ROW&lt;br /&gt; 4   BEGIN&lt;br /&gt; 5   UTL_MAIL.send&lt;br /&gt; 6   (sender=&gt;'app@miempresa.com',&lt;br /&gt; 7   recipients=&gt;'pedidos@miempresa.com',&lt;br /&gt; 8   subject=&gt;'Nuevo pedido '||:new.num_pedido,&lt;br /&gt; 9   message=&gt;'Nuevo pedido recibido');&lt;br /&gt; 10  END;&lt;br /&gt; 11  /&lt;br /&gt;&lt;br /&gt;Trigger created.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;El trigger es sintácticamente correcto, compilará sin problemas y, si se inserta un nuevo pedido en la tabla "pedidos", nuestros usuarios recibirán un email indicándoselo. Sin embargo, el trigger tiene un grave problema. Alguien se ha preguntado qué ocurrirá si insertamos cien registros en la mencionada tabla y, por cualquier razón, decidimos deshacer las inserciones ejecutando el comando &lt;i&gt;ROLLBACK&lt;/i&gt;. Pues simplemente lo que ocurrirá es que habremos enviado cien emails a los usuarios indicándoles que existen cien nuevos pedidos que en realidad no existen.&lt;br /&gt;&lt;br /&gt;En este sentido conviene señalar que, para que este tipo de situaciones ocurran, no es necesario que uno mismo invoque el &lt;i&gt;ROLLBACK&lt;/i&gt;, sino que continuamente la base de datos Oracle deshace operaciones &lt;i&gt;INSERT&lt;/i&gt;, &lt;i&gt;UPDATE&lt;/i&gt;, &lt;i&gt;MERGE&lt;/i&gt; o &lt;i&gt;DELETE&lt;/i&gt;, sin necesidad de que uno se lo indique personalmente.&lt;br /&gt;&lt;br /&gt;Podemos decir que prácticamente cualquier trigger PL/SQL que llame a funciones &lt;i&gt;UTL_&lt;/i&gt; (como &lt;i&gt;UTL_FILE, UTL_HTTP, UTL_MAIL, UTL_SMTP, UTL_TCP,&lt;/i&gt; etcétera) estará incorrectamente implementado. En conclusión, &lt;b&gt;con los triggers no se debe realizar ninguna operación que no pueda ser deshecha con posterioridad&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Los triggers también pueden causar problemas cuando los programadores no entienden debidamente una de las características más interesantes de las bases de datos Oracle, el hecho de que las lecturas no bloquean las escrituras, y de que las escrituras no bloquean las lecturas. Esto se torna especialmente crítico cuando los triggers se utilizan para forzar que se cumpla alguna regla determinada. Todo esto será tema de otro artículo posterior.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-3199546674015103071?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/ycz39sba46Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/3199546674015103071/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=3199546674015103071&amp;isPopup=true" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3199546674015103071?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3199546674015103071?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/ycz39sba46Q/problemas-con-los-triggers-sql.html" title="Problemas con los triggers SQL" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_ObxKtfPuuSQ/SUaZajXolqI/AAAAAAAAD9Y/8wcAVPN67XQ/s72-c/programador-PL-SQL.gif" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://www.plsql.biz/2008/12/problemas-con-los-triggers-sql.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMHQ3g_cSp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-3537758855461023029</id><published>2008-11-04T18:26:00.008+01:00</published><updated>2009-04-03T12:27:12.649+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:27:12.649+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Acceso remoto mediante DBLINK a una base de datos Oracle</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/hxXdccWW5zadA9M1V02pjTQQ0tw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/hxXdccWW5zadA9M1V02pjTQQ0tw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/hxXdccWW5zadA9M1V02pjTQQ0tw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/hxXdccWW5zadA9M1V02pjTQQ0tw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ObxKtfPuuSQ/SRCJZ-9_nDI/AAAAAAAAD0M/ioOaMt7OuXQ/s1600-h/PLSQL-dormir-contador-ovejas.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 146px;" src="http://1.bp.blogspot.com/_ObxKtfPuuSQ/SRCJZ-9_nDI/AAAAAAAAD0M/ioOaMt7OuXQ/s200/PLSQL-dormir-contador-ovejas.jpg" border="0" alt="Accesso remoto a una base de datos Oracle en PL/SQL y SQL" id="BLOGGER_PHOTO_ID_5264859043888864306" /&gt;&lt;/a&gt;La manera más sencilla de acceder desde una base de datos Oracle a tablas y vistas (&lt;i&gt;views&lt;/i&gt;) de otra base de datos Oracle es mediante el uso de un &lt;span style="font-weight:bold;"&gt;DBLink&lt;/span&gt; (enlace a base de datos). No obstante, en muchos casos &lt;span style="font-weight:bold;"&gt;puede no ser recomendable la utilización de DBLinks&lt;/span&gt;, ya que el acceso a tablas y vistas remotas puede provocar importantes problemas de rendimiento en ambas bases de datos, tanto la remota como la local. En la mayoría de los casos estos problemas de rendimiento se deben a la imposibilidad de utilizar los índices de las tablas remotas.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Los DBLinks se crean en la base de datos local utilizando el comando PL/SQL o SQL &lt;span style="font-weight:bold;"&gt;&lt;span style="font-style:italic;"&gt;CREATE DATABASE LINK&lt;/span&gt;&lt;/span&gt;. Obviamente, el usuario que ejecute dicho comando debe tener los permisos necesarios para poder hacerlo. La &lt;span style="font-weight:bold;"&gt;sintaxis&lt;/span&gt; del comando SQL mencionado es como sigue:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE [PUBLIC] DATABASE LINK Nombre_dblink&lt;br /&gt;  CONNECT TO Nombre_usuario INDENTIFIED BY Contraseña&lt;br /&gt;  USING 'Cadena_conexion';&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En donde:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Nombre_dblink&lt;/b&gt; es el nombre del DBLink.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Nombre_usuario&lt;/b&gt; y &lt;b&gt;Contraseña&lt;/b&gt; son los identificadores que utilizará el DBLink para conectarse a la base de datos remota. Por lo tanto, el acceso a las tablas y vistas de la base de datos remota se hará con los permisos que dicho usuario tenga sobre ellas en dicha base de datos. Si se omiten estos valores, entonces el acceso a la base de datos remota se hará con el mismo nombre de usuario y contraseña con el que el usuario en cuestión esté conectado a la base de datos local.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Cadena_conexion&lt;/b&gt; identifica a la base de datos remota. Puede ser el &lt;b&gt;nombre de la instancia&lt;/b&gt;, si ésta está definida en el &lt;span style="font-weight:bold;"&gt;archivo tnsnames.ora&lt;/span&gt; de la base de datos origen, o también, para la versión 8 de la base de datos Oracle o posteriores, podemos utilizar directamente la &lt;b&gt;definición TNS&lt;/b&gt; (esto posibilita que no tengamos que modificar el fichero tnsnames.ora).&lt;br /&gt;&lt;br /&gt;Por otro lado, si definimos el DBLink como &lt;span style="font-weight:bold;"&gt;&lt;span style="font-style:italic;"&gt;PUBLIC&lt;/span&gt;&lt;/span&gt;, todos los usuarios tendrán acceso al mismo.&lt;br /&gt;&lt;br /&gt;A continuación os pongo algunos ejemplos del comando &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;CREATE DATABASE LINK&lt;/span&gt;&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE DATABASE LINK dbl_bd001&lt;br /&gt;  CONNECT TO repadmin IDENTIFIED BY reppass1&lt;br /&gt;  USING 'bd001';&lt;br /&gt;&lt;br /&gt;CREATE PUBLIC DATABASE LINK dbl_bd001&lt;br /&gt;  CONNECT TO repadmin IDENTIFIED BY reppass1&lt;br /&gt;  USING '(description = &lt;br /&gt;           (address = &lt;br /&gt;             (protocol = tcp)&lt;br /&gt;             (host = bd.miempresa.com)&lt;br /&gt;             (Port = 1521) )&lt;br /&gt;           (connect_data = &lt;br /&gt;             (sid = bd001) )&lt;br /&gt;         )';&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Una vez creado el DBLink, para &lt;span style="font-weight:bold;"&gt;referenciar una tabla o vista&lt;/span&gt; de la base de datos remota se ha de indicar el nombre de la tabla o vista, concatenado con el carácter "@" y el nombre que se le ha dado al DBLink. Las tablas y vistas remotas podrán usarse en consultas (sentencias &lt;i&gt;SELECT&lt;/i&gt;) y, si la base de datos Oracle utiliza el sistema de bases de datos distribuidas, entonces se podrán ejecutar también sentencias SQL o PLSQL del tipo &lt;i&gt;DELETE&lt;/i&gt;, &lt;i&gt;INSERT&lt;/i&gt;, &lt;i&gt;UPDATE&lt;/i&gt; o &lt;i&gt;LOCK TABLE&lt;/i&gt;. Asimismo, se pueden crear sinónimos para abreviar la forma de referenciar a las tablas y vistas remotas. A continuación os pongo algunos ejemplos de sentencias:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT * FROM Nombre_tabla_remota@Nombre_dblink;&lt;br /&gt;&lt;br /&gt;INSERT INTO Nombre_tabla_remota@Nombre_dblink (campo1)&lt;br /&gt;  VALUES ('prueba');&lt;br /&gt;&lt;br /&gt;UPDATE Nombre_tabla_remota@Nombre_dblink;&lt;br /&gt;  SET campo1 = 'actualizado'&lt;br /&gt;  WHERE campo1 = 'prueba';&lt;br /&gt;&lt;br /&gt;CREATE SYNONYM tremota&lt;br /&gt;  FOR Nombre_tabla_remota@Nombre_dblink;&lt;br /&gt;&lt;br /&gt;SELECT * FROM tremota;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;Nota&lt;/b&gt;: Existe otro concepto que no he explicado aquí y es el de los DBLinks compartidos (comando CREATE SHARED DATABASE LINK), el que necesite más información sobre este concepto puede mirar en el manual de Oracle sobre sistemas de base de datos distribuidos (&lt;i&gt;Oracle Distributed Database Systems&lt;/i&gt;). Por otro lado, también es posible acceder mediante DBLink a bases de datos remotas que no sean Oracle. Para crear un DBLink de este tipo, y antes de que nadie me pregunte, podéis realizar una búsqueda en Internet de las palabras: "Oracle Heterogeneous Services". Quizá más adelante escriba algún artículo con referencia a estos dos temas.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-3537758855461023029?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/pw1I2TaU5cs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/3537758855461023029/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=3537758855461023029&amp;isPopup=true" title="6 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3537758855461023029?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3537758855461023029?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/pw1I2TaU5cs/acceso-remoto-mediante-dblink-una-base.html" title="Acceso remoto mediante DBLINK a una base de datos Oracle" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_ObxKtfPuuSQ/SRCJZ-9_nDI/AAAAAAAAD0M/ioOaMt7OuXQ/s72-c/PLSQL-dormir-contador-ovejas.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total><feedburner:origLink>http://www.plsql.biz/2008/11/acceso-remoto-mediante-dblink-una-base.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMER3k7fyp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-3825409546890574050</id><published>2008-09-26T00:38:00.009+02:00</published><updated>2009-04-03T12:26:46.707+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:26:46.707+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Librerías estándar PLSQL" /><title>Generador de números aleatorios en PL/SQL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Rne6jCSViqAuArjuKya5mLiMqp8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Rne6jCSViqAuArjuKya5mLiMqp8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Rne6jCSViqAuArjuKya5mLiMqp8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Rne6jCSViqAuArjuKya5mLiMqp8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_ObxKtfPuuSQ/SNwWEaTTUyI/AAAAAAAACtA/_i4bUxGMC08/s1600-h/PLSQL-numeros-aleatorios.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_ObxKtfPuuSQ/SNwWEaTTUyI/AAAAAAAACtA/_i4bUxGMC08/s200/PLSQL-numeros-aleatorios.jpg" border="0" alt="Seguridad en PLSQL" id="BLOGGER_PHOTO_ID_5250095530642723618" /&gt;&lt;/a&gt;Es posible que en alguna ocasión necesitéis utilizar un &lt;span style="font-weight:bold;"&gt;generador de números aleatorios&lt;/span&gt; en un programa PL/SQL. Oracle proporciona el &lt;span style="font-weight:bold;"&gt;paquete estándar DBMS_RANDOM&lt;/span&gt; para este propósito. Obviamente podemos escribir nuestra propia rutina PLSQL que genere números aleatorios, pero paquete estándar de Oracle DBMS_RANDOM es más rápido ya que llama al generador de números aleatorios interno de la base de datos Oracle. &lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Los procedimientos y funciones que incluye este paquete son:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;INITIALIZE&lt;/span&gt;: Inicializa el valor de la semilla del generador de números aleatorios. &lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 85%;"&gt;&lt;pre&gt;EXEC dbms_random.initialize(12345678);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;NORMAL&lt;/span&gt;: Devuelve números aleatorios siguiendo un distribución normal.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 85%;"&gt;&lt;pre&gt;SELECT dbms_random.normal&lt;br /&gt;FROM dual;&lt;br /&gt;-- Sólo valores positivos&lt;br /&gt;SELECT ABS(dbms_random.normal)&lt;br /&gt;FROM   dual;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;RANDOM&lt;/span&gt;: Genera números aleatorios.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 85%;"&gt;&lt;pre&gt;SET SERVEROUTPUT ON&lt;br /&gt;DECLARE&lt;br /&gt; aleator PLS_INTEGER;&lt;br /&gt; semilla NUMBER(20); &lt;br /&gt;BEGIN&lt;br /&gt; SELECT hsecs&lt;br /&gt; INTO   semilla&lt;br /&gt; FROM   gv$timer;&lt;br /&gt; dbms_random.initialize(semilla);&lt;br /&gt; FOR i IN 1..10&lt;br /&gt; LOOP&lt;br /&gt;   aleator := dbms_random.random;&lt;br /&gt;   dbms_output.put_line(aleator);&lt;br /&gt;   semilla := aleator; &lt;br /&gt; END LOOP;&lt;br /&gt; dbms_random.terminate;&lt;br /&gt;END;&lt;br /&gt;-- Genera números positivos entre 1 y 100&lt;br /&gt;SELECT (1+ABS(MOD(dbms_random.random,100)))&lt;br /&gt;FROM   dual;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;SEED&lt;/span&gt;: Reinicializa el valor de la semilla&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 85%;"&gt;&lt;pre&gt;EXEC dbms_random.seed(87654321);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;STRING&lt;/span&gt;: Crea una cadena de caracteres aleatoria. La definición de la función es como sigue, dbms_random.string(opcion IN CHAR, longitud IN NUMBER), donde opción puede tomar los valores:&lt;br /&gt;&lt;br /&gt;'a' o 'A': Genera una cadena aleatoria con sólo letras íncluyendo mayúsculas y minúsculas.&lt;br /&gt;'l' o 'L': Genera una cadena aleatoria con sólo letras minúsculas.&lt;br /&gt;'p' o 'P': Genera una cadena aleatoria con cualquier tipo de carácter imprimible.&lt;br /&gt;'u' o 'U': Genera una cadena aleatoria con sólo letras mayúsculas.&lt;br /&gt;'x' o 'X': Genera una cadena aleatoria con caracteres alfa numéricos en mayúsculas.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 85%;"&gt;&lt;pre&gt;-- Genera una cadena de 10 caracteres formada&lt;br /&gt;-- por letras mayúsculas y minúsculas&lt;br /&gt;SELECT dbms_random.string('A', 10)&lt;br /&gt;FROM   dual;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;TERMINATE&lt;/span&gt;: Indica que hemos terminado de usar el paquete DBMS_RANDOM.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 85%;"&gt;&lt;pre&gt;dbms_random.terminate;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;VALUE&lt;/span&gt;: Sin parámetros devuelve un valor aleatorio entre 0 y 1 con un número fijo de decimales, pero también puede usarse con parametros para que devuelva números aleatorios entre un rango de numeros.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 85%;"&gt;&lt;pre&gt;SELECT dbms_random.value&lt;br /&gt;FROM   dual;&lt;br /&gt;-- Develve números aleatorios entre 10 y 20&lt;br /&gt;SELECT dbms_random.value(10, 20)&lt;br /&gt;FROM dual;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;A continuación podéis ver un &lt;span style="font-weight:bold;"&gt;ejemplo de programa PL/SQL que genera números aleatorios&lt;/span&gt; entre 0 y 1 y que no utiliza el paquete DBMS_RANDOM&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 85%;"&gt;&lt;pre&gt;DECLARE&lt;br /&gt; semilla NUMBER;&lt;br /&gt;BEGIN&lt;br /&gt; semilla := EXP(TO_NUMBER(TO_CHAR(SYSDATE,'ss'))/59);&lt;br /&gt; FOR i IN 1..10&lt;br /&gt; LOOP&lt;br /&gt;   semilla := 1/(semilla - TRUNC(semilla));&lt;br /&gt;   semilla := semilla - TRUNC(semilla);&lt;br /&gt;   dbms_output.put_line(semilla);&lt;br /&gt; END LOOP;&lt;br /&gt;END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-3825409546890574050?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/tZqxT0zP6sU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/3825409546890574050/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=3825409546890574050&amp;isPopup=true" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3825409546890574050?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3825409546890574050?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/tZqxT0zP6sU/generador-de-nmeros-aleatorios-en-plsql.html" title="Generador de números aleatorios en PL/SQL" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_ObxKtfPuuSQ/SNwWEaTTUyI/AAAAAAAACtA/_i4bUxGMC08/s72-c/PLSQL-numeros-aleatorios.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://www.plsql.biz/2008/09/generador-de-nmeros-aleatorios-en-plsql.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkQNQXk8fCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-6881004673685523149</id><published>2008-08-18T08:30:00.004+02:00</published><updated>2009-04-03T12:26:30.774+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:26:30.774+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Librerías estándar PLSQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Utilidades PLSQL" /><title>Paquete DBMS_SQL para utilizar SQL dinámico (Dynamic SQL)</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/DkDuBQgzj2EixyChIUbMBSq2UsY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/DkDuBQgzj2EixyChIUbMBSq2UsY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/DkDuBQgzj2EixyChIUbMBSq2UsY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/DkDuBQgzj2EixyChIUbMBSq2UsY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ObxKtfPuuSQ/SKB9dw7KT_I/AAAAAAAACjg/s72sfvYkcJA/s1600-h/PLSQL-equipo-dise%C3%B1o-DBMS_SQL.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://4.bp.blogspot.com/_ObxKtfPuuSQ/SKB9dw7KT_I/AAAAAAAACjg/s72sfvYkcJA/s200/PLSQL-equipo-dise%C3%B1o-DBMS_SQL.jpg" alt="Oracle SQL dinámico o dynamic SQL con el paquete estándar DBMS_SQL" id="BLOGGER_PHOTO_ID_5233320717307236338" border="0" /&gt;&lt;/a&gt;El grupo de &lt;span style="font-weight: bold;"&gt;paquetes DBMS&lt;/span&gt; se trata de un conjunto de funciones y procedimientos que el PL/SQL de Oracle incorpora de forma estándar. Estos paquetes DBMS pueden ser de mucha utilidad cuando estamos programando en PLSQL. Hoy voy a hablar sobre &lt;span style="font-weight: bold;"&gt;el paquete DBMS_SQL que permite utilizar SQL dinámico&lt;/span&gt; en procedimientos almacenados y bloques PL/SQL.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Las &lt;span style="font-weight: bold;"&gt;sentencias de SQL dinámico&lt;/span&gt; tienen la característica de que no forman parte del código fuente PL/SQL, sino que &lt;span style="font-weight: bold;"&gt;están almacenadas dentro de cadenas de caracteres&lt;/span&gt; que, bien forman parte de los parámetros de entrada, o bien son construidas durante la ejecución del programa PLSQL. Esto posibilita la creación de programas de propósito mucho más general. Por ejemplo, se pueden crear procedimientos que operen sobre una tabla cuyo nombre no se conoce hasta el momento de la ejecución, ya que se trata de un parámetro de entrada de dicho procedimiento.&lt;br /&gt;&lt;br /&gt;Además, con el paquete DBMS_SQL &lt;span style="font-weight: bold;"&gt;se pueden ejecutar cualquier tipo de sentencias&lt;/span&gt;, tanto DML (&lt;i&gt;Data Manipulation Language&lt;/i&gt; - Lenguaje de manipulación de datos), como DDL (&lt;i&gt;Data Definition Language&lt;/i&gt; - Lenguaje de definición de datos). Por lo tanto, también permite directamente desde PL/SQL crear, modificar o borrar: tablas, índices, o cualquier objeto DDL (incluso procedimientos).&lt;br /&gt;&lt;br /&gt;En el siguiente ejemplo os muestro un procedimiento almacenado en el que, dependiendo de los parámetros de entrada, se actualiza, bien el sueldo (campo &lt;i&gt;salario&lt;/i&gt;), bien el departamento (campo &lt;i&gt;dpto&lt;/i&gt;), de un determinado empleado.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 85%;"&gt;&lt;pre&gt;CREATE OR REPLACE PROCEDURE upd_tabla_empleados&lt;br /&gt;(   p_campo  IN VARCHAR2&lt;br /&gt;  , p_emp_id IN VARCHAR2&lt;br /&gt;  , p_valor  IN VARCHAR2 ) IS&lt;br /&gt;&lt;br /&gt;  l_cursor        INTEGER;&lt;br /&gt;  -- Sentencia a ejecutar&lt;br /&gt;  l_sql           VARCHAR2(500);&lt;br /&gt;  -- Número de rows actualizadas&lt;br /&gt;  l_updated_rows  INTEGER;&lt;br /&gt;BEGIN&lt;br /&gt;&lt;br /&gt;  l_sql := &lt;br /&gt;  'UPDATE empleados SET ' || p_campo || ' = ' ||&lt;br /&gt;  p_valor || ' WHERE empleado_id = ' || p_emp_id;&lt;br /&gt;&lt;br /&gt;  l_cursor := dbms_sql.open_cursor;&lt;br /&gt;  dbms_sql.parse (l_cursor, l_sql, dbms_sql.native);&lt;br /&gt;  l_update_rows := dbms_sql.execute (l_cursor);&lt;br /&gt;  dbms_sql.close_cursor (l_cursor);&lt;br /&gt;&lt;br /&gt;END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Por lo tanto si ejecutamos el comando:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 85%;"&gt;&lt;pre&gt;SQL&gt; EXEC upd_tabla_empleados ('dpto', '1234', 'Compras');&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Estaremos asignando el empleado con ID 1234 al departamento de compras.&lt;br /&gt;&lt;br /&gt;Y si ejecutamos:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 85%;"&gt;&lt;pre&gt;SQL&gt; EXEC upd_tabla_empleados ('salario', '1122', '3000');&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Estaremos asignando al empleado con ID 1122 un salario de 3.000 euros.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-6881004673685523149?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/cYvnFTyg6nQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/6881004673685523149/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=6881004673685523149&amp;isPopup=true" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6881004673685523149?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6881004673685523149?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/cYvnFTyg6nQ/paquete-dbmssql-para-utilizar-sql.html" title="Paquete DBMS_SQL para utilizar SQL dinámico (Dynamic SQL)" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_ObxKtfPuuSQ/SKB9dw7KT_I/AAAAAAAACjg/s72sfvYkcJA/s72-c/PLSQL-equipo-dise%C3%B1o-DBMS_SQL.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://www.plsql.biz/2008/08/paquete-dbmssql-para-utilizar-sql.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkQBSHw4cCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-4059052115024885023</id><published>2008-06-30T08:30:00.001+02:00</published><updated>2009-04-03T12:25:59.238+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:25:59.238+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Integridad virtual con la base de datos Oracle 11g</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/QBrEKPAT2g1A5OEdouA4L28CL3I/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/QBrEKPAT2g1A5OEdouA4L28CL3I/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/QBrEKPAT2g1A5OEdouA4L28CL3I/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/QBrEKPAT2g1A5OEdouA4L28CL3I/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_ObxKtfPuuSQ/SFfhRZxKuzI/AAAAAAAACaU/JXyH8qe0WCI/s1600-h/PLSQL-base-de-datos-Oracle-11g-columnas-virtuales.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_ObxKtfPuuSQ/SFfhRZxKuzI/AAAAAAAACaU/JXyH8qe0WCI/s200/PLSQL-base-de-datos-Oracle-11g-columnas-virtuales.jpg" border="0" alt="Nuevas funcionalidades en Oracle 11g, las columnas virtuales" id="BLOGGER_PHOTO_ID_5212882782795774770" /&gt;&lt;/a&gt;Supongamos que nuestra base de datos Oracle dispone de una tabla en la que uno de los campos está formado realmente por muchos &lt;i&gt;subcampos&lt;/i&gt; concatenados. El campo en cuestión no puede modificarse pero necesitamos validar y forzar la integridad de uno de los &lt;i&gt;subcampos&lt;/i&gt;. Por ejemplo, queremos que los valores devueltos al ejecutar la función &lt;i&gt;SUBSTR(column,7,3)&lt;/i&gt; sobre dicho campo estén incluidos en una tabla de referencia o &lt;i&gt;lookup table&lt;/i&gt;.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Para versiones de la base de datos Oracle anteriores a la 11g no existía una forma sencilla de lidiar con este tipo de requerimientos ya que la validación de valores almacenados en una determinada columna, obligaba a que dichos valores estuvieran almacenados en una columna real que apuntase a la tabla de referencia o &lt;i&gt;lookup table&lt;/i&gt;. En nuestro problema no existe una columna real sino que estamos tratando con una función aplicada sobre una columna. En la versión de la &lt;b&gt;base de datos Oracle 10g y anteriores&lt;/b&gt;, lo más razonable sería crear una columna adicional en la tabla y, mediante el uso de un &lt;a href="http://www.plsql.biz/2007/02/triggers-en-plsql.html"&gt;trigger PL/SQL&lt;/a&gt;, insertar o actualizar el valor retornado por la función &lt;i&gt;SUBSTR&lt;/i&gt; en dicha nueva columna. A continuación podéis ver el código correspondiente a la implementación de esta solución:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL &gt; CREATE TABLE tabla_referencia&lt;br /&gt;  2   ( validacion VARCHAR2(3) PRIMARY KEY,&lt;br /&gt;  3     descripcion VARCHAR2(20) );&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL &gt; CREATE TABLE tabla_datos&lt;br /&gt;  2   ( concatenados VARCHAR2(20) PRIMARY KEY,&lt;br /&gt;  3     validacion VARCHAR2(3) NOT NULL&lt;br /&gt;  4     REFERENCES tabla_referencia );&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL &gt; CREATE TRIGGER tabla_datos&lt;br /&gt;  2   BEFORE INSERT OR UPDATE &lt;br /&gt;  3   OF concatenados ON tabla_datos&lt;br /&gt;  4   FOR EACH ROW&lt;br /&gt;  5   BEGIN&lt;br /&gt;  6     :new.validacion := &lt;br /&gt;  7       SUBSTR(:new.concatenados,7,3);&lt;br /&gt;  8   END;&lt;br /&gt;  9   /&lt;br /&gt;Trigger created.&lt;br /&gt;&lt;br /&gt;SQL &gt; INSERT INTO tabla_referencia (validacion)&lt;br /&gt;  2   VALUES ('XYZ');&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL &gt; INSERT INTO tabla_datos (concatenados)&lt;br /&gt;      VALUES ('123456XYZ012345');&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL &gt; INSERT INTO tabla_datos (concatenados)&lt;br /&gt;  2   VALUES ('123456ABC012345');&lt;br /&gt;INSERT INTO .....&lt;br /&gt;*&lt;br /&gt;ERROR at line 2:&lt;br /&gt;ORA-02291: Integrity constraint violated - &lt;br /&gt;parent key not found&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;La solución propuesta conlleva &lt;b&gt;un par de problemas&lt;/b&gt;. El primero se trata de un obvio problema de almacenamiento ya que estamos almacenando el mismo dato dos veces, una vez en el campo &lt;i&gt;concatenados&lt;/i&gt; y otra en el campo &lt;i&gt;validacion&lt;/i&gt;. El segundo problema viene dado por la necesidad de utilizar un &lt;i&gt;trigger PL/SQL&lt;/i&gt; y las implicaciones que este hecho puede conllevar sobre el rendimiento y los tiempos de ejecución de los comandos &lt;i&gt;INSERT&lt;/i&gt; y &lt;i&gt;UPDATE&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;El caso es que con la versión de la &lt;b&gt;base de datos Oracle 11g&lt;/b&gt; podemos forzar la integridad utilizando funciones aplicadas sobre una determinada columna; es decir, no estamos limitados a utilizar columnas reales, &lt;b&gt;podemos utilizar una columna virtual&lt;/b&gt;. Así pues, con Oracle 11g podemos prescindir del &lt;i&gt;trigger PL/SQL&lt;/i&gt;. Veamos a continuación como debemos crear nuestra tabla de datos para conseguir que esto ocurra:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL &gt; CREATE TABLE tabla_datos&lt;br /&gt;  2   ( concatenados VARCHAR2(20) PRIMARY KEY,&lt;br /&gt;  3     validacion AS &lt;br /&gt;  4       (SUBSTR(concatenados,7,3))&lt;br /&gt;  5     REFERENCES tabla_referencia );&lt;br /&gt;Table created.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En este caso hemos definido una &lt;b&gt;columna virtual&lt;/b&gt; con el nombre &lt;i&gt;concatenados&lt;/i&gt;, columna que es simplemente un metadato, que no se almacena de manera redundante, y que no requiere de un &lt;i&gt;trigger PL/SQL&lt;/i&gt; para su mantenimiento, pero que si que se puede utilizar para forzar la integridad de nuestra base de datos cruzando dicho valor con los valores almacenados en nuestra tabla de referencia o &lt;i&gt;lookup table&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Artículos relacionados&lt;/b&gt;: &lt;a href="http://www.plsql.biz/2007/10/oracle-lanza-la-base-de-datos-oracle.html"&gt;Oracle lanza la versión 11g de su base de datos&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-4059052115024885023?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/D9H0LC18ZMQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/4059052115024885023/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=4059052115024885023&amp;isPopup=true" title="1 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/4059052115024885023?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/4059052115024885023?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/D9H0LC18ZMQ/integridad-virtual-con-la-base-de-datos.html" title="Integridad virtual con la base de datos Oracle 11g" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp0.blogger.com/_ObxKtfPuuSQ/SFfhRZxKuzI/AAAAAAAACaU/JXyH8qe0WCI/s72-c/PLSQL-base-de-datos-Oracle-11g-columnas-virtuales.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://www.plsql.biz/2008/06/integridad-virtual-con-la-base-de-datos.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkQGSHw9cCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-3746288158476468416</id><published>2008-05-20T21:14:00.008+02:00</published><updated>2009-04-03T12:25:29.268+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:25:29.268+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><title>Oracle 11g y la cláusula PIVOT: Como girar columnas en SQL y PL/SQL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/kwDrfLffKt1vM3Kt4mtAas1cxTk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/kwDrfLffKt1vM3Kt4mtAas1cxTk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/kwDrfLffKt1vM3Kt4mtAas1cxTk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/kwDrfLffKt1vM3Kt4mtAas1cxTk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_ObxKtfPuuSQ/SDMjiFZePVI/AAAAAAAACTg/gEdmG2wYVxA/s1600-h/PLSQL-clausula-pivot.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://bp3.blogger.com/_ObxKtfPuuSQ/SDMjiFZePVI/AAAAAAAACTg/gEdmG2wYVxA/s200/PLSQL-clausula-pivot.jpg" alt="" id="BLOGGER_PHOTO_ID_5202541063014595922" border="0" /&gt;&lt;/a&gt;Una tarea que puede resultarnos útil en determinadas circunstancias es &lt;span style="font-weight: bold;"&gt;conseguir girar&lt;/span&gt; (en inglés &lt;i&gt;pivot&lt;/i&gt;) &lt;span style="font-weight: bold;"&gt;los resultados entregados por una consulta SQL o PLSQL&lt;/span&gt;. Por ejemplo, supongamos que escribimos la siguiente consulta para calcular el precio medio de los productos por almacen:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; SELECT almacen, producto, AVG(precio) pmedio&lt;br /&gt; 2  FROM  productos&lt;br /&gt; 3  GROUP BY almacen, producto&lt;br /&gt; 4  /&lt;br /&gt;&lt;br /&gt;ALMACEN PRODUCTO PMEDIO&lt;br /&gt;------- -------- ------&lt;br /&gt;Ba      ABC123       95&lt;br /&gt;Ba      DEF456       75&lt;br /&gt;Ba      XYZ987      160&lt;br /&gt;Ma      ABC123      100&lt;br /&gt;Ma      XYZ987      150&lt;br /&gt;Va      DEF456       80&lt;br /&gt;&lt;br /&gt;6 rows selected.       &lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;Basándonos en esta consulta, lo que queremos conseguir es que los distintos productos (ABC123, DEF456 y XYZ987) lleguen a ser las cabeceras de la consulta. Esto se puede conseguir modificando la consulta &lt;i&gt;SELECT&lt;/i&gt; de la siguiente manera:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; SELECT almacen,&lt;br /&gt; 2     AVG(DECODE(producto,'ABC123',precio)) ABC123,&lt;br /&gt; 3     AVG(DECODE(producto,'DEF456',precio)) DEF456,&lt;br /&gt; 4     AVG(DECODE(producto,'XYZ987',precio)) XYZ987&lt;br /&gt; 5   FROM productos&lt;br /&gt; 6   GROUP BY almacen&lt;br /&gt; 7   ORDER BY almacen&lt;br /&gt; 8   /&lt;br /&gt;&lt;br /&gt;ALMACEN ABC123 DEF456 XYZ987&lt;br /&gt;------- ------ ------ ------&lt;br /&gt;Ba          95     75    160&lt;br /&gt;Ma         100           150&lt;br /&gt;Va                 80       &lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;No obstante, aunque la consulta tal y como está escrita funciona, no podemos decir que el llegar a ella sea fácil o intuitivo. Para poder escribir de una manera más sencilla y entendible este tipo de consultas, la &lt;a href="http://www.plsql.biz/2007/10/oracle-lanza-la-base-de-datos-oracle.html"&gt;versión 11g de las bases de datos Oracle&lt;/a&gt; incorpora la &lt;span style="font-weight: bold;"&gt;nueva cláusula &lt;/span&gt;&lt;i style="font-weight: bold;"&gt;PIVOT&lt;/i&gt;. Veamos como quedaría nuestra consulta utilizando la cláusula &lt;i&gt;PIVOT&lt;/i&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; SELECT *&lt;br /&gt; 2   FROM (SELECT almacen, producto, precio&lt;br /&gt; 3         FROM   productos) prod&lt;br /&gt; 4   PIVOT (AVG(precio) FOR producto IN&lt;br /&gt; 5   ('ABC123','DEF456','XYZ987'))&lt;br /&gt; 6   ORDER BY almacen&lt;br /&gt; 7   /&lt;br /&gt;&lt;br /&gt;ALMACEN 'ABC123' 'DEF456' 'XYZ987'&lt;br /&gt;------- -------- -------- --------&lt;br /&gt;Ba            95       75      160&lt;br /&gt;Ma           100               150&lt;br /&gt;Va                     80         &lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Otro artículo del tutorial PL/SQL&lt;/span&gt;: &lt;a href="http://www.plsql.biz/2007/11/sql-y-plsql-la-sentencia-merge.html"&gt;La nueva sentencia SQL MERGE&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-3746288158476468416?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/VLT6Amvwe4Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/3746288158476468416/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=3746288158476468416&amp;isPopup=true" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3746288158476468416?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3746288158476468416?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/VLT6Amvwe4Q/oracle-11g-y-la-clusula-pivot-como.html" title="Oracle 11g y la cláusula PIVOT: Como girar columnas en SQL y PL/SQL" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp3.blogger.com/_ObxKtfPuuSQ/SDMjiFZePVI/AAAAAAAACTg/gEdmG2wYVxA/s72-c/PLSQL-clausula-pivot.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://www.plsql.biz/2008/05/oracle-11g-y-la-clusula-pivot-como.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0cHQn85fCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-6826924415130940514</id><published>2008-04-14T19:46:00.010+02:00</published><updated>2009-04-03T12:37:13.124+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:37:13.124+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Utilidades PLSQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><title>Vistas materializadas y la funcionalidad "Query Rewrite"</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/IhD2VLangt_97NCdFyWHEiw1lQ4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/IhD2VLangt_97NCdFyWHEiw1lQ4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/IhD2VLangt_97NCdFyWHEiw1lQ4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/IhD2VLangt_97NCdFyWHEiw1lQ4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_ObxKtfPuuSQ/SAOatyNCE5I/AAAAAAAACMY/8Dac9XyauFs/s1600-h/PLSQL-Cumplea%C3%B1os-Vistas-Materializadas.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_ObxKtfPuuSQ/SAOatyNCE5I/AAAAAAAACMY/8Dac9XyauFs/s200/PLSQL-Cumplea%C3%B1os-Vistas-Materializadas.jpg" border="0" alt="PL/SQL - La funcionalidad de reescritura de consultas y las vistas materializadas" id="BLOGGER_PHOTO_ID_5189161307022168978" /&gt;&lt;/a&gt;Ya he escrito anteriormente un par de artículos sobre vistas materializadas (&lt;i&gt;materialized views&lt;/i&gt;): uno sobre los &lt;a href="http://www.plsql.biz/2007/06/vistas-materializadas-materialized.html"&gt;aspectos generales de las vistas materializadas en SQL y PLSQL&lt;/a&gt; y otro sobre &lt;a href="http://www.plsql.biz/2007/12/el-refresco-de-las-vistas.html"&gt;el refresco de las vistas materializadas en SQL y PL/SQL&lt;/a&gt;. En este artículo voy a tratar una de las funcionalidades soportadas por las vistas materializadas, funcionalidad conocida como &lt;i&gt;QUERY REWRITE&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;b&gt;Funcionalidad de reescritura de una consulta&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Esta claro que acceder a una vista materializada puede ser significativamente más rápido que acceder a todas las tablas base utilizadas al crear dicha vista materializada. Es por esta causa por la que, si así lo hemos indicado al crear la vista materializada, el &lt;a href="http://www.plsql.biz/2006/12/hints-en-plsql-para-el-modo-de.html"&gt;optimizador Oracle&lt;/a&gt;, si la consulta o &lt;i&gt;query&lt;/i&gt; lo permite, puede reescribir el plan de ejecución de dicha consulta para acceder a la vista en lugar de a las tablas base. Obviamente, la reescritura de la consulta es transparente a las aplicaciones que la estén utilizando. Así pues, de alguna manera, el uso del &lt;i&gt;QUERY REWRITE&lt;/i&gt; es similar al uso de un índice.&lt;br /&gt;&lt;br /&gt;Los usuarios no necesitan tener privilegios especiales sobre la vista materializada para poder utilizar la reescritura de una consulta. Cualquier consulta ejecutada por un usuario que tenga permisos sobre las tablas base involucradas, podrá hacer uso de la reescritura de la consulta mediante el acceso a la vista materializada en lugar de acceder a las tablas base directamente. Esto sólo dependerá de lo que decida el optimizador.&lt;br /&gt;&lt;br /&gt;Por otro lado, la funcionalidad de reescritura de una consulta puede ser habilitada o deshabilitada a la hora de crear o modificar una vista materializada:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE MATERIALIZED VIEW nombre_vistam&lt;br /&gt;.....&lt;br /&gt;[{ENABLE|&lt;u&gt;DISABLE&lt;/u&gt;} QUERY REWRITE]&lt;br /&gt;.....&lt;br /&gt;AS SELECT ... FROM ... WHERE ...&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;Cómo determinar si el optimizador usa el QUERY REWRITE&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;La mejor forma para saber si el optimizador está utilizando la funcionalidad de reescritura de una consulta es utilizar el comando &lt;i&gt;EXPLAIN PLAN&lt;/i&gt; o la &lt;a href="http://www.plsql.biz/2007/05/cmo-obtener-el-plan-de-ejecucin-de-una.html"&gt;funcionalidad &lt;i&gt;AUTOTRACE&lt;/i&gt;&lt;/a&gt;. Otro aspecto a considerar es que, si el optimizador utiliza una vista materializada, entonces se debe observar una mejora en el tiempo de respuesta de la consulta.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Control de la funcionalidad QUERY REWRITE&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;La funcionalidad de reescritura de una consulta sólo puede utilizarse cuando se utiliza el &lt;a href="http://www.plsql.biz/2006/12/hints-en-plsql-para-el-modo-de.html"&gt;optimizador Oracle basado en costes&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Por otro lado, el parámetro dinámico &lt;b&gt;&lt;i&gt;QUERY_REWRITE_ENABLED&lt;/i&gt;&lt;/b&gt; se utiliza para configurar una sesión o instancia de una base de datos Oracle, indicando si permitimos al optimizador el uso o no de la funcionalidad de reescritura de una consulta. Si ponemos este parámetro a &lt;i&gt;FALSE&lt;/i&gt;, entonces habremos desactivado la funcionalidad de &lt;i&gt;QUERY REWRITE&lt;/i&gt; para dicha sesión o instancia.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;QUERY_REWRITE_INTEGRITY&lt;/i&gt;&lt;/b&gt; es otro parámetro dinámico que nos permite configurar la funcionalidad de reescritura de una consulta para una sesión o instancia. Dicho parámetro acepta los siguientes valores:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;ENFORCED&lt;/i&gt;: Se trata del valor por defecto. Si el parámetro toma este valor, entonces el optimizador reescribirá las consultas sólo si puede garantizar la consistencia de los datos entregados. Es decir, sólo las vistas materializadas que han sido actualizadas pueden ser utilizadas por la funcionalidad.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;TRUSTED&lt;/i&gt;: El optimizador reescribirá las consultas basándose en relaciones declaradas previamente sin necesidad de que estas hayan sido impuestas (&lt;i&gt;ENFORCED&lt;/i&gt;). Es decir, todas las vistas materializadas que estén debidamente actualizadas serán utilizadas por la funcionalidad de reescritura de consultas.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;STALE_TOLERATED&lt;/i&gt;: Siempre que sea posible el optimizador utilizará las vistas materializadas aunque éstas no estén actualizadas. Obviamente esto puede causar que la consulta devuelva valores incorrectos.&lt;br /&gt;&lt;br /&gt;También existen &lt;a href="http://www.plsql.biz/2007/01/hints-en-plsql-para-determinar-el-mtodo.html"&gt;&lt;i&gt;hints&lt;/i&gt;&lt;/a&gt; que permiten influir al optimizador en la decisión de utilizar o no la funcionalidad de &lt;i&gt;QUERY REWRITE&lt;/i&gt; al elaborar el plan de ejcución de una consulta, estos &lt;i&gt;hints&lt;/i&gt; son &lt;i&gt;REWRITE&lt;/i&gt; y &lt;i&gt;NOREWRITE&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-6826924415130940514?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/yDht5LDsv9E" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/6826924415130940514/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=6826924415130940514&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6826924415130940514?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6826924415130940514?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/yDht5LDsv9E/vistas-materializadas-y-la.html" title="Vistas materializadas y la funcionalidad &quot;Query Rewrite&quot;" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp2.blogger.com/_ObxKtfPuuSQ/SAOatyNCE5I/AAAAAAAACMY/8Dac9XyauFs/s72-c/PLSQL-Cumplea%C3%B1os-Vistas-Materializadas.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.plsql.biz/2008/04/vistas-materializadas-y-la.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkUDQ3c-eip7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-5481334996323847633</id><published>2008-02-28T17:52:00.004+01:00</published><updated>2009-04-03T12:24:32.952+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:24:32.952+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Nueva wiki oficial de Oracle</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/zOIamxgLT_VfSFWqTCi9Hl-4L4g/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/zOIamxgLT_VfSFWqTCi9Hl-4L4g/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/zOIamxgLT_VfSFWqTCi9Hl-4L4g/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/zOIamxgLT_VfSFWqTCi9Hl-4L4g/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_ObxKtfPuuSQ/R8bnWOqIjRI/AAAAAAAACDs/WEkyjAgGWTs/s1600-h/PLSQL-OTN-Oracle-Technology-Network.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_ObxKtfPuuSQ/R8bnWOqIjRI/AAAAAAAACDs/WEkyjAgGWTs/s200/PLSQL-OTN-Oracle-Technology-Network.JPG" border="0" alt="PL/SQL: La wiki oficial de Oracle en PLSQL" id="BLOGGER_PHOTO_ID_5172075591159549202" /&gt;&lt;/a&gt;Desde 1990, el sitio web &lt;a href="http://otn.oracle.com/" target="_blank" rel="nofollow"&gt;Oracle Technology Network&lt;/a&gt; (OTN), ha proporcionado a la comunidad de usuarios de las bases de datos Oracle un medio informal de comunicarse e intercambiar conocimientos entre ellos mismos, así como con empleados de Oracle. En 2005, esta fórmula fue mejorada al surgir una gran colección de &lt;a href="http://blogs.oracle.com/" target="_blank" rel="nofollow"&gt;blogs oficiales de Oracle&lt;/a&gt;. No obstante, esto recursos contienen fundamentalmente información proporcionada por Oracle.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Ahora, por primera vez, clientes y asociados pueden complementar este contenido oficial con el que ha generado la comunidad OTN, todo gracias a la &lt;span style="font-weight:bold;"&gt;nueva &lt;i&gt;wiki&lt;/i&gt; de Oracle&lt;/span&gt;. Para los que no sepan que es una &lt;i&gt;wiki&lt;/i&gt;, diré que se trata de un &lt;a href="http://www.aplicacion.org/" target="_blank"&gt;software&lt;/a&gt; que permite a los usuarios editar, crear y enlazar páginas web de una manera muy sencilla. En esencia se trata de un sistema de gestión de contenidos muy fácil de usar y que facilita el cruce de enlaces y la publicación de artículos en colaboración con otros usuarios.&lt;br /&gt;&lt;br /&gt;Con la nueva &lt;a href="http://wiki.oracle.com/" target="_blank" rel="nofollow"&gt;wiki de Oracle&lt;/a&gt;, puedes crear tu propio contenido web, incluyendo trucos y guías de instalación, documentación de proyectos, notas técnicas o cualquier otra cosa que capture tu imaginación. También se pueden publicar comentarios en las páginas web creadas por otros miembros de la comunidad Oracle. Sin duda, este mundo paralelo de Oracle formado por clientes y asociados, se trata del &lt;span style="font-weight:bold;"&gt;complemento perfecto al sitio web OTN y a los foros de discusión y blogs de Oracle&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Otro hecho que quiero mencionar es la existencia del llamado &lt;a href="http://otn.oracle.com/goto/oracle-ace" target="_blank" rel="nofollow"&gt;Oracle ACE Program&lt;/a&gt;, &lt;span style="font-weight:bold;"&gt;los Oracle ACEs son &lt;a href="http://www.eformate.es/" target="_blank"&gt;profesionales&lt;/a&gt; conocidos por sus profundos conocimientos en aplicaciones Oracle&lt;/span&gt; y por sus credenciales como entusiastas defensores de la comunidad Oracle. Los candidatos a ser Oracle ACEs pueden ser nominados por cualquier miembro de comunidad de aplicaciones y tecnología Oracle. Para poder acceder al programa Oracle ACE, los candidatos deben ser expertos que pongan su conocimiento de las aplicaciones Oracle  al alcance de todos, a través de blogs, podcasts, conferencias, artículos o foros.&lt;br /&gt;&lt;br /&gt;A continuación os pongo algunos enlaces a &lt;span style="font-weight:bold;"&gt;sitios de la Oracle Technology Network&lt;/span&gt; que no he mencionado anteriormente en el contenido de este artículo:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://otn.oracle.com/pub/news" target="_blank" rel="nofollow"&gt;Notas de prensa&lt;/a&gt;&lt;br /&gt;&lt;a href="http://otn.oracle.com/software" target="_blank" rel="nofollow"&gt;Descargas de software gratis&lt;/a&gt;&lt;br /&gt;&lt;a href="http://otn.oracle.com/documentation" target="_blank" rel="nofollow"&gt;Documentación&lt;/a&gt;&lt;br /&gt;&lt;a href="http://otn.oracle.com/tech" target="_blank" rel="nofollow"&gt;Centros de tecnología&lt;/a&gt;&lt;br /&gt;&lt;a href="http://otn.oracle.com/techcasts" target="_blank" rel="nofollow"&gt;Podcasts&lt;/a&gt;&lt;br /&gt;&lt;a href="http://otn.oracle.com/pub/articles" target="_blank" rel="nofollow"&gt;Artículos técnicos&lt;/a&gt;&lt;br /&gt;&lt;a href="http://forums.oracle.com/" target="_blank" rel="nofollow"&gt;Foros de discusión&lt;/a&gt;&lt;br /&gt;&lt;a href="http://otn.oracle.com/events" target="_blank" rel="nofollow"&gt;Calendario de eventos para desarrolladores&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-5481334996323847633?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/HzMJVURAS3w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/5481334996323847633/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=5481334996323847633&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5481334996323847633?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5481334996323847633?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/HzMJVURAS3w/nueva-wiki-oficial-de-oracle.html" title="Nueva wiki oficial de Oracle" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp0.blogger.com/_ObxKtfPuuSQ/R8bnWOqIjRI/AAAAAAAACDs/WEkyjAgGWTs/s72-c/PLSQL-OTN-Oracle-Technology-Network.JPG" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.plsql.biz/2008/02/nueva-wiki-oficial-de-oracle.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkUBRXg9eCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-8145090658230989746</id><published>2008-01-30T23:54:00.002+01:00</published><updated>2009-04-03T12:24:14.660+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:24:14.660+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Cambios de rendimiento en una sentencia SQL al activar el trazado</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/vQ5sDAsbnm4_SzC_M8UIgUVIbE4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/vQ5sDAsbnm4_SzC_M8UIgUVIbE4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/vQ5sDAsbnm4_SzC_M8UIgUVIbE4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/vQ5sDAsbnm4_SzC_M8UIgUVIbE4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_ObxKtfPuuSQ/R6ECUZKbPuI/AAAAAAAAB7s/h9qCAai88Ss/s1600-h/PLSQL-esperando-que-windows-arranque-SQL.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_ObxKtfPuuSQ/R6ECUZKbPuI/AAAAAAAAB7s/h9qCAai88Ss/s200/PLSQL-esperando-que-windows-arranque-SQL.jpg" border="0" alt="PLSQL y SQL esperando a que windows arranque" id="BLOGGER_PHOTO_ID_5161409197318553314" /&gt;&lt;/a&gt;Hace unos días un lector del blog me enviaba un email contándome un &lt;i&gt;"extraño"&lt;/i&gt; problema de rendimiento que tenía con una sentencia SQL o PL/SQL. Dicha sentencia SQL tardaba mucho tiempo en devolver resultados y, tras activar la &lt;a href="http://www.plsql.biz/2007/05/como-usar-la-utilidad-de-trazado-del.html"&gt;utilidad de trazado SQL&lt;/a&gt; (&lt;i&gt;SQL_TRACE=TRUE&lt;/i&gt;), el problema desaparecía y la respuesta de la sentencia SQL era inmediata.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;La verdad es que el fenómeno no es tan extraño una vez que se conoce la causa. Cuando se activa el trazado haciendo &lt;i&gt;SQL_TRACE=TRUE&lt;/i&gt;, lo que ocurre es que la sesión Oracle utiliza una nueva &lt;a href="http://www.plsql.biz/2008/01/optimizacin-sql-y-plsql-cdigo.html"&gt;área de SQL compartido&lt;/a&gt;. Esto supone que el &lt;i&gt;parsing&lt;/i&gt; (ver artículo sobre las &lt;a href="http://www.plsql.biz/2007/04/fases-durante-el-procesamiento-de-una.html"&gt;fases durante el procesamiento de una sentencia SQL&lt;/a&gt;) de cualquier sentencia SQL que se ejecute después de activar el trazado vuelva a tener lugar o que, de existir una versión de dicha sentencia SQL ya &lt;i&gt;parseada&lt;/i&gt; en la nueva área de SQL compartido, dicha versión no coincida con la versión existente cuando el trazado no está activo. Esto causa que, cuando la sentencia SQL utiliza variables (&lt;i&gt;bind variables&lt;/i&gt;), puesto que los valores reales de dichas variables son tomados en el momento del &lt;i&gt;parsing&lt;/i&gt;, muy probablemente, los &lt;a href="http://www.plsql.biz/2007/05/cmo-obtener-el-plan-de-ejecucin-de-una.html"&gt;planes de ejecución&lt;/a&gt; de la misma sentencia SQL sean diferentes antes y después de activar el trazado al haberse generado utilizando valores de variable distintos.&lt;br /&gt;&lt;br /&gt;Veamos un ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; CREATE TABLE pepe AS&lt;br /&gt; 2   SELECT CASE WHEN rownum = 1 THEN 1&lt;br /&gt; 3   ELSE 0 END pepe_id, all_tab_columns.*&lt;br /&gt; 4   FROM all_tab_columns&lt;br /&gt; 5   /&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; CREATE INDEX pepe_idx ON pepe(pepe_id);&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL&gt; BEGIN&lt;br /&gt; 2   DBMS_STATS.gather_table_stats (&lt;br /&gt; 3   user, 'T', method_opt=&gt;&lt;br /&gt; 4   'for all'||&lt;br /&gt; 5   'indexed columns'||&lt;br /&gt; 6   'size 254' );&lt;br /&gt; 7   END;&lt;br /&gt; 8   /&lt;br /&gt;PL/SQL procedure successfully completed.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;De esta manera habremos generado la tabla &lt;i&gt;pepe&lt;/i&gt; que contendrá un registro con la columna pepe_id=1, mientras que para el resto de &lt;a href="http://www.registradores.biz/" target="_blank"&gt;registros&lt;/a&gt; (digamos que alrededor de 75.000), dicha columna tomará el valor de 0. &lt;br /&gt;&lt;br /&gt;Resulta pues evidente que, si utilizamos el &lt;a href="http://www.plsql.biz/2006/12/hints-en-plsql-para-el-modo-de.html"&gt;optimizador basado en costes&lt;/a&gt;, al haber generado los correspondientes histogramas con el procedimiento PL/SQL &lt;i&gt;gather_table_stats&lt;/i&gt;, los planes de ejecución serán muy diferentes si ejecutamos &lt;i&gt;SELECT * FROM pepe WHERE pepe_id=1&lt;/i&gt;, sentencia SQL para la cual el optimizador elegirá utilizar el índice &lt;i&gt;pepe_idx&lt;/i&gt;, o si, por contra, ejecutamos &lt;i&gt;SELECT * FROM pepe WHERE pepe_id=0&lt;/i&gt;, sentencia para la cual el optimizador elegirá realizar un &lt;i&gt;full scan&lt;/i&gt; de la tabla.&lt;br /&gt;&lt;br /&gt;Pero que ocurriría si utilizamos una variable a la hora de ejecutar la sentencia SQL, es decir, si ejecutamos &lt;i&gt;SELECT * FROM pepe WHERE pepe_id=:id&lt;/i&gt;. En este caso la respuesta varía dependiendo de cual es la versión de la base de datos Oracle que estemos utilizando:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Base de datos Oracle 8i Release 3 y anteriores&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;En este caso el optimizador dispone de la siguiente información, la columna &lt;i&gt;pepe_id&lt;/i&gt; puede tomar dos valores (1 y 0) y, además, la tabla tiene unos 75.000 registros. Debido al hecho de que la columna puede tomar sólo dos valores, el viejo optimizador de la versión 8i supondrá que para cualquier valor de la variable &lt;i&gt;:id&lt;/i&gt;, la consulta &lt;i&gt;SELECT&lt;/i&gt; asociada devolverá aproximadamente la mitad de los registros de la tabla. Así que, la decisión más probable del optimizador será realizar un escaneado completo (&lt;i&gt;full scan&lt;/i&gt;) de la tabla.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bases de datos Oracle 9i y 10g&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Si disponemos de una versión de la base de datos Oracle incluida entre las versiones 9i y 10g, entonces la base de datos Oracle esperará a que se suministre el valor de la variable, antes de que el optimizador decida cual es el mejor plan de ejecución. Esto es lo que se conoce como &lt;i&gt;bind variable peeking&lt;/i&gt; (que traducido vendría a ser algo como &lt;i&gt;echar un vistazo a la variable&lt;/i&gt;). Así pues, en lo que se refiere a nuestra consulta &lt;i&gt;SELECT&lt;/i&gt;, el optimizador elegirá bien realizar un &lt;i&gt;full scan&lt;/i&gt; (:id=0), o bien utilizar el índice &lt;i&gt;pepe_idx&lt;/i&gt; (:id=1), dependiendo del valor de la variable &lt;i&gt;:id&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Y aquí es donde nos podemos encontrar con un problema de rendimiento ya que, el plan de ejecución que el optimizador utilice por primera vez, será el plan de ejecución que se almacene en el área de SQL compartido, y dicho plan será el que se utilice para ejecutar nuestra sentencia SQL &lt;i&gt;SELECT * FROM pepe WHERE pepe_id=:id&lt;/i&gt;, independientemente del valor que demos a la variable &lt;i&gt;:id&lt;/i&gt; con posterioridad.&lt;br /&gt;&lt;br /&gt;Entonces ya sabemos que es lo que le pasó al lector que inspiró este artículo:&lt;br /&gt;&lt;br /&gt;a) Alguien ejecuto la consulta en cuestión utilizando un determinado valor para la variable &lt;i&gt;:id&lt;/i&gt;. &lt;br /&gt;&lt;br /&gt;b) El plan de ejecución correspondiente a dicho valor de la variable se almacenó en el área de SQL compartido.&lt;br /&gt;&lt;br /&gt;c) Nuestro lector ejecuto la misma consulta pero utilizando otro valor para la variable &lt;i&gt;:id&lt;/i&gt;. El optimizador reutilizó el plan de ejecución almacenado en el área de SQL compartido. Dicho plan no era el más eficiente para el nuevo valor de la variable, de hecho se trataba de un plan de ejecución bastante pobre. Como resultado la consulta tardó en responder más tiempo del esperado.&lt;br /&gt;&lt;br /&gt;d) Posteriormente nuestro lector activó el trazado (&lt;i&gt;SQL_TRACE=TRUE&lt;/i&gt;) y volvió a ejecutar la consulta pero, en este caso, al estar el trazado activo, el plan de ejecución no se sacó del área de SQL compartido, sino que se elaboró un nuevo plan de ejecución mucho más eficiente, dando como resultado que la sentencia SQL se ejecutase de forma rápida.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Posibles soluciones&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Existen algunas soluciones que nos permitirán evitar este tipo de situaciones provocadas por el &lt;i&gt;bind variable peeking&lt;/i&gt;:&lt;br /&gt;&lt;br /&gt;a) No utilizar variables (&lt;i&gt;bind variables&lt;/i&gt;). En el ejemplo que he puesto es bastante evidente que no es necesario utilizar variables, simplemente podemos utilizar constantes (valores literales) en nuestra cláusula &lt;i&gt;WHERE&lt;/i&gt;. Esta es, sin duda, la mejor solución.&lt;br /&gt;&lt;br /&gt;b) No generar estadísticas que puedan hacer variar los planes de ejecución. Obviamente, si no hubiéramos generado los histogramas de la tabla &lt;i&gt;pepe&lt;/i&gt;, el optimizador siempre generaría el mismo plan de ejecución con independencia del valor de la variable. A veces esto puede ser recomendable, pero si no generamos estadísticas, entonces perderemos algunas de las ventajas derivadas de utilizar el optimizador basado en costes.&lt;br /&gt;&lt;br /&gt;c) Cambiar el valor del parámetro &lt;i&gt;CURSOR_SHARING&lt;/i&gt; para que en vez de &lt;i&gt;FORCE&lt;/i&gt; sea &lt;i&gt;SIMILAR&lt;/i&gt;. Si utilizamos el valor &lt;i&gt;SIMILAR&lt;/i&gt; evitaremos que el &lt;i&gt;bind variable peeking&lt;/i&gt; entre en juego.&lt;br /&gt;&lt;br /&gt;d) Actualizar nuestra base de datos Oracle a la &lt;a href="http://www.plsql.biz/2007/10/oracle-lanza-la-base-de-datos-oracle.html"&gt;versión 11g&lt;/a&gt;, ya que esta versión está dotada de una nueva funcionalidad conocida como &lt;i&gt;intelligent cursor sharing&lt;/i&gt; (compartición de cursores inteligente). Así pues, para nuestra consulta ejemplo, el optimizador de la versión 11g es capaz de identificar que un plan de ejecución no es suficiente para todos los valores posibles de la variable &lt;i&gt;:id&lt;/i&gt;, permitiendo que se generen varios planes de ejecución distintos para una misma sentencia SQL.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-8145090658230989746?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/bImtd6BpDak" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/8145090658230989746/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=8145090658230989746&amp;isPopup=true" title="3 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/8145090658230989746?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/8145090658230989746?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/bImtd6BpDak/cambios-de-rendimiento-en-una-sentencia.html" title="Cambios de rendimiento en una sentencia SQL al activar el trazado" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp3.blogger.com/_ObxKtfPuuSQ/R6ECUZKbPuI/AAAAAAAAB7s/h9qCAai88Ss/s72-c/PLSQL-esperando-que-windows-arranque-SQL.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://www.plsql.biz/2008/01/cambios-de-rendimiento-en-una-sentencia.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkUGQX8yfCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-5037922472876339895</id><published>2008-01-10T18:39:00.001+01:00</published><updated>2009-04-03T12:23:40.194+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:23:40.194+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><title>Optimización SQL y PL/SQL - Código compartido</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/X4Vsbk5szIrLHq9jd96Rx3kxk4g/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/X4Vsbk5szIrLHq9jd96Rx3kxk4g/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/X4Vsbk5szIrLHq9jd96Rx3kxk4g/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/X4Vsbk5szIrLHq9jd96Rx3kxk4g/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_ObxKtfPuuSQ/R4ZevqdZasI/AAAAAAAAB2A/1C1yew0kAp8/s1600-h/PLSQL-Codigo-Compartido-Shared-Cursors.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_ObxKtfPuuSQ/R4ZevqdZasI/AAAAAAAAB2A/1C1yew0kAp8/s200/PLSQL-Codigo-Compartido-Shared-Cursors.jpg" border="0" alt="Optimización de la base de datos Oracle mediante el uso de código compartido o los shared cursors" id="BLOGGER_PHOTO_ID_5153910996516563650" /&gt;&lt;/a&gt;Cuando alguien solicita la ejecución de algún código SQL o PL/SQL, Oracle busca dicho código o sentencia en el área de SQL compartido. Si la sentencia y su correspondiente análisis sintáctico (&lt;i&gt;parsing&lt;/i&gt;) existen en la librería caché de la base de datos, entonces Oracle reutiliza el ejecutable correspondiente. Dentro de este área de SQL compartido, cada sentencia SQL ocupa un espacio determinado conocido como área contextual o, en terminología inglesa, &lt;i&gt;cursor&lt;/i&gt; (ojo, esto no tiene nada que ver con los &lt;a href="http://www.plsql.biz/2006/12/cursores-en-plsql.html"&gt;cursores PL/SQL&lt;/a&gt;). &lt;span style="font-weight:bold;"&gt;Cada &lt;i&gt;cursor&lt;/i&gt; localizado en el área de SQL compartido contiene la siguiente información&lt;/span&gt;:&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;- El análisis sintáctico de la sentencia SQL (ver artículo sobre &lt;a href="http://www.plsql.biz/2007/04/fases-durante-el-procesamiento-de-una.html"&gt;las fases del procesamiento de una sentencia SQL&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;- El &lt;a href="http://www.plsql.biz/2007/05/cmo-obtener-el-plan-de-ejecucin-de-una.html"&gt;plan de ejecución&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;- La lista de objetos de la base de datos que son referenciados por la sentencia.&lt;br /&gt;&lt;br /&gt;Si dos usuarios ejecutan la misma sentencia SQL o PL/SQL, entonces ambos utilizaran el mismo &lt;i&gt;cursor&lt;/i&gt;. Los &lt;span style="font-weight:bold;"&gt;beneficios de los &lt;i&gt;cursores&lt;/i&gt; compartidos&lt;/span&gt; son evidentes:&lt;br /&gt;&lt;br /&gt;- Reducción del tiempo de &lt;i&gt;parsing&lt;/i&gt; o análisis sintáctico de la sentencia SQL o PLSQL.&lt;br /&gt;&lt;br /&gt;- Las necesidades de memoria se ajustan dinámicamente y el uso de la misma puede mejorar de forma importante.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Los &lt;i&gt;cursores&lt;/i&gt; sólo pueden ser compartidos por sentencias SQL que tienen las siguientes características&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;- Se trata de sentencias SQL idénticas.&lt;br /&gt;&lt;br /&gt;- El texto de las sentencias SQL debe ser exactamente el mismo, incluyendo mayúsculas, espacios, tabuladores, retornos de carro y comentarios.&lt;br /&gt;&lt;br /&gt;- Los objetos de la base de datos Oracle a los que hace referencia la sentencia SQL deben ser también idénticos. Por ejemplo, dos tablas pueden recibir el mismo nombre pero pertenecer a diferentes usuarios o esquemas y, por tanto, tratarse a nivel de base de datos de tablas diferentes.&lt;br /&gt;&lt;br /&gt;- Los tipos de las variables usadas en la sentencia SQL deben ser iguales (ver &lt;a href="http://www.plsql.biz/2006/10/tipos-de-datos-en-plsql.html"&gt;tipos de datos en PL/SQL&lt;/a&gt;). No es necesario que los nombres de las variables sean idénticos, es decir, sentencias SQL como &lt;i&gt;SELECT * FROM clientes WHERE cliente_id = :c&lt;/i&gt; y  &lt;i&gt;SELECT * FROM clientes WHERE cliente_id = :d&lt;/i&gt; pueden ser sentencias idénticas ya que las variables &lt;i&gt;c:&lt;/i&gt; y &lt;i&gt;d:&lt;/i&gt; son renombradas internamente (la primera variable de una sentencia SQL es siempre renombrada como &lt;i&gt;:b1&lt;/i&gt;, la segunda como &lt;i&gt;:b2&lt;/i&gt; y así sucesivamente).&lt;br /&gt;&lt;br /&gt;Con respecto a este punto debo mencionar que la mayoría de las herramientas Oracle, tales como PL/SQL, precompiladores y Oracle Developer, realizan un preprocesado de las sentencias SQL para hacerlas tan idénticas como sea posible mediante la eliminación de comentarios y de espacios innecesarios, así como convirtiendo mayúsculas y minúsculas cuando esto es posible. SQL*Plus es la excepción y envía las sentencias SQL tal y como son escritas por el usuario.&lt;br /&gt;&lt;br /&gt;Por esta razón &lt;span style="font-weight:bold;"&gt;es muy importante que&lt;/span&gt;, cuando escribamos código SQL y PLSQL, &lt;span style="font-weight:bold;"&gt;creemos código lo más genérico posible&lt;/span&gt; mediante:&lt;br /&gt;&lt;br /&gt;- La utilización de &lt;a href="http://www.plsql.biz/2007/03/procedimientos-y-funciones-en-plsql.html"&gt;procedimientos, funciones y paquetes PL/SQL almacenados&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;- El uso de &lt;a href="http://www.plsql.biz/2007/02/triggers-en-plsql.html"&gt;triggers PL/SQL&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;- Hacer llamadas a triggers y procedimientos almacenados en la base de datos cuando utilicemos Oracle Developer.&lt;br /&gt;&lt;br /&gt;- Escribir librerías de rutinas y procedimientos.&lt;br /&gt;&lt;br /&gt;También es crucial que sigamos unos &lt;span style="font-weight:bold;"&gt;estándares a la hora de escribir código SQL&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;- Seguir unos estándares para todo tipo de sentencias, incluyendo las sentencias en código PLSQL.&lt;br /&gt;&lt;br /&gt;- Desarrollar normas en cuanto al uso de mayúsculas y minúsculas.&lt;br /&gt;&lt;br /&gt;- Desarrollar normas en cuanto a la utilización de espacios, tabuladores y retornos de carro.&lt;br /&gt;&lt;br /&gt;- Seguir unos estándares para los comentarios. Preferiblemente los comentarios deben mantenerse fuera de las sentencias SQL o PL/SQL.&lt;br /&gt;&lt;br /&gt;- Utilizar los mismos nombres a la hora de referirse a objetos de la base de datos idénticos. Por ejemplo, aunque incomodo puede resultar interesante preceder a los nombres de las tablas con el nombre del esquema (o usuario) al que pertenecen.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-5037922472876339895?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/Y7859lpe47o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/5037922472876339895/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=5037922472876339895&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5037922472876339895?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5037922472876339895?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/Y7859lpe47o/optimizacin-sql-y-plsql-cdigo.html" title="Optimización SQL y PL/SQL - Código compartido" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp3.blogger.com/_ObxKtfPuuSQ/R4ZevqdZasI/AAAAAAAAB2A/1C1yew0kAp8/s72-c/PLSQL-Codigo-Compartido-Shared-Cursors.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.plsql.biz/2008/01/optimizacin-sql-y-plsql-cdigo.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkYMSHw6cCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-5651150824718635202</id><published>2007-12-18T17:58:00.001+01:00</published><updated>2009-04-03T12:23:09.218+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:23:09.218+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Utilidades PLSQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><title>El refresco de las vistas materializadas en SQL y PL/SQL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/As7noTUfiqZ0DVm_OX5XdR9TO0A/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/As7noTUfiqZ0DVm_OX5XdR9TO0A/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/As7noTUfiqZ0DVm_OX5XdR9TO0A/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/As7noTUfiqZ0DVm_OX5XdR9TO0A/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_ObxKtfPuuSQ/R2gDmadZaRI/AAAAAAAAByk/QaMDPFVLc6E/s1600-h/PLSQL-Ordenador-Teclado.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_ObxKtfPuuSQ/R2gDmadZaRI/AAAAAAAAByk/QaMDPFVLc6E/s200/PLSQL-Ordenador-Teclado.jpg" border="0" alt="Como quedó un teclado después del refresco (refresh) de vistas materializadas (Materialized views) en las bases de datos Oracle" id="BLOGGER_PHOTO_ID_5145366532743325970" /&gt;&lt;/a&gt;Ya he hablado en otro artículo acerca del &lt;a href="http://www.plsql.biz/2007/06/vistas-materializadas-materialized.html"&gt;funcionamiento básico de las vistas materializadas&lt;/a&gt; (&lt;a href="http://www.plsql.biz/2007/06/vistas-materializadas-materialized.html"&gt;&lt;i&gt;materialized views&lt;/i&gt;&lt;/a&gt;), en éste voy a exponer los distintos tipos de refresco en SQL y PLSQL que se pueden utilizar para actualizar una vista materializada con los cambios provocados por las actualizaciones en las tablas base utilizadas en la misma. &lt;span style="font-weight:bold;"&gt;El tipo de refresco que debemos elegir dependerá de la frecuencia de actualización de las tablas base y de las necesidades que tengamos de disponer de datos exactos&lt;/span&gt;.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;b&gt;Tipos de refresco&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;COMPLETE&lt;/span&gt;: Este tipo de refresco implica el borrado de los datos existentes y la reinserción de todos los datos mediante la reejecución de la consulta &lt;i&gt;SELECT&lt;/i&gt; que define la vista materializada.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;FAST&lt;/span&gt;: El refresco aplica sólo a los cambios realizados sobre las tablas base desde el último refresco. Puede ser de dos tipos:&lt;br /&gt;&lt;br /&gt;- &lt;span style="font-style: italic;"&gt;Utilizando los logs de la vista materializada&lt;/span&gt;: En este caso todos los cambios sobre las tablas base se almacenan en dichos logs, aplicándose en el momento del refresco a la vista materializada. &lt;b&gt;Estos logs deben ser creados sobre todas las tablas base de la vista&lt;/b&gt; utilizando el comando &lt;i&gt;CREATE MATERIALIZED VIEW LOG&lt;/i&gt;, es decir, necesitaremos crear un log por cada tabla base que se utilice en la vista materializada.&lt;br /&gt;&lt;br /&gt;- &lt;i style="font-style: italic;"&gt;Utilizando rangos ROWID&lt;/i&gt;: Este tipo de refresco necesita de otro tipo de logs, los llamados logs de carga directa (&lt;i&gt;direct loader logs&lt;/i&gt;).&lt;br /&gt;&lt;br /&gt;Conviene puntualizar que no todas las vistas materializadas pueden soportar el refresco &lt;i&gt;FAST&lt;/i&gt;. Por ejemplo, el uso de funciones SQL como &lt;i&gt;SUM&lt;/i&gt;, &lt;i&gt;AVG&lt;/i&gt;, &lt;i&gt;MAX&lt;/i&gt;, &lt;i&gt;MIN&lt;/i&gt; o &lt;i&gt;COUNT&lt;/i&gt; no son admitidas por este tipo de refresco. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;FORCE&lt;/span&gt;: Es la opción por defecto si no se selecciona ningún tipo de refresco a la hora de crear la vista materializada. Este tipo de refresco funciona de la siguiente manera, si es posible la vista se refrescará utilizando el mecanismo &lt;i&gt;FAST&lt;/i&gt;, en caso contrario se empleará la opción &lt;i&gt;COMPLETE&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;NEVER&lt;/span&gt;: Esta opción suprime todos los refrescos de la vista materializada.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Formas de refresco&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Refresco manual&lt;/span&gt;: Los refrescos manuales de las vistas materializadas se realizan utilizando el paquete PL/SQL estándar &lt;i&gt;DBMS_MVIEW&lt;/i&gt;. Este paquete incluye un buen número de funciones y procedimientos PLSQL que permiten gestionar las vistas materializadas. Entre ellos cabe destacar:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;DBMS_MVIEW.REFRESH ('nombre_vista_materializada')&lt;/span&gt; - Refresca una vista materializada específica.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;DBMS_MVIEW.REFRESH_DEPENDENT ('nom_tab1, nom_tab2, ...')&lt;/span&gt; - Refresca todas las vistas materializadas que utilicen como tabla base alguna de las tablas o vistas materializadas indicadas en la lista (los nombres de las tablas o vistas materializadas deben separarse mediante comas).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;DBMS_MVIEW.REFRESH_ALL_MVIEWS (n)&lt;/span&gt; - Refresca todas las vistas materializadas del sistema devolviendo un entero (n) que indica el número de registros que se han refrescado.&lt;br /&gt;&lt;br /&gt;Todos estos procedimientos y funciones &lt;span style="font-weight: bold;"&gt;admiten parámetros adicionales&lt;/span&gt; entre los que cabe mencionar: el tipo de refresco (? - force, f - fast, c - complete), el segmento de &lt;i&gt;rollback&lt;/i&gt; que se debe usar durante el refresco, si se continúa  (&lt;i&gt;true&lt;/i&gt;) o no  (&lt;i&gt;false&lt;/i&gt;) realizando el refresco tras detectar un error, si el refresco de todas las vistas materializadas se realiza en una sola transacción de manera que si falla el refresco de una vista falla el refresco de todas (&lt;i&gt;true&lt;/i&gt;) o de si cada vista materializada se refresca en transacciones separadas (&lt;i&gt;false&lt;/i&gt;).&lt;br /&gt;&lt;br /&gt;Los refrescos manuales requieren que los parámetros del sistema &lt;span style="font-style: italic;"&gt;JOB_QUEUE_PROCESSES&lt;/span&gt; y&lt;span style="font-style: italic;"&gt; JOB_QUEUE_INTERVAL&lt;/span&gt; estén configurados para permitir la ejecución de trabajos encolados.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Refresco automático&lt;/span&gt;: Esta forma de refresco puede realizarse de dos formas:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;ON COMMIT&lt;/span&gt;: La vista materializada se refresca cada vez que se ejecuta un &lt;i&gt;COMMIT&lt;/i&gt; sobre alguna de las tablas base de la vista. Esto significa que la ejecución del &lt;i&gt;COMMIT&lt;/i&gt; tomará más tiempo lo que puede afectar seriamente al rendimiento. Yo personalmente no recomiendo este tipo de refresco.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Refresco programado&lt;/span&gt;: El refresco se programa para que ocurra a una hora o tiempo determinado. Por ejemplo, una vista se podría programar para que se refresque todos los días a una determinada hora mediante el uso de las cláusulas &lt;i&gt;START WITH&lt;/i&gt; (seguido de la hora en formato &lt;i&gt;datetime&lt;/i&gt; del primer refresco automático) y &lt;i&gt;NEXT&lt;/i&gt; (seguido de una expresión en formato &lt;i&gt;datetime&lt;/i&gt; que se utilizará para calcular el intervalo entre refrescos automáticos). En este caso también es necesario configurar correctamente el parámetro del sistema &lt;span style="font-style:italic;"&gt;JOB_QUEUE_PROCESSES&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE MATERIALIZED VIEW nombre_vm&lt;br /&gt;...&lt;br /&gt;REFRESH START WITH ROUND(SYSDATE + 1) + 9/24&lt;br /&gt;NEXT NEXT_DAY(TRUNC(SYSDATE), 'TUESDAY') + 16/24&lt;br /&gt;AS SELECT ...;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En caso del ejemplo, la base de datos Oracle refrescará automáticamente la vista materializada mañana a la 9:00 AM y posteriormente todos los martes a la 4:00 PM.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-5651150824718635202?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/OJTgk7su7zc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/5651150824718635202/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=5651150824718635202&amp;isPopup=true" title="10 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5651150824718635202?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5651150824718635202?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/OJTgk7su7zc/el-refresco-de-las-vistas.html" title="El refresco de las vistas materializadas en SQL y PL/SQL" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp0.blogger.com/_ObxKtfPuuSQ/R2gDmadZaRI/AAAAAAAAByk/QaMDPFVLc6E/s72-c/PLSQL-Ordenador-Teclado.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">10</thr:total><feedburner:origLink>http://www.plsql.biz/2007/12/el-refresco-de-las-vistas.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkYAQX07fCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-3017909395770385451</id><published>2007-11-26T18:49:00.001+01:00</published><updated>2009-04-03T12:22:20.304+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:22:20.304+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><title>SQL y PL/SQL - La nueva sentencia MERGE</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ectmtmkNMm1u4iqxG1rUK6S4D9s/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ectmtmkNMm1u4iqxG1rUK6S4D9s/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ectmtmkNMm1u4iqxG1rUK6S4D9s/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ectmtmkNMm1u4iqxG1rUK6S4D9s/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_ObxKtfPuuSQ/R0sKNcdu5hI/AAAAAAAABuI/vy8hLgVz4xg/s1600-h/PLSQL-Dios-y-su-ordenador.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_ObxKtfPuuSQ/R0sKNcdu5hI/AAAAAAAABuI/vy8hLgVz4xg/s200/PLSQL-Dios-y-su-ordenador.jpg" border="0" alt="Dios y su ordenador de SQL y PLSQL" id="BLOGGER_PHOTO_ID_5137211026041660946" /&gt;&lt;/a&gt;La sentencia &lt;i&gt;MERGE&lt;/i&gt;, a la que muchos denominan &lt;i&gt;UPSERT&lt;/i&gt; debido a su funcionalidad, está disponible desde la aparición de la versión 9i de la base de datos Oracle. Se trata de una de las funcionalidades del &lt;i&gt;kernel&lt;/i&gt; de Oracle más utiles a la hora de permitir el uso de la &lt;a href="http://es.wikipedia.org/wiki/ETL" target="_blank" rel="nofollow"&gt;tecnología &lt;i&gt;ETL&lt;/i&gt;&lt;/a&gt; (&lt;i&gt;Extract, Transform and Load&lt;/i&gt; - Extraer, Transformar y Cargar) con las bases de datos Oracle. Este tipo de tecnología puede utilizarse y está especialmente enfocada para ser usada en aplicaciones de &lt;a href="http://es.wikipedia.org/wiki/Almac%C3%A9n_de_datos" target="_blank" rel="nofollow"&gt;&lt;i&gt;data warehousing&lt;/i&gt;&lt;/a&gt; (almacen de datos). Básicamente, &lt;b&gt;lo que permite la sentencia SQL &lt;i&gt;MERGE&lt;/i&gt; es, dependiendo de una condición lógica, actualizar registros (&lt;i&gt;UPDATE&lt;/i&gt;) cuando la condición se cumple, o insertar registros (&lt;i&gt;INSERT&lt;/i&gt;) cuando dicha condición no se cumple&lt;/b&gt;, de ahí surge la denominación de &lt;i&gt;UPSERT&lt;/i&gt;.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Anteriormente a la versión 9i de Oracle, la alternativa en SQL era ejecutar dos sentencias DML, un &lt;i&gt;UPDATE&lt;/i&gt; y un &lt;i&gt;INSERT&lt;/i&gt;, cada una utilizando condiciones lógicas opuestas. En cuanto a las alternativas en PL/SQL eran: bien intentar insertar un registro y si la sentencia &lt;i&gt;INSERT&lt;/i&gt; fallaba debido a una &lt;a href="http://www.plsql.biz/2007/01/manejo-de-excepciones-el-plsql.html"&gt;excepción PLSQL&lt;/a&gt; del tipo &lt;i&gt;DUP_VAL_ON_INDEX&lt;/i&gt;, entonces realizar un &lt;i&gt;UPDATE&lt;/i&gt; del registro en cuestión; bien intentar actualizar un registro y si la sentencia &lt;i&gt;UPDATE&lt;/i&gt; devolvía &lt;i&gt;SQL%NOTFOUND&lt;/i&gt;, entonces ejecutar la correspondiente sentencia &lt;i&gt;INSERT&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Las &lt;b&gt;ventajas de la sentencia SQL &lt;i&gt;MERGE&lt;/i&gt;&lt;/b&gt; son claras:&lt;br /&gt;&lt;br /&gt;- Permite con una misma sentencia realizar un &lt;i&gt;UPDATE&lt;/i&gt; si el registro existe, o un &lt;i&gt;INSERT&lt;/i&gt; si se trata de un nuevo registro.&lt;br /&gt;&lt;br /&gt;- La sentencia puede paralelizarse de forma transparente.&lt;br /&gt;&lt;br /&gt;- Se evita la necesidad de realizar actualizaciones múltiples.&lt;br /&gt;&lt;br /&gt;- Es especialmente útil para realizar operaciones en masa y, como ya he mencionado, en aplicaciones de &lt;i&gt;data warehousing&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;- El rendimiento de la base de datos mejora ya que, al necesitarse menos sentencias SQL para realizar las mismas operaciones, también se necesitan menos accesos a las tablas fuente.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ejemplo de sentencia MERGE&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;En el presente ejemplo voy a partir del hecho de que tenemos dos tablas en un &lt;i&gt;data warehouse&lt;/i&gt;, una de ellas con muchos registros denominada &lt;i&gt;clientes&lt;/i&gt; y otra más pequeña denominada &lt;i&gt;datos_cli&lt;/i&gt; cuyos registros deben insertarse en la tabla &lt;i&gt;clientes&lt;/i&gt;. En pocas palabras, estoy suponiendo que la tabla &lt;i&gt;datos_cli&lt;/i&gt; contiene los cambios a realizar sobre la tabla &lt;i&gt;clientes&lt;/i&gt;. La sentencia SQL &lt;i&gt;MERGE&lt;/i&gt; que debemos utilizar para actualizar la tabla &lt;i&gt;cliente&lt;/i&gt; podría ser la siguiente:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 80%;"&gt;&lt;pre&gt;MERGE INTO clientes cli USING datos_cli dac&lt;br /&gt;  ON (cli.cliente_id = dac.cliente_id)&lt;br /&gt;  WHEN MATCHED THEN&lt;br /&gt;    UPDATE SET &lt;br /&gt;      cli.nombre = dac.nombre,&lt;br /&gt;      cli.direccion = dac.direccion&lt;br /&gt;  WHEN NOT MATCHED THEN&lt;br /&gt;    INSERT (cliente_id, nombre, direccion)&lt;br /&gt;    VALUES (dac.cliente_id, dac.nombre, dac.direccion);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Así pues, la sentencia &lt;i&gt;MERGE&lt;/i&gt; del ejemplo realizará las siguientes operaciones:&lt;br /&gt;&lt;br /&gt;- Si existe un registro en &lt;i&gt;datos_cli&lt;/i&gt; con el mismo ID de otro registro en la tabla &lt;i&gt;clientes&lt;/i&gt;, entoces actualizará el valor de los campos nombre y dirección de la tabla &lt;i&gt;clientes&lt;/i&gt; con los valores correspondientes de la tabla &lt;i&gt;datos_cli&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;- Si no existe un registro en &lt;i&gt;clientes&lt;/i&gt; con el ID del registro de &lt;i&gt;datos_cli&lt;/i&gt; que está siendo procesado, entonces se insertará dicho registro en la tabla &lt;i&gt;clientes&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;En resumen, &lt;b&gt;la sintaxis de la sentencia MERGE debe incorporar&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;Una &lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;cláusula &lt;i&gt;INTO&lt;/i&gt;&lt;/span&gt;, que especifica la tabla destino donde los registros serán actualizados o insertados.&lt;br /&gt;&lt;br /&gt;Una &lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;cláusula &lt;i&gt;USING&lt;/i&gt;&lt;/span&gt;, especificando el origen de los datos que van a ser insertados o que van a servir para actualizar la tabla destino. El origen de los datos puede tratarse de una tabla, una vista, o del resultado de la ejecución de una consulta &lt;i&gt;SELECT&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Una &lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;cláusula &lt;i&gt;ON&lt;/i&gt;&lt;/span&gt;, que especifica la condición bajo la cual se realizará, bien la operación &lt;i&gt;UPDATE&lt;/i&gt; (si la condición se cumple), bien la operación &lt;i&gt;INSERT&lt;/i&gt; (si la condición no se cumple).&lt;br /&gt;&lt;br /&gt;Las &lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;cláusulas &lt;i&gt;WHEN MATCHED | NOT MATCHED&lt;/i&gt;&lt;/span&gt;, que son las que indican a la base de datos Oracle que acción realizar si se cumple o no se cumple la condición del &lt;i&gt;ON&lt;/i&gt;. Estas dos cláusulas se pueden poner en cualquier orden.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Artículos relacionados&lt;/span&gt;: &lt;br /&gt;&lt;a href="http://www.plsql.biz/2007/11/sql-y-plsql-la-sentencia-insert.html"&gt;La sentencia INSERT multitabla de la versión 9i de Oracle&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://www.plsql.biz/2007/09/la-clasula-with-en-sql-y-plsql.html"&gt;La cláusula WITH de la versión 9i de la base de datos Oracle&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-3017909395770385451?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/O7q_yL0NvAI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/3017909395770385451/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=3017909395770385451&amp;isPopup=true" title="1 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3017909395770385451?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3017909395770385451?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/O7q_yL0NvAI/sql-y-plsql-la-sentencia-merge.html" title="SQL y PL/SQL - La nueva sentencia MERGE" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp0.blogger.com/_ObxKtfPuuSQ/R0sKNcdu5hI/AAAAAAAABuI/vy8hLgVz4xg/s72-c/PLSQL-Dios-y-su-ordenador.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://www.plsql.biz/2007/11/sql-y-plsql-la-sentencia-merge.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkcNRng9eSp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-1786201000898115007</id><published>2007-11-06T17:57:00.001+01:00</published><updated>2009-04-03T12:21:37.661+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:21:37.661+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><title>SQL y PL/SQL - La sentencia INSERT multitabla de Oracle 9i</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/83qcTf0HL6Crx6wS0dzuM3Ui2yo/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/83qcTf0HL6Crx6wS0dzuM3Ui2yo/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/83qcTf0HL6Crx6wS0dzuM3Ui2yo/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/83qcTf0HL6Crx6wS0dzuM3Ui2yo/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_ObxKtfPuuSQ/RzCdeWCmExI/AAAAAAAABqo/LbNQoiiixMs/s1600-h/PLSQL-Vaca-Mujiendo.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://bp1.blogger.com/_ObxKtfPuuSQ/RzCdeWCmExI/AAAAAAAABqo/LbNQoiiixMs/s200/PLSQL-Vaca-Mujiendo.jpg" alt="La vaca de SQL y PLSQL y la sentencia INSERT multitabla" id="BLOGGER_PHOTO_ID_5129773120213095186" border="0" /&gt;&lt;/a&gt;La versión 9i de las bases de datos Oracle ha introducido la posibilidad de utilizar sentencias &lt;i&gt;INSERT&lt;/i&gt; multitabla. Así pues, la sentencia SQL o PLSQL &lt;i&gt;INSERT... SELECT&lt;/i&gt; ha cambiado ligeramente su sintaxis, de manera que ahora permite la inserción de datos en más de una tabla de la base de datos de forma paralela. Existen dos formas de utilizar el comando &lt;i&gt;INSERT&lt;/i&gt; multitabla: no condicional y condicional. En la forma no condicional, una cláusula compuesta &lt;i&gt;INTO&lt;/i&gt; se ejecuta cada vez que la consulta &lt;i&gt;SELECT&lt;/i&gt; devuelve un registro. En la forma condicional, las cláusulas compuestas &lt;i&gt;INTO&lt;/i&gt; figuran dentro de cláusulas &lt;i&gt;WHEN&lt;/i&gt; a partir de las que se determina si la correspondiente cláusula compuesta &lt;i&gt;INTO&lt;/i&gt; se ejecuta o no.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Una claúsula compuesta &lt;i&gt;INTO&lt;/i&gt; consiste de una o más cláusulas &lt;i&gt;INTO&lt;/i&gt;. Una cláusula &lt;i&gt;INTO&lt;/i&gt; debe especificar la tabla de la base de datos sobre la que se van a insertar los datos. Esta cláusula no admite alias. La cláusula &lt;i&gt;INTO&lt;/i&gt; tambien proporciona el valor del los campos a ser insertados mediante la cláusula &lt;i&gt;VALUES&lt;/i&gt;. La expresiones usadas en la cláusula &lt;i&gt;VALUE&lt;/i&gt; pueden tratarse de cualquier expresión permitida, pero siempre debe hacer referencia a columnas devueltas por la consulta &lt;i&gt;SELECT&lt;/i&gt; incluida en el &lt;i&gt;INSERT&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Veamos un &lt;span style="font-weight: bold;"&gt;ejemplo de la forma no condicional&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 90%;"&gt;&lt;pre&gt;INSERT ALL&lt;br /&gt; INTO productos&lt;br /&gt; VALUES (producto_id, producto, cantidad)&lt;br /&gt; INTO ventas&lt;br /&gt; VALUES (cliente_id, producto_id, cantidad, total)&lt;br /&gt;SELECT cliente_id,&lt;br /&gt;      producto_id,&lt;br /&gt;      producto,&lt;br /&gt;      SUM(cantidad) cantidad,&lt;br /&gt;      SUM(precio) total&lt;br /&gt;FROM   pedidos, lineas_pedidos&lt;br /&gt;WHERE  pedidos.pedido_id = lineas_pedidos.pedido_id&lt;br /&gt;GROUP BY cliente_id, producto_id, producto;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En el ejemplo vemos como la sentencia &lt;i&gt;INSERT&lt;/i&gt; se utiliza para insertar los valores &lt;i&gt;producto_id, producto&lt;/i&gt; y &lt;i&gt;cantidad&lt;/i&gt; en la tabla &lt;i&gt;productos&lt;/i&gt; y los valores &lt;i&gt;cliente_id, producto_id, cantidad&lt;/i&gt; y &lt;i&gt;total&lt;/i&gt; en la tabla &lt;i&gt;ventas&lt;/i&gt;, todo ello utilizando una sola sentencia SQL o PL/SQL. Resulta obvio comentar que la sentencia que he utilizado como ejemplo, podría escribirse de forma alternativa mediante el empleo de dos sentencias &lt;i&gt;INSERT&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;La forma condicional de las sentencias SQL o PLSQL &lt;i&gt;INSERT&lt;/i&gt; multitabla&lt;/span&gt; dispone, a su vez, de dos opciones:&lt;br /&gt;&lt;br /&gt;1) &lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Forma condicional &lt;/span&gt;&lt;i style="font-weight: bold; color: rgb(0, 0, 153);"&gt;FIRST&lt;/i&gt;, sólo se insertan los valores especificados en la primera condicion verdadera.&lt;br /&gt;&lt;br /&gt;2) &lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Forma condicional &lt;/span&gt;&lt;i style="font-weight: bold; color: rgb(0, 0, 153);"&gt;ALL&lt;/i&gt;, se insertan los valores especificados en todas las condiciones verdaderas.&lt;br /&gt;&lt;br /&gt;La sintaxis sería:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;INSERT [ALL/FIRST]&lt;br /&gt;WHEN &lt;condición&gt; THEN&lt;br /&gt; INTO &lt;tabla&gt; (col, col, ...) VALUES (val, val, ...)&lt;br /&gt; .....&lt;br /&gt; INTO &lt;tabla&gt; (col, col, ...) VALUES (val, val, ...)&lt;br /&gt;WHEN &lt;condición&gt; THEN&lt;br /&gt; INTO &lt;tabla&gt; (col, col, ...) VALUES (val, val, ...)&lt;br /&gt; .....&lt;br /&gt; INTO &lt;tabla&gt; (col, col, ...) VALUES (val, val, ...)&lt;br /&gt;ELSE&lt;br /&gt; INTO &lt;tabla&gt; (col, col, ...) VALUES (val, val, ...)&lt;br /&gt; .....&lt;br /&gt; INTO &lt;tabla&gt; (col, col, ...) VALUES (val, val, ...)&lt;br /&gt;SELECT ...;&lt;/tabla&gt;&lt;/tabla&gt;&lt;/condición&gt;&lt;/tabla&gt;&lt;/condición&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;Ventajas de las sentencias INSERT multitabla en SQL y PL/SQL&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;1) Eliminan la necesidad de utilizar múltiples sentencias &lt;i&gt;INSERT ... SELECT&lt;/i&gt; para añadir registros en varias tablas de la base de datos.&lt;br /&gt;&lt;br /&gt;2) Elimina la necesidad de utilizar un procedimiento PL/SQL para realizar diferentes inserciones de registros dependiendo de diferentes condiciones lógicas. Esto ya se puede realizar con una única sentencia SQL del tipo &lt;i&gt;INSERT ... SELECT&lt;/i&gt;, mediante el uso de la cláusula &lt;i&gt;WHEN&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;3) &lt;span style="font-weight: bold;"&gt;Mejora el rendimiento significativamente&lt;/span&gt; ya que la consulta &lt;i&gt;SELECT&lt;/i&gt; correspondiente, al existir un sólo comando &lt;i&gt;INSERT&lt;/i&gt;, sólo tienen que ejecutarse una vez, en lugar de tener que repetirse su ejecución en cada &lt;i&gt;INSERT&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Artículos relacionados&lt;/span&gt;: &lt;a href="http://www.plsql.biz/2007/09/la-clasula-with-en-sql-y-plsql.html"&gt;La nueva cláusula WITH de la versión 9i de la base de datos Oracle&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-1786201000898115007?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/i4YZjgoGYeA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/1786201000898115007/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=1786201000898115007&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/1786201000898115007?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/1786201000898115007?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/i4YZjgoGYeA/sql-y-plsql-la-sentencia-insert.html" title="SQL y PL/SQL - La sentencia INSERT multitabla de Oracle 9i" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp1.blogger.com/_ObxKtfPuuSQ/RzCdeWCmExI/AAAAAAAABqo/LbNQoiiixMs/s72-c/PLSQL-Vaca-Mujiendo.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.plsql.biz/2007/11/sql-y-plsql-la-sentencia-insert.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkcMRHozfyp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-8780952151400758824</id><published>2007-10-12T14:48:00.002+02:00</published><updated>2009-04-03T12:21:25.487+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:21:25.487+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Oracle lanza la base de datos Oracle 11g</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/q2Y76kSRrwC-9nKW7xKlssAMLTo/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/q2Y76kSRrwC-9nKW7xKlssAMLTo/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/q2Y76kSRrwC-9nKW7xKlssAMLTo/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/q2Y76kSRrwC-9nKW7xKlssAMLTo/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_ObxKtfPuuSQ/Rw-ooK_5opI/AAAAAAAABmY/VmNd1Mp8iCg/s1600-h/Base-de-Datos-Oracle-11g.gif"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://bp2.blogger.com/_ObxKtfPuuSQ/Rw-ooK_5opI/AAAAAAAABmY/VmNd1Mp8iCg/s200/Base-de-Datos-Oracle-11g.gif" alt="SQL y PL/SQL - Oracle ha lanzado la base de datos Oracle 11g" id="BLOGGER_PHOTO_ID_5120496709444149906" border="0" /&gt;&lt;/a&gt;Oracle ha lanzado la base de datos Oracle 11g, disponible globalmente sobre plataforma Linux, y que ha sido especialmente desarrollada para ayudar a los clientes a abordar los retos derivados de un entorno empresarial cada vez más cambiante y competitivo, la necesidad de manejar grandes volúmenes de datos y la posibilidad de ofrecer una mayor calidad de servicio y reducir a la vez los costes tecnológicos asociados.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;La base de datos Oracle Database 11g proporciona nuevas funcionalidades que garantizan un alto rendimiento, una alta escalabilidad, fiabilidad y seguridad, permitiendo el uso de plataformas grid y asegurando altos niveles de calidad de servicio e incrementos de la flexibilidad de negocio reduciendo además los costes de explotación. Con la base de datos Oracle 11g los clientes pueden resolver los problema más exigentes en todas las áreas, incluyendo aplicaciones transaccionales, de inteligencia de negocio y de gestión de contenidos.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Nuevas funcionalidades de la base de datos Oracle 11g&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Secure Files&lt;/span&gt;: gestión eficiente y segura de todo tipo de datos. Permite la &lt;span style="font-weight: bold;"&gt;gestión de todo tipo de datos&lt;/span&gt;, incluyendo &lt;span style="font-weight: bold;"&gt;imágenes&lt;/span&gt;, &lt;span style="font-weight: bold;"&gt;ficheros de texto&lt;/span&gt; o tipos avanzados de datos, como &lt;span style="font-weight: bold;"&gt;XML&lt;/span&gt;, &lt;span style="font-weight: bold;"&gt;imágenes médicas&lt;/span&gt; y &lt;span style="font-weight: bold;"&gt;objetos en 3D&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Information Lifecycle Management y Oracle Advanced Compression&lt;/span&gt;: permite realizar una gestión eficaz del ciclo de vida de los datos y reduccir los costes de almacenamiento. Oracle Database 11g &lt;span style="font-weight: bold;"&gt;ha mejorado significativamente las funcionalidades de particionamiento y compresión de datos&lt;/span&gt;, con importantes novedades para mejorar la gestión del almacenamiento y del ciclo de vida de la información. La base de datos Oracle 11g también dispone de funcionalidades avanzadas de compresión de datos para información estructurada y no estructurada (&lt;a href="http://en.wikipedia.org/wiki/BLOB" target="_blank" rel="nofollow"&gt;LOBs&lt;/a&gt;), consiguiendo ratios de compresión del orden de 2 ó 3.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Oracle Real Application Testing&lt;/span&gt;: permite reducir el tiempo, el riesgo y los costes derivados de la realización de cambios y mejoras. Esto es debido a que Oracle 11g &lt;span style="font-weight: bold;"&gt;permite realizar pruebas simulando entornos de producción&lt;/span&gt; con numerosos usuarios interactuando con la base de datos.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Oracle Active Data Guard&lt;/span&gt;: facilita el incremento del &lt;a href="http://en.wikipedia.org/wiki/Return_on_Investment" target="_blank" rel="nofollow"&gt;ROI&lt;/a&gt; de las plataformas utilizadas para recuperación de desastres. Con Oracle 11g se puede utilizar las bases de datos en modo &lt;i&gt;standby&lt;/i&gt;. Las bases de datos en &lt;i&gt;standby&lt;/i&gt; pueden utilizarse para generar informes, realizar backups y testear cambios y mejoras realizados en los sistemas de producción, proporcionando por lo tanto un ROI mayor cuando se invierte en sistemas para la recuperación de desastres (&lt;i&gt;Disaster Recovery&lt;/i&gt;).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Oracle Total Recall&lt;/span&gt;: permite realizar un rastreo eficaz de cambios, asegurando el cumplimiento de normativas de seguridad. Esta funcionalidad permite el acceso transparente a los datos en momentos del tiempo configurados por el administrador. &lt;i&gt;Oracle Total Recall&lt;/i&gt; permite la realización sencilla de operaciones de rastreo de cambios, auditoría de modificaciones y análisis para el cumplimiento de regulaciones de seguridad.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Arquitectura de máxima disponibilidad&lt;/span&gt;: Oracle Database 11g ofrece importantes innovaciones para garantizar la disponibilidad de los datos ante paradas planificadas y no planificadas. Se incluyen mejoras para conseguir &lt;span style="font-weight: bold;"&gt;mejorar los tiempos de &lt;i&gt;backup&lt;/i&gt; y recuperación&lt;/span&gt; de grandes volúmenes de información y funcionalidades de &lt;i&gt;hot patching&lt;/i&gt; que mejoran la disponibilidad de los sistemas al &lt;span style="font-weight: bold;"&gt;permitir la aplicación de parches sin necesidad de apagar las bases de datos&lt;/span&gt;. El nuevo asistente &lt;i&gt;Data Recovery Advisor&lt;/i&gt; ayuda a los administradores a reducir de manera muy significativa los tiempos de parada ofreciendo la automatización de muchas de las tareas asociadas, incluyendo tanto el diagnóstico de los problemas, como el establecimiento del plan de recuperación.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Transparent Encryption&lt;/span&gt;: encriptación transparente de los datos. Con la base de datos Oracle 11g &lt;span style="font-weight: bold;"&gt;aumentan las funcionalidades de encriptación de datos&lt;/span&gt;, incluyendo no sólo la encriptación de columnas, sino también la encriptación de tablas completas, índices y tipos de dato LOB. Todo esto implica una seguridad de los datos mayor.&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Gestión de datos XML&lt;/span&gt;: Oracle Database 11g mejora de manera significativa las funcionalidades de XML DB. Se incluye el &lt;span style="font-weight: bold;"&gt;soporte a formatos binarios de XML&lt;/span&gt; (&lt;i&gt;Binary XML&lt;/i&gt;), una importante innovación que proporciona un rendimiento superior en la gestión de datos &lt;a href="http://es.wikipedia.org/wiki/XML" target="_blank" rel="nofollow"&gt;XML&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;&lt;/span&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Cubos OLAP embebidos&lt;/span&gt;: Oracle Database 11g también proporciona innovaciones importantes en el ámbito de los &lt;span style="font-style: italic;"&gt;Data Warehouses&lt;/span&gt;. Los &lt;a href="http://es.wikipedia.org/wiki/Cubos_OLAP" target="_blank" rel="nofollow"&gt;cubos OLAP&lt;/a&gt; ahora pueden comportarse como &lt;a href="http://www.plsql.biz/2007/06/vistas-materializadas-materialized.html"&gt;vistas materializadas en la base de datos&lt;/a&gt;, permitiendo que los desarrolladores puedan utilizar el estándar SQL y PLSQL para realizar operaciones de consulta, pero manteniendo los beneficios en el rendimiento derivados del uso de cubos OLAP.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Mejoras en los pool de conexiones y en los caches de de resultados&lt;/span&gt;: la base de datos Oracle 11g ofrece nuevas funcionalidades para &lt;a href="http://www.plsql.biz/2006/08/bucles-y-problemas-de-rendimiento.html"&gt;mejorar los tiempos de respuesta de las aplicaciones, mejorando el rendimiento&lt;/a&gt;. Con &lt;span style="font-style: italic;"&gt;Connection Pooling&lt;/span&gt; y &lt;span style="font-style: italic;"&gt;Query Results Cache&lt;/span&gt; los resultados de las queries más utilizadas son almacenadas y reutilizadas de manera transparente a las aplicaciones, mejorando los tiempos de respuesta y haciendo un uso más efectivo de los recursos hardware disponibles.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;&lt;/span&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Mejoras en la automatización y en la auto administración de la base de datos&lt;/span&gt;: Con el objetivo de reducir los costes de gestión de los sistemas e incrementar el rendimiento, la disponibilidad, la escalabilidad y la seguridad, la base de datos Oracle 11g introduce un conjunto de nuevas funcionalidades en la línea de la auto administración de la base de datos. Estas funcionalides incluyen asistentes para reducir las tareas de particionamiento y ciclo de vida de los datos, funcionalidades que permiten la o&lt;span style="font-weight: bold;"&gt;ptimización automática de sentencias SQL y PL/SQL&lt;/span&gt;  o un nuevo &lt;span style="font-style: italic;"&gt;Support Workbench&lt;/span&gt; que proporciona una &lt;span style="font-weight: bold;"&gt;interfaz simple para visualizar incidencias en la base de datos&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;Facilita el desarrollo de aplicaciones:&lt;/span&gt; Oracle Database 11g incorpora un nuevo compilador Java &lt;span style="font-style: italic;"&gt;just-in-time&lt;/span&gt; que proporciona un &lt;span style="font-weight: bold;"&gt;alto rendimiento en la ejecución de procedimientos almacenados en Java&lt;/span&gt; sin necesidad de utilizar compiladores de terceros. También se incorpora una integración nativa con &lt;span style="font-style: italic;"&gt;Visual Studio 2005&lt;/span&gt; para facilitar el desarrollo de aplicaciones &lt;span style="font-style: italic;"&gt;.NET&lt;/span&gt; con Oracle.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-8780952151400758824?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/Jh4IlpvJeDw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/8780952151400758824/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=8780952151400758824&amp;isPopup=true" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/8780952151400758824?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/8780952151400758824?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/Jh4IlpvJeDw/oracle-lanza-la-base-de-datos-oracle.html" title="Oracle lanza la base de datos Oracle 11g" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp2.blogger.com/_ObxKtfPuuSQ/Rw-ooK_5opI/AAAAAAAABmY/VmNd1Mp8iCg/s72-c/Base-de-Datos-Oracle-11g.gif" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://www.plsql.biz/2007/10/oracle-lanza-la-base-de-datos-oracle.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkcHQHY6cCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-8759865861216332665</id><published>2007-09-25T18:35:00.001+02:00</published><updated>2009-04-03T12:20:31.818+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:20:31.818+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><title>La claúsula WITH en SQL y PL/SQL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/OHuLIABw7-X87SeOvJydPBj3RKc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/OHuLIABw7-X87SeOvJydPBj3RKc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/OHuLIABw7-X87SeOvJydPBj3RKc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/OHuLIABw7-X87SeOvJydPBj3RKc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a href="http://bp0.blogger.com/_ObxKtfPuuSQ/Rvk6SUYwMrI/AAAAAAAABhA/LJ-mR9a8HPw/s1600-h/PLSQL-Atencion-del-Programador.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_ObxKtfPuuSQ/Rvk6SUYwMrI/AAAAAAAABhA/LJ-mR9a8HPw/s200/PLSQL-Atencion-del-Programador.jpg" border="0" alt="Esposa reclama la atención del programador PL/SQL con la claúsula WITH del SQL" id="BLOGGER_PHOTO_ID_5114182938240496306" /&gt;&lt;/a&gt;La versión 9i de las bases de datos Oracle permite el uso de la claúsula &lt;i&gt;WITH&lt;/i&gt; en SQL y PLSQL. Este comando permite reusar una consulta &lt;i&gt;SELECT&lt;/i&gt; cuando esta hay que utilizarla más de una vez en una sentencia o consulta SQL compleja. Los resultados de la consulta definida en la claúsula &lt;i&gt;WITH&lt;/i&gt; son almacenados en una tabla temporal pudiendo de esta forma mejorar el rendimiento de la sentencia principal.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Aunque no siempre conseguiremos mejorar el rendimiento utilizando la claúsula &lt;i&gt;WITH&lt;/i&gt;, lo que sin duda facilitaremos es la lectura y el mantenimiento del código PL/SQL o SQL. Dentro de la claúsula &lt;i&gt;WITH&lt;/i&gt; daremos un nombre a las consultas &lt;i&gt;SELECT&lt;/i&gt; a reutilizar (&lt;i&gt;WITH&lt;/i&gt; admite la definición de múltiples consultas con sólo separarlas por comas), dicho nombre será visible para todas las consultas definidas posteriormente dentro del mismo &lt;i&gt;WITH&lt;/i&gt;. Obviamente, también será visible para la sentencia o consulta principal.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Uso de la claúsula &lt;i&gt;WITH&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;En el siguiente ejemplo encontraremos todos las divisiones de una empresa cuyos empleados tienen un salario medio un 10 por ciento por encima del salario medio de la empresa:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size:85%;"&gt;WITH salario_division AS (&lt;br /&gt;&amp;nbsp; SELECT division,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AVG(salario) salario_medio&lt;br /&gt;&amp;nbsp; FROM&amp;nbsp;&amp;nbsp; empleados&lt;br /&gt;&amp;nbsp; GROUP BY division )&lt;br /&gt;SELECT division,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; salario_medio&lt;br /&gt;FROM&amp;nbsp;&amp;nbsp; salario_division&lt;br /&gt;WHERE&amp;nbsp; salario_medio &gt; (&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SELECT AVG(salario_medio) * 1.1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FROM&amp;nbsp;&amp;nbsp; salario_division )&lt;br /&gt;ORDER BY salario_medio;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Como se puede observar en la consulta existen dos bloques, una consulta principal y una subconsulta. Ambas necesitan realizar operaciones agrupando datos. Reescribamos la sentencia sin utilizar la claúsula &lt;i&gt;WITH&lt;/i&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size:85%;"&gt;SELECT division,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AVG(salario) salario_medio&lt;br /&gt;FROM&amp;nbsp;&amp;nbsp; empleados&lt;br /&gt;GROUP BY division&lt;br /&gt;HAVING AVG(salario) &gt; (&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SELECT AVG(salario) * 1.1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FROM&amp;nbsp;&amp;nbsp; empleados )&lt;br /&gt;ORDER BY AVG(salario);&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Comparando ambas sentencias podemos concluir que al utilizar la claúsula SQL &lt;i&gt;WITH&lt;/i&gt; y almacenar en una tabla temporal la consulta que hace el &lt;i&gt;GROUP BY&lt;/i&gt;, evitamos que se tengan que agrupar los datos más de una vez. Este hecho debe hacernos pensar que el rendimiento de la sentencia que utiliza la claúsula &lt;i&gt;WITH&lt;/i&gt; debe ser mejor que el de la sentencia que no lo utiliza.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Características de la claúsula &lt;i&gt;WITH&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;a) Sólo se puede usar en sentencias &lt;i&gt;SELECT&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;b) Cuando se define una consulta con el mismo nombre de una tabla existente en la base de datos Oracle, puesto que el analizador sintáctico o &lt;i&gt;parser&lt;/i&gt; (ver &lt;a href="http://www.plsql.biz/2007/04/fases-durante-el-procesamiento-de-una.html" title="Fases durante el procesamiento de una sentencia PLSQL"&gt;fases en la ejecución de una sentencia SQL&lt;/a&gt;) de las sentencias SQL o PLSQL busca de dentro a fuera, el nombre dentro de la claúsula &lt;i&gt;WITH&lt;/i&gt; tendrá prioridad frente al nombre de la tabla.&lt;br /&gt;&lt;br /&gt;c) Puede contener más de una consulta. Cada consulta se separa mediante comas. Las consultas definidas después de otras consultas pueden utilizar las definiciones previas.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Artículos relacionados&lt;/b&gt;: &lt;br /&gt;&lt;a href="http://www.plsql.biz/2007/06/vistas-materializadas-materialized.html" title="PL/SQL vistas materializadas o materializaed views"&gt;Vistas materializadas o &lt;i&gt;materializaed views&lt;/i&gt;&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://www.plsql.biz/2006/12/cursores-en-plsql.html" title="Uso de cursores en PLSQL"&gt;Cursores PL/SQL&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://www.plsql.biz/2006/09/tablas-externas.html" title="Uso de tablas externas en PLSQL"&gt;Tablas externas en el SQL de Oracle&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-8759865861216332665?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/EfKd3344sgU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/8759865861216332665/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=8759865861216332665&amp;isPopup=true" title="3 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/8759865861216332665?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/8759865861216332665?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/EfKd3344sgU/la-clasula-with-en-sql-y-plsql.html" title="La claúsula WITH en SQL y PL/SQL" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp0.blogger.com/_ObxKtfPuuSQ/Rvk6SUYwMrI/AAAAAAAABhA/LJ-mR9a8HPw/s72-c/PLSQL-Atencion-del-Programador.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://www.plsql.biz/2007/09/la-clasula-with-en-sql-y-plsql.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkcFR3c8cCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-3080758525546023768</id><published>2007-08-31T15:14:00.003+02:00</published><updated>2009-04-03T12:20:16.978+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:20:16.978+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><title>Hints avanzados en PL/SQL para forzar la forma de acceder a una tabla</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/40o1jpKLwX7r6X4lO8Yd5nCJk7E/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/40o1jpKLwX7r6X4lO8Yd5nCJk7E/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/40o1jpKLwX7r6X4lO8Yd5nCJk7E/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/40o1jpKLwX7r6X4lO8Yd5nCJk7E/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_ObxKtfPuuSQ/RtgtVM77LpI/AAAAAAAABbg/qQFdZPCNs10/s1600-h/PLSQL-Hints-Chiste.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_ObxKtfPuuSQ/RtgtVM77LpI/AAAAAAAABbg/qQFdZPCNs10/s200/PLSQL-Hints-Chiste.gif" border="0" alt="Chiste en hints avanzados en PLSQL, SQL y PL/SQL" id="BLOGGER_PHOTO_ID_5104880019897527954" /&gt;&lt;/a&gt;Ya he hablado anteriormente acerca de los &lt;a href="http://www.plsql.biz/2007/01/hints-en-plsql-para-determinar-el-mtodo.html"&gt;&lt;i&gt;hints&lt;/i&gt; PLSQL más comunes (&lt;i&gt;FULL, ROWID, INDEX, NO_INDEX&lt;/i&gt;) para forzar el método de accesso a una tabla&lt;/a&gt; Oracle. En este artículo voy a presentar algunos &lt;i&gt;hints&lt;/i&gt; más de este tipo, que se utilizan menos frecuentemente, pero no por ello menos útiles.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 51, 153); font-style: italic;"&gt;CLUSTER (nombre_de_tabla)&lt;/span&gt;: Fuerza el accesso a la tabla indicada utilizando un índice de tipo &lt;i&gt;cluster&lt;/i&gt;. Los índices de este tipo se utilizan para localizar registros que comparten valores comunes de una forma rápida. La clave del índice &lt;i&gt;cluster&lt;/i&gt; puede estar constituida por una o más columnas de la tabla. Los registros de la tabla son agrupados según la clave y almacenados físicamente juntos en el disco duro. &lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Es decir, suponiendo que tenemos una tabla de empleados, si definimos un índice &lt;i&gt;cluster&lt;/i&gt; basado en el país del empleado, cuando un usuario inserta un nuevo empleado, el índice &lt;i&gt;cluster&lt;/i&gt; asegura que el nuevo empleado se almacene en el mismo bloque de datos donde figuran los empleados correspondientes a ese país.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Nota&lt;/span&gt;: Pueden definirse &lt;i&gt;clusters&lt;/i&gt; en los que se almacene más de una tabla. Obviamente la única limitación es que dicho &lt;i&gt;cluster&lt;/i&gt; se defina sobre columnas con valores comunes.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 51, 153); font-style: italic;"&gt;HASH (nombre_de_tabla)&lt;/span&gt;: Fuerza a que la tabla se combine utilizando un &lt;a href="http://es.wikipedia.org/wiki/Hash" target="_blank"&gt;algoritmo de &lt;i&gt;hash&lt;/i&gt;&lt;/a&gt;. Sólo aplica a los &lt;i&gt;clusters&lt;/i&gt; tipo &lt;i&gt;hash&lt;/i&gt; que utilizan una función &lt;i&gt;hash&lt;/i&gt; para calcular la localización de los registros de una tabla Oracle. En este caso, los registros son almacenados en el disco duro según sea el valor devuelto por la función &lt;i&gt;hash&lt;/i&gt;. La función se utiliza para localizar los registros sin necesidad de utilizar el tiempo de entrada/salida o de CPU que requiere la búsqueda a través de un índice normal.&lt;br /&gt;&lt;br /&gt;La unión de tablas a través de un &lt;i&gt;cluster hush&lt;/i&gt; es recomendable para tablas con un gran número de registros ya que, para dichas tablas, los índices pueden estar estructurados en mucho niveles, siendo el acceso al índice más lento.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic; color: rgb(51, 51, 153);"&gt;HASH_AJ (nombre_de_tabla)&lt;/span&gt;: Transforma una &lt;i&gt;subquery NOT IN&lt;/i&gt; en una anti-unión de tipo &lt;i&gt;hash&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Una anti-únión (&lt;i&gt;anti-join&lt;/i&gt;) es una forma de unir tablas que utiliza una lógica inversa. En vez de devolver registros cuando ambas partes cumplen con una condición, una anti-unión devuelve aquellos registros de una de las partes que no están en la otra (exactamente lo que hace una &lt;i&gt;subquery NOT IN&lt;/i&gt;).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Nota explicativa&lt;/span&gt;: Una &lt;i&gt;subquery&lt;/i&gt; no es más que una sentencia &lt;i&gt;SELECT&lt;/i&gt; que está dentro de otra sentencia SQL o PL/SQL.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-3080758525546023768?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/S4MGo6fxZKs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/3080758525546023768/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=3080758525546023768&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3080758525546023768?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3080758525546023768?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/S4MGo6fxZKs/hints-avanzados-en-plsql-para-forzar-la.html" title="Hints avanzados en PL/SQL para forzar la forma de acceder a una tabla" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp1.blogger.com/_ObxKtfPuuSQ/RtgtVM77LpI/AAAAAAAABbg/qQFdZPCNs10/s72-c/PLSQL-Hints-Chiste.gif" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.plsql.biz/2007/08/hints-avanzados-en-plsql-para-forzar-la.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU4DR3g8eCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-5966536555503238969</id><published>2007-07-26T19:06:00.001+02:00</published><updated>2009-04-03T12:19:36.670+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:19:36.670+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><title>El optimizador PL/SQL basado en normas (Rule-Based Optimizer)</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/I0dVRcnVQm4f27ejQhl7ZQXCfMI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/I0dVRcnVQm4f27ejQhl7ZQXCfMI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/I0dVRcnVQm4f27ejQhl7ZQXCfMI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/I0dVRcnVQm4f27ejQhl7ZQXCfMI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_ObxKtfPuuSQ/RqjeAqysmlI/AAAAAAAABWQ/7XgcSwG5_kU/s1600-h/nuevos-ordenadores-PL-SQL.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_ObxKtfPuuSQ/RqjeAqysmlI/AAAAAAAABWQ/7XgcSwG5_kU/s200/nuevos-ordenadores-PL-SQL.jpg" border="0" alt="Diagrama del optimizador Oracle PL/SQL basado en normas" id="BLOGGER_PHOTO_ID_5091563481810639442" /&gt;&lt;/a&gt;En este artículo voy a mencionar algunas de las características del &lt;span style="font-weight:bold;"&gt;optimizador PL/SQL basado en normas&lt;/span&gt; (&lt;i&gt;Rule-Based Optimizer&lt;/i&gt;). Lo primero que quiero mencionar es que Oracle recomienda utilizar el optimizador PLSQL basado en costes (&lt;i&gt;cost-based optimizer&lt;/i&gt;), no obstante, en algunos casos, el hecho de tener que activar las estadísticas de la base de datos para poder utilizar este último optimizador, puede hacer que resulte interesante utilizar el optimizador basado en normas y dejar las estadísticas desactivadas para no afectar al &lt;a href="http://www.plsql.biz/2006/08/bucles-y-problemas-de-rendimiento.html"&gt;rendimiento de la base de datos&lt;/a&gt;.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;El optimizador PLSQL basado en normas utiliza siempre que puede los índices, incluso cuando las tablas son pequeñas o cuando el número de registros que devuelve la sentencia &lt;i&gt;SELECT&lt;/i&gt; es un porcentaje elevado con respecto al número total de registros de la tabla, casos para los que es mejor realizar un escaneado total (&lt;i&gt;full scan&lt;/i&gt;) ya que la respuesta es más rápida (mejora el rendimiento). Esto es debido a que el optimizador basado en normas no hace uso de valores estadísticos, tales como el &lt;a href="http://www.plsql.biz/2007/06/identificacin-de-tablas-y-columnas-en.html"&gt;número total de registros de una tabla&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;El optimizador PL/SQL basado en normas hace uso del siguiente orden de prioridades para determinar cual va a ser la forma de acceder a las tablas y determinar finalmente cual va a ser el &lt;a href="http://www.plsql.biz/2007/05/cmo-obtener-el-plan-de-ejecucin-de-una.html"&gt;plan de ejecución&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size:85%;"&gt;Prio&amp;nbsp Forma de acceso&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;1&amp;nbsp Single row by ROWID&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;2&amp;nbsp Single row by cluster join&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;3&amp;nbsp Single row by hash cluster key with unique or&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; primary key&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;4&amp;nbsp Single row by unique or primary key&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;5&amp;nbsp Cluster join&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;6&amp;nbsp Hash cluster key&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;7&amp;nbsp Indexed cluster key&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;8&amp;nbsp Composite index&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;9&amp;nbsp Single-column index&lt;br /&gt;&amp;nbsp;&amp;nbsp;10&amp;nbsp Bounded range search on indexed column&lt;br /&gt;&amp;nbsp;&amp;nbsp;11&amp;nbsp Unbounded range search on indexed column&lt;br /&gt;&amp;nbsp;&amp;nbsp;12&amp;nbsp Sort-merge join&lt;br /&gt;&amp;nbsp;&amp;nbsp;13&amp;nbsp MAX or MIN of indexed column&lt;br /&gt;&amp;nbsp;&amp;nbsp;14&amp;nbsp ORDER BY on indexed column&lt;br /&gt;&amp;nbsp;&amp;nbsp;15&amp;nbsp Full table scan&lt;/blockquote&gt;&lt;br /&gt;Los distintos métodos de acceso los he dejado en inglés, ya que es bastante complicado traducir esta terminología. En el presente artículo no voy a explicar cuales son las diferencias existentes entre las distintas formas de acceso. No obstante, en sucesivos artículos pondré algunos ejemplos que permitirán diferenciar estos conceptos.&lt;br /&gt;&lt;br /&gt;Siguiendo con el tema que concierne a este &lt;i&gt;post&lt;/i&gt;, el optimizador basado en normas analiza la sintaxis de la sentencia SQL para establecer los distintos métodos de acceso a las tablas. Básicamente lo que hace es determinar todas las formas de acceso posibles y escoger aquella que tiene una prioridad menor.&lt;br /&gt;&lt;br /&gt;Este esquema siempre asume que un escaneado total (&lt;i&gt;full scan&lt;/i&gt;) es el peor método de accesso (prioridad 15). Sin embargo, ya he mencionado al principio del artículo que esto no siempre es verdad.&lt;br /&gt;&lt;br /&gt;Estos métodos de acceso, así como otros adicionales,  están también disponibles para el optimizador PL/SQL basado en costes. Sin embargo, este optimizador ignora las prioridades, y determina el coste esperado de ejecución de la sentencia SQL para cada uno de las formas de acceso posibles basándose en las estadísticas, escogiendo después aquella forma de acceso con el menor coste estimado. Muchas funcionalidades del Oracle, como los &lt;i&gt;hash joins&lt;/i&gt;, &lt;i&gt;star queries&lt;/i&gt; e histogramas, sólo están disponibles para el optimizador PLSQL basado en costes.&lt;br /&gt;&lt;br /&gt;Artículos relacionados: &lt;br /&gt;- &lt;a href="http://www.plsql.biz/2006/12/hints-en-plsql-para-el-modo-de.html"&gt;Hints PLSQL para determinar el modo de optimización&lt;/a&gt;.&lt;br /&gt;- &lt;a href="http://www.plsql.biz/2007/01/hints-en-plsql-para-determinar-el-mtodo.html"&gt;Hints PL/SQL para forzar la forma de accesso&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-5966536555503238969?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/uV8l1L82e-E" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/5966536555503238969/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=5966536555503238969&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5966536555503238969?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5966536555503238969?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/uV8l1L82e-E/el-optimizador-plsql-basado-en-normas.html" title="El optimizador PL/SQL basado en normas (Rule-Based Optimizer)" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp0.blogger.com/_ObxKtfPuuSQ/RqjeAqysmlI/AAAAAAAABWQ/7XgcSwG5_kU/s72-c/nuevos-ordenadores-PL-SQL.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.plsql.biz/2007/07/el-optimizador-plsql-basado-en-normas.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU4ASHo4fCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-7124581046706283988</id><published>2007-07-12T18:19:00.001+02:00</published><updated>2009-04-03T12:19:09.434+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:19:09.434+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><category scheme="http://www.blogger.com/atom/ns#" term="Utilidades PLSQL" /><title>Análisis de la salida del comando TKPROF en PL/SQL y SQL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ss23ZaJN9s6SDUwAuW49caD06fw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ss23ZaJN9s6SDUwAuW49caD06fw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ss23ZaJN9s6SDUwAuW49caD06fw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ss23ZaJN9s6SDUwAuW49caD06fw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_ObxKtfPuuSQ/RpZbfcs0D8I/AAAAAAAABTg/IO9az1uGndE/s1600-h/PLSQL-PC-Colgado.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_ObxKtfPuuSQ/RpZbfcs0D8I/AAAAAAAABTg/IO9az1uGndE/s200/PLSQL-PC-Colgado.jpg" border="0" alt="PC colgado rtas ejecutar un comando TKPROF para analizar la ejecución de una sentencia PL/SQL" id="BLOGGER_PHOTO_ID_5086353424999780290" /&gt;&lt;/a&gt;Ya hemos hablado en otro artículo acerca de las &lt;a href="http://www.plsql.biz/2007/06/uso-del-comando-tkprof-para-formatear.html"&gt;opciones y parámetros del comando &lt;i&gt;TKPROF&lt;/i&gt;&lt;/a&gt; y de su uso para analizar los fichero de trazado PLSQL. Ahora es el momento de escribir acerca de cómo interpretar la salida de dicho comando. La salida del comando &lt;i&gt;TKPROF&lt;/i&gt; muestra las estadíticas resultantes de la ejecución de una sentencia SQL o PLSQL agrupadas por fases de procesamiento. En el artículo "&lt;a href="http://www.plsql.biz/2007/04/fases-durante-el-procesamiento-de-una.html"&gt;Pasos seguidos durante la ejecución de una sentencia SQL&lt;/a&gt;", ya hice una exposición detallada de lo que acontece en cada una de estas fases, no obstante, a continuación, voy a realizar un pequeño resumen.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Fases de procesamiento analizadas por el comando &lt;i&gt;TKPROF&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 51, 153);"&gt;Fase de parsing (&lt;/span&gt;&lt;i style="font-weight: bold; color: rgb(51, 51, 153);"&gt;parse&lt;/i&gt;&lt;span style="font-weight: bold; color: rgb(51, 51, 153);"&gt;)&lt;/span&gt;: Durante este paso se genera el &lt;a href="http://www.plsql.biz/2007/05/cmo-obtener-el-plan-de-ejecucin-de-una.html"&gt;plan de ejecución de la sentencia SQL o PL/SQL&lt;/a&gt;, se comprueban los permisos de ejecución y se verifica la existencia de los objetos referenciados en dicha sentencia (tablas, vistas, columnas, etcétera).&lt;br /&gt;&lt;br /&gt;El &lt;i&gt;parsing&lt;/i&gt; puede ser duro (&lt;i&gt;hard&lt;/i&gt;) o blando (&lt;i&gt;soft&lt;/i&gt;). El &lt;i&gt;parsing&lt;/i&gt; duro se realiza cuando se genera el plan de ejecución, incluyendo la &lt;a href="http://www.plsql.biz/search/label/Optimizaci%C3%B3n%20y%20tuning%20de%20bases%20de%20datos"&gt;optimización&lt;/a&gt; del mismo, y dicho plan de ejecución se almacena en la librería &lt;i&gt;cache&lt;/i&gt;. El &lt;i&gt;parsing&lt;/i&gt; blando hace referencia a que cuando la sentencia SQL o PLSQL es enviada al &lt;i&gt;kernel&lt;/i&gt; para generar el plan de ejecución, el &lt;i&gt;kernel&lt;/i&gt; encuentra dicho plan en la librería &lt;i&gt;cache&lt;/i&gt;, por lo que no es necesario generar el plan de ejecución, y sólo se necesitan verificar los permisos de acceso a los objetos Oracle utilizados en la sentencia.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 51, 153);"&gt;Fase de ejecución (&lt;/span&gt;&lt;i style="font-weight: bold; color: rgb(51, 51, 153);"&gt;execute&lt;/i&gt;&lt;span style="font-weight: bold; color: rgb(51, 51, 153);"&gt;)&lt;/span&gt;: Este paso se corresponde con la ejecución propiamente dicha de la sentencia SQL o PLSQL por parte del servidor de la base de datos Oracle. Para las sentencias &lt;i&gt;INSERT&lt;/i&gt;, &lt;i&gt;UPDATE&lt;/i&gt; y &lt;i&gt;DELETE&lt;/i&gt;, este paso implica la modificación de los datos y, a veces, su ordenación. Para la sentencia &lt;i&gt;SELECT&lt;/i&gt;, esta fase supone la identificación de los registros seleccionados.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 51, 153);"&gt;Fase de fetching (&lt;/span&gt;&lt;i style="font-weight: bold; color: rgb(51, 51, 153);"&gt;fetch&lt;/i&gt;&lt;span style="font-weight: bold; color: rgb(51, 51, 153);"&gt;)&lt;/span&gt;: Durante este paso el servidor Oracle devuelve los registros resultantes de la ejecución de una sentencia &lt;i&gt;SELECT&lt;/i&gt; y, si es necesario, los ordena. Esta fase sólo afecta a las sentencias &lt;i&gt;SELECT&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Columnas del informe&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Las estadísticas entregadas por el comando &lt;i&gt;TKPROF&lt;/i&gt; se organizan en siete categorías o columnas:&lt;br /&gt;&lt;br /&gt;&lt;i style="font-weight: bold; color: rgb(51, 51, 153);"&gt;Count&lt;/i&gt;: Número de veces que una sentencia SQL o PL/SQL fue analizada según fases. A la hora de interpretar los resultados que aparecen en las otras columnas del informe generado por el comando &lt;i&gt;TKPROF&lt;/i&gt;, es importante comprobar si esta columna toma valores mayores que uno, ya que si hemos utilizado la opción &lt;i&gt;AGGREGATE=YES&lt;/i&gt;, el comando &lt;i&gt;TKPROF&lt;/i&gt; agrupará la ejecución de todas las sentencias SQL o PLSQL idénticas en una sola tabla de resultados.&lt;br /&gt;&lt;br /&gt;&lt;i style="font-weight: bold; color: rgb(51, 51, 153);"&gt;CPU&lt;/i&gt;: Tiempo total en segundos de uso de CPU según fases.&lt;br /&gt;&lt;br /&gt;&lt;i style="font-weight: bold; color: rgb(51, 51, 153);"&gt;Elapsed&lt;/i&gt;: Lapso de tiempo total en segundos según fases.&lt;br /&gt;&lt;br /&gt;&lt;i style="font-weight: bold; color: rgb(51, 51, 153);"&gt;Disk&lt;/i&gt;: Número de bloques de datos físicamente leídos desde disco según fases.&lt;br /&gt;&lt;br /&gt;&lt;i style="font-weight: bold; color: rgb(51, 51, 153);"&gt;Query&lt;/i&gt;: Número de bloques de memoria intermedia (&lt;i&gt;buffers&lt;/i&gt;) leídos en modo consistente. Las lecturas consistentes se producen generalmente para las sentencias &lt;i&gt;SELECT&lt;/i&gt;. Una lectura consistente indica que los registros leídos que han sido modificados y que no han sido confirmados mediante el comando &lt;i&gt;COMMIT&lt;/i&gt;, tienen que reconstruirse a partir de los segmentos de &lt;i&gt;rollback&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;i style="font-weight: bold; color: rgb(51, 51, 153);"&gt;Current&lt;/i&gt;: Número de bloques de memoria intermedia (&lt;i&gt;buffers&lt;/i&gt;) leídos en modo normal (&lt;i&gt;current&lt;/i&gt;). Las lecturas en modo normal se producen generalmente para las sentencias &lt;i&gt;DML&lt;/i&gt; (sentencias de manipulación de datos).&lt;br /&gt;&lt;br /&gt;&lt;i style="font-weight: bold; color: rgb(51, 51, 153);"&gt;Rows&lt;/i&gt;: Número de registros procesados por la sentencia SQL o PL/SQL. Este valor no incluye los registros procesados por las &lt;i&gt;subqueries&lt;/i&gt; (sentencias &lt;i&gt;SELECT&lt;/i&gt; incluídas en otras sentencias SQL o PLSQL). Para las sentencias &lt;i&gt;SELECT&lt;/i&gt;, el número de registros devueltos se mostrará en la fase de &lt;i&gt;fetching&lt;/i&gt;. Para las sentencias &lt;i&gt;INSERT&lt;/i&gt;, &lt;i&gt;UPDATE&lt;/i&gt; y &lt;i&gt;DELETE&lt;/i&gt;, el número de registros procesados se mostrará en la fase de ejecución.&lt;br /&gt;&lt;br /&gt;Ejemplo de salida resultado de la ejecución del comando &lt;i&gt;TKPROF&lt;/i&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size:85%;"&gt;select categoria, trabajo, nombre, salario&lt;br /&gt;from empleados, salarios&lt;br /&gt;where salario between infsal and supsal&lt;br /&gt;order by categoria, trabajo&lt;br /&gt;&lt;br /&gt;call&amp;nbsp;&amp;nbsp;&amp;nbsp; count&amp;nbsp;&amp;nbsp; cpu elapsed disk query current rows&lt;br /&gt;------- -----&amp;nbsp; ---- ------- ---- ----- ------- ----&lt;br /&gt;Parse&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp; 0.06&amp;nbsp;&amp;nbsp;&amp;nbsp; 0.08&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&amp;nbsp;&amp;nbsp;&amp;nbsp; 18&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;br /&gt;Execute&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp; 0.00&amp;nbsp;&amp;nbsp;&amp;nbsp; 0.00&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;br /&gt;Fetch&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp; 0.01&amp;nbsp;&amp;nbsp;&amp;nbsp; 0.01&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp; 11&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 12&amp;nbsp;&amp;nbsp; 14&lt;br /&gt;------- -----&amp;nbsp; ---- ------- ---- ----- ------- ----&lt;br /&gt;total&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3&amp;nbsp; 0.07&amp;nbsp;&amp;nbsp;&amp;nbsp; 0.09&amp;nbsp;&amp;nbsp;&amp;nbsp; 3&amp;nbsp;&amp;nbsp;&amp;nbsp; 29&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13&amp;nbsp;&amp;nbsp; 14&lt;br /&gt;&lt;br /&gt;Misses in library cache during parse: 1&lt;br /&gt;Optimizer goal: CHOOSE&lt;br /&gt;Parsing user id: 8 (SCOTT)&lt;br /&gt;&lt;br /&gt;Rows&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Execution Plan&lt;br /&gt;-------&amp;nbsp; -------------------------------------------&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp; SELECT STATEMENT&amp;nbsp;&amp;nbsp; GOAL: CHOOSE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 14&amp;nbsp;&amp;nbsp; SORT (ORDER BY)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 14&amp;nbsp;&amp;nbsp;&amp;nbsp; NESTED LOOPS&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TABLE ACCESS (FULL) OF 'SALARIOS'&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 70&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TABLE ACCESS (FULL) OF 'EMPLEADOS'&lt;/blockquote&gt;&lt;br /&gt;Obviamente, si el fichero de trazado que estamos analizando con el comando &lt;i&gt;TKPROF&lt;/i&gt;, contiene información sobre la ejecución de varias sentencias SQL o PL/SQL, la salida mostrará la información arriba indicada para cada una de las sentencias SQL que han sido analizadas.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-7124581046706283988?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/DCc7-lwPquw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/7124581046706283988/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=7124581046706283988&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7124581046706283988?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7124581046706283988?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/DCc7-lwPquw/anlisis-de-la-salida-del-comando-tkprof.html" title="Análisis de la salida del comando TKPROF en PL/SQL y SQL" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp1.blogger.com/_ObxKtfPuuSQ/RpZbfcs0D8I/AAAAAAAABTg/IO9az1uGndE/s72-c/PLSQL-PC-Colgado.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.plsql.biz/2007/07/anlisis-de-la-salida-del-comando-tkprof.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU4HRHo5eCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-3561439201904251832</id><published>2007-06-26T18:16:00.001+02:00</published><updated>2009-04-03T12:18:55.420+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:18:55.420+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Identificación de tablas y columnas en una base de datos Oracle mediante sentencias SQL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ifYrFAhqs1nGsXykitQ-Z8p6Y6U/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ifYrFAhqs1nGsXykitQ-Z8p6Y6U/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ifYrFAhqs1nGsXykitQ-Z8p6Y6U/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ifYrFAhqs1nGsXykitQ-Z8p6Y6U/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_ObxKtfPuuSQ/RoE-EKTlvwI/AAAAAAAABPI/GMuVXidwrhM/s1600-h/teclado-programacion-PLSQL.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://bp0.blogger.com/_ObxKtfPuuSQ/RoE-EKTlvwI/AAAAAAAABPI/GMuVXidwrhM/s200/teclado-programacion-PLSQL.jpg" alt="Nuevo teclado de programación PL/SQL y PLSQL" id="BLOGGER_PHOTO_ID_5080410095857549058" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Una vez que una tabla Oracle es creada utilizando el comando &lt;i&gt;CREATE TABLE&lt;/i&gt;, la estructura de dicha tabla se puede visualizar utilizando las siguientes vistas (&lt;i&gt;views&lt;/i&gt;) del sistema de la base de datos Oracle:&lt;br /&gt;&lt;br /&gt;&lt;i style="font-weight: bold; color: rgb(0, 0, 153);"&gt;DBA_TABLES&lt;/i&gt;: Muestra la información de la tabla a nivel de cabecera.&lt;br /&gt;&lt;br /&gt;&lt;i style="font-weight: bold; color: rgb(0, 0, 153);"&gt;DBA_TAB_COLUMNS&lt;/i&gt;: Muestra la información de la tabla a nivel de columna.&lt;br /&gt;&lt;br /&gt;&lt;i style="font-weight: bold; color: rgb(0, 0, 153);"&gt;DBA_TAB_PRIVS&lt;/i&gt;: Muestra los privilegios de acceso a las tablas de los usuarios de la base de datos.&lt;br /&gt;&lt;br /&gt;&lt;i style="font-weight: bold; color: rgb(0, 0, 153);"&gt;DBA_COL_PRIVS&lt;/i&gt;: Muestra los privilegios de acceso a nivel de columna. Es bastante raro tener la necesidad de definir permisos a nivel de columna, de hecho, yo nunca me he encontrado con una bases de datos Oracle que utilice esta funcionalidad.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;A nivel de cabecera, es decir en la vista &lt;span style="font-style: italic; font-weight: bold;"&gt;DBA_TABLES&lt;/span&gt;, se muestran datos como el esquema al que pertenece la tabla (columna &lt;i&gt;OWNER&lt;/i&gt;), el nombre de la tabla (&lt;i&gt;TABLE_NAME&lt;/i&gt;), el nombre del &lt;i&gt;tablespace&lt;/i&gt; donde se almacenan físicamente los datos contenidos en la tabla (&lt;i&gt;TABLESPACE_NAME&lt;/i&gt;), el número de registros que contiene la tabla (&lt;i&gt;NUM_ROWS&lt;/i&gt;), el número de bloques que ocupan físicamente los datos (&lt;i&gt;BLOCKS&lt;/i&gt;), así como otros datos estadísticos que son utilizados por el &lt;a href="http://www.plsql.biz/2006/12/hints-en-plsql-para-el-modo-de.html"&gt;optimizador basado en costes&lt;/a&gt; para generar los &lt;a href="http://www.plsql.biz/2007/05/cmo-obtener-el-plan-de-ejecucin-de-una.html"&gt;planes de ejecución de las sentencias SQL o PL/SQL&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;La vista &lt;i&gt;DBA_TABLES&lt;/i&gt; se puede unir con la vista &lt;i style="font-weight: bold;"&gt;DBA_TAB_COLUMNS&lt;/i&gt; mediante los campos &lt;i&gt;OWNER&lt;/i&gt; y &lt;i&gt;TABLE_NAME&lt;/i&gt;. Esta última vista del sistema Oracle nos dará información acerca los nombres de las columnas (&lt;i&gt;COLUMN_NAME&lt;/i&gt;), del &lt;a href="http://www.plsql.biz/2006/10/tipos-de-datos-en-plsql.html"&gt;tipo de dato SQL&lt;/a&gt; almacenado en dicha columna (&lt;i&gt;DATA_TYPE&lt;/i&gt;), de las dimensiones o tamaño de la columna (&lt;i&gt;DATA_LENGTH&lt;/i&gt;) y nos indicará si la columna permite o no tomar valores nulos o en blanco (&lt;i&gt;NULLABLE&lt;/i&gt;). También esta vista del sistema Oracle muestra algunos valores estadísticos que son utilizados por el optimizador.&lt;br /&gt;&lt;br /&gt;La vista&lt;span style="font-style: italic;"&gt; &lt;/span&gt;&lt;i style="font-weight: bold;"&gt;DBA_TAB_PRIVS&lt;/i&gt; se une de igual manera que la &lt;i&gt;DBA_TAB_COLUMNS&lt;/i&gt; con la vista &lt;i&gt;DBA_TABLES&lt;/i&gt;. Las columnas fundamentales de la vista &lt;i&gt;DBA_TAB_PRIVS&lt;/i&gt; dan información sobre cual es el usuario al que se le concede un permiso determinado (&lt;i&gt;GRANTEE&lt;/i&gt;), cual es el usuario que concedió el permiso (&lt;i&gt;GRANTOR&lt;/i&gt;), cual es el permiso concedido (&lt;i&gt;PRIVILEGE&lt;/i&gt;) y si el usuario al que se le concede el permiso puede a su vez conceder dicho permiso a otros usuarios (&lt;i&gt;GRANTABLE&lt;/i&gt;).&lt;br /&gt;&lt;br /&gt;Los permiso de acceso que se pueden asignar a una tabla son los siguientes: &lt;i&gt;ALL&lt;/i&gt;, &lt;i&gt;ALTER&lt;/i&gt;, &lt;i&gt;DELETE&lt;/i&gt;, &lt;i&gt;INDEX&lt;/i&gt;, &lt;i&gt;INSERT&lt;/i&gt;, &lt;i&gt;REFERENCES&lt;/i&gt;, &lt;i&gt;SELECT&lt;/i&gt; y &lt;i&gt;UPDATE&lt;/i&gt;. Además, cuando se dan permisos de acceso al usuario genérico &lt;i&gt;PUBLIC&lt;/i&gt;, automáticamente se están asignando dichos permisos a todos los usuarios de la base de datos Oracle.&lt;br /&gt;&lt;br /&gt;A continuación encontraréis un par de &lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;ejemplos de sentencias SQL&lt;/span&gt; con los que podréis:&lt;br /&gt;&lt;br /&gt;1) Encontrar el nombre de una tabla de la que sólo recordáis parte del nombre (la sentencia SQL del ejemplo devolverá todas las tablas y el esquema al que pertenecen que contienen en su nombre la secuencia de caracteres &lt;i&gt;EMPL&lt;/i&gt;):&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%; font-family: courier new;"&gt;SELECT DISTINCT owner, table_name&lt;br /&gt;&amp;nbsp;  FROM dba_tables&lt;br /&gt;&amp;nbsp;WHERE table_name LIKE '%EMPL%'&lt;/span&gt;&lt;/blockquote&gt;&lt;br /&gt;2) Encontrar las tablas que tienen una columna con un nombre específico (la sentencia SQL del ejemplo devolverá todas las tablas que tienen una columna cuyo nombre es &lt;i&gt;CLIENTE&lt;/i&gt;):&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%; font-family: courier new;"&gt;SELECT DISTINCT owner, table_name, column_name&lt;br /&gt;&amp;nbsp; FROM dba_tab_columns&lt;br /&gt;&amp;nbsp;WHERE column_name = 'CLIENTE'&lt;/span&gt;&lt;/blockquote&gt;&lt;br /&gt;3) Listar los permisos que un determinado usuario tiene sobre un esquema de la base de datos determinado (la sentencia SQL del ejemplo mostraría los permisos del usuario &lt;i&gt;JLOPEZ&lt;/i&gt; sobre el esquema &lt;i&gt;FACTURAS&lt;/i&gt;):&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%; font-family: courier new;"&gt;SELECT owner, table_name, grantee, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; privilege, grantable&lt;br /&gt;&amp;nbsp; FROM dba_tab_privs&lt;br /&gt;&amp;nbsp;WHERE owner = 'FACTURAS'&lt;br /&gt;&amp;nbsp;&amp;nbsp; AND grantee = 'JLOPEZ'&lt;/span&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;Nota&lt;/b&gt;: Es importante utilizar mayúsculas a la hora de tratar de identificar un esquema, una tabla, una columna o un usuario, ya que sus nombres son almacenados con este formato.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Aclaración&lt;/b&gt;: Es posible que un determinado usuario tenga acceso a una tabla y no aparezca ningún registro para dicho usuario en la tabla &lt;i&gt;DBA_TAB_PRIVS&lt;/i&gt;. Estas son algunas de las posibles razones por las que esto puede ocurrir:&lt;br /&gt;&lt;br /&gt;1) La tabla en cuestión puede estar habilitada para ser accedida públicamente, es decir, tiene permisos de acceso el usuario genérico &lt;i&gt;PUBLIC&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;2) Un determinado rol (&lt;i&gt;ROLE&lt;/i&gt;) tiene permiso de acceso sobre la tabla y el usuario en cuestión pertenece a dicho rol (ver tablas &lt;i&gt;DBA_ROLES&lt;/i&gt; y &lt;i&gt;DBA_ROLE_PRIVS&lt;/i&gt;).&lt;br /&gt;&lt;br /&gt;3) El usuario tiene acceso a nivel de sistema para acceder a cualquier tabla, es decir, se le ha asignado el privilegio &lt;i&gt;SELECT ANY TABLE&lt;/i&gt; (ver tabla &lt;i&gt;DBA_SYS_PRIVS&lt;/i&gt;). También existen privilegios equivalentes para las acciones de &lt;i&gt;CREATE&lt;/i&gt;, &lt;i&gt;ALTER&lt;/i&gt;, &lt;i&gt;BACKUP&lt;/i&gt;, &lt;i&gt;DELETE&lt;/i&gt;, &lt;i&gt;DROP&lt;/i&gt;, &lt;i&gt;INSERT&lt;/i&gt;, &lt;i&gt;LOCK&lt;/i&gt; y &lt;i&gt;UPDATE&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-3561439201904251832?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/frceCNi4BTE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/3561439201904251832/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=3561439201904251832&amp;isPopup=true" title="1 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3561439201904251832?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3561439201904251832?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/frceCNi4BTE/identificacin-de-tablas-y-columnas-en.html" title="Identificación de tablas y columnas en una base de datos Oracle mediante sentencias SQL" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp0.blogger.com/_ObxKtfPuuSQ/RoE-EKTlvwI/AAAAAAAABPI/GMuVXidwrhM/s72-c/teclado-programacion-PLSQL.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://www.plsql.biz/2007/06/identificacin-de-tablas-y-columnas-en.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0YBQXg_cCp7ImA9WxVbF0g.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-5954022025853627439</id><published>2007-06-20T18:18:00.003+02:00</published><updated>2009-04-03T12:39:10.648+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-03T12:39:10.648+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><category scheme="http://www.blogger.com/atom/ns#" term="Utilidades PLSQL" /><title>Uso del comando TKPROF para formatear los ficheros de trazado PL/SQL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/aG03rCEzKQ97ePxG1qlSPFFQABM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/aG03rCEzKQ97ePxG1qlSPFFQABM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/aG03rCEzKQ97ePxG1qlSPFFQABM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/aG03rCEzKQ97ePxG1qlSPFFQABM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_ObxKtfPuuSQ/RnlY36TlviI/AAAAAAAABNY/JlEhLd8n6K0/s1600-h/programamcion-plsql-chat.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_ObxKtfPuuSQ/RnlY36TlviI/AAAAAAAABNY/JlEhLd8n6K0/s200/programamcion-plsql-chat.jpg" border="0" alt="Chat en PL/SQL con el comando tkprof para formatear ficheros de trazado SQL" id="BLOGGER_PHOTO_ID_5078187772404416034" /&gt;&lt;/a&gt;Ya hablamos en un articulo anterior acerca de como &lt;a href="http://www.plsql.biz/2007/05/como-usar-la-utilidad-de-trazado-del.html"&gt;activar y desactivar la funcionalidad de trazado SQL o PLSQL&lt;/a&gt;. En este artículo voy a escribir acerca de como utilizar el comando del sistema operativo &lt;i&gt;TKPROF&lt;/i&gt;, cuyo cometido es formatear los ficheros binarios generados mientras la traza SQL esta activa, de manera que éstos sean legibles. La sintaxis del comando es como sigue:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;UNIX&gt; tkprof fichero_trazado fichero_salida [opciones]&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;Cuando el comando &lt;i&gt;TKPROF&lt;/i&gt; es ejecutado sin utilizar ningún argumento, la salida del comando muestra un mensaje de ayuda junto con la descripción de todas las opciones del comando disponibles.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Opciones del comando &lt;i&gt;TKPROF&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Sort=opción&lt;/i&gt; - Ordena las sentencias SQL de la traza en base a distintos criterios. Los criterios de ordenación más útiles a mí entender son:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;i&gt;prscpu&lt;/i&gt;: Ordenado según el tiempo de CPU.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;i&gt;prsela&lt;/i&gt;: Ordenado según el tiempo de ejecución.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Print=n&lt;/i&gt; - El informe mostrará únicamente las &lt;i&gt;n&lt;/i&gt; primeras sentencias SQL o PL/SQL. Esta opción es especialmente útil si se utiliza conjuntamente con las opción &lt;i&gt;sort&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Explain=usuario/password&lt;/i&gt; - Se conecta a la base de datos utilizando dicho usuario y genera los &lt;a href="http://www.plsql.biz/2007/05/cmo-obtener-el-plan-de-ejecucin-de-una.html"&gt;planes de ejecución&lt;/a&gt; (&lt;i&gt;EXPLAIN PLAN&lt;/i&gt;) de las sentencias SQL o PL/SQL en el esquema de la base de datos especificado.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Insert=nombre_fichero&lt;/i&gt; - Crea un &lt;i&gt;script&lt;/i&gt; SQL para cargar los resultados del comando &lt;i&gt;TKPROF&lt;/i&gt; dentro de una tabla de la &lt;a href="http://www.plsql.biz/search/label/Bases%20de%20datos%20Oracle"&gt;base de datos Oracle&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;&lt;i&gt;Sys=no&lt;/i&gt; - El informe no muestra las sentencias SQL ejecutadas por el usuario SYS. Esta opción es bastante útil ya que elimina todas sentencias SQL internas. Estas sentencias, al ejecutarlas internamente el sistema de la base de datos Oracle, no pueden ser optimizadas y no es necesario, por lo general, tenerlas en consideración.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Aggregate=no&lt;/i&gt; - No acumula los resultados de las sentencias SQL idénticas en un solo registro.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Record=nombre_fichero&lt;/i&gt; - Crea un &lt;i&gt;script&lt;/i&gt; SQL con todas las sentencias SQL no recursivas encontradas en el fichero de trazado. Este &lt;i&gt;script&lt;/i&gt; se puede utilizar posteriormente para repetir la sesión de optimización o para modificar y optimizar las sentencias SQL sobre dicho &lt;i&gt;script&lt;/i&gt; y comprobar que el rendimiento mejora al ejecutarlo tras la modificación.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Table=esquema.nombre_tabla&lt;/i&gt; - Especifica el esquema y el nombre de la tabla donde se almacenará temporalmente el &lt;a href="http://www.plsql.biz/2007/05/cmo-obtener-el-plan-de-ejecucin-de-una.html"&gt;plan de ejecución&lt;/a&gt; antes de que se genere el fichero de salida. Este parámetro es ignorado si no se utiliza la opción &lt;i&gt;explain&lt;/i&gt;. Puede ser útil cuando simultáneamente varios usuarios utilizan el comando &lt;i&gt;TKPROF&lt;/i&gt; para optimizar el mismo esquema de la base de datos.&lt;br /&gt;&lt;br /&gt;Ya he hablado acerca de como ejecutar el comando &lt;i&gt;TKPROF&lt;/i&gt;, ahora queda la ardua tarea de escribir acerca de como interpretar la salida de dicho comando, pero esto será objeto de otro artículo en esta bitácora. &lt;br /&gt;&lt;br /&gt;Como avance mencionaré algunas circunstancias que nos pueden llevar a &lt;b&gt;malinterpretar la salida del comando &lt;i&gt;TKPROF&lt;/i&gt;&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Problemas con el número de lecturas consistentes&lt;/b&gt;: Si en el momento de la ejecución de una sentencia SQL o PLSQL hay transacciones no confirmadas (&lt;i&gt;uncommitted transactions&lt;/i&gt;) sobre la tabla utilizada, entonces el número de bloques leídos aumentará ya que la base de datos Oracle necesitará construir y leer bloques adicionales para  mantener la integridad.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Interpretación del tiempo de ejecución&lt;/b&gt;: Si una sentencia DML (sentencias de manipulación de datos) muestra un tiempo de ejecución elevado, puede ocurrir que esto sea debido a que otra transacción esté bloqueando la tabla involucrada. Por esta razón, el tiempo de CPU es un mejor indicador que el tiempo de ejecución.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Diferencias en el tiempo&lt;/b&gt;: Si el plan de ejecución de la sentencia SQL analizada indica que la tabla en cuestión es accedida a través de un índice, pero las estadísticas del comando &lt;i&gt;TKPROF&lt;/i&gt; muestran un número de bloques leídos muy elevado y, además, los valores para la columna &lt;i&gt;current&lt;/i&gt; del informe no son cero, entonces probablemente lo que haya ocurrido es que la tabla fue realmente accedida realizando un &lt;a href="http://www.plsql.biz/2007/01/hints-en-plsql-para-determinar-el-mtodo.html"&gt;escaneado completo&lt;/a&gt; (&lt;i&gt;full scan&lt;/i&gt;). Este tipo de situaciones se pueden dar cuando la generación del fichero de trazado haya sido anterior a la creación del índice o, si estamos utilizando el &lt;a href="http://www.plsql.biz/2006/12/hints-en-plsql-para-el-modo-de.html"&gt;optimizador basado en costes&lt;/a&gt;, a la regeneración de las estadísticas asociadas con la tabla involucrada. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;La existencia de &lt;a href="http://www.plsql.biz/2007/02/triggers-en-plsql.html"&gt;&lt;i&gt;triggers&lt;/i&gt;&lt;/b&gt;&lt;/a&gt;: Los valores mostrados por el comando &lt;i&gt;TKPROF&lt;/i&gt; a la hora de analizar una sentencia SQL incluyen todas las sub-sentencias SQL ejecutadas desde la sentencia principal. Es decir, si una sentencia SQL dispara un &lt;i&gt;trigger&lt;/i&gt;, los recursos utilizados por dicho &lt;i&gt;trigger&lt;/i&gt; serán incluidos en el informe final acerca de la sentencia principal.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-5954022025853627439?l=www.plsql.biz'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/tjqKV2QOJzk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/5954022025853627439/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=26801181&amp;postID=5954022025853627439&amp;isPopup=true" title="4 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5954022025853627439?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5954022025853627439?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/tjqKV2QOJzk/uso-del-comando-tkprof-para-formatear.html" title="Uso del comando TKPROF para formatear los ficheros de trazado PL/SQL" /><author><name>Pepelu</name><uri>http://www.blogger.com/profile/00891045835734721638</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="18072941500668390493" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp3.blogger.com/_ObxKtfPuuSQ/RnlY36TlviI/AAAAAAAABNY/JlEhLd8n6K0/s72-c/programamcion-plsql-chat.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://www.plsql.biz/2007/06/uso-del-comando-tkprof-para-formatear.html</feedburner:origLink></entry></feed>
