<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-4063906708258638821</atom:id><lastBuildDate>Fri, 29 May 2026 07:35:26 +0000</lastBuildDate><title>El Blog de Leonardo Horikian</title><description>Oracle Performance</description><link>http://lhorikian.blogspot.com/</link><managingEditor>noreply@blogger.com (Leonardo Horikian)</managingEditor><generator>Blogger</generator><openSearch:totalResults>64</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-8885249564980281232</guid><pubDate>Fri, 10 Feb 2012 17:01:00 +0000</pubDate><atom:updated>2012-02-10T14:03:38.288-03:00</atom:updated><title>Simposio HOTSOS 2012</title><description>&lt;a href=&quot;http://www.hotsos.com/img/sym_logo_2012.jpg&quot;&gt;&lt;img style=&quot;cursor:pointer; cursor:hand;width: 203px; height: 114px;&quot; src=&quot;http://www.hotsos.com/img/sym_logo_2012.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Todos los años, se realiza un evento llamado Simposio HOTSOS. Este evento esta exclusivamente dedicado a Oracle Performance Tuning.&lt;br /&gt;&lt;br /&gt;El Simposio se realiza todos los años en el mes de Marzo en la ciudad de Dallas, Texas (USA). Esta conferencia es única a nivel mundial! Reune a los mejores expertos de Oracle del mundo!&lt;br /&gt;&lt;br /&gt;En el año 2010 y 2011, tuve la oportunidad de poder viajar y presenciar el Simposio. Definitivamente se lo recomiendo a todas las personas que utilizan Oracle y que se especializan o que se encuentran interesadas en lo que respecta a Oracle Performance Tuning.</description><link>http://lhorikian.blogspot.com/2012/02/simposio-hotsos-2012.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-4648910066394330510</guid><pubDate>Tue, 06 Jul 2010 16:41:00 +0000</pubDate><atom:updated>2010-07-06T17:34:33.665-03:00</atom:updated><title>Eliminar una consulta de la Shared Pool</title><description>A partir de Oracle 10.2.0.4 en adelante, el paquete DBMS_SHARED_POOL contiene nuevos procedimientos. Uno de esos procedimientos se llama PURGE y es el encargado de eliminar determinados objetos de la Shared Pool. Estos objetos pueden ser: cursores (consultas SQL), paquetes, procedimientos, funciones, triggers, secuencias y tipos.&lt;br /&gt;En versiones anterior, esto no podíamos hacerlo y lo que debíamos hacer era eliminar todas las consultas de la Shared Pool mediante la sentencia &quot;ALTER SYSTEM FLUSH SHARED_POOL&quot;.&lt;br /&gt;&lt;br /&gt;La creación del paquete DBMS_SHARED_POOL podemos encontrarla en $ORACLE_HOME/rdbms/admin/dbmspool.sql&lt;br /&gt;&lt;br /&gt;En Oracle 10.2.0.4 existe el bug 5614566 que provoca el mal funcionamiento del procedimiento PURGE. Pero, tenemos un manera de evitar este bug en esa versión.&lt;br /&gt;&lt;br /&gt;Comencemos viendo un ejemplo en 11gR1:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; desc DBMS_SHARED_POOL&lt;br /&gt;&lt;br /&gt;PROCEDURE ABORTED_REQUEST_THRESHOLD&lt;br /&gt; Argument Name                  Type                    In/Out Default?&lt;br /&gt; ------------------------------ ----------------------- ------ --------&lt;br /&gt; THRESHOLD_SIZE                 NUMBER                  IN&lt;br /&gt;PROCEDURE KEEP&lt;br /&gt; Argument Name                  Type                    In/Out Default?&lt;br /&gt; ------------------------------ ----------------------- ------ --------&lt;br /&gt; NAME                           VARCHAR2                IN&lt;br /&gt; FLAG                           CHAR                    IN     DEFAULT&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;PROCEDURE PURGE&lt;br /&gt; Argument Name                  Type                    In/Out Default?&lt;br /&gt; ------------------------------ ----------------------- ------ --------&lt;br /&gt; NAME                           VARCHAR2                IN&lt;br /&gt; FLAG                           CHAR                    IN     DEFAULT&lt;br /&gt; HEAPS                          NUMBER                  IN     DEFAULT&lt;/span&gt;&lt;br /&gt;PROCEDURE SIZES&lt;br /&gt; Argument Name                  Type                    In/Out Default?&lt;br /&gt; ------------------------------ ----------------------- ------ --------&lt;br /&gt; MINSIZE                        NUMBER                  IN&lt;br /&gt;PROCEDURE UNKEEP&lt;br /&gt; Argument Name                  Type                    In/Out Default?&lt;br /&gt; ------------------------------ ----------------------- ------ --------&lt;br /&gt; NAME                           VARCHAR2                IN&lt;br /&gt; FLAG                           CHAR                    IN     DEFAULT&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Para ejecutar el procedimiento PURGE, debemos pasar como valor en el parámetro NAME el ADDRESS junto con el HASH_VALUE separados por una coma(,).&lt;br /&gt;Estos valores podemos encontrarlos en la vista V$SQLAREA.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; SELECT /* prueba */ 1 FROM DUAL;&lt;br /&gt;&lt;br /&gt;         1&lt;br /&gt;----------&lt;br /&gt;         1&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT address||&#39;,&#39;||hash_value name &lt;br /&gt;     FROM v$sqlarea &lt;br /&gt;     WHERE sql_text = &#39;SELECT /* prueba */ 1 FROM DUAL&#39;;&lt;br /&gt;&lt;br /&gt;NAME&lt;br /&gt;-------------------&lt;br /&gt;2E10E868,4198164882&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como podemos ver, lo que hicimos fue ejecutar una consulta para que sea parseada por primera vez y almacenada en la Shared Pool. Luego buscamos en la vista V$SQLAREA los valores que debo pasar en el parámetro NAME del procedimiento PURGE.&lt;br /&gt;&lt;br /&gt;Ahora vamos a eliminar la consulta de la Shared Pool y a verificar que se haya eliminado.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; &lt;span style=&quot;font-weight:bold;&quot;&gt;exec dbms_shared_pool.purge(&#39;2E10E868,4198164882&#39;,&#39;C&#39;,1);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT address||&#39;,&#39;||hash_value name &lt;br /&gt;     FROM v$sqlarea &lt;br /&gt;     WHERE sql_text = &#39;SELECT /* prueba */ 1 FROM DUAL&#39;;&lt;br /&gt;&lt;br /&gt;no rows selected&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;La consulta fue eliminada con éxito!&lt;br /&gt;&lt;br /&gt;Para hacer funcionar el procedimiento en 10.2.0.4, debemos realizar el siguiente alter en la sesión antes de ejecutar el procedimiento PURGE:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; alter session set events &#39;5614566 trace name context forever&#39;;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Pueden utilizar el siguiente script para hacer el purge de un determinado cursor de la Shared Pool:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;DECLARE&lt;br /&gt; name    varchar2(50);&lt;br /&gt; version varchar2(3);&lt;br /&gt;BEGIN&lt;br /&gt; select regexp_replace(version,&#39;\..*&#39;) &lt;br /&gt; into version &lt;br /&gt; from v$instance;&lt;br /&gt;&lt;br /&gt; if version = &#39;10&#39; then&lt;br /&gt;   execute immediate&lt;br /&gt;   q&#39;[alter session set events &#39;5614566 trace name context forever&#39;]&#39;; -- bug 5614566&lt;br /&gt; end if;&lt;br /&gt;&lt;br /&gt; select address||&#39;,&#39;||hash_value &lt;br /&gt; into name&lt;br /&gt; from v$sqlarea&lt;br /&gt; where sql_id like &#39;&amp;sql_id&#39;;&lt;br /&gt;&lt;br /&gt; dbms_shared_pool.purge(name,&#39;C&#39;,1);&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;</description><link>http://lhorikian.blogspot.com/2010/07/eliminar-una-consulta-de-la-shared-pool.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-4419795490719036757</guid><pubDate>Tue, 06 Jul 2010 05:04:00 +0000</pubDate><atom:updated>2010-07-06T13:39:44.121-03:00</atom:updated><title>Diferencias entre  BETWEEN  y  &gt;= &lt;=</title><description>Muchas personas prefieren utilizar el BETWEEN en vez de &gt;= &lt;= y otras personas prefieren usar lo contrario porque suponen que hay alguna diferencia a nivel de performance. Esto es un mito muy antiguo. La realidad es que no existen diferencias.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;BETWEEN es una &quot;sinónimo&quot; de  &gt;= &lt;=&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;Voy a demostrar esto con una sencilla prueba:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; CREATE TABLE test AS&lt;br /&gt;  2  SELECT level id, &#39;nombre_&#39;||level nombre&lt;br /&gt;  3  FROM dual&lt;br /&gt;  4  CONNECT BY level &lt;= 100;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, &#39;TEST&#39;, estimate_percent =&gt; 100, method_opt =&gt; &#39;for all columns size 1&#39;);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; DESC test&lt;br /&gt;&lt;br /&gt; Name          Null?    Type&lt;br /&gt; ------------- -------- --------------&lt;br /&gt; ID                     NUMBER&lt;br /&gt; NOMBRE                 VARCHAR2(47)&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT nombre&lt;br /&gt;  2  FROM test&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;  3  WHERE id BETWEEN 20 AND 25;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;NOMBRE&lt;br /&gt;---------&lt;br /&gt;nombre_20&lt;br /&gt;nombre_21&lt;br /&gt;nombre_22&lt;br /&gt;nombre_23&lt;br /&gt;nombre_24&lt;br /&gt;nombre_25&lt;br /&gt;&lt;br /&gt;6 rows selected.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,&#39;ALLSTATS LAST&#39;));&lt;br /&gt;&lt;br /&gt;Plan hash value: 1357081020&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;|*  1 |  TABLE ACCESS FULL| TEST |      1 |      7 |      6 |00:00:00.01 |       4 |&lt;/span&gt;&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;   1 - filter((&quot;ID&quot;&lt;=25 AND &quot;ID&quot;&gt;=20))&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como podemos ver en la sección &quot;Predicate Information&quot;, el optimizador reemplazó la sentencia BETWEEN por &quot;ID&quot;&lt;=25 AND &quot;ID&quot;&gt;=20.&lt;br /&gt;También podemos ver, que leyó 4 bloques de datos para recuperar los 6 registros mediante un acceso FULL SCAN.&lt;br /&gt;&lt;br /&gt;Ahora, probemos colocando el &gt;= &lt;= en la consulta ...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; SELECT nombre&lt;br /&gt;  2  FROM test&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;  3  WHERE id &gt;= 20 AND id &lt;= 25;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;NOMBRE&lt;br /&gt;---------&lt;br /&gt;nombre_20&lt;br /&gt;nombre_21&lt;br /&gt;nombre_22&lt;br /&gt;nombre_23&lt;br /&gt;nombre_24&lt;br /&gt;nombre_25&lt;br /&gt;&lt;br /&gt;6 rows selected.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,&#39;ALLSTATS LAST&#39;));&lt;br /&gt;&lt;br /&gt;Plan hash value: 1357081020&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;|*  1 |  TABLE ACCESS FULL| TEST |      1 |      7 |      6 |00:00:00.01 |       4 |&lt;/span&gt;&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;   1 - filter((&quot;ID&quot;&lt;=25 AND &quot;ID&quot;&gt;=20)) &lt;/span&gt;  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Acá volvemos a ver en la a sección &quot;Predicate Information&quot; que el optimizador no realizó ningún reemplazo.&lt;br /&gt;Seguimos leyendo 4 bloques de datos para recuperar los 6 registros mediante un acceso FULL SCAN.&lt;br /&gt;&lt;br /&gt;Una cosa muy importante a destacar entre estas 2 consultas es que el hash value de ambas es el mismo (1357081020). Esto nos indica que la utilización de BETWEEN o &gt;= &lt;= es transparente para el optimizador ya que siempre va a utilizar &gt;= &lt;= para resolver la consulta.&lt;br /&gt;&lt;br /&gt;Qué sucede si creamos un índice en la tabla? Obtendremos alguna diferencia?&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; CREATE UNIQUE INDEX test_uq ON test(id, nombre);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL&gt; EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, &#39;TEST&#39;, estimate_percent =&gt; 100, method_opt =&gt; &#39;for all columns size 1&#39;, cascade =&gt; true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT nombre&lt;br /&gt;  2  FROM test&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;  3  WHERE id BETWEEN 20 AND 25;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;NOMBRE&lt;br /&gt;---------&lt;br /&gt;nombre_20&lt;br /&gt;nombre_21&lt;br /&gt;nombre_22&lt;br /&gt;nombre_23&lt;br /&gt;nombre_24&lt;br /&gt;nombre_25&lt;br /&gt;&lt;br /&gt;6 rows selected.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,&#39;ALLSTATS LAST&#39;));&lt;br /&gt;&lt;br /&gt;Plan hash value: 1087767317&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation        | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |&lt;br /&gt;--------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;|*  1 |  INDEX RANGE SCAN| TEST_UQ |      1 |      7 |      6 |00:00:00.01 |       2 |&lt;/span&gt;&lt;br /&gt;--------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;   1 - access(&quot;ID&quot;&gt;=20 AND &quot;ID&quot;&lt;=25)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Vemos que se volvió a reemplazar el BETWEEN por &gt;= &lt;= y ahora como estamos accediendo por índice, leemos solamente 2 bloques de datos.&lt;br /&gt;&lt;br /&gt;Qué sucederá si modificamos nuevamente la consulta?&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; SELECT nombre&lt;br /&gt;  2  FROM test&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;  3  WHERE id &gt;= 20 AND id &lt;= 25;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;NOMBRE&lt;br /&gt;---------&lt;br /&gt;nombre_20&lt;br /&gt;nombre_21&lt;br /&gt;nombre_22&lt;br /&gt;nombre_23&lt;br /&gt;nombre_24&lt;br /&gt;nombre_25&lt;br /&gt;&lt;br /&gt;6 rows selected.&lt;br /&gt;&lt;br /&gt;sys@orcl&gt; select * from table(dbms_xplan.display_cursor(null,null,&#39;ALLSTATS LAST&#39;));&lt;br /&gt;&lt;br /&gt;Plan hash value: 1087767317&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation        | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |&lt;br /&gt;--------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;|*  1 |  INDEX RANGE SCAN| TEST_UQ |      1 |      7 |      6 |00:00:00.01 |       2 |&lt;/span&gt;&lt;br /&gt;--------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;   1 - access(&quot;ID&quot;&gt;=20 AND &quot;ID&quot;&lt;=25)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Obtuvimos exactamente el mismo resultado que con el BETWEEN.&lt;br /&gt;El hash value entre las 2 consultas sigue siendo el mismo (1087767317).&lt;br /&gt;&lt;br /&gt;En esta prueba, colocamos el campo numérico ID en el WHERE pero qué sucedería si utilizamos un campo caracter?&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; DROP TABLE test;&lt;br /&gt;&lt;br /&gt;Table dropped.&lt;br /&gt;&lt;br /&gt;SQL&gt; CREATE TABLE test AS&lt;br /&gt;  2  SELECT to_char(level) id, &#39;nombre_&#39;||level nombre&lt;br /&gt;  3  FROM dual&lt;br /&gt;  4  CONNECT BY level &lt;= 100;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; DESC test&lt;br /&gt;&lt;br /&gt; Name          Null?    Type&lt;br /&gt; ------------- -------- --------------&lt;br /&gt; ID                     VARCHAR2(40)&lt;br /&gt; NOMBRE                 VARCHAR2(47)&lt;br /&gt;&lt;br /&gt;SQL&gt; EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, &#39;TEST&#39;, estimate_percent =&gt; 100, method_opt =&gt; &#39;for all columns size 1&#39;);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT nombre&lt;br /&gt;  2  FROM test&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;  3  WHERE id BETWEEN 20 AND 25;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;NOMBRE&lt;br /&gt;---------&lt;br /&gt;nombre_20&lt;br /&gt;nombre_21&lt;br /&gt;nombre_22&lt;br /&gt;nombre_23&lt;br /&gt;nombre_24&lt;br /&gt;nombre_25&lt;br /&gt;&lt;br /&gt;6 rows selected.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,&#39;ALLSTATS LAST&#39;));&lt;br /&gt;&lt;br /&gt;Plan hash value: 1357081020&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;|*  1 |  TABLE ACCESS FULL| TEST |      1 |      1 |      6 |00:00:00.01 |       4 |&lt;/span&gt;&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;   1 - filter((TO_NUMBER(&quot;ID&quot;)&gt;=20 AND TO_NUMBER(&quot;ID&quot;)&lt;=25))&lt;/span&gt;&lt;br /&gt;   &lt;br /&gt;SQL&gt; SELECT nombre&lt;br /&gt;  2  FROM test&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;  3  WHERE id &gt;= 20 AND id &lt;= 25;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;NOMBRE&lt;br /&gt;-----------------------------------------------&lt;br /&gt;nombre_20&lt;br /&gt;nombre_21&lt;br /&gt;nombre_22&lt;br /&gt;nombre_23&lt;br /&gt;nombre_24&lt;br /&gt;nombre_25&lt;br /&gt;&lt;br /&gt;6 rows selected.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,&#39;ALLSTATS LAST&#39;));&lt;br /&gt;&lt;br /&gt;Plan hash value: 1357081020&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;|*  1 |  TABLE ACCESS FULL| TEST |      1 |      1 |      6 |00:00:00.01 |       4 |&lt;/span&gt;&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;   1 - filter((TO_NUMBER(&quot;ID&quot;)&gt;=20 AND TO_NUMBER(&quot;ID&quot;)&lt;=25))&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como observamos, ambas consultas siguen siendo idénticas.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Estas pruebas nos indican que no existe diferencia alguna entre la utilización del BETWEEN y el &gt;= &lt;=</description><link>http://lhorikian.blogspot.com/2010/07/diferencias-entre-between-y.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-4022916383463348388</guid><pubDate>Wed, 30 Jun 2010 22:49:00 +0000</pubDate><atom:updated>2010-07-01T10:32:13.339-03:00</atom:updated><title>Crear una base de datos de prueba en minutos!</title><description>La tarea de crear una base de datos puede parecer bastante complicada para algunos o bastante agotadora para otros... pero la realidad es que podemos crear una base de datos de prueba en tan solo minutos sin utilizar entorno gráfico como el X Windows o la VNC. Tan solo ejecutando DBCA podemos crear por linea de comando y de manera no interactica una base de datos!!!&lt;br /&gt;&lt;br /&gt;En este sencillo ejemplo creé el archivo testdb.dbt copiando uno de los archivos template que se encuentran en el directorio $ORACLE_HOME/assistants/dbca/templates/. Luego modifiqué este archivo colocando algunas configuraciones para la nueva base de datos que quiero crear. Por último, ejecuté el siguiente comando ...&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;$ dbca -Silent -CreateDatabase -gdbName testdb -sid testdb -templateName $ORACLE_HOME/assistants/dbca/templates/testdb.dbt -sysPassword oracle -systemPassword oracle -dbsnmpPassword oracle -sysmanPassword oracle&lt;br /&gt;&lt;br /&gt;4% complete&lt;br /&gt;Creating and starting Oracle instance&lt;br /&gt;5% complete&lt;br /&gt;6% complete&lt;br /&gt;7% complete&lt;br /&gt;12% complete&lt;br /&gt;Creating database files&lt;br /&gt;20% complete&lt;br /&gt;Creating data dictionary views&lt;br /&gt;22% complete&lt;br /&gt;24% complete&lt;br /&gt;27% complete&lt;br /&gt;28% complete&lt;br /&gt;29% complete&lt;br /&gt;30% complete&lt;br /&gt;31% complete&lt;br /&gt;32% complete&lt;br /&gt;33% complete&lt;br /&gt;34% complete&lt;br /&gt;39% complete&lt;br /&gt;41% complete&lt;br /&gt;44% complete&lt;br /&gt;46% complete&lt;br /&gt;Adding Oracle Application Express&lt;br /&gt;47% complete&lt;br /&gt;48% complete&lt;br /&gt;49% complete&lt;br /&gt;50% complete&lt;br /&gt;51% complete&lt;br /&gt;52% complete&lt;br /&gt;53% complete&lt;br /&gt;54% complete&lt;br /&gt;55% complete&lt;br /&gt;56% complete&lt;br /&gt;57% complete&lt;br /&gt;58% complete&lt;br /&gt;59% complete&lt;br /&gt;60% complete&lt;br /&gt;62% complete&lt;br /&gt;Adding Oracle Warehouse Builder&lt;br /&gt;63% complete&lt;br /&gt;64% complete&lt;br /&gt;65% complete&lt;br /&gt;66% complete&lt;br /&gt;67% complete&lt;br /&gt;75% complete&lt;br /&gt;78% complete&lt;br /&gt;Completing Database Creation&lt;br /&gt;80% complete&lt;br /&gt;83% complete&lt;br /&gt;85% complete&lt;br /&gt;92% complete&lt;br /&gt;100% complete&lt;br /&gt;&lt;br /&gt;Look at the log file &quot;/u01/app/oracle/cfgtoollogs/dbca/soldb/testdb.log&quot; for further details.&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Existen muchos parámetros opcionales con los que podemos ejecutar el DBCA para crear una base de datos. También, podemos ejecutar DBCA para realizar otras operaciones por linea de comando; como borrar una base de datos, crear scripts, etc.&lt;br /&gt;&lt;br /&gt;Podemos ver todas las operaciones que se pueden ejecutar con el comando DBCA -H ...&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;$ dbca -h&lt;br /&gt;&lt;br /&gt;dbca  [-silent | -progressOnly | -customCreate] {&lt;command&gt; &lt;options&gt; }  | { [&lt;command&gt; [options] ] -responseFile  &lt;response file &gt; } [-continueOnNonFatalErrors &lt;true | false&gt;]&lt;br /&gt;Please refer to the manual for details.&lt;br /&gt;You can enter one of the following command:&lt;br /&gt;&lt;br /&gt;Create a database by specifying the following parameters:&lt;br /&gt;        -createDatabase&lt;br /&gt;                -templateName &lt;name of an existing  template&gt;&lt;br /&gt;                [-cloneTemplate]&lt;br /&gt;                -gdbName &lt;global database name&gt;&lt;br /&gt;                [-policyManaged | -adminManaged &lt;Policy managed or Admin managed Database, default is Admin managed database&gt;]&lt;br /&gt;                        [-createServerPool &lt;To create ServerPool which will be used by the database to be created&gt;]&lt;br /&gt;                        [-force &lt;To create serverpool by force when adequate free servers are not available. This may affect already running database&gt;]&lt;br /&gt;                        -serverPoolName &lt;One serverPool Name in case of create server pool and comma separated list of serverPool name in case of use serverpool&gt;&lt;br /&gt;                        -[cardinality &lt;Specify cardinality for new serverPool to be created, default is the number of qualified nodes&gt;]&lt;br /&gt;                [-sid &lt;database system identifier prefix&gt;]&lt;br /&gt;                [-sysPassword &lt;SYS user password&gt;]&lt;br /&gt;                [-systemPassword &lt;SYSTEM user password&gt;]&lt;br /&gt;                [-emConfiguration &lt;CENTRAL|LOCAL|ALL|NOBACKUP|NOEMAIL|NONE&gt;&lt;br /&gt;                        -dbsnmpPassword &lt;DBSNMP user password&gt;&lt;br /&gt;                        -sysmanPassword &lt;SYSMAN user password&gt;&lt;br /&gt;                        [-hostUserName &lt;Host user name for EM backup job&gt;&lt;br /&gt;                         -hostUserPassword &lt;Host user password for EM backup job&gt;&lt;br /&gt;                         -backupSchedule &lt;Daily backup schedule in the form of hh:mm&gt;]&lt;br /&gt;                        [-smtpServer &lt;Outgoing mail (SMTP) server for email notifications&gt;&lt;br /&gt;                         -emailAddress &lt;Email address for email notifications&gt;]&lt;br /&gt;                        [-centralAgent &lt;Enterprise Manager central agent home&gt;]]&lt;br /&gt;                [-disableSecurityConfiguration &lt;ALL|AUDIT|PASSWORD_PROFILE|NONE&gt;&lt;br /&gt;                [-datafileDestination &lt;destination directory for all database files&gt; |  -datafileNames &lt;a text file containing database objects such as controlfiles, tablespaces, redo log files and spfile to their corresponding raw device file names mappings in name=value format.&gt;]&lt;br /&gt;                [-redoLogFileSize &lt;size of each redo log file in megabytes&gt;]&lt;br /&gt;                [-recoveryAreaDestination &lt;destination directory for all recovery files&gt;]&lt;br /&gt;                [-datafileJarLocation  &lt;location of the data file jar, used only for clone database creation&gt;]&lt;br /&gt;                [-storageType &lt; CFS | ASM &gt;&lt;br /&gt;                        [-asmsnmpPassword     &lt;ASMSNMP password for ASM monitoring&gt;]&lt;br /&gt;                         -diskGroupName   &lt;database area disk group name&gt;&lt;br /&gt;                         -recoveryGroupName       &lt;recovery area disk group name&gt;&lt;br /&gt;                [-nodelist &lt;node names separated by comma for the database&gt;]&lt;br /&gt;                [-characterSet &lt;character set for the database&gt;]&lt;br /&gt;                [-nationalCharacterSet  &lt;national character set for the database&gt;]&lt;br /&gt;                [-registerWithDirService &lt;true | false&gt;&lt;br /&gt;                        -dirServiceUserName    &lt;user name for directory service&gt;&lt;br /&gt;                        -dirServicePassword    &lt;password for directory service &gt;&lt;br /&gt;                        -walletPassword    &lt;password for database wallet &gt;]&lt;br /&gt;                [-listeners  &lt;list of listeners to configure the database with&gt;]&lt;br /&gt;                [-variablesFile   &lt;file name for the variable-value pair for variables in the template&gt;]]&lt;br /&gt;                [-variables  &lt;comma seperated list of name=value pairs&gt;]&lt;br /&gt;                [-initParams &lt;comma seperated list of name=value pairs&gt;]&lt;br /&gt;                [-memoryPercentage &lt;percentage of physical memory for Oracle&gt;]&lt;br /&gt;                [-automaticMemoryManagement ]&lt;br /&gt;                [-totalMemory &lt;memory allocated for Oracle in MB&gt;]&lt;br /&gt;                [-databaseType &lt;MULTIPURPOSE|DATA_WAREHOUSING|OLTP&gt;]]&lt;br /&gt;&lt;br /&gt;Configure a database by specifying the following parameters:&lt;br /&gt;        -configureDatabase&lt;br /&gt;                -sourceDB    &lt;local instance_name of source database&gt;&lt;br /&gt;                [-sysDBAUserName     &lt;user name  with SYSDBA privileges&gt;&lt;br /&gt;                 -sysDBAPassword     &lt;password for sysDBAUserName user name&gt;]&lt;br /&gt;                [-registerWithDirService|-unregisterWithDirService|-regenerateDBPassword &lt;true | false&gt;&lt;br /&gt;                        -dirServiceUserName    &lt;user name for directory service&gt;&lt;br /&gt;                        -dirServicePassword    &lt;password for directory service &gt;&lt;br /&gt;                        -walletPassword    &lt;password for database wallet &gt;]&lt;br /&gt;                [-disableSecurityConfiguration &lt;ALL|AUDIT|PASSWORD_PROFILE|NONE&gt;&lt;br /&gt;                [-enableSecurityConfiguration &lt;true|false&gt;&lt;br /&gt;                [-emConfiguration &lt;CENTRAL|LOCAL|ALL|NOBACKUP|NOEMAIL|NONE&gt;&lt;br /&gt;                        -dbsnmpPassword &lt;DBSNMP user password&gt;&lt;br /&gt;                        -symanPassword &lt;SYSMAN user password&gt;&lt;br /&gt;                        [-hostUserName &lt;Host user name for EM backup job&gt;&lt;br /&gt;                         -hostUserPassword &lt;Host user password for EM backup job&gt;&lt;br /&gt;                         -backupSchedule &lt;Daily backup schedule in the form of hh:mm&gt;]&lt;br /&gt;                        [-smtpServer &lt;Outgoing mail (SMTP) server for email notifications&gt;&lt;br /&gt;                         -emailAddress &lt;Email address for email notifications&gt;]&lt;br /&gt;                        [-centralAgent &lt;Enterprise Manager central agent home&gt;]]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Create a template from an existing database by specifying the following parameters:&lt;br /&gt;        -createTemplateFromDB&lt;br /&gt;                -sourceDB    &lt;service in the form of &lt;host&gt;:&lt;port&gt;:&lt;sid&gt;&gt;&lt;br /&gt;                -templateName      &lt;new template name&gt;&lt;br /&gt;                -sysDBAUserName     &lt;user name  with SYSDBA privileges&gt;&lt;br /&gt;                -sysDBAPassword     &lt;password for sysDBAUserName user name&gt;&lt;br /&gt;                [-maintainFileLocations &lt;true | false&gt;]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Create a clone template from an existing database by specifying the following parameters:&lt;br /&gt;        -createCloneTemplate&lt;br /&gt;                -sourceSID    &lt;local instance_name of source database&gt;&lt;br /&gt;                -templateName      &lt;new template name&gt;&lt;br /&gt;                [-sysDBAUserName     &lt;user name  with SYSDBA privileges&gt;&lt;br /&gt;                 -sysDBAPassword     &lt;password for sysDBAUserName user name&gt;]&lt;br /&gt;                [-maintainFileLocations &lt;true | false&gt;]&lt;br /&gt;                [-datafileJarLocation       &lt;directory to place the datafiles in a compressed format&gt;]&lt;br /&gt;&lt;br /&gt;Generate scripts to create database by specifying the following parameters:&lt;br /&gt;        -generateScripts&lt;br /&gt;                -templateName &lt;name of an existing  template&gt;&lt;br /&gt;                -gdbName &lt;global database name&gt;&lt;br /&gt;                [-scriptDest       &lt;destination for all the scriptfiles&gt;]&lt;br /&gt;&lt;br /&gt;Delete a database by specifying the following parameters:&lt;br /&gt;        -deleteDatabase&lt;br /&gt;                -sourceDB    &lt;source database global database name&gt;&lt;br /&gt;                [-sid    &lt;local instance_name of source database&gt;]&lt;br /&gt;                [-sysDBAUserName     &lt;user name  with SYSDBA privileges&gt;&lt;br /&gt;                 -sysDBAPassword     &lt;password for sysDBAUserName user name&gt;]&lt;br /&gt;&lt;br /&gt;Add an instance to a cluster database by specifying the following parameters:&lt;br /&gt;        -addInstance&lt;br /&gt;                -gdbName &lt;global database name&gt;&lt;br /&gt;                -nodelist &lt;node name for the new instance to add&gt;&lt;br /&gt;                [-instanceName &lt;instance name for the new instance to add&gt;]&lt;br /&gt;                [-sysDBAUserName     &lt;user name  with SYSDBA privileges&gt;]&lt;br /&gt;                 -sysDBAPassword     &lt;password for sysDBAUserName user name&gt;&lt;br /&gt;                [-updateDirService &lt;true | false&gt;&lt;br /&gt;                        -dirServiceUserName    &lt;user name for directory service&gt;&lt;br /&gt;                        -dirServicePassword    &lt;password for directory service &gt;]&lt;br /&gt;&lt;br /&gt;Delete an instance from a cluster database by specifying the following parameters:&lt;br /&gt;        -deleteInstance&lt;br /&gt;                -gdbName &lt;global database name&gt;&lt;br /&gt;                -instanceName &lt;instance name for the instance to be removed&gt;&lt;br /&gt;                [-nodelist &lt;node name for the instance to be removed&gt;]&lt;br /&gt;                [-sysDBAUserName     &lt;user name  with SYSDBA privileges&gt;]&lt;br /&gt;                 -sysDBAPassword     &lt;password for sysDBAUserName user name&gt;&lt;br /&gt;                [-updateDirService &lt;true | false&gt;&lt;br /&gt;                        -dirServiceUserName    &lt;user name for directory service&gt;&lt;br /&gt;                        -dirServicePassword    &lt;password for directory service &gt;]&lt;br /&gt;&lt;br /&gt;Query for help by specifying the following options: -h | -help&lt;br /&gt;&lt;/code&gt;</description><link>http://lhorikian.blogspot.com/2010/06/crear-una-base-de-datos-de-prueba-en.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-7640126126413582192</guid><pubDate>Wed, 17 Feb 2010 22:02:00 +0000</pubDate><atom:updated>2010-02-17T19:16:32.166-03:00</atom:updated><title>Simposio HOTSOS 2010</title><description>&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://www.hotsos.com/img/sym_logo.gif&quot;&gt;&lt;img style=&quot;cursor:pointer; cursor:hand;width: 201px; height: 111px;&quot; src=&quot;http://www.hotsos.com/img/sym_logo.gif&quot; border=&quot;0&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Todos los años, se realiza un evento llamado Simposio HOTSOS. Este evento esta exclusivamente dedicado a Oracle Performance Tuning.&lt;br /&gt;&lt;br /&gt;El Simposio se realiza todos los años en el mes de Marzo en la ciudad de Dallas, Texas (USA). Esta conferencia es única a nivel mundial! Reune a los mejores expertos de Oracle del mundo!&lt;br /&gt;&lt;br /&gt;El año pasado, tuve la oportunidad de poder viajar y presenciar el Simposio. Definitivamente se lo recomiendo a todas las personas que utilizan Oracle y que se especializan o que se encuentran interesadas en lo que respecta a Performance Tuning. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Este año (días 7 al 11 de Marzo) volveré a presenciar el Simposio! Nos vemos ahí!!!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Para más información: &lt;a href=&quot;http://www.hotsos.com/sym10.html&quot;&gt;AQUI&lt;/a&gt;</description><link>http://lhorikian.blogspot.com/2010/02/simposio-hotsos-2010.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>7</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-7698628241114973502</guid><pubDate>Thu, 22 Oct 2009 04:39:00 +0000</pubDate><atom:updated>2009-10-22T02:32:46.561-03:00</atom:updated><title>Necesitás ayuda sobre un tema en específico? Postealo AQUI !!!</title><description>&lt;span style=&quot;font-weight:bold;&quot;&gt;A pedido de mucha gente que visita el blog y que utiliza Oracle...&lt;br /&gt;&lt;br /&gt;Este post es para que ustedes puedan sugerir los temas que tienen dudas y que les gustaría que se les explicara con más detalle en éste blog. Los temas pueden estar relacionados con Performance Tuning, Administración, Desarrollo, etc.&lt;br /&gt;&lt;br /&gt;Los invito a todos a sugerir temas técnicos relacionados con Oracle que requieran ser explicados en detalle.&lt;br /&gt;&lt;br /&gt;Saludos a todos!&lt;/span&gt;</description><link>http://lhorikian.blogspot.com/2009/10/necesitas-ayuda-sobre-un-tema-en.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>77</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-5409580349480811478</guid><pubDate>Wed, 21 Oct 2009 02:16:00 +0000</pubDate><atom:updated>2009-10-21T00:01:51.688-03:00</atom:updated><title>Añadiendo columnas con valores por defecto y constraint NOT NULL en Oracle 11g</title><description>Antes de la versión 11g, cuando agregabamos una columna a una tabla con valores por defecto y una constraint del tipo NOT NULL, Oracle realizaba un loqueo exclusivo en la tabla para insertar los valores en cada uno de los registros existentes en la misma.&lt;br /&gt;&lt;br /&gt;A partir de la versión 11g release 1, ésta operación se optimizó para mejorar en gran medida la utilización de los recursos del sistema y el espacio de almacenamiento de los nuevos valores. Oracle logra ésta optimización, guardando el valor por defecto en el diccionario de datos en vez de modificar todos los registros de la tabla, haciendo la ejecución de ésta operacion de manera instantánea. Gracias a ésto, obtenemos un beneficio enorme a la hora de modificar tablas con millones de registros.&lt;br /&gt;&lt;br /&gt;Por otro lado, las siguientes operaciones ADD COLUMN ahora pueden ejecutarse de manera concurrente junto con las operaciones DML:&lt;br /&gt;- Agregar una columna NOT NULL con un valor por defecto.&lt;br /&gt;- Agregar una columna NULL sin un valor por defecto.&lt;br /&gt;- Agregar una columna virtual.&lt;br /&gt;&lt;br /&gt;Veamos un ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;- versión 9i release 2&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; CREATE TABLE test&lt;br /&gt;  2  NOLOGGING&lt;br /&gt;  3  AS&lt;br /&gt;  4  SELECT level id&lt;br /&gt;  5  FROM dual&lt;br /&gt;  6  CONNECT BY level &lt;= 1000000;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; ALTER TABLE test ADD (nombre VARCHAR2(100) &lt;span style=&quot;font-weight:bold;&quot;&gt;DEFAULT &#39;LEONARDO_HORIKIAN&#39; NOT NULL&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Elapsed: 00:01:17.62&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; DESC test&lt;br /&gt;&lt;br /&gt; Name                                      Null?    Type&lt;br /&gt; ----------------------------------------- -------- ----------------------------&lt;br /&gt; ID                                                 NUMBER&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt; NOMBRE                                    NOT NULL VARCHAR2(100)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; exec dbms_stats.gather_table_stats(USER, &#39;TEST&#39;);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; SELECT count(*)&lt;br /&gt;  2  FROM test&lt;br /&gt;  3  WHERE nombre &lt;span style=&quot;font-weight:bold;&quot;&gt;IS NULL&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;         0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;- versión 11g release 1&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_11gR1&gt; CREATE TABLE test&lt;br /&gt;  2  NOLOGGING&lt;br /&gt;  3  AS&lt;br /&gt;  4  SELECT level id&lt;br /&gt;  5  FROM dual&lt;br /&gt;  6  CONNECT BY level &lt;= 1000000;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL_11gR1&gt; ALTER TABLE test ADD (nombre VARCHAR2(100) &lt;span style=&quot;font-weight:bold;&quot;&gt;DEFAULT &#39;LEONARDO_HORIKIAN&#39; NOT NULL&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Elapsed: 00:00:00.17&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_11gR1&gt; DESC test&lt;br /&gt;&lt;br /&gt; Name                                      Null?    Type&lt;br /&gt; ----------------------------------------- -------- ----------------------------&lt;br /&gt; ID                                                 NUMBER&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt; NOMBRE                                    NOT NULL VARCHAR2(100)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_11gR1&gt; exec dbms_stats.gather_table_stats(USER, &#39;TEST&#39;);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL_11gR1&gt; SELECT count(*)&lt;br /&gt;  2  FROM test&lt;br /&gt;  3  WHERE nombre &lt;span style=&quot;font-weight:bold;&quot;&gt;IS NULL&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;         0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como podemos observar, creamos una tabla con 1 millón de registros y luego agregamos una columna con valores por defecto. En 9iR2, esa operación se ejecutó en 1 minuto 17 segundos; en cambio en 11gR1, se ejecutó de manera instantánea en tan solo 17 milisegundos!!!&lt;br /&gt;&lt;br /&gt;IMPORTANTE: Tengan en cuenta que existen bugs en 11g (leer nota Metalink 602327.1) relacionados con éste tipo de operación.</description><link>http://lhorikian.blogspot.com/2009/10/anadiendo-columnas-con-valores-por.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>5</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-6038572716708263726</guid><pubDate>Thu, 15 Oct 2009 15:18:00 +0000</pubDate><atom:updated>2009-11-11T08:34:48.544-03:00</atom:updated><title>TKPROF y el parámetro SYS=Y</title><description>Conozco muchas personas que al ejecutar la herramienta TKPROF, lo hacen con el parámetros SYS=N para que no se incluyan en el archivo de salida las consultas recursivas que realiza internamente la base de datos. Si bien en algunos casos se hace ésto para que el archivo de salida no contenga información en exceso, muchas veces cuando buscamos la causa de un determinado problema, si colocamos SYS=N lo único que estaremos logrando será &quot;ocultar&quot; el causante de ese problema.&lt;br /&gt;&lt;br /&gt;Hace unos días, investigando un problema de performance en un proceso de un cliente, ejecuté la herramienta TKPROF con el parámetro SYS=Y (que es la opción por defecto) y noté que había una consulta recursiva que estaba leyendo millones de bloques de datos!&lt;br /&gt;&lt;br /&gt;La consulta es la siguiente:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;select min(bitmapped)&lt;br /&gt;from&lt;br /&gt; ts$ where dflmaxext =:1 and bitand(flags,1024) =1024&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse     7915      0.08       0.07          0          0          0           0&lt;br /&gt;Execute   7915      0.17       0.14          0          0          0           0&lt;br /&gt;Fetch    15830      4.24       4.23          0    3245150          0        7915&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;total    31660      4.50       4.45          0    3245150          0        7915&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Misses in library cache during parse: 0&lt;br /&gt;Optimizer mode: CHOOSE&lt;br /&gt;Parsing user id: SYS   (recursive depth: 2)&lt;br /&gt;&lt;br /&gt;Rows     Row Source Operation&lt;br /&gt;-------  ---------------------------------------------------&lt;br /&gt;      1  SORT AGGREGATE (cr=410 pr=0 pw=0 time=691 us)&lt;br /&gt;      2   TABLE ACCESS FULL TS$ (cr=410 pr=0 pw=0 time=659 us)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Esta consulta es interna de la base de datos. Debido a que el proceso se ejecuta con el usuario APPS y debido a que a éste usuario durante su creación se lo asignó dentro de un grupo de tablespaces temporales (new feature en 10g) llamado TEMP (que contienen los tablespaces TEMP1 y TEMP2), Oracle necesita ejecutar ésta consulta para determinar cuál de los dos tablespaces es el mejor para realizar la operación de sort que está requiriendo el proceso. Como pueden ver, esta consulta es ALTAMENTE costosa y consume muchos recursos del sistema ya que la consulta lee 3,245,150 bloques (410 * 7915) y el proceso en total lee 4,452,813 bloques! Por lo cual, ésta consulta está leyendo más del 70% del total de todos los bloques de datos del proceso!!!&lt;br /&gt;&lt;br /&gt;Para solucionar éste problema, lo que hice fue modificar el usuario APPS, sacarlo del  grupo de tablespaces temporales TEMP y asignarle directamente el tablespace TEMP2 que tiene un tamaño de 39 GB a comparación del TEMP1 que tiene sólo 6 GB. Al hacer éste cambio, lo que logramos fue que Oracle deje de ejecutar esa consulta debido a que ya no tiene necesidad de determinar cual de los dos tablespaces es el mejor para ejecutar una determinada operación de sort.&lt;br /&gt;&lt;br /&gt;Al ejecutar nuevamente el proceso, podemos ver el resultado de la solución implementada:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;select min(bitmapped)&lt;br /&gt;from&lt;br /&gt; ts$ where dflmaxext =:1 and bitand(flags,1024) =1024&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.00          0          0          0           0&lt;br /&gt;Execute      1      0.00       0.00          0          0          0           0&lt;br /&gt;Fetch        2      0.00       0.00          0        410          0           1&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;total        4      0.00       0.00          0        410          0           1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Misses in library cache during parse: 0&lt;br /&gt;Optimizer mode: CHOOSE&lt;br /&gt;Parsing user id: SYS   (recursive depth: 3)&lt;br /&gt;&lt;br /&gt;Rows     Row Source Operation&lt;br /&gt;-------  ---------------------------------------------------&lt;br /&gt;      1  SORT AGGREGATE (cr=410 pr=0 pw=0 time=3045 us)&lt;br /&gt;      2   TABLE ACCESS FULL TS$ (cr=410 pr=0 pw=0 time=2952 us)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Luego que se realizó éste cambio, se notó una mejora muy importante en cuanto a la performance y se notó una disminución drástica de la utilización de los recursos del sistema.&lt;br /&gt;&lt;br /&gt;Tengan siempre presente la importancia del parámetro SYS=Y al momento de ejecutar la herramienta TKPROF.&lt;br /&gt;&lt;br /&gt;Por cierto, esta consulta es un Bug! Hay que aplicar el Parche 5455880 para solucionar éste grave problema de performance.</description><link>http://lhorikian.blogspot.com/2009/10/tkprof-y-el-parametro-sysy.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-48743975517302275</guid><pubDate>Thu, 03 Sep 2009 02:28:00 +0000</pubDate><atom:updated>2010-07-08T01:26:58.415-03:00</atom:updated><title>Oracle OpenWorld</title><description>&lt;a href=&quot;http://www.oracle.com/us/openworld&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://www.oracle.com/ocom/groups/public/documents/digitalasset/register_banner.gif&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 250px; height: 151px;&quot; src=&quot;http://www.oracle.com/ocom/groups/public/documents/digitalasset/register_banner.gif&quot; border=&quot;0&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;</description><link>http://lhorikian.blogspot.com/2009/09/oracle-openworld-2009.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-9013133876867972161</guid><pubDate>Mon, 06 Jul 2009 19:18:00 +0000</pubDate><atom:updated>2009-07-13T16:16:24.161-03:00</atom:updated><title>adiós a los RAW-devices en 12g...</title><description>Según la nota de Metalink 578455.1 en 12g NO se seguirán soportando RAW-devices para los archivos físicos de la base de datos (data files, redo logs, control files), OCR (Oracle Cluster Registry) o discos Voting.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;IMPORTANTE!!! Este anuncio NO afecta a ASM.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En 12g, ya NO será posible ejecutar la siguiente sentencia porque intenta utilizar RAW-devices directamente...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create tablespace ABC DATAFILE &#39;/dev/raw/ABC1.dbf&#39; size 2GB;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Lo que tendremos que hacer, es ejecutar alguna de las siguientes sentencias para crear un diskgroup...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; alter diskgroup MYDG add disk &#39;/dev/raw/ABC1.dbf&#39;;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;o&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create diskgroup MYDB EXTERNAL REDUNDANCY disk &#39;dev/raw/ABC1.dbf&#39;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Luego de crear el diskgroup, podemos ejecutar la sentencia que acostumbramos...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create tablespace ABC DATAFILE &#39;+MYDG&#39; size 2GB;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Para los que tengan que migrar desde RAW-devices, las opciones existentes incluyen ASM (Automatic Storage Management), OCFS (Oracle Cluster File System) y otros sistemas de archivos clúster.&lt;br /&gt;&lt;br /&gt;Muchos estarán leyendo ésta nota y diciendo... OUCHHHH!!!!!! Y bueno... tendremos que irnos aconstumbrando a ésta clase de cambios ya que éste es sólo uno de los grandes cambios que veremos en 12g...</description><link>http://lhorikian.blogspot.com/2009/07/adios-los-volumenes-raw-en-12g.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-3142161754434203279</guid><pubDate>Thu, 25 Jun 2009 13:03:00 +0000</pubDate><atom:updated>2009-06-25T12:02:27.607-03:00</atom:updated><title>Stress Test</title><description>El &quot;Stress Test&quot; es una prueba que mide el comportamiento de nuestro sistema bajo una cierta demanda concurrente de conexiones. Esta es una de las pruebas claves que se DEBEN realizar durante el ciclo de vida del software para garantizar que nuestro sistema va a cumplir con las expectativas previstas cuando sea implementado en producción.&lt;br /&gt;&lt;br /&gt;Quiero mostrarles una simple prueba de Stress que hice para que vean qué tan importante es realizarla y qué tan mortal puede ser evitarla.&lt;br /&gt;&lt;br /&gt;Ejemplo:&lt;br /&gt;&lt;br /&gt;Vamos a crear una tabla llamada TEST que vamos a utilizar en el Stress para cargar datos.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; CREATE TABLE test (id NUMBER);&lt;br /&gt;&lt;br /&gt;Tabla creada.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Bien, yo tengo creados unos Shell Scripts propios para realizar el Stress Test. Los más importantes son los siguientes:&lt;br /&gt; - iniciar_stress.sh --&gt; Iniciamos el Stress Test.&lt;br /&gt; - reporte.sh --&gt; Genera el reporte con el detalle sobre el Stress Test.&lt;br /&gt;&lt;br /&gt;Lo que vamos a realizar es un Stress Test simulando una concurrencia de 100 conexiones simultáneas ejecutando el siguiente bloque PL/SQL anónimo:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;BEGIN&lt;br /&gt;  FOR x IN 1 .. 100 LOOP&lt;br /&gt;    INSERT INTO test VALUES (x);&lt;br /&gt;  END LOOP;&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como pueden observar, el INSERT que vamos a realizar es muy sencillo y debería ejecutarse rápido, cierto? Veamos cuánto demora si lo ejecuto sin concurrencia:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; BEGIN&lt;br /&gt;  2    FOR x IN 1 .. 100 LOOP&lt;br /&gt;  3      INSERT INTO test VALUES (x);&lt;br /&gt;  4    END LOOP;&lt;br /&gt;  5  END;&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;Procedimiento PL/SQL terminado correctamente.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Transcurrido: 00:00:01.25&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Bien, vemos que demoró 1.25 segundos. El bloque se ejecutó bastante rápido y esperamos que se comporte de la misma manera cuando realicemos el Stress Test... asi que hagamos la prueba para comprobarlo!&lt;br /&gt;&lt;br /&gt;Para comenzar el Stress Test, vamos a ejecutar el Shell Script llamado iniciar_stress.sh&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ ./iniciar_stress.sh&lt;br /&gt;&lt;br /&gt;=================================================================&lt;br /&gt;====================== STRESS TEST ==============================&lt;br /&gt;=================================================================&lt;br /&gt;&lt;br /&gt;Autor: Leonardo Horikian - Oracle Argentina&lt;br /&gt;&lt;br /&gt;Descripción: Programa que inicia un StressTest&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Apretar ENTER para continuar...&lt;br /&gt;&lt;br /&gt;=================================================================&lt;br /&gt;&lt;br /&gt;Ingresar la password del usuario STRESS_TEST:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;TNS alias (para local dejar en blanco):&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Esta instancia se encuentra configurada para soportar 769 conexiones concurrentes!&lt;br /&gt;Cuántas conexiones concurrentes querés ejecutar [1-769]?&lt;br /&gt;&lt;br /&gt;100&lt;br /&gt;&lt;br /&gt;=================================================================&lt;br /&gt;&lt;br /&gt;StressTest listo para ser ejecutado.&lt;br /&gt;&lt;br /&gt;Para comenzar, apretar ENTER&lt;br /&gt;&lt;br /&gt;=================================================================&lt;br /&gt;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;nohup: appending output to `nohup.out&#39;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como pueden ver, se iniciaron 100 conexiones a la base de datos. Las mismas se ejecutan con el comando nohup realizando la conexión a la instancia vía SQL*PLUS.&lt;br /&gt;Ahora vamos a ejecutar el Shell Script llamado reporte.sh para generar el reporte con la información resultante de la prueba.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ ./reporte.sh&lt;br /&gt;&lt;br /&gt;=================================================================&lt;br /&gt;====================== STRESS TEST ==============================&lt;br /&gt;=================================================================&lt;br /&gt;&lt;br /&gt;Autor: Leonardo Horikian - Oracle Argentina&lt;br /&gt;&lt;br /&gt;Descripción: Programa que genera el reporte con la información&lt;br /&gt;    sobre la ejecución del StressTest&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;Apretar ENTER para continuar...&lt;br /&gt;&lt;br /&gt;=================================================================&lt;br /&gt;&lt;br /&gt;Ingresar la password del usuario STRESS_TEST:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;TNS alias (para local dejar en blanco):&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;NOTA: Este script generará un archivo con la información del reporte en el directorio /reportes/.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ingresar el nombre del archivo de salida:&lt;br /&gt;&lt;br /&gt;stress_test.txt&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Elegir el TEST_ID con el cual se quiere generar el reporte...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;   TEST_ID TEST_DATE                      COMMENTS&lt;br /&gt;********** ****************************** ****************************************&lt;br /&gt;        37 06/25/2009 08:46               100 conexiones concurrentes.&lt;br /&gt;&lt;br /&gt;Ingresá el TEST_ID: 37&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;El archivo fue generado satisfactoriamente!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Abrimos el reporte y vemos lo siguiente...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------- INFORMACION SOBRE EL STRESS_TEST -------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;ID: 37 - FECHA: 06/25/2009 08:46 - COMENTARIOS: 100 conexiones concurrentes.&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------- RESULTADO INDIVIDUAL DEL STRESS_TEST (para cada una de las sesiones) -------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;SID: 248 - SPID: 11065&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;TOTAL_TIME_IN_SEC: 1.51&lt;/span&gt; - TOTAL_TIME_IN_MIN: .03 - TOTAL_TIME_IN_HO: 0&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;- EVENTOS ---------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;EVENT: buffer busy waits                        --&gt; WAITS: 47  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 1.3&lt;br /&gt;EVENT: library cache pin                        --&gt; WAITS: 4   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .52&lt;br /&gt;EVENT: enqueue                                  --&gt; WAITS: 8   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .31&lt;br /&gt;EVENT: latch free                               --&gt; WAITS: 6   - TIMEOUTS: 2   - TIME_WAITED_IN_SEC: .03&lt;br /&gt;EVENT: row cache lock                           --&gt; WAITS: 11  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .03&lt;br /&gt;EVENT: SQL*Net message from client              --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .01&lt;br /&gt;EVENT: control file sequential read             --&gt; WAITS: 4   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;EVENT: SQL*Net message to client                --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;EVENT: db file sequential read                  --&gt; WAITS: 4   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;SID: 808 - SPID: 11143&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;TOTAL_TIME_IN_SEC: 1.5&lt;/span&gt; - TOTAL_TIME_IN_MIN: .03 - TOTAL_TIME_IN_HO: 0&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;- EVENTOS ---------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;EVENT: buffer busy waits                        --&gt; WAITS: 47  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 1.3&lt;br /&gt;EVENT: enqueue                                  --&gt; WAITS: 7   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .3 &lt;br /&gt;EVENT: library cache pin                        --&gt; WAITS: 3   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .16&lt;br /&gt;EVENT: latch free                               --&gt; WAITS: 1   - TIMEOUTS: 1   - TIME_WAITED_IN_SEC: .01&lt;br /&gt;EVENT: SQL*Net message from client              --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .01&lt;br /&gt;EVENT: control file sequential read             --&gt; WAITS: 4   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;EVENT: SQL*Net message to client                --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;SID: 714 - SPID: 11116&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;TOTAL_TIME_IN_SEC: 1.48&lt;/span&gt; - TOTAL_TIME_IN_MIN: .02 - TOTAL_TIME_IN_HO: 0&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;- EVENTOS ---------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;EVENT: buffer busy waits                        --&gt; WAITS: 35  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 1.3&lt;br /&gt;EVENT: library cache pin                        --&gt; WAITS: 3   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .31&lt;br /&gt;EVENT: enqueue                                  --&gt; WAITS: 2   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .24&lt;br /&gt;EVENT: SQL*Net message from client              --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .01&lt;br /&gt;EVENT: latch free                               --&gt; WAITS: 2   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;EVENT: control file sequential read             --&gt; WAITS: 4   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;EVENT: SQL*Net message to client                --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;SID: 204 - SPID: 11110&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;TOTAL_TIME_IN_SEC: 1.48&lt;/span&gt; - TOTAL_TIME_IN_MIN: .02 - TOTAL_TIME_IN_HO: 0&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;- EVENTOS ---------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;EVENT: buffer busy waits                        --&gt; WAITS: 38  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 1.2&lt;br /&gt;EVENT: library cache pin                        --&gt; WAITS: 3   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .36&lt;br /&gt;EVENT: enqueue                                  --&gt; WAITS: 4   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .3 &lt;br /&gt;EVENT: db file sequential read                  --&gt; WAITS: 4   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .09&lt;br /&gt;EVENT: row cache lock                           --&gt; WAITS: 7   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .03&lt;br /&gt;EVENT: SQL*Net message from client              --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .02&lt;br /&gt;EVENT: latch free                               --&gt; WAITS: 2   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;EVENT: SQL*Net message to client                --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;EVENT: control file sequential read             --&gt; WAITS: 4   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;...&lt;br /&gt;...&lt;br /&gt;CONTINUA ...&lt;br /&gt;...&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://1.bp.blogspot.com/_M2xLBs8np6Q/SkORIOMf8rI/AAAAAAAAAB4/zzZc6scw0FI/s1600-h/stress_test_100.jpg&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 159px;&quot; src=&quot;http://1.bp.blogspot.com/_M2xLBs8np6Q/SkORIOMf8rI/AAAAAAAAAB4/zzZc6scw0FI/s320/stress_test_100.jpg&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5351280352681783986&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Como podemos observar, el comportamiento para cada sesión fue el mismo que cuando ejecutamos la prueba sin concurrencia. El bloque se ejecutó en un promedio de 1.5 segundos por sesión.&lt;br /&gt;&lt;br /&gt;Qué sucede si repetimos la prueba con 300 usuarios concurrentes? El resultado seguirá siendo el mismo? Veamos el reporte de la ejecución...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------- INFORMACION SOBRE EL STRESS_TEST -------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;ID: 38 - FECHA: 06/25/2009 09:07 - COMENTARIOS: 300 conexiones concurrentes.&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------- RESULTADO INDIVIDUAL DEL STRESS_TEST (para cada una de las sesiones) -------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;SID: 607 - SPID: 15310&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;TOTAL_TIME_IN_SEC: 5.47&lt;/span&gt; - TOTAL_TIME_IN_MIN: .09 - TOTAL_TIME_IN_HO: 0&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;- EVENTOS ---------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;EVENT: enqueue                                  --&gt; WAITS: 12  - TIMEOUTS: 1   - TIME_WAITED_IN_SEC: 4.8&lt;br /&gt;EVENT: row cache lock                           --&gt; WAITS: 12  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 2.9&lt;br /&gt;EVENT: latch free                               --&gt; WAITS: 15  - TIMEOUTS: 1   - TIME_WAITED_IN_SEC: 1.2&lt;br /&gt;EVENT: buffer busy waits                        --&gt; WAITS: 11  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .77&lt;br /&gt;EVENT: SQL*Net message from client              --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .06&lt;br /&gt;EVENT: db file sequential read                  --&gt; WAITS: 3   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .04&lt;br /&gt;EVENT: log file sync                            --&gt; WAITS: 1   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .03&lt;br /&gt;EVENT: control file sequential read             --&gt; WAITS: 4   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;EVENT: SQL*Net message to client                --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;SID: 286 - SPID: 15362&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;TOTAL_TIME_IN_SEC: 5.19&lt;/span&gt; - TOTAL_TIME_IN_MIN: .09 - TOTAL_TIME_IN_HO: 0&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;- EVENTOS ---------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;EVENT: enqueue                                  --&gt; WAITS: 13  - TIMEOUTS: 1   - TIME_WAITED_IN_SEC: 4.9&lt;br /&gt;EVENT: row cache lock                           --&gt; WAITS: 11  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 2.9&lt;br /&gt;EVENT: latch free                               --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 1.1&lt;br /&gt;EVENT: buffer busy waits                        --&gt; WAITS: 5   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .45&lt;br /&gt;EVENT: SQL*Net message from client              --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .12&lt;br /&gt;EVENT: db file sequential read                  --&gt; WAITS: 3   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .02&lt;br /&gt;EVENT: control file sequential read             --&gt; WAITS: 4   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;EVENT: SQL*Net message to client                --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;SID: 435 - SPID: 15329&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;TOTAL_TIME_IN_SEC: 4.97&lt;/span&gt; - TOTAL_TIME_IN_MIN: .08 - TOTAL_TIME_IN_HO: 0&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;- EVENTOS ---------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;EVENT: enqueue                                  --&gt; WAITS: 9   - TIMEOUTS: 1   - TIME_WAITED_IN_SEC: 3.5&lt;br /&gt;EVENT: row cache lock                           --&gt; WAITS: 8   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 2.7&lt;br /&gt;EVENT: latch free                               --&gt; WAITS: 37  - TIMEOUTS: 1   - TIME_WAITED_IN_SEC: 2.5&lt;br /&gt;EVENT: buffer busy waits                        --&gt; WAITS: 7   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .58&lt;br /&gt;EVENT: db file sequential read                  --&gt; WAITS: 17  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .18&lt;br /&gt;EVENT: SQL*Net message from client              --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .07&lt;br /&gt;EVENT: library cache pin                        --&gt; WAITS: 1   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .01&lt;br /&gt;EVENT: control file sequential read             --&gt; WAITS: 4   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;EVENT: SQL*Net message to client                --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;SID: 202 - SPID: 15746&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;TOTAL_TIME_IN_SEC: 4.63&lt;/span&gt; - TOTAL_TIME_IN_MIN: .08 - TOTAL_TIME_IN_HO: 0&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;- EVENTOS ---------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;EVENT: enqueue                                  --&gt; WAITS: 76  - TIMEOUTS: 1   - TIME_WAITED_IN_SEC: 5.5&lt;br /&gt;EVENT: row cache lock                           --&gt; WAITS: 181 - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 2.1&lt;br /&gt;EVENT: latch free                               --&gt; WAITS: 47  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 2.0&lt;br /&gt;EVENT: buffer busy waits                        --&gt; WAITS: 5   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .16&lt;br /&gt;EVENT: SQL*Net message from client              --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .07&lt;br /&gt;EVENT: wait list latch free                     --&gt; WAITS: 1   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: .01&lt;br /&gt;EVENT: control file sequential read             --&gt; WAITS: 4   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;EVENT: SQL*Net message to client                --&gt; WAITS: 19  - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;EVENT: db file sequential read                  --&gt; WAITS: 2   - TIMEOUTS: 0   - TIME_WAITED_IN_SEC: 0  &lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;...&lt;br /&gt;...&lt;br /&gt;CONTINUA ...&lt;br /&gt;...&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://4.bp.blogspot.com/_M2xLBs8np6Q/SkORdV3xXpI/AAAAAAAAACA/vZATfGLsVYU/s1600-h/stress_test_300.jpg&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 159px;&quot; src=&quot;http://4.bp.blogspot.com/_M2xLBs8np6Q/SkORdV3xXpI/AAAAAAAAACA/vZATfGLsVYU/s320/stress_test_300.jpg&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5351280715519581842&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Como observamos, cuando incrementamos la cantidad de conexiones concurrentes, aumentó casi un 400 porciento el tiempo de ejecución de cada conexión!!! Este es un ejemplo muy claro del porqué es TAN importante hacer ésta clase de pruebas de Stress para evitar problemas futuros cuando pasemos nuestro sistema a producción!</description><link>http://lhorikian.blogspot.com/2009/06/stress-test.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_M2xLBs8np6Q/SkORIOMf8rI/AAAAAAAAAB4/zzZc6scw0FI/s72-c/stress_test_100.jpg" height="72" width="72"/><thr:total>18</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-503331525881455237</guid><pubDate>Tue, 19 May 2009 20:20:00 +0000</pubDate><atom:updated>2009-05-19T18:09:48.524-03:00</atom:updated><title>Costo super alto!</title><description>En Febrero del 2008, publiqué un post llamado &quot;&lt;a href=&quot;http://lhorikian.blogspot.com/2008/02/tunear-en-base-al-costo-del-plan-de.html&quot;&gt;¿Tunear en base al COSTO del plan de ejecución?&lt;/a&gt;&quot;. En ese post, les decía que NO hay que tunear en base al costo del plan de ejecución, sino al trabajo que realiza la consulta en la base de datos.&lt;br /&gt;&lt;br /&gt;Voy a mostrarles una consulta en el que el costo es super alto pero el trabajo que realiza en la base de datos no lo es:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; explain plan for&lt;br /&gt;2  SELECT (t5.column_value).getstringval() t5&lt;br /&gt;3  FROM TABLE(xmlsequence(extract(xmltype(&#39;&lt;x&gt;&#39;),&#39;/x&#39;))) t1,&lt;br /&gt;4       TABLE(xmlsequence(t1.column_value))t2,&lt;br /&gt;5       TABLE(xmlsequence(t2.column_value))t3,&lt;br /&gt;6       TABLE(xmlsequence(t3.column_value))t4,&lt;br /&gt;7       TABLE(xmlsequence(t4.column_value))t5;&lt;br /&gt;&lt;br /&gt;Explicado.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT * FROM table(dbms_xplan.display(&#39;plan_table&#39;,null,&#39;ALL&#39;));&lt;br /&gt;&lt;br /&gt;PLAN_TABLE_OUTPUT&lt;br /&gt;-------------------------------------------------------------------------------------------------------------------&lt;br /&gt;Plan hash value: 4104774429&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                             | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;----------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;|   0 | SELECT STATEMENT                      |                        |    18E|    15E|    98P  (2)|999:59:59 |&lt;/span&gt;&lt;br /&gt;|   1 |  NESTED LOOPS                         |                        |    18E|    15E|    98P  (2)|999:59:59 |&lt;br /&gt;|   2 |   NESTED LOOPS                        |                        |  4451T|    31P|    12T  (2)|999:59:59 |&lt;br /&gt;|   3 |    NESTED LOOPS                       |                        |   544G|  3045G|  1481M  (2)|999:59:59 |&lt;br /&gt;|   4 |     NESTED LOOPS                      |                        |    66M|   254M|   181K  (2)| 00:36:18 |&lt;br /&gt;|   5 |      COLLECTION ITERATOR PICKLER FETCH| XMLSEQUENCEFROMXMLTYPE |       |       |            |          |&lt;br /&gt;|   6 |      COLLECTION ITERATOR PICKLER FETCH| XMLSEQUENCEFROMXMLTYPE |       |       |            |          |&lt;br /&gt;|   7 |     COLLECTION ITERATOR PICKLER FETCH | XMLSEQUENCEFROMXMLTYPE |       |       |            |          |&lt;br /&gt;|   8 |    COLLECTION ITERATOR PICKLER FETCH  | XMLSEQUENCEFROMXMLTYPE |       |       |            |          |&lt;br /&gt;|   9 |   COLLECTION ITERATOR PICKLER FETCH   | XMLSEQUENCEFROMXMLTYPE |       |       |            |          |&lt;br /&gt;----------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Query Block Name / Object Alias (identified by operation id):&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;1 - SEL$E270DE78&lt;br /&gt;&lt;br /&gt;Column Projection Information (identified by operation id):&lt;br /&gt;-----------------------------------------------------------&lt;br /&gt;1 - (#keys=0) VALUE(A0)[40], VALUE(A0)[40], VALUE(A0)[40], VALUE(A0)[40], VALUE(A0)[40]&lt;br /&gt;2 - (#keys=0) VALUE(A0)[40], VALUE(A0)[40], VALUE(A0)[40], VALUE(A0)[40]&lt;br /&gt;3 - (#keys=0) VALUE(A0)[40], VALUE(A0)[40], VALUE(A0)[40]&lt;br /&gt;4 - (#keys=0) VALUE(A0)[40], VALUE(A0)[40]&lt;br /&gt;5 - VALUE(A0)[40]&lt;br /&gt;6 - VALUE(A0)[40]&lt;br /&gt;7 - VALUE(A0)[40]&lt;br /&gt;8 - VALUE(A0)[40]&lt;br /&gt;9 - VALUE(A0)[40]&lt;br /&gt;&lt;/x&gt;&lt;/pre&gt;&lt;br /&gt;Wow!!! Y esos números??? Extremadamente altos no??? Bueno, según el plan de ejecución, esa consulta retorna 18 cuatrillones (18E) de registros, 15 exabytes (15E), tiene un costo de 1.8E19 (98P) y el tiempo de ejecución es de aproximadamente 1 mes (999:59:59)!!!&lt;br /&gt;&lt;br /&gt;Veamos qué sucede cuando ejecutamos la consulta y obtenemos las estadísticas de la misma:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT (t5.column_value).getstringval() t5&lt;br /&gt;2  FROM TABLE(xmlsequence(extract(xmltype(&#39;&lt;x&gt;&#39;),&#39;/x&#39;))) t1,&lt;br /&gt;3       TABLE(xmlsequence(t1.column_value))t2,&lt;br /&gt;4       TABLE(xmlsequence(t2.column_value))t3,&lt;br /&gt;5       TABLE(xmlsequence(t3.column_value))t4,&lt;br /&gt;6       TABLE(xmlsequence(t4.column_value))t5;&lt;br /&gt;&lt;br /&gt;T5&lt;br /&gt;----------------------------------------------------------------&lt;br /&gt;&lt; x/ &gt;&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Transcurrido: 00:00:00.04&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Estadísticas&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;    0  recursive calls&lt;br /&gt;    0  db block gets&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;     3  consistent gets&lt;/span&gt;&lt;br /&gt;    0  physical reads&lt;br /&gt;    0  redo size&lt;br /&gt;  425  bytes sent via SQL*Net to client&lt;br /&gt;  381  bytes received via SQL*Net from client&lt;br /&gt;    2  SQL*Net roundtrips to/from client&lt;br /&gt;    0  sorts (memory)&lt;br /&gt;    0  sorts (disk)&lt;br /&gt;    1  rows processed&lt;br /&gt;&lt;/x&gt;&lt;/pre&gt;&lt;br /&gt;Como podemos observar, la consulta retorna un sólo registro y se ejecuta en tan solo 4 milisegundos, consumiendo sólo 3 bloques de datos.&lt;br /&gt;&lt;br /&gt;Este ejemplo es otra prueba de cómo el optimizador puede calcular valores totalmente erróneos en el plan de ejecución. Es por eso, que hay que tener &lt;span style=&quot;font-style: italic;&quot;&gt;especial cuidado&lt;/span&gt; al ver esos números cuando obtenemos un plan de ejecución y al tratar de optimizar la consulta en base a esos números.&lt;br /&gt;&lt;br /&gt;Para ésta consulta en particular, el problema es que el optimizador desconoce las estadísticas de las tablas (que en éste caso no son tablas, son llamadas a funciones). Fácilmente, podemos &quot;mejorar&quot; el plan de ejecución, agregando el hint CARDINALITY para decirle al optimizador cuántos registros va a retornar esa función... que en éste caso, es un sólo registro:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; explain plan for&lt;br /&gt;2  SELECT /*+ &lt;span style=&quot;font-weight: bold;&quot;&gt;cardinality(t1 1) cardinality(t2 1) cardinality(t3 1) cardinality(t4 1) cardinality(t5 1)&lt;/span&gt; */&lt;br /&gt;3        (t5.column_value).getstringval() t5&lt;br /&gt;4  FROM TABLE(xmlsequence(extract(xmltype(&#39;&lt;x&gt;&#39;),&#39;/x&#39;))) t1,&lt;br /&gt;5       TABLE(xmlsequence(t1.column_value))t2,&lt;br /&gt;6       TABLE(xmlsequence(t2.column_value))t3,&lt;br /&gt;7       TABLE(xmlsequence(t3.column_value))t4,&lt;br /&gt;8       TABLE(xmlsequence(t4.column_value))t5;&lt;br /&gt;&lt;br /&gt;Explicado.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT * FROM table(dbms_xplan.display(&#39;plan_table&#39;,null,&#39;ALL&#39;));&lt;br /&gt;&lt;br /&gt;PLAN_TABLE_OUTPUT&lt;br /&gt;------------------------------------------------------------------------------------------------------------------&lt;br /&gt;Plan hash value: 4104774429&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                             | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;----------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;|   0 | SELECT STATEMENT                      |                        |     1 |    10 |   122   (2)| 00:00:02 |&lt;/span&gt;&lt;br /&gt;|   1 |  NESTED LOOPS                         |                        |     1 |    10 |   122   (2)| 00:00:02 |&lt;br /&gt;|   2 |   NESTED LOOPS                        |                        |     1 |     8 |    97   (2)| 00:00:02 |&lt;br /&gt;|   3 |    NESTED LOOPS                       |                        |     1 |     6 |    73   (2)| 00:00:01 |&lt;br /&gt;|   4 |     NESTED LOOPS                      |                        |     1 |     4 |    49   (3)| 00:00:01 |&lt;br /&gt;|   5 |      COLLECTION ITERATOR PICKLER FETCH| XMLSEQUENCEFROMXMLTYPE |       |       |            |          |&lt;br /&gt;|   6 |      COLLECTION ITERATOR PICKLER FETCH| XMLSEQUENCEFROMXMLTYPE |       |       |            |          |&lt;br /&gt;|   7 |     COLLECTION ITERATOR PICKLER FETCH | XMLSEQUENCEFROMXMLTYPE |       |       |            |          |&lt;br /&gt;|   8 |    COLLECTION ITERATOR PICKLER FETCH  | XMLSEQUENCEFROMXMLTYPE |       |       |            |          |&lt;br /&gt;|   9 |   COLLECTION ITERATOR PICKLER FETCH   | XMLSEQUENCEFROMXMLTYPE |       |       |            |          |&lt;br /&gt;----------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Query Block Name / Object Alias (identified by operation id):&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;1 - SEL$E270DE78&lt;br /&gt;&lt;br /&gt;Column Projection Information (identified by operation id):&lt;br /&gt;-----------------------------------------------------------&lt;br /&gt;1 - (#keys=0) VALUE(A0)[40], VALUE(A0)[40], VALUE(A0)[40], VALUE(A0)[40], VALUE(A0)[40]&lt;br /&gt;2 - (#keys=0) VALUE(A0)[40], VALUE(A0)[40], VALUE(A0)[40], VALUE(A0)[40]&lt;br /&gt;3 - (#keys=0) VALUE(A0)[40], VALUE(A0)[40], VALUE(A0)[40]&lt;br /&gt;4 - (#keys=0) VALUE(A0)[40], VALUE(A0)[40]&lt;br /&gt;5 - VALUE(A0)[40]&lt;br /&gt;6 - VALUE(A0)[40]&lt;br /&gt;7 - VALUE(A0)[40]&lt;br /&gt;8 - VALUE(A0)[40]&lt;br /&gt;9 - VALUE(A0)[40]&lt;br /&gt;&lt;/x&gt;&lt;/pre&gt;&lt;br /&gt;Recuerden siempre el acrónimo GIGO: Garbage In ... Garbage Out   :)</description><link>http://lhorikian.blogspot.com/2009/05/costo-super-alto.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-2435431516148546481</guid><pubDate>Wed, 13 May 2009 16:58:00 +0000</pubDate><atom:updated>2009-05-13T16:04:04.388-03:00</atom:updated><title>DML Error Logging</title><description>Alguna vez trataron de actualizar 10 millones de registros? Que sucedía cuando uno de los registros fallaba? Oracle realizaba un rollback automático de los cambios... y qué sucede si el registro que falló era uno de los últimos en actualizarse? Y bueno... seguramente desperdiciamos todos el tiempo de ejecución de ese proceso ya que los cambios se perdieron tan solo porque un registro falló! A muchos les habrá pasado lo mismo al ejecutar una sentencia del tipo INSERT AS SELECT cierto? En donde uno de los registros no pudo insertarse por X motivo y por consecuencia toda la sentencia falló!&lt;br /&gt;&lt;br /&gt;Generalmente, sabemos que la manera más rápida de realizar un DML es en una sola sentencia. Cuando tenemos una sentencia en donde pueden haber registros que terminen en error, se solía armar un procedimiento que recorra los datos que se quieren insertar, updatear, eliminar, etc. e ir ejecutando la senetencia de a un/o varios registros a la vez, y los registros que fallaban, se insertaban en una tabla de errores. Este procedimiento suele ser muy lento, ya que no estamos realizando un DML en una sola sentencia y en una sola vez para todos los registros; sino que estamos recorriendo los datos proceduralmente y realizando el DML de a X cantidad de registros a la vez con el fin de loguear los registros que terminaron en error e ir comiteando, a medida que se va ejecutando la sentencia DML, los registros que terminan satisfactoriamente.&lt;br /&gt;&lt;br /&gt;En Oracle 10g Release 2, existe una nueva funcionalidad llamada &quot;DML Error Logging&quot; que nos permite ejecutar una sentencia DML de &quot;principio a fin&quot;... y si en el transcurso de ejecución de esa sentencia, uno o más registros fallaran, esos registros se loguean en una tabla de errores para que luego podramos corregirlos, sin necesidad, de volver a insertar todos los registros nuevamente ya que sólo tendremos que volver a insertar los que terminaron en error.&lt;br /&gt;Por ejemplo: Si intentamos insertar 1 millón de registros, y sólo un registro falla, se insertarán en la tabla final 999,999 de registros, y el registro que terminó en error, se loguea en otra tabla para que luego podamos corregirlo y volverlo a insertar. Acaso no es sensacional ésto???&lt;br /&gt;&lt;br /&gt;Veamos un ejemplo muy simple:&lt;br /&gt;&lt;br /&gt;Creamos una tabla llamada TEST, en donde vamos a insertar 1 millón de registros.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE TABLE test&lt;br /&gt;2  (&lt;br /&gt;3  ID     NUMBER,&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;4  NOMBRE VARCHAR2(7)&lt;/span&gt;&lt;br /&gt;5  );&lt;br /&gt;&lt;br /&gt;Tabla creada.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Creamos una tabla llamada ERROR_LOG_TEST que apunta a la tabla TEST. Esta tabla va a contener todos los registros que terminen en error cuando queramos realizar un DML en la tabla TEST.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; EXEC dbms_errlog.create_error_log(&#39;TEST&#39;,&#39;&lt;span style=&quot;font-weight: bold;&quot;&gt;ERROR_LOG_TEST&lt;/span&gt;&#39;) ;&lt;br /&gt;&lt;br /&gt;Procedimiento PL/SQL terminado correctamente.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; DESC error_log_test&lt;br /&gt;&lt;br /&gt;Nombre                                                Nulo?    Tipo&lt;br /&gt;----------------------------------------------------- -------- ------------------------------------&lt;br /&gt;ORA_ERR_NUMBER$                                                NUMBER&lt;br /&gt;ORA_ERR_MESG$                                                  VARCHAR2(2000)&lt;br /&gt;ORA_ERR_ROWID$                                                 ROWID&lt;br /&gt;ORA_ERR_OPTYP$                                                 VARCHAR2(2)&lt;br /&gt;ORA_ERR_TAG$                                                   VARCHAR2(2000)&lt;br /&gt;ID                                                             VARCHAR2(4000)&lt;br /&gt;NOMBRE                                                         VARCHAR2(4000)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Bien, ahora vamos a insertar los registros en la tabla TEST.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; INSERT INTO test&lt;br /&gt;2  SELECT level, &#39;nom_&#39;||level&lt;br /&gt;3  FROM dual&lt;br /&gt;4  CONNECT BY level &lt;= 1000000   &lt;span style=&quot;color: rgb(255, 0, 0); font-weight: bold;&quot;&gt;&lt;br /&gt;5  LOG ERRORS INTO ERROR_LOG_TEST REJECT LIMIT UNLIMITED;  &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;999 filas creadas.&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;WAW! De 1 millón de registros, sólo 999 registros fueron insertados satisfactoriamente, el resto de los registros se insertaron en la tabla de logueo de errores.&lt;br /&gt;&lt;br /&gt;Como notarán, use la cláusula LOG ERRORS INTO ... REJECT LIMIT UNLIMITED. Esta cláusula nos permite decirle a Oracle, que queremos utilizar &quot;DML Error Logging&quot; para nuestra sentencia. Fijense que coloqué UNLIMITED como parámetro de REJECT LIMIT. Esto le dice a Oracle, que no se fije en la cantidad de registros que terminan en error, que simplemente me inserte todos esos registros en la tabla de logueo de errores y que continúe con la ejecución de la sentencia hasta que termine. En vez de UNLIMITED, podría haber puesto 100, 1000, etc... que denota el máximo número de registros que quiero que Oracle loguee. Si hay más registros que terminan en error, Oracle simplemente aborta la ejecución de la consulta y devuelve un error por pantalla.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT count(*) FROM error_log_test;&lt;br /&gt;&lt;br /&gt;COUNT(*)&lt;br /&gt;----------&lt;br /&gt;999001&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como verán, no perdimos ningún registros. Los registros que se insertaron satisfactoriamente están en la tabla final TEST y el resto en la tabla de logueo de errores. Tener los registros en la tabla de logueo, nos permite corregirlos y tratar de insertarlos nuevamente en la tabla TEST.&lt;br /&gt;&lt;br /&gt;Veamos porqué falló la inserción de la mayoría de los registros...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT count(*), ora_err_mesg$ FROM error_log_test GROUP BY ora_err_mesg$;&lt;br /&gt;&lt;br /&gt;COUNT(*) ORA_ERR_MESG$&lt;br /&gt;---------- ----------------------------------------------------------------------------------------------------&lt;br /&gt;90000 &lt;span style=&quot;font-weight: bold;&quot;&gt;ORA-12899: el valor es demasiado grande para la columna &quot;TEST&quot;.&quot;TEST&quot;.&quot;NOMBRE&quot;&lt;/span&gt; (real: 9, máximo: 7)&lt;br /&gt; 9000 ORA-12899: el valor es demasiado grande para la columna &quot;TEST&quot;.&quot;TEST&quot;.&quot;NOMBRE&quot; (real: 8, máximo: 7)&lt;br /&gt;    1 ORA-12899: el valor es demasiado grande para la columna &quot;TEST&quot;.&quot;TEST&quot;.&quot;NOMBRE&quot; (real: 11, máximo: 7)&lt;br /&gt;900000 ORA-12899: el valor es demasiado grande para la columna &quot;TEST&quot;.&quot;TEST&quot;.&quot;NOMBRE&quot; (real: 10, máximo: 7)&lt;br /&gt;&lt;br /&gt;4 filas seleccionadas.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como podemos ver, los registros fallaron porque el campo NOMBRE tiene un tamaño de 7 caracteres, y nosotros estamos intentando insertar valores más grandes. Solucionar éste problema es muy sencillo, simplemente, tenemos que agrandar el campo NOMBRE.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; ALTER TABLE test MODIFY &lt;span style=&quot;font-weight: bold;&quot;&gt;nombre VARCHAR2(100)&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;Tabla modificada.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ahora vamos a tratar de insertar nuevamente los registros en la tabla TEST desde la tabla de logueo de errores.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; INSERT INTO test&lt;br /&gt;2  SELECT id, nombre&lt;br /&gt;3  FROM error_log_test;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;999001 filas creadas.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT count(*) FROM test;&lt;br /&gt;&lt;br /&gt;COUNT(*)&lt;br /&gt;----------&lt;br /&gt;1000000&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Excelenete!!! Ya tenemos todos los registros en la tabla TEST! Ahora sólo resta truncar la tabla de logueo de errores o simplemente borrarla en caso de que no la vayamos a utilizar nunca más.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; TRUNCATE TABLE error_log_test;&lt;br /&gt;&lt;br /&gt;Tabla truncada.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;CONCLUSIÓN:&lt;br /&gt;&lt;br /&gt;Esta nueva funcionalidad está acotada para ser utilizada sólo para algunos casos en particulares, pero espero que en lo posible, todos puedan comenzar a hacer uso de ésta funcionalidad ya que es muy simple de utilizar y muy eficiente a la hora de realizar carga masiva de datos y de logueo de errores de manera simultánea en una sola sentencia DML sin necesidad de recurrir a procedimientos costosos en cuanto a performance, desarrollo, mantenimiento y debuging.</description><link>http://lhorikian.blogspot.com/2009/05/dml-error-logging.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>6</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-6067478786535677790</guid><pubDate>Fri, 08 May 2009 22:54:00 +0000</pubDate><atom:updated>2009-05-08T20:04:22.239-03:00</atom:updated><title>Oracle adquiere Sun Microsystems</title><description>En momentos de crisis... pueden pasar cosas increíbles...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://1.bp.blogspot.com/_M2xLBs8np6Q/SgS4jOW2pII/AAAAAAAAABw/kfovFwo8GkE/s1600-h/oracle-sun.jpg&quot;&gt;&lt;img style=&quot;margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 216px;&quot; src=&quot;http://1.bp.blogspot.com/_M2xLBs8np6Q/SgS4jOW2pII/AAAAAAAAABw/kfovFwo8GkE/s320/oracle-sun.jpg&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5333590774002394242&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;unbreakable Linux... y ahora? unbreakable Solaris???   =)&lt;br /&gt;&lt;br /&gt;Para más información... &lt;a href=&quot;http://www.oracle.com/sun/index.html&quot;&gt;AQUI&lt;/a&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;</description><link>http://lhorikian.blogspot.com/2009/05/oracle-adquiere-sun-microsystems.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_M2xLBs8np6Q/SgS4jOW2pII/AAAAAAAAABw/kfovFwo8GkE/s72-c/oracle-sun.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-1500366452033578809</guid><pubDate>Sun, 03 May 2009 03:12:00 +0000</pubDate><atom:updated>2009-05-03T19:31:09.262-03:00</atom:updated><title>Cuántos registros hay en cada bloque de mi tabla?</title><description>En el día de hoy, me llegó una e-mail de una persona preguntándome lo siguiente: &quot;Me podrías decir cómo hago para saber cuántos registros hay en cada bloque de mi tabla?&quot;. Bueno, la verdad es que es muy fácil ver cuántos registros caben en cada bloque y también es muy fácil comprobarlo.&lt;br /&gt;&lt;br /&gt;Veamos un ejemplo:&lt;br /&gt;&lt;br /&gt;En la base de datos de prueba en la que estoy actualmente, tengo bloques de 8 KB.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; show parameter db_block_size&lt;br /&gt;&lt;br /&gt;NAME                                 TYPE        VALUE&lt;br /&gt;------------------------------------ ----------- ------------------------------&lt;br /&gt;db_block_size                        integer     8192&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Vamos a crear una tabla llamada TEST con 1.000 registros.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE TABLE test AS&lt;br /&gt;2  SELECT level id, &#39;nom_&#39;||level nombre&lt;br /&gt;3  FROM dual&lt;br /&gt;4  CONNECT BY level &lt;= 1000;      &lt;br /&gt;&lt;br /&gt;Tabla creada. &lt;/pre&gt;&lt;br /&gt;Para ver la cantidad de bloques que necesité para almacenar los 1.000 registros y la cantidad de registros que hay en cada uno de esos bloques, podemos ejecutar la siguiente consulta.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT dbms_rowid.rowid_block_number(rowid) &quot;Número de Bloque&quot;, count(*)&lt;br /&gt;2  FROM test&lt;br /&gt;3  GROUP BY dbms_rowid.rowid_block_number(rowid)&lt;br /&gt;4  ORDER BY dbms_rowid.rowid_block_number(rowid) ASC;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Número de Bloque   COUNT(*)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;---------------- ----------&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold; color: rgb(255, 0, 0);&quot;&gt;           46196        438&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold; color: rgb(255, 0, 0);&quot;&gt;           46197        425&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold; color: rgb(255, 0, 0);&quot;&gt;           46198        137&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;3 filas seleccionadas.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Por lo que podemos observar, en el bloque 46196 tengo 438 registros, en el bloque 46197 tengo 425 registros y en el bloque 46198 tengo 137 registros. Pero cómo hacemos para comprobar que realmente es cierto? Cómo hacemos para verificar que el resultado de la consulta es verdadero? Bueno, lo que vamos a hacer, es realizar un Dump de los 3 bloques y ver la información del Trace que se genera automáticamente. Para ejecutar un Dump, primero necesitamos obtener el número del DataFile donde se encuentra almacenada nuestra tabla (segmento). Para ésto, primero vamos a obtener ésta información y luego a realizar el Dump de los bloques. Veamos...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL_10gR2&gt; SELECT header_file FROM dba_segments WHERE segment_name = &#39;TEST&#39;;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;HEADER_FILE&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;-----------&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;          4&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt;  &lt;span style=&quot;font-weight: bold;&quot;&gt;alter system dump &lt;span style=&quot;color: rgb(255, 0, 0);&quot;&gt;datafile 4 block min 46196 block max 46198&lt;/span&gt;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;Sistema modificado.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; select spid&lt;br /&gt;2  from v$session s, v$process p&lt;br /&gt;3  where p.addr = s.paddr&lt;br /&gt;4  and s.audsid = sys_context(&#39;userenv&#39;,&#39;sessionid&#39;)&lt;br /&gt;5  /&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;SPID&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;------------&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;4360&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ya se generó el Trace en el directorio especificado en el parámetro user_dump_dest. El nombre con el que se generó es test_ora_4360.trc (el número es el SPID... &quot;System Process Identifier&quot; que obtuvimos). Veamos las partes que más nos interesan del archivo de Trace...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;data_block_dump,data header at 0x4f06a7c&lt;br /&gt;===============&lt;br /&gt;tsiz: 0x1f80&lt;br /&gt;hsiz: 0x37e&lt;br /&gt;pbl: 0x04f06a7c&lt;br /&gt;bdba: 0x0100b474&lt;br /&gt;76543210&lt;br /&gt;flag=--------&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;ntab=1  &lt;-- número de tablas en el bloque 46196.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold; color: rgb(255, 0, 0);&quot;&gt;nrow=438  &lt;-- número de registros contenidos dentro del bloque 46196. &lt;/span&gt;&lt;br /&gt;frre=-1&lt;br /&gt;fsbo=0x37e  &lt;-- comienzo del espacio libre del bloque 46196.&lt;br /&gt;fseo=0x6a9  &lt;-- fin del espacio libre del bloque 46196.&lt;br /&gt;avsp=0x32b  &lt;-- espacio disponible del bloque 46196.&lt;br /&gt;tosp=0x32b  &lt;-- espacio total del bloque 46196. &lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;br /&gt;0xe:pti[0]    nrow=438    offs=0  &lt;-- hay 438 registros en el bloque 46196 comenzando desde el registro número 0.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;data_block_dump,data header at 0xa206a7c&lt;br /&gt;===============&lt;br /&gt;tsiz: 0x1f80&lt;br /&gt;hsiz: 0x364&lt;br /&gt;pbl: 0x0a206a7c&lt;br /&gt;bdba: 0x0100b475&lt;br /&gt; 76543210&lt;br /&gt;flag=--------&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;ntab=1&lt;/span&gt;  &lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;-- número de tablas en el bloque 46197.&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: rgb(255, 0, 0);&quot;&gt;nrow=425&lt;/span&gt;  &lt;span style=&quot;font-weight: bold; color: rgb(255, 0, 0);&quot;&gt;&lt;-- número de registros contenidos dentro del bloque 46197.&lt;br /&gt;&lt;/span&gt;frre=-1&lt;br /&gt;fsbo=0x364&lt;br /&gt;fseo=0x69d&lt;br /&gt;avsp=0x339&lt;br /&gt;tosp=0x339&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;0xe:pti[0] nrow=425 offs=0&lt;/span&gt;  &lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;-- hay 425 registros en el bloque &lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;46197 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;comenzando desde el registro número 0.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;data_block_dump,data header at 0xa206a7c&lt;br /&gt;===============&lt;br /&gt;tsiz: 0x1f80&lt;br /&gt;hsiz: 0x124&lt;br /&gt;pbl: 0x0a206a7c&lt;br /&gt;bdba: 0x0100b476&lt;br /&gt; 76543210&lt;br /&gt;flag=--------&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;ntab=1&lt;/span&gt;  &lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;-- número de tablas en el bloque 46198. &lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: rgb(255, 0, 0);&quot;&gt;&lt;br /&gt;nrow=137&lt;/span&gt;  &lt;span style=&quot;font-weight: bold; color: rgb(255, 0, 0);&quot;&gt;&lt;-- número de registros contenidos dentro del bloque 46198. &lt;/span&gt;&lt;br /&gt;frre=-1&lt;br /&gt;fsbo=0x124&lt;br /&gt;fseo=0x177a&lt;br /&gt;avsp=0x1656&lt;br /&gt;tosp=0x1656&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;0xe:pti[0] nrow=137 offs=0&lt;/span&gt;  &lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;-- hay 137 registros en el bloque &lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;46198&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt; comenzando desde el registro número 0.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Bien, con éste ejemplo pudimos comprobar y verificar el resultado de nuestra primer consulta.</description><link>http://lhorikian.blogspot.com/2009/05/cuantos-registros-hay-en-cada-bloque-de.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-3736087926348268718</guid><pubDate>Sat, 02 May 2009 04:13:00 +0000</pubDate><atom:updated>2009-05-02T03:34:47.021-03:00</atom:updated><title>Encriptando columnas con TDE (Transparent Data Encryption) en tablas con millones de registros</title><description>Para los que no conocen TDE, es una nueva funcionalidad de 10g R2 que permite proteger datos sensibles de las columnas de nuestras tablas encriptando los datos al almacenarlos en los respectivos Data Files en el sistema operativo. Para protegernos de personas malintencionadas que quieran desencriptar los datos sin autorización, guarda las claves de encriptamiento en un módulo seguro externo a la base de datos.&lt;br /&gt;&lt;br /&gt;Mi intención en éste post no es mostrar el paso a paso de cómo implementar TDE, sino mostrarles cómo encriptar columnas con TDE en tablas con millones de datos de la manera más eficiente y con el menor impacto posible en cuanto a la performance.&lt;br /&gt;&lt;br /&gt;En el momento en el que encriptamos columnas en una tabla, sólo podemos acceder a la tabla en modo lectura... NO están permitidas las operaciones DML hasta que el encriptamiento termine. En tablas chicas, con muy pocos datos, ésto no suele perjudicarnos demasiado. Pero qué sucede en tablas con millones de registros? En este caso, el encriptamiento puede durar varias horas!!!&lt;br /&gt;&lt;br /&gt;La estrategia que vamos a utilizar para encriptar columnas en tablas con millones de registros, es utilizando el paquete DBMS_REDEFINITION.&lt;br /&gt;&lt;br /&gt;Veamos un ejemplo:&lt;br /&gt;&lt;br /&gt;Tengo una tabla llamada TEST_TDE con 1 millón de registros. Esta tabla tiene 3 columnas: ID (primary key), NOMBRE, NUM_TARJETA. La columna que vamos a encriptar es NUM_TARJETA ya que tiene los números de tarjetas, y como para mi es información sensible, quiero encriptarla y protegerla con TDE.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT count(*) FROM test_tde;&lt;br /&gt;&lt;br /&gt;COUNT(*)&lt;br /&gt;----------&lt;br /&gt;1000000&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; desc TEST_TDE&lt;br /&gt;&lt;br /&gt;Nombre                                     Nulo?   Tipo&lt;br /&gt;----------------------------------------- -------- ----------------------------&lt;br /&gt;ID                                        NOT NULL NUMBER&lt;br /&gt;NOMBRE                                             VARCHAR2(44)&lt;br /&gt;NUM_TARJETA                                        NUMBER&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT index_name, column_name FROM dba_ind_columns WHERE table_name = &#39;TEST_TDE&#39;;&lt;br /&gt;&lt;br /&gt;INDEX_NAME                               COLUMN_NAME&lt;br /&gt;---------------------------------------- ----------------------------------------&lt;br /&gt;TEST_TDE_ID_PK                           ID&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT constraint_name, constraint_type FROM dba_constraints WHERE table_name = &#39;TEST_TDE&#39;;&lt;br /&gt;&lt;br /&gt;CONSTRAINT_NAME                C&lt;br /&gt;------------------------------ -&lt;br /&gt;TEST_TDE_ID_PK                 P&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Bien, ahora vamos a encriptar la columna NUM_TARJETA de la manera tradicional y sin utilizar ninguna técnica. En la SESIÓN 1, voy a encriptar la columna, en la SESIÓN 2, voy a modificar el valor de uno de los registros de la tabla mientras se está realizando el procedimiento de encriptado... veamos qué sucede!&lt;br /&gt;&lt;br /&gt;SESIÓN 1:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; ALTER TABLE test_tde MODIFY (num_tarjeta &lt;span style=&quot;font-weight: bold;&quot;&gt;ENCRYPT&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;Tabla modificada.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold; color: rgb(255, 0, 0);&quot;&gt;Transcurrido: 00:01:20.11&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;SESIÓN 2:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL_10gR2&gt; UPDATE test_tde SET num_tarjeta = 123456789 WHERE id = 1000000;&lt;br /&gt;&lt;br /&gt;1 fila actualizada.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold; color: rgb(255, 0, 0);&quot;&gt;Transcurrido: 00:01:17.64&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como podemos observar, el encriptamiento de 1 millón de registros demoró 1 minuto 20 segundos. Mientras que se encriptaban los datos, ejecuté un UPDATE en otra sesión y demoró 1 minuto 17 segundos. Esto es debido a que la tabla quedó loqueda en la SESIÓN 1 por el proceso de encriptamiento y la SESIÓN 2 tuvo que esperar que termine de encriptar los datos para poder modificarlos. Imaginen los problemas de performance que tendrían en sus aplicaciones si tienen varios usuarios concurrentes realizando operaciones DML sobre esa tabla mientras se está ejecutando el procedimiento de encriptación!!!&lt;br /&gt;&lt;br /&gt;Si ejecutamos el mismo UPDATE luego de que los datos ya se encuentran encriptados, podemos observar que no demoró nada en ejecutarse la operación DML.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; UPDATE test_tde SET num_tarjeta = 123456789 WHERE id = 1000000;&lt;br /&gt;&lt;br /&gt;1 fila actualizada.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Transcurrido: 00:00:00.00&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ahora vamos a ver la técnica que les mencionaba con el paquete DBMS_REDEFINITION. Lo primero que vamos a hacer, es ejecutar el procedimiento CAN_REDEF_TABLE de éste paquete para corroborar que la tabla puede redefinirse online.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; exec DBMS_REDEFINITION.CAN_REDEF_TABLE(&#39;TEST&#39;, &#39;TEST_TDE&#39;, dbms_redefinition.cons_use_pk);&lt;br /&gt;&lt;br /&gt;Procedimiento PL/SQL terminado correctamente.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Transcurrido: 00:00:00.31&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como el procedimiento terminó sin errores, quiere decir que la tabla puede ser redefinida online.&lt;br /&gt;Ahora vamos a comenzar el proceso de redefinir la tabla. Para ésto, primero tenemos que crear una tabla intermedia (TEST_TDE_TEMP) con la estructura de la tabla TEST_TDE en donde Oracle va a colocar los datos de la tabla original de manera temporal.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; ALTER TABLE test_tde MODIFY (num_tarjeta DECRYPT);&lt;br /&gt;&lt;br /&gt;Tabla modificada.&lt;br /&gt;&lt;br /&gt;Transcurrido: 00:01:04.53&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SET long 400000000&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT dbms_metadata.get_ddl(&#39;TABLE&#39;, &#39;TEST_TDE&#39;, &#39;TEST&#39;) FROM dual;&lt;br /&gt;&lt;br /&gt;DBMS_METADATA.GET_DDL(&#39;TABLE&#39;,&#39;TEST_TDE&#39;,&#39;TEST&#39;)&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;CREATE TABLE &quot;TEST&quot;.&quot;TEST_TDE&quot;&lt;br /&gt;(    &quot;ID&quot; NUMBER,&lt;br /&gt;&quot;NOMBRE&quot; VARCHAR2(44),&lt;br /&gt;&quot;NUM_TARJETA&quot; NUMBER,&lt;br /&gt;CONSTRAINT &quot;TEST_TDE_ID_PK&quot; PRIMARY KEY (&quot;ID&quot;)&lt;br /&gt;USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS&lt;br /&gt;STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645&lt;br /&gt;PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)&lt;br /&gt;TABLESPACE &quot;USERS&quot;  ENABLE&lt;br /&gt;) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING&lt;br /&gt;STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645&lt;br /&gt;PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)&lt;br /&gt;TABLESPACE &quot;USERS&quot;&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE TABLE TEST_TDE_TEMP&lt;br /&gt;2  (&lt;br /&gt;3  ID          NUMBER,&lt;br /&gt;4  NOMBRE      VARCHAR2(44),&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;5  NUM_TARJETA NUMBER ENCRYPT,&lt;/span&gt;&lt;br /&gt;6  CONSTRAINT TEST_TDE_TEMP_ID_PK PRIMARY KEY (ID)&lt;br /&gt;7  );&lt;br /&gt;&lt;br /&gt;Tabla creada.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como pueden notar, lo primero que hice fue desencriptar la columna NUM_TARJETA para poder volver a encriptarla con ésta técnica. Luego utilicé el paquete DBMS_METADATA para ver la estructura de la tabla TEST_TDE y poder crear una similar con el nombre TEST_TDE_TEMP.&lt;br /&gt;&lt;br /&gt;Ahora vamos a redefinir la tabla con el procedimiento START_REDEF_TABLE.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; exec dbms_redefinition.start_redef_table(&#39;TEST&#39;, &#39;TEST_TDE&#39;, &#39;TEST_TDE_TEMP&#39;, &#39;id, nombre, num_tarjeta&#39;, DBMS_REDEFINITION.CONS_USE_PK);&lt;br /&gt;&lt;br /&gt;Procedimiento PL/SQL terminado correctamente.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Transcurrido: 00:00:41.18&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT count(*) FROM test_tde_temp;&lt;br /&gt;&lt;br /&gt;COUNT(*)&lt;br /&gt;----------&lt;br /&gt;1000000&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; desc TEST_TDE_TEMP&lt;br /&gt;&lt;br /&gt;Nombre                                                 Nulo?   Tipo&lt;br /&gt;----------------------------------------------------- -------- ------------------------------------&lt;br /&gt;ID                                                    NOT NULL NUMBER&lt;br /&gt;NOMBRE                                                         VARCHAR2(44)&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;NUM_TARJETA                                                    NUMBER ENCRYPT&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT master, log_table FROM dba_mview_logs WHERE log_table LIKE &#39;%TEST%&#39;;&lt;br /&gt;&lt;br /&gt;MASTER                         LOG_TABLE&lt;br /&gt;------------------------------ ------------------------------&lt;br /&gt;TEST_TDE                       MLOG$_TEST_TDE&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Fijense, que al redefinir la tabla, Oracle copió los datos de la tabla TEST_TDE y los insertó en la tabla transitoria TEST_TDE_TEMP con la columna NUM_TARJETA encriptada. A su vez, Oracle creó una vista materializada para registrar cualquier cambio que se realice sobre la tabla TEST_TDE para luego, en el momento de hacer la sincronización de datos entre las 2 tablas, pueda impactar cualquier cambio realizado.&lt;br /&gt;&lt;br /&gt;Antes de realizar el proceso de sincronización entre las 2 tablas, voy a agregar algunos registros a la tabla TEST_TDE para ver si luego de la sincronización, los últimos cambios que realicé fueron impactados. Recuerden, que todos éstos pasos los estamos haciendo online, y que por el momento, los usuarios siguen trabajando sobre la tabla TEST_TDE sin ningún tipo de impacto en cuanto a la performance.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; INSERT INTO test_tde&lt;br /&gt;2  SELECT * FROM&lt;br /&gt;3  (&lt;br /&gt;4  SELECT level id, &#39;nom_&#39;||level, round(dbms_random.value(100000000000,900000000000))&lt;br /&gt;5  FROM dual&lt;br /&gt;6  CONNECT BY level &lt;= 1000100  &lt;br /&gt;7  )  &lt;br /&gt;8  WHERE id &gt; 1000000;&lt;br /&gt;&lt;br /&gt;100 filas creadas.&lt;br /&gt;&lt;br /&gt;Transcurrido: 00:00:06.93&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; COMMIT;&lt;br /&gt;&lt;br /&gt;Confirmación terminada.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ahora vamos a sincronizar con el procedimiento SYNC_INTERIM_TABLE, las 2 tablas para aplicar cualquier cambio realizado en la tabla TEST_TDE a la tabla TEST_TDE_TEMP.&lt;br /&gt;En éste paso en adelante, necesitamos una ventana de tiempo muy chica para ejecutar los 2 procedimientos que restan. En esta ventana de tiempo, los usuarios no deben estar modificando la tabla TEST_TDE.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; exec DBMS_REDEFINITION.SYNC_INTERIM_TABLE(&#39;TEST&#39;, &#39;TEST_TDE&#39;, &#39;TEST_TDE_TEMP&#39;);&lt;br /&gt;&lt;br /&gt;Procedimiento PL/SQL terminado correctamente.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Transcurrido: 00:00:00.29&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT count(*) FROM test_tde_temp;&lt;br /&gt;&lt;br /&gt;COUNT(*)&lt;br /&gt;----------&lt;br /&gt;1000100&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;br /&gt;Transcurrido: 00:00:06.92&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; desc TEST_TDE&lt;br /&gt;&lt;br /&gt;Nombre                                                 Nulo?   Tipo&lt;br /&gt;----------------------------------------------------- -------- ------------------------------------&lt;br /&gt;ID                                                    NOT NULL NUMBER&lt;br /&gt;NOMBRE                                                         VARCHAR2(44)&lt;br /&gt;NUM_TARJETA                                                    NUMBER&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Los 100 registros que habiamos agregado a la tabla TEST_TDE fueron impactados con éxito en la tabla TEST_TDE_TEMP! Para finalizar, lo único que resta es ejecutar el procedimiento FINISH_REDEF_TABLE para aplicar en la tabla TEST_TDE todos los cambios de la tabla TEST_TDE_TEMP (incluyendo la columna encriptada).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL_10gR2&gt; exec DBMS_REDEFINITION.FINISH_REDEF_TABLE(&#39;TEST&#39;, &#39;TEST_TDE&#39;, &#39;TEST_TDE_TEMP&#39;);&lt;br /&gt;&lt;br /&gt;Procedimiento PL/SQL terminado correctamente.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Transcurrido: 00:00:00.37&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; desc TEST_TDE&lt;br /&gt;&lt;br /&gt;Nombre                                                 Nulo?   Tipo&lt;br /&gt;----------------------------------------------------- -------- ------------------------------------&lt;br /&gt;ID                                                    NOT NULL NUMBER&lt;br /&gt;NOMBRE                                                         VARCHAR2(44)&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;NUM_TARJETA                                                    NUMBER ENCRYPT&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; DROP TABLE test_tde_temp;&lt;br /&gt;&lt;br /&gt;Tabla borrada.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Con el mas mínimo impacto en la performance, pudimos encriptar la columna NUM_TARJETA de la tabla TEST_TDE (que contiene 1 millón de registros) y no afectar las operaciones de los usuarios online sobre esa tabla! Sólo tuvimos que necesitar una ventana de tiempo de 1 minuto para colocar la tabla TEST_TDE nuevamente disponible para todos los usuarios.</description><link>http://lhorikian.blogspot.com/2009/05/encriptando-columnas-con-tde-en-tablas.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>17</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-8680256895345343728</guid><pubDate>Thu, 30 Apr 2009 18:11:00 +0000</pubDate><atom:updated>2009-04-30T16:09:05.983-03:00</atom:updated><title>En lo posible, evitá utilizar el comando &quot;EXPLAIN PLAN FOR&quot; !!!</title><description>&quot;EXPLAIN PLAN FOR&quot; tiene problemas...&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;1er. PROBLEMA: Trata todas las Bind Variables como VARCHAR2.&lt;/li&gt;&lt;/ul&gt;Veamos un ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; DESC emp&lt;br /&gt;&lt;br /&gt;Nombre                  Nulo?    Tipo&lt;br /&gt;----------------------- -------- -------------&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt; EMPNO                   NOT NULL NUMBER(4)&lt;/span&gt;&lt;br /&gt;ENAME                            VARCHAR2(10)&lt;br /&gt;JOB                              VARCHAR2(9)&lt;br /&gt;GENDER                           VARCHAR2(1)&lt;br /&gt;MGR                              NUMBER(4)&lt;br /&gt;HIREDATE                         DATE&lt;br /&gt;SAL                              NUMBER(7,2)&lt;br /&gt;COMM                             NUMBER(7,2)&lt;br /&gt;DEPTNO                           NUMBER(2)&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; VAR &lt;span style=&quot;font-weight: bold;&quot;&gt;bind NUMBER&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; EXECUTE :bind := 7900&lt;br /&gt;&lt;br /&gt;Procedimiento PL/SQL terminado correctamente.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; explain plan for&lt;br /&gt;2  SELECT * FROM emp WHERE empno = :bind;&lt;br /&gt;&lt;br /&gt;Explicado.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT * FROM table(dbms_xplan.display(&#39;plan_table&#39;,null,&#39;typical&#39;));&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                   | Name         | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;--------------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT            |              |     1 |    39 |     1   (0)| 00:00:01 |&lt;br /&gt;|   1 |  TABLE ACCESS BY INDEX ROWID| EMP          |     1 |    39 |     1   (0)| 00:00:01 |&lt;br /&gt;|*  2 |   INDEX UNIQUE SCAN         | EMP_EMPNO_PK |     1 |       |     0   (0)| 00:00:01 |&lt;br /&gt;--------------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;2 - access(&quot;EMPNO&quot;=&lt;span style=&quot;font-weight: bold; color: rgb(255, 0, 0);&quot;&gt;TO_NUMBER(:BIND)&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 0, 0);&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como podemos ver, creé una variable del tipo NUMBER pero al ejecutar el comando &quot;EXPLAIN PLAN FOR&quot; utilizando esa variable, Oracle la convirtió en una variable del tipo VARCHAR2 para luego aplicarle la función de conversión TO_NUMBER y volver a convertirla en tipo NUMBER.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;2do. PROBLEMA: Puede NO mostrarte el plan de ejecución real que será utilizado en el ejecución de tu consulta.&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; DESC dept&lt;br /&gt;Nombre                  Nulo?    Tipo&lt;br /&gt;----------------------- -------- ----------------&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt; DEPTNO                  NOT NULL VARCHAR2(10)&lt;/span&gt;&lt;br /&gt;DNAME                            VARCHAR2(14)&lt;br /&gt;LOC                              VARCHAR2(13)&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT * FROM dept;&lt;br /&gt;&lt;br /&gt;DEPTNO     DNAME          LOC&lt;br /&gt;---------- -------------- -------------&lt;br /&gt;10         ACCOUNTING     NEW YORK&lt;br /&gt;20         RESEARCH       DALLAS&lt;br /&gt;30         SALES          CHICAGO&lt;br /&gt;40         OPERATIONS     BOSTON&lt;br /&gt;&lt;br /&gt;4 filas seleccionadas.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; VAR &lt;span style=&quot;font-weight: bold;&quot;&gt;bind NUMBER;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; EXECUTE :bind := 20&lt;br /&gt;&lt;br /&gt;Procedimiento PL/SQL terminado correctamente.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; explain plan for&lt;br /&gt;2  SELECT * FROM dept WHERE deptno = :bind;&lt;br /&gt;&lt;br /&gt;Explicado.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT * FROM table(&lt;span style=&quot;font-weight: bold;&quot;&gt;dbms_xplan.display&lt;/span&gt;(&#39;plan_table&#39;,null,&#39;typical&#39;));&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;----------------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT            |                |     1 |    20 |     1   (0)| 00:00:01 |&lt;br /&gt;|   1 |  TABLE ACCESS BY INDEX ROWID| DEPT           |     1 |    20 |     1   (0)| 00:00:01 |&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;|*  2 |   INDEX UNIQUE SCAN         | DEPT_DEPTNO_PK |     1 |       |     0   (0)| 00:00:01 |&lt;/span&gt;&lt;br /&gt;----------------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;2 - access(&quot;DEPTNO&quot;=:BIND)&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT * FROM dept WHERE deptno = :bind;&lt;br /&gt;&lt;br /&gt;DEPTNO     DNAME          LOC&lt;br /&gt;---------- -------------- -------------&lt;br /&gt;20         RESEARCH       DALLAS&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT * FROM table(&lt;span style=&quot;font-weight: bold;&quot;&gt;dbms_xplan.display_cursor&lt;/span&gt;(null,null,&#39;ALLSTATS LAST&#39;));&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;|*  1 |  TABLE ACCESS FULL| DEPT |      1 |      1 |      1 |00:00:00.01 |       8 |&lt;/span&gt;&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;1 - filter(&lt;span style=&quot;font-weight: bold; color: rgb(255, 0, 0);&quot;&gt;TO_NUMBER(&quot;DEPTNO&quot;)=:BIND&lt;/span&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como pueden observar, la primera vez que obtuve el plan de ejecución de la consulta, lo hice con el comando &quot;EXPLAIN PLAN FOR&quot; y vemos que estamos utilizando un índice para acceder a los datos. La segunda vez, optamos por ejecutar la consulta y luego obtener el plan de ejecución REAL (utilizando dbms_xplan.display_cursor). Qué observamos? Oracle está realizando un acceso a datos através de un FULL SCAN de la tabla. Esto sucedió porque el tipo de dato VARCHAR2 es siempre elegido para ser convertido en una comparación de tipos de datos distintos. En este caso, como la columna DEPTNO es del tipo VARCHAR2, y como estoy comparando esa columna con una variable del tipo NUMBER, Oracle tuvo que aplicar la función TO_NUMBER a la columna DEPTNO y como ya sabemos, si aplicamos una función a una columna indexada, Oracle no puede utilizar el índice en esa columna ya que la función lo deshabilita. Cuando obtuve el plan de ejecución de la primer consulta no hubo ningún tipo de conversión de datos (es por eso que accedimos por índice) ya que estoy comparando una columna del tipo VARCHAR2 (deptno) con una variable NUMBER... pero que en realidad esa variable NUMBER es un VARCHAR2 (ya que como dijimos anteriormente, si ejecutamos el comando &quot;EXPLAIN PLAN FOR&quot;, Oracle trata todas las Bind Variables como VARCHAR2).&lt;br /&gt;&lt;br /&gt;Siempre es bueno tener en mente cuál es la diferencia entre lo IDEAL y lo REAL. Lo IDEAL para éste caso sería acceder por índice a los datos, lo REAL es que Oracle está realizando lo contrario.&lt;br /&gt;&lt;p:colorscheme colors=&quot;#FFFFFF,#000000,#777777,#000000,#FD0000,#C0C0C0,#4D4D4D,#667263&quot;&gt;  &lt;/p:colorscheme&gt;</description><link>http://lhorikian.blogspot.com/2009/04/en-lo-posible-evita-utilizar-el-comando.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-7379671309246560814</guid><pubDate>Wed, 29 Apr 2009 21:03:00 +0000</pubDate><atom:updated>2009-04-30T19:10:46.930-03:00</atom:updated><title>Diferencias entre COUNT(1) y COUNT(*) - Parte 2</title><description>Hace unas semanas, se me acercaron y me hicieron la siguiente pregunta:&lt;br /&gt;&lt;br /&gt;&quot;Me dijeron que si en una consulta se coloca el COUNT(*), por cada registro que leamos en un acceso por índice, vamos a tener que acceder también a la tabla ya que el símbolo * significa que estoy colocando todas las columnas de la tabla en la consulta, y si no tengo todas las columnas de la tabla en el índice, entonces Oracle tiene que acceder a la tabla a buscar el resto de las columnas. Es cierto?&quot;.&lt;br /&gt;&lt;br /&gt;Bueno, no... no es cierto. Para validar el porque digo ésto, primero veamos un ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE TABLE test AS&lt;br /&gt;2  SELECT level id, &#39;texto_&#39;||level texto&lt;br /&gt;3  FROM dual&lt;br /&gt;4  CONNECT BY level &lt;= 100000 ; &lt;br /&gt;&lt;br /&gt;Tabla creada. &lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE UNIQUE INDEX test_id_uq ON test(id) ;&lt;br /&gt;&lt;br /&gt;Índice creado.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; EXEC dbms_stats.GATHER_TABLE_STATS(USER,&#39;TEST&#39;,CASCADE=&gt;TRUE) ;&lt;br /&gt;&lt;br /&gt;Procedimiento PL/SQL terminado correctamente.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Bien, veamos el plan de ejecución de la siguiente consulta:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL_10gR2&gt; SELECT COUNT(*)&lt;br /&gt;2  FROM test&lt;br /&gt;3  WHERE id = 100;&lt;br /&gt;&lt;br /&gt;COUNT(*)&lt;br /&gt;----------&lt;br /&gt;      1&lt;br /&gt;&lt;br /&gt;1 fila seleccionada.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; select * from table(dbms_xplan.display_cursor(null,null,&#39;ALLSTATS LAST&#39;));&lt;br /&gt;&lt;br /&gt;PLAN_TABLE_OUTPUT&lt;br /&gt;----------------------------------------------------------------------------------------------&lt;br /&gt;SQL_ID  d6urw3zfuxz32, child number 0&lt;br /&gt;-------------------------------------&lt;br /&gt;SELECT COUNT(*) FROM test WHERE id = 100&lt;br /&gt;&lt;br /&gt;Plan hash value: 4041652814&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation          | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers |&lt;br /&gt;-------------------------------------------------------------------------------------------&lt;br /&gt;|   1 |  SORT AGGREGATE    |            |      1 |      1 |      1 |00:00:00.01 |       2 |&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;|*  2 |   INDEX UNIQUE SCAN| TEST_ID_UQ |      1 |      1 |      1 |00:00:00.01 |       2 |&lt;/span&gt;&lt;br /&gt;-------------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;2 - access(&quot;ID&quot;=100)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como podemos observar, coloqué el COUNT con el símbolo * y accedí al índice con el ID 100; pero luego de acceder al índice NO accedí a la tabla, simplemente accedí al índice (ya que tiene la columna que estoy utilizando en el predicado). Como anteriormente dije en el post &quot;Diferencias entre COUNT(1) y COUNT(*) - Parte 1&quot;, no existe ninguna diferencia entre el COUNT(1) y COUNT(*), pero si hay diferencia si ejecutamos COUNT(*) y COUNT(nonbre_de_columna) ya que si colocamos una columna de la tabla en el COUNT, Oracle hace un conteo sólo de los valores de esa columna que NO tengan valores nulos.</description><link>http://lhorikian.blogspot.com/2009/04/diferencias-entre-count1-y-count-parte.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-8105145605436188367</guid><pubDate>Mon, 29 Sep 2008 14:19:00 +0000</pubDate><atom:updated>2008-09-29T11:25:27.172-03:00</atom:updated><title>Explain Plan Vs. Bind Variables</title><description>&lt;div&gt;Obtener el plan de ejecución de una consulta que contiene Bind Variables sin haberlas reemplazado??? NO!!! NO!!! NO!!!!!!!!!!&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ya hablamos en otras ocasiones del beneficio que obtenemos al utilizar Bind Variables y también explicamos qué son. Cuando ejecutamos una consulta con Bind Variables (sin haberlas reemplazado) para obtener el plan de ejecución, el optimizador de costos (CBO) no sabe el valor de la Bind Variable; y por lo tanto, calcula la selectividad del filtro utilizando reglas definidas por defecto. Que quiere decir ésto? Que el plan de ejecución que obtenemos puede ser MUY distinto al plan de ejecución real!!! Porqué muy distinto? Porque todo depende del valor con el que se reemplazará la Bind Variable y el tipo de dato de la misma.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Veamos un ejemplo:&lt;/div&gt;&lt;pre&gt;&lt;br /&gt;&lt;div&gt;SQL_10gR&gt; CREATE TABLE test AS&lt;/div&gt;&lt;div&gt;  2 SELECT TO_CHAR(level) id, &#39;test&#39;||level descripcion&lt;/div&gt;&lt;div&gt;  3 FROM dual&lt;/div&gt;&lt;div&gt;  4 CONNECT BY level &lt;= 100000;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Table created.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;SQL_10gR&gt; DESC test&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Name                    Null?    Type&lt;/div&gt;&lt;div&gt;----------------------- -------- ----------------&lt;/div&gt;&lt;div&gt;ID                               VARCHAR2(40)&lt;/div&gt;&lt;div&gt;DESCRIPCION                      VARCHAR2(44)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;SQL_10gR&gt; CREATE UNIQUE INDEX test_uq ON test(id, descripcion);&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Index created.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;SQL_10gR&gt; EXEC dbms_stats.gather_table_stats(user, &#39;TEST&#39;, cascade=&gt;true) ;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;PL/SQL procedure successfully completed.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;div&gt;Supongamos que detectamos un problema grave en la performance de una de nuestras aplicaciones. Al identificar la consulta que nos está causando problemas, obtenemos el plan de ejecución de la misma para ver si está accediendo correctamente...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ejecutamos la consulta &lt;span class=&quot;Apple-style-span&quot; style=&quot;font-style: italic;&quot;&gt;con &lt;/span&gt;Bind Variable:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;&lt;div&gt;SQL_10gR&gt; EXPLAIN PLAN FOR&lt;/div&gt;&lt;div&gt;  2 SELECT descripcion&lt;/div&gt;&lt;div&gt;  3 FROM test&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold;&quot;&gt;  4 WHERE id = :b1;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Explained.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;SQL_10gR&gt; @explains&lt;/div&gt;&lt;div&gt;Plan hash value: 1087767317&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;----------------------------------------------------------------------------&lt;/div&gt;&lt;div&gt;| Id | Operation        | Name    | Rows  | Bytes | Cost (%CPU)| Time      |&lt;/div&gt;&lt;div&gt;----------------------------------------------------------------------------&lt;/div&gt;&lt;div&gt;|  0 | SELECT STATEMENT |         |     1 |    15 |       2 (0)|  00:00:01 |&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold;&quot;&gt;|* 1 | INDEX RANGE SCAN | TEST_UQ |     1 |    15 |       2 (0)|  00:00:01 |&lt;/span&gt;&lt;/div&gt;&lt;div&gt;----------------------------------------------------------------------------&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Predicate Information (identified by operation id):&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;---------------------------------------------------&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold;&quot;&gt;1 - access(&quot;ID&quot;=:B1)&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Por lo que vemos en el plan de ejecución, si filtramos la columna ID con un valor del mismo tipo de dato, el optimizador eligirá acceder por índice en vez de realizar un full scan de la tabla. Esto suena lógico sabiendo que los valores de la columna ID son únicos y que por cada valor con el que filtremos, a lo sumo obtendremos una ocurrencia del mismo valor en la tabla.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ejecutamos una consulta &lt;span class=&quot;Apple-style-span&quot; style=&quot;font-style: italic;&quot;&gt;sin &lt;/span&gt;Bind Variable:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;&lt;div&gt;SQL_10gR&gt; EXPLAIN PLAN FOR&lt;/div&gt;&lt;div&gt;  2 SELECT descripcion&lt;/div&gt;&lt;div&gt;  3 FROM test&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold;&quot;&gt;  4 WHERE id = 10000;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Explained.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;SQL_10gR&gt; @explains&lt;/div&gt;&lt;div&gt;Plan hash value: 1357081020&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;--------------------------------------------------------------------------&lt;/div&gt;&lt;div&gt;| Id | Operation        | Name | Rows | Bytes | Cost (%CPU)| Time        |&lt;/div&gt;&lt;div&gt;--------------------------------------------------------------------------&lt;/div&gt;&lt;div&gt;|  0 | SELECT STATEMENT |      |    1 |    15 |      54 (4)|    00:00:01 |&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold;&quot;&gt;|* 1 | TABLE ACCESS FULL| TEST |    1 |    15 |      54 (4)|    00:00:01 |&lt;/span&gt;&lt;/div&gt;&lt;div&gt;--------------------------------------------------------------------------&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Predicate Information (identified by operation id):&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;---------------------------------------------------&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold;&quot;&gt;1 - filter(TO_NUMBER(&quot;ID&quot;)=10000)&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Pero qué sucede si filtramos la columna ID con un valor de distinto tipo de dato? Como en éste caso estamos realizando una conversión implícita, el optimizador no puede utilizar el índice que tenemos creado en la tabla y por lo tanto se ve forzado a realizar un full scan de la misma.&lt;/div&gt;&lt;div&gt;Si en nuestra aplicación el problema es justamente éste (que estamos realizando una conversión implícita), si no reemplazamos las Bind Variables con valores reales, estaremos pensando que el optimizador está accediendo de la manera correcta... cuando en realidad ésto no es cierto.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold;&quot;&gt;Recuerden lo siguiente:&lt;/span&gt; Siempre que obtengan el plan de ejecución de una consulta... reemplacen las Bind Variables con valores reales!!! En caso contrario... no deberíamos fiarnos demasiado con el plan de ejecución obtenido.&lt;/div&gt;</description><link>http://lhorikian.blogspot.com/2008/09/explain-plan-vs-bind-variables_29.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>12</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-7596419103284392758</guid><pubDate>Tue, 26 Feb 2008 17:39:00 +0000</pubDate><atom:updated>2008-05-13T09:12:14.470-03:00</atom:updated><title>¿Tunear en base al COSTO del plan de ejecución?</title><description>Todos los días observo que alguien está queriendo tunear una consulta en base al costo del plan de ejecución. Pero... ¿Qué es el COSTO? ¿Qué representa? La respuesta es simple: El costo representa unidades de trabajo o recursos utilizados. El optimizador usa I/O a disco, CPU y memoria como unidades de trabajo. Entonces, el costo para una determinada consulta representa una estimación de la cantidad de I/O a disco, de CPU y memoria que se utilizará para la ejecución de la consulta.&lt;br /&gt;&lt;br /&gt;Bien, con ésto ya dicho, porqué hay personas que tratan de tunear una consulta en base al costo??? El costo es simplemente un número que le asigna el optimizador de costos (CBO) a la consulta para saber qué plan de ejecución elegir entre todos los planes que genera en el momento de la optimización (el plan de ejecución que se genera con el menor costo es el que Oracle utiliza para ejecutar nuestra consulta), pero no existe un &quot;mejor número&quot; que debemos tener en mente para deducir si una consulta es óptima o no.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;NO&lt;/span&gt; debemos tunear en base al costo. &lt;span style=&quot;font-weight: bold;&quot;&gt;SI&lt;/span&gt; debemos tunear en base a los I/O lógicos (LIO&#39;s).&lt;br /&gt;&lt;br /&gt;Veamos un ejemplo:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; CREATE TABLE test AS&lt;br /&gt;2  SELECT level id, &#39;nombre_&#39;||level nom&lt;br /&gt;3  FROM dual&lt;br /&gt;4  CONNECT BY level &lt;= 100000 ; &lt;br /&gt;&lt;br /&gt;Table created. &lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; EXEC dbms_stats.gather_table_stats(user,&#39;TEST&#39;) ;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; SET AUTOTRACE TRACEONLY&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; SELECT nom&lt;br /&gt;2  FROM test&lt;br /&gt;3  WHERE id = 50000 ;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;0      SELECT STATEMENT Optimizer=CHOOSE (&lt;span style=&quot;font-weight: bold;&quot;&gt;Cost=49&lt;/span&gt; Card=1 Bytes=17)&lt;br /&gt;1    0   TABLE ACCESS (FULL) OF &#39;TEST&#39; (Cost=49 Card=1 Bytes=17)&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;     5  recursive calls&lt;br /&gt;     0  db block gets&lt;br /&gt;  &lt;span style=&quot;font-weight: bold;&quot;&gt; 323  consistent gets&lt;/span&gt;&lt;br /&gt;     0  physical reads&lt;br /&gt;     0  redo size&lt;br /&gt;   335  bytes sent via SQL*Net to client&lt;br /&gt;   495  bytes received via SQL*Net from client&lt;br /&gt;     2  SQL*Net roundtrips to/from client&lt;br /&gt;     2  sorts (memory)&lt;br /&gt;     0  sorts (disk)&lt;br /&gt;     1  rows processed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como podemos observar, el costo de la consulta es de 49. Si yo tuneo en base al costo no puedo saber si el plan de ejecución que se está utilizando para ésta consulta es óptimo o no porque no sé que costo sería el ideal para saberlo. Es por eso que NO debemos tunear en base al costo. Por otro lado, podríamos tunear en base a la cantidad de I/O lógicos que se estén realizando. En éste ejemplo, se realizan 323 LIO&#39;s. Si observamos la consulta, estoy seleccionando la columna NOM que corresponde al ID 50000 . Como creé la tabla de forma tal que todos los ID&#39;s sean únicos, ésta consulta me debería traer un solo registro. Si nos ponemos a pensar, hacer 323 LIO&#39;s para traer sólo un registro es demasiado. En éste momento es en donde nos damos cuenta que tenemos un problema de performance porque estamos haciendo un Full Scan de una tabla de 100.000 registros para buscar solamente el ID 50000 que nos devuelve un sólo registro.&lt;br /&gt;&lt;br /&gt;Veamos qué sucede si creamos un índice único por la columna ID,NOM...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; CREATE UNIQUE INDEX test_id_nom_uq ON test(id,nom) ;&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; EXEC dbms_stats.gather_index_stats(user,&#39;TEST_ID_NOM_UQ&#39;) ;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; SELECT nom&lt;br /&gt;2  FROM test&lt;br /&gt;3  WHERE id = 50000 ;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;0      SELECT STATEMENT Optimizer=CHOOSE (&lt;span style=&quot;font-weight: bold;&quot;&gt;Cost=2&lt;/span&gt; Card=1 Bytes=17)&lt;br /&gt;1    0   INDEX (RANGE SCAN) OF &#39;TEST_ID_NOM_UQ&#39; (UNIQUE) (Cost=2 Card=1 Bytes=17)&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;     0  recursive calls&lt;br /&gt;     0  db block gets&lt;br /&gt;   &lt;span style=&quot;font-weight: bold;&quot;&gt;  3  consistent gets&lt;/span&gt;&lt;br /&gt;     0  physical reads&lt;br /&gt;     0  redo size&lt;br /&gt;   335  bytes sent via SQL*Net to client&lt;br /&gt;   495  bytes received via SQL*Net from client&lt;br /&gt;     2  SQL*Net roundtrips to/from client&lt;br /&gt;     0  sorts (memory)&lt;br /&gt;     0  sorts (disk)&lt;br /&gt;     1  rows processed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Luego de crear un índice único por la columna ID,NOM, ejecutando nuevamente la consulta, notamos que ahora sólo estamos realizando 3 LIO&#39;s y, por consiguiente, el costo se decrementó ya que estamos utilizando menos recursos que el caso anterior.</description><link>http://lhorikian.blogspot.com/2008/02/tunear-en-base-al-costo-del-plan-de.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>8</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-1703366817046458341</guid><pubDate>Tue, 16 Oct 2007 19:19:00 +0000</pubDate><atom:updated>2007-10-16T17:11:46.035-03:00</atom:updated><title>DB_FILE_MULTIBLOCK_READ_COUNT (MBRC)</title><description>El parámetro DB_FILE_MULTIBLOCK_READ_COUNT especifica la cantidad de bloques que van a ser leídos en cada I/O a través de un Full Scan.&lt;br /&gt;&lt;br /&gt;En Oracle 10g Release 2, el valor por default de éste parámetro, es el valor que corresponde a la cantidad máxima de I/O que se puede realizar de forma más eficiente. &lt;br /&gt;&lt;br /&gt;En ambientes OLTP o Batch, éste parámetro suele setearse en valores entre 4 y 16 bloques. En ambientes DSS o Data Warehouse, éste parámetro suele setearse en un valor mayor.&lt;br /&gt;&lt;br /&gt;Este parámetro afecta al Optimizador de Costos (CBO) ya que si seteamos un valor alto, el CBO puede ser influenciado en elegir realizar un Full Scan en vez de acceder por índice. Como en los ambientes DSS o Data Warehouse suelen utilizarse planes de ejecución que incluyen Full Scan, aumentar el valor de éste parámetro puede ser beneficioso.&lt;br /&gt;&lt;br /&gt;El máximo valor que podemos setear depende del sistema operativo.&lt;br /&gt;La fórmula que se utiliza para buscar el valor de éste parámetro es:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;DB_FILE_MULTIBLOCK_READ_COUNT = ( (MAX I/O SIZE) / DB_BLOCK_SIZE )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Si elegimos un valor mayor al máximo soportado, Oracle simplemente elige el valor máximo que puede utilizar.&lt;br /&gt;&lt;br /&gt;Veamos un ejemplo de cómo buscar el valor máximo del parámetro DB_FILE_MULTIBLOCK_READ_COUNT sin utilizar la fórmula:&lt;br /&gt;&lt;br /&gt;Primero, veamos el valor actual del MBRC:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; show parameter multiblock&lt;br /&gt;&lt;br /&gt;NAME_COL_PLUS_SHOW_PARAM       TYPE        VALUE_COL_PLUS_SHOW_PARAM&lt;br /&gt;------------------------------ ----------- ------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;db_file_multiblock_read_count  integer     16&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Observemos el Explain Plan de una de las tablas:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; explain plan for&lt;br /&gt;  2  SELECT /*+ full(test) noparallel(test) nocache(test) */ count(*)&lt;br /&gt;  3  FROM test ;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; @explains&lt;br /&gt;Plan hash value: 3740828345&lt;br /&gt;&lt;br /&gt;---------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation             | Name                  | Rows  | Cost (%CPU)| Time     |&lt;br /&gt;---------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;|   0 | SELECT STATEMENT      |                       |     1 | 46709   (1)| 00:10:55 |&lt;/span&gt;&lt;br /&gt;|   1 |  SORT AGGREGATE       |                       |     1 |            |          |&lt;br /&gt;|   2 |   TEST ACCESS FULL    | TEST                  |    20M| 46709   (1)| 00:10:55 |&lt;br /&gt;---------------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El costo de la consulta es 46709. &lt;br /&gt;Veamos qué sucede si seteo el valor del parámetro muy alto:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; ALTER SESSION SET DB_FILE_MULTIBLOCK_READ_COUNT = 50000 ;&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; show parameter multiblock&lt;br /&gt;&lt;br /&gt;NAME_COL_PLUS_SHOW_PARAM       TYPE        VALUE_COL_PLUS_SHOW_PARAM&lt;br /&gt;------------------------------ ----------- ------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;db_file_multiblock_read_count  integer     64&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Bien, como podemos observar, seteamos el parámetro en 50.000 bloques; pero como excedemos el máximo permitido, Oracle setea el máximo valor que puede alcanzar.&lt;br /&gt;&lt;br /&gt;Ahora veamos si Oracle realmente está utilizando el nuevo MBRC que acabamos de setear:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; ALTER SESSION SET EVENTS &#39;10046 trace name context forever, level 8&#39; ;&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; SELECT /*+ full(test) noparallel(test) nocache(test) */ count(*)&lt;br /&gt;  2  FROM test ;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;  52673028&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; ALTER SESSION SET EVENTS &#39;10046 trace name context off&#39; ;&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; @trace_file_name&lt;br /&gt;&lt;br /&gt;TRACE_FILE_NAME&lt;br /&gt;-----------------------------&lt;br /&gt;testdb_ora_11379.trc&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;[test@linux_test udump]$ cat testdb_ora_11379.trc | grep &quot;scattered read&quot; | awk &#39;{ split ($11,listado,&quot;=&quot;); print listado[2];}&#39; | sort -n | tail -1&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;64&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;[test@linux_test udump]$ more testdb_ora_11379.trc | grep scattered&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;...&lt;br /&gt;WAIT #1: nam=&#39;db file scattered read&#39; ela= 1411 file#=115 block#=241927 &lt;span style=&quot;font-weight:bold;&quot;&gt;blocks=64&lt;/span&gt; obj#=70390 tim=1164040832168738&lt;br /&gt;WAIT #1: nam=&#39;db file scattered read&#39; ela= 1394 file#=115 block#=241991 &lt;span style=&quot;font-weight:bold;&quot;&gt;blocks=64&lt;/span&gt; obj#=70390 tim=1164040832171544&lt;br /&gt;WAIT #1: nam=&#39;db file scattered read&#39; ela= 1426 file#=115 block#=242055 &lt;span style=&quot;font-weight:bold;&quot;&gt;blocks=64&lt;/span&gt; obj#=70390 tim=1164040832174390&lt;br /&gt;WAIT #1: nam=&#39;db file scattered read&#39; ela= 1399 file#=115 block#=242119 &lt;span style=&quot;font-weight:bold;&quot;&gt;blocks=64&lt;/span&gt; obj#=70390 tim=1164040832177182&lt;br /&gt;WAIT #1: nam=&#39;db file scattered read&#39; ela= 1391 file#=115 block#=242183 &lt;span style=&quot;font-weight:bold;&quot;&gt;blocks=64&lt;/span&gt; obj#=70390 tim=1164040832179966&lt;br /&gt;...&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Bien. Vemos que efectivamente estamos leyendo 64 bloques en cada I/O que realizamos.&lt;br /&gt;&lt;br /&gt;Ahora ejecutamos nuevamente nuestra consulta y observamos el Explain Plan:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; explain plan for&lt;br /&gt;  2  SELECT /*+ full(test) noparallel(test) nocache(test) */ count(*)&lt;br /&gt;  3  FROM test ;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; @explains&lt;br /&gt;Plan hash value: 3740828345&lt;br /&gt;&lt;br /&gt;---------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation             | Name                  | Rows  | Cost (%CPU)| Time     |&lt;br /&gt;---------------------------------------------------------------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;|   0 | SELECT STATEMENT      |                       |     1 | 41994   (1)| 00:09:49 |&lt;/span&gt;&lt;br /&gt;|   1 |  SORT AGGREGATE       |                       |     1 |            |          |&lt;br /&gt;|   2 |   TEST ACCESS FULL    | TEST                  |    20M| 41994   (1)| 00:09:49 |&lt;br /&gt;---------------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ahora el costo de la consulta pasa a ser 41994. &lt;br /&gt;&lt;br /&gt;Hay que tener mucho cuidado si decidimos modificar éste parámetro ya que no siempre obtenemos un beneficio. Recuerden que modificando el MBRC por default puede producir que nuestro sistema funcione peor, igual o mejor. Por lo general, el MBRC por default suele ser el correcto.&lt;br /&gt;&lt;br /&gt;Mi consejo es que no modifiquen el MBRC si no es necesario.</description><link>http://lhorikian.blogspot.com/2007/10/dbfilemultiblockreadcount-mbrc.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>6</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-967170335870270678</guid><pubDate>Sat, 29 Sep 2007 20:14:00 +0000</pubDate><atom:updated>2007-09-30T11:22:32.971-03:00</atom:updated><title>Problemas utilizando Analytic Functions junto con PL/SQL  en 8i</title><description>La idea de éste post no es explicar el funcionamiento de las Analytic Functions, sino el error PLS-00103 al utilizar Analytic Functions.&lt;br /&gt;La mejor manera de explicar éste problema, es realizando un ejemplo.&lt;br /&gt;&lt;br /&gt;La tabla de prueba que vamos a utilizar en nuestro ejemplo, contiene los siguientes registros:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; SELECT * FROM test ;&lt;br /&gt;&lt;br /&gt;        ID      NIVEL    SALARIO&lt;br /&gt;---------- ---------- ----------&lt;br /&gt;        10          1       2500&lt;br /&gt;        20          2       3000&lt;br /&gt;        30          1       3500&lt;br /&gt;        40          2       4000&lt;br /&gt;        50          1       4500&lt;br /&gt;        60          2       5000&lt;br /&gt;        70          1       5500&lt;br /&gt;        80          2       6000&lt;br /&gt;        90          1       6500&lt;br /&gt;       100          2       7000&lt;br /&gt;&lt;br /&gt;10 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Primero vamos a realizar el ejemplo en una base de datos 8.1.7.4.0 &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_8i&gt; SELECT id, nivel, salario, DENSE_RANK() OVER (PARTITION BY nivel ORDER BY salario DESC) AS rank&lt;br /&gt;  2      FROM test ;&lt;br /&gt;&lt;br /&gt;        ID      NIVEL    SALARIO       RANK&lt;br /&gt;---------- ---------- ---------- ----------&lt;br /&gt;        90          1       6500          1&lt;br /&gt;        70          1       5500          2&lt;br /&gt;        50          1       4500          3&lt;br /&gt;        30          1       3500          4&lt;br /&gt;        10          1       2500          5&lt;br /&gt;       100          2       7000          1&lt;br /&gt;        80          2       6000          2&lt;br /&gt;        60          2       5000          3&lt;br /&gt;        40          2       4000          4&lt;br /&gt;        20          2       3000          5&lt;br /&gt;&lt;br /&gt;10 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como podemos observar, si ejecutamos esa consulta con Analytic Functions en SQL*Plus, funciona a la perfección. Veamos qué sucede si intentamos ejecutar la consulta dentro de PL/SQL:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_8i&gt; CREATE PROCEDURE pr_test&lt;br /&gt;  2  IS&lt;br /&gt;  3  BEGIN&lt;br /&gt;  4      --&lt;br /&gt;  5      FOR cur IN ( SELECT id, nivel, salario, &lt;span style=&quot;font-weight:bold;&quot;&gt;DENSE_RANK() OVER (PARTITION BY&lt;br /&gt; nivel ORDER BY salario DESC&lt;/span&gt;) AS rank&lt;br /&gt;  6                 FROM test ) LOOP&lt;br /&gt;  7          --&lt;br /&gt;  8          dbms_output.put_line(&#39;ID: &#39;||cur.id||&lt;br /&gt;  9                               &#39; - NIVEL: &#39;||cur.nivel||&lt;br /&gt; 10                               &#39; - SALARIO: &#39;||cur.salario||&lt;br /&gt; 11                               &#39; - RANK: &#39;||cur.rank) ;&lt;br /&gt; 12          --&lt;br /&gt; 13      END LOOP ;&lt;br /&gt; 14      --&lt;br /&gt; 15  END pr_test ;&lt;br /&gt; 16  /&lt;br /&gt;&lt;br /&gt;Warning: Procedure created with compilation errors.&lt;br /&gt;&lt;br /&gt;SQL_8i&gt; show errors&lt;br /&gt;&lt;br /&gt;Errors for PROCEDURE PR_TEST:&lt;br /&gt;&lt;br /&gt;LINE/COL     ERROR&lt;br /&gt;-----------  ------------------------------------------------------------------------&lt;br /&gt;5/63         &lt;span style=&quot;font-weight:bold;&quot;&gt;PLS-00103&lt;/span&gt;: Encountered the symbol &quot;(&quot; when expecting one of the following:&lt;br /&gt;             , from&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Este error se debe a que según la Nota 147808.1, no podemos utilizar Analytic Functions dentro de PL/SQL en versiones anteriores a 9i. Podemos utilizar éste tipo de funciones en SQL, pero no en PL/SQL. Desde las versiones de la 9i en adelante, podemos utilizar Analytic Functions tanto en SQL como en PL/SQL.&lt;br /&gt;&lt;br /&gt;Hay 2 formas de solucionar éste problema:&lt;br /&gt;- Crear vistas utilizando Analytic Function y luego hacer referencia a esas vistas dentro de PL/SQL. &lt;br /&gt;- Usar Dynamic SQL.&lt;br /&gt;&lt;br /&gt;Veamos la implementación de esas 2 soluciones:&lt;br /&gt;&lt;br /&gt;Utilizando una vista...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_8i&gt; CREATE VIEW pr_test_view AS&lt;br /&gt;  2  SELECT id, nivel, salario, DENSE_RANK() OVER (PARTITION BY nivel ORDER BY s&lt;br /&gt;alario DESC) AS rank&lt;br /&gt;  3  FROM test ;&lt;br /&gt;&lt;br /&gt;View created.&lt;br /&gt;&lt;br /&gt;SQL_8i&gt; CREATE OR REPLACE PROCEDURE pr_test&lt;br /&gt;  2  IS&lt;br /&gt;  3  BEGIN&lt;br /&gt;  4      --&lt;br /&gt;  5      FOR cur IN ( SELECT id, nivel, salario, rank&lt;br /&gt;  6                   FROM &lt;span style=&quot;font-weight:bold;&quot;&gt;pr_test_view&lt;/span&gt; ) LOOP&lt;br /&gt;  7          --&lt;br /&gt;  8          dbms_output.put_line(&#39;ID: &#39;||cur.id||&lt;br /&gt;  9                               &#39; - NIVEL: &#39;||cur.nivel||&lt;br /&gt; 10                               &#39; - SALARIO: &#39;||cur.salario||&lt;br /&gt; 11                               &#39; - RANK: &#39;||cur.rank) ;&lt;br /&gt; 12          --&lt;br /&gt; 13      END LOOP ;&lt;br /&gt; 14      --&lt;br /&gt; 15  END pr_test ;&lt;br /&gt; 16  /&lt;br /&gt;&lt;br /&gt;Procedure created.&lt;br /&gt;&lt;br /&gt;SQL_8i&gt; EXEC pr_test&lt;br /&gt;&lt;br /&gt;ID: 90 - NIVEL: 1 - SALARIO: 6500 - RANK: 1&lt;br /&gt;ID: 70 - NIVEL: 1 - SALARIO: 5500 - RANK: 2&lt;br /&gt;ID: 50 - NIVEL: 1 - SALARIO: 4500 - RANK: 3&lt;br /&gt;ID: 30 - NIVEL: 1 - SALARIO: 3500 - RANK: 4&lt;br /&gt;ID: 10 - NIVEL: 1 - SALARIO: 2500 - RANK: 5&lt;br /&gt;ID: 100 - NIVEL: 2 - SALARIO: 7000 - RANK: 1&lt;br /&gt;ID: 80 - NIVEL: 2 - SALARIO: 6000 - RANK: 2&lt;br /&gt;ID: 60 - NIVEL: 2 - SALARIO: 5000 - RANK: 3&lt;br /&gt;ID: 40 - NIVEL: 2 - SALARIO: 4000 - RANK: 4&lt;br /&gt;ID: 20 - NIVEL: 2 - SALARIO: 3000 - RANK: 5&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Utilizando Dynamic SQL...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_8i&gt; CREATE OR REPLACE PROCEDURE pr_test&lt;br /&gt;  2  IS&lt;br /&gt;  3  --&lt;br /&gt;  4  TYPE   mi_cursor IS REF CURSOR ;&lt;br /&gt;  5  cur    mi_cursor ;&lt;br /&gt;  6  --&lt;br /&gt;  7  l_consulta  VARCHAR2(1000) ;&lt;br /&gt;  8  l_id        test.id%TYPE ;&lt;br /&gt;  9  l_nivel     test.nivel%TYPE ;&lt;br /&gt; 10  l_salario   test.salario%TYPE ;&lt;br /&gt; 11  l_rank      NUMBER ;&lt;br /&gt; 12  --&lt;br /&gt; 13  BEGIN&lt;br /&gt; 14      --&lt;br /&gt; 15      l_consulta := &#39;SELECT id, nivel, salario, DENSE_RANK() OVER (PARTITION&lt;br /&gt;BY nivel ORDER BY salario DESC) AS rank FROM test&#39; ;&lt;br /&gt; 16      --&lt;br /&gt; 17      &lt;span style=&quot;font-weight:bold;&quot;&gt;OPEN cur FOR l_consulta ;&lt;/span&gt;&lt;br /&gt; 18      --&lt;br /&gt; 19      LOOP&lt;br /&gt; 20          --&lt;br /&gt; 21          FETCH cur INTO l_id, l_nivel, l_salario, l_rank ;&lt;br /&gt; 22          --&lt;br /&gt; 23          EXIT WHEN cur%NOTFOUND ;&lt;br /&gt; 24          --&lt;br /&gt; 25          dbms_output.put_line(&#39;ID: &#39;||l_id||&lt;br /&gt; 26                               &#39; - NIVEL: &#39;||l_nivel||&lt;br /&gt; 27                               &#39; - SALARIO: &#39;||l_salario||&lt;br /&gt; 28                               &#39; - RANK: &#39;||l_rank) ;&lt;br /&gt; 29          --&lt;br /&gt; 30      END LOOP ;&lt;br /&gt; 31      --&lt;br /&gt; 32      CLOSE cur ;&lt;br /&gt; 33      --&lt;br /&gt; 34  END pr_test ;&lt;br /&gt; 35  /&lt;br /&gt;&lt;br /&gt;Procedure created.&lt;br /&gt;&lt;br /&gt;SQL_8i&gt; EXEC pr_test&lt;br /&gt;&lt;br /&gt;ID: 90 - NIVEL: 1 - SALARIO: 6500 - RANK: 1&lt;br /&gt;ID: 70 - NIVEL: 1 - SALARIO: 5500 - RANK: 2&lt;br /&gt;ID: 50 - NIVEL: 1 - SALARIO: 4500 - RANK: 3&lt;br /&gt;ID: 30 - NIVEL: 1 - SALARIO: 3500 - RANK: 4&lt;br /&gt;ID: 10 - NIVEL: 1 - SALARIO: 2500 - RANK: 5&lt;br /&gt;ID: 100 - NIVEL: 2 - SALARIO: 7000 - RANK: 1&lt;br /&gt;ID: 80 - NIVEL: 2 - SALARIO: 6000 - RANK: 2&lt;br /&gt;ID: 60 - NIVEL: 2 - SALARIO: 5000 - RANK: 3&lt;br /&gt;ID: 40 - NIVEL: 2 - SALARIO: 4000 - RANK: 4&lt;br /&gt;ID: 20 - NIVEL: 2 - SALARIO: 3000 - RANK: 5&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ahora vamos a realizar el ejemplo anterior pero en una base de datos 9.2.0.8.0&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9i&gt; CREATE PROCEDURE pr_test&lt;br /&gt;  2  IS&lt;br /&gt;  3  BEGIN&lt;br /&gt;  4      --&lt;br /&gt;  5      FOR cur IN ( SELECT id, nivel, salario, DENSE_RANK() OVER (PARTITION BY&lt;br /&gt; nivel ORDER BY salario DESC) AS rank&lt;br /&gt;  6               FROM test ) LOOP&lt;br /&gt;  7          --&lt;br /&gt;  8          dbms_output.put_line(&#39;ID: &#39;||cur.id||&lt;br /&gt;  9                               &#39; - NIVEL: &#39;||cur.nivel||&lt;br /&gt; 10                               &#39; - SALARIO: &#39;||cur.salario||&lt;br /&gt; 11                               &#39; - RANK: &#39;||cur.rank) ;&lt;br /&gt; 12          --&lt;br /&gt; 13      END LOOP ;&lt;br /&gt; 14      --&lt;br /&gt; 15  END pr_test ;&lt;br /&gt; 16  /&lt;br /&gt;&lt;br /&gt;Procedure created.&lt;br /&gt;&lt;br /&gt;SQL_9i&gt; EXEC pr_test&lt;br /&gt;&lt;br /&gt;ID: 90 - NIVEL: 1 - SALARIO: 6500 - RANK: 1&lt;br /&gt;ID: 70 - NIVEL: 1 - SALARIO: 5500 - RANK: 2&lt;br /&gt;ID: 50 - NIVEL: 1 - SALARIO: 4500 - RANK: 3&lt;br /&gt;ID: 30 - NIVEL: 1 - SALARIO: 3500 - RANK: 4&lt;br /&gt;ID: 10 - NIVEL: 1 - SALARIO: 2500 - RANK: 5&lt;br /&gt;ID: 100 - NIVEL: 2 - SALARIO: 7000 - RANK: 1&lt;br /&gt;ID: 80 - NIVEL: 2 - SALARIO: 6000 - RANK: 2&lt;br /&gt;ID: 60 - NIVEL: 2 - SALARIO: 5000 - RANK: 3&lt;br /&gt;ID: 40 - NIVEL: 2 - SALARIO: 4000 - RANK: 4&lt;br /&gt;ID: 20 - NIVEL: 2 - SALARIO: 3000 - RANK: 5&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Realizamos el ejemplo anterior en una base de datos 10.1.0.2.0, veremos el mismo resultado que en 9i...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10g&gt; CREATE PROCEDURE pr_test&lt;br /&gt;  2  IS&lt;br /&gt;  3  BEGIN&lt;br /&gt;  4      --&lt;br /&gt;  5      FOR cur IN ( SELECT id, nivel, salario, DENSE_RANK() OVER (PARTITION BY&lt;br /&gt; nivel ORDER BY salario DESC) AS rank&lt;br /&gt;  6                   FROM test ) LOOP&lt;br /&gt;  7          --&lt;br /&gt;  8          dbms_output.put_line(&#39;ID: &#39;||cur.id||&lt;br /&gt;  9                               &#39; - NIVEL: &#39;||cur.nivel||&lt;br /&gt; 10                               &#39; - SALARIO: &#39;||cur.salario||&lt;br /&gt; 11                               &#39; - RANK: &#39;||cur.rank) ;&lt;br /&gt; 12          --&lt;br /&gt; 13      END LOOP ;&lt;br /&gt; 14      --&lt;br /&gt; 15  END pr_test ;&lt;br /&gt; 16  /&lt;br /&gt;&lt;br /&gt;Procedure created.&lt;br /&gt;&lt;br /&gt;SQL_10g&gt; EXEC pr_test&lt;br /&gt;&lt;br /&gt;ID: 90 - NIVEL: 1 - SALARIO: 6500 - RANK: 1&lt;br /&gt;ID: 70 - NIVEL: 1 - SALARIO: 5500 - RANK: 2&lt;br /&gt;ID: 50 - NIVEL: 1 - SALARIO: 4500 - RANK: 3&lt;br /&gt;ID: 30 - NIVEL: 1 - SALARIO: 3500 - RANK: 4&lt;br /&gt;ID: 10 - NIVEL: 1 - SALARIO: 2500 - RANK: 5&lt;br /&gt;ID: 100 - NIVEL: 2 - SALARIO: 7000 - RANK: 1&lt;br /&gt;ID: 80 - NIVEL: 2 - SALARIO: 6000 - RANK: 2&lt;br /&gt;ID: 60 - NIVEL: 2 - SALARIO: 5000 - RANK: 3&lt;br /&gt;ID: 40 - NIVEL: 2 - SALARIO: 4000 - RANK: 4&lt;br /&gt;ID: 20 - NIVEL: 2 - SALARIO: 3000 - RANK: 5&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como pudimos observar, de la versión 9i en adelante, podemos utilizar Analytic Functions tanto en SQL como en PL/SQL.</description><link>http://lhorikian.blogspot.com/2007/09/problemas-utilizando-analytic-functions.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-3273184379334305455</guid><pubDate>Mon, 24 Sep 2007 14:31:00 +0000</pubDate><atom:updated>2007-09-24T12:53:58.725-03:00</atom:updated><title>Problemas en Vistas Materializadas - Parte 2</title><description>Como pudimos ver en el post &quot;Problemas en Vistas Materializadas - Parte 1&quot;, podemos identificar con el paquete DBMS_MVIEW (procedimiento EXPLAIN_MVIEW), los problemas que pueden haber en nuestra Vista Materializada e implementar las soluciones necesarias. La contra de ese procedimiento es que nos muestra los errores que tenemos en la Vista Materializada, pero no nos dice cómo solucionarlos.&lt;br /&gt;&lt;br /&gt;En Oracle 10g se introduce el paquete DBMS_ADVISOR (procedimiento TUNE_MVIEW) que nos dice qué cambios debemos implementar en nuestra Vista Materializada para soportar las capacidades que necesitamos.&lt;br /&gt;&lt;br /&gt;Veamos un ejemplo:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE TABLE test1 AS&lt;br /&gt;  2  SELECT level id, level*level total&lt;br /&gt;  3  FROM dual&lt;br /&gt;  4  CONNECT BY level &lt;= 10 ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE TABLE test2 AS&lt;br /&gt;  2  SELECT level id, &#39;nom_&#39;||level nom&lt;br /&gt;  3  FROM dual&lt;br /&gt;  4  CONNECT BY level &lt;= 20 ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; COMMIT ;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; ALTER TABLE test2 ADD CONSTRAINT id_2_pk PRIMARY KEY (id) ;&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; EXEC DBMS_STATS.GATHER_TABLE_STATS(user, &#39;TEST1&#39;) ;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; EXEC DBMS_STATS.GATHER_TABLE_STATS(user, &#39;TEST2&#39;) ;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT test2.id,&lt;br /&gt;  2         test2.nom,&lt;br /&gt;  3         SUM(test1.total) sum_total&lt;br /&gt;  4  FROM test1,&lt;br /&gt;  5       test2&lt;br /&gt;  6  WHERE test1.id = test2.id(+)&lt;br /&gt;  7  GROUP BY test2.id,&lt;br /&gt;  8           test2.nom&lt;br /&gt;  9  ORDER BY id ;&lt;br /&gt;&lt;br /&gt;        ID NOM         SUM_TOTAL&lt;br /&gt;---------- ---------- ----------&lt;br /&gt;         1 nom_1               1&lt;br /&gt;         2 nom_2               4&lt;br /&gt;         3 nom_3               9&lt;br /&gt;         4 nom_4              16&lt;br /&gt;         5 nom_5              25&lt;br /&gt;         6 nom_6              36&lt;br /&gt;         7 nom_7              49&lt;br /&gt;         8 nom_8              64&lt;br /&gt;         9 nom_9              81&lt;br /&gt;        10 nom_10            100&lt;br /&gt;&lt;br /&gt;10 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Bien, ya creé mi ambiente de prueba. Ahora voy a crear las tablas de Log necesarias para la capacidad de REFRESH FAST. Pero las voy a crear de forma errónea para demostrar el poder que nos brinda el paquete DBMS_ADVISOR.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE MATERIALIZED VIEW LOG ON test1&lt;br /&gt;  2  WITH ROWID, SEQUENCE (id) ;&lt;br /&gt;&lt;br /&gt;Materialized view log created.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE MATERIALIZED VIEW LOG ON test2&lt;br /&gt;  2  WITH ROWID, SEQUENCE (id,nom) ;&lt;br /&gt;&lt;br /&gt;Materialized view log created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Procedemos a crear la Vista Materializada:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE MATERIALIZED VIEW TEST_MV&lt;br /&gt;  2  NOCOMPRESS&lt;br /&gt;  3  LOGGING&lt;br /&gt;  4  BUILD IMMEDIATE&lt;br /&gt;  5  USING INDEX&lt;br /&gt;  6  REFRESH FAST ON DEMAND&lt;br /&gt;  7  USING DEFAULT LOCAL ROLLBACK SEGMENT&lt;br /&gt;  8  DISABLE QUERY REWRITE&lt;br /&gt;  9  AS&lt;br /&gt; 10  SELECT test2.id,&lt;br /&gt; 11         test2.nom,&lt;br /&gt; 12         SUM(test1.total) sum_total,&lt;br /&gt; 13         COUNT(*) cnt,&lt;br /&gt; 14         COUNT(test1.total) cnt_sum&lt;br /&gt; 15  FROM test1,&lt;br /&gt; 16       test2&lt;br /&gt; 17  WHERE test1.id = test2.id(+)&lt;br /&gt; 18  GROUP BY test2.id,&lt;br /&gt; 19           test2.nom&lt;br /&gt; 20  ORDER BY id ;&lt;br /&gt;FROM test1,&lt;br /&gt;     *&lt;br /&gt;ERROR at line 15:&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;ORA-32401: materialized view log on &quot;CDW&quot;.&quot;TEST2&quot; does not have new values&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Efectivamente como supusimos, tenemos errores en las tablas de Log que creamos.&lt;br /&gt;Ahora vamos a ejecutar DBMS_ADVISOR.TUNE_MVIEW y ver los resultados en la tabla USER_TUNE_MVIEW...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; DECLARE&lt;br /&gt;  2  l_nombre  VARCHAR2(100) := &#39;test_1&#39; ;&lt;br /&gt;  3  BEGIN&lt;br /&gt;  4  &lt;span style=&quot;font-weight:bold;&quot;&gt;DBMS_ADVISOR.TUNE_MVIEW&lt;/span&gt;(l_nombre, &#39;CREATE MATERIALIZED VIEW TEST_MV&lt;br /&gt;  5  NOCOMPRESS&lt;br /&gt;  6  LOGGING&lt;br /&gt;  7  BUILD IMMEDIATE&lt;br /&gt;  8  USING INDEX&lt;br /&gt;  9  REFRESH FAST ON DEMAND&lt;br /&gt; 10  USING DEFAULT LOCAL ROLLBACK SEGMENT&lt;br /&gt; 11  DISABLE QUERY REWRITE&lt;br /&gt; 12  AS&lt;br /&gt; 13  SELECT test2.id,&lt;br /&gt; 14         test2.nom,&lt;br /&gt; 15         SUM(test1.total) sum_total,&lt;br /&gt; 16         COUNT(*) cnt,&lt;br /&gt; 17         COUNT(test1.total) cnt_sum&lt;br /&gt; 18  FROM test1,&lt;br /&gt; 19       test2&lt;br /&gt; 20  WHERE test1.id = test2.id(+)&lt;br /&gt; 21  GROUP BY test2.id,&lt;br /&gt; 22           test2.nom&lt;br /&gt; 23  ORDER BY id&#39;) ;&lt;br /&gt; 24  END ;&lt;br /&gt; 25  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT *&lt;br /&gt;  2  FROM user_tune_mview&lt;br /&gt;  3  WHERE task_name = &#39;test_1&#39; ;&lt;br /&gt;&lt;br /&gt;TASK_NAME        ACTION_ID SCRIPT_TYPE    STATEMENT&lt;br /&gt;--------------- ---------- -------------- -----------------------------------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;test_1                   1 IMPLEMENTATION ALTER MATERIALIZED VIEW LOG FORCE O&lt;br /&gt;                                          N &quot;CDW&quot;.&quot;TEST2&quot; ADD ROWID, SEQUENCE&lt;br /&gt;                                           (&quot;ID&quot;,&quot;NOM&quot;)  INCLUDING NEW VALUES&lt;br /&gt;&lt;br /&gt;test_1                   2 IMPLEMENTATION ALTER MATERIALIZED VIEW LOG FORCE O&lt;br /&gt;                                          N &quot;CDW&quot;.&quot;TEST1&quot; ADD ROWID, SEQUENCE&lt;br /&gt;                                           (&quot;ID&quot;,&quot;TOTAL&quot;)  INCLUDING NEW VALU&lt;br /&gt;                                          ES&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;test_1                   3 IMPLEMENTATION CREATE MATERIALIZED VIEW CDW.TEST_M&lt;br /&gt;                                          V NOCOMPRESS&lt;br /&gt;                                          LOGGING&lt;br /&gt;                                          BUILD IMMEDIATE&lt;br /&gt;                                          USING INDEX&lt;br /&gt;                                           REFRESH FAST WITH ROWID DISABLE QU&lt;br /&gt;                                          ERY REWRITE AS SELECT test2.id,&lt;br /&gt;                                                 test2.nom,&lt;br /&gt;                                                 SUM(test1.total) sum_total,&lt;br /&gt;                                                 COUNT(*) cnt,&lt;br /&gt;                                                 COUNT(test1.total) cnt_sum&lt;br /&gt;                                          FROM test1,&lt;br /&gt;                                               test2&lt;br /&gt;                                          WHERE test1.id = test2.id(+)&lt;br /&gt;                                          GROUP BY test2.id,&lt;br /&gt;                                                   test2.nom&lt;br /&gt;                                          ORDER BY id&lt;br /&gt;&lt;br /&gt;test_1                   4 UNDO           DROP MATERIALIZED VIEW CDW.TEST_MV&lt;br /&gt;&lt;br /&gt;4 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;La columna SCRIPT_TYPE nos muestra el tipo de recomendación. Deberíamos ver las columnas con el valor &#39;IMPLEMENTED&#39; (la recomendación ya está implementada); pero en cambio, estamos viendo que aparecen como &#39;IMPLEMENTATION&#39; (la recomendación NO está implementada).&lt;br /&gt;El próximo paso es ejecutar los script recomendados...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; ALTER MATERIALIZED VIEW LOG FORCE ON &quot;CDW&quot;.&quot;TEST2&quot;&lt;br /&gt;  2  ADD ROWID, SEQUENCE(&quot;ID&quot;,&quot;NOM&quot;)  INCLUDING NEW VALUES ;&lt;br /&gt;&lt;br /&gt;Materialized view log altered.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; ALTER MATERIALIZED VIEW LOG FORCE ON &quot;CDW&quot;.&quot;TEST1&quot;&lt;br /&gt;  2  ADD ROWID, SEQUENCE(&quot;ID&quot;,&quot;TOTAL&quot;)  INCLUDING NEW VALUES ;&lt;br /&gt;&lt;br /&gt;Materialized view log altered.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE MATERIALIZED VIEW TEST_MV&lt;br /&gt;  2  NOCOMPRESS&lt;br /&gt;  3  LOGGING&lt;br /&gt;  4  BUILD IMMEDIATE&lt;br /&gt;  5  USING INDEX&lt;br /&gt;  6  REFRESH FAST ON DEMAND&lt;br /&gt;  7  USING DEFAULT LOCAL ROLLBACK SEGMENT&lt;br /&gt;  8  DISABLE QUERY REWRITE&lt;br /&gt;  9  AS&lt;br /&gt; 10  SELECT test2.id,&lt;br /&gt; 11         test2.nom,&lt;br /&gt; 12         SUM(test1.total) sum_total,&lt;br /&gt; 13         COUNT(*) cnt,&lt;br /&gt; 14         COUNT(test1.total) cnt_sum&lt;br /&gt; 15  FROM test1,&lt;br /&gt; 16       test2&lt;br /&gt; 17  WHERE test1.id = test2.id(+)&lt;br /&gt; 18  GROUP BY test2.id,&lt;br /&gt; 19           test2.nom&lt;br /&gt; 20  ORDER BY id ;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Materialized view created.&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ejecutando las recomendaciones de la tabla, pudimos solucionar rápidamente el problema que teníamos en la Vista Materializada.&lt;br /&gt;&lt;br /&gt;Qué sucede si ejecutamos el DBMS_ADVISOR.TUNE_MVIEW nuevamente?&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; DECLARE&lt;br /&gt;  2  l_nombre  VARCHAR2(100) := &#39;test_1&#39; ;&lt;br /&gt;  3  BEGIN&lt;br /&gt;  4  DBMS_ADVISOR.TUNE_MVIEW(l_nombre, &#39;CREATE MATERIALIZED VIEW TEST_MV&lt;br /&gt;  5  NOCOMPRESS&lt;br /&gt;  6  LOGGING&lt;br /&gt;  7  BUILD IMMEDIATE&lt;br /&gt;  8  USING INDEX&lt;br /&gt;  9  REFRESH FAST ON DEMAND&lt;br /&gt; 10  USING DEFAULT LOCAL ROLLBACK SEGMENT&lt;br /&gt; 11  DISABLE QUERY REWRITE&lt;br /&gt; 12  AS&lt;br /&gt; 13  SELECT test2.id,&lt;br /&gt; 14         test2.nom,&lt;br /&gt; 15         SUM(test1.total) sum_total,&lt;br /&gt; 16         COUNT(*) cnt,&lt;br /&gt; 17         COUNT(test1.total) cnt_sum&lt;br /&gt; 18  FROM test1,&lt;br /&gt; 19       test2&lt;br /&gt; 20  WHERE test1.id = test2.id(+)&lt;br /&gt; 21  GROUP BY test2.id,&lt;br /&gt; 22           test2.nom&lt;br /&gt; 23  ORDER BY id&#39;) ;&lt;br /&gt; 24  END ;&lt;br /&gt; 25  /&lt;br /&gt;DECLARE&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-13600: error encountered in Advisor&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;QSM-03116: The materialized view is already optimal and cannot be tuned any further&lt;/span&gt;&lt;br /&gt;ORA-06512: at &quot;SYS.DBMS_SYS_ERROR&quot;, line 86&lt;br /&gt;ORA-06512: at &quot;SYS.PRVT_ACCESS_ADVISOR&quot;, line 202&lt;br /&gt;ORA-06512: at &quot;SYS.PRVT_TUNE_MVIEW&quot;, line 1042&lt;br /&gt;ORA-06512: at &quot;SYS.DBMS_ADVISOR&quot;, line 754&lt;br /&gt;ORA-06512: at line 4&lt;br /&gt;&lt;br /&gt;[TEST@TEST_10GR2 ~]$ oerr qsm 03116&lt;br /&gt;03116, 00000, &quot;The materialized view is already optimal and cannot be tuned any further&quot;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;// *Cause:   The materialized view has the capabilities that are specified in&lt;br /&gt;//           the statement.&lt;br /&gt;// *Action:  none&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Oracle nos está diciendo que el script de creación de la Vista Materializada que nosotros ejecutamos, ya cuenta con todas las capacidades necesarias para su creación, y por lo tanto, ya se encuentra óptima y no se puede seguir tuneando.</description><link>http://lhorikian.blogspot.com/2007/09/problemas-en-vistas-materializadas.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>13</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-9146087600416776061</guid><pubDate>Fri, 21 Sep 2007 18:56:00 +0000</pubDate><atom:updated>2007-09-24T11:08:50.033-03:00</atom:updated><title>Exchange Partition</title><description>Exchange Partition permite cargar en tablas particionadas datos en forma rápida y con muy poco impacto para los usuarios que se encuentran activos.&lt;br /&gt;En resumen, lo que hace la sentencia Exchange Partition es modificar el diccionario de datos y simular que los datos que ya tenemos cargados en una tabla, corresponden a una partición determinada de otra tabla.&lt;br /&gt;&lt;br /&gt;Veamos un ejemplo muy sencillo para entender mejor éste tema:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE TABLE datos_1 AS&lt;br /&gt;  2  SELECT level id, timestamp&#39;2000-11-02 09:00:00&#39; fecha&lt;br /&gt;  3  FROM dual&lt;br /&gt;  4  CONNECT BY level &lt;= 100000 ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:01.06&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE TABLE datos_2 AS&lt;br /&gt;  2  SELECT level id, timestamp&#39;2001-09-10 13:00:00&#39; fecha&lt;br /&gt;  3  FROM dual&lt;br /&gt;  4  CONNECT BY level &lt;= 100000 ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Lo que hicimos fue crear 2 tablas con distintas fechas en cada una de ellas.&lt;br /&gt;&lt;br /&gt;Ahora creamos solamente la estructura de la tabla particionada en donde vamos a cargar los datos:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE TABLE test&lt;br /&gt;  2  ( id, fecha )&lt;br /&gt;  3  PARTITION BY RANGE ( fecha )&lt;br /&gt;  4  (&lt;br /&gt;  5      PARTITION year_2000 VALUES LESS THAN ( timestamp&#39;2000-12-02 00:00:00&#39; ),&lt;br /&gt;  6      PARTITION year_2001 VALUES LESS THAN ( timestamp&#39;2001-10-10 00:00:00&#39; )&lt;br /&gt;  7  )&lt;br /&gt;  8  AS&lt;br /&gt;  9  SELECT 1, timestamp&#39;2000-11-02 09:00:00&#39;&lt;br /&gt; 10  FROM dual&lt;br /&gt; 11  WHERE 1 = 0 ;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Vamos a realizar un alter para modificar el diccionario de datos y relacionar cada  una de las 2 tablas que creamos con la respectiva partición de la tabla TEST...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; ALTER TABLE test&lt;br /&gt;  2  &lt;span style=&quot;font-weight:bold;&quot;&gt;EXCHANGE PARTITION&lt;/span&gt; year_2000&lt;br /&gt;  3  WITH table datos_1&lt;br /&gt;  4  &lt;span style=&quot;font-weight:bold;&quot;&gt;WITHOUT VALIDATION&lt;/span&gt; ;&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Elapsed: 00:00:00.03&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; ALTER TABLE test&lt;br /&gt;  2  &lt;span style=&quot;font-weight:bold;&quot;&gt;EXCHANGE PARTITION&lt;/span&gt; year_2001&lt;br /&gt;  3  WITH table datos_2&lt;br /&gt;  4  &lt;span style=&quot;font-weight:bold;&quot;&gt;WITHOUT VALIDATION&lt;/span&gt; ;&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Elapsed: 00:00:00.02&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT count(*)&lt;br /&gt;  2  FROM test ;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;    200000&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT count(*)&lt;br /&gt;  2  FROM datos_1 ;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;         0&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT count(*)&lt;br /&gt;  2  FROM datos_2 ;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;         0&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como podemos ver, con el Exchange Partition no tardamos casi nada en cargar los datos en la tabla particionada ya que en realidad no estamos cargando los datos, simplemente se modifica el diccionario de datos.&lt;br /&gt;&lt;br /&gt;Pueden notar que agregué la sentencia WITHOUT VALIDATION. Que es ésto? WITHOUT VALIDATION suele ser una operación rápida porque sólo realiza modificaciones en el diccionario de datos. Si la tabla o tabla particionada que colocamos en el Exchange Partition tiene una primary key o unique constraint habilitado, entonces el Exchange Partition se realiza como WITH VALIDATION para mantener la integridad de las constraints.&lt;br /&gt; &lt;br /&gt;Vamos a ejecutar nuevamente los 2 alter anteriores sin la sentencia WITHOUT VALIDATION...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; ALTER TABLE test&lt;br /&gt;  2  &lt;span style=&quot;font-weight:bold;&quot;&gt;EXCHANGE PARTITION&lt;/span&gt; year_2000&lt;br /&gt;  3  WITH table datos_1 ;&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Elapsed: 00:00:01.00&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; ALTER TABLE test&lt;br /&gt;  2  &lt;span style=&quot;font-weight:bold;&quot;&gt;EXCHANGE PARTITION&lt;/span&gt; year_2001&lt;br /&gt;  3  WITH table datos_2 ;&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Elapsed: 00:00:01.05&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Si hubiera ejecutado esos alter con un Trace, el reporte del Trace me mostraría, entre otras sentencias, las siguientes...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;select 1&lt;br /&gt;from&lt;br /&gt; &quot;DATOS_1&quot; where TBL$OR$IDX$PART$NUM(&quot;TEST&quot;, 0, 3,1048576,&quot;FECHA&quot;) != :1&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.00          0          0          0           0&lt;br /&gt;Execute      1      0.00       0.00          0          1          0           0&lt;br /&gt;Fetch        1      0.04       0.04          0         65          0           0&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;total        3      0.04       0.04          0         66          0           0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Misses in library cache during parse: 1&lt;br /&gt;Misses in library cache during execute: 1&lt;br /&gt;Optimizer mode: ALL_ROWS&lt;br /&gt;Parsing user id: 81     (recursive depth: 1)&lt;br /&gt;&lt;br /&gt;Rows     Row Source Operation&lt;br /&gt;-------  ---------------------------------------------------&lt;br /&gt;      0  TABLE ACCESS FULL DATOS_1 (cr=65 pr=0 pw=0 time=44582 us)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;select 1&lt;br /&gt;from&lt;br /&gt; &quot;DATOS_2&quot; where TBL$OR$IDX$PART$NUM(&quot;TEST&quot;, 0, 3,1048576,&quot;FECHA&quot;) != :1&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.00          0          0          0           0&lt;br /&gt;Execute      1      0.00       0.00          0          1          0           0&lt;br /&gt;Fetch        1      0.04       0.04          0         65          0           0&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;total        3      0.04       0.04          0         66          0           0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Misses in library cache during parse: 1&lt;br /&gt;Misses in library cache during execute: 1&lt;br /&gt;Optimizer mode: ALL_ROWS&lt;br /&gt;Parsing user id: 81     (recursive depth: 1)&lt;br /&gt;&lt;br /&gt;Rows     Row Source Operation&lt;br /&gt;-------  ---------------------------------------------------&lt;br /&gt;      0  TABLE ACCESS FULL DATOS_2 (cr=65 pr=0 pw=0 time=46957 us)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Antes que nada, notamos que los alter se ejecutaron en mayor tiempo, cierto? De las consultas que observamos del Trace, vemos que se está realizando un FULL SCAN de las tablas y que se está ejecutando una función en el WHERE de cada consulta. Imagínense si tenemos que realizar ésta clase de procesos en ambientes con gran volumen de datos y en donde el sistema se encuentra saturado por el I/O a disco.&lt;br /&gt;&lt;br /&gt;Qué sucede si no tenemos los datos separados por año en distintas tablas, y en cambio, tenemos todos los datos en una misma tabla? Bueno, tomando como ejemplo la tabla TEST que acabamos de cargar, podríamos realizar lo siguiente...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_10gR2&gt; CREATE TABLE test_2&lt;br /&gt;  2  ( id, fecha )&lt;br /&gt;  3  PARTITION BY RANGE ( fecha )&lt;br /&gt;  4  (&lt;br /&gt;  5      PARTITION year_2000 VALUES LESS THAN ( timestamp&#39;2000-12-02 00:00:00&#39; ),&lt;br /&gt;  6      PARTITION year_2001 VALUES LESS THAN ( timestamp&#39;2001-10-10 00:00:00&#39; )&lt;br /&gt;  7  )&lt;br /&gt;  8  AS&lt;br /&gt;  9  SELECT *&lt;br /&gt; 10  FROM test ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:05.04&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; DROP TABLE test ;&lt;br /&gt;&lt;br /&gt;Table dropped.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; ALTER TABLE test_2 RENAME TO test ;&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;SQL_10gR2&gt; SELECT count(*)&lt;br /&gt;  2  FROM test ;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;    200000&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;/pre&gt;</description><link>http://lhorikian.blogspot.com/2007/09/exchange-partition.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>15</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4063906708258638821.post-8314856157262862105</guid><pubDate>Thu, 20 Sep 2007 13:56:00 +0000</pubDate><atom:updated>2007-09-20T16:07:19.946-03:00</atom:updated><title>Buscar valores NULL en forma eficiente</title><description>Un problema que veo seguidamente, se relaciona con la escritura de consultas que buscan valores NULL en una tabla. Este es un problema muy común. &lt;br /&gt;A continuación vamos a ver algunas soluciones que podemos implementar para buscar los valores NULL en forma eficiente y evitar lecturas innecesarias de bloques de datos. &lt;br /&gt;&lt;br /&gt;Veamos algunos ejemplos:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; CREATE TABLE test AS&lt;br /&gt;  2  SELECT decode(mod(rownum,100),0,null,level) id, &#39;nom_&#39;||level nom&lt;br /&gt;  3  FROM dual&lt;br /&gt;  4  CONNECT BY level &lt;= 1000000 ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; EXEC dbms_stats.GATHER_TABLE_STATS(USER,&#39;TEST&#39;) ;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Veamos la cantidad de valores NULL que tiene la tabla que creamos:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; SELECT count(*)&lt;br /&gt;  2  FROM test&lt;br /&gt;  3  WHERE id IS NULL ;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;     10000&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Veamos cómo se ejecuta internamente esa consulta:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; explain plan for&lt;br /&gt;  2  SELECT count(*)&lt;br /&gt;  3  FROM test&lt;br /&gt;  4  WHERE id IS NULL ;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; @explains&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------&lt;br /&gt;| Id  | Operation            |  Name       | Rows  | Bytes | Cost  |&lt;br /&gt;--------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT     |             |     1 |     7 |   304 |&lt;br /&gt;|   1 |  SORT AGGREGATE      |             |     1 |     7 |       |&lt;br /&gt;|*  2 |  &lt;span style=&quot;font-weight:bold;&quot;&gt; TABLE ACCESS FULL  | TEST        | 10000 | 70000 |   304&lt;/span&gt; |&lt;br /&gt;--------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - filter(&quot;TEST&quot;.&quot;ID&quot; IS NULL)&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.00          0          0          0           0&lt;br /&gt;Execute      1      0.00       0.00          0          0          0           0&lt;br /&gt;Fetch        2      0.16       0.16       3144       3150          0           1&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;total        4      0.17       0.16       3144       3150          0           1&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Observamos que obviamente está realizando un Full Scan. Qué sucede si creamos un índice B*Tree?&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; CREATE INDEX test_id_idx ON test(id) ;&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; EXEC dbms_stats.GATHER_TABLE_STATS(USER,&#39;TEST&#39;,cascade=&gt;true) ;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; explain plan for&lt;br /&gt;  2  SELECT count(*)&lt;br /&gt;  3  FROM test&lt;br /&gt;  4  WHERE id IS NULL ;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; @explains&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------&lt;br /&gt;| Id  | Operation            |  Name       | Rows  | Bytes | Cost  |&lt;br /&gt;--------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT     |             |     1 |     7 |   304 |&lt;br /&gt;|   1 |  SORT AGGREGATE      |             |     1 |     7 |       |&lt;br /&gt;|*  2 |   &lt;span style=&quot;font-weight:bold;&quot;&gt;TABLE ACCESS FULL  | TEST        | 10000 | 70000 |   304&lt;/span&gt; |&lt;br /&gt;--------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - filter(&quot;TEST&quot;.&quot;ID&quot; IS NULL)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Porqué nuestra consulta no está accediendo a través del índice? por la simple razón de que los índices B*Tree no indexan los valores NULL. Como estamos realizando un COUNT buscando la cantidad de valores NULL de la tabla, el CBO sabe que si accede por índice, la consulta nos va a retornar un resultado erróneo ya que no existen valores NULL en el índice. Es por eso, que en vez de acceder por índice, realiza un Full Scan de la tabla. Esto tiene como consecuencia una lectura innecesaria de bloques por no haber accedido a través de un índice.&lt;br /&gt;&lt;br /&gt;Una solución para éste problema sería crear un FBI (Function Based-Index) para indexar sólo los valores NULL de la columna:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; CREATE INDEX test_id_nulo_idx ON test(NVL(id,&#39;nulo&#39;)) ;&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; EXEC dbms_stats.GATHER_TABLE_STATS(USER,&#39;TEST&#39;,cascade=&gt;true) ;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; explain plan for&lt;br /&gt;  2  SELECT count(*)&lt;br /&gt;  3  FROM test&lt;br /&gt;  4  WHERE &lt;span style=&quot;font-weight:bold;&quot;&gt;NVL(id,&#39;nulo&#39;) = &#39;nulo&#39;&lt;/span&gt; ;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; @explains&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation            |  Name             | Rows  | Bytes | Cost  |&lt;br /&gt;--------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT     |                   |     1 |     7 |    27 |&lt;br /&gt;|   1 |  SORT AGGREGATE      |                   |     1 |     7 |       |&lt;br /&gt;|*  2 |   &lt;span style=&quot;font-weight:bold;&quot;&gt;INDEX RANGE SCAN   | TEST_ID_NULO_IDX  | 10001 | 70007 |    27&lt;/span&gt; |&lt;br /&gt;--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - access(NVL(&quot;TEST&quot;.&quot;ID&quot;,&#39;nulo&#39;)=&#39;nulo&#39;)&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.00          0          0          0           0&lt;br /&gt;Execute      1      0.00       0.00          0          0          0           0&lt;br /&gt;Fetch        2      0.00       0.00          1         26          0           1&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;total        4      0.00       0.00          1         26          0           1&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Veamos qué sucede si creamos un índice Bitmap:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL_9iR2&gt; DROP INDEX test_id_idx ;&lt;br /&gt;&lt;br /&gt;Index dropped.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; CREATE BITMAP INDEX test_id_bitmap ON test(id) ;&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; EXEC dbms_stats.GATHER_TABLE_STATS(USER,&#39;TEST&#39;,cascade=&gt;true) ;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; explain plan for&lt;br /&gt;  2  SELECT count(*)&lt;br /&gt;  3  FROM test&lt;br /&gt;  4  WHERE id IS NULL ;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;SQL_9iR2&gt; @explains&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                   |  Name            | Rows  | Bytes | Cost  |&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT            |                  |     1 |     7 |    &lt;span style=&quot;font-weight:bold;&quot;&gt;44&lt;/span&gt; |&lt;br /&gt;|   1 |  SORT AGGREGATE             |                  |     1 |     7 |       |&lt;br /&gt;|   2 |   BITMAP CONVERSION COUNT   |                  |       |       |       |&lt;br /&gt;|*  3 |    &lt;span style=&quot;font-weight:bold;&quot;&gt;BITMAP INDEX SINGLE VALUE| TEST_ID_BITMAP&lt;/span&gt;   |       |       |       |&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   3 - access(&quot;TEST&quot;.&quot;ID&quot; IS NULL)&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.00          0          0          0           0&lt;br /&gt;Execute      1      0.00       0.00          0          0          0           0&lt;br /&gt;Fetch        2      0.00       0.00          1          5          0           1&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;total        4      0.00       0.00          1          5          0           1&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Observamos que también podemos utilizar índices Bitmap para buscar valores NULL ya que ésta clase de índices SI indexan éstos valores. &lt;br /&gt;Lo importante a tener en cuenta si utilizamos éste índice es que cada modificación que se realiza sobre el índice, requiere un gran trabajo del sistema que si utilizamos índices B*tree. Por otro lado, si a ésto le sumamos las modificaciones concurrentes que se realizan sobre la columna indexada, puede llegar a ser mortal para el sistema.</description><link>http://lhorikian.blogspot.com/2007/09/buscar-valores-null-en-forma-eficiente.html</link><author>noreply@blogger.com (Leonardo Horikian)</author><thr:total>18</thr:total></item></channel></rss>