<?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:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;CUIBSX8yfCp7ImA9WhRaEUg.&quot;"><id>tag:blogger.com,1999:blog-26801181</id><updated>2012-02-13T18:19:18.194+01:00</updated><category term="Tutorial PL/SQL" /><category term="Optimización y tuning de bases de datos" /><category term="Librerías estándar PLSQL" /><category term="Utilidades PLSQL" /><category term="Bases de datos Oracle" /><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>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>76</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/PLSQL" /><feedburner:info uri="plsql" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" /><feedburner:emailServiceId>PLSQL</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><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><entry gd:etag="W/&quot;CUIMSHszfip7ImA9WhRUEEU.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-7002315022076874631</id><published>2012-01-20T19:13:00.000+01:00</published><updated>2012-01-20T19:19:49.586+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-20T19:19:49.586+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><title>Trabajando con fechas en PL/SQL: los tipos DATE, TIMESTAMP e INTERVAL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/l6Pa6hzU43pDDB7b35oPXkGvwLY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/l6Pa6hzU43pDDB7b35oPXkGvwLY/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/l6Pa6hzU43pDDB7b35oPXkGvwLY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/l6Pa6hzU43pDDB7b35oPXkGvwLY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.plsql.biz/2012/01/tipos-plsql-date-timestamp-interval.html#more" imageanchor="1" style="clear:right; float:right; margin-left:1em; margin-bottom:1em"&gt;&lt;img border="0" height="135" width="200" src="http://3.bp.blogspot.com/-bs65Z1w1-0I/TxmuvuhFjxI/AAAAAAAAGlU/DXnODSuAGWk/s200/date-timestamp-interval-oracle-plsql.jpg" alt="Los tipos PL/SQL DATE, TIMESTAMP e INTERVAL" /&gt;&lt;/a&gt;&lt;/div&gt;Las fechas son un &lt;a href="http://www.plsql.biz/2006/10/tipos-de-datos-en-plsql.html"&gt;tipo de datos del PL/SQL&lt;/a&gt; considerablemente más complejo que un tipo carácter o un tipo numérico. Una fecha o momento de tiempo está compuesto de múltiples campos (año, mes, día, hora, minutos, etcétera) y, además, existen un buen número de normas para determinar si una fecha es válida o no (los años bisiestos, los cambios de hora, etcétera). Como consecuencia de todo esto, en PLSQL resulta habitual tener que:&lt;ul&gt;&lt;li&gt;Declarar constantes y variables de tipo fecha o tiempo.&lt;/li&gt;
&lt;li&gt;Utilizar funciones para modificar dichas variables y mostrarlas en el formato deseado por el usuario.&lt;/li&gt;
&lt;li&gt;Manipular fechas y tiempos para realizar cálculos variados.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Este artículo será el primero de una serie en los que explicaré todo lo que un programador PL/SQL necesita conocer para trabajar con los diferentes &lt;b&gt;tipos de datos asociados con fechas y momentos de tiempo&lt;/b&gt; (DATE, TIMESTAMP e INTERVAL). &lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;b&gt;Los tipos DATE, TIMESTAMP e INTERVAL&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Afortunadamente la base de datos Oracle y el PLSQL proporciona diferentes tipos de datos que permiten manejar fechas y momentos de tiempo, almacenando ambos tipos de información en un formato interno estándar.&lt;br /&gt;
&lt;br /&gt;
Las bases de datos Oracle permiten utilizar &lt;b&gt;tres tipos de datos diferentes para trabajar con fechas y momentos de tiempo&lt;/b&gt;:&lt;ul&gt;&lt;li&gt;&lt;b&gt;DATE&lt;/b&gt;: este tipo de dato permite almacenar una fecha y un tiempo hasta el nivel de segundos. No incluye información sobre la zona horaria. Es el tipo de dato que más se utiliza para trabajar con fechas dentro de cualquier aplicación Oracle.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;TIMESTAMP&lt;/b&gt;:  se trata de un tipo de dato similar al DATE pero con dos diferencias clave, permiten almacenar y manipular momentos de tiempo hasta la mil millonésima de segundo (con una precisión de 9 decimales), y también es posible asociarle una zona horaria de tal manera que la base de datos Oracle tendrá en cuenta dicha zona horaria cuando manipulemos y realicemos cálculos utilizando este tipo de dato.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;INTERVAL&lt;/b&gt;:  mientras que los tipos DATE y TIMESTAMP indican un momento específico de tiempo, INTERVAL almacena y permite trabajar con duraciones de tiempo, siendo posible definir intervalos de tiempo en términos de años y meses, o de días y segundos.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
A continuación os dejo un &lt;b&gt;ejemplo de código PL/SQL&lt;/b&gt; donde se declaran diferentes variables que utilizan los &lt;b&gt;tipos de dato DATE, TIMESTAMP e INTERVAL&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;DECLARE
   l_hoy_date       DATE := SYSDATE;
   l_hoy_timestamp  TIMESTAMP := SYSTIMESTAMP;
   l_hoy_timetzone  TIMESTAMP WITH TIME ZONE
                    := SYSTIMESTAMP;
   l_interval_ym    INTERVAL YEAR (4) TO MONTH 
                    := '2011-11';
   l_interval_ds    INTERVAL DAY (2) TO SECOND 
                    := '15 00:30:44';
BEGIN
   null;
END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Como comentario os diré que resulta poco usual que un programador de PLSQL tenga que utilizar los tipos TIMESTAMP e INTERVAL con zona horaria, algo que, por cierto, resulta algo complicado y se necesitan conocer algunas funcionalidades avanzadas.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;¿Cómo escoger el tipo de dato fecha adecuado?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Ante la diversidad de tipos de dato fecha y momentos de tiempo que ofrece la base de datos Oracle, al escribir nuestro código PL/SQL puede resultarnos complicado decidirnos por un tipo de dato u otro. Estas son las normas que yo utilizo para decantarme por uno u otro:&lt;ul&gt;&lt;li&gt;Utilizar el tipo &lt;b&gt;TIMESTAMP&lt;/b&gt; cuando es necesario controlar momentos de tiempo por debajo de la fracción de segundo.&lt;/li&gt;
&lt;li&gt;En general es posible utilizar el tipo TIMESTAMP en lugar del tipo DATE, ya que la base de datos es capaz de distinguir cuando un TIMESTAMP no almacena fracciones de segundo, reservando sólo 7 bytes de almacenamiento para dicho dato, exactamente lo mismo que para un dato DATE. Cuando un TIMESTAMP contiene fracciones de segundo, entonces la base de datos Oracle necesita 11 bytes de almacenamiento.&lt;/li&gt;
&lt;li&gt;Utilizar &lt;b&gt;TIMESTAMP WITH TIME ZONE&lt;/b&gt; cuando sea necesario realizar un seguimiento de la zona horaria de la sesión en la que el dato fue introducido.&lt;/li&gt;
&lt;li&gt;Utilizar &lt;b&gt;TIMESTAMP WITH LOCAL TIME ZONE&lt;/b&gt; cuando la base de datos Oracle tenga que convertir automáticamente tiempos entre bases de datos y sesiones que operan bajo diferentes zonas horarias.&lt;/li&gt;
&lt;li&gt;Utilizar &lt;b&gt;DATE&lt;/b&gt; cuando sea necesario mantener la compatibilidad con una aplicación que fue escrita antes de que el tipo de dato TIMESTAMP fuera introducido.&lt;/li&gt;
&lt;li&gt;En nuestro código PL/SQL siempre deberemos utilizar tipos de datos que se correspondan, o que al menos sean compatibles, con el tipo de dato asociado con el campo de la tabla que queramos almacenar en la variable correspondiente. Debemos ser conscientes de que si el campo de una tabla es tipo TIMESTAMP y lo almacenamos en una variable tipo DATE, podremos estar perdiendo información (en este caso la relativa a la zona horaria o a las fracciones de segundo).&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
En futuros artículos hablaré sobre las funciones que permiten obtener los valores de la fecha y el tiempo actual, las funciones para convertir fechas a caracteres y caracteres a fechas, y las funciones de truncado de fechas.&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-7002315022076874631?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=0I7RsOS2il4:87iRPrs0rLs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=0I7RsOS2il4:87iRPrs0rLs:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=0I7RsOS2il4:87iRPrs0rLs:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=0I7RsOS2il4:87iRPrs0rLs:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=0I7RsOS2il4:87iRPrs0rLs:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=0I7RsOS2il4:87iRPrs0rLs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=0I7RsOS2il4:87iRPrs0rLs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=0I7RsOS2il4:87iRPrs0rLs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=0I7RsOS2il4:87iRPrs0rLs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=0I7RsOS2il4:87iRPrs0rLs:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=0I7RsOS2il4:87iRPrs0rLs:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/0I7RsOS2il4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/7002315022076874631/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=7002315022076874631&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7002315022076874631?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7002315022076874631?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/0I7RsOS2il4/tipos-plsql-date-timestamp-interval.html" title="Trabajando con fechas en PL/SQL: los tipos DATE, TIMESTAMP e INTERVAL" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-bs65Z1w1-0I/TxmuvuhFjxI/AAAAAAAAGlU/DXnODSuAGWk/s72-c/date-timestamp-interval-oracle-plsql.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.plsql.biz/2012/01/tipos-plsql-date-timestamp-interval.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUUFRXYzfyp7ImA9WhRRFk0.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-2032317954061957271</id><published>2011-11-29T23:30:00.000+01:00</published><updated>2011-11-29T23:53:34.887+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-29T23:53:34.887+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><title>Funciones númericas en PL/SQL y el SQL de Oracle</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/6YRJ6C3cITnKyhGYvx2R2034UV4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/6YRJ6C3cITnKyhGYvx2R2034UV4/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/6YRJ6C3cITnKyhGYvx2R2034UV4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/6YRJ6C3cITnKyhGYvx2R2034UV4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;Las bases de datos Oracle ofrecen un extenso conjunto de &lt;b&gt;funciones estándar SQL y PL/SQL para manipular números&lt;/b&gt; y realizar conversiones entre números y cadenas de caracteres. En este artículo hablaremos sobre las funciones numéricas más comunes y que se tienen que utilizar con mayor frecuencia a la hora de programar en PL/SQL, siendo su conocimiento fundamental para cualquier programador de bases de datos Oracle.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.plsql.biz/2011/11/funciones-numericas-plsql-oracle.html#more" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="150" width="320" src="http://1.bp.blogspot.com/-CfgSICQsUxg/TtVb4MTFRWI/AAAAAAAAGZ8/i7iK0CkZzUc/s320/funciones-numericas-plsql-oracle.jpg" alt="Funciones númericas en PL/SQL y el SQL de Oracle" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Las funciones numéricas estándar más comunes del PLSQL y el SQL de Oracle son:&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;b style="color: #ff9604"&gt;ROUND (m, n)&lt;/b&gt;: la función ROUND acepta como entrada un número y devuelve como salida otro número redondeado a un número específico de decimales (que se indica en el segundo parámetro de la función). Si no se especifica el número de decimales a mostrar (parámetro n), la función ROUND devolverá el número redondeado al entero más próximo. Si el número de decimales a redondear es negativo (n es negativo), entonces el redondeo se realiza avanzando hacia la izquierda del separador decimal, en lugar de hacia la derecha (ver ejemplo).&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;BEGIN
  DBMS_OUTPUT.put_line (ROUND (20.35));
  DBMS_OUTPUT.put_line (ROUND (20.35, 1));
  DBMS_OUTPUT.put_line (ROUND (20.33, 1));
  DBMS_OUTPUT.put_line (ROUND (20.35, 2));
  DBMS_OUTPUT.put_line (ROUND (20.35, -2));
  DBMS_OUTPUT.put_line (ROUND (225, -2));
END;

La salida de este bloque PL/SQL sería:
20
20.4
20.3
20.35
0
200&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;b style="color: #ff9604"&gt;TRUNC (m, n)&lt;/b&gt;: la función TRUNC es muy parecida a la función ROUND, permitiendo también indicar el número de decimales a truncar a derecha (n positivo) o izquierda (n negativo) del separador decimal. La diferencia estriba en que la función TRUNC simplemente elimina o trunca los dígitos, es decir, no realiza un redondeo.&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;BEGIN
  DBMS_OUTPUT.put_line (TRUNC (20.35));
  DBMS_OUTPUT.put_line (TRUNC (20.35, 1));
  DBMS_OUTPUT.put_line (TRUNC (20.33, 1));
  DBMS_OUTPUT.put_line (TRUNC (20.35, 2));
  DBMS_OUTPUT.put_line (TRUNC (20.35, -2));
  DBMS_OUTPUT.put_line (TRUNC (225, -2));
END;
/

La salida de este bloque PL/SQL sería:
20
20.3
20.3
20.35
0
200&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;b style="color: #ff9604"&gt;FLOOR (m)&lt;/b&gt;: esta función devuelve el mayor entero menor o igual que el número especificado como parámetro.&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;BEGIN
  DBMS_OUTPUT.put_line (FLOOR (3.5));
END;
/

La salida de este bloque PL/SQL sería:
3&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;b style="color: #ff9604"&gt;CEIL (m)&lt;/b&gt;: esta función devuelve el menor entero mayor o igual que el número especificado como parámetro.&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;BEGIN
  DBMS_OUTPUT.put_line (CEIL (3.5));
END;
/

La salida de este bloque PL/SQL sería:
4&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;b style="color: #ff9604"&gt;MOD (m, n)&lt;/b&gt;: calcula el resto del resultado de dividir m entre n. La fórmula que emplea la base de datos Oracle para realizar el cálculo es la siguiente MOD (m, n) = m - n * FLOOR (m/n).&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;BEGIN
  DBMS_OUTPUT.put_line (MOD (25, 6));
  DBMS_OUTPUT.put_line (MOD (25, 9));
END;
/

La salida de este bloque PL/SQL sería:
1
7&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;b style="color: #ff9604"&gt;REMAINDER (m, n)&lt;/b&gt;: calcula el resto por exceso del resultado de dividir m entre n. La fórmula que emplea la base de datos Oracle para realizar el cálculo es la siguiente REMAINDER (m, n) = m - n * ROUND (m/n).&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;BEGIN
  DBMS_OUTPUT.put_line (REMAINDER (25, 6));
  DBMS_OUTPUT.put_line (REMAINDER (25, 9));
END;
/

La salida de este bloque PL/SQL sería:
1
-2&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;b style="color: #ff9604"&gt;TO _CHAR (m)&lt;/b&gt;: la función PL/SQL TO_CHAR, en su formato más sencillo, sirve para convertir un número en una cadena de caracteres. Se pasa como único parámetro un número, devolviendo la función la cadena de caracteres que representa a dicho número mostrando únicamente los dígitos significativos de dicho número (es decir, los ceros que no son significativos son eliminados).&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;BEGIN
  DBMS_OUTPUT.put_line (TO_CHAR (200.64));
  DBMS_OUTPUT.put_line (TO_CHAR (000200.6400));
  DBMS_OUTPUT.put_line (TO_CHAR (20000.00));
END;

La salida de este bloque PL/SQL sería:
200.64
200.64
20000&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;b style="color: #ff9604"&gt;Función TO_CHAR (m, formato)&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Con cierta frecuencia, los programadores PL/SQL se encuentran con la necesidad de que al convertir un número en una cadena de caracteres, dicho número cumpla con cierto formato. Por ejemplo, puede ser necesario mostrar el número de manera que incluya siempre un número fijo de decimales (aunque estos sean cero), o puede necesitarse mostrar el separador de miles. Para estos casos la función TO_CHAR debe incluir un segundo parámetro que representa la &lt;b&gt;máscara del formato&lt;/b&gt; que debe seguir el número a representar.&lt;br /&gt;
&lt;br /&gt;
Esta máscara se representa mediante una cadena de caracteres y lo más sencillo para comprender su funcionamiento es utilizar una serie de ejemplos.&lt;br /&gt;
&lt;br /&gt;
En el primer ejemplo vemos que el &lt;b&gt;carácter "G"&lt;/b&gt; indica en qué lugar de la cadena se quiere colocar el separador de grupo (en nuestro caso separador de miles). El carácter separador de grupo viene determinado por el valor del parámetro NLS_NUMERIC_CHARACTERS de la configuración del lenguaje de la base de datos Oracle, es decir, puede modificarse según las necesidades específicas de cada país (por ejemplo, en España se utiliza el punto "." mientras que en UK o USA se utiliza la coma ","). El &lt;b&gt;carácter "9"&lt;/b&gt; le indica a la base de datos Oracle que en esa posición debe colocar un dígito significativo o, si el dígito no es significativo, un espacio en blanco.&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;BEGIN
  DBMS_OUTPUT.put_line (TO_CHAR (20000, '9G999G999'));
END;
/
La salida de este bloque PL/SQL sería:
   20,000&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
El este otro ejemplo podéis ver que si queremos que aparezcan ceros en lugar de espacios en blanco para los dígitos no significativos, entonces deberemos utilizar el &lt;b&gt;carácter "0"&lt;/b&gt; en lugar del "9".&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;BEGIN
  DBMS_OUTPUT.put_line (TO_CHAR (20000, '0G000G999'));
END;
/
La salida de este bloque PL/SQL sería:
0,020,000&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Pero si lo que queremos es que no aparezcan ni ceros, ni espacios en blanco al convertir nuestro número en una cadena de caracteres, tendremos que usar la &lt;b&gt;cadena "FM"&lt;/b&gt;.&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;BEGIN
  DBMS_OUTPUT.put_line (TO_CHAR (20000, 'FM9G999G999'));
END;
/
La salida de este bloque PL/SQL sería:
20,000&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Ahora supongamos que nuestro número debe representar una cantidad en euros incluyendo los céntimos de euro y, además, queremos mostrar el símbolo del euro. Entonces deberemos usar la siguiente máscara:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;BEGIN
  DBMS_OUTPUT.put_line (TO_CHAR (20000.57, 'FM999G999D99L'));
END;
/
La salida de este bloque PL/SQL sería:
20000.57€&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
En el ejemplo vemos que el &lt;b&gt;carácter "L"&lt;/b&gt; especifica el lugar donde aparecerá el símbolo de la moneda local (dicho símbolo viene determinado por el parámetro de la base de datos Oracle NLS_CURRENCY). El &lt;b&gt;carácter "D"&lt;/b&gt; determina la posición del separador decimal (al igual que el separador de grupo, el carácter que se utiliza como separador decimal, en España la coma "," y en UK o USA el punto ".", viene determinado por el parámetro NLS_NUMERIC_CHARACTERS).&lt;br /&gt;
&lt;br /&gt;
Para finalizar, sólo comentaros que si estáis interesado en ver todos los caracteres y elementos que se pueden usar en PLSQL y el SQL de Oracle en la máscara de los formatos numéricos, podéis chequear la siguiente &lt;a href="http://docs.oracle.com/cd/E11882_01/server.112/e17118/sql_elements004.htm#SQLRF00211" target="_blank"&gt;página de la web oficial de Oracle&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-2032317954061957271?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=OOuOodbOeOs:_jAVII0TCbM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=OOuOodbOeOs:_jAVII0TCbM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=OOuOodbOeOs:_jAVII0TCbM:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=OOuOodbOeOs:_jAVII0TCbM:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=OOuOodbOeOs:_jAVII0TCbM:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=OOuOodbOeOs:_jAVII0TCbM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=OOuOodbOeOs:_jAVII0TCbM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=OOuOodbOeOs:_jAVII0TCbM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=OOuOodbOeOs:_jAVII0TCbM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=OOuOodbOeOs:_jAVII0TCbM:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=OOuOodbOeOs:_jAVII0TCbM:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/OOuOodbOeOs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/2032317954061957271/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=2032317954061957271&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/2032317954061957271?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/2032317954061957271?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/OOuOodbOeOs/funciones-numericas-plsql-oracle.html" title="Funciones númericas en PL/SQL y el SQL de Oracle" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-CfgSICQsUxg/TtVb4MTFRWI/AAAAAAAAGZ8/i7iK0CkZzUc/s72-c/funciones-numericas-plsql-oracle.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.plsql.biz/2011/11/funciones-numericas-plsql-oracle.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C04NQ387cCp7ImA9WhRSEkQ.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-743119505486001958</id><published>2011-10-20T12:46:00.000+02:00</published><updated>2011-11-14T18:33:12.108+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-14T18:33:12.108+01:00</app:edited><title>Uso de Rollback Segments por sentencias SELECT</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/3uG-n2Ps8in1jtn9Qo4K_ZLrXxc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3uG-n2Ps8in1jtn9Qo4K_ZLrXxc/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/3uG-n2Ps8in1jtn9Qo4K_ZLrXxc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3uG-n2Ps8in1jtn9Qo4K_ZLrXxc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.plsql.biz/2011/10/rollback-segment-sentencia-plsql-select.html#more" imageanchor="1" style="clear:right; float:right; margin-left:1em; margin-bottom:1em"&gt;&lt;img border="0" height="157" width="200" src="http://3.bp.blogspot.com/-65lSRNVZYT4/Tp_7SptTSbI/AAAAAAAAGUA/Pm0qZCSYpsI/s200/rollback-segment-select.JPG" alt="Uso de Rollback Segments en sentencias SELECT" /&gt;&lt;/a&gt;&lt;/div&gt;En alguna ocasión algún lector me ha preguntado, con cierta sorpresa, acerca del por qué una sentencia SELECT le fallaba con el mensaje de error "No es posible ampliar el segmento de rollback" (&lt;i&gt;"Unable to extend rollback segment"&lt;/i&gt;). La sorpresa proviene del hecho de que son muchos los desarrolladores PL/SQL los que piensan que los segmentos de rollback sólo se utilizan cuando se emplean sentencias PLSQL en las que se modifican o actualizan datos dentro de la base de datos Oracle. Bajo este tipo de pensamiento es normal que, cuando se produce el error mencionado anteriormente al ejecutar una sentencia SELECT, uno se pregunte: ¿utiliza la base de datos Oracle segmentos de rollback al ejecutar sentencias SELECT?&lt;br /&gt;
&lt;br /&gt;
Bueno, en mi opinión, lo primero que hay que hacer es reformular la pregunta y cambiarla por la siguiente: &lt;b&gt;¿una sentencia SELECT necesita crear o leer segmentos de rollback?&lt;/b&gt; La pregunta formulada de esta manera seguro que nos ayudará a comprender mejor este artículo, ya que el verbo "utilizar" usado en la primera pregunta no es lo suficientemente específico.&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;En PL/SQL, &lt;b&gt;todas las sentencias SELECT tienen el potencial de utilizar los datos de &lt;i&gt;rollback&lt;/i&gt; o &lt;i&gt;undo&lt;/i&gt;&lt;/b&gt; (o datos de vuelta atrás). La base de datos Oracle a la hora de procesar cualquier tipo de consulta utiliza los segmentos de rollback para producir conjuntos de resultados de lectura consistente (la lectura consistente es una característica de las bases de datos Oracle que asegura que todos los registros dentro de un conjunto de resultados, cuando son presentados a una aplicación PLSQL o a cualquier otro tipo de aplicación, provengan de un mismo instante de tiempo).&lt;br /&gt;
&lt;br /&gt;
No obstante, este uso de los segmentos de rollback no va a causar nunca por sí solo un error como el antes mencionado (&lt;i&gt;ORA-01650 Unable to extend rollback segment...&lt;/i&gt; u &lt;i&gt;ORA-01651 Unable to extend undo segment...&lt;/i&gt; ). Sin embargo, sí que puede provocar un error del tipo &lt;i&gt;"ORA-01555 Snapshot too old"&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
Para que una sentencia SELECT pueda generar los errores ORA-01650 u ORA-01651, es necesario que esté &lt;b&gt;generando segmentos de rollback&lt;/b&gt; y los motivos pueden ser los siguientes:&lt;ul&gt;&lt;li&gt;La sentencia SELECT contiene la cláusula FOR UPDATE.&lt;/li&gt;
&lt;li&gt;La funcionalidad de auditoría está habilitada.&lt;/li&gt;
&lt;li&gt;La sentencia SELECT invoca a algún tipo de transacción que escribe en la base de datos Oracle.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Lo más corriente es que la causa sea que la sentencia SELECT contenga una cláusula FOR UPDATE, cuando esto ocurre la base de datos Oracle bloquea todos los registros necesarios antes de que la sentencia SELECT empiece a devolver resultados, y bloquear un registro en la base de datos Oracle implica modificar un bloque de la base de datos para registrar dicho bloqueo. Por otro lado, cada vez que se modifica un bloque de la base de datos, se necesita generar un &lt;i&gt;undo&lt;/i&gt; para esa operación.&lt;br /&gt;
&lt;br /&gt;
Demostrar este hecho es muy sencillo. Creemos una sesión SQL y ejecutemos los siguientes comandos sin que haya ningún otro usuario utilizando la base de datos:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; SELECT used_ublk FROM v$transaction

No rows selected

SQL&gt; BEGIN
  2    FOR cursor IN (
  3      SELECT * FROM nombre_tabla
  4        FOR UPDATE)
  5    LOOP null;
  6    END LOOP;
  7  END;
  10 /

PL/SQL procedure successfully completed.

SQL&gt; SELECT used_ublk FROM v$transaction

USED_UBLK
---------
      932

SQL&gt; COMMIT;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Según queríamos confirmar, podemos ver que la ejecución de la sentencia SELECT FOR UPDATE ha generado 932 bloques de &lt;i&gt;undo&lt;/i&gt; (tened en cuenta que &lt;i&gt;nombre_tabla&lt;/i&gt; debe ser una tabla que exista en vuestra base de datos Oracle).&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-743119505486001958?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=2A4nw89QgKU:jW1lYoTDVoQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=2A4nw89QgKU:jW1lYoTDVoQ:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=2A4nw89QgKU:jW1lYoTDVoQ:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=2A4nw89QgKU:jW1lYoTDVoQ:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=2A4nw89QgKU:jW1lYoTDVoQ:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=2A4nw89QgKU:jW1lYoTDVoQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=2A4nw89QgKU:jW1lYoTDVoQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=2A4nw89QgKU:jW1lYoTDVoQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=2A4nw89QgKU:jW1lYoTDVoQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=2A4nw89QgKU:jW1lYoTDVoQ:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=2A4nw89QgKU:jW1lYoTDVoQ:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/2A4nw89QgKU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/743119505486001958/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=743119505486001958&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/743119505486001958?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/743119505486001958?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/2A4nw89QgKU/rollback-segment-sentencia-plsql-select.html" title="Uso de Rollback Segments por sentencias SELECT" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-65lSRNVZYT4/Tp_7SptTSbI/AAAAAAAAGUA/Pm0qZCSYpsI/s72-c/rollback-segment-select.JPG" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.plsql.biz/2011/10/rollback-segment-sentencia-plsql-select.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4NQXw_fyp7ImA9WhdVEEQ.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-6945198761990557601</id><published>2011-09-15T06:30:00.000+02:00</published><updated>2011-09-15T16:29:50.247+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-15T16:29:50.247+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>Cláusula BULK COLLECT para mejorar el rendimiento al realizar procesamiento masivo</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/NK3SUfTo6Cl8D_Udd7MLiNnbQWI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/NK3SUfTo6Cl8D_Udd7MLiNnbQWI/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/NK3SUfTo6Cl8D_Udd7MLiNnbQWI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/NK3SUfTo6Cl8D_Udd7MLiNnbQWI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.plsql.biz/2011/09/clausula-bulk-collect-plsql.html#more" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="200" width="200" src="http://3.bp.blogspot.com/-wRpuS-gs8sE/TnIKJ9wvLSI/AAAAAAAAGNY/Rvj1hNp8tOA/s200/clausula-bulk-collect-plsql.JPG" alt="Utilidad de la cláusula BULK COLLECT en PL/SQL" /&gt;&lt;/a&gt;&lt;/div&gt;Yo siempre he dicho que cuando para hacer algo se pueden utilizar sentencias SQL sencillas, no resulta conveniente emplear complicados &lt;a href="http://www.plsql.biz/2007/03/procedimientos-y-funciones-en-plsql.html"&gt;procedimientos PL/SQL&lt;/a&gt; que implementen la misma solución. Sin embargo, hay situaciones en que para mejorar el rendimiento de determinados bucles FOR en los que se realizan actualizaciones masivas sobre una determinada tabla de la base de datos Oracle, resulta conveniente utilizar &lt;b&gt;técnicas PLSQL de procesamiento masivo&lt;/b&gt; (lo que en inglés se denomina &lt;b&gt;&lt;i&gt;BULK COLLECT&lt;/i&gt;&lt;/b&gt;).&lt;br /&gt;
&lt;br /&gt;
Para entender mejor en qué consiste esta técnica, primero hay que comprender los motivos por los que un simple bucle FOR puede generar importantes problemas de rendimiento. Veamos el siguiente código PL/SQL:&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;FOR selrec IN 
  (SELECT * FROM tabla_enorme 
  ORDER BY muchas columnas)
LOOP
  -- Gran cantidad de código que omito y al final:
  UPDATE tabla_enorme SET ...
    WHERE clave_primaria = selrec.clave_primaria;
  COMMIT;
END LOOP;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
El presente código es un extracto de un código PLSQL que uno de los lectores de este blog me envió por correo electrónico indicándome que presentaba graves problemas de rendimiento, cosa que desde un primer momento a mi no me extrañó, considerando que la tabla &lt;i&gt;tabla_enorme&lt;/i&gt; contenía más de 30 millones de registros. Es el típico ejemplo de código PL/SQL que para mejorar su rendimiento necesita que se aplique la técnica o funcionalidad de &lt;b&gt;&lt;i&gt;BULK COLLECT&lt;/i&gt;&lt;/b&gt; (que traducido directamente a castellano sería similar a decir "recogida a granel" pero que aquí traduciremos por procesamiento masivo).&lt;br /&gt;
&lt;br /&gt;
Antes de profundizar en el tema, debemos considerar que si leemos los datos de una base de datos Oracle sin necesidad de enviar datos de vuelta a la misma base de datos, no es necesario aplicar la técnica del &lt;b&gt;&lt;i&gt;BULK COLLECT&lt;/i&gt;&lt;/b&gt;. Es decir, el siguiente código PL/SQL es perfectamente utilizable y no debería generar problemas de rendimiento:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;FOR x IN 
  (SELECT * FROM tabla_enorme t WHERE ...)
LOOP
  DBMS_OUTPUT.PUT_LINE (x.col1||...||x.colN);
END LOOP;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
En el ejemplo vemos que estamos leyendo los datos utilizando un SELECT, pero que, aun tratándose de una operación registro a registro, no se utilizan para ser retornados a la base de datos utilizando un INSERT, UPDATE o DELETE. La sentencia SQL SELECT ya utiliza, de por sí, la funcionalidad de procesamiento masivo, ya que, desde la versión 10g de la base de datos Oracle, el código "&lt;b&gt;FOR x IN (SELECT ...)&lt;/b&gt;", aunque no sea visible para el desarrollador, ha sido optimizado para almacenar internamente en matrices de 100 registros los resultados que devuelve dicho SELECT.&lt;br /&gt;
&lt;br /&gt;
Sin embargo, veamos que ocurre con este otro ejemplo de código PL/SQL:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;FOR x IN 
  (SELECT clave_primaria, col1, col2 
  FROM tabla_enorme)
LOOP
  x.col1 := calculos(x.col1, x.col2);
  UPDATE tabla_enorme
    SET col1 = x.col1
    WHERE clave_primaria = x.clave_primaria;
  COMMIT;
END LOOP;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
En este código PLSQL, la sentencia SELECT estará utilizando la funcionalidad de procesamiento masivo incorporada con la versión 10g, pero no ocurrirá lo mismo con el procesamiento de la sentencia UPDATE. La pregunta ahora es, ¿cómo podemos mejorar el rendimiento de un código tan sencillo? El primer cambio que realizaremos es algo bastante sencillo:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;FOR x IN
  (SELECT rowid, col1, col2 
  FROM tabla_enorme)
LOOP
  x.col1 := calculos(x.col1, x.col2);
  UPDATE tabla_enorme
    SET col1 = x.col1
    WHERE rowid = x.rowid;
END LOOP;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
El nuevo código lee cada registro de la &lt;i&gt;tabla_enorme&lt;/i&gt;, realiza algún tipo de procesado de los datos y, finalmente, &lt;b&gt;realiza el UPDATE de un campo de la misma tabla por ROWID&lt;/b&gt;. ¿Qué hemos conseguido con respecto al primer código?, habremos evitado realizar 30 millones de veces un UNIQUE SCAN sobre el índice de la clave primaria, ya que estaremos accediendo a la tabla por ROWID. Acceder a la tabla a través del índice de la clave primaria puede implicar de tres a cinco operaciones de entrada/salida (I/O) por iteración, por lo que acceder a la tabla por ROWID en una tabla tan grande nos puede ahorrar más de cien millones de operaciones de entrada/salida.&lt;br /&gt;
&lt;br /&gt;
Otro cambio que observaréis es que &lt;b&gt;hemos eliminado el COMMIT después de cada iteración&lt;/b&gt;. Aparte de que realizar un COMMIT después de que cada registro es procesado resultará lento y reducirá bastante el rendimiento, si en mitad del procesamiento se produce algún error, habremos dejado la base de datos Oracle en un estado bastante inconsistente, por no decir corrupto (con media &lt;i&gt;tabla_enorme&lt;/i&gt; actualizada y la otra sin actualizar).&lt;br /&gt;
&lt;br /&gt;
Pero esto que hemos hecho no tiene en realidad nada que ver con la técnica o funcionalidad de &lt;b&gt;procesamiento masivo&lt;/b&gt;. Veamos el siguiente código PL/SQL en el que hemos, por fin, utilizado un &lt;b&gt;FETCH con la cláusula BULK COLLECT&lt;/b&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE OR REPLACE PROCEDURE procesamiento_masivo
AS
  TYPE matriz_rowid IS TABLE OF ROWID;
  TYPE matriz_col1 IS TABLE OF tabla_enorme.col1%TYPE;
  TYPE matriz_col2 IS TABLE OF tabla_enorme.col2%TYPE;

  CURSOR cur IS SELECT rowid, col1, col2
    FROM tabla_enorme;
  m_rowid matriz_rowid;
  m_col1 matriz_col1;
  m_col2 matriz_col2;
  contador NUMBER := 100;

BEGIN
  OPEN cur;
  LOOP
    FETCH cur BULK COLLECT
      INTO m_rowid, m_col1, m_col2 LIMIT contador;
    FOR i IN 1 .. m_rowid.count
    LOOP
      m_col1(i) := calculos(m_col1(i), m_col2(i));
    END LOOP;
    FORALL i IN 1 .. m_rowid.count
      UPDATE tabla_enorme
        SET col1 = m_col1(i)
        WHERE rowid = m_rowid(i);
    EXIT WHEN cur%NOTFOUND;
  END LOOP;
  CLOSE cur;
END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
El nuevo código utiliza si ningún tipo de restricción la &lt;b&gt;funcionalidad de procesamiento masivo &lt;i&gt;BULK COLLECT&lt;/i&gt;&lt;/b&gt;, de manera que los registros se procesan de cien en cien (valor que podremos cambiar con sólo asignar un valor diferente a la variable &lt;i&gt;contador&lt;/i&gt;). Y, una vez procesados, lo que hacemos es un UPDATE masivo (&lt;i&gt;bulk update&lt;/i&gt;) utilizando la &lt;b&gt;sentencia PL/SQL FORALL&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
Es fácil observar que el código PLSQL que utiliza la funcionalidad &lt;i&gt;BULK COLLECT&lt;/i&gt; es bastante más complicado y mucho menos intuitivo que el original, pero los resultados a nivel de rendimiento van a ser realmente sorprendentes. Para tablas con 30 millones de registros, utilizar la funcionalidad de procesamiento masivo puede hacer que nuestro código se ejecute entre diez y veinte veces más rápido que sin utilizar dicha funcionalidad (ojo que, a veces, la mejora de rendimiento puede ser incluso hasta superior).&lt;br /&gt;
&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-6945198761990557601?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=MjHctkgoFGY:4rh8_XuvhVM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=MjHctkgoFGY:4rh8_XuvhVM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=MjHctkgoFGY:4rh8_XuvhVM:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=MjHctkgoFGY:4rh8_XuvhVM:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=MjHctkgoFGY:4rh8_XuvhVM:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=MjHctkgoFGY:4rh8_XuvhVM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=MjHctkgoFGY:4rh8_XuvhVM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=MjHctkgoFGY:4rh8_XuvhVM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=MjHctkgoFGY:4rh8_XuvhVM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=MjHctkgoFGY:4rh8_XuvhVM:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=MjHctkgoFGY:4rh8_XuvhVM:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/MjHctkgoFGY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/6945198761990557601/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=6945198761990557601&amp;isPopup=true" title="5 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6945198761990557601?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6945198761990557601?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/MjHctkgoFGY/clausula-bulk-collect-plsql.html" title="Cláusula BULK COLLECT para mejorar el rendimiento al realizar procesamiento masivo" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-wRpuS-gs8sE/TnIKJ9wvLSI/AAAAAAAAGNY/Rvj1hNp8tOA/s72-c/clausula-bulk-collect-plsql.JPG" height="72" width="72" /><thr:total>5</thr:total><feedburner:origLink>http://www.plsql.biz/2011/09/clausula-bulk-collect-plsql.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4FRns_eyp7ImA9WhdRGUU.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-3122976210627714299</id><published>2011-08-10T06:08:00.005+02:00</published><updated>2011-08-10T16:35:17.543+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-10T16:35:17.543+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="Librerías estándar PLSQL" /><title>PLSQL dinámico con las funciones DBM_SESSION.SET_CONTEXT y SYS_CONTEXT (¿Por qué?)</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/CfZEikaQ_RzdqwbF9OQcTL2u6Uw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/CfZEikaQ_RzdqwbF9OQcTL2u6Uw/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/CfZEikaQ_RzdqwbF9OQcTL2u6Uw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/CfZEikaQ_RzdqwbF9OQcTL2u6Uw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.plsql.biz/2011/08/dbmssession-setcontext-syscontext.html#more" imageanchor="1" style="clear:right; float:right; margin-left:1em; margin-bottom:1em"&gt;&lt;img border="0" height="190" width="254" src="http://4.bp.blogspot.com/-idI1CRscSpk/TkKRZFhbixI/AAAAAAAAGG0/f5LoypDys6Y/s320/procesamiento-sentencias-SQL-Oracle.JPG" alt="PLSQL dinámico con las funciones SYS_CONTEXT y DBM_SESSION.SET_CONTEXT" /&gt;&lt;/a&gt;&lt;/div&gt;En programación PL/SQL siempre tenemos lectores que nos hacen preguntas interesantes, en esta ocasión se nos ha preguntado acerca del motivo por el cual al utilizar &lt;b&gt;PLSQL dinámico&lt;/b&gt; hay mucha gente que utiliza las funciones estándar de Oracle &lt;b&gt;SYS_CONTEXT y DBM_SESSION.SET_CONTEXT&lt;/b&gt;, de manera que en la cláusula WHERE de cualquier consulta SQL, en lugar de utilizar simplemente literales se utiliza la función SYS_CONTEXT.&lt;br /&gt;
&lt;br /&gt;
Es decir, por qué utilizar &lt;i&gt;"WHERE valor = SYS_CONTEXT('mi_contexto','valor')"&lt;/i&gt;, en vez de, por ejemplo, la simple y más corta sentencia &lt;i&gt;"WHERE valor = 15"&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;Desde luego mi opinión está claramente a favor de utilizar siempre la función SYS_CONTEXT en lugar de literales, las razones son variadas y están relacionadas con el hecho de que utilizar literales es malo para:&lt;ul&gt;&lt;li&gt;El rendimiento de la base de datos Oracle.&lt;/li&gt;
&lt;li&gt;La utilización de la &lt;i&gt;shared pool&lt;/i&gt;.&lt;/li&gt;
&lt;li&gt;La escalabilidad de la aplicación.&lt;/li&gt;
&lt;li&gt;La seguridad (al favorecer algunos métodos de infiltración mediante inyección SQL).&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
A continuación voy a explicar un poco más en detalle sobre cómo cada uno de los aspectos anteriormente mencionados se pueden ver afectados por el uso o no uso de las funciones estándar de Oracle &lt;b&gt;DBM_SESSION.SET_CONTEXT y SYS_CONTEXT&lt;/b&gt; al utilizar PL/SQL dinámico. Para ir avanzando os diré que el problema, en lo que respecta al rendimiento, radica en que utilizar literales implica que las tasas de &lt;i&gt;hard parsing&lt;/i&gt; se incrementan significativamente (para saber que es el &lt;i&gt;parsing&lt;/i&gt; podéis leer el artículo &lt;a href="http://www.plsql.biz/2007/04/fases-durante-el-procesamiento-de-una.html"&gt;fases del procesamiento de una sentencia SQL&lt;/a&gt;), ya que diferentes valores de estos literales implican diferentes consultas SQL que la base de datos Oracle debe "parsear" desde cero al no encontrarse almacenadas en la &lt;i&gt;shared pool&lt;/i&gt;. &lt;br /&gt;
&lt;br /&gt;
No debe sorprenderos saber que para pequeñas sentencias PLSQL, &lt;b&gt;casi un 95% del tiempo de ejecución se corresponde con el tiempo de &lt;i&gt;parsing&lt;/i&gt;&lt;/b&gt; y no con el tiempo real de ejecución. Por este motivo, reducir el tiempo de &lt;i&gt;parsing&lt;/i&gt; es fundamental a la hora de mejorar el rendimiento de nuestra base de datos Oracle. Tampoco debe extrañaros que una de las principales causas de la existencia de problemas de rendimiento en las bases de datos Oracle, sea tener una alta tasa de &lt;i&gt;hard parsing&lt;/i&gt; al utilizar SQL dinámico no reutilizable.&lt;br /&gt;
&lt;br /&gt;
Lo mejor para comprender todo este entramado que estoy contando es utilizar un ejemplo. Veamos el siguiente código:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 90%;"&gt;&lt;pre&gt;CREATE OR REPLACE FUNCTION
apps.query_dinamica (p_owner dba_tables.owner%TYPE)
RETURN SYS_REFCURSOR
IS
  c_query SYS_REFCURSOR;
  v_query VARCHAR2(1000);
BEGIN
  v_query := 'SELECT table_name
              FROM   dba_tables
              WHERE  1 = 1';
  IF p_owner IS NOT NULL THEN
  -- p_owner es VARCHAR2 por eso hay que utilizar '''
     v_query := v_query ||
                ' AND owner = '''||p_owner||'''';
  END IF;

  OPEN c_query FOR v_query;

  RETURN (c_query);
  
END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
En este ejemplo de PL/SQL dinámico hemos utilizado una forma de codificar que, como veremos a continuación, nos garantiza que la base de datos Oracle tenga que hacer &lt;i&gt;hard parsing&lt;/i&gt; (sin posibilidad de acudir a la &lt;i&gt;shared pool&lt;/i&gt;) cada vez que utilizamos un valor del parámetro &lt;i&gt;p_owner&lt;/i&gt; diferente. Veamos por qué.&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 90%;"&gt;&lt;pre&gt;SQL&gt; variable x refcursor;
SQL&gt; EXECUTE :x := query_dinamica ('ASP');

PL/SQL procedure successfully completed.

SQL&gt; print x

TABLE_NAME
------------------------------
ASP_ALERT_SUBSCRIPTIONS
ASP_PROGRAM_RUN_DATES

SQL&gt;  EXECUTE :x := query_dinamica ('MWA');

PL/SQL procedure successfully completed.

SQL&gt; print x

TABLE_NAME
------------------------------
MWA_CLASS_CUSTOM_FILES

SQL&gt; SELECT sql_text, executions 
  2  FROM   v$sql
  3  WHERE  parsing_schema_name = 'APPS'
  4  AND    sql_text like '%table_name%dba_tables%1 = 1%';

SQL_TEXT             EXECUTIONS
-------------------------------
SELECT table_name
FROM   dba_tables
WHERE  1 = 1AND
owner = 'ASP'        1

SELECT table_name
FROM   dba_tables
WHERE  1 = 1AND
owner = 'MWA'        1&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Como podéis ver, cada llamada a la función &lt;i&gt;query_dinamica&lt;/i&gt; genera sentencias totalmente independientes que deben pasar por la fase de &lt;i&gt;parsing&lt;/i&gt; de forma separada. &lt;br /&gt;
&lt;br /&gt;
La solución para evitar esta circunstancia pasa por utilizar la &lt;b&gt;librería estándar Oracle CONTEXTS&lt;/b&gt;, que si bien apareció para soportar VPD (Virtual Private Database), también puede emplearse para el caso que estamos analizando. Con la aplicación CONTEXTS es posible definir atributos y asignarles valores que residirán en el UGA (User Global Area) durante la vida de la sesión, son valores de carácter privado y, por lo tanto, cada sesión utiliza los suyos propios y no pueden ser accedidos desde otras sesiones.&lt;br /&gt;
&lt;br /&gt;
Comprenderéis un poco mejor el funcionamiento de esta librería cuando vayamos resolviendo el problema que nos hemos planteado. Primero deberemos crear el contexto.&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 90%;"&gt;&lt;pre&gt;SQL&gt; CREATE CONTEXT query_dinamica_ctx 
  2  USING apps.query_dinamica;

Context created.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Al crear el contexto &lt;i&gt;query_dinamica_ctx&lt;/i&gt; hemos utilizado la cláusula USING para indicar a la base de datos Oracle que dicho contexto sólo puede ser utilizado desde nuestra función PLSQL &lt;i&gt;apps.query_dinamica&lt;/i&gt;. Esto quiere decir que la creación de atributos y la asignación de valores sólo se puede hacer dentro de la función apps.query_dinamica, lo cual añade un nivel de seguridad adicional, ya que garantiza que los usuarios no van a poder crear atributos ni modificar sus valores desde, por ejemplo, SQL*Plus. Veamos a continuación un ejemplo para que veáis claramente a que me refiero.&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 90%;"&gt;&lt;pre&gt;SQL&gt; EXECUTE DBMS_SESSION.SET_CONTEXT
  2  ('query_dinamica_ctx','owner_ctx','ASP');

BEGIN DBMS_SESSION.SET_CONTEXT
('query_dinamica_ctx','owner_ctx','ASP'); END;

*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "SYS.DBMS_SESSION", line 101
ORA-06512: at line 1&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
A continuación veremos cómo quedaría nuestra función PLSQL &lt;i&gt;apps.query_dinamica&lt;/i&gt; utilizando las funciones estándar Oracle DBM_SESSION.SET_CONTEXT y SYS_CONTEXT.&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 90%;"&gt;&lt;pre&gt;CREATE OR REPLACE FUNCTION 
apps.query_dinamica (p_owner dba_tables.owner%TYPE)
RETURN SYS_REFCURSOR
IS
  c_query SYS_REFCURSOR;
  v_query VARCHAR2(1000);
BEGIN
  v_query := 'SELECT table_name
              FROM   dba_tables
              WHERE  1 = 1';
  IF p_owner IS NOT NULL THEN
     v_query := v_query ||
                ' AND owner = 
                  SYS_CONTEXT(''query_dinamica_ctx''
                             ,''owner_ctx'')';
     DBMS_SESSION.SET_CONTEXT
       ('query_dinamica_ctx','owner_ctx',p_owner);
  END IF;

  OPEN c_query FOR v_query;

  RETURN (c_query);
  
END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Con la función PL/SQL DBMS_SESSION.SET_CONTEXT lo que hacemos es asignar el valor del parámetro &lt;i&gt;p_owner&lt;/i&gt; al atributo &lt;i&gt;owner_ctx&lt;/i&gt;, y con la función SYS_CONTEXT leemos desde el UGA el valor asignado a dicho atributo. Veamos a continuación como los cambios que hemos realizado afectan positivamente al rendimiento de nuestra función PLSQL.&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 90%;"&gt;&lt;pre&gt;SQL&gt; execute :x := query_dinamica ('ASP');

PL/SQL procedure successfully completed.

SQL&gt; print x

TABLE_NAME
------------------------------
ASP_ALERT_SUBSCRIPTIONS
ASP_PROGRAM_RUN_DATES

SQL&gt; execute :x := query_dinamica ('MWA');

PL/SQL procedure successfully completed.

SQL&gt; print x

TABLE_NAME
------------------------------
MWA_CLASS_CUSTOM_FILES

SQL&gt; SELECT sql_text, executions 
  2  FROM   v$sql
  3  WHERE  parsing_schema_name = 'APPS'
  4  AND    sql_text like '%table_name%dba_tables%SYS_CON%';

SQL_TEXT                              EXECUTIONS
------------------------------------------------
SELECT table_name
FROM   dba_tables
WHERE  1 = 1
AND owner = SYS_CONTEXT
('query_dinamica_ctx','owner_ctx')    2&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Hemos conseguido que la sentencia SQL que hemos ejecutado en un par de ocasiones, a pesar de utilizar parámetros con valores diferentes, sea exactamente la misma. De esta manera el &lt;i&gt;parsing&lt;/i&gt; se hará durante la primera ejecución, mientras que para la segunda, el gestor de la base de datos Oracle acudirá a la &lt;i&gt;shared pool&lt;/i&gt; evitándose el tiempo de &lt;i&gt;parsing&lt;/i&gt;. Hemos dicho adiós al &lt;i&gt;hard parsing&lt;/i&gt; y generado código PL/SQL reutilizable. &lt;br /&gt;
&lt;br /&gt;
Pero no sólo la utilización de las funciones CONTEXT es importante a la hora de mejorar el rendimiento de una base de datos Oracle, sino que también resulta imperativo para conseguir escalabilidad multiusuario. Al realizar &lt;i&gt;hard parsing&lt;/i&gt; es necesario bloquear o serializar las estructuras de datos en la SGA (System Global Area), lo que constituye una de las principales limitaciones a la escalabilidad de los sistemas actuales.&lt;br /&gt;
&lt;br /&gt;
Y si nos fijamos en la seguridad de los sistemas, al utilizar funciones CONTEXT estaremos evitando problemas indeseados de inyección SQL, porque podremos evitar que los parámetros de entrada a nuestros procedimientos PL/SQL sean parte de la sentencia SQL. Usando funciones CONTEXT podemos conseguir que sólo valores contextuales, valores que podremos controlar, sean valores de entrada a dichos procedimientos (ojo, no es el caso de nuestro ejemplo).&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Nota&lt;/b&gt;: en los scripts SQL también se pueden utilizar las llamadas &lt;i&gt;bind variables&lt;/i&gt; para conseguir el mismo efecto que utilizando funciones CONTEXT (las variables bind son las que en el código SQL se representan con dos punto [":"] al principio de la misma).&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-3122976210627714299?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=DPqwqXMZjJE:tUNaIMr-ZT4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=DPqwqXMZjJE:tUNaIMr-ZT4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=DPqwqXMZjJE:tUNaIMr-ZT4:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=DPqwqXMZjJE:tUNaIMr-ZT4:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=DPqwqXMZjJE:tUNaIMr-ZT4:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=DPqwqXMZjJE:tUNaIMr-ZT4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=DPqwqXMZjJE:tUNaIMr-ZT4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=DPqwqXMZjJE:tUNaIMr-ZT4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=DPqwqXMZjJE:tUNaIMr-ZT4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=DPqwqXMZjJE:tUNaIMr-ZT4:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=DPqwqXMZjJE:tUNaIMr-ZT4:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/DPqwqXMZjJE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/3122976210627714299/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=3122976210627714299&amp;isPopup=true" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3122976210627714299?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3122976210627714299?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/DPqwqXMZjJE/dbmssession-setcontext-syscontext.html" title="PLSQL dinámico con las funciones DBM_SESSION.SET_CONTEXT y SYS_CONTEXT (¿Por qué?)" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-idI1CRscSpk/TkKRZFhbixI/AAAAAAAAGG0/f5LoypDys6Y/s72-c/procesamiento-sentencias-SQL-Oracle.JPG" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://www.plsql.biz/2011/08/dbmssession-setcontext-syscontext.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkIBRnk9fSp7ImA9WhZaGUg.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-6888697594554806669</id><published>2011-07-06T14:14:00.002+02:00</published><updated>2011-07-06T14:15:57.765+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-06T14:15:57.765+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><title>Tuning y constraints (o restricciones en la base de datos Oracle)</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/RxrhoVrUkG40SsJ4QyipBVifLuk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/RxrhoVrUkG40SsJ4QyipBVifLuk/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/RxrhoVrUkG40SsJ4QyipBVifLuk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/RxrhoVrUkG40SsJ4QyipBVifLuk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.plsql.biz/2011/07/tuning-constraints-base-de-datos-oracle.html#more" imageanchor="1" style="clear:right; float:right; margin-left:1em; margin-bottom:1em"&gt;&lt;img border="0" height="195" width="200" src="http://2.bp.blogspot.com/-AqK5Zr77FXU/ThRRZ4zE0PI/AAAAAAAAGC4/1mxS1krLs98/s200/tuning-y-constraints-bbdd-oracle.JPG" alt="Tuning y constraints en la base de datos Oracle" /&gt;&lt;/a&gt;&lt;/div&gt;En este artículo continuaré con el caso de &lt;b&gt;&lt;i&gt;tuning&lt;/i&gt; PL/SQL&lt;/b&gt; planteado en el artículo &lt;a href="http://www.plsql.biz/2011/04/tuning-consultas-select-count-plsql.html"&gt;&lt;b&gt;Tuning de consultas SELECT COUNT(*)&lt;/b&gt;&lt;/a&gt; y determinaré cómo es posible mejorar aún más el rendimiento de la consulta SELECT objeto del mencionado artículo. Eso sí, para poder profundizar en el estudio del rendimiento, tuve que solicitar al lector que me hizo la pregunta inicial que me enviase los datos del esquema de la base de datos Oracle para las tablas involucradas en la consulta PLSQL SELECT. Después de un par de correos pude disponer de toda la información que necesitaba, los campos de las tablas, los índices asociados, las claves primarias (&lt;i&gt;primary keys&lt;/i&gt;), las claves extranjeras (&lt;i&gt;foreign keys&lt;/i&gt;), y las diferentes restricciones (&lt;a href="http://www.plsql.biz/2009/12/clausula-constraint-para-mejorar-el.html"&gt;&lt;i&gt;constraints&lt;/i&gt;&lt;/a&gt;) aplicadas sobre las mencionadas tablas.&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;En suma, disponía de la siguiente información incluida en las siguientes sentencias SQL:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT COUNT(*)
INTO   v_count
FROM   tabla1 t1, tabla2 t2
WHERE  t1.id = t2.id
  AND  t2.otro_id = v_otro_id;

CREATE TABLE tabla1 (
  id       NUMBER(10) NOT NULL,
  ...
  datos    VARCHAR2(250)
);

ALTER TABLE tabla1 ADD CONSTRAINT t1_pk
PRIMARY KEY (id);

CREATE TABLE tabla2 (
  otro_id  NUMBER(10) NOT NULL,
  id       NUMBER(10) NOT NULL,
  ...
  datos  VARCHAR2(250)
);

ALTER TABLE tabla2 ADD CONSTRAINT t2_pk
PRIMARY KEY (otro_id);

ALTER TABLE tabla2 ADD CONSTRAINT t2_fk1
FOREIGN KEY (id) REFERENCES t1 (id);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Lo primero que me llamó la atención a ver esta información fue que la columna ID era la clave primaria de la TABLA1 y que, a la vez, era la clave extranjera (&lt;i&gt;foreign key&lt;/i&gt;) de la TABLA2 a la TABLA1; además, TABLA2.id estaba definido como un campo no nulo (NOT NULL). Esto me daba una clara idea para poder reescribir la consulta SELECT de manera que mejorase su rendimiento, todo ello gracias a que además sabía lo siguiente:&lt;ul&gt;&lt;li&gt;La salida del SELECT no proporcionaba ningún campo de la TABLA1.&lt;/li&gt;
&lt;li&gt;Al relacionar las tablas TABLA1 y TABLA2 por el campo ID, los registros en la TABLA2 son de clave preservada, ya que TABLA1.id es un valor único y al unir dicha tabla con la TABLA2, como mucho la consulta SELECT devolverá los registros contenidos en esta última tabla una única vez.&lt;/li&gt;
&lt;li&gt;Pero además, como TABLA2.id es una clave extranjera de TABLA2 a TABLA1, todos los registros de la TABLA2 que tienen un valor no nulo del campo ID, aparecerán al menos una vez en la salida de la consulta SELECT.&lt;/li&gt;
&lt;li&gt;Por último, como TABLA2.id es un campo no nulo (NOT NULL), todos los registros de la TABLA2 aparecerán a menos una vez y como mucho una vez en dicha salida.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Por los tanto, la consulta SELECT antes mencionada es equivalente a la siguiente consulta mucho más sencilla:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT COUNT(*)
INTO   v_count
FROM   tabla2 t2
WHERE  t2.otro_id = v_otro_id;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Hemos podido por tanto eliminar una de las tablas de la consulta SELECT, con lo cual accederemos a un solo objeto de la base de datos Oracle, la TABLA2. 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; de la sentencia se ha simplificado notablemente ya que, entre otras cosas, no será necesario utilizar en índice de la clave primaria T1_PK.&lt;br /&gt;
&lt;br /&gt;
Lo que seguro que muchos os estaréis preguntando ahora es: "Si nosotros, tras realizar un sencillo análisis, hemos podido eliminar la TABLA1 de la consulta SELECT, ¿no podría hacer lo mismo el &lt;a href="http://www.plsql.biz/2007/07/el-optimizador-plsql-basado-en-normas.html"&gt;optimizador de la base de datos Oracle&lt;/a&gt;?" La respuesta es que sí, pero esto sólo ocurre para las versiones de la base de datos Oracle 11g Release 1 y posteriores, que además tengan habilitados los &lt;i&gt;constraints&lt;/i&gt; a nivel de la base de datos. Si la base de datos Oracle no utiliza &lt;i&gt;constraints&lt;/i&gt;, entonces los &lt;i&gt;constraints&lt;/i&gt; que aparecen en las definiciones de la tablas sólo se aplican a nivel de la aplicación, pero no de la base de datos. Es decir, es la aplicación la que fuerza a que se cumplan esos &lt;i&gt;constraints&lt;/i&gt; y no la base de datos misma.&lt;br /&gt;
&lt;br /&gt;
En conclusión, para realizar el &lt;i&gt;tuning&lt;/i&gt; o puesta a punto de una sentencia PL/SQL &lt;b&gt;es necesario conocer el modelo de datos y la estructura de la base de datos&lt;/b&gt; (al menos de las tablas y objetos involucrados). Si en el anterior ejemplo yo no hubiera conocido las claves primarias, las claves extranjeras, ni los campos NOT NULL, no hubiera sido capaz de poner a punto la consulta SELECT de una manera eficaz. Por otro lado, &lt;b&gt;es imprescindible que el optimizador conozca los &lt;i&gt;constraints&lt;/i&gt;&lt;/b&gt;, si decidimos que la base de datos no use &lt;i&gt;constraints&lt;/i&gt;, estaremos limitando enormemente la capacidad del optimizador que será incapaz de optimizar eficazmente cualquier consulta, en nuestro caso habría sido incapaz de eliminar el uso de la TABLA1, pero incluso puede ser incapaz de utilizar algún que otro índice, ya que los &lt;i&gt;constraints&lt;/i&gt; NOT NULL pueden tener un gran impacto sobre el optimizador a la hora de saber si puede o no utilizar un índice determinado.&lt;br /&gt;
&lt;br /&gt;
En mi opinión es mucho mejor que sea el optimizador el que elabore el plan de ejecución correcto, y no ser nosotros los que tengamos que pensar si un plan es equivalente a otro. Por lo tanto, los metadatos o &lt;i&gt;constraints&lt;/i&gt; no sólo son imprescindibles para asegurar la integridad de los datos, sino también para que el optimizador pueda elegir el mejor plan de ejecución.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Artículos relacionados&lt;/b&gt;: &lt;a href="http://www.plsql.biz/2011/01/bloqueo-tablas-hijo-por-tablas-padre.html"&gt;Bloqueo de tablas hijo al ejecutar sentencia PL/SQL sobre tablas padre&lt;/a&gt; (sobre el uso de claves extranjeras o &lt;i&gt;foreign keys&lt;/i&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-6888697594554806669?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Dluh9t3ZuUo:3Bc4M6EIAKA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Dluh9t3ZuUo:3Bc4M6EIAKA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Dluh9t3ZuUo:3Bc4M6EIAKA:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Dluh9t3ZuUo:3Bc4M6EIAKA:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=Dluh9t3ZuUo:3Bc4M6EIAKA:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Dluh9t3ZuUo:3Bc4M6EIAKA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=Dluh9t3ZuUo:3Bc4M6EIAKA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Dluh9t3ZuUo:3Bc4M6EIAKA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=Dluh9t3ZuUo:3Bc4M6EIAKA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Dluh9t3ZuUo:3Bc4M6EIAKA:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=Dluh9t3ZuUo:3Bc4M6EIAKA:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/Dluh9t3ZuUo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/6888697594554806669/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=6888697594554806669&amp;isPopup=true" title="1 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6888697594554806669?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6888697594554806669?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/Dluh9t3ZuUo/tuning-constraints-base-de-datos-oracle.html" title="Tuning y constraints (o restricciones en la base de datos Oracle)" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-AqK5Zr77FXU/ThRRZ4zE0PI/AAAAAAAAGC4/1mxS1krLs98/s72-c/tuning-y-constraints-bbdd-oracle.JPG" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://www.plsql.biz/2011/07/tuning-constraints-base-de-datos-oracle.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkQNQns9cCp7ImA9WhZUFEs.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-4242780373087665151</id><published>2011-06-03T13:20:00.003+02:00</published><updated>2011-06-07T19:39:53.568+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-07T19:39:53.568+02:00</app:edited><title>La competencia de Oracle Business Suite: Open Apps de Velneo V7</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/DLxMSF3UOBKf4SQK_bfoO9Ru4Ig/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/DLxMSF3UOBKf4SQK_bfoO9Ru4Ig/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/DLxMSF3UOBKf4SQK_bfoO9Ru4Ig/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/DLxMSF3UOBKf4SQK_bfoO9Ru4Ig/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.plsql.biz/2011/06/open-apps-velneo-v7-competencia-oracle.html#more" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="142" width="200" src="http://3.bp.blogspot.com/-CU9Hoj_2tTA/TejDUCXApJI/AAAAAAAAF90/fkOiyoTERsw/s200/velneo-aplicaciones-gestion.jpg" alt="La competencia de Oracle Business Suite: Open Apps de Velneo V7" /&gt;&lt;/a&gt;&lt;/div&gt;Hace unos días me propusieron hacer una revisión y opinar sobre &lt;b&gt;Velneo V7&lt;/b&gt;, una plataforma completa con base de datos integrada de desarrollo de aplicaciones empresariales que además incorpora plantillas de código abierto y editable FLOSS para desarrollar aplicaciones del tipo ERP (&lt;i&gt;Enterprise Resource Planning&lt;/i&gt;) o CRM (&lt;i&gt;Customer Relationship Management&lt;/i&gt;). Por lo tanto Velneo es, de alguna manera, &lt;b&gt;competencia de Oracle Business Suite&lt;/b&gt;, aunque ni por las dimensiones de una y otra empresa, ni por lo que ofrecen una aplicación y otra, esa competencia sea real.&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;Las &lt;a href="http://velneo.es/seccion/velneo-open-apps/" target="_blank" rel="nofollow"&gt;&lt;b&gt;aplicaciones de gestion&lt;/b&gt;&lt;/a&gt; desarrolladas con Velneo V7 pueden ejecutarse en múltiples plataformas (Windows, Linux, Mac o MeeGo), soportando &lt;b&gt;arquitectura Cliente/Servidor&lt;/b&gt; con escritorios monopuesto y multipuesto, SaaS, HTML Web, Web RIA y terminales móviles.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Velneo V7 es una plataforma completa&lt;/b&gt; que no necesita componentes de terceros ni DLLs, con un multiservidor que distribuye aplicaciones, datos (base de datos), web, disco y ediciones. Todos los componentes (componentes de edición, de servicio y administración, y de ejecución) se conectan al servidor para poder realizar todas las fases del ciclo de desarrollo y despliegue.&lt;br /&gt;
&lt;br /&gt;
Las aplicaciones se pueden desarrollar, administrar y ejecutar &lt;b&gt;en local, en la nube y a través de dispositivos móviles&lt;/b&gt;, soportando todos los sistemas operativos simultáneamente.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Permite controlar el ciclo de desarrollo completo&lt;/b&gt;: diseño del proyecto, diseño de la base de datos, crear la lógica del negocio, construir y generar la interfaz de usuario, y crear el software de instalación.&lt;br /&gt;
&lt;br /&gt;
Velneo V7 ha sido especialmente diseñada para desarrollar &lt;b&gt;aplicaciones de gestión empresarial&lt;/b&gt; destacando por la ejecución nativa multiplataforma (sin máquinas virtuales), el código distribuido y reutilizable, y la integración total de bases de datos, lógica del negocio e interfaz de usuario.&lt;br /&gt;
&lt;br /&gt;
La &lt;b&gt;base de datos de Velneo V7&lt;/b&gt; destaca por su rapidez y alto rendimiento en la ejecución de búsquedas y transacciones de bases de datos, su gran fiabilidad y la utilización de &lt;b&gt;programación avanzada&lt;/b&gt; sin usar sentencias SQL.&lt;br /&gt;
&lt;br /&gt;
Además, Velneo V7 dispone de un amplio catálogo de Open Apps con tutoriales y ejemplos para aprender a trabajar con la aplicación, con componentes que pueden reutilizarse en nuestro código, y con plantillas empresariales para poder desarrollar nuestras propias aplicaciones. Entre estas Open Apps destacaremos vConta (software para contabilidad), vGestion (gestión de compras, ventas y almacén), vCash (gestión de cuentas bancarias), vTodo Plus (software de planificación y productividad empresarial) y Business Center (software de gestion ERP).&lt;br /&gt;
&lt;br /&gt;
Y para terminar, dejo un enlace para todos aquellos que estén interesados en &lt;a href="http://velneo.es/info/velneo-v7/empezar-con-velneo-v7/" target="_blank" rel="nofollow"&gt;probar el software de Velneo V7&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://bit.ly/kA5kJe" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-4242780373087665151?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=u-zGEXzUrEI:bPmE5_T3sNM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=u-zGEXzUrEI:bPmE5_T3sNM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=u-zGEXzUrEI:bPmE5_T3sNM:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=u-zGEXzUrEI:bPmE5_T3sNM:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=u-zGEXzUrEI:bPmE5_T3sNM:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=u-zGEXzUrEI:bPmE5_T3sNM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=u-zGEXzUrEI:bPmE5_T3sNM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=u-zGEXzUrEI:bPmE5_T3sNM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=u-zGEXzUrEI:bPmE5_T3sNM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=u-zGEXzUrEI:bPmE5_T3sNM:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=u-zGEXzUrEI:bPmE5_T3sNM:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/u-zGEXzUrEI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/4242780373087665151/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=4242780373087665151&amp;isPopup=true" title="3 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/4242780373087665151?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/4242780373087665151?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/u-zGEXzUrEI/open-apps-velneo-v7-competencia-oracle.html" title="La competencia de Oracle Business Suite: Open Apps de Velneo V7" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-CU9Hoj_2tTA/TejDUCXApJI/AAAAAAAAF90/fkOiyoTERsw/s72-c/velneo-aplicaciones-gestion.jpg" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://www.plsql.biz/2011/06/open-apps-velneo-v7-competencia-oracle.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkUERHw5cCp7ImA9WhZQGE8.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-7711604440238906254</id><published>2011-04-26T06:27:00.001+02:00</published><updated>2011-04-26T16:30:05.228+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-26T16:30:05.228+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><title>Tuning o puesta a punto de consultas SELECT COUNT(*) en PL/SQL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/9e3lIRvDZ45IA6DfpZ4naDZKnuQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/9e3lIRvDZ45IA6DfpZ4naDZKnuQ/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/9e3lIRvDZ45IA6DfpZ4naDZKnuQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/9e3lIRvDZ45IA6DfpZ4naDZKnuQ/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://www.plsql.biz/2011/04/tuning-consultas-select-count-plsql.html#more"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 189px;" src="http://4.bp.blogspot.com/--O418HMmj7o/TbbWmH8zw6I/AAAAAAAAF6M/vuGaikNtmW4/s200/tuning-consultas-select-plsql.bmp" border="0" alt="Tuning o puesta a punto de consultas SELECT en PL/SQL" id="BLOGGER_PHOTO_ID_5599899137139262370" /&gt;&lt;/a&gt;De vez en cuando recibo consultas sobre cómo sería posible &lt;b&gt;mejorar el rendimiento de sentencias PL/SQL concretas&lt;/b&gt;. En la mayoría de los casos contestar a estas preguntas puede ser poco menos que imposible, más que nada porque realizar el &lt;b&gt;&lt;i&gt;tuning&lt;/i&gt; de una consulta PL/SQL&lt;/b&gt; sin conocer el contexto en que se ejecuta dicha consulta resulta muy complicado. Cada vez que esto ocurre siempre me asaltan preguntas como: ¿por qué se ejecuta dicha consulta?, ¿puede eliminarse la consulta y ser incluida en otro proceso?, ¿está la consulta dentro de un bucle LOOP y realmente debe formar parte del bucle?, ¿están creados todos los índices que podrían acelerar su ejecución? Por si esto fuera poco, una vez que tenemos la respuesta a preguntas como las antes mencionadas, sin duda, surgirán nuevas preguntas.&lt;br /&gt;&lt;br /&gt;No obstante, el otro día un asiduo lector de este blog me envió una &lt;b&gt;consulta SELECT&lt;/b&gt; bastante sencilla que, aún utilizando los índices de forma adecuada y ejecutándose bastante rápido, terminaba consumiendo muchos recursos de CPU en su base de datos Oracle debido a que era ejecutaba con mucha frecuencia dentro un procedimiento PLSQL. Dicho lector me pedía ayuda para realizar el &lt;i&gt;tuning&lt;/i&gt; o puesta a punto de la mencionada consulta.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;La consulta SQL era la siguiente:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT COUNT(*)&lt;br /&gt;INTO   v_count&lt;br /&gt;FROM   tabla1 t1, tabla2 t2&lt;br /&gt;WHERE  t1.id = t2.id(+)&lt;br /&gt;  AND  t2.otro_id = v_otro_id;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Lo primero que se me vino a la mente es que había muchas posibilidades de que realmente no hiciera falta ejecutar dicha sentencia, y no existe ninguna posibilidad de ejecutar de forma más rápida una sentencia SELECT que no tener que ejecutarla. El caso es que &lt;b&gt;siempre que me encuentro una cláusula COUNT(*) en una consulta SQL, tiendo a intentar eliminarla&lt;/b&gt;. La razón es que en la mayoría de los casos el procedimiento PL/SQL que la contiene, utiliza dicho COUNT(*) para hacer lo siguiente:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT COUNT(*)&lt;br /&gt;INTO   v_count&lt;br /&gt;FROM   ...;&lt;br /&gt;&lt;br /&gt;IF v_count &gt; 0 THEN&lt;br /&gt;  ejecutar_algo();&lt;br /&gt;ENF IF;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En mi opinión, en estos casos lo mejor para el rendimiento de la base de datos Oracle es reescribir el código antes mencionado y reemplazarlo por un código tan simple como este:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;ejecutar_algo();&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Si el procedimiento PLSQL ejecutar_algo() lo que hace es analizar un conjunto de resultados dentro de un bucle, terminando cuando dicho conjunto ha sido analizado en su totalidad, entonces llamar a dicho procedimiento PLSQL cuando no hay datos para procesar, significará que el conjunto de resultados inicial estará vacío y la rutina ejecutar_algo() no hará nada. Por otro lado, si existen datos para procesar, entonces el procedimiento PL/SQL se ejecutará más rápido porque habremos eliminado la primera consulta SELECT realmente innecesaria. Y si lo pensamos detenidamente, es más que probable que incluso no habiendo datos para procesar, también el rendimiento de nuestro proceso mejore.&lt;br /&gt;&lt;br /&gt;De cualquier forma, si asumimos que por cualquier motivo no hay forma de eliminar la consulta SELECT y que ésta tiene que ser realmente ejecutada, entonces deberemos pensar en qué podemos hacer para ponerla a punto o "tunearla". En el caso que os he planteado, algo que me resultó evidente nada más ver la consulta fue que &lt;b&gt;el &lt;i&gt;outer join&lt;/i&gt; podía eliminarse sin problemas&lt;/b&gt;, no en vano la primera consulta SQL es equivalente a esta otra:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT COUNT(*)&lt;br /&gt;INTO   v_count&lt;br /&gt;FROM   tabla1 t1, tabla2 t2&lt;br /&gt;WHERE  t1.id = t2.id&lt;br /&gt;  AND  t2.otro_id = v_otro_id;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;El &lt;i&gt;outer join&lt;/i&gt; desde la tabla t1 a la t2 lo que hace es devolver resultados de la tabla t1 cuando no existe ninguno asociado por el mismo id en la t2, pero en este caso el campo t2.otro_id será NULL, y el valor NULL nunca es igual a nada, por lo que el último límite de la consulta nunca se cumplirá cuando la tabla t2 no devuelve ningún registro asociado.&lt;br /&gt;&lt;br /&gt;No obstante, realizar este cambio en el código PLSQL &lt;b&gt;no implicará ninguna mejora en el rendimiento de nuestro proceso&lt;/b&gt;, y esto es debido a que el &lt;a href="http://www.plsql.biz/2007/07/el-optimizador-plsql-basado-en-normas.html"&gt;optimizador de la base de datos Oracle&lt;/a&gt; es lo suficientemente "inteligente" como para darse cuenta de que puede eliminar sin problemas el &lt;i&gt;outer join&lt;/i&gt;, y así lo hará al escribir 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; correspondiente. Lo único que realmente habremos hecho es conseguir que nuestra consulta SELECT esté escrita de forma correcta. &lt;br /&gt;&lt;br /&gt;Por lo tanto, llegado a este punto tuve que preguntarme si verdaderamente se podía hacer algo más para mejorar el rendimiento de esa sentencia SELECT tan simple, y me encontré con que sabiendo lo que sabía no era posible hacer nada. Sin conocer nada acerca del esquema de la base de datos Oracle, no podía hacer ninguna suposición, ni llegar a ninguna conclusión. ¿Realmente se podría hacer algo más conociendo el esquema y la relación existente entre las tablas t1 y t2? La respuesta es sí, pero esto será materia de otro artículo.&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/04/puesta-punto-de-sentencias-sql-tuning.html"&gt;Puesta a punto de sentencias SQL&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://www.plsql.biz/2006/08/bucles-y-problemas-de-rendimiento.html"&gt;Bucles y problemas de rendimiento&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://www.plsql.biz/2009/03/la-funcionalidad-de-muestreo-dinamico-o.html"&gt;Dinamic sampling o muestreo dinámico&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://www.plsql.biz/2009/12/clausula-constraint-para-mejorar-el.html"&gt;La cláusula CONSTRAINT&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://www.plsql.biz/2011/03/subqueries-o-joins-rendimiento-plsql.html"&gt;Subqueries o joins&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-7711604440238906254?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=UjzAXjl1-wQ:22cCarfCgY8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=UjzAXjl1-wQ:22cCarfCgY8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=UjzAXjl1-wQ:22cCarfCgY8:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=UjzAXjl1-wQ:22cCarfCgY8:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=UjzAXjl1-wQ:22cCarfCgY8:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=UjzAXjl1-wQ:22cCarfCgY8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=UjzAXjl1-wQ:22cCarfCgY8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=UjzAXjl1-wQ:22cCarfCgY8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=UjzAXjl1-wQ:22cCarfCgY8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=UjzAXjl1-wQ:22cCarfCgY8:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=UjzAXjl1-wQ:22cCarfCgY8:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/UjzAXjl1-wQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/7711604440238906254/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=7711604440238906254&amp;isPopup=true" title="5 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7711604440238906254?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7711604440238906254?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/UjzAXjl1-wQ/tuning-consultas-select-count-plsql.html" title="Tuning o puesta a punto de consultas SELECT COUNT(*) en PL/SQL" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/--O418HMmj7o/TbbWmH8zw6I/AAAAAAAAF6M/vuGaikNtmW4/s72-c/tuning-consultas-select-plsql.bmp" height="72" width="72" /><thr:total>5</thr:total><feedburner:origLink>http://www.plsql.biz/2011/04/tuning-consultas-select-count-plsql.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk8MRXw4fip7ImA9WhZTEkU.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-6695615248976674025</id><published>2011-03-16T06:55:00.000+01:00</published><updated>2011-03-16T15:14:44.236+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-16T15:14:44.236+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><title>¿Qué es mejor para el rendimiento, utilizar consultas PLSQL con subqueries o con joins?</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/HjblbnK833haf5Hra0JWef1nh-Q/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/HjblbnK833haf5Hra0JWef1nh-Q/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/HjblbnK833haf5Hra0JWef1nh-Q/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/HjblbnK833haf5Hra0JWef1nh-Q/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://www.plsql.biz/2011/03/subqueries-o-joins-rendimiento-plsql.html#more"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 198px; height: 198px;" src="http://1.bp.blogspot.com/-zwNkVOPYapI/TYDECuUoMSI/AAAAAAAAF2c/wdOtaXnqKoE/s200/subqueries-o-joins-plsql-bbdd-oracle.JPG" border="0" alt="Qué utilizar en PLSQL, subqueries o joins" id="BLOGGER_PHOTO_ID_5584679089012748578" /&gt;&lt;/a&gt;Alguna vez me han llegado cuestiones en la que se me preguntaba qué es mejor, en términos de rendimiento de las bases de datos Oracle, &lt;span style="font-weight:bold;"&gt;si utilizar en las consultas PLSQL subqueries&lt;/span&gt; (subconsultas) &lt;span style="font-weight:bold;"&gt;o utilizar joins&lt;/span&gt; (es decir, listar todas las tablas en la clausula FROM y unirlas en el WHERE). Lo primero que hay que tener claro es que escribir una consulta PL/SQL utilizando subqueries o utilizando joins es semánticamente diferente; además, utilizar una u otra forma puede derivar en que ambas consultas devuelvan resultados diferentes y que no sean directamente intercambiables.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;Lo que yo recomiendo para elegir entre un tipo de consulta u otro es, en general, hacer lo siguiente:&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;Utilizar una subquery o subconsulta&lt;/span&gt; cuando no se necesita ninguna columna de la tabla que es referenciada en la subquery.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;Utilizar un join&lt;/span&gt; en caso de necesitar alguna de las columnas.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Para intentar dejar las cosas algo más claras os dejo el siguiente ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT * &lt;br /&gt;FROM   pedidos&lt;br /&gt;WHERE  tipo_pedido IN&lt;br /&gt;       (SELECT tipo_pedido &lt;br /&gt;        FROM   tipo_pedidos);&lt;br /&gt;&lt;br /&gt;SELECT p.* &lt;br /&gt;FROM   pedidos p, &lt;br /&gt;       tipo pedidos t&lt;br /&gt;WHERE  p.tipo_pedido = t.tipo_pedido;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Mi opinión es que es mejor utilizar el primer SELECT, pero única y exclusivamente por razones semánticas, ya que la primera consulta es más fácil de "leer" que la segunda. Mientras que el primer SELECT lo que nos dice es "estoy obteniendo todos los registros de la tabla pedidos tales que el campo pedidos.tipo_pedido esté en la tabla tipo_pedidos", la segunda consulta lo que nos dice es "estoy juntando la tabla pedidos con la tabla tipo_pedidos". No obstante, &lt;span style="font-weight:bold;"&gt;para el optimizador de la base de datos Oracle ambas consultas son idénticas&lt;/span&gt; y el rendimiento de ambas será el mismo.&lt;br /&gt; &lt;br /&gt;Otro aspecto a recordar es que normalmente una subconsulta o subquery no puede reemplazarse por un join o viceversa, la razón es que a menudo los resultados que devolverán serán diferentes. Por ejemplo:&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 tipo_pedidos&lt;br /&gt; 3   WHERE tipo_pedido IN&lt;br /&gt; 4    (SELECT tipo_pedido &lt;br /&gt; 5    FROM pedidos)&lt;br /&gt; 6   /&lt;br /&gt;&lt;br /&gt;ID TIPO_PEDIDO&lt;br /&gt;-- -----------&lt;br /&gt; 1 Online&lt;br /&gt; 2 Tienda&lt;br /&gt;&lt;br /&gt;2 rows selected.&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT t.* &lt;br /&gt; 2   FROM pedidos p, tipo pedidos t&lt;br /&gt; 3   WHERE p.tipo_pedido = t.tipo_pedido&lt;br /&gt; 4   /&lt;br /&gt;&lt;br /&gt;ID TIPO_PEDIDO&lt;br /&gt;-- -----------&lt;br /&gt; 1 Online&lt;br /&gt; 1 Online&lt;br /&gt;..............&lt;br /&gt; 1 Online&lt;br /&gt; 2 Tienda&lt;br /&gt;..............&lt;br /&gt; 2 Tienda&lt;br /&gt;&lt;br /&gt;14321 rows selected.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En conclusión, el optimizador de la base de datos Oracle siempre sabrá que es lo que tiene que hacer, por lo que a la hora de construir una consulta PL/SQL y valorar si utilizar una subquery o un join, deberemos tener en cuenta que una y otra manera de construir una consulta no son, por lo general, intercambiables, y de ser posible elegir, &lt;span style="font-weight:bold;"&gt;deberemos escoger aquella que sea más sencilla de interpretar&lt;/span&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/2011/02/almacenamiento-subconsultas-bbdd-oracle.html"&gt;El almacenamiento de subconsultas PLSQL&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-6695615248976674025?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=nj_PL9r_wyc:Ovr76mjv4kE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=nj_PL9r_wyc:Ovr76mjv4kE:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=nj_PL9r_wyc:Ovr76mjv4kE:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=nj_PL9r_wyc:Ovr76mjv4kE:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=nj_PL9r_wyc:Ovr76mjv4kE:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=nj_PL9r_wyc:Ovr76mjv4kE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=nj_PL9r_wyc:Ovr76mjv4kE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=nj_PL9r_wyc:Ovr76mjv4kE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=nj_PL9r_wyc:Ovr76mjv4kE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=nj_PL9r_wyc:Ovr76mjv4kE:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=nj_PL9r_wyc:Ovr76mjv4kE:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/nj_PL9r_wyc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/6695615248976674025/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=6695615248976674025&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6695615248976674025?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6695615248976674025?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/nj_PL9r_wyc/subqueries-o-joins-rendimiento-plsql.html" title="¿Qué es mejor para el rendimiento, utilizar consultas PLSQL con subqueries o con joins?" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-zwNkVOPYapI/TYDECuUoMSI/AAAAAAAAF2c/wdOtaXnqKoE/s72-c/subqueries-o-joins-plsql-bbdd-oracle.JPG" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.plsql.biz/2011/03/subqueries-o-joins-rendimiento-plsql.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkQBRX44fyp7ImA9Wx9UEkg.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-6310624836782672635</id><published>2011-02-09T06:57:00.012+01:00</published><updated>2011-02-09T14:32:34.037+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-09T14:32:34.037+01: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="Bases de datos Oracle" /><title>Almacenamiento de subconsultas (subqueries PL/SQL) en la caché de las bases de datos Oracle</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/_r269nxQn7YV4s4kq8Fd_5Sw-CU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_r269nxQn7YV4s4kq8Fd_5Sw-CU/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/_r269nxQn7YV4s4kq8Fd_5Sw-CU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_r269nxQn7YV4s4kq8Fd_5Sw-CU/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://www.plsql.biz/2011/02/almacenamiento-subconsultas-bbdd-oracle.html#more"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 200px;" src="http://4.bp.blogspot.com/_ObxKtfPuuSQ/TVKPYHPrjfI/AAAAAAAAFxk/g8GNVj42iaw/s200/subqueries-almacenamiento-cache-bbdd-oracle.JPG" border="0" alt="Almacenamiento de subconsultas (subqueries PL/SQL) en la caché de las bases de datos Oracle" id="BLOGGER_PHOTO_ID_5571673333435174386" /&gt;&lt;/a&gt;El &lt;span style="font-weight:bold;"&gt;almacenamiento caché de subconsultas o &lt;i&gt;subqueries&lt;/i&gt; PL/SQL&lt;/span&gt; se trata de una funcionalidad de las bases de datos Oracle, denominada en inglés &lt;i&gt;scalar subquery caching&lt;/i&gt;, que se encarga de optimizar internamente la ejecución de aquellas consultas que incorporan subconsultas. El funcionamiento es bastante intuitivo, si durante la ejecución de una consulta PLSQL compleja, dicha consulta incluye alguna subquery, la base de datos Oracle intentará almacenar en la caché la salida de dicha subconsulta con el objetivo de poder reutilizar dichos datos, una y otra vez, durante la ejecución de la consulta PL/SQL principal. Obviamente esto será mucho mejor para el rendimiento de la base de datos que el tener que re-ejecutar la subconsulta múltiples veces.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;span style="font-weight:bold;"&gt;Los resultados de la subconsulta quedan almacenados en una estructura de datos interna&lt;/span&gt; o &lt;i&gt;hash table&lt;/i&gt; que, mientras dura la ejecución de la consulta PLSQL, queda residente en la memoria caché de la sesión Oracle correspondiente. Dicha estructura de datos desaparece de la caché en el momento que la consulta PL/SQL termina.&lt;br /&gt;&lt;br /&gt;Aunque nosotros como desarrolladores no podremos localizar físicamente esa &lt;i&gt;hash table&lt;/i&gt;, si que seremos capaces de "verla" en acción realizando algunas medidas bastante sencillas. Para ello podemos utilizar el siguiente ejemplo. &lt;br /&gt;&lt;br /&gt;Primero crearemos una función PL/SQL como sigue:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; CREATE OR REPLACE &lt;br /&gt;  2  FUNCTION subc (v in VARCHAR2)&lt;br /&gt;  3  RETURN NUMBER AS&lt;br /&gt;  4  BEGIN&lt;br /&gt;  5    DBMS_APPLICATION_INFO.SET_CLIENT_INFO&lt;br /&gt;  6      (userenv('client_info')+1);&lt;br /&gt;  7    RETURN LENGTH(v);&lt;br /&gt;  8  END;&lt;br /&gt;  9  /&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;La función PLSQL que hemos creado lo que hace básicamente es &lt;span style="font-weight:bold;"&gt;contar cuantas veces ha sido llamada&lt;/span&gt; almacenando el resultado en CLIENT_INFO en la vista estándar V$SESSION. &lt;br /&gt;&lt;br /&gt;A continuación invocamos la función utilizando la siguiente secuencia de comandos SQL:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; SELECT DBMS_UTILITY.GET_CPU_TIME FROM DUAL;&lt;br /&gt;&lt;br /&gt;GET_CPU_TIME&lt;br /&gt;------------&lt;br /&gt;         710&lt;br /&gt;&lt;br /&gt;SQL&gt; EXEC &lt;br /&gt;  2  DBMS_APPLICATION_INFO.SET_CLIENT_INFO(0);&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;SQL&gt; SET AUTOTRACE TRACEONLY STATISTICS&lt;br /&gt;SQL&gt; &lt;span style="color: #ff9604"&gt;SELECT OWNER, subc(OWNER) FROM ALL_TABLES;&lt;/span&gt;&lt;br /&gt;28764 rows selected.&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;---------------------------&lt;br /&gt;      .....................&lt;br /&gt;      28764  rows processed&lt;br /&gt;&lt;br /&gt;SQL&gt; SET AUTOTRACE OFF&lt;br /&gt;SQL&gt; SELECT DBMS_UTILITY.GET_CPU_TIME FROM DUAL;&lt;br /&gt;&lt;br /&gt;GET_CPU_TIME&lt;br /&gt;------------&lt;br /&gt;         997&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT USERENV('client_info') FROM DUAL;&lt;br /&gt;&lt;br /&gt;USERENV('CLIENT_INFO')&lt;br /&gt;----------------------&lt;br /&gt;28764&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;El script SQL superior llama directamente a la función PL/SQL subc desde una consulta SELECT (ver línea en color naranja), mostrando cual ha sido el tiempo de CPU consumido por dicha consulta (997-710=287) y cuantas veces fue llamada dicha función (en este caso una vez por cada registro en la tabla ALL_TABLES, es decir, &lt;span style="font-weight:bold;"&gt;28764 veces&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;A continuación, en lugar de realizar la llamada a la función PLSQL directamente desde la consulta SELECT principal, &lt;span style="font-weight:bold;"&gt;hacemos dicha llamada dentro de una subconsulta&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; SELECT DBMS_UTILITY.GET_CPU_TIME FROM DUAL;&lt;br /&gt;&lt;br /&gt;GET_CPU_TIME&lt;br /&gt;------------&lt;br /&gt;         997&lt;br /&gt;&lt;br /&gt;SQL&gt; EXEC &lt;br /&gt;  2  DBMS_APPLICATION_INFO.SET_CLIENT_INFO(0);&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;SQL&gt; SET AUTOTRACE TRACEONLY STATISTICS&lt;br /&gt;SQL&gt; &lt;span style="color: #ff9604"&gt;SELECT&lt;/span&gt;&lt;br /&gt;  2  &lt;span style="color: #ff9604"&gt;  OWNER,&lt;/span&gt;&lt;br /&gt;  3  &lt;span style="color: #ff9604"&gt;  (SELECT subc(OWNER) FROM DUAL) subc&lt;/span&gt;&lt;br /&gt;  4  &lt;span style="color: #ff9604"&gt;FROM ALL_TABLES;&lt;/span&gt;&lt;br /&gt;28764 rows selected.&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;---------------------------&lt;br /&gt;      .....................&lt;br /&gt;      28764  rows processed&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT DBMS_UTILITY.GET_CPU_TIME FROM DUAL;&lt;br /&gt;&lt;br /&gt;GET_CPU_TIME&lt;br /&gt;------------&lt;br /&gt;        1186&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT USERENV('client_info') FROM DUAL;&lt;br /&gt;&lt;br /&gt;USERENV('CLIENT_INFO')&lt;br /&gt;----------------------&lt;br /&gt;1444&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;De esta manera observamos que la función PL/SQL sólo ha sido llamada &lt;span style="font-weight:bold;"&gt;1444 veces&lt;/span&gt; (frente a las 28764 anteriores) y que el tiempo de CPU se ha visto reducido en 0,98 segundos (1186-997=189, 287-189=98). &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Nota&lt;/span&gt;: la función &lt;span style="font-weight:bold;"&gt;DBMS_UTILITY.GET_CPU_TIME&lt;/span&gt; fue introducida en la versión 10g de las bases de datos Oracle y mide el tiempo de uso de la CPU en centésimas de segundo.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-6310624836782672635?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=3dkCFIyBKMk:Fd0Fra7f3rk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=3dkCFIyBKMk:Fd0Fra7f3rk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=3dkCFIyBKMk:Fd0Fra7f3rk:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=3dkCFIyBKMk:Fd0Fra7f3rk:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=3dkCFIyBKMk:Fd0Fra7f3rk:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=3dkCFIyBKMk:Fd0Fra7f3rk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=3dkCFIyBKMk:Fd0Fra7f3rk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=3dkCFIyBKMk:Fd0Fra7f3rk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=3dkCFIyBKMk:Fd0Fra7f3rk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=3dkCFIyBKMk:Fd0Fra7f3rk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=3dkCFIyBKMk:Fd0Fra7f3rk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/3dkCFIyBKMk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/6310624836782672635/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=6310624836782672635&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6310624836782672635?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6310624836782672635?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/3dkCFIyBKMk/almacenamiento-subconsultas-bbdd-oracle.html" title="Almacenamiento de subconsultas (subqueries PL/SQL) en la caché de las bases de datos Oracle" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_ObxKtfPuuSQ/TVKPYHPrjfI/AAAAAAAAFxk/g8GNVj42iaw/s72-c/subqueries-almacenamiento-cache-bbdd-oracle.JPG" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.plsql.biz/2011/02/almacenamiento-subconsultas-bbdd-oracle.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8GRHY9fCp7ImA9Wx9XEUk.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-5419460202887544720</id><published>2011-01-04T14:22:00.006+01:00</published><updated>2011-01-04T14:30:25.864+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-04T14:30:25.864+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Utilidades PLSQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Bloqueo de tablas hijo por causa de ejecutar sentencias PL/SQL sobre tablas padre</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/a5u3DlqZay8IabGwpda9H-ZSjPs/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/a5u3DlqZay8IabGwpda9H-ZSjPs/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/a5u3DlqZay8IabGwpda9H-ZSjPs/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/a5u3DlqZay8IabGwpda9H-ZSjPs/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/TSMfk7_b1PI/AAAAAAAAFt0/2rwkL3ume9M/s1600/tabla-hijo-bloqueada-por-tabla-padre-plsql.JPG"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 194px; height: 194px;" src="http://4.bp.blogspot.com/_ObxKtfPuuSQ/TSMfk7_b1PI/AAAAAAAAFt0/2rwkL3ume9M/s200/tabla-hijo-bloqueada-por-tabla-padre-plsql.JPG" border="0" alt="Bloqueo de tablas hijo por ejecutar sentencias PLSQL sobre  tablas padre" id="BLOGGER_PHOTO_ID_5558321084544832754" /&gt;&lt;/a&gt;En versiones de la base de datos Oracle anteriores a la 9i, &lt;b&gt;cuando la clave primaria de una tabla padre&lt;/b&gt; (&lt;i&gt;parent table&lt;/i&gt;) &lt;b&gt;no se encuentra indexada en la tabla hijo&lt;/b&gt; (&lt;i&gt;child table&lt;/i&gt;), es muy probable que tengamos &lt;b&gt;problemas con los bloqueos de la tabla hijo&lt;/b&gt; que se producen, bien cuando se actualiza (con la sentencia PLSQL UPDATE) la clave primaria de la tabla padre (lo cual ocurre con relativa frecuencia ya que existen determinados trabajos que actualizan todas las columnas de una tabla incluso cuando el valor de la misma no ha cambiado), o bien cuando se realizaba el borrado (con la sentencia PL/SQL DELETE) de algún registro de la tabla padre.&lt;br /&gt;&lt;br /&gt;El caso es que en las circunstancias anteriores y para evitar que se produzca un bloqueo completo de la tabla hijo (&lt;i&gt;full table lock&lt;/i&gt;), &lt;b&gt;lo más recomendable es indexar también en la tabla hijo la clave primaria de la tabla padre&lt;/b&gt;. No obstante, esta norma de bloqueo cambió con la versión 9i de la base de datos Oracle.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;b&gt;La norma en Oracle 9i&lt;/b&gt;, aunque básicamente el bloqueo completo no se puede evitar, lo que sí que hace es &lt;b&gt;disminuir sensiblemente el tiempo que dura dicho bloqueo&lt;/b&gt;. Así, para versiones de la base de datos Oracle posteriores a la 9i, si se actualiza o se borra algún registro de una tabla padre cuya clave primaria no está indexada en la tabla hijo, dicha tabla hijo queda totalmente bloqueada sólo mientras dura la ejecución de las sentencias PLSQL UPDATE o DELETE. Es decir, el bloqueo se libera cuando las sentencias terminan y no es necesario esperar a que se ejecute la sentencia PL/SQL COMMIT. Por lo tanto, aunque la situación es mejor que para versiones anteriores a Oracle 9i, el bloqueo completo de la tabla hijo todavía ocurre.&lt;br /&gt;&lt;br /&gt;También conviene señalar que este bloqueo completo de la tabla hijo también ocurre cuando se ejecuta la &lt;a href="http://www.plsql.biz/2007/11/sql-y-plsql-la-sentencia-merge.html"&gt;&lt;b&gt;sentencia PLSQL MERGE&lt;/b&gt;&lt;/a&gt; (sentencia que apareció precisamente con la versión de la base de datos Oracle 9i). No obstante, después de la versión Oracle 11g Release 1, la ejecución de una sentencia PL/SQL MERGE no siempre produce el bloqueo de la tabla hijo, esto ocurre cuando el MERGE es simplemente un INSERT, o cuando el MERGE funciona también como un UPDATE pero dicho UPDATE no cambia la clave primaria de la tabla padre.&lt;br /&gt;&lt;br /&gt;A continuación os dejo un script SQL con el que es posible &lt;b&gt;detectar las claves primarias que no están indexadas en las tablas hijo&lt;/b&gt; correspondientes (por cierto, en inglés las claves primarias de una tabla padre se conocen, desde el punto de vista de las tablas hijos, como &lt;i&gt;foreign keys&lt;/i&gt; o claves extranjeras). El script sólo funciona para claves primarias que incluyen un máximo de ocho columnas.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 90%;"&gt;&lt;pre&gt;SELECT nombre_tabla,&lt;br /&gt;       nombre_constraint,&lt;br /&gt;       cnom1 || NVL2(cnom2,','||cnom2,NULL) ||&lt;br /&gt;                NVL2(cnom3,','||cnom3,NULL) ||&lt;br /&gt;                NVL2(cnom4,','||cnom4,NULL) ||&lt;br /&gt;                NVL2(cnom5,','||cnom5,NULL) || &lt;br /&gt;                NVL2(cnom6,','||cnom6,NULL) ||&lt;br /&gt;                NVL2(cnom7,','||cnom7,NULL) || &lt;br /&gt;                NVL2(cnom8,','||cnom8,NULL) columnas&lt;br /&gt;FROM ( SELECT uc.table_name nombre_tabla,&lt;br /&gt;              uc.constraint_name nombre_constraint,&lt;br /&gt;              MAX(DECODE(pos,1,coln,NULL)) cnom1,&lt;br /&gt;              MAX(DECODE(pos,2,coln,NULL)) cnom2,&lt;br /&gt;              MAX(DECODE(pos,3,coln,NULL)) cnom3,&lt;br /&gt;              MAX(DECODE(pos,4,coln,NULL)) cnom4,&lt;br /&gt;              MAX(DECODE(pos,5,coln,NULL)) cnom5,&lt;br /&gt;              MAX(DECODE(pos,6,coln,NULL)) cnom6,&lt;br /&gt;              MAX(DECODE(pos,7,coln,NULL)) cnom7,&lt;br /&gt;              MAX(DECODE(pos,8,coln,NULL)) cnom8,&lt;br /&gt;              count(*) ncol&lt;br /&gt;       FROM ( SELECT SUBSTR(table_name,1,30) tn,&lt;br /&gt;                     SUBSTR(constraint_name,1,30) cn,&lt;br /&gt;                     SUBSTR(column_name,1,30) coln,&lt;br /&gt;                     position pos&lt;br /&gt;              FROM user_cons_columns ) ucc,&lt;br /&gt;              user_constraints uc&lt;br /&gt;       WHERE  ucc.cn = uc.constraint_name&lt;br /&gt;         AND  uc.constraint_type = 'R'&lt;br /&gt;       GROUP BY uc.table_name, uc.constraint_name&lt;br /&gt;     ) user_cons&lt;br /&gt; WHERE ncol &gt; ALL&lt;br /&gt;     ( SELECT count(*)&lt;br /&gt;       FROM   user_ind_columns uic&lt;br /&gt;       WHERE  uic.table_name = user_cons.nombre_tabla&lt;br /&gt;         AND  uic.column_name in (cnom1, cnom2, &lt;br /&gt;                                  cnom3, cnom4,&lt;br /&gt;                                  cnom5, cnom6,&lt;br /&gt;                                  cnom7, cnom8)&lt;br /&gt;         AND  uic.column_position &lt;= user_cons.ncol&lt;br /&gt;       GROUP BY uic.index_name&lt;br /&gt;     )&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En conclusión, la clara recomendación que personalmente doy es que, si sobre una tabla padre vamos a realizar operaciones de actualización de la clave primaria, borrado de registros o &lt;i&gt;merge&lt;/i&gt; (inserción + actualización + borrado), &lt;b&gt;lo mejor es proceder a indexar dicha clave primaria en la tabla hijo&lt;/b&gt;. Además, otra circunstancia que hace recomendable la indexación de la clave primaria de la tabla padre en la tabla hijo, es que no hacerlo puede derivar en graves problemas de rendimiento.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-5419460202887544720?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=9W3Ek-_pJB0:B5oD1Op7tPs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=9W3Ek-_pJB0:B5oD1Op7tPs:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=9W3Ek-_pJB0:B5oD1Op7tPs:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=9W3Ek-_pJB0:B5oD1Op7tPs:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=9W3Ek-_pJB0:B5oD1Op7tPs:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=9W3Ek-_pJB0:B5oD1Op7tPs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=9W3Ek-_pJB0:B5oD1Op7tPs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=9W3Ek-_pJB0:B5oD1Op7tPs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=9W3Ek-_pJB0:B5oD1Op7tPs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=9W3Ek-_pJB0:B5oD1Op7tPs:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=9W3Ek-_pJB0:B5oD1Op7tPs:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/9W3Ek-_pJB0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/5419460202887544720/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=5419460202887544720&amp;isPopup=true" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5419460202887544720?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5419460202887544720?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/9W3Ek-_pJB0/bloqueo-tablas-hijo-por-tablas-padre.html" title="Bloqueo de tablas hijo por causa de ejecutar sentencias PL/SQL sobre tablas padre" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_ObxKtfPuuSQ/TSMfk7_b1PI/AAAAAAAAFt0/2rwkL3ume9M/s72-c/tabla-hijo-bloqueada-por-tabla-padre-plsql.JPG" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://www.plsql.biz/2011/01/bloqueo-tablas-hijo-por-tablas-padre.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkAFQHk4eSp7ImA9Wx9TFE4.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-7042826486272946523</id><published>2010-11-22T06:01:00.000+01:00</published><updated>2010-11-22T15:05:11.731+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-22T15:05:11.731+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><title>Diferencias entre restricciones PLSQL (cláusula CONSTRAINT) a nivel de tabla y a nivel de columna</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/0mwYmvsCiC9dlYc-XdyM3OUBe5o/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/0mwYmvsCiC9dlYc-XdyM3OUBe5o/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/0mwYmvsCiC9dlYc-XdyM3OUBe5o/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/0mwYmvsCiC9dlYc-XdyM3OUBe5o/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/TOp4KO5Lt9I/AAAAAAAAFp0/JzbFyuMNyjs/s1600/chiste-constraint-plsql.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 177px;" src="http://4.bp.blogspot.com/_ObxKtfPuuSQ/TOp4KO5Lt9I/AAAAAAAAFp0/JzbFyuMNyjs/s200/chiste-constraint-plsql.JPG" border="0" alt="Diferencias entre restricciones PLSQL (cláusula CONSTRAINT) a nivel de tabla y a nivel de columna" id="BLOGGER_PHOTO_ID_5542374408624781266" /&gt;&lt;/a&gt;Resulta de perogrullo decir que una &lt;b&gt;restricción PLSQL (cláusula CONSTRAINT)&lt;/b&gt; a nivel de columna sólo aplica a la columna sobre la que se ha definido, mientras que una restricción a nivel de tabla puede incluir todas las columnas de dicha tabla. Esta es la diferencia básica en PL/SQL, pero también conviene señalar que cualquier restricción a nivel de columna puede definirse también a nivel de tabla, sin embargo, no todas las restricciones a nivel de tabla pueden definirse a nivel de columna. &lt;br /&gt;&lt;br /&gt;Ojo, no estoy diciendo que todas las restricciones deban definirse a nivel de tabla, simplemente estoy diciendo que es posible hacerlo. De hecho, mi opinión es que, &lt;b&gt;siempre que se pueda expresar una restricción utilizando una CONSTRAINT PLSQL a nivel de columna, debemos hacerlo de esta manera&lt;/b&gt;. Una razón evidente es que una restricción a nivel de columna es sintácticamente más clara ya que resulta obvio que aplica a sólo una columna, mientras que si utilizamos una CONSTRAINT a nivel de tabla para definir la misma restricción, deberemos prestar más atención al código PL/SQL para comprender lo que hace. Pero esta no es la única razón, las hay todavía de mayor peso.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;Antes de empezar con el ejemplo, creo que resulta conveniente mencionar que, a nivel de tiempos de ejecución, las restricciones a nivel de tabla y a nivel de columna son idénticas. Es decir, no hay diferencias en los tiempos de ejecución al emplear una u otra opción.&lt;br /&gt;&lt;br /&gt;A continuación os expongo un caso mucho más indicativo de por qué deben utilizarse las restricciones a nivel de columna cuando esto es posible. Una restricción NOT NULL se implementa técnicamente en las bases de datos Oracle con una restricción de tipo CHECK. Así, por ejemplo, si creamos la siguiente tabla:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; CREATE TABLE ejemplo&lt;br /&gt;2    ( a INT NOT NULL,&lt;br /&gt;3      b INT CHECK(b IS NOT NULL),&lt;br /&gt;4      c INT,&lt;br /&gt;5      CONSTRAINT c_chk CHECK(c IS NOT NULL)&lt;br /&gt;6    );&lt;br /&gt;&lt;br /&gt;Table created.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Podremos comprobar de una manera muy sencilla que las tres restricciones aparecen definidas de la misma manera en el diccionario de datos de la base de datos Oracle.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; SELECT constraint_type,&lt;br /&gt;2           search_condition&lt;br /&gt;3    FROM   user_constraints&lt;br /&gt;4    WHERE  table_name = 'EJEMPLO';&lt;br /&gt;&lt;br /&gt;C SEARCH_CONDITION&lt;br /&gt;— ————————————————&lt;br /&gt;C "A" IS NOT NULL&lt;br /&gt;C b IS NOT NULL&lt;br /&gt;C c IS NOT NULL&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Por lo tanto, podemos ver que las tres restricciones, aunque han sido creadas de forma distinta, todas se han generado como una restricción de tipo CHECK (el valor C de la columna CONSTRAINT_TYPE es la inicial de CHECK). &lt;br /&gt;&lt;br /&gt;No obstante, si una columna es NOT NULL, debemos utilizar siempre la restricción NOT NULL en lugar de una restricción CHECK que realice el chequeo de que la columna es NOT NULL. La razón es que &lt;b&gt;el &lt;a href="http://www.plsql.biz/2007/07/el-optimizador-plsql-basado-en-normas.html"&gt;optimizador PLSQL&lt;/a&gt; de las bases de datos Oracle reconoce la existencia de las restricciones NOT NULL sobre una columna&lt;/b&gt;, mientras que no es capaz de reconocer la existencia de una restricción CHECK sobre la misma columna. Seguro que ahora muchos os estaréis preguntando, ¿y qué más da que el optimizador sea capaz de identificar si una columna es NOT NULL?&lt;br /&gt;&lt;br /&gt;Ciertamente no da lo mismo, imaginad que definimos un índice sobre la columna A de nuestra tabla EJEMPLO, en este caso el optimizador PL/SQL será capaz de utilizar dicho índice para ejecutar un COUNT(*). Sin embargo, si reemplazamos dicho índice y lo definimos sobre las columnas B o C, en ninguno de los casos el optimizador podrá utilizar dichos índices para ejecutar el COUNT(*). La razón es bien sencilla, en el primero de los casos el optimizador PLSQL sabía que la columna A era NOT NULL y que el índice sobre dicha columna apuntaba a todos y cada uno de los registros de la tabla EJEMPLO, por lo que era posible utilizar el índice para contar el número de registros de la misma. Sin embargo, al utilizar las otras dos columnas, el optimizador no es capaz de llegar a la misma conclusión porque la restricción CHECK no le proporciona la misma información que una restricción NOT NULL directa, concluyendo que los índices sobre las columnas B o C podrían no apuntar a todos los registros de la tabla, ya que las columnas con valor NULL no se incluyen en el índice.&lt;br /&gt;&lt;br /&gt;En conclusión, este hecho demuestra que &lt;b&gt;siempre es conveniente ser lo más específico posible y utilizar las restricciones a nivel de columna en lugar de a nivel de tabla cuando esto sea posible&lt;/b&gt;. De esta manera estaremos aportando más información al optimizador PLSQL.&lt;br /&gt;&lt;br /&gt;Artículos relacionados: &lt;a href="http://www.plsql.biz/2009/12/clausula-constraint-para-mejorar-el.html"&gt;Cláusula CONSTRAINT para mejorar el rendimiento de las consultas PL/SQL&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-7042826486272946523?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=QRUK2rcr2c0:apZgeYNHu9E:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=QRUK2rcr2c0:apZgeYNHu9E:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=QRUK2rcr2c0:apZgeYNHu9E:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=QRUK2rcr2c0:apZgeYNHu9E:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=QRUK2rcr2c0:apZgeYNHu9E:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=QRUK2rcr2c0:apZgeYNHu9E:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=QRUK2rcr2c0:apZgeYNHu9E:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=QRUK2rcr2c0:apZgeYNHu9E:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=QRUK2rcr2c0:apZgeYNHu9E:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=QRUK2rcr2c0:apZgeYNHu9E:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=QRUK2rcr2c0:apZgeYNHu9E:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/QRUK2rcr2c0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/7042826486272946523/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=7042826486272946523&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7042826486272946523?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7042826486272946523?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/QRUK2rcr2c0/constraint-pl-sql-nivel-tabla-columna.html" title="Diferencias entre restricciones PLSQL (cláusula CONSTRAINT) a nivel de tabla y a nivel de columna" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_ObxKtfPuuSQ/TOp4KO5Lt9I/AAAAAAAAFp0/JzbFyuMNyjs/s72-c/chiste-constraint-plsql.JPG" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.plsql.biz/2010/11/constraint-pl-sql-nivel-tabla-columna.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A08NRX88cCp7ImA9Wx5UFE0.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-7986634420256015657</id><published>2010-10-18T06:38:00.003+02:00</published><updated>2010-10-18T15:51:34.178+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-10-18T15:51:34.178+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Base de datos Oracle 11g: nueva funcionalidad errorlogging del SQL*Plus</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/8vlZQZvkh1W8okDSCPU6X7-9fPE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/8vlZQZvkh1W8okDSCPU6X7-9fPE/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/8vlZQZvkh1W8okDSCPU6X7-9fPE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/8vlZQZvkh1W8okDSCPU6X7-9fPE/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://2.bp.blogspot.com/_ObxKtfPuuSQ/TLxOCPW_UDI/AAAAAAAAFmU/WMK-MJlqL8M/s1600/gato-durmiendo-sobre-teclado-PLSQL.JPG"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 198px; height: 198px;" src="http://2.bp.blogspot.com/_ObxKtfPuuSQ/TLxOCPW_UDI/AAAAAAAAFmU/WMK-MJlqL8M/s200/gato-durmiendo-sobre-teclado-PLSQL.JPG" border="0" alt="Gato durmiendo sobre teclado de programación PL/SQL" id="BLOGGER_PHOTO_ID_5529380242893787186" /&gt;&lt;/a&gt;Hasta la versión de la base de datos Oracle 10g Release 2 no era posible &lt;b&gt;capturar en SQL*Plus los errores que se generan cuando escribíamos incorrectamente una sentencia SQL&lt;/b&gt;. Es decir, los errores conocidos como SP2 no podían ser gestionados por las funcionalidades estándar del SQL*Plus OSERROR o SQLERROR. Esto era algo normal ya que los errores SP2 no son errores del tipo OS, como el típico error "&lt;i&gt;unable to open spool file&lt;/i&gt;", ni tampoco son errores de tipo SQL o PLSQL, ya que la sentencia que hemos escrito incorrectamente en realidad no se trata de ningún comando SQL y nunca llegó a alcanzar la capa SQL o PL/SQL de la base de datos Oracle.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;Para los que todavía estén un poco confundidos y aún no hayan identificado cuáles son &lt;b&gt;los errores SQL de tipo SP2&lt;/b&gt; os dejo el siguiente ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 90%;"&gt;&lt;pre&gt;SQL &gt; SLECT * FROM empleados;&lt;br /&gt;SP2-0734: unknown command beginning "SLECT * FR..." - &lt;br /&gt;rest of line ignored.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Una vez que ya he dejado claro cuales son los errores SP2, y después de remarcar que este tipo de errores no podían ser capturados en versiones de la base de datos Oracle anteriores a la 11g, sólo queda señalar que esto ha cambiado con la versión 11g de SQL*Plus al incluir la nueva &lt;b&gt;funcionalidad errorlogging&lt;/b&gt;, una funcionalidad que se habilita mediante el siguiente comando SQL*Plus:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 90%;"&gt;&lt;pre&gt;SQL &gt; SET errorlogging ON&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;De esta manera en las versiones de la bases de datos Oracle 11g y superiores podremos guardar cualquier error, ya sea SQL, OS o SP2, en la &lt;b&gt;tabla de &lt;i&gt;logging&lt;/i&gt; denominada SPERRORLOG&lt;/b&gt;. Además, es posible marcar los errores con un identificador específico, de forma que sea mucho más sencillo encontrar nuestros registros de error propios. Por lo tanto, si durante la ejecución de un programa que utilice SQL*Plus se ha producido algún tipo de error, podremos, durante la misma sesión Oracle y utilizando sentencias SQL, chequear los errores que se han producido. Veamos un sencillo ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 90%;"&gt;&lt;pre&gt;SQL*Plus: Release 11.2.0.1.0 - &lt;br /&gt;Production on Mon Oct 18 12:55:41 2010&lt;br /&gt;&lt;br /&gt;Copyright (c) 1982, 2009, Oracle. All rights reserved.&lt;br /&gt;&lt;br /&gt;Connected to:&lt;br /&gt;Oracle9i Enterprise Edition Release 9.1.7.4.0 - Production&lt;br /&gt;With the Partitioning option&lt;br /&gt;JServer Release 9.1.7.4.0 - Production&lt;br /&gt;&lt;br /&gt;SQL &gt; SET errorlogging ON&lt;br /&gt;SQL &gt; SLECT * FROM empleados;&lt;br /&gt;SP2-0734: unknown command beginning "SLECT * FR..." - &lt;br /&gt;rest of line ignored.&lt;br /&gt;SQL &gt;&lt;br /&gt;SQL &gt; SELECT statement, message&lt;br /&gt;  2   FROM sperrorlog;&lt;br /&gt;&lt;br /&gt;STATEMENT     MESSAGE&lt;br /&gt;————————————  ———————&lt;br /&gt;SLECT * FROM  SP2-0734: unknown command beginning&lt;br /&gt;empleados;    "SLECT * FR..." - rest of line ignored.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Como podeís observar en el ejemplo, hemos utilizado la funcionalidad errorlogging contra una versión de la base de datos 9i, esto ha sido posible gracias a que dicha funcionalidad es propia de la versión 11g de SQL*Plus, por lo tanto puede utilizarse con versiones antiguas de la base de datos Oracle siempre que la versión de SQL*Plus sea la 11g o superior.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-7986634420256015657?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=WrNVe6p89I0:M7vB0GWxRcM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=WrNVe6p89I0:M7vB0GWxRcM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=WrNVe6p89I0:M7vB0GWxRcM:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=WrNVe6p89I0:M7vB0GWxRcM:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=WrNVe6p89I0:M7vB0GWxRcM:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=WrNVe6p89I0:M7vB0GWxRcM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=WrNVe6p89I0:M7vB0GWxRcM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=WrNVe6p89I0:M7vB0GWxRcM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=WrNVe6p89I0:M7vB0GWxRcM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=WrNVe6p89I0:M7vB0GWxRcM:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=WrNVe6p89I0:M7vB0GWxRcM:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/WrNVe6p89I0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/7986634420256015657/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=7986634420256015657&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7986634420256015657?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7986634420256015657?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/WrNVe6p89I0/set-errorlogging-sql-plus-oracle-11g.html" title="Base de datos Oracle 11g: nueva funcionalidad errorlogging del SQL*Plus" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_ObxKtfPuuSQ/TLxOCPW_UDI/AAAAAAAAFmU/WMK-MJlqL8M/s72-c/gato-durmiendo-sobre-teclado-PLSQL.JPG" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.plsql.biz/2010/10/set-errorlogging-sql-plus-oracle-11g.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk4CQ3szfSp7ImA9Wx5XEEg.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-1916387523400812370</id><published>2010-09-09T06:39:00.000+02:00</published><updated>2010-09-09T19:56:02.585+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-09T19:56:02.585+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>Ejecución de cursores PLSQL y sentencias DML utilizando SQL dinámico nativo (Native Dynamic SQL)</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/36eJRBATrxMojMl2KsrPhwd3cTA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/36eJRBATrxMojMl2KsrPhwd3cTA/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/36eJRBATrxMojMl2KsrPhwd3cTA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/36eJRBATrxMojMl2KsrPhwd3cTA/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://2.bp.blogspot.com/_ObxKtfPuuSQ/TIkfSjRc64I/AAAAAAAAFio/UTgo95VGbj0/s1600/SQL-dinamico-nativo-cursor-update.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 203px;" src="http://2.bp.blogspot.com/_ObxKtfPuuSQ/TIkfSjRc64I/AAAAAAAAFio/UTgo95VGbj0/s320/SQL-dinamico-nativo-cursor-update.jpg" border="0" alt="Ejecución de cursores PLSQL y sentencias DML utilizando SQL dinámico nativo" id="BLOGGER_PHOTO_ID_5514973622258428802" /&gt;&lt;/a&gt;Una de las preguntas más frecuentes que me suele hacer la gente es acerca de la posibilidad de &lt;b&gt;definir un &lt;a href="http://www.plsql.biz/2007/03/procedimientos-y-funciones-en-plsql.html"&gt;procedimiento PL/SQL&lt;/a&gt; en el que se declaren múltiples &lt;a href="http://www.plsql.biz/2006/12/cursores-en-plsql.html"&gt;cursores&lt;/a&gt;&lt;/b&gt; en base al valor de los parámetros de entrada de dicho procedimiento. Las preguntas suelen incluir condiciones muy variadas, pero lo normal es que los implicados sólo necesiten hacer variable la cláusula WHERE y que el resto del cuerpo del cursor PLSQL se mantenga fijo. Cuando esto ocurre yo siempre contesto remitiendo a los que preguntan al artículo que escribí sobre el &lt;a href="http://www.plsql.biz/2008/08/paquete-dbmssql-para-utilizar-sql.html"&gt;&lt;b&gt;paquete estándar PL/SQL DBMS_SQL&lt;/b&gt;&lt;/a&gt;, un paquete que &lt;b&gt;permite crear sentencias SQL dinámicas&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Si he decidido escribir un artículo nuevo al respecto es por el hecho de que &lt;b&gt;resulta mucho más legible un código que utilice directamente SQL dinámico nativo&lt;/b&gt;, es decir, un código en el que no se utiliza el paquete estándar DBMS_SQL. A continuación os dejo un sencillo ejemplo de cómo se podría construir, utilizando SQL dinámico nativo, un &lt;b&gt;procedimiento PLSQL en el que se define un cursor cuya cláusula WHERE varíe en función de un parámetro&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE OR REPLACE PROCEDURE varcursor&lt;br /&gt;(par1 IN NUMBER DEFAULT NULL)&lt;br /&gt;AS&lt;br /&gt;  l_query VARCHAR2(500);&lt;br /&gt;  TYPE tcursor IS REF CURSOR;&lt;br /&gt;  l_cursor tcursor;&lt;br /&gt;  l_cursor_rec dba_users%ROWTYPE;&lt;br /&gt;BEGIN&lt;br /&gt;  l_query := 'SELECT * FROM dba_users WHERE 1=1';&lt;br /&gt;  IF par1 IS NOT NULL THEN&lt;br /&gt;    l_query := l_query ||&lt;br /&gt;               ' AND user_id = ' || par1;&lt;br /&gt;  END IF;&lt;br /&gt;  OPEN l_cursor FOR l_query;&lt;br /&gt;  LOOP&lt;br /&gt;    FETCH l_cursor INTO l_cursor_rec;&lt;br /&gt;    EXIT WHEN l_cursor%NOTFOUND;&lt;br /&gt;    DBMS_OUTPUT.PUT_LINE&lt;br /&gt;      (l_cursor_rec.username);&lt;br /&gt;  END LOOP;&lt;br /&gt;  CLOSE l_cursor;&lt;br /&gt;END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En nuestro ejemplo, el procedimiento &lt;i&gt;varcursor&lt;/i&gt; sacará por pantalla el &lt;i&gt;username&lt;/i&gt; con el &lt;i&gt;user_id&lt;/i&gt; que se le pase como parámetro, y si el parámetro se pasa con valor NULL, entonces sacara por pantalla todos los &lt;i&gt;username&lt;/i&gt; de la tabla &lt;i&gt;dba_users&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Por otro lado, muchas otras preguntas hacen referencia a la &lt;b&gt;utilización de SQL dinámico nativo con sentencias SQL DML&lt;/b&gt;. De todas las sentencias DML la que se lleva la palma es las &lt;b&gt;sentencia UPDATE&lt;/b&gt; y, en este caso concreto, las preguntas hacen referencia tanto a la posibilidad de utilizar una cláusula WHERE variable, como a la posibilidad de modificar o no una determinada columna en base a si se pasa o no un determinado parámetro.  &lt;br /&gt;&lt;br /&gt;Antes de poneros un ejemplo concreto, os diré que la implementación de una cláusula WHERE variable se realiza de forma similar a la utilizada en el procedimiento &lt;i&gt;varcursor&lt;/i&gt;, sin embargo, para el caso de que una columna se actualice según se pase o no un determinado parámetro, es mejor no utilizar SQL dinámico. Veamos ahora un ejemplo concreto.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE OR REPLACE PROCEDURE varupdate&lt;br /&gt;(vsal IN NUMBER DEFAULT NULL,&lt;br /&gt; veid IN NUMBER DEFAULT NULL)&lt;br /&gt;AS&lt;br /&gt;  l_query VARCHAR2(500);&lt;br /&gt;BEGIN&lt;br /&gt;  l_query := 'UPDATE empleados&lt;br /&gt;              SET fecha_actualizacion = SYSDATE,&lt;br /&gt;              salario = NVL(:vsal, salario)&lt;br /&gt;              WHERE 1=1';&lt;br /&gt;  IF par2 IS NOT NULL THEN&lt;br /&gt;    l_query := l_query ||&lt;br /&gt;               ' AND empleado_id = :veid';&lt;br /&gt;  END IF;&lt;br /&gt;  EXECUTE IMMEDIATE l_query USING vsal, veid;&lt;br /&gt;END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En el ejemplo podéis ver que el primer parámetro es el que determina si la columna &lt;i&gt;salario&lt;/i&gt; se actualiza o no, sin embargo, dicha columna la hemos incluido en el bloque estático de la sentencia SQL UPDATE utilizando la función NVL(), de manera que si el parámetro &lt;i&gt;vsal&lt;/i&gt; toma el valor NULL, entonces actualizaremos la columna &lt;i&gt;salario&lt;/i&gt; con el mismo valor que tenía anteriormente. Esto es mucho más sencillo que utilizar un IF y cambiar dinámicamente la sentencia UPDATE en base a si el parámetro &lt;i&gt;vsal&lt;/i&gt; toma el valor NULL o no. No obstante, alguien podría pensar que utilizar el IF puede ahorrar tiempo de procesamiento ya que se evitaría el tener que actualizar una columna innecesariamente, sin embargo, puesto que los índices no tienen que reconstruirse cuando el valor de una columna no cambia realmente, el aumento en tiempo de procesamiento es mínimo y &lt;b&gt;lo más conveniente es utilizar SQL estático y la función NVL()&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;En cambio, para el parámetro que se utiliza en la cláusula WHERE de nuestro UPDATE, hemos utilizado la misma técnica que en el procedimiento &lt;i&gt;varcursor&lt;/i&gt;, es decir, hemos empleado un IF para así utilizar o no la condición asociada con dicho parámetro.&lt;br /&gt;&lt;br /&gt;En conclusión, nuestro procedimiento actualizará la columna &lt;i&gt;salario&lt;/i&gt; del empleado con id &lt;i&gt;veid&lt;/i&gt; al valor &lt;i&gt;vsal&lt;/i&gt;, pero si &lt;i&gt;vsal&lt;/i&gt; es NULL, entonces el valor de la columna &lt;i&gt;salario&lt;/i&gt; continuará siendo el mismo y sólo se habrá actualizado el valor de la columna &lt;i&gt;fecha_actualizacion&lt;/i&gt;. Y si &lt;i&gt;veid&lt;/i&gt; toma el valor de NULL, entonces el valor de la columna &lt;i&gt;fecha_actualizacion&lt;/i&gt; se actualizará para todos los empleados, ocurriendo lo mismo para la columna &lt;i&gt;salario&lt;/i&gt; que tomará el valor &lt;i&gt;vsal&lt;/i&gt;, siempre que &lt;i&gt;vsal&lt;/i&gt; no sea NULL, en cuyo caso dicha columna no cambiaría.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-1916387523400812370?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=m62AfITqdXg:AiEbRhJOBr4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=m62AfITqdXg:AiEbRhJOBr4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=m62AfITqdXg:AiEbRhJOBr4:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=m62AfITqdXg:AiEbRhJOBr4:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=m62AfITqdXg:AiEbRhJOBr4:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=m62AfITqdXg:AiEbRhJOBr4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=m62AfITqdXg:AiEbRhJOBr4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=m62AfITqdXg:AiEbRhJOBr4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=m62AfITqdXg:AiEbRhJOBr4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=m62AfITqdXg:AiEbRhJOBr4:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=m62AfITqdXg:AiEbRhJOBr4:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/m62AfITqdXg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/1916387523400812370/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=1916387523400812370&amp;isPopup=true" title="8 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/1916387523400812370?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/1916387523400812370?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/m62AfITqdXg/sql-dinamico-nativo-cursor-dml-update.html" title="Ejecución de cursores PLSQL y sentencias DML utilizando SQL dinámico nativo (Native Dynamic SQL)" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_ObxKtfPuuSQ/TIkfSjRc64I/AAAAAAAAFio/UTgo95VGbj0/s72-c/SQL-dinamico-nativo-cursor-update.jpg" height="72" width="72" /><thr:total>8</thr:total><feedburner:origLink>http://www.plsql.biz/2010/09/sql-dinamico-nativo-cursor-dml-update.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0QDQn07eCp7ImA9Wx5TFE8.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-7268010763774960352</id><published>2010-07-29T19:08:00.010+02:00</published><updated>2010-07-29T19:42:53.300+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-29T19:42:53.300+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>La cláusula PIPELINED en las funciones PL/SQL y la excepción NO_DATA_NEEDED</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/oCsYqYgqTenj_45YeVawRw_MNik/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/oCsYqYgqTenj_45YeVawRw_MNik/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/oCsYqYgqTenj_45YeVawRw_MNik/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/oCsYqYgqTenj_45YeVawRw_MNik/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://2.bp.blogspot.com/_ObxKtfPuuSQ/TFG7DZFx4uI/AAAAAAAAFf4/s5wulKi_hS4/s1600/pipelined-PLSQL-chiste.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 187px; height: 200px;" src="http://2.bp.blogspot.com/_ObxKtfPuuSQ/TFG7DZFx4uI/AAAAAAAAFf4/s5wulKi_hS4/s200/pipelined-PLSQL-chiste.jpg" border="0" alt="Pipelined functions o funciones tubería en PL/SQL" id="BLOGGER_PHOTO_ID_5499382286945215202" /&gt;&lt;/a&gt;En esta entrada explicaré para que sirve la &lt;b&gt;cláusula PIPELINED&lt;/b&gt; en las &lt;a href="http://www.plsql.biz/2007/03/procedimientos-y-funciones-en-plsql.html"&gt;&lt;b&gt;funciones PL/SQL&lt;/b&gt;&lt;/a&gt;, y como se debe utilizar la excepción &lt;b&gt;NO_DATA_NEEDED&lt;/b&gt;, que nada tiene que ver con la excepción NO_DATA_FOUND, para controlar las funciones PLSQL que incluyen dicha cláusula y que en inglés se denominan &lt;b&gt;&lt;i&gt;pipelined functions&lt;/i&gt;&lt;/b&gt;. Primero quiero remarcar que la funcionalidad &lt;i&gt;pipelined&lt;/i&gt; fue introducida por primera vez en la versión 9i de las bases de datos Oracle. Básicamente, el uso de la cláusula PIPELINED resulta de gran utilidad y es prácticamente imprescindible cuando necesitamos que en lugar de una tabla sea una rutina PL/SQL la que nos sirva como fuente de datos.&lt;br /&gt;&lt;br /&gt;La mejor forma de explicar el funcionamiento de esta cláusula es con un &lt;span style="font-weight:bold;"&gt;ejemplo&lt;/span&gt;. &lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; CREATE OR REPLACE FUNCTION&lt;br /&gt;  2    generador_numeros (n IN NUMBER DEFAULT NULL)&lt;br /&gt;  3    RETURN sys.odciNumberList&lt;br /&gt;  4    PIPELINED&lt;br /&gt;  5    AS&lt;br /&gt;  6      BEGIN&lt;br /&gt;  7        FOR i IN 1 .. NVL(n,100)&lt;br /&gt;  8        LOOP&lt;br /&gt;  9          PIPE ROW(i);&lt;br /&gt; 10        END LOOP;&lt;br /&gt; 11        DBMS_OUTPUT.PUT_LINE&lt;br /&gt; 12          ('Ejecutando return');&lt;br /&gt; 13        RETURN;&lt;br /&gt; 14      END;&lt;br /&gt; 15  /&lt;br /&gt;&amp;nbsp;&lt;br /&gt;Function created&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En el ejemplo la &lt;b&gt;cláusula PIPELINED&lt;/b&gt; permite que la función &lt;i&gt;generador_numeros&lt;/i&gt; &lt;span style="font-weight:bold;"&gt;funcione exactamente como una tabla&lt;/span&gt;. Veamos como.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; SELECT * FROM TABLE(generador_numeros(4));&lt;br /&gt;&amp;nbsp;&lt;br /&gt;COLUMN_VALUE&lt;br /&gt;------------&lt;br /&gt;           1&lt;br /&gt;           2&lt;br /&gt;           3&lt;br /&gt;           4&lt;br /&gt;&amp;nbsp;&lt;br /&gt;Ejecutando return&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Una vez explicado para que sirve la &lt;b&gt;cláusula PIPELINED&lt;/b&gt;, ya es posible explicar como se debe utilizar la excepción &lt;b&gt;NO_DATA_NEEDED&lt;/b&gt; dentro de este tipo de funciones PLSQL, de hecho se trata de una excepción muy importante cuando estamos escribiendo el código de una &lt;i&gt;pipelined function&lt;/i&gt;. Si alguna vez habéis utilizado la cláusula PIPELINED y dentro de la función PL/SQL asociada no habéis utilizado la mencionada excepción, es más que probable que en vuestro código tengáis un &lt;i&gt;bug&lt;/i&gt; en estado latente. Ahondando un poco más en el tema, os diré que la excepción &lt;b&gt;NO_DATA_NEEDED&lt;/b&gt; aparece cuando una función que utiliza la cláusula PIPELINED puede devolver más datos pero la sentencia SQL que invoca dicha función no los ha solicitado.&lt;br /&gt;&lt;br /&gt;Como anteriormente, la mejor forma de explicar cuando aparece y como debemos manejar la excepción &lt;b&gt;NO_DATA_NEEDED&lt;/b&gt; es con un ejemplo.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; SELECT * FROM TABLE(generador_numeros(4))&lt;br /&gt;  2  WHERE rownum &lt; 3;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;COLUMN_VALUE&lt;br /&gt;------------&lt;br /&gt;           1&lt;br /&gt;           2&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Aparentemente la función PL/SQL &lt;i&gt;generador_numeros&lt;/i&gt; ha funcionado correctamente, pero si nos fijamos bien, la salida por pantalla que yo había incluido al final de la función no se ha ejecutado, es decir, en la pantalla no aparece por ningún lado el esperado "&lt;i&gt;Ejecutando Return&lt;/i&gt;". Simplemente esa parte del código no se ha ejecutado porque la sentencia SQL con la que hemos invocado la función &lt;i&gt;generador_numeros&lt;/i&gt; no lo necesitaba.&lt;br /&gt;&lt;br /&gt;Ciertamente todo parece haber funcionado correctamente, sin embargo, aunque haya permanecido oculto a nuestros ojos, &lt;b&gt;se ha generado una excepción NO_DATA_NEEDED&lt;/b&gt;. En este sentido, la excepción &lt;b&gt;NO_DATA_NEEDED&lt;/b&gt; se trata de una excepción totalmente diferente a todas las demás, ya que si ésta no se trata dentro del código, simplemente es ignorada (por contra, el resto de excepciones, en caso de no ser tratadas, generan un error).&lt;br /&gt;&lt;br /&gt;A continuación cambiaré someramente el código de nuestra función PLSQL pipelined para demostrar lo que estoy diciendo.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; CREATE OR REPLACE FUNCTION&lt;br /&gt;  2    generador_numeros (n IN NUMBER DEFAULT NULL)&lt;br /&gt;  3    RETURN sys.odciNumberList&lt;br /&gt;  4    PIPELINED&lt;br /&gt;  5    AS&lt;br /&gt;  6      BEGIN&lt;br /&gt;  7        FOR i IN 1 .. NVL(n,100)&lt;br /&gt;  8        LOOP&lt;br /&gt;  9          PIPE ROW(i);&lt;br /&gt; 10        END LOOP;&lt;br /&gt; 11        DBMS_OUTPUT.PUT_LINE&lt;br /&gt; 12          ('Ejecutando return');&lt;br /&gt; 13        RETURN;&lt;br /&gt; 14      EXCEPTION&lt;br /&gt; 15        WHEN NO_DATA_NEEDED&lt;br /&gt; 16        THEN&lt;br /&gt; 17          DBMS_OUTPUT.PUT_LINE&lt;br /&gt; 18            ('Ejecutando excepcion');&lt;br /&gt; 19          RETURN;&lt;br /&gt; 20      END;&lt;br /&gt; 21  /&lt;br /&gt;&amp;nbsp;&lt;br /&gt;Function created&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Ahora si ejecutamos nuestra sentencia SQL obtendremos lo siguiente:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; SELECT * FROM TABLE(generador_numeros(4))&lt;br /&gt;  2  WHERE rownum &lt; 3;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;COLUMN_VALUE&lt;br /&gt;------------&lt;br /&gt;           1&lt;br /&gt;           2&lt;br /&gt;&amp;nbsp;&lt;br /&gt;Ejecutando excepcion&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Es decir, vemos que la salida por pantalla muestra el texto "&lt;i&gt;Ejecutando excepcion&lt;/i&gt;", quedando así demostrado que &lt;span style="font-weight:bold;"&gt;el bloque EXCEPTION se ha ejecutado&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Por último, sólo me queda mencionar que utilizando la sentencia &lt;span style="font-weight:bold;"&gt;WHEN OTHERS&lt;/span&gt; en lugar de &lt;span style="font-weight:bold;"&gt;WHEN NO_DATA_NEEDED&lt;/span&gt;, habríamos conseguido el mismo resultado, sin embargo no sería la manera correcta de hacerlo ya que estaríamos enmascarando otros errores que podrían generarse por otros motivos. La &lt;span style="font-weight:bold;"&gt;excepción NO_DATA_NEEDED&lt;/span&gt; está especialmente diseñada para tratar este tipo de situaciones y debe ser utilizada de forma apropiada cuando estemos escribiendo una &lt;span style="font-weight:bold;"&gt;función PL/SQL que utilice la cláusula PIPELINED&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-7268010763774960352?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=D3c9fBr0Bp8:j-zqTHb85BI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=D3c9fBr0Bp8:j-zqTHb85BI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=D3c9fBr0Bp8:j-zqTHb85BI:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=D3c9fBr0Bp8:j-zqTHb85BI:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=D3c9fBr0Bp8:j-zqTHb85BI:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=D3c9fBr0Bp8:j-zqTHb85BI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=D3c9fBr0Bp8:j-zqTHb85BI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=D3c9fBr0Bp8:j-zqTHb85BI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=D3c9fBr0Bp8:j-zqTHb85BI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=D3c9fBr0Bp8:j-zqTHb85BI:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=D3c9fBr0Bp8:j-zqTHb85BI:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/D3c9fBr0Bp8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/7268010763774960352/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=7268010763774960352&amp;isPopup=true" title="5 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7268010763774960352?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7268010763774960352?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/D3c9fBr0Bp8/pipelined-funcion-plsql-no-data-needed.html" title="La cláusula PIPELINED en las funciones PL/SQL y la excepción NO_DATA_NEEDED" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_ObxKtfPuuSQ/TFG7DZFx4uI/AAAAAAAAFf4/s5wulKi_hS4/s72-c/pipelined-PLSQL-chiste.jpg" height="72" width="72" /><thr:total>5</thr:total><feedburner:origLink>http://www.plsql.biz/2010/07/pipelined-funcion-plsql-no-data-needed.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0EGSXw9eCp7ImA9WxFWGUk.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-1109011665066103671</id><published>2010-06-07T21:44:00.008+02:00</published><updated>2010-06-07T22:13:48.260+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-07T22:13:48.260+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Creación diferida de segmentos (Deferred Segment Creation), nueva funcionalidad de la release 2 de Oracle 11g</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/-hxicrjBnkmf-KcbfW3GTsll8vA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/-hxicrjBnkmf-KcbfW3GTsll8vA/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/-hxicrjBnkmf-KcbfW3GTsll8vA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/-hxicrjBnkmf-KcbfW3GTsll8vA/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/TA1OMdzjCzI/AAAAAAAAFbc/11D3alKg5aE/s1600/creacion-diferida-segmentos-PLSQL-SQL.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://4.bp.blogspot.com/_ObxKtfPuuSQ/TA1OMdzjCzI/AAAAAAAAFbc/11D3alKg5aE/s200/creacion-diferida-segmentos-PLSQL-SQL.JPG" border="0" alt="Programación PLSQL y la creación diferida de segmentos" id="BLOGGER_PHOTO_ID_5480122297645992754" /&gt;&lt;/a&gt;En las versiones anteriores a la &lt;span style="font-weight:bold;"&gt;release 2 de la base de datos Oracle 11g&lt;/span&gt;, cuando se creaba cualquier objeto en la base de datos utilizando una sentencia SQL o PL/SQL, ya fuera un tabla, un índice o cualquier otro objeto que requiriese ser almacenado, el gestor de la base de datos creaba los segmentos necesarios y asignaba un tamaño inicial a los mismos, un tamaño que podía ser pequeño, de unos 64 Kbytes mínimo, pero dicho espacio ya no podía ser utilizado para otras necesidades. En los tiempos actuales, un tamaño de 64 Kbytes no es nada, pero si algo tan pequeño se tienen que repetir muchas veces, entonces el consumo de recursos de almacenamiento puede llegar a ser bastante grande.&lt;br /&gt;&lt;br /&gt;Por ejemplo, podemos pensar en alguna aplicación que necesitase crear cientos o miles de tablas, tablas que nunca van a ser utilizadas pero cuyos segmentos de almacenamiento tienen que ser creados de todas formas. Algunos pueden preguntarse por qué un aplicación puede necesitar crear tablas que nunca van a ser utilizadas, este hecho no es tan extraño ya que existen muchas aplicaciones, como por ejemplo Oracle Financials, que ofrecen funcionalidades que requieren el uso de determinadas tablas y dichas tablas sólo contienen datos cuando se hace uso de dichas funcionalidades. Para evitar el consumo de almacenamiento en este tipo de situaciones, la release 2 de la base de datos Oracle 11g incorpora una &lt;span style="font-weight:bold;"&gt;nueva funcionalidad&lt;/span&gt; conocida como &lt;span style="font-weight:bold;"&gt;creación diferida de segmentos&lt;/span&gt; (&lt;span style="font-weight:bold;"&gt;&lt;i&gt;Deferred Segment Creation&lt;/i&gt;&lt;/span&gt;), funcionalidad que a continuación explicaremos y analizaremos.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;La funcionalidad de &lt;span style="font-weight:bold;"&gt;creación diferida de segmentos&lt;/span&gt; puede establecerse a nivel de sistema, a nivel de sesión, o incluso a nivel de sentencia SQL DDL. Veamos a continuación los diferentes comandos SQL que permiten realizar este cambio:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;ALTER SYSTEM SET deferred_segment_creation=true;&lt;br /&gt;&lt;br /&gt;ALTER SESSION SET deferred_segment_creation=true;&lt;br /&gt;&lt;br /&gt;CREATE TABLE table_name&lt;br /&gt;....................&lt;br /&gt;SEGMENT CREATION DEFERRED;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Obviamente el &lt;span style="font-weight:bold;"&gt;parámetro &lt;i&gt;deferred_segment_creation&lt;/i&gt;&lt;/span&gt; puede inicializarse desde el fichero &lt;i&gt;init.ora&lt;/i&gt; y, por defecto, es decir, si no se le asigna un valor en concreto en dicho fichero, tomará el valor de &lt;i&gt;true&lt;/i&gt;, por lo que la funcionalidad de creación diferida de segmentos estará habilitada a menos que hayamos decidido cambiar el valor de dicho parámetro a &lt;i&gt;false&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Por otro lado, también conviene mencionar que es posible forzar la creación de los segmentos a nivel de sentencia SQL utilizando la cláusula &lt;i&gt;SEGMENT CREATION IMMEDIATE&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Por lo tanto, cuando la funcionalidad de creación diferida de segmentos está habilitada, &lt;span style="font-weight:bold;"&gt;una tabla&lt;/span&gt; de la base de datos Oracle &lt;span style="font-weight:bold;"&gt;no consumirá recursos de almacenamiento hasta que se inserta el primer registro&lt;/span&gt; en dicha tabla, esto es también extensible a cualquier índice u objeto que esté asociado con dicha tabla. &lt;br /&gt;&lt;br /&gt;Con la &lt;span style="font-weight:bold;"&gt;release 2 de la base de datos Oracle 11g&lt;/span&gt;, la tabla del sistema &lt;span style="font-weight:bold;"&gt;&lt;i&gt;USER_TABLES&lt;/i&gt;&lt;/span&gt; incorpora un nuevo campo denominado &lt;i&gt;SEGMENT_CREATED&lt;/i&gt;, campo que tomará el valor de NO en el momento de la creación de una tabla siempre que hayamos utilizado la cláusula &lt;i&gt;SEGMENT CREATION DEFERRED&lt;/i&gt; o que el parámetro &lt;i&gt;deferred_segment_creation&lt;/i&gt; tenga el valor de &lt;i&gt;true&lt;/i&gt;. Sólo en el instante en que se inserte el primer registro en dicha tabla, el mencionado campo &lt;i&gt;SEGMENT_CREATED&lt;/i&gt; pasará a tomar el valor de YES.&lt;br /&gt;&lt;br /&gt;Otras tablas del sistema que pueden ser útiles para verificar la utilización de segmentos de almacenamiento son &lt;span style="font-weight:bold;"&gt;&lt;i&gt;USER_EXTENTS&lt;/i&gt;&lt;/span&gt; y &lt;span style="font-weight:bold;"&gt;&lt;i&gt;USER_SEGMENTS&lt;/i&gt;&lt;/span&gt;, ambas tablas muestran información muy similar por lo que en el siguiente ejemplo sólo utilizaré una de ellas.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; CREATE TABLE p1 (&lt;br /&gt;     a INT CONSTRAINT p1_pk PRIMARY KEY,&lt;br /&gt;     b INT CONSTRAINT p1_b UNIQUE,&lt;br /&gt;     c CLOB )&lt;br /&gt;     LOB (c) STORED AS p1_c&lt;br /&gt;     SEGMENT CREATION DEFERRED;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT segment_created&lt;br /&gt;     FROM user_tables&lt;br /&gt;     WHERE table_name = 'P1';&lt;br /&gt;&lt;br /&gt;SEGMENT_CREATED&lt;br /&gt;---------------&lt;br /&gt;NO&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT &lt;br /&gt;       segment_name,&lt;br /&gt;       segment_type,&lt;br /&gt;       bytes&lt;br /&gt;     FROM user_segments&lt;br /&gt;     WHERE segment_name like 'P1%';&lt;br /&gt;&lt;br /&gt;no rows selected.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Con el comando SQL &lt;i&gt;CREATE TABLE&lt;/i&gt;, que incorpora la cláusula &lt;i&gt;SEGMENT CREATION DEFERRED&lt;/i&gt;, hemos creado la tabla P1. Después, la información que devuelven las sentencias &lt;i&gt;SELECT&lt;/i&gt; nos demuestra que los segmentos de almacenamiento no han sido creados todavía.&lt;br /&gt;&lt;br /&gt;Sigamos con el ejemplo.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; INSERT INTO p1&lt;br /&gt;     VALUES (1,2,'Prueba PLSQL');&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT segment_created&lt;br /&gt;     FROM user_tables&lt;br /&gt;     WHERE table_name = 'P1';&lt;br /&gt;&lt;br /&gt;SEGMENT_CREATED&lt;br /&gt;---------------&lt;br /&gt;YES&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT &lt;br /&gt;       segment_name,&lt;br /&gt;       segment_type,&lt;br /&gt;       bytes&lt;br /&gt;     FROM user_segments&lt;br /&gt;     WHERE segment_name like 'P1%';&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;SEGMENT_NAME  SEGMENT_TYPE  BYTES&lt;br /&gt;------------  ------------  -----&lt;br /&gt;P1            TABLE         65536&lt;br /&gt;P1_PK         INDEX         65536&lt;br /&gt;P1_B          INDEX         65536&lt;br /&gt;P1_C          LOBSEGMENT    65536&lt;br /&gt;&lt;br /&gt;4 rows selected.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Una vez que se inserta un registro en la tabla P1, las mismas sentencias &lt;i&gt;SELECT&lt;/i&gt; que hemos utilizado anteriormente indican que ya se están consumiendo recursos de almacenamiento, y, en concreto, en la tabla &lt;span style="font-weight:bold;"&gt;&lt;i&gt;USER_SEGMENTS&lt;/i&gt;&lt;/span&gt;, aparecen todos los objetos asociados con la tabla P1 que consumen recursos de este tipo.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-1109011665066103671?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=CTEhnHatBE0:CpMKqXlrjZE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=CTEhnHatBE0:CpMKqXlrjZE:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=CTEhnHatBE0:CpMKqXlrjZE:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=CTEhnHatBE0:CpMKqXlrjZE:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=CTEhnHatBE0:CpMKqXlrjZE:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=CTEhnHatBE0:CpMKqXlrjZE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=CTEhnHatBE0:CpMKqXlrjZE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=CTEhnHatBE0:CpMKqXlrjZE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=CTEhnHatBE0:CpMKqXlrjZE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=CTEhnHatBE0:CpMKqXlrjZE:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=CTEhnHatBE0:CpMKqXlrjZE:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/CTEhnHatBE0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/1109011665066103671/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=1109011665066103671&amp;isPopup=true" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/1109011665066103671?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/1109011665066103671?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/CTEhnHatBE0/creacion-diferida-segmentos-deferred.html" title="Creación diferida de segmentos (Deferred Segment Creation), nueva funcionalidad de la release 2 de Oracle 11g" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_ObxKtfPuuSQ/TA1OMdzjCzI/AAAAAAAAFbc/11D3alKg5aE/s72-c/creacion-diferida-segmentos-PLSQL-SQL.JPG" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://www.plsql.biz/2010/06/creacion-diferida-segmentos-deferred.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEEARXk_eip7ImA9WxFRGU0.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-8479201835294712375</id><published>2010-05-03T18:17:00.011+02:00</published><updated>2010-05-03T19:10:44.742+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-03T19:10:44.742+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Uso de ediciones para actualizar la base de datos, nueva funcionalidad de la release 2 de Oracle 11g</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/HRqo3F0MUlkS0wxyFGUVFBw15a8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/HRqo3F0MUlkS0wxyFGUVFBw15a8/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/HRqo3F0MUlkS0wxyFGUVFBw15a8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/HRqo3F0MUlkS0wxyFGUVFBw15a8/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/S97_MdB7c7I/AAAAAAAAFXY/P2Jt4i8XRSA/s1600/uso-ediciones-oracle-11g-rel2-plsql.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 182px;" src="http://1.bp.blogspot.com/_ObxKtfPuuSQ/S97_MdB7c7I/AAAAAAAAFXY/P2Jt4i8XRSA/s200/uso-ediciones-oracle-11g-rel2-plsql.JPG" border="0" alt="Uso de ediciones para actualizar las base de datos Oracle en programación PLSQL" id="BLOGGER_PHOTO_ID_5467087587090592690" /&gt;&lt;/a&gt;Desde mi punto de vista, la &lt;b&gt;nueva funcionalidad &lt;i&gt;Edition-Based Redefinition&lt;/i&gt;&lt;/b&gt;, cuya traducción directa sería "redefinición basada en ediciones", se trata de la característica más interesante de la &lt;b&gt;release 2 de la base de datos Oracle 11g&lt;/b&gt;. En pocas palabras se trata de la posibilidad de actualizar nuestra aplicación PL/SQL sin tener que poner en modo restringido la base de datos, es decir, con esta nueva versión de la base de datos Oracle &lt;b&gt;es posible realizar online la actualización de nuestra aplicación&lt;/b&gt;. Explicado de esta manera tan sencilla la nueva funcionalidad suena como un juego de niños, pero en realidad es algo cuya verdadera dimensión resulta difícil de medir por sus enormes implicaciones.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;Las &lt;b&gt;versiones anteriores de la base de datos Oracle&lt;/b&gt; ya permitían realizar un gran número de operaciones online:&lt;ul&gt;&lt;li&gt;&lt;b&gt;Modificar la mayoría de los parámetros&lt;/b&gt; de la base de datos Oracle (sólo 90 de los 350 parámetros requieren reiniciar la base de datos).&lt;/li&gt;&lt;li&gt;&lt;b&gt;Aplicar patches&lt;/b&gt; a la base de datos utilizando Oracle Real Application Clusters.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Reorganizar objetos&lt;/b&gt; de la base de datos en el espacio de almacenamiento.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Crear índices&lt;/b&gt; (CREATE INDEX).&lt;/li&gt;&lt;li&gt;&lt;b&gt;Realizar actualizaciones&lt;/b&gt; de las base de datos Oracle de una release principal a otra release principal.&lt;/li&gt;&lt;/ul&gt;En conclusión, mientras la base de datos estaba online, podíamos realizar casi cualquier actualización, con excepción de operaciones del tipo recrear un &lt;a href="http://www.plsql.biz/2007/03/procedimientos-y-funciones-en-plsql.html"&gt;procedimiento almacenado PL/SQL&lt;/a&gt;, cambiar un &lt;a href="http://www.plsql.biz/2007/02/triggers-en-plsql.html"&gt;trigger PLSQL&lt;/a&gt;, actualizar permisos, o modificar una vista normal o una &lt;a href="http://www.plsql.biz/2007/06/vistas-materializadas-materialized.html"&gt;vista materializada&lt;/a&gt;. En pocas palabras, mientras los usuarios los estaban utilizando, &lt;b&gt;no nos era posible actualizar los objetos que constituyen la base de nuestra aplicación&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Supongo que muchos DBAs habrán experimentado una enorme frustración cuando han intentado recrear un procedimiento PL/SQL para corregir algún tipo de problema, y no han podido hacerlo porque el procedimiento estaba siendo ejecutado por algún usuario del sistema. Obviamente, cuando se trata de un solo procedimiento PLSQL, el problema se solventa esperando a que el usuario lo deje de utilizar y el objeto se desbloquee, pero en la mayoría de los casos, los DBAs no tienen que reemplazar un solo procedimiento PL/SQL, sino un buen número de ellos, y muchos de estos procedimientos estarán relacionados con otros objetos de la base de datos Oracle. Por lo tanto, si un DBA debe actualizar varios procedimientos, paquetes, vistas y triggers PLSQL simultáneamente, y existen usuarios utilizando la aplicación, por un lado los usuarios bloquearan las transacciones iniciadas por el DBA y, por otro, el DBA estará bloqueando las operaciones de dichos usuarios. Esto supone que, para realizar este tipo de actualizaciones, &lt;b&gt;el DBA necesitará poner en modo restringido la base de datos Oracle&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Todo esto termina con la &lt;b&gt;release 2 de la base de datos Oracle 11g&lt;/b&gt; y la &lt;b&gt;funcionalidad &lt;i&gt;Edition-Based Redefinition&lt;/i&gt;&lt;/b&gt;, que habilita a DBAs y usuarios para que accedan a distintas versiones de procedimientos, triggers, vistas, sinónimos y otros objetos de la base de datos almacenados bajo un mismo esquema, siendo ambas versiones totalmente independientes, de manera que coexisten en el sistema pero no interfieren entre ellas.&lt;br /&gt;&lt;br /&gt;Esto es posible gracias a que la creación de objetos en la release 2 de la base de datos Oracle 11g introduce una &lt;b&gt;tercera dimensión en la funcionalidad que permite resolver el nombre de los objetos&lt;/b&gt;, esta tercera dimensión es &lt;b&gt;la edición de la base de datos que está utilizando nuestra sesión&lt;/b&gt;. Es decir, para referirnos a un objeto, aparte del dueño del mismo y su propio nombre, también se utiliza la edición de la sesión. Cuando un usuario crea una sesión en la release 2 de la base de datos Oracle 11g, dicha sesión tendrá asociado un atributo que identifica la edición de la sesión, edición que si no se cambia con el comando ALTER SESSION tomará el valor por defecto de la edición de la base de datos (&lt;b&gt;normalmente el nombre de la edición por defecto es ora$base&lt;/b&gt;).&lt;br /&gt;&lt;br /&gt;Todo esto posibilita que &lt;b&gt;los DBAs puedan utilizar el comando SQL ALTER SESSION para cambiar la edición de la sesión que estén utilizando&lt;/b&gt; y, por ejemplo, utilizar una edición denominada "ver2", edición sobre la que podrán compilar todos los procedimientos PLSQL que quieran actualizar. Obviamente los cambios realizados sobre la edición "ver2" sólo serán visibles por las sesiones que utilicen dicha edición, y como dicha edición no es la edición por defecto, ningún usuario verá dichos cambios a menos que lo pida a través del comando ALTER SESSION.&lt;br /&gt;&lt;br /&gt;Veamos a continuación como debería proceder un DBA para actualizar la aplicación sin afectar a los usuarios que la están utilizando.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE EDITION ver2 AS CHILD OF ora$base;&lt;br /&gt;&lt;br /&gt;ALTER SESSION SET EDITION = ver2;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;Con el comando SQL CREATE EDITION&lt;/b&gt; hemos creado una nueva edición como hija de la edición de la base de datos por defecto. La edición ver2 es sólo una &lt;b&gt;copia virtual&lt;/b&gt; de la base de datos, es decir, la base de datos no se copia físicamente sino que apunta a los objetos de la edición por defecto ora$base. Sólo se empezará a utilizar espacio de almacenamiento físico cuando se modifique algún objeto de la base de datos en el contexto de la edición ver2.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Con el comando SQL ALTER SESSION&lt;/b&gt; hemos indicado a la base de datos que los cambios que realicemos a continuación deben hacerse sobre la edición ver2. En este momento el DBA puede realizar la actualización de la aplicación modificando procedimientos, triggers, vistas, objetos, etcétera.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;ALTER DATABASE DEFAULT EDITION = ver2;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Una vez completados los cambios, el DBA puede &lt;b&gt;llevarlos a producción utilizando el comando SQL ALTER DATABASE&lt;/b&gt;, mediante el cual indicará que la versión por defecto de la base de datos es la ver2, de manera que el nuevo código se hace accesible a todos los usuarios de forma instantánea.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-8479201835294712375?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=a5ozt8E3sTI:6QmLo90GpJA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=a5ozt8E3sTI:6QmLo90GpJA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=a5ozt8E3sTI:6QmLo90GpJA:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=a5ozt8E3sTI:6QmLo90GpJA:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=a5ozt8E3sTI:6QmLo90GpJA:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=a5ozt8E3sTI:6QmLo90GpJA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=a5ozt8E3sTI:6QmLo90GpJA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=a5ozt8E3sTI:6QmLo90GpJA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=a5ozt8E3sTI:6QmLo90GpJA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=a5ozt8E3sTI:6QmLo90GpJA:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=a5ozt8E3sTI:6QmLo90GpJA:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/a5ozt8E3sTI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/8479201835294712375/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=8479201835294712375&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/8479201835294712375?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/8479201835294712375?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/a5ozt8E3sTI/funcionalidad-edicion-actualizar-oracle.html" title="Uso de ediciones para actualizar la base de datos, nueva funcionalidad de la release 2 de Oracle 11g" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_ObxKtfPuuSQ/S97_MdB7c7I/AAAAAAAAFXY/P2Jt4i8XRSA/s72-c/uso-ediciones-oracle-11g-rel2-plsql.JPG" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.plsql.biz/2010/05/funcionalidad-edicion-actualizar-oracle.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk4GSXwyeSp7ImA9WxBaGEQ.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-4965081005724815426</id><published>2010-03-29T06:25:00.009+02:00</published><updated>2010-03-29T22:35:28.291+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-29T22:35:28.291+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>Memoria PGA frente a espacio de almacenamiento temporal en las bases de datos Oracle</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/olm_cp-UVutrgBE30-Ykn6cgCV4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/olm_cp-UVutrgBE30-Ykn6cgCV4/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/olm_cp-UVutrgBE30-Ykn6cgCV4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/olm_cp-UVutrgBE30-Ykn6cgCV4/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/S7EKXgWBG2I/AAAAAAAAFSs/6IgyJ8bGbUw/s1600/memoria-PGA-plsql-pinocho-chateando.JPG"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 139px;" src="http://3.bp.blogspot.com/_ObxKtfPuuSQ/S7EKXgWBG2I/AAAAAAAAFSs/6IgyJ8bGbUw/s200/memoria-PGA-plsql-pinocho-chateando.JPG" border="0" alt="Utilización de la memoria PGA en PLSQL de Oracle" id="BLOGGER_PHOTO_ID_5454152022657932130" /&gt;&lt;/a&gt;Recientemente he recibido en este blogs sobre programación PL/SQL algunas preguntas referentes a las &lt;b&gt;diferencias&lt;/b&gt; entre el tipo de operaciones que la base de datos Oracle realiza en la &lt;b&gt;memoria del área global de programa&lt;/b&gt; (PGA - &lt;i&gt;Program Global Area&lt;/i&gt;) frente a las que realiza en el &lt;b&gt;espacio del almacenamiento temporal&lt;/b&gt; (&lt;i&gt;TEMP space&lt;/i&gt;). Primero os comentaré que las &lt;b&gt;versiones antiguas de las bases de datos Oracle&lt;/b&gt; solían utilizar los parámetros &lt;i&gt;SORT_AREA_SIZE&lt;/i&gt; y &lt;i&gt;HASH_AREA_SIZE&lt;/i&gt; para controlar cuanta memoria PGA era posible utilizar antes de empezar a tener que usar el espacio de disco temporal (TEMP).&lt;br /&gt;&lt;br /&gt;No obstante en las &lt;b&gt;nuevas versiones&lt;/b&gt; de las bases de datos Oracle, &lt;b&gt;la gestión de la memoria PGA se realiza de forma automática&lt;/b&gt; y mucho más dinámica.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;Supongamos que ejecutamos algún tipo de sentencia SQL y que esta sentencia SQL tiene que realizar algún tipo de operación del tipo:&lt;ul&gt;&lt;li&gt;Enlazar tablas usando un algoritmo de tipo hash (&lt;b&gt;&lt;i&gt;Hash join&lt;/i&gt;&lt;/b&gt;).&lt;/li&gt;&lt;li&gt;Ordenar datos (&lt;b&gt;operación de tipo &lt;i&gt;SORT&lt;/i&gt;&lt;/b&gt;), por ejemplo cuando se aplican cláusulas como ORDER BY, DISTINCT o GROUP BY.&lt;/li&gt;&lt;li&gt;Realizar cualquier operación que necesite la utilización de memoria.&lt;/li&gt;&lt;/ul&gt;En estas situaciones &lt;b&gt;la base de datos Oracle asigna un área de trabajo&lt;/b&gt;, ya sea un área HASH, un área SORT o lo que sea, cuyo tamaño será determinado dependiendo de la carga de trabajo del sistema en ese momento concreto. Cuando este área de trabajo se llena y la operación SQL que está teniendo lugar no se ha completado, entonces la base de datos Oracle procede a llevar los datos de la operación en proceso al disco, es decir realiza una operación de escritura al espacio de almacenamiento temporal (TEMP). De tal manera que el espacio TEMP, aún no siendo tal cosa, funcionará como una memoria virtual, como si se tratase de una auténtica memoria PGA. Obviamente, para optimizar el rendimiento de la base de datos Oracle lo mejor es evitar la escrituras en el espacio de disco TEMP.&lt;br /&gt;&lt;br /&gt;El objetivo de la gestión automática de la memoria PGA es no utilizar toda la memoria a la vez. La idea es &lt;b&gt;reservar siempre algo de memoria&lt;/b&gt; previniendo la incorporación momentánea de usuarios adicionales y evitando situaciones en la que un determinado usuario acapare toda la memoria disponible.&lt;br /&gt;&lt;br /&gt;De todas formas los DBA (analistas de bases de datos) pueden elegir entre una gestión manual de la memoria o una gestión automática. Yo personalmente me inclino por utilizar por defecto la gestión automática de la memoria PGA.&lt;br /&gt;&lt;br /&gt;Por lo tanto, es posible asignar valores fijos a los parámetros &lt;i&gt;SORT_AREA_SIZE&lt;/i&gt; y &lt;i&gt;HASH_AREA_SIZE&lt;/i&gt;, el problema es que &lt;b&gt;los valores recomendables para estos parámetros van a variar a lo largo del tiempo&lt;/b&gt;, ya que dichos valores no pueden ser idénticos cuando la base de datos Oracle cuenta con, por ejemplo, cinco usuarios activos, frente a unas horas más tarde cuando el número de usuarios activos va a ser de cuatrocientos. &lt;br /&gt;&lt;br /&gt;Por eso resulta muy recomendable asignar al parámetro &lt;i&gt;WORKAREA_SIZE_POLICY&lt;/i&gt; el valor de &lt;i&gt;AUTO&lt;/i&gt;, momento en el cual entra en juego el parámetro &lt;i&gt;PGA_AGGREGATE_TARGET&lt;/i&gt;, parámetro que sirve para asignar la &lt;b&gt;máxima cantidad de memoria PGA&lt;/b&gt; que la base de datos Oracle debe procurar utilizar. De esta manera, &lt;b&gt;la base de datos Oracle distribuirá dicha memoria entre todas las sesiones activas&lt;/b&gt;. Además, desde la versión Oracle 9i Release 2, el paquete de estadísticas disponible a través de la vista de rendimiento dinámico &lt;i&gt;V$&lt;/i&gt;, que es accesible desde &lt;i&gt;Oracle Enterprise Manager&lt;/i&gt;, nos indica cual debe ser, en nuestro sistema, el valor óptimo del parámetro &lt;i&gt;PGA_AGGREGATE_TARGET&lt;/i&gt; para minimizar las operaciones de entrada/salida en el espacio de alojamiento temporal. Esta información nos permitirá cambiar dinámicamente el tamaño de nuestra memoria PGA o tomar la decisión de aumentar el tamaño de la memoria RAM en nuestro servidor para conseguir un rendimiento óptimo de nuestra base de datos Oracle.&lt;br /&gt;&lt;br /&gt;Situaciones en las puede ser necesario un &lt;b&gt;uso extensivo de la memoria&lt;/b&gt;, por ejemplo, si necesitamos ejecutar diariamente un procedimiento PLSQL que realiza &lt;i&gt;hash joins&lt;/i&gt; tremendamente grandes u otro tipo de acciones que requieren utilizar mucha memoria PGA, entonces puede ser recomendable &lt;b&gt;crear un &lt;i&gt;script&lt;/i&gt; SQL&lt;/b&gt; para ejecutar dicho procedimiento, script que utilizará el comando &lt;i&gt;ALTER SESSION&lt;/i&gt; para que dicha sesión pueda hacer uso de todos los recursos de memoria PGA disponibles mediante la inicialización adecuada de los parámetros &lt;i&gt;SORT_AREA_SIZE&lt;/i&gt; y &lt;i&gt;HASH_AREA_SIZE&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;En conclusión, mi recomendación es hacer uso de la &lt;b&gt;gestión automática de la memoria PGA&lt;/b&gt; para las sesiones de usuario y aplicaciones que se ejecutan en el día a día. &lt;b&gt;La gestión manual de la memoria PGA sólo tiene sentido para aquellos procesos con mucha carga de trabajo&lt;/b&gt;, que requieren un uso extensivo de la memoria PGA y que se ejecutarán en periodos de tiempo durante los cuales sólo estarán en ejecución este tipo de procesos (generalmente serán trabajos programados que se lanzan en horas con poca o ninguna actividad de usuarios).&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-4965081005724815426?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=syYKsO6dlG8:g08FwKTqTj8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=syYKsO6dlG8:g08FwKTqTj8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=syYKsO6dlG8:g08FwKTqTj8:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=syYKsO6dlG8:g08FwKTqTj8:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=syYKsO6dlG8:g08FwKTqTj8:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=syYKsO6dlG8:g08FwKTqTj8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=syYKsO6dlG8:g08FwKTqTj8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=syYKsO6dlG8:g08FwKTqTj8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=syYKsO6dlG8:g08FwKTqTj8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=syYKsO6dlG8:g08FwKTqTj8:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=syYKsO6dlG8:g08FwKTqTj8:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/syYKsO6dlG8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/4965081005724815426/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=4965081005724815426&amp;isPopup=true" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/4965081005724815426?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/4965081005724815426?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/syYKsO6dlG8/memoria-pga-espacio-temp-oracle-bd.html" title="Memoria PGA frente a espacio de almacenamiento temporal en las bases de datos Oracle" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_ObxKtfPuuSQ/S7EKXgWBG2I/AAAAAAAAFSs/6IgyJ8bGbUw/s72-c/memoria-PGA-plsql-pinocho-chateando.JPG" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://www.plsql.biz/2010/03/memoria-pga-espacio-temp-oracle-bd.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMNQXY_fyp7ImA9WxFUE04.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-5121124915603732908</id><published>2010-02-25T06:45:00.007+01:00</published><updated>2010-06-24T01:08:10.847+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-24T01:08:10.847+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="Tutorial PL/SQL" /><title>Utilidad del paquete estándar PL/SQL DBMS_ROWID</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/cB0DUVuP1wUpmUTbO-Dwh5NQdgM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/cB0DUVuP1wUpmUTbO-Dwh5NQdgM/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/cB0DUVuP1wUpmUTbO-Dwh5NQdgM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/cB0DUVuP1wUpmUTbO-Dwh5NQdgM/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/S4aOmDmY2cI/AAAAAAAAFL8/mTqhiakxwJQ/s1600-h/DBMS_ROWID-utilidad-PLSQL-estandar.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 180px;" src="http://1.bp.blogspot.com/_ObxKtfPuuSQ/S4aOmDmY2cI/AAAAAAAAFL8/mTqhiakxwJQ/s200/DBMS_ROWID-utilidad-PLSQL-estandar.JPG" border="0" alt="Un chiste y la utilidad del paquete estándar PL/SQL DBMS_ROWID" id="BLOGGER_PHOTO_ID_5442193984176314818" /&gt;&lt;/a&gt;Hace unas semanas me llegó una consulta sobre PL/SQL en la que se me preguntaba si era posible conocer, utilizando una consulta SQL, el &lt;b&gt;nombre de la partición&lt;/b&gt; en la que se encontraba almacenado un determinado registro de una tabla. El &lt;b&gt;paquete estándar PLSQL DBMS_ROWID&lt;/b&gt; nos puede ayudar a obtener esta información mediante la extracción del &lt;b&gt;ROWID_OBJECT&lt;/b&gt; que identifica de manera única el segmento donde se encuentran los datos. Después bastará que asociemos este identificador con una de las vistas &lt;b&gt;XXX_OBJECTS&lt;/b&gt; (donde XXX puede ser &lt;b&gt;DBA&lt;/b&gt;, &lt;b&gt;ALL&lt;/b&gt; o &lt;b&gt;USER&lt;/b&gt;) para obtener el nombre de la partición (ver ejemplo a continuación).&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT sh.order_number&lt;br /&gt;     , do.subobject_name&lt;br /&gt;     , do.data_object_id&lt;br /&gt;FROM   oe.so_headers_all sh&lt;br /&gt;     , dba_objects do&lt;br /&gt;WHERE  do.data_object_id = &lt;br /&gt;         DBMS_ROWID.ROWID_OBJECT(sh.rowid)&lt;br /&gt;  AND  sh.order_number = '123456'&lt;br /&gt;&lt;br /&gt;ORDER_NUMBER SUBOBJECT_NAME DATA_OBJECT_ID&lt;br /&gt;------------ -------------- --------------&lt;br /&gt;      123456 PART1          15107         &lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;El &lt;b&gt;paquete estándar PLSQL DBMS_ROWID&lt;/b&gt; resulta de mucha utilidad &lt;b&gt;para ver como los datos están organizados dentro de una tabla&lt;/b&gt; e identificar donde están almacenados los distintos registros de la misma. Veamos a continuación &lt;b&gt;otro ejemplo de uso del paquete DBMS_ROWID&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT sh.order_number&lt;br /&gt;     , dt.tablespace_name&lt;br /&gt;     , dt.status&lt;br /&gt;FROM   oe.so_headers_all sh&lt;br /&gt;     , dba_data_files  ddf&lt;br /&gt;     , dba_tablespaces dt&lt;br /&gt;WHERE ddf.tablespace_name = dt.tablespace_name&lt;br /&gt;  AND ddf.relative_fno = &lt;br /&gt;        DBMS_ROWID.ROWID_RELATIVE_FNO(sh.rowid)&lt;br /&gt;  AND ddf.file_id = &lt;br /&gt;        DBMS_ROWID.ROWID_TO_ABSOLUTE_FNO&lt;br /&gt;          (sh.rowid, 'OE', 'SO_HEADERS_ALL')&lt;br /&gt;  AND sh.order_number = '123456'&lt;br /&gt;&lt;br /&gt;ORDER_NUMBER TABLESPACE_NAME STATUS   &lt;br /&gt;------------ --------------- ---------&lt;br /&gt;      123456 RESDATOS        ONLINE   &lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En este caso hemos utilizado el &lt;b&gt;paquete estándar PL/SQL DBMS_ROWID&lt;/b&gt; para obtener, para un determinado &lt;b&gt;ROWID&lt;/b&gt;, el número de bloque y el número de fichero en la base de datos Oracle, con estos valores podremos consultar en la vista &lt;b&gt;DBA_DATA_FILES&lt;/b&gt; el &lt;b&gt;nombre del TABLESPACE&lt;/b&gt; en el que está almacenado el registro en cuestión y después, utilizando la vista &lt;b&gt;DBA_TABLESPACES&lt;/b&gt;, podremos determinar el &lt;b&gt;estado de dicho TABLESPACE&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-5121124915603732908?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NGxRuKBqvlE:Gx_3mJk9DF8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NGxRuKBqvlE:Gx_3mJk9DF8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NGxRuKBqvlE:Gx_3mJk9DF8:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NGxRuKBqvlE:Gx_3mJk9DF8:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=NGxRuKBqvlE:Gx_3mJk9DF8:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NGxRuKBqvlE:Gx_3mJk9DF8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=NGxRuKBqvlE:Gx_3mJk9DF8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NGxRuKBqvlE:Gx_3mJk9DF8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=NGxRuKBqvlE:Gx_3mJk9DF8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NGxRuKBqvlE:Gx_3mJk9DF8:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=NGxRuKBqvlE:Gx_3mJk9DF8:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/NGxRuKBqvlE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/5121124915603732908/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=5121124915603732908&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5121124915603732908?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/5121124915603732908?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/NGxRuKBqvlE/dbmsrowid-utilidad-plsql-estandar.html" title="Utilidad del paquete estándar PL/SQL DBMS_ROWID" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_ObxKtfPuuSQ/S4aOmDmY2cI/AAAAAAAAFL8/mTqhiakxwJQ/s72-c/DBMS_ROWID-utilidad-PLSQL-estandar.JPG" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.plsql.biz/2010/02/dbmsrowid-utilidad-plsql-estandar.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkIEQHg_eip7ImA9WxFUE04.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-7261706108476842025</id><published>2010-01-19T19:29:00.004+01:00</published><updated>2010-06-24T01:08:21.642+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-24T01:08:21.642+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Bases de datos Oracle" /><title>Cambios en los parámetros de la base de datos Oracle, cuándo tienen lugar</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/gPFlfhQ6vVm9a3DnWNfuNho4UKo/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/gPFlfhQ6vVm9a3DnWNfuNho4UKo/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/gPFlfhQ6vVm9a3DnWNfuNho4UKo/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/gPFlfhQ6vVm9a3DnWNfuNho4UKo/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a href="http://1.bp.blogspot.com/_ObxKtfPuuSQ/S1X-rn6zypI/AAAAAAAAFGo/P6yIn6cBWLs/s1600-h/windows-nueva-opcion-PLSQL.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 175px;" src="http://1.bp.blogspot.com/_ObxKtfPuuSQ/S1X-rn6zypI/AAAAAAAAFGo/P6yIn6cBWLs/s200/windows-nueva-opcion-PLSQL.jpg" border="0" alt="Cambios en los parámetros de la base de datos Oracle, nueva opción windows XP en PL/SQL" id="BLOGGER_PHOTO_ID_5428524951268477586" /&gt;&lt;/a&gt;Existe la creencia un tanto generalizada de que cuando realizamos un &lt;b&gt;cambio en los parámetros de la base de datos Oracle&lt;/b&gt; (utilizando el comando SQL &lt;i&gt;ALTER SYSTEM SET &lt;parámetro&gt; ...&lt;/i&gt;) mientras la instancia de la base de datos está operativa y si los cambios los realizamos utilizando el fichero de parámetros almacenado (&lt;i&gt;SPFILE&lt;/i&gt;), estos cambios no tienen lugar hasta que la base de datos se reinicia. Esta idea es falsa y equivocada, ya que el &lt;b&gt;comando SQL &lt;i&gt;ALTER SYSTEM&lt;/i&gt;&lt;/b&gt; dispone del &lt;b&gt;parámetro &lt;i&gt;SCOPE&lt;/i&gt;&lt;/b&gt; para especificar en que momento deben tener efecto los cambios.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;El &lt;b&gt;parámetro &lt;i&gt;SCOPE&lt;/i&gt;&lt;/b&gt; puede tomar los valores &lt;i&gt;MEMORY&lt;/i&gt;, &lt;i&gt;SPFILE&lt;/i&gt;, or &lt;i&gt;BOTH&lt;/i&gt;. Si le asignamos el &lt;b&gt;valor &lt;i&gt;MEMORY&lt;/i&gt;&lt;/b&gt; el cambio aplicará de forma inmediata siempre que estemos cambiando un parámetro que no requiera el reinicio de la base de datos Oracle para que el cambio sea efectivo. Si le asignamos el &lt;b&gt;valor &lt;i&gt;SPFILE&lt;/i&gt;&lt;/b&gt; entonces únicamente estaremos modificando el fichero de parámetros almacenado y el cambio tendrá lugar en el siguiente reinicio de la base de datos Oracle. Pero si utilizamos el &lt;b&gt;valor &lt;i&gt;BOTH&lt;/i&gt;&lt;/b&gt;, entonces el cambio será inmediato y, además, cambiaremos el valor en el fichero de parámetros almacenado (&lt;i&gt;SPFILE&lt;/i&gt;).&lt;br /&gt;&lt;br /&gt;En este punto concreto, desde programación PLSQL queremos indicar que, dependiendo de si la base de datos Oracle está o no &lt;i&gt;online&lt;/i&gt;, podemos encontrarnos con tres tipos diferentes de parámetros:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Parámetros que no pueden cambiarse online&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;La guía de referencia de bases de datos Oracle (&lt;i&gt;Oracle Database Reference&lt;/i&gt;) describe claramente los parámetros de inicialización y sus propiedades, señalando si un parámetro es modificable o no, si un parámetro es no modificable, entonces no podremos cambiarlo mientras la base de datos está online. Por ejemplo es el caso del parámetro &lt;i&gt;AUDIT_TRAIL&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Parámetros que pueden cambiarse online pero sólo para sesiones futuras&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;En este caso el cambio realizado no afectará a ninguna de las sesiones que estén conectadas a la base de datos en el momento que realicemos en cambio, sino que afectará sólo a las sesiones que se conecten posteriormente a la ejecución del comando &lt;i&gt;ALTER SYSTEM&lt;/i&gt;. Es el caso de, por ejemplo, el parámetro &lt;i&gt;SORT_AREA_SIZE&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; ALTER SYSTEM&lt;br /&gt;2    SET sort_area_size = 12345&lt;br /&gt;3    DEFERRED SCOPE=memory;&lt;br /&gt;System altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; SHOW PARAMETER sort_area_size&lt;br /&gt;&lt;br /&gt;NAME           TYPE    VALUE&lt;br /&gt;-------------- ------- -----&lt;br /&gt;sort_area_size integer 65536&lt;br /&gt;&lt;br /&gt;SQL&gt; connect /&lt;br /&gt;Connected.&lt;br /&gt;&lt;br /&gt;SQL&gt; SHOW PARAMETER sort_area_size&lt;br /&gt;&lt;br /&gt;NAME           TYPE    VALUE&lt;br /&gt;-------------- ------- -----&lt;br /&gt;sort_area_size integer 12345&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;Parámetros que pueden cambiarse online de forma inmediata&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;En este caso los cambios se reflejan inmediatamente, propagándose a todas las sesiones que, en ese momento, estén conectadas a la base de datos Oracle. Así ocurre, por ejemplo, con el parámetro &lt;i&gt;USER_DUMP_DEST&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; SHOW PARAMETER user_dump_dest&lt;br /&gt;&lt;br /&gt;NAME           TYPE    VALUE&lt;br /&gt;-------------- ------- -----&lt;br /&gt;user_dump_dest string  /tmp&lt;br /&gt;&lt;br /&gt;SQL&gt; ALTER SYSTEM&lt;br /&gt;2    SET user_dump_dest = '/u02/tmp';&lt;br /&gt;System altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; SHOW PARAMETER user_dump_dest&lt;br /&gt;&lt;br /&gt;NAME           TYPE    VALUE&lt;br /&gt;-------------- ------- --------&lt;br /&gt;user_dump_dest string  /u02/tmp&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Por otro lado, si lo que queremos es &lt;b&gt;eliminar un parámetro de configuración de la &lt;i&gt;SPFILE&lt;/i&gt;&lt;/b&gt;, puesto que se trata de un fichero que no se puede editar utilizando un editor de textos, también deberemos utilizar el &lt;b&gt;comando SQL &lt;i&gt;ALTER SYSTEM&lt;/i&gt;&lt;/b&gt;, pero en este caso utilizando la &lt;b&gt;cláusula &lt;i&gt;RESET&lt;/i&gt;&lt;/b&gt;. Así, por ejemplo, si lo que queremos es eliminar el parámetro &lt;i&gt;USER_DUMP_DEST&lt;/i&gt; y que tome el valor por defecto que hemos sobre escrito anteriormente, deberemos proceder como sigue:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; ALTER SYSTEM&lt;br /&gt;2    RESET user_dump_dest&lt;br /&gt;3    SCOPE=spfile SID='*';&lt;br /&gt;System altered.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En este momento habremos eliminado el parámetro &lt;i&gt;USER_DUMP_DEST&lt;/i&gt; del fichero &lt;i&gt;SPFILE&lt;/i&gt; cosa que podremos verificar ejecutando el comando SQL:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SQL&gt; CREATE pfile='/tmp/pfile.txt'&lt;br /&gt;2    FROM spfile;&lt;br /&gt;File created.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Ahora, si revisamos el fichero &lt;i&gt;/tmp/pfile.txt&lt;/i&gt; veremos que el parámetro &lt;i&gt;USER_DUMP_DEST&lt;/i&gt;  no aparece en el mismo.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-7261706108476842025?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=rM_sAlHCOgU:nHCFfVRwFpM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=rM_sAlHCOgU:nHCFfVRwFpM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=rM_sAlHCOgU:nHCFfVRwFpM:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=rM_sAlHCOgU:nHCFfVRwFpM:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=rM_sAlHCOgU:nHCFfVRwFpM:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=rM_sAlHCOgU:nHCFfVRwFpM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=rM_sAlHCOgU:nHCFfVRwFpM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=rM_sAlHCOgU:nHCFfVRwFpM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=rM_sAlHCOgU:nHCFfVRwFpM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=rM_sAlHCOgU:nHCFfVRwFpM:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=rM_sAlHCOgU:nHCFfVRwFpM:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/rM_sAlHCOgU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/7261706108476842025/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=7261706108476842025&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7261706108476842025?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/7261706108476842025?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/rM_sAlHCOgU/cambios-en-los-parametros-de-la-base-de.html" title="Cambios en los parámetros de la base de datos Oracle, cuándo tienen lugar" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_ObxKtfPuuSQ/S1X-rn6zypI/AAAAAAAAFGo/P6yIn6cBWLs/s72-c/windows-nueva-opcion-PLSQL.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.plsql.biz/2010/01/cambios-en-los-parametros-de-la-base-de.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8CQH09eCp7ImA9WxBTGEw.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-9067737878145583246</id><published>2009-12-14T18:26:00.004+01:00</published><updated>2009-12-14T18:47:41.360+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-14T18:47:41.360+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Optimización y tuning de bases de datos" /><title>Cláusula CONSTRAINT para mejorar el rendimiento de las consultas PL/SQL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/_s8pGDlGdKVqhB5_DqEuePvnYjU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_s8pGDlGdKVqhB5_DqEuePvnYjU/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/_s8pGDlGdKVqhB5_DqEuePvnYjU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_s8pGDlGdKVqhB5_DqEuePvnYjU/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/SyZ4yuaUHvI/AAAAAAAAFCY/NzLBWVC_gnk/s1600-h/rendimiento-PLSQL-y-clausula-constraint.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 200px;" src="http://3.bp.blogspot.com/_ObxKtfPuuSQ/SyZ4yuaUHvI/AAAAAAAAFCY/NzLBWVC_gnk/s200/rendimiento-PLSQL-y-clausula-constraint.jpg" border="0" alt="Rendimiento bases de datos Oracle y la clausula CONSTRAINT en programación PL/SQL" id="BLOGGER_PHOTO_ID_5415148414806793970" /&gt;&lt;/a&gt;Resulta bastante interesante mencionar la importancia de que, al crear las tablas de nuestra base de datos Oracle, utilicemos, cuando sea posible, la &lt;b&gt;cláusula &lt;i&gt;CONSTRAINT&lt;/i&gt;&lt;/b&gt; para mejorar el rendimiento de las consultas PLSQL que utilicen dicha tabla. Muchos desarrolladores de PL/SQL piensan que la utilización de la cláusula CONSTRAINT sólo sirve para garantizar la integridad de los datos, lo cual es cierto, pero esta cláusula también &lt;b&gt;la utiliza el optimizador Oracle&lt;/b&gt; para determinar 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; óptimo.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;De hecho, el &lt;a href="http://www.plsql.biz/2007/07/el-optimizador-plsql-basado-en-normas.html"&gt;optimizador PLSQL&lt;/a&gt; utiliza como entradas:&lt;ul&gt;&lt;li&gt;La consulta SQL que se va a optimizar.&lt;/li&gt;&lt;li&gt;Todas las estadísticas disponibles de los objetos de la base de datos.&lt;/li&gt;&lt;li&gt;De estar disponibles, las estadísticas del sistema (velocidad de CPU, velocidad de los dispositivos de entrada/salida, etcétera).&lt;/li&gt;&lt;li&gt;Los parámetros de inicialización.&lt;/li&gt;&lt;li&gt;Las &lt;i&gt;constraints&lt;/i&gt; (restricciones).&lt;/li&gt;&lt;/ul&gt;El optimizador utiliza todos estos datos para determinar el mejor plan de ejecución posible. Personalmente he detectado que mucha gente tiende a no emplear la funcionalidad &lt;i&gt;CONSTRAINT&lt;/i&gt; en los &lt;i&gt;data warehouse&lt;/i&gt; y sistemas de &lt;i&gt;reporting&lt;/i&gt;. Su argumento es decir: "Ya hicimos una limpieza de datos y no necesitamos aplicar restricciones a los datos". Ciertamente es posible que no necesiten utilizar &lt;i&gt;constraints&lt;/i&gt; para garantizar la integridad de los datos, pero lo que es seguro es que &lt;b&gt;necesitarán dichas &lt;i&gt;constraints&lt;/i&gt; para conseguir que el optimizador genere los planes de ejecución más adecuados&lt;/b&gt;. En un &lt;i&gt;data warehouse&lt;/i&gt; un plan de ejecución incorrecto puede causar que la ejecución de una consulta lleve horas e incluso días. Por lo tanto, en un &lt;i&gt;data warehouse&lt;/i&gt; la utilización de las cláusulas &lt;i&gt;CONSTRAINT&lt;/i&gt; es necesaria por razones de rendimiento.&lt;br /&gt;&lt;br /&gt;En el siguiente &lt;b&gt;ejemplo&lt;/b&gt; analizaremos como conseguir eliminar el acceso a una tabla de una vista (&lt;i&gt;view&lt;/i&gt;) gracias a la utilización de la &lt;b&gt;cláusula &lt;i&gt;CONSTRAINT&lt;/i&gt;&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE TABLE e1 AS&lt;br /&gt;  SELECT * FROM empleados&lt;br /&gt;  WHERE nombre IN ('Pepe', 'Juan');&lt;br /&gt;&lt;br /&gt;ALTER TABLE e1 MODIFY nombre NOT NULL;&lt;br /&gt;&lt;br /&gt;ALTER TABLE e1 ADD CONSTRAINT e1_check&lt;br /&gt;  CHECK (nombre IN ('Pepe', 'Juan'));&lt;br /&gt;&lt;br /&gt;CREATE TABLE e2 AS&lt;br /&gt;  SELECT * FROM empleados&lt;br /&gt;  WHERE nombre IN ('Paco', 'Luis');&lt;br /&gt;&lt;br /&gt;ALTER TABLE e2 MODIFY nombre NOT NULL;&lt;br /&gt;&lt;br /&gt;ALTER TABLE e2 ADD CONSTRAINT e1_check&lt;br /&gt;  CHECK (nombre IN ('Paco', 'Luis'));&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE VIEW vemp AS&lt;br /&gt;  SELECT * FROM e1&lt;br /&gt;  UNION ALL&lt;br /&gt;  SELECT * FROM e2;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Según podéis ver, hemos creado dos tablas que contienen datos mutuamente excluyentes y una vista que utiliza la cláusula &lt;i&gt;UNION ALL&lt;/i&gt; para mostrar todos los datos juntos. A continuación lo que haremos es realizar una consulta sobre dicha vista utilizando el campo &lt;i&gt;nombre&lt;/i&gt; en la cláusula &lt;i&gt;WHERE&lt;/i&gt; para ver como el optimizador decide no utilizar una de las tablas.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT * FROM vemp WHERE name = 'Juan';&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;————————————————————————————————&lt;br /&gt;Plan hash value: 3412345678&lt;br /&gt;&lt;br /&gt;————————————————————————————————&lt;br /&gt;|Id | Operation         | Name | &lt;br /&gt;————————————————————————————————&lt;br /&gt;| 0 | SELECT STATEMENT  |      |&lt;br /&gt;| 1 | VIEW              | VEMP | &lt;br /&gt;| 2 | UNION-ALL         |      | &lt;br /&gt;| 3 | TABLE ACCESS FULL | E1   |&lt;br /&gt;| 4 | FILTER            |      |&lt;br /&gt;| 5 | TABLE ACCESS FULL | E2   |&lt;br /&gt;————————————————————————————————&lt;br /&gt;&lt;br /&gt;Predicate Information &lt;br /&gt;(identified by operation id):&lt;br /&gt;————————————————————————————————&lt;br /&gt;3 - filter(“NOMBRE”='Juan')&lt;br /&gt;4 - filter(NULL IS NOT NULL)&lt;br /&gt;5 - filter(“NOMBRE”='Juan')&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Arriba podemos ver el &lt;b&gt;plan de ejecución&lt;/b&gt; generado desde una base de datos Oracle versión 11g y en el que hemos quitado algunas columnas que no son de utilidad en este ejemplo. En él podemos observar que el optimizador realmente no ha prescindido de realizar un &lt;i&gt;FULL SCAN&lt;/i&gt; de la tabla &lt;i&gt;e2&lt;/i&gt;, lo cual, evidentemente, podría haber hecho ya que dicha tabla sólo contiene a los empleados cuyos nombres son Paco o Luis. Sin embargo, si realizamos una análisis más concienzudo, observaremos que, en el paso 4, &lt;b&gt;el optimizador ha aplicado un filtro&lt;/b&gt;, y que dicho filtro reza &lt;i&gt;NULL IS NOT NULL&lt;/i&gt;. Está claro que nuestra consulta &lt;i&gt;SELECT&lt;/i&gt; no incluye esta condición, sino que el optimizador la ha añadido automáticamente. Puesto que &lt;i&gt;NULL IS NOT NULL&lt;/i&gt; es una condición falsa, esa parte del árbol del plan de ejecución nunca se ejecutará, por lo que &lt;b&gt;en ningún momento se accederá a la tabla &lt;i&gt;e2&lt;/i&gt;&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Este ejemplo es muy simple, pero creo que bastante explicativo de como las cláusulas CONSTRAINT pueden ayudar a mejorar el rendimiento de nuestra base de datos Oracle.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-9067737878145583246?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=zNq8W44zysY:WTZ9sXKuSLk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=zNq8W44zysY:WTZ9sXKuSLk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=zNq8W44zysY:WTZ9sXKuSLk:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=zNq8W44zysY:WTZ9sXKuSLk:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=zNq8W44zysY:WTZ9sXKuSLk:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=zNq8W44zysY:WTZ9sXKuSLk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=zNq8W44zysY:WTZ9sXKuSLk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=zNq8W44zysY:WTZ9sXKuSLk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=zNq8W44zysY:WTZ9sXKuSLk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=zNq8W44zysY:WTZ9sXKuSLk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=zNq8W44zysY:WTZ9sXKuSLk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/zNq8W44zysY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/9067737878145583246/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=9067737878145583246&amp;isPopup=true" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/9067737878145583246?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/9067737878145583246?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/zNq8W44zysY/clausula-constraint-para-mejorar-el.html" title="Cláusula CONSTRAINT para mejorar el rendimiento de las consultas PL/SQL" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_ObxKtfPuuSQ/SyZ4yuaUHvI/AAAAAAAAFCY/NzLBWVC_gnk/s72-c/rendimiento-PLSQL-y-clausula-constraint.jpg" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://www.plsql.biz/2009/12/clausula-constraint-para-mejorar-el.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0IERng6eSp7ImA9Wx5TFE8.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-8090929788017307553</id><published>2009-11-10T18:48:00.015+01:00</published><updated>2010-07-29T19:45:07.611+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-29T19:45:07.611+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><title>Sentencia CASE en PL/SQL de Oracle</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/IxTRxUmzGuxEOHzFQDDTKxhI6zc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/IxTRxUmzGuxEOHzFQDDTKxhI6zc/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/IxTRxUmzGuxEOHzFQDDTKxhI6zc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/IxTRxUmzGuxEOHzFQDDTKxhI6zc/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/Svm4QhlFYCI/AAAAAAAAE9s/0wivuuR20WI/s1600-h/plsql-sentencia-case-chiste-ordenador-raton.JPG"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 200px;" src="http://3.bp.blogspot.com/_ObxKtfPuuSQ/Svm4QhlFYCI/AAAAAAAAE9s/0wivuuR20WI/s200/plsql-sentencia-case-chiste-ordenador-raton.JPG" border="0" alt="La sentencia CASE en PLSQL y un chiste de ordenadores y ratones" id="BLOGGER_PHOTO_ID_5402551822038949922" /&gt;&lt;/a&gt;Las versiones de base de datos Oracle 9i y posteriores incluyen la posibilidad de utilizar la &lt;span style="font-weight:bold;"&gt;sentencia CASE&lt;/span&gt; dentro de una sentencia SQL (SELECT, UPDATE, etcétera). La sentencia CASE permite realizar las mismas operaciones que las &lt;a href="http://www.plsql.biz/2006/11/sentencias-de-control-en-plsql.html"&gt;sentencias de control PL/SQL&lt;/a&gt; IF-THEN-ELSIF-ELSE pero con la particularidad de que pueden utilizarse dentro de una sentencia SQL. La &lt;span style="font-weight:bold;"&gt;sintaxis de la sentencia CASE&lt;/span&gt; es como sigue:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CASE  [ expresion ]&lt;br /&gt;  WHEN condicion_1 THEN resultado_1&lt;br /&gt;  WHEN condicion_2 THEN resultado_2&lt;br /&gt;  ...&lt;br /&gt;  WHEN condicion_n THEN resultado_n&lt;br /&gt;  ELSE resultado&lt;br /&gt;END&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;La &lt;span style="font-weight:bold;"&gt;opción&lt;/span&gt; &lt;i&gt;expresion&lt;/i&gt; es opcional. Se trata del valor que estaríamos comparando con la lista de condiciones (&lt;span style="font-style:italic;"&gt;condicion_1&lt;/span&gt;, &lt;span style="font-style:italic;"&gt;condicion_2&lt;/span&gt;, ..., &lt;span style="font-style:italic;"&gt;condicion_n&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;Las &lt;span style="font-weight:bold;"&gt;condiciones&lt;/span&gt;, &lt;span style="font-style:italic;"&gt;condicion_1&lt;/span&gt; a &lt;span style="font-style:italic;"&gt;condicion_n&lt;/span&gt;, son analizadas en el mismo orden en que aparecen listadas y, en el momento en que una de estas condiciones se cumple como verdadera, la sentencia CASE devuelve el resultado correspondiente y deja de analizar el resto de condiciones. &lt;br /&gt;&lt;br /&gt;Los &lt;span style="font-weight:bold;"&gt;resultados&lt;/span&gt;, &lt;span style="font-style:italic;"&gt;resultado_1&lt;/span&gt; a &lt;span style="font-style:italic;"&gt;resultado_n&lt;/span&gt;, deben ser todos del mismo &lt;a href="http://www.plsql.biz/2006/10/tipos-de-datos-en-plsql.html"&gt;tipo de dato PLSQL&lt;/a&gt;. Son los valores que devuelve la sentencia CASE en el momento en que una condición se cumple como verdadera.&lt;br /&gt;&lt;br /&gt;Si ninguna de la condiciones se cumple como verdadera, entonces la sentencia CASE devuelve el valor de la &lt;span style="font-weight:bold;"&gt;cláusula ELSE&lt;/span&gt;. Si la cláusula ELSE se omite y ninguna condición se cumple como verdadera, entonces la sentencia CASE devuelve el valor NULL.&lt;br /&gt;&lt;br /&gt;En una sentencia CASE puede haber hasta 255 cláusulas WHEN-THEN-ELSE, por lo tanto podremos construir &lt;span style="font-weight:bold;"&gt;hasta 128 comparaciones&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;A continuación podéis ver un par de &lt;span style="font-weight:bold;"&gt;ejemplos de sentencia SELECT&lt;/span&gt; que incluyen una sentencia CASE:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT apellido, &lt;br /&gt;  CASE nombre&lt;br /&gt;    WHEN 'Pepe' THEN 'se llama Pepe'&lt;br /&gt;    WHEN 'Juan' THEN 'se llama Juan'&lt;br /&gt;    ELSE 'no se llama ni Pepe, ni Juan'&lt;br /&gt;  END&lt;br /&gt;FROM empleados;&lt;br /&gt;&lt;br /&gt;SELECT apellido, &lt;br /&gt;  CASE&lt;br /&gt;    WHEN nombre = 'Pepe' THEN 'se llama Pepe'&lt;br /&gt;    WHEN nombre = 'Juan' THEN 'se llama Juan'&lt;br /&gt;    ELSE 'no se llama ni Pepe, ni Juan'&lt;br /&gt;  END&lt;br /&gt;FROM empleados;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Ambas sentencias SQL devolverán los mismo resultados aunque la segunda sentencia se ha escrito si utilizar la opción &lt;span style="font-style:italic;"&gt;expresion&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;La sentencia CASE admite múltiples combinaciones, por ejemplo se puede utilizar más de un campo y las condiciones que se pueden contruir pueden ser todo lo complejas que se nos ocurra. Veamos &lt;span style="font-weight:bold;"&gt;otro ejemplo&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT apellido, nombre,&lt;br /&gt;  CASE&lt;br /&gt;    WHEN salario &gt; pago_especie&lt;br /&gt;     AND apellido &lt; 'P' THEN 'es rico'&lt;br /&gt;    WHEN salario &lt; pago_especie&lt;br /&gt;     AND apellido &gt;= 'Q' THEN 'es pobre'&lt;br /&gt;    ELSE 'no es ni pobre ni rico'&lt;br /&gt;  END&lt;br /&gt;FROM empleados;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En nuestro ejemplo resultaría que los empleados cuyo salarío fuera mayor que lo que reciben en especie y cuyo nombre empezase por una letra de la A a la O, serían considerados &lt;span style="font-style:italic;"&gt;ricos&lt;/span&gt;, los empleados cuyo salarío fuera menor que lo que reciben en especie y cuyo nombre empezase por una letra de la Q a la Z, serían considerados &lt;span style="font-style:italic;"&gt;pobres&lt;/span&gt;, y el resto de los empleados no serían considerados ni &lt;span style="font-style:italic;"&gt;ricos&lt;/span&gt; ni &lt;span style="font-style:italic;"&gt;pobres&lt;/span&gt;.&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-merge.html"&gt;La sentencia MERGE en PL/SQL&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://www.plsql.biz/2008/05/oracle-11g-y-la-clusula-pivot-como.html"&gt;La cláusula PIVOT en PLSQL&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 en PL/SQL&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-8090929788017307553?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Hv3zDtuW1qM:47hVZ7fsmo0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Hv3zDtuW1qM:47hVZ7fsmo0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Hv3zDtuW1qM:47hVZ7fsmo0:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Hv3zDtuW1qM:47hVZ7fsmo0:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=Hv3zDtuW1qM:47hVZ7fsmo0:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Hv3zDtuW1qM:47hVZ7fsmo0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=Hv3zDtuW1qM:47hVZ7fsmo0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Hv3zDtuW1qM:47hVZ7fsmo0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=Hv3zDtuW1qM:47hVZ7fsmo0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=Hv3zDtuW1qM:47hVZ7fsmo0:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=Hv3zDtuW1qM:47hVZ7fsmo0:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/Hv3zDtuW1qM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/8090929788017307553/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=8090929788017307553&amp;isPopup=true" title="4 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/8090929788017307553?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/8090929788017307553?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/Hv3zDtuW1qM/sentencia-case-en-plsql-de-oracle.html" title="Sentencia CASE en PL/SQL de Oracle" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_ObxKtfPuuSQ/Svm4QhlFYCI/AAAAAAAAE9s/0wivuuR20WI/s72-c/plsql-sentencia-case-chiste-ordenador-raton.JPG" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://www.plsql.biz/2009/11/sentencia-case-en-plsql-de-oracle.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkIFQHo7fyp7ImA9WxFUE04.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-3043481632730716550</id><published>2009-10-06T18:15:00.007+02:00</published><updated>2010-06-24T01:08:31.407+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-24T01:08:31.407+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>Cómo ejecutar sentencias DDL dentro de un trigger PL/SQL</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/o0ye0FzM_FHrgNByPRC30xzkddU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/o0ye0FzM_FHrgNByPRC30xzkddU/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/o0ye0FzM_FHrgNByPRC30xzkddU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/o0ye0FzM_FHrgNByPRC30xzkddU/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/SstthPZqupI/AAAAAAAAE5c/y_zJv6N5VtU/s1600-h/PLSQL-en-la-red.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 174px;" src="http://4.bp.blogspot.com/_ObxKtfPuuSQ/SstthPZqupI/AAAAAAAAE5c/y_zJv6N5VtU/s200/PLSQL-en-la-red.jpg" border="0" alt="Triggers PLSQL y sentencias DDL o no transaccionales" id="BLOGGER_PHOTO_ID_5389521796916230802" /&gt;&lt;/a&gt;Supongo que muchos conoceréis el hecho de que &lt;b&gt;no es posible incluir sentencias DDL&lt;/b&gt; (es decir, sentencias de definición de objetos como CREATE, REVOKE, GRANT, ALTER, etcétera) &lt;b&gt;dentro de un &lt;a href="http://www.plsql.biz/2007/02/triggers-en-plsql.html"&gt;trigger PL/SQL&lt;/a&gt;&lt;/b&gt;. Esto es un hecho que tiene una explicación muy sencilla, ¿qué ocurriría si dentro de un trigger ejecutamos una sentencia no transaccional o DDL y necesitamos deshacer la transacción (&lt;i&gt;rollback&lt;/i&gt;)? Sencillamente no podríamos deshacer la ejecución de dicha sentencia. En esta situación nuestra transacción dentro del trigger PLSQL no habría tenido lugar, pero la sentencia DDL se habría ejecutado dejando nuestra base de datos Oracle en una situación claramente no deseable.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;La ejecución de sentencias no transaccionales en un trigger sólo puede derivar en problemas, así que, en el caso improbable de que necesitemos ejecutar una sentencia DDL dentro de un trigger, mi recomendación es utilizar el &lt;a href="http://www.plsql.biz/2009/06/el-paquete-plsql-dbmsscheduler-para.html"&gt;&lt;b&gt;paquete PLSQL DBMS_JOB&lt;/b&gt;&lt;/a&gt; para programar la ejecución de dicha sentencia DDL (CREATE, REPLACE, DROP, etc.). Veamos un ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE TABLE ejecutar_DDL (&lt;br /&gt;  njob NUMBER PRIMARY KEY,&lt;br /&gt;  sentencia VARCHAR2(2000) );&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE&lt;br /&gt;PROCEDURE ejecutar_DDL_en_trigger&lt;br /&gt;(  p_njob IN NUMBER )&lt;br /&gt;IS&lt;br /&gt;  l_ejecutar_DDL ejecutar_DDL%ROWTYPE;&lt;br /&gt;BEGIN&lt;br /&gt;  SELECT * INTO l_ejecutar_DDL&lt;br /&gt;  FROM ejecutar_DDL&lt;br /&gt;  WHERE njob = p_njob;&lt;br /&gt;  EXECUTE IMMEDIATE l_ejecutar_DDL.sentencia;&lt;br /&gt;END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Ahora lo único que queda es, una vez que nuestra transacción en el trigger PL/SQL se ha completado, invocar nuestra sentencia DDL utilizando un bloque PLSQL similar a este:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;DECLARE&lt;br /&gt;  l_njob NUMBER;&lt;br /&gt;BEGIN&lt;br /&gt;  DBMS_JOB.SUBMIT (&lt;br /&gt;    l_njob,&lt;br /&gt;    'ejecutar_DDL_en_trigger(JOB);' );&lt;br /&gt;  INSERT INTO ejecutar_DDL&lt;br /&gt;  VALUES (&lt;br /&gt;    l_njob,&lt;br /&gt;    '...sentencia DDL (sin ; al final)...' );&lt;br /&gt;  COMMIT;&lt;br /&gt;END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;De esta forma dispondremos de un procedimiento PL/SQL, &lt;i&gt;ejecutar_DDL_en_trigger&lt;/i&gt;, que nos permitirá ejecutar nuestra sentencia DDL recogiendo, de ser necesario, los errores producidos y que, además, se ejecutará poco tiempo después de que la transacción realizada dentro del trigger se haya completado (&lt;i&gt;commit&lt;/i&gt;). Y lo mejor de todo es que si nuestra transacción se deshace (&lt;i&gt;rollback&lt;/i&gt;), el INSERT en la tabla &lt;i&gt;ejecutar_DDL&lt;/i&gt; también se habrá deshecho y la sentencia DDL no se ejecutará.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusión&lt;/b&gt;: dentro de un trigger PL/SQL, siempre deberemos crear soluciones semejantes a ésta cuando estemos pensando en ejecutar algo del tipo no transaccional (ver artículo &lt;a href="http://www.plsql.biz/2008/12/problemas-con-los-triggers-sql.html"&gt;&lt;b&gt;problemas con los triggers PLSQL&lt;/b&gt;&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-3043481632730716550?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=L85KfFTpBdw:txDLF2-6c3c:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=L85KfFTpBdw:txDLF2-6c3c:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=L85KfFTpBdw:txDLF2-6c3c:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=L85KfFTpBdw:txDLF2-6c3c:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=L85KfFTpBdw:txDLF2-6c3c:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=L85KfFTpBdw:txDLF2-6c3c:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=L85KfFTpBdw:txDLF2-6c3c:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=L85KfFTpBdw:txDLF2-6c3c:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=L85KfFTpBdw:txDLF2-6c3c:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=L85KfFTpBdw:txDLF2-6c3c:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=L85KfFTpBdw:txDLF2-6c3c:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/L85KfFTpBdw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/3043481632730716550/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=3043481632730716550&amp;isPopup=true" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3043481632730716550?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3043481632730716550?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/L85KfFTpBdw/como-ejecutar-sentencias-ddl-dentro-de.html" title="Cómo ejecutar sentencias DDL dentro de un trigger PL/SQL" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_ObxKtfPuuSQ/SstthPZqupI/AAAAAAAAE5c/y_zJv6N5VtU/s72-c/PLSQL-en-la-red.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.plsql.biz/2009/10/como-ejecutar-sentencias-ddl-dentro-de.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkIGQHwzeCp7ImA9WxFUE04.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-6333491315969511507</id><published>2009-09-03T20:14:00.013+02:00</published><updated>2010-06-24T01:08:41.280+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-24T01:08:41.280+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="Bases de datos Oracle" /><title>Transacciones parciales en PLSQL para lidiar con tablas muy grandes o voluminosas</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/2tjRtA_m0WkB2V8spExsDI7_3rs/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/2tjRtA_m0WkB2V8spExsDI7_3rs/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/2tjRtA_m0WkB2V8spExsDI7_3rs/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/2tjRtA_m0WkB2V8spExsDI7_3rs/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/SqAKZMA2OQI/AAAAAAAAE1I/X_JbGqgY6Sw/s1600-h/tansacciones-parciales-PLSQL.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 186px; height: 200px;" src="http://1.bp.blogspot.com/_ObxKtfPuuSQ/SqAKZMA2OQI/AAAAAAAAE1I/X_JbGqgY6Sw/s200/tansacciones-parciales-PLSQL.jpg" border="0" alt="Posibles problemas con las transacciones parciales en PLSQL" id="BLOGGER_PHOTO_ID_5377309382918158594" /&gt;&lt;/a&gt;Hace unas semanas alguien mencionó en un comentario que en su base de datos Oracle tenía una &lt;span style="font-weight:bold;"&gt;tabla con millones de registros&lt;/span&gt; y que, utilizando un &lt;span style="font-weight:bold;"&gt;bucle PLSQL&lt;/span&gt;, pensaba ejecutar un &lt;i&gt;UPDATE&lt;/i&gt; y un &lt;i&gt;COMMIT&lt;/i&gt; por cada, digamos, 500 registros procesados en dicha tabla, evitando así posibles problemas con los segmentos de &lt;i&gt;rollback&lt;/i&gt;. El caso es que dicho lector me preguntaba si yo tenía alguna sugerencia al respecto. Este tipo de problema es, ciertamente, algo más complejo de lo que a simple vista parece, y sobre el que conviene escribir con cierta calma.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;span style="font-weight:bold;"&gt;Si dividimos una transacción muy larga en muchas otras más pequeñas&lt;/span&gt;, existen bastantes probabilidades de que a mitad de la transacción global se produzca un error &lt;span style="font-weight:bold;"&gt;ORA-01555&lt;/span&gt; (&lt;i&gt;snapshop too old&lt;/i&gt;), problema generalmente causado por la alta frecuencia en la ejecución de sentencias COMMIT, o que simplemente se produzca un fallo del sistema. Entonces nos encontraremos con una transacción realizada parcialmente. Por lo tanto, antes de implementar este tipo de solución, deberemos asegurarnos de la que la transacción global puede &lt;span style="font-weight:bold;"&gt;re-ejecutarse&lt;/span&gt;. La cuestión es que en muchos casos esto no será posible y, por tanto, tendremos que escribir bastante código PL/SQL para hacer posible esta re-ejecución.&lt;br /&gt;&lt;br /&gt;Supongamos que tenemos el siguiente bloque de código PLSQL:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;DECLARE&lt;br /&gt;  CURSOR cur IS SELECT * FROM tmillon;&lt;br /&gt;BEGIN&lt;br /&gt;  OPEN cur;&lt;br /&gt;  LOOP&lt;br /&gt;    FETCH cur BULK COLLECT INTO cur_rec LIMIT 500;&lt;br /&gt;    ...&lt;br /&gt;    FORALL i IN 1 .. cur_rec.COUNT&lt;br /&gt;      UPDATE tmillon SET ... ;&lt;br /&gt;      -- cur_rec se usa en el UPDATE&lt;br /&gt;    ...&lt;br /&gt;    COMMIT;&lt;br /&gt;    EXIT WHEN cur%NOTFOUND;&lt;br /&gt;  END LOOP;&lt;br /&gt;  CLOSE cur;&lt;br /&gt;END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;En el caso concreto de este &lt;span style="font-weight:bold;"&gt;bloque PL/SQL&lt;/span&gt; os puedo asegurar de que la probabilidad de que se produzca un error ORA-01555 es bastante elevada, la razón principal es que estamos leyendo y modificando la misma tabla y que el &lt;i&gt;SELECT&lt;/i&gt; del cursor debe entregar, en todo instante, los valores del momento en que se inició la consulta. El caso es que la base de datos necesitará espacio para el &lt;i&gt;SELECT&lt;/i&gt; y para poder hacer un &lt;i&gt;ROLLBACK&lt;/i&gt; de los &lt;i&gt;UPDATE&lt;/i&gt; ejecutados, siendo muy probable que se produzca el mencionado error.&lt;br /&gt;&lt;br /&gt;Entonces, puesto que se puede producir este error, deberemos pensar en &lt;span style="font-weight:bold;"&gt;alguna forma de poder re-ejecutar el bloque PL/SQL&lt;/span&gt;, necesitaremos bien una columna en la tabla que nos indique que dicho registro ha sido actualizado, u otra tabla en la que se inserten las claves primarias de los registros que han sido modificados. Si empleamos esta última solución, nuestro cursor quedaría de la siguiente manera:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CURSOR cur IS SELECT * FROM tmillon t&lt;br /&gt;WHERE NOT EXISTS (&lt;br /&gt;  SELECT 'Existe'&lt;br /&gt;  FROM claves_ok c&lt;br /&gt;  WHERE c.prclave = t.prclave);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Estás dos soluciones no son únicas, pero lo que yo quiero hacer patente es que, en cualquier caso, siempre tendremos que escribir algo de código adicional. &lt;br /&gt;&lt;br /&gt;Para lidiar con este tipo de situaciones yo recomiendo:&lt;ul&gt;&lt;li&gt;&lt;b&gt;Actualizar la tabla utilizando una sola sentencia PLSQL&lt;/b&gt;. Esta es, sin duda, la manera más eficiente en términos de utilización de los recursos de la base de datos. Además, no nos debemos preocupar demasiado por un posible problema con los segmentos de &lt;i&gt;rollback&lt;/i&gt;, ya que estos se necesitan para que la transacción se complete correctamente.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Utilizar el paquete PL/SQL DBMS_REDEFINITION&lt;/b&gt;. Con este paquete crearemos una copia de la tabla base de forma online, es decir, que no será necesario bloquear las modificaciones de la tabla, y, después, utilizaremos dicha tabla en el cursor principal. Estaremos utilizando una estructura de datos más compacta e incluso, si nos interesa, en la versión 10g de la base de datos Oracle, podemos añadir un comando &lt;i&gt;ORDER BY&lt;/i&gt; a la redefinición de la tabla para reordenar los registros de la misma en el disco. De este paquete es muy probable que hable en una entrada posterior.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Utilizar el comando CREATE TABLE AS SELECT&lt;/b&gt;. Esto sería similar a utilizar el paquete PL/SQL &lt;i&gt;DBMS_REDEFINITION&lt;/i&gt; pero, en este caso, no debemos permitir que mientras se realice esta operación se esté modificando la tabla fuente original. Además, el paquete DBMS_REDEFINITION se encarga de la creación automática de todo los objetos necesarios para crear una copia de la tabla (índices, permisos, etcétera), mientras que si utilizamos &lt;i&gt;CREATE TABLE AS SELECT&lt;/i&gt;, de ser necesario, tendremos que crear todos estos objetos manualmente.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-6333491315969511507?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NihEwA-U7QY:eRzSj3sNvkQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NihEwA-U7QY:eRzSj3sNvkQ:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NihEwA-U7QY:eRzSj3sNvkQ:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NihEwA-U7QY:eRzSj3sNvkQ:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=NihEwA-U7QY:eRzSj3sNvkQ:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NihEwA-U7QY:eRzSj3sNvkQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=NihEwA-U7QY:eRzSj3sNvkQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NihEwA-U7QY:eRzSj3sNvkQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=NihEwA-U7QY:eRzSj3sNvkQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=NihEwA-U7QY:eRzSj3sNvkQ:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=NihEwA-U7QY:eRzSj3sNvkQ:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/NihEwA-U7QY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/6333491315969511507/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=6333491315969511507&amp;isPopup=true" title="10 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6333491315969511507?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/6333491315969511507?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/NihEwA-U7QY/transacciones-parciales-en-plsql-para.html" title="Transacciones parciales en PLSQL para lidiar con tablas muy grandes o voluminosas" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_ObxKtfPuuSQ/SqAKZMA2OQI/AAAAAAAAE1I/X_JbGqgY6Sw/s72-c/tansacciones-parciales-PLSQL.jpg" height="72" width="72" /><thr:total>10</thr:total><feedburner:origLink>http://www.plsql.biz/2009/09/transacciones-parciales-en-plsql-para.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEACRX0yfSp7ImA9WxNaFUk.&quot;"><id>tag:blogger.com,1999:blog-26801181.post-3316707522995874351</id><published>2009-07-22T17:47:00.012+02:00</published><updated>2009-11-30T01:59:24.395+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-30T01:59:24.395+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tutorial PL/SQL" /><title>Cómo utilizar un cursor PL/SQL como parámetro de salida en un procedimiento</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/KeUoPsnt8fmkoDUCKcAmDqoXEdE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/KeUoPsnt8fmkoDUCKcAmDqoXEdE/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/KeUoPsnt8fmkoDUCKcAmDqoXEdE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/KeUoPsnt8fmkoDUCKcAmDqoXEdE/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/Smc5ILSL2cI/AAAAAAAAEt8/2Qx0zK41ZBA/s1600-h/Cursores-PLSQL-milagrosos-salida-procedimiento.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 199px; height: 200px;" src="http://1.bp.blogspot.com/_ObxKtfPuuSQ/Smc5ILSL2cI/AAAAAAAAEt8/2Qx0zK41ZBA/s200/Cursores-PLSQL-milagrosos-salida-procedimiento.JPG" border="0" alt="Cursores PLSQL usados como parámetro de salida de un procedure o procedimiento" id="BLOGGER_PHOTO_ID_5361316694038665666" /&gt;&lt;/a&gt;En este artículo o voy a proponer un ejercicio práctico bastante sencillo que puede resultar de bastante útilidad práctica. Supongamos que queremos crear un &lt;span style="font-weight:bold;"&gt;procedimiento PLSQL&lt;/span&gt; que utilice un par de parámetros de entrada, que podrán tomar el valor &lt;span style="font-style:italic;"&gt;NULL&lt;/span&gt;, y con un &lt;span style="font-weight:bold;"&gt;parámetro de salida&lt;/span&gt; que será el &lt;a href="http://www.plsql.biz/2006/12/cursores-en-plsql.html"&gt;&lt;span style="font-weight:bold;"&gt;cursor PL/SQL&lt;/span&gt;&lt;/a&gt; correspondiente a la siguiente sentencia &lt;span style="font-style:italic;"&gt;SELECT&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;SELECT * FROM empleados&lt;br /&gt;WHERE nombre = parametro_1&lt;br /&gt;  AND apellidos = parametro_2;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;Lo primero que tendremos que hacer es crear un paquete PL/SQL que contenga la &lt;span style="font-weight:bold;"&gt;definición del cursor&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE OR REPLACE PACKAGE empleados_pkg&lt;br /&gt;IS&lt;br /&gt;  /* Definición del REF CURSOR type */&lt;br /&gt;  TYPE empleados_type IS REF CURSOR&lt;br /&gt;  RETURN empleados%ROWTYPE;&lt;br /&gt;END empleados_pkg;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Después sólo tendremos que crear el &lt;span style="font-weight:bold;"&gt;procedimiento PLSQL que devolverá como salida el cursor&lt;/span&gt; definido en el paso anterior, y que utilizará los dos parámetros de entrada indicados en el enunciado del problema que estamos intentando resolver.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE OR REPLACE PROCEDURE datos_empleados&lt;br /&gt;  (&lt;br /&gt;  par1 IN VARCHAR2,&lt;br /&gt;  par2 IN VARCHAR2,&lt;br /&gt;  cursor1 OUT empleados_pkg.empleados_type&lt;br /&gt;  )&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;  OPEN cursor1 FOR&lt;br /&gt;  SELECT * FROM empleados&lt;br /&gt;  WHERE nombre = NVL(par1,nombre)&lt;br /&gt;    AND apellidos = NVL(par2,apellidos);&lt;br /&gt;END datos_empleados;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;No obstante, la utilización de la &lt;span style="font-weight:bold;"&gt;función NVL&lt;/span&gt; (si par1 es NULL toma el valor &lt;span style="font-style:italic;"&gt;nombre&lt;/span&gt;, si par2 es NULL toma el valor &lt;span style="font-style:italic;"&gt;apellidos&lt;/span&gt;), podría no ser muy conveniente y derivar en &lt;span style="font-weight:bold;"&gt;problemas de rendimiento&lt;/span&gt;, por lo que es recomendable definir el procedimiento anterior como sigue:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new; font-size: 100%;"&gt;&lt;pre&gt;CREATE OR REPLACE PROCEDURE datos_empleados&lt;br /&gt;  (&lt;br /&gt;  par1 IN VARCHAR2,&lt;br /&gt;  par2 IN VARCHAR2,&lt;br /&gt;  cursor1 OUT empleados_pkg.empleados_type&lt;br /&gt;  )&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;&lt;br /&gt;  /* Todos los parámetros tienen valor */&lt;br /&gt;  IF par1 IS NOT NULL AND par2 IS NOT NULL&lt;br /&gt;  THEN&lt;br /&gt;    OPEN cursor1 FOR&lt;br /&gt;    SELECT * FROM empleados&lt;br /&gt;    WHERE nombre = par1&lt;br /&gt;      AND apellidos = par2;&lt;br /&gt;&lt;br /&gt;  /* Sólo tiene valor par1 */&lt;br /&gt;  ELSIF par1 IS NOT NULL&lt;br /&gt;  THEN&lt;br /&gt;    OPEN cursor1 FOR&lt;br /&gt;    SELECT * FROM empleados&lt;br /&gt;    WHERE nombre = par1;&lt;br /&gt;&lt;br /&gt;  /* Sólo tienen valor par2 */&lt;br /&gt;  ELSIF par2 IS NOT NULL&lt;br /&gt;  THEN&lt;br /&gt;    OPEN cursor1 FOR&lt;br /&gt;    SELECT * FROM empleados&lt;br /&gt;    WHERE apellidos = par2;&lt;br /&gt;&lt;br /&gt;  /* Ningún parámetro tiene valor */&lt;br /&gt;  ELSE&lt;br /&gt;  THEN&lt;br /&gt;    OPEN cursor1 FOR&lt;br /&gt;    SELECT * FROM empleados;&lt;br /&gt;&lt;br /&gt;  END IF;&lt;br /&gt;&lt;br /&gt;END datos_empleados;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Artículos relacionados&lt;/span&gt;: &lt;a href="http://www.plsql.biz/2006/12/cursores-en-plsql.html" rel="nofollow"&gt;Cursores en PLSQL&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26801181-3316707522995874351?l=www.plsql.biz' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=uIXDrGqlpsE:keNGE7p0nrY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=uIXDrGqlpsE:keNGE7p0nrY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=uIXDrGqlpsE:keNGE7p0nrY:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=uIXDrGqlpsE:keNGE7p0nrY:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=uIXDrGqlpsE:keNGE7p0nrY:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=uIXDrGqlpsE:keNGE7p0nrY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=uIXDrGqlpsE:keNGE7p0nrY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=uIXDrGqlpsE:keNGE7p0nrY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=uIXDrGqlpsE:keNGE7p0nrY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/PLSQL?a=uIXDrGqlpsE:keNGE7p0nrY:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/PLSQL?i=uIXDrGqlpsE:keNGE7p0nrY:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PLSQL/~4/uIXDrGqlpsE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.plsql.biz/feeds/3316707522995874351/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=26801181&amp;postID=3316707522995874351&amp;isPopup=true" title="31 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3316707522995874351?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/26801181/posts/default/3316707522995874351?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/PLSQL/~3/uIXDrGqlpsE/como-utilizar-un-cursor-plsql-como.html" title="Cómo utilizar un cursor PL/SQL como parámetro de salida en un procedimiento" /><author><name>José Luis Pérez</name><uri>https://profiles.google.com/102634062599347146150</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh5.googleusercontent.com/-4QS7hkQaOzk/AAAAAAAAAAI/AAAAAAAAGhg/FWpeXVJY9cg/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_ObxKtfPuuSQ/Smc5ILSL2cI/AAAAAAAAEt8/2Qx0zK41ZBA/s72-c/Cursores-PLSQL-milagrosos-salida-procedimiento.JPG" height="72" width="72" /><thr:total>31</thr:total><feedburner:origLink>http://www.plsql.biz/2009/07/como-utilizar-un-cursor-plsql-como.html</feedburner:origLink></entry></feed>

