tag:blogger.com,1999:blog-31925621197385993052024-03-08T01:42:57.408-08:00Oracle Database TutorialNuestra misión es transmitir conocimientos sobre la base de datos Oracle. Nos motivan las consultas cotidianas de nuestros compañeros y usuarios orientadas al dominio de este arte.Unknownnoreply@blogger.comBlogger20125tag:blogger.com,1999:blog-3192562119738599305.post-16136958331749690742007-12-21T11:45:00.000-08:002007-12-21T03:31:03.253-08:00Valor NULL en sentencia IFNecesitamos evaluar si contenido de una variable denominada <tt>nombre</tt> contiene la cadena de caracteres <tt>'JAVIER'</tt>. <br />En el primer ejemplo asignamos a la variable <tt>nombre</tt> el valor <tt>NULL</tt>. La condición <tt><span style="color:red">nombre != 'JAVIER'</span></tt> se evalúa como <tt><span style="color:red">NULL != 'JAVIER'</span></tt> y resulta FALSA! El valor <tt>NULL</tt> es distinto de la cadena de caracteres 'JAVIER', sin embargo la ejecución se bifurca por la rama del <tt>ELSE</tt>. Se muestra el mensaje <tt><span style="color:green">'CONDICION FALSA, SON IGUALES. EJECUTA ELSE.'</span></tt>. <strong>Dónde está el error?</strong><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> RUN<br /> 1 DECLARE<br /> 2 <span style="color:green">nombre VARCHAR2(40) := NULL; </span><br /> 3 BEGIN<br /> 4 IF <span style="color:red">nombre != 'JAVIER'</span> THEN<br /> 5 DBMS_OUTPUT.PUT_LINE('CONDICION VERDADERA, SON DISTINTOS. EJECUTA THEN.');<br /> 6 ELSE<br /> 7 DBMS_OUTPUT.PUT_LINE('CONDICION FALSA, SON IGUALES. EJECUTA ELSE.');<br /> 8 END IF;<br /> 9* END;<br /> <br /><span style="color:green">CONDICION FALSA, SON IGUALES. EJECUTA ELSE.</span><br /> <br />Procedimiento PL/SQL terminado con éxito.</pre></span><br />En el segundo ejemplo asignamos a la variable <tt>nombre</tt> la cadena de caracteres <tt>'NULL'</tt>. La condición <tt><span style="color:red">nombre != 'JAVIER'</span></tt> se evalúa como una comparación entre dos cadenas de caracteres <tt><span style="color:red">'NULL' != 'JAVIER'</span></tt> y resulta VERDADERA! Se muestra el mensaje <tt><span style="color:green">'CONDICION VERDADERA, SON DISTINTOS. EJECUTA THEN.'</span></tt>.<span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> RUN<br /> 1 DECLARE<br /> 2 <span style="color:green">nombre VARCHAR2(40) := 'NULL';</span><br /> 3 BEGIN<br /> 4 IF <span style="color:red">nombre != 'JAVIER'</span> THEN<br /> 5 DBMS_OUTPUT.PUT_LINE('CONDICION VERDADERA, SON DISTINTOS. EJECUTA THEN.');<br /> 6 ELSE<br /> 7 DBMS_OUTPUT.PUT_LINE('CONDICION FALSA, SON IGUALES. EJECUTA ELSE.');<br /> 8 END IF;<br /> 9* END;<br /> <br /><span style="color:green">CONDICION VERDADERA, SON DISTINTOS. EJECUTA THEN.</span><br /> <br />Procedimiento PL/SQL terminado con éxito.</pre></span><br /><strong>La clave es entender que la evaluación de condiciones que contienen al menos un elemento con valor <tt>NULL</tt>, SIEMPRE resultan FALSAS. La única excepción es la cláusula <tt>IS NULL</tt>.</strong><br /><br /><tt><span style="color:red">NULL != 'JAVIER'</span></tt> es <strong>FALSO</strong><br /><tt><span style="color:red">NULL = 'JAVIER'</span></tt> es <strong>FALSO</strong><br /><tt><span style="color:red">NULL = NULL</span></tt> es <strong>FALSO</strong><br /><br /><tt><span style="color:red">NULL IS NULL</span></tt> es <strong>VERDADERO</strong><br /><tt><span style="color:red">NULL IS NOT NULL</span></tt> es <strong>FALSO</strong><br /><br />En el tercer ejemplo tomamos precauciones para no repetir el comportamiento inesperado del primer ejemplo. Modificamos la línea 4 de evaluación de la condición agregando la función NVL que reemplazará los valores nulos de la variable <tt>nombre</tt> por la cadena de caracteres 'VALOR NULO'. Ahora si, la condición <tt><span style="color:red">'VALOR NULO' != 'JAVIER'</span></tt> es VERDADERA.<span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> RUN<br /> 1 DECLARE<br /> 2 <span style="color:green">nombre VARCHAR2(40) := NULL;</span><br /> 3 BEGIN<br /> 4 IF <span style="color:red">NVL(nombre,'VALOR NULO') != 'JAVIER'</span> THEN<br /> 5 DBMS_OUTPUT.PUT_LINE('CONDICION VERDADERA, SON DISTINTOS. EJECUTA THEN.');<br /> 6 ELSE<br /> 7 DBMS_OUTPUT.PUT_LINE('CONDICION FALSA, SON IGUALES. EJECUTA ELSE.');<br /> 8 END IF;<br /> 9* END;<br /> <br /><span style="color:green">CONDICION VERDADERA, SON DISTINTOS. EJECUTA THEN.</span><br /> <br />Procedimiento PL/SQL terminado con éxito.</pre></span>Unknownnoreply@blogger.com8tag:blogger.com,1999:blog-3192562119738599305.post-55100647075563245532007-11-24T10:29:00.000-08:002007-12-04T18:21:43.574-08:00De FULL TABLE SCAN a INDEX RANGE SCANNecesitamos reducir el tiempo de ejecución de un <tt>SELECT</tt> que accede a una tabla filtrando filas por la columna definida <tt>PRIMIRY KEY</tt> que posee un índice único.<br />La tabla <tt>ESTADO_APLICACION</tt> posee una fila por cada medición de disponibilidad de una aplicación. Cada fila de la tabla detalla la fecha y hora de medición (columna <tt>FECHA</tt>) y el valor "ACTIVA" o "INACTIVA" (columna <tt>ESTADO</tt>) según correponda al resultado del proceso de verificación de disponibilidad de la aplicación.<br />Veamos un ejemplo del contenido de esta tabla y las disponibilidades registradas:<br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> RUN<br /> 1 SELECT TO_CHAR(FECHA,'DD/MM/YYYY HH24:MI:SS') AS FECHA,<br /> 2 ESTADO<br /> 3 FROM <span style="color:green">ESTADO_APLICACION</span><br /> 4* WHERE ROWNUM < 11<br /> <br />FECHA ESTADO<br />------------------- ----------------<br />23/11/2007 20:24:34 ACTIVA<br />23/11/2007 20:23:34 INACTIVA<br />23/11/2007 20:22:34 INACTIVA<br />23/11/2007 20:21:34 INACTIVA<br />23/11/2007 20:20:34 ACTIVA<br />23/11/2007 20:19:34 ACTIVA<br />23/11/2007 20:18:34 ACTIVA<br />23/11/2007 20:17:34 ACTIVA<br />23/11/2007 20:16:34 ACTIVA<br />23/11/2007 20:15:34 ACTIVA<br /> <br />10 filas seleccionadas.</pre></span>El objetivo de la sentencia <tt>SELECT</tt> es contar la cantidad de filas con fecha 23/11/2007. La columna <tt>FECHA</tt> posee un índice único y la sentencia <tt>SELECT</tt> filtra las filas seleccionadas por esta misma columna. Sin embargo el optimizador de ejecución de consultas decide recorrer toda la tabla para recuperar las filas seleccionadas. Ejecutamos la sentencia <tt>SELECT</tt> sin recuperar las filas pero mostrando el plan de ejecución del optimizador para recuperarlas configurando <span style="color:#3333ff"><tt>SET AUTOTRACE TRACEONLY EXPLAIN</tt></span>. Podemos observar que se accederá de manera <span style="color:red"><tt>TABLE ACCESS FULL</tt></span> al la tabla <tt><span style="color:green">ESTADO_APLICACION</span></tt> para ejecutar la sentencia <TT>SELECT</TT>. Es decir, se leerán en forma secuencial todos los bloques de datos asociados a las filas de la tabla y se evaluará el criterio de selección a cada fila. Estas lecturas pueden representar un gran volumen de transferencias de datos de entrada aumentando el tiempo de ejecución de la sentencia <tt>SELECT</tt>.<br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre><strong>SQL> SET AUTOTRACE TRACEONLY EXPLAIN</strong><br /> <br />SQL> RUN<br /> 1 SELECT COUNT(*)<br /> 2 FROM <span style="color:green">ESTADO_APLICACION</span><br /> 3* WHERE <span style="color:red">TO_CHAR(FECHA,'DD/MM/YYYY') = '23/11/2007'</span><br /> <br />Plan de Ejecucion<br />----------------------------------------------------------<br />Plan hash value: 111243894<br /> <br />-----------------------------------------------------------------------------<br />| Id|Operation | Name |Rows|Bytes|Cost (%CPU)|Time |<br />-----------------------------------------------------------------------------<br />| 0|SELECT STATEMENT | | 1| 9| 24 (5)|00:00:01|<br />| 1| SORT AGGREGATE | | 1| 9| | |<br />|* 2|<span style="color:red"> TABLE ACCESS FULL</span>|<span style="color:green">ESTADO_APLICACION</span> |1829|16461| 24 (5)|00:00:01|<br />-----------------------------------------------------------------------------</pre></span>El optimizador de ejecución de sentencias no puede utilizar el índice sobre la columna <tt>FECHA</tt> evaluada por el criterio de selección porque esta columna es convertida del tipo de dato original de la tabla <tt>DATE</tt> a <tt>VARCHAR2</tt> con una función <tt>TO_CHAR</tt> para comparar con la cadena de caracteres <tt>'23/11/2007'</tt> y el índice almacena los valores en el tipo <tt>DATE</tt>.<br />Modificamos la sentencia para no utilizar una función sobre la columna <tt>FECHA</tt> y asi poder evaluar el criterio de seleccion sobre los valores contenidos en el índice de la <tt>PRIMARY KEY</tt>. Para esto filtramos las filas con valores entre el 23 de noviembre de 2007 a la hora 00:00:00 y el 23 de noviembre a la hora 23:59:59 ambos límites definidos en tipos de dato <tt>DATE</tt> y comparados con una columna de tipo de dato <tt>DATE</tt>. Mostramos el plan de ejecución de la sentencia modificada y podemos ver que la forma de acceso ahora es <tt><span style="color:red">INDEX RANGE SCAN</span></tt> al índice <tt><span style="color:green">ESTADO_APLICACION_PK</span></tt>.<br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre><strong>SQL> SET AUTOTRACE TRACEONLY EXPLAIN</strong><br />SQL> RUN<br /> 1 SELECT COUNT(*)<br /> 2 FROM <span style="color:green">ESTADO_APLICACION</span><br /> 3 WHERE <span style="color:red">FECHA BETWEEN TO_DATE('23/11/2007 00:00:00','DD/MM/YYYY HH24:MI:SS')</span><br /> 4* <span style="color:red">AND TO_DATE('23/11/2007 23:59:59','DD/MM/YYYY HH24:MI:SS')</span><br /><br />Plan de EjecuciOn<br />----------------------------------------------------------<br />Plan hash value: 4117644086<br /><br />-----------------------------------------------------------------------------<br />|Id|Operation | Name |Rows|Bytes|Cost (%CPU)|Time |<br />-----------------------------------------------------------------------------<br />| 0| SELECT STATEMENT | | 1| 8| 8 (0)|00:00:01|<br />| 1| SORT AGGREGATE | | 1| 8| | |<br />|*2| <span style="color:red">INDEX RANGE SCAN</span>|<span style="color:green">ESTADO_APLICACION_PK</span>|1441|11528| 8 (0)|00:00:01|<br />-----------------------------------------------------------------------------</pre></span>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-3192562119738599305.post-4388953321314913452007-11-02T21:13:00.000-07:002007-11-24T10:36:55.146-08:00Variables de ambiente, binds y sqlplusQueremos realizar múltiples ejecuciones de un Unix shell script que a su vez ejecuta una sentencia SQL. Deseamos facilitar la reutilización de recursos de la base de datos asociados a la sentencia SQL y lograr una performance de ejecución razonable.<br /><br />Los valores variables de la cláusula <tt>WHERE</tt> que utilizamos para filtrar las filas seleccionadas los representamos con <strong>bind variables</strong> o <strong>variables de enlace</strong>. Es importante no utilizar valores fijos en lugar de variables. De esta manera facilitamos al motor de base de datos el reconocimiento de la múltiple ejecución de la misma sentencia SQL con distintas condiciones y podrá reutilizar recursos asignados en la primera ejecución de la sentencia y saltear algunos pasos en las ejecuciones siguientes.<br /><br />Listamos el fichero que contiene el shell script (comando <tt>ls</tt>) y mostramos el contenido (comando <tt>cat</tt>).<br />Utilizamos dos variables de shell (<tt>shell_usuario</tt> y <tt>shell_numero</tt>) y asignamos su contenido a dos variables de <tt>sqlplus</tt> (<tt>sql_usuario</tt> y <tt>sql_numero</tt>) mediante el comando <tt>EXECUTE</tt>.<br />Luego ejecutamos la sentencia SQL utilizando las dos variables de <tt>sqlplus</tt> precedidas por dos puntos, es decir, <strong>bind variables</strong>.<br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>monitor@DESA] $ ls -l consulta5.sh<br />-rwxr-xr-x 1 monitor dba 356 Nov 2 18:09 consulta5.sh<br /> <br />[monitor@DESA] $ cat consulta5.sh<br />#!/bin/ksh<br /> <br /><span style="color:green">shell_usuario</span>=SYS<br /><span style="color:green">shell_numero</span>=0<br /> <br />sqlplus -s monitor/monitor <<FIN_SQL<br /> <br />VARIABLE <span style="color:red">sql_usuario</span> VARCHAR2(30)<br />VARIABLE <span style="color:red">sql_numero</span> NUMBER<br /> <br />EXECUTE <span style="color:red">:sql_usuario</span> := <span style="color:green">'${shell_usuario}'</span><br />EXECUTE <span style="color:red">:sql_numero</span> := <span style="color:green">${shell_numero}</span><br /> <br />SELECT USERNAME, USER_ID, CREATED<br />FROM ALL_USERS<br />WHERE USERNAME = <span style="color:red">:sql_usuario</span><br />AND USER_ID = <span style="color:red">:sql_numero</span><br />/<br />FIN_SQL</pre></span>Ejecutamos el shell script, que ejecuta el <tt>sqlplus</tt> que realiza las dos asignaciones de variables y ejecuta la sentencia SQL. Muestra el resultado de la sentencia SQL en pantalla.<br /> <br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>monitor@DESA] $ consulta5.sh<br /> <br />PL/SQL procedure successfully completed.<br /> <br /> <br />PL/SQL procedure successfully completed.<br /> <br /> <br />USERNAME USER_ID CREATED<br />------------------------------ ---------- ----------<br />SYS 0 12/05/2002</pre></span>Podemos leer este tema en la documentación estandar de Oracle:<br /><br />Manual: SQL*Plus User's Guide and Reference<br />Capítulo: 5 Using Scripts in SQL*Plus<br /><a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14357/ch5.htm#i1211850">Título: Using Bind Variables</a>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3192562119738599305.post-51713766335744301542007-10-26T17:15:00.000-07:002007-11-21T17:35:15.097-08:00PL/SQL TutorialEsta entrada está dedicada a organizar los temas publicados asociados al tutorial de PL/SQL:<br /><br /><a href="http://databasetutorial.blogspot.com/2007/06/primer-procedimiento-plsql.html">Primer procedimiento PL/SQL</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/cursor-plsql-notacion-selectinto.html">Cursores PL/SQL notación SELECT .. INTO</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/cursores-plsql-notacin.html">Cursores PL/SQL notación SELECT .. INTO .. EXCEPTION</a><br /><a href="http://databasetutorial.blogspot.com/2007/05/create-or-replace-procedure.html">Cursores PL/SQL notación FOR .. LOOP</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql.html">Cursores PL/SQL notación CURSOR .. FOR .. LOOP</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql_13.html">Cursores PL/SQL notación OPEN .. FETCH .. CLOSE</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql_16.html">Cursores PL/SQL notación FETCH .. BULK COLLECT INTO ..</a><br /><a href="http://databasetutorial.blogspot.com/2007/07/cursores-plsql-notacin-fetch-bulk.html">Cursores PL/SQL notación FETCH .. BULK COLLECT INTO .. LIMIT</a>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3192562119738599305.post-31994044404602364352007-10-25T11:27:00.000-07:002007-12-06T04:32:17.693-08:00Unix shell script para Oracle (IV)Necesitamos determinar en el shell script si la sentencia SQL ejecutada tuvo errores. Debemos evaluar el valor de retorno del <tt>sqlplus</tt> luego de ejecutar la sentencia para saber se generaron errores.<br />Cada comando que ejecutamos devuelve al terminar un valor de retorno que es recibido por el Korn shell. Por ejemplo al ejecutar el comando <tt>ls</tt> para saber si la carpeta actual contiene el fichero de nombre <tt>fichero_inexistente.txt</tt>.<br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA] $ <strong>ls fichero_inexistente.txt</strong><br />fichero_inexistente.txt: No such file or directory</pre></span>La variable de ambiente <tt>$?</tt> nos muestra el valor retornado por el último comando ejecutado. Si el valor o estado de retorno es 0 (cero) la ejecución fue exitosa y un valor distinto de cero representa un error de ejecución. En este caso el comando <tt>ls</tt> no encontró ficheros con el nombre <tt>fichero_inexistente.txt</tt> por lo tanto retornó un valor distinto de 0 (cero).<br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA] $ <strong>echo <span style="color:green">$?</span></strong><br /><span style="color:green">2</span></pre></span>La carpeta actual contiene un fichero de nombre <tt>fichero_xx.txt</tt> por lo tanto la ejecución del comando <tt>ls</tt> es exitosa y el estado de retorno del último comando ejecutado reflejado en la variable de ambiente <tt>$?</tt> es 0 (cero).<br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA] $ <strong>ls fichero_xx.txt</strong><br />fichero_xx.txt</pre></span>Verificamos el valor de retorno del comando <tt>ls</tt>.<span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA] $ <strong>echo <span style="color:green">$?</span></strong><br /><span style="color:green">0</span></pre></span>Para consultar la base de datos utilizaremos dos scripts denominados <tt>consulta3.sh</tt> y <tt>consulta4.sh</tt>.<br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA] $ <strong>ls -l consulta3.sh consulta4.sh</strong><br />-rwxrwxrwx 1 monitor dba 178 Oct 25 15:30 consulta3.sh<br />-rwxrwxrwx 1 monitor dba 168 Oct 25 15:30 consulta4.sh</pre></span>Agregamos la línea <tt><span style="color:red">WHENEVER SQLERROR EXIT SQL.SQLCODE</span></tt> para indicar al <tt>sqlplus</tt> que <strong>en caso de producirse un error de SQL como consultar una tabla inexistente debe terminar la ejecución y retornar el código de error asociado.</strong> De esta forma el Korn shell recibe el código de error del <tt>sqlplus</tt> y puede ser consultado en la variable de ambiente <tt>$?</tt>. Luego en el shell script podemos programar acciones correctivas en caso de detectarse un error en la ejecución de la sentencia SQL.<br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA] $ <strong>cat consulta3.sh</STRONG><br />#!/bin/ksh<br /> <br />sqlplus -s monitor/colopass07 <<FIN<br /><span style="color:red">WHENEVER SQLERROR EXIT SQL.SQLCODE</span><br />SELECT 1 AS COLUMNA FROM DUAL_NO_EXISTE<br />/<br />FIN<br /> <br />echo 'consulta3.sh EL VALOR DE RETORNO <span style="color:green">$?</span> ES ' <span style="color:green">$?</span></pre></span>Ejecutamos el shell script <tt>consulta3.sh</tt> y vemos el código de error que el envía el <tt>sqlplus</tt>:<br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA] $ <strong>consulta3.sh</strong><br />SELECT 1 AS COLUMNA FROM DUAL_NO_EXISTE<br /> *<br />ERROR at line 1:<br />ORA-00942: table or view does not exist<br /><br />consulta3.sh EL VALOR DE RETORNO <span style="color:green">$?</span> ES <span style="color:green">174</span></pre></span>Si la ejecución del <tt>sqlplus</tt> no produce errores el código de retorno es cero o ejecución exitosa. Así lo podemos ver en el ejemplo de ejecución de <tt>consulta4.sh</tt> donde la variable de ambiente <tt>$?</tt> toma el valor 0 (cero) luego de la ejecución del <tt>sqlplus</tt>.<br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA] $ <strong>cat consulta4.sh</strong><br />#!/bin/ksh<br /> <br />sqlplus -s monitor/colopass07<<FIN<br /><span style="color:red">WHENEVER SQLERROR EXIT SQL.SQLCODE</span><br />SELECT 1 AS COLUMNA FROM DUAL<br />/<br />FIN<br /><br />echo 'consulta4.sh EL VALOR DE RETORNO <span style="color:green">$?</span> ES ' <span style="color:green">$?</span></pre></span>Ejecutamos el shell script <tt>consulta4.sh</tt> y vemos el código de error que envía el <tt>sqlplus</tt>:<span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA] $ <strong>consulta4.sh</strong><br /><br /> COLUMNA<br />----------<br /> 1<br /><br />consulta4.sh EL VALOR DE RETORNO <span style="color:green">$?</span> ES <span style="color:green">0</span></pre></span>Podemos leer este tema en la documentación estandar de Oracle:<br /><br />Manual: SQL*Plus User's Guide and Reference<br />Capítulo: 12 SQL*Plus Command Reference<br /><a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14357/ch12052.htm#i2700066">Párrafo: WHENEVER SQLERROR</a><br /><a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14357/ch12023.htm#i2697968">Párrafo: EXIT</a>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3192562119738599305.post-87316329433213802242007-10-21T01:01:00.000-07:002007-10-22T07:04:11.843-07:00Unix shell script para Oracle SQL (III)Podemos ejecutar una sentencia SQL embebida en el shell script, es decir, script y SQL todo en el mismo fichero.<br />Listemos el archivo de prueba llamado <tt>consulta2.sh</tt>.<br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA]<strong>ls -l consulta2.sh</strong><br />-rwxrw-r-- 1 monitor dba 69 Aug 14 12:20 consulta2.sh</pre></span><br />Veamos el contenido del fichero que contiene el shell script. Observamos que llamada al ejecutable <tt>sqlplus monitor/colopass07</tt> es seguida por la cadena <strong><tt><<FIN</tt></strong>, la sentencia SQL y la cadena <strong><tt>FIN</tt></strong>. El texto encerrado entre estas dos cadenas será recibido por el fichero ejecutable por la entrada estandar (stdin).<br />Los primeros dos caracteres del shell script (<tt>#!</tt>) NO son un comentario, se utilizan para indicar que el resto de la línea detalla el interprete de comandos que se debe utilizar para ejecutar el script en este caso el Korn shell o <tt>/bin/ksh</tt>. <br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA]<strong>cat consulta2.sh</strong><br />#!/bin/ksh<br />#<br />sqlplus monitor/colopass07 <strong><span style="color:green;"><<FIN</span></strong><br />SELECT 1<br /> FROM DUAL<br />/<br /><strong><span style="color:green;">FIN</span></strong></pre></span><br />El <tt>sqlplus</tt> recibe por la entrada estándar la sentencia SQL, la ejecuta y termina la ejecución retornando el control al <tt>ksh</tt>.<br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA]<strong>consulta2.sh</strong><br />SQL*Plus: Release 9.2.0.8.0 - Production on Tue Aug 14 12:21:44 2007<br />Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.<br /><br />Connected to: <br />Oracle9i Enterprise Edition Release 9.2.0.8.0 - 64bit Production<br />With the Partitioning, OLAP and Oracle Data Mining options<br />JServer Release 9.2.0.8.0 - Production<br /> <br />SQL> <br /><strong> 1<br />----------<br /> 1</strong><br /> <br />SQL> Disconnected from Oracle9i Enterprise Edition Release 9.2.0.8.0 - 64bit Production<br />With the Partitioning, OLAP and Oracle Data Mining options<br />JServer Release 9.2.0.8.0 - Production<br /><br />[monitor@DESA]</pre></span>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-3192562119738599305.post-42668450608660690592007-10-20T03:18:00.000-07:002007-12-18T02:23:12.386-08:00Unix shell script para Oracle SQL (II)Queremos crear un script para el shell de Unix (podría ser el Korn shell, ksh) ejecute una sentencia SQL y nos muestre los datos en la pantalla.<br />Simplemente escribimos la instrucción aparecida en la entrada <a href="http://databasetutorial.blogspot.com/2007/09/unix-shell-script-para-oracle-sql-i.html">Oracle Database Tutorial: Unix shell script para Oracle SQL (I)</a> en una fichero con extensión ".sh".<br />Para ello utilizaremos los ficheros llamados <tt>sentencia.sql</tt> y <tt>consulta1.sh</tt>.<br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA]<strong>ls -l sentencia.sql consulta1.sh consulta2.sh</strong><br />-rwxr--r-- 1 monitor dba 51 Aug 14 12:20 consulta1.sh<br />-rwxrw-r-- 1 monitor dba 69 Aug 14 12:20 consulta2.sh<br />-rw-r--r-- 1 monitor dba 29 Aug 14 12:19 sentencia.sql</pre></span><br />El fichero <tt>sentencia.sql</tt> solo contiene la sentencia SQL a ejecutar en la base de datos. <br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA]<strong>cat sentencia.sql</strong><br />SELECT 1 <br /> FROM DUAL<br />/<br />EXIT</pre></span><br />El fichero <tt>consulta1.sh</tt> sólo contiene la llamada a ejecución del SQL*Plus con el usuario <tt>monitor</tt>, la password <tt>colopass07</tt>, arroba (@) y el nombre del fichero que contiene la sentencia SQL a ejecutar <tt>sentencia.sql</tt> (los primeros dos caracteres del shell script (<tt>#!</tt>) se utilizan para indicar que el resto de la línea detalla el interprete de comandos que se debe utilizar para ejecutar el script en este caso el Korn shell o <tt>/bin/ksh</tt>).<br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA]<strong>cat consulta1.sh</strong><br />#!/bin/ksh<br /><br /><strong>sqlplus monitor/colopass07 <span style="color:green">@sentencia.sql</span></strong></pre></span><br />Ejecutamos el shell script: se conecta, ejecuta el select, muestra una fila con el valor uno, nombre de la columna uno y se desconecta.<br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA]<strong>consulta1.sh</strong><br />SQL*Plus: Release 9.2.0.8.0 - Production on Tue Aug 14 12:21:32 2007<br />Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.<br /><br />Connected to:<br />Oracle9i Enterprise Edition Release 9.2.0.8.0 - 64bit Production<br />With the Partitioning, OLAP and Oracle Data Mining options<br />JServer Release 9.2.0.8.0 - Production<br /><br /><strong> 1<br />----------<br /> 1</strong><br /><br />Disconnected from Oracle9i Enterprise Edition Release 9.2.0.8.0 - 64bit Production<br />With the Partitioning, OLAP and Oracle Data Mining options<br />JServer Release 9.2.0.8.0 - Production</pre></span>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3192562119738599305.post-6978202158138736752007-10-19T09:54:00.000-07:002007-12-18T02:23:47.990-08:00Unix shell script para Oracle SQL (I)Queremos ejecutar una consulta con una sentencia <tt>SELECT</tt> almacenada en un fichero con extensión .sql en el file system. ¿Cómo ejecutamos la consulta?<br />Primero, listamos los ficheros a utilizar en el ejemplo y mostramos en la pantalla el contenidos de <tt>sentencia.sql</tt>.<br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA]<strong>ls sentencia.sql</strong><br />sentencia.sql</pre></span><br />Podemos ver que el fichero <tt>sentencia1.sql</tt> sólo contiene una sentencia <tt>SELECT 1 FROM DUAL</tt> la barra (/) para ejecutarla y el comando <tt>EXIT</tt> para indicar la salida del SQL*Plus.<br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA]<strong>cat sentencia.sql</strong><br />SELECT 1 <br /> FROM DUAL<br />/<br />EXIT</pre></span><br />Para ejecutar este <tt>SELECT</tt> debemos invocar al SQL*Plus pasándole por parámetro el usuario (<tt>monitor</tt>) y la password (<tt>colopass</tt>) para conectarnos y arroba (<tt>@</tt>) el nombre del fichero (<tt>sentencia.sql</tt>) que contiene la sentencia SQL. Ejecutamos la línea donde se conecta, ejecuta el select, muestra una fila con el valor uno y se desconecta.<br /><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>[monitor@DESA]<strong>sqlplus monitor/colopass07 <span style="color:green">@sentencia.sql</span></strong><br /><br />SQL*Plus: Release 9.2.0.8.0 - Production on Fri Sep 28 14:16:33 2007<br />Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.<br /><br />Connected to:<br />Oracle9i Enterprise Edition Release 9.2.0.8.0 - 64bit Production<br />With the Partitioning, OLAP and Oracle Data Mining options<br />JServer Release 9.2.0.8.0 - Production<br /><br /><strong> 1<br />----------<br /> 1</strong><br /><br />Disconnected from Oracle9i Enterprise Edition Release 9.2.0.8.0 - 64bit Production<br />With the Partitioning, OLAP and Oracle Data Mining options<br />JServer Release 9.2.0.8.0 - Production</pre></span>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3192562119738599305.post-30792510460211048692007-10-18T17:13:00.000-07:002007-12-18T02:24:19.328-08:00Cursores PL/SQL notación FETCH .. BULK COLLECT INTO .. LIMIT<span style="font-family:georgia;font-size:100%;color:#000000;">Esta notación nos permite recuperar las filas asociadas a la sentencia <tt>SELECT</tt> que representa el cursor con la ejecución de varias sentencias <tt>FETCH</tt> <strong>en grupos de filas de un número determinado por la cláusula <tt>LIMIT</tt></strong>. <strong>Los grupos de filas recuperados se asignan a un array o vector.</strong><br /><br />Resumiremos linea a línea el código fuente del procedimiento del ejemplo llamado <tt>FETCH_BULK_COLLECT_INTO_LIMIT</tt><br /><br /><strong>Línea 2:</strong> Inicio del procedimiento.<br /><strong>Líneas 4 a 7:</strong> Declaramos el cursor de nombre <tt>CursorVistas</tt> y lo asociamos a la sentencia <tt>SELECT</tt> que recupera las filas.<br /><strong>Línea 9:</strong> Declaramos un tipo de dato de array o vector que almacena filas de la tabla <tt>ALL_VIEWS</tt> y lo llamamos <tt>TipoArrayRegistros</tt>. Es decir, cada elemento del array permite almacenar todos los valores de una fila de la tabla <tt>ALL_VIEWS</tt>. Cada elemento del array contiene tantos campos como columnas tiene la tabla, con los mismos nombres y tipos de dato.<br /><strong>Línea 10:</strong> Declaramos la variable <tt>ArrayRegistros</tt> del tipo de datos <tt>TipoArrayRegistros</tt>.<br /><strong>Línea 12:</strong> Declaramos la variable <tt>indice</tt> de tipo de dato entero. La utilizaremos para hacer referencia a los valores que contiene el array o vector.<br /><strong>Línea 14:</strong> Abrimos el cursor. Este paso selecciona las filas de la tabla que evalúan verdadera la condición que la sentencia <tt>SELECT</tt> declara en su cláusula <tt>WHERE</tt>. <strong>Si cambian los valores de las filas antes de ser recuperadas, estos cambios no se verán reflejados. Tomamos la foto de los datos a recuperar en este punto en el tiempo. No se consideran modificaciones posteriores.</strong><br /><strong>Línea 15:</strong> Iniciamos el ciclo o bucle repetitivo en el que recuperamos los grupos de filas (la <strong>línea 26</strong> fuerza la salida del bucle o ciclo repetitivo si no hay mas filas para recuperar del cursor).<br /><strong>Línea 16:</strong> Recuperamos las filas que cumplen la condición de la sentencia <tt>SELECT</tt> con la instrucción <tt>FETCH</tt>. El <tt>BULK COLLECT INTO</tt> permite asignar el grupo de filas recuperadas a un array o vector. Cada elemento del array contiene una fila recuperada. Los elementos están formados por una estructura de campos con nombres y tipos de datos equivalentes a las columnas seleccionadas. <strong>La cantidad máxima del grupo de filas recuperadas por la ejecución de cada <tt>FETCH</tt> está determinada por la cláusula <tt>LIMIT</tt>.</strong> En este ejemplo recuperamos las filas en grupos de 5 filas.<br /><strong>Línea 17 a 25:</strong> Recorremos el array o vector <tt>ArrayRegistros</tt> que contiene las filas recuperadas por la sentencia <tt>FETCH</tt> y mostramos el contenido en pantalla. <br /><strong>Línea 17:</strong> Asignamos a la variable <tt>indice</tt> la posición del primer elemento almacenado en el array o vector. Si no contiene elementos se asigna el valor <tt>NULL</tt>.<br /><strong>Línea 18 a 25:</strong> Mientras la variable <tt>indice</tt> sea distinta de <tt>NULL</tt> iteramos dentro del ciclo definido por el <tt>LOOP .. END LOOP</tt>. Por cada ciclo mostramos en pantalla con la instrucción <tt>DBMS_OUTPUT.PUT_LINE</tt> el contenido de los campos <tt>OWNER</tt> y <tt>VIEW_NAME</tt> en la posición <tt>indice</tt> del array.<br />Luego asignamos a la variable <tt>indice</tt> la siguiente posición del array que contiene una fila. Si no hay mas filas en el array se asigna <tt>NULL</tt>. Al evaluarse la condición de <tt>WHILE (indice IS NOT NULL)</tt> se corta la iteración del ciclo que recorre el vector o array.<br /><strong>Línea 28:</strong> Cerramos el cursor. Liberamos los recursos en memoria asociados a la sentencia <tt>SELECT</tt>.<br /><strong>Línea 28:</strong> Fin del procedimiento <tt>FETCH_BULK_COLLECT_INTO_LIMIT</tt>.</span><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> CONNECT FERNANDEZ/FERNANDEZ<br />Conectado.<br /><br />SQL> create or replace<br /> 2 PROCEDURE FETCH_BULK_COLLECT_INTO_LIMIT<br /> 3 IS<br /> 4 <strong>CURSOR</strong> <span style="color:Green;">CursorVistas</span> IS SELECT *<br /> 5 FROM ALL_VIEWS<br /> 6 WHERE OWNER = 'SYS'<br /> 7 AND VIEW_NAME LIKE 'ALL_TAB%';<br /> 8<br /> 9 TYPE TipoArrayRegistros IS TABLE OF CursorVistas%ROWTYPE;<br /> 10 <span style="color:Green;">ArrayRegistros</span> TipoArrayRegistros;<br /> 11<br /> 12 <span style="color:Green;">indice</span> PLS_INTEGER;<br /> 13 BEGIN<br /> 14 <strong>OPEN</strong> <span style="color:Green;">CursorVistas</span>;<br /> 15 LOOP<br /> 16 <strong>FETCH</strong> <span style="color:Green;">CursorVistas</span> <strong>BULK COLLECT INTO</strong> <span style="color:Green;">ArrayRegistros</span> <strong>LIMIT 5</strong>;<br /> 17 <span style="color:Green;">indice</span> := <span style="color:Green;">ArrayRegistros</span>.FIRST;<br /> 18 WHILE (<span style="color:Green;">indice</span> IS NOT NULL)<br /> 19 LOOP<br /> 20 DBMS_OUTPUT.PUT_LINE(<span style="color:Red;">'ESQUEMA: '</span> ||<br /> 21 <span style="color:Green;">ArrayRegistros</span>(<span style="color:Green;">indice</span>).OWNER ||<br /> 22 <span style="color:Red;">' VISTA : '</span> ||<br /> 23 <span style="color:Green;">ArrayRegistros</span>(<span style="color:Green;">indice</span>).VIEW_NAME);<br /> 24 <span style="color:Green;">indice</span> := <span style="color:Green;">ArrayRegistros</span>.NEXT(<span style="color:Green;">indice</span>);<br /> 25 END LOOP;<br /> 26 <strong>EXIT WHEN</strong> <span style="color:Green;">CursorVistas</span><strong>%NOTFOUND</strong>;<br /> 27 END LOOP;<br /> 28 <strong>CLOSE</strong> <span style="color:Green;">CursorVistas</span>;<br /> 29<br /> 30 END FETCH_BULK_COLLECT_INTO_LIMIT;<br /> 31 /<br /> <br />Procedimiento creado.<br /> <br />SQL> DESCRIBE FETCH_BULK_COLLECT_INTO_LIMIT;<br />PROCEDURE FETCH_BULK_COLLECT_INTO_LIMIT<br /> <br />SQL> SET SERVEROUTPUT ON<br /> <br />SQL> EXEC FETCH_BULK_COLLECT_INTO_LIMIT;<br /><span style="color:Red;">ESQUEMA: SYS VISTA : ALL_TABLES<br />ESQUEMA: SYS VISTA : ALL_TAB_COLS<br />ESQUEMA: SYS VISTA : ALL_TAB_COLUMNS<br />ESQUEMA: SYS VISTA : ALL_TAB_COL_STATISTICS<br />ESQUEMA: SYS VISTA : ALL_TAB_COMMENTS<br />ESQUEMA: SYS VISTA : ALL_TAB_HISTOGRAMS<br />ESQUEMA: SYS VISTA : ALL_TAB_MODIFICATIONS<br />ESQUEMA: SYS VISTA : ALL_TAB_PARTITIONS<br />ESQUEMA: SYS VISTA : ALL_TAB_PRIVS<br />ESQUEMA: SYS VISTA : ALL_TAB_PRIVS_MADE<br />ESQUEMA: SYS VISTA : ALL_TAB_PRIVS_RECD<br />ESQUEMA: SYS VISTA : ALL_TAB_STATISTICS<br />ESQUEMA: SYS VISTA : ALL_TAB_STATS_HISTORY<br />ESQUEMA: SYS VISTA : ALL_TAB_SUBPARTITIONS</span><br /> <br />Procedimiento PL/SQL terminado correctamente.<br /> <br />SQL></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Podemos leer este tema en la documentación estandar de Oracle:<br /><br />Manual: Oracle Database PL/SQL User's Guide and Reference<br />Capítulo: 11 Tuning PL/SQL Applications for Performance<br />Título: Reducing Loop Overhead for DML Statements and Queries with Bulk SQL <br />Subtítulo: Retrieving Query Results into Collections with the BULK COLLECT clause<br />Párrafo: <a href="http://download-east.oracle.com/docs/cd/B19306_01/appdev.102/b14261/tuning.htm#sthref2231">Limiting the Rows for a Bulk FETCH Operation with the LIMIT clause</a></span>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3192562119738599305.post-16416790867065806162007-10-17T12:45:00.000-07:002007-12-18T02:24:41.585-08:00Tiempo de respuesta lento de aplicaciones Oracle (III)Podemos mejorar el tiempo de respuesta lento de una aplicación de gestión comercial sin mejorar la calidad del enlace ni mejorar la calidad del código fuente de la aplicación.<br />La opción es utilizar una implementacion de <strong>Terminal Server</strong>. Asumimos que la aplicación de gestión comercial es ejecutada en puntos de venta alejados de la base de datos. El Terminal Server debe ser instalado con un enlace de calidad y baja latencia donde la aplicación se ejecute con tiempos de respuesta satisfactorios para el usuario. Por ejemplo podría residir en el mismo centro de cómputos que la base de datos. Estamos moviendo la ejecución de la aplicación de gestión. Reducimos la distancia entre la aplicación y la base. <br />Los usuarios de la aplicación ubicados en los puntos de venta lejanos ejecutan un <strong>Terminal Server Client</strong> que se conecta con el Terminal Server. A través de ella los usuarios pueden ejecutar la aplicación de gestión comercial remotamente en el Terminal Server.<br /><strong>El enlace entre los puntos de venta y el Terminal Server de menor calidad, económico y gran longitud transmite diálogos que poco sencibles a la baja latencia. El cliente envía tipeos de teclas, movimientos y clicks del ratón. El servidor responde con las actualizaciónes gráficas de las pantallas de la aplicación visualizadas en el monitor.</strong> La aplicación de gestión comercial se ejecuta en el Terminal Server sin ser mejoradas sus ineficientes comunicaciones con la base de datos. <br /><strong>Esta opción es apliamente recomendada para aplicaciones de gestión comercial ya desarrolladas, que no escalan a nuevos puntos de venta alejados geográficamente y que utilizan enlaces baratos. Modificar íntegramente la aplicación puede no ser una opción económica viable por la gran cantidad de línes de código fuente involucradas. No se recomienda como solución informática para aplicaciones de gestión comercial nuevas. En este último caso la opción correcta es programar la aplicación utilizando buenas prácticas y haciendo un uso eficiente del vínculo con la base de datos.</strong>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3192562119738599305.post-9941294386338229842007-10-16T05:39:00.000-07:002007-12-18T02:25:10.966-08:00Tiempo de respuesta lento de aplicaciones Oracle (II)Reduciendo el tiempo de respuesta con PL/SQL<br /><br />Si nuestra aplicación programada en Java o Visual Basic contiene sentencias SQL embebidas en el código, la base de datos debe recibir y procesar una por una cada sentencia SQL. <strong>Las aplicaciones que utilizan muchas sentencias SQL aisladas requieren múltiples llamadas a la base de datos, provocando overhead de red y performance.</strong><br /><br />Ver la documentación oficial:<br /><br />Manual: Oracle Database PL/SQL User's Guide and Reference<br />Capítulo: 1 Overview of PL/SQL<br />Sección: Advantages of PL/SQL<br />Párrafo: <a href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14261/toc.htm">Better Performance</a><br /><br />Utilizando PL/SQL podemos solicitar y ejecutar un bloque entero de sentencias SQL de una sola vez. Reduciremos en gran medida el tráfico de red entre la aplicación y la base de datos. Si el enlace es lento o está muy congestionado reduciremos el tiempo de respuesta de la aplicación.<br />Los procedimientos PL/SQL almacenados en la base de datos o (PL/SQL stored procedures) se compilan una sóla vez y se almacenan en formato ejecutable ganando eficiencia.<br /><strong>Una única llamada a través de la red puede iniciar la ejecución de una gran tarea conformada por transacciones y sentencias SQL porque el procedimiento está almacenado en la base de datos.</strong> De esta forma reducimos el trafico de la red y mejoramos el tiempo de respuesta. Los stored procedures son reutilizados y compartidos entre usuarios por lo que reducimos los requerimientos de memoria y overhead de ejecución.<br /><br />¿Si ningún módulo de la aplicación utiliza stored procedures? Modificar miles de líneas de código fuente de performance ineficiente y mediocre puede ser muy costoso.<br /><strong>¿Cómo mejorar el tiempo de respuesta de la aplicación sin modificar la calidad del enlace ni modificar la aplicación?</strong>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3192562119738599305.post-61558890505756564802007-10-15T02:36:00.000-07:002007-12-18T02:25:36.127-08:00Tiempo de respuesta lento de aplicaciones Oracle (I)Introducción al problema<br /><br />Las aplicaciones que utilizan el protocolo SQLNet pueden ser muy sensibles a la calidad del enlace que las conecta a la base de datos. Las aplicaciones desarrolladas en Java o Visual Basic con sentencias SQL embebidas en el código utilizan una cantidad importante de mensajes para comunicarse con la base de datos y ejecutar las sentencias.<br />Considerando la <strong>baja latencia</strong> de una red LAN (<strong>reducida demora</strong> en recorrer dos puntos de un enlace), ésta generalmente no impacta negativamente en el tiempo de respuesta de la aplicación. No ocurre lo mismo cuando la aplicación es ejecutada en un punto de venta alejado geograficamente del centro de cómputos que aloja la base de datos. Estos puntos de venta lejanos en ocaciones utilizan enlaces de baja calidad y bajo costo. Hemos registrado latencias de red muy altas de hasta 150 milisegundos en el horario pico. <strong>Estas condiciones del enlace provocan gran lentitud del tiempo de respuesta de consultas y transacciones aunque el ancho de banda del enlace se encuentre parcialmente libre.</strong><br /><br />Podemos encontrar en la documentación oficial la siguiente recomendación:<br /><br /><strong>"La latencia de red punto a punto debería estar entre 1 y 25 miliseg."</strong><br /><br />Manual: Oracle9 i Database Performance Planning<br />Capítulo: 2 Monitoring and Improving Application Performance<br />Sección: The Oracle Performance Improvement Method <br />Párrafo: <a href="http://download.oracle.com/docs/cd/B10501_01/server.920/a96532/ch2.htm#15241">Performance Characteristics of Hardware Configurations</a><br /><br />La documentación oficial de 10g no vuelve a mencionar valores absolutos como 25 miliseg. sino que recomienda distintas técnicas de evaluación con distintas cargas de trabajo y la participación del usuario final para validar un tiempo de respuesta satisfactorio.<br /><br /><strong>¿Cómo mejorar el tiempo de respuesta entre la aplicación cliente y el servidor de base de datos sin utilizar un enlace de mayor calidad y por ende mas costoso?</strong>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3192562119738599305.post-90041371556921110042007-10-14T19:57:00.000-07:002007-12-18T02:26:00.582-08:00Cursores PL/SQL notación SELECT..INTO..EXCEPTION<span style="font-family:georgia;font-size:100%;color:#000000;">Creamos dos procedimientos que ejecutan la misma sentencia SQL, recuperan las mismas filas y las muestran en pantalla. El primero NO gestiona las excepciones y muestra errores en pantalla. El segundo gestiona las excepciones y no deja que se vean errores en pantalla. Comparamos los funcionamientos.<br />El procedimiento sin gestión de excepciones se llama <tt>SELECT_INTO_SIN_EXCEPTION</tt>. Selecciona una fila y muestra el valor que contiene una columna de la fila seleccionada. Utilizamos para selecionar la fila el <tt>Parametro_Nombre</tt> que nos permite ejecutar la lógica o programa trabajando con distintas filas en cada ejecución.</span><span style="font-family:courier new;font-size:80%;color:Blue;"><pre>SQL> CONNECT FERNANDEZ/FERNANDEZ<br />Conectado.<br /> <br />SQL> SET SERVEROUTPUT ON<br /> <br />SQL> create or replace<br /> 2 PROCEDURE SELECT_INTO_SIN_EXCEPTION(<span style="color:Green;">Parametro_Nombre</span> IN <span style="color:Maroon;">ALL_VIEWS.VIEW_NAME</span>%TYPE)<br /> 3 AS<br /> 4 <span style="color:Green;">vTEXT_LENGTH</span> <span style="color:Maroon;">ALL_VIEWS.TEXT_LENGTH</span>%TYPE;<br /> 5 BEGIN<br /> 6 SELECT <span style="color:Maroon;">TEXT_LENGTH</span><br /> 7 INTO <span style="color:Green;">vTEXT_LENGTH</span><br /> 8 FROM SYS.ALL_VIEWS<br /> 9 WHERE OWNER = 'SYS'<br /> 10 AND VIEW_NAME LIKE <span style="color:Green;">Parametro_Nombre</span>;<br /> 11<br /> 12 DBMS_OUTPUT.PUT_LINE(<span style="color:Red;">'La longitud del texto de la vista es:'</span>);<br /> 13 DBMS_OUTPUT.PUT_LINE(<span style="color:Green;">vTEXT_LENGTH</span>);<br /> 14 END SELECT_INTO_SIN_EXCEPTION;<br /> 15 /<br /> <br />Procedimiento creado.<br /></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Verificamos la existencia del procedimiento con el comando <tt>DESCRIBE</tt>. Vemos que no solo se muestra el nombre del procedimiento <tt>SELECT_INTO_SIN_EXCEPTION</tt> sino que también se detalla el nombre del parámetro <tt>PARAMETRO_NOMBRE</tt> que es sólo de entrada de datos (<tt>E/S=IN</tt>).</span><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> DESC SELECT_INTO_SIN_EXCEPTION;<br />PROCEDURE SELECT_INTO_SIN_EXCEPTION<br /> Nombre de Argumento Tipo E/S ┐Por Defecto?<br /> ------------------------------ ----------------------- ------ --------<br /> PARAMETRO_NOMBRE VARCHAR2(30) IN<br /> <br /></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Ejecutamos el procedimiento pasando por parámetro el valor <tt>'USER_TABLES'</tt>. Sólo una fila de <tt>ALL_VIEWS</tt> evalúa verdadera la condición de la cláusula <tt>WHERE VIEW_NAME LIKE 'USER_TABLES'</tt>. Se recupera la fila y se muestra en pantalla el valor de la columna <tt>TEXT_LENGTH</tt> de dicha fila.</span><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> EXECUTE SELECT_INTO_SIN_EXCEPTION(<span style="color:Green;">'USER_TABLES'</span>);<br /><span style="color:Red;">La longitud del texto de la vista es:<br />3971</span><br /> <br />Procedimiento PL/SQL terminado correctamente.<br /> <br />SQL></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Ejecutamos el procedimiento pasando por parámetro el valor <tt>'NOMBRE_INEXISTENTE'</tt>. No existe ninguna fila con dicho nombre en <tt>ALL_VIEWS</tt> por lo tanto se levanta la excepción <tt>"ORA-01403: No se ha encontrado ningún dato" ("ORA-01403: no data found")</tt>. El procedimiento no posee instrucciones para atrapar, capturar ni gestionar esta excepción por lo tanto se propaga haca el nivel superior. El SQL*Plus es el proceso ejecutor del procedimiento, recibe la excepción levantada por el procedimiento y muestra el mensaje asociado a la excepción en la pantalla. </span><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> EXECUTE SELECT_INTO_SIN_EXCEPTION(<span style="color:Green;">'NOMBRE_INEXISTENTE'</span>);<br />BEGIN SELECT_INTO_SIN_EXCEPTION('NOMBRE_INEXISTENTE'); END;<br /> <br /><span style="color:Red;">*<br />ERROR en línea 1:<br />ORA-01403: No se ha encontrado ningún dato<br />ORA-06512: en "FERNANDEZ.SELECT_INTO_SIN_EXCEPTION", línea 5<br />ORA-06512: en línea 1</span></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Ejecutamos el procedimiento pasando por parámetro el valor comodín <tt>'%'</tt>. Todas las filas comparadas con el comodín <tt>'%'</tt> evalúan la condición con el valor verdadero por lo tanto se recuperan todas las filas de <tt>ALL_VIEWS</tt>. El procedimiento ejecuta la sentencia con la notación <tt>SELECT..INTO..</tt> contemplanto sólo la recuperación de una única fila. Como no tiene soporte para la recuperación de múltiples filas, se levanta la excepción <tt>"ORA-01403: No se ha encontrado ningún dato" ("ORA-01403: no data found")</tt>. El procedimiento no posee instrucciones para atrapar, capturar ni gestionar esta excepción por lo tanto se propaga haca el nivel superior. El SQL*Plus es el proceso ejecutor del procedimiento, recibe la excepción levantada por el procedimiento y muestra el mensaje asociado a la excepción en la pantalla.</span><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> EXECUTE SELECT_INTO_SIN_EXCEPTION(<span style="color:Green;">'%'</span>);<br />BEGIN SELECT_INTO_SIN_EXCEPTION('%'); END;<br /> <br /><span style="color:Red;">*<br />ERROR en línea 1:<br />ORA-01422: la recuperación exacta devuelve un número mayor de filas que el solicitado<br />ORA-06512: en "FERNANDEZ.SELECT_INTO_SIN_EXCEPTION", línea 5<br />ORA-06512: en línea 1</span></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Creamos el procedimiento <tt>SELECT_INTO_CON_EXCEPTION</tt> similar al procedimiento <tt>SELECT_INTO_SIN_EXCEPTION</tt> con el agregado del manejo de excepciones de error:<br /><b>Línea 15:</b> Inicio del bloque de gestión de excepciones.<br /><b>Líneas 16 a 17:</b> Definimos qué debe hacer el procedimiento sin entre el <tt>BEGIN</tt> y el <tt>EXCEPTION</tt> se levanta un error de excepción del tipo <tt>NO_DATA_FOUND</tt>, es decir, <tt>"ORA-01403: No se ha encontrado ningún dato"</tt>. Se muestra el texto <tt>DATOS BUSCADOS NO ENCONTRADOS!</tt> y no se propaga el error al nivel superior.<br /><b>Líneas 18 a 19:</b> Si se leventa la excepción <tt>TOO_MANY_ROWS</tt>, es decir, <tt>"ORA-01422: la recuperación exacta devuelve un número mayor de filas que el solicitado" ("ORA-01422: exact fetch returns more than requested number of rows")</tt> se muestra en pantalla el mensaje <tt>SE ENCONTRO MAS DE UNA FILA!</tt> y no se propaga el error al nivel superior.</span><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre><br />SQL> create or replace<br /> 2 PROCEDURE SELECT_INTO_CON_EXCEPTION(Parametro_Nombre IN ALL_VIEWS.VIEW_NAM<br />E%TYPE)<br /> 3 AS<br /> 4 vTEXT_LENGTH ALL_VIEWS.TEXT_LENGTH%TYPE;<br /> 5 BEGIN<br /> 6 SELECT TEXT_LENGTH<br /> 7 INTO vTEXT_LENGTH<br /> 8 FROM SYS.ALL_VIEWS<br /> 9 WHERE OWNER = 'SYS'<br /> 10 AND VIEW_NAME LIKE Parametro_Nombre;<br /> 11<br /> 12 DBMS_OUTPUT.PUT_LINE(<span style="color:Red;">'La longitud del texto de la vista es:'</span>);<br /> 13 DBMS_OUTPUT.PUT_LINE(vTEXT_LENGTH);<br /> 14<br /> 15 <span style="color:Maroon;">EXCEPTION</span><br /> 16 WHEN <span style="color:Maroon;">NO_DATA_FOUND</span> THEN<br /> 17 DBMS_OUTPUT.PUT_LINE(<span style="color:Red;">'DATOS BUSCADOS NO ENCONTRADOS!'</span>);<br /> 18 WHEN <span style="color:Maroon;">TOO_MANY_ROWS</span> THEN<br /> 19 DBMS_OUTPUT.PUT_LINE(<span style="color:Red;">'SE ENCONTRO MAS DE UNA FILA!'</span>);<br /> 20 END SELECT_INTO_CON_EXCEPTION;<br /> 21 /<br /> <br />Procedimiento creado.<br /> <br />SQL> DESCRIBE SELECT_INTO_CON_EXCEPTION<br />PROCEDURE SELECT_INTO_CON_EXCEPTION<br /> Nombre de Argumento Tipo E/S ┐Por Defecto?<br /> ------------------------------ ----------------------- ------ --------<br /> PARAMETRO_NOMBRE VARCHAR2(30) IN<br /></span></pre><span style="font-family:georgia;font-size:100%;color:#000000;">Ejecutamos el procedimiento con los distintos valores de parámetros ejecutados anteriormente y vemos que no se muestran errores en pantalla. El manejo de excepciones las detecta y toma acciones específicas para cada una de ellas.</span></span><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> EXECUTE SELECT_INTO_CON_EXCEPTION(<span style="color:Green;">'NOMBRE_INEXISTENTE'</span>);<br /><span style="color:Red;">DATOS BUSCADOS NO ENCONTRADOS!</span><br /> <br />Procedimiento PL/SQL terminado correctamente.<br /> <br />SQL> EXECUTE SELECT_INTO_CON_EXCEPTION(<span style="color:Green;">'%'</span>);<br /><span style="color:Red;">SE ENCONTRO MAS DE UNA FILA!</span><br /> <br />Procedimiento PL/SQL terminado correctamente.<br /> <br />SQL> EXECUTE SELECT_INTO_CON_EXCEPTION(<span style="color:Green;">'USER_TABLES'</span>);<br /><span style="color:Red;">La longitud del texto de la vista es:<br />3971</span><br /> <br />Procedimiento PL/SQL terminado correctamente.</pre></span>Unknownnoreply@blogger.com11tag:blogger.com,1999:blog-3192562119738599305.post-18569226853611545282007-10-13T03:33:00.000-07:002007-12-18T02:26:26.983-08:00Cursores PL/SQL notación SELECT..INTO<span style="font-family:georgia;font-size:100%;color:#000000;">Creamos un procedimiento almacenado PL/SQL o PL/SQL stored procedure de nombre <tt>SELECT_INTO</tt>. El procedimiento recupera una columna de una fila dada, lo muestra en pantalla con un mensaje y termina.<br /><br /><strong>Líneas 1 a 2:</strong> Crean un procedimiento nuevo o lo reemplazan si ya existe uno anteriormente almacenado con el nombre <tt>SELECT_INTO</tt>.<br /><strong>Línea 3:</strong> Declara la variable <tt>vTEXT_LENGTH</tt> del mismo tipo de datos que la columna <tt>TEXT_LENGTH</tt> de la vista <tt>ALL_VIEWS</tt>.<br /><strong>Líneas 5 a 9:</strong> Declaran la sentencia <tt>SELECT</tt> que recupera la longitud del texto de la vista <tt>USER_TABLES</tt> y lo asigna a la variable <tt>vTEXT_LENGTH</tt>.<br /><strong>Líneas 11 y 12:</strong> Muestran en pantalla un mensaje y el valor recuperado por la sentencia <tt>SELECT</tt>.</span><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> CREATE OR REPLACE<br /> 2 PROCEDURE SELECT_INTO AS<br /> 3 <span style="color:Green;">vTEXT_LENGTH</span> <span style="color:Maroon;">ALL_VIEWS.TEXT_LENGTH</span>%TYPE;<br /> 4 BEGIN<br /> 5 SELECT <span style="color:Maroon;">TEXT_LENGTH</span><br /> 6 INTO <span style="color:Green;">vTEXT_LENGTH</span><br /> 7 FROM ALL_VIEWS<br /> 8 WHERE OWNER = 'SYS'<br /> 9 AND VIEW_NAME = 'USER_TABLES';<br /> 10<br /> 11 DBMS_OUTPUT.PUT_LINE(<span style="color:Red;">'La longitud del texto de la vista es:'</span>);<br /> 12 DBMS_OUTPUT.PUT_LINE(<span style="color:Green;">vTEXT_LENGTH</span>);<br /> 13<br /> 14 END SELECT_INTO;<br /> 15 /<br /> <br />Procedimiento creado.<br /> <br />SQL> DESCRIBE SELECT_INTO<br />PROCEDURE SELECT_INTO<br /></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Normalmente se encuentra deshabilitada la salida de mensajes por pantalla. Asignando el valor <tt>ON</tt> a la variable de ambiente <tt>SERVEROUTPUT</tt>, SQL*Plus muestra los mensajes de salida de los procedimientos almacenados.</span><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> SET SERVEROUTPUT ON<br /></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Ejecutamos el procedimiento con el comando <tt>EXECUTE</tt>.</span><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> EXECUTE SELECT_INTO<br /><span style="color:Red;">La longitud del texto de la vista es:<br />3971</span><br /> <br />Procedimiento PL/SQL terminado correctamente.<br /> <br />SQL></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Podemos leer este tema en la documentación estandar de Oracle:<br /><br />Manual: Oracle Database PL/SQL User's Guide and Reference<br />Capítulo: 6 Performing SQL Operations from PL/SQL<br />Título: Querying Data with PL/SQL <br />Subtítulo:<a href="http://download-east.oracle.com/docs/cd/B19306_01/appdev.102/b14261/sqloperations.htm#sthref1348">Selecting At Most One Row: SELECT INTO Statement</a><br /><br />Entradas relacionadas:<br /><a href="http://databasetutorial.blogspot.com/2007/06/plsql-tutorial.html">PL/SQL Tutorial</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/cursor-plsql-notacion-selectinto.html">Cursores PL/SQL notación SELECT..INTO</a><br /><a href="http://databasetutorial.blogspot.com/2007/05/create-or-replace-procedure.html">Cursores PL/SQL notación FOR...LOOP</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql.html">Cursores PL/SQL notación CURSOR...FOR...LOOP</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql_13.html">Cursores PL/SQL notación OPEN...FETCH...CLOSE</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql_16.html">Cursores PL/SQL notación FETCH...BULK COLLECT INTO...</a></span>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-3192562119738599305.post-79343362629548539472007-10-12T03:08:00.000-07:002007-12-18T02:27:23.275-08:00Primer procedimiento PL/SQL<span style="font-family:georgia;font-size:100%;color:#000000;">Creamos un procedimiento almacenado PL/SQL o PL/SQL stored procedure de nombre <tt>PRIMERO</tt>. El procedimiento sólo muestra el mensaje <tt>Hola mundo!</tt> y termina.<br /><br /><strong>Líneas 1 a 2:</strong> Crean un procedimiento nuevo o lo reemplazan si ya existe uno anteriormente almacenado con el nombre <tt>PRIMERO</tt>.<br /><strong>Línea 3:</strong> La palabra reservada <tt>BEGIN</tt> determina el inicio del cuerpo del procedimiento que contiene la lógica o algoritmo a ejecutar.<br /><strong>Línea 4:</strong> Una única línea de codigo con la instrucción <tt>DBMS_OUTPUT.PUT_LINE</tt> define el cuerpo del procedimiento con la sola intención de mostrar en pantalla el mensaje <tt>Hola mundo!</tt>.<br /><strong>Línea 5:</strong> Fin del procedimiento.<br /><strong>Línea 6:</strong> Ejecutar sentencia de creación del procedimiento.</span><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> CONNECT FERNANDEZ/FERNANDEZ<br />Conectado.<br /> <br />SQL> CREATE OR REPLACE<br /> 2 PROCEDURE PRIMERO AS<br /> 3 BEGIN<br /> 4 <span style="color:Maroon;">DBMS_OUTPUT.PUT_LINE(</span><span style="color:Red;">'Hola mundo!</span>'<span style="color:Maroon;">)</span>;<br /> 5 END PRIMERO;<br /> 6 /<br /> <br />Procedimiento creado.<br /></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Normalmente se encuentra deshabilitada la salida de mensajes por pantalla. Asignando el valor <tt>ON</tt> a la variable de ambiente <tt>SERVEROUTPUT</tt>, SQL*Plus muestra los mensajes de salida de los procedimientos almacenados.</span><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> SET SERVEROUTPUT ON</pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Ejecutamos el procedimiento con el comando <tt>EXECUTE</tt>.</span><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> EXECUTE PRIMERO<br /><span style="color:Red;">Hola mundo!</span><br /> <br />Procedimiento PL/SQL terminado correctamente.<br /> <br />SQL></pre></span></span><span style="font-family:georgia;font-size:100%;color:#000000;">Podemos leer este tema en la documentación estandar de Oracle:<br /><br />Manual: Oracle Database PL/SQL User's Guide and Reference<br />Capítulo: 8 Using PL/SQL Subprograms<br />Título:<a href="http://download-east.oracle.com/docs/cd/B19306_01/appdev.102/b14261/subprograms.htm#sthref1640">Understanding PL/SQL Procedures</a><br /><br />Entradas relacionadas:<br /><a href="http://databasetutorial.blogspot.com/2007/06/plsql-tutorial.html">PL/SQL Tutorial</a></span>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3192562119738599305.post-86627089193687228502007-10-11T13:17:00.000-07:002007-12-18T02:27:47.895-08:00Cursores PL/SQL notación FETCH...BULK COLLECT INTO...<span style="font-family:georgia;font-size:100%;color:#000000;">Esta notación nos permite recuperar todas las filas asociadas a la sentencia SELECT que representa el cursor con la ejecución de una única instrucción FETCH.<br />Resumiremos linea a línea el código fuente del procedimiento del ejemplo:<br /><br /><strong>Línea 2:</strong> Inicio del procedimiento.<br /><strong>Línea 4:</strong> Declaramos un tipo de dato de array o vector que almacena filas de la tabla <tt>ALL_VIEWS</tt> y lo llamamos <tt>TipoArrayRegistros</tt>. Es decir, cada elemento del array permite almacenar todos los valores de una fila de la tabla <tt>ALL_VIEWS</tt>. Cada elemento del array contiene tantos campos como columnas tiene la tabla, con los mismos nombres y tipos de dato.<br /><strong>Línea 5:</strong> Declaramos la variable <tt>ArrayRegistros</tt> del tipo de datos <tt>TipoArrayRegistros</tt>.<br /><strong>Líneas 7 a 10:</strong> Declaramos el cursor de nombre <tt>CursorVistas</tt> y lo asociamos a la sentencia <tt>SELECT</tt> que recupera las filas.<br /><strong>Línea 11:</strong> Declaramos la variable <tt>indice</tt> de tipo de dato entero. La utilizaremos para hacer referencia a los valores que contiene el array o vector.<br /><strong>Línea 14:</strong> Abrimos el cursor. Este paso selecciona las filas de la tabla que evalúan verdadera la condición de la sentencia <tt>SELECT</tt>. <strong>Si cambian los valores de las filas antes de ser recuperadas, estos cambios no se verán reflejados. Tomamos la foto de los datos a recuperar en este punto en el tiempo. No se consideran modificaciones posteriores.</strong><br /><strong>Línea 15:</strong> Recuperamos las filas que cumplen la condición de la sentencia <tt>SELECT</tt> con la instrucción <tt>FETCH</tt>. El <tt>BULK COLLECT INTO</tt> permite asignar todas las filas recuperadas a un array o vector. Cada elemento del array contiene una fila recuperada. Los elementos están formados por una estructura de campos con nombres y tipos de datos equivalentes a las columnas seleccionadas.<br /><strong>Línea 16:</strong> Cerramos el cursor. Liberamos los recursos en memoria asociados a la sentencia <tt>SELECT</tt>.<br /><strong>Línea 18:</strong> Asignamos a la variable <tt>indice</tt> la posición del primer elemento almacenado en el array o vector. Si no contiene elementos se asigna el valor <tt>NULL</tt>.<br /><strong>Línea 19 a 26:</strong> Mientras la variable <tt>indice</tt> sea distinta de <tt>NULL</tt> iteramos dentro del ciclo definido por el <tt>LOOP...END LOOP</tt>. Por cada ciclo mostramos en pantalla con la instrucción <tt>DBMS_OUTPUT.PUT_LINE</tt> el contenido de los campos <tt>OWNER</tt> y <tt>VIEW_NAME</tt> en la posición <tt>indice</tt> del array. Luego asignamos a la variable <tt>indice</tt> la siguiente posición del array que contiene una fila. Si no hay mas filas en el array se asigna <tt>NULL</tt>. Al evaluarse la condición de <tt>WHILE (indice IS NOT NULL)</tt> se corta la iteración de los ciclos.<br /><strong>Línea 28:</strong> Fin del procedimiento.</span><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> CONNECT FERNANDEZ/FERNANDEZ<br />Conectado.<br /> <br />SQL> create or replace<br /> 2 PROCEDURE FETCH_BULK_COLLECT_INTO<br /> 3 IS<br /> 4 TYPE TipoArrayRegistros IS TABLE OF ALL_VIEWS%ROWTYPE;<br /> 5 <span style="color:Green;">ArrayRegistros</span> TipoArrayRegistros;<br /> 6<br /> 7 <strong>CURSOR</strong> <span style="color:Green;">CursorVistas</span> IS SELECT *<br /> 8 FROM ALL_VIEWS<br /> 9 WHERE OWNER = 'SYS'<br />10 AND VIEW_NAME LIKE 'ALL_TAB%';<br />11 <span style="color:Green;">indice</span> PLS_INTEGER;<br />12 BEGIN<br />13<br />14 <strong>OPEN</strong> <span style="color:Green;">CursorVistas</span>;<br />15 <strong>FETCH</strong> <span style="color:Green;">CursorVistas</span> <strong>BULK COLLECT INTO</strong> <span style="color:Green;">ArrayRegistros</span>;<br />16 <strong>CLOSE</strong> <span style="color:Green;">CursorVistas</span>;<br />17<br />18 <span style="color:Green;">indice</span> := <span style="color:Green;">ArrayRegistros</span>.FIRST;<br />19 WHILE (<span style="color:Green;">indice</span> IS NOT NULL)<br />20 LOOP<br />21 DBMS_OUTPUT.PUT_LINE(<span style="color:Red;">'ESQUEMA: '</span> ||<br />22 <span style="color:Green;">ArrayRegistros</span>(<span style="color:Green;">indice</span>).OWNER ||<br />23 <span style="color:Red;">' VISTA : '</span> ||<br />24 <span style="color:Green;">ArrayRegistros</span>(<span style="color:Green;">indice</span>).VIEW_NAME);<br />25 <span style="color:Green;">indice</span> := <span style="color:Green;">ArrayRegistros</span>.NEXT(<span style="color:Green;">indice</span>);<br />26 END LOOP;<br />27<br />28 END FETCH_BULK_COLLECT_INTO;<br />29 /<br /> <br />Procedimiento creado.<br /> <br />SQL> SET SERVEROUTPUT ON<br /> <br />SQL> EXECUTE FETCH_BULK_COLLECT_INTO;<br /><span style="color:Red;">ESQUEMA: SYS VISTA : ALL_TABLES<br />ESQUEMA: SYS VISTA : ALL_TAB_COLS<br />ESQUEMA: SYS VISTA : ALL_TAB_COLUMNS<br />ESQUEMA: SYS VISTA : ALL_TAB_COL_STATISTICS<br />ESQUEMA: SYS VISTA : ALL_TAB_COMMENTS<br />ESQUEMA: SYS VISTA : ALL_TAB_HISTOGRAMS<br />ESQUEMA: SYS VISTA : ALL_TAB_MODIFICATIONS<br />ESQUEMA: SYS VISTA : ALL_TAB_PARTITIONS<br />ESQUEMA: SYS VISTA : ALL_TAB_PRIVS<br />ESQUEMA: SYS VISTA : ALL_TAB_PRIVS_MADE<br />ESQUEMA: SYS VISTA : ALL_TAB_PRIVS_RECD<br />ESQUEMA: SYS VISTA : ALL_TAB_STATISTICS<br />ESQUEMA: SYS VISTA : ALL_TAB_STATS_HISTORY<br />ESQUEMA: SYS VISTA : ALL_TAB_SUBPARTITIONS</span><br /> <br />Procedimiento PL/SQL terminado correctamente.<br /> <br />SQL></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Podemos leer este tema en la documentación estandar de Oracle:<br /><br />Manual: Oracle Database PL/SQL User's Guide and Reference<br />Capítulo: 6 Performing SQL Operations from PL/SQL<br />Título: Managing Cursors in PL/SQL <br />Subtítulo: Explicit Cursors <br />Párrafo: <a href="http://download-east.oracle.com/docs/cd/B19306_01/appdev.102/b14261/sqloperations.htm#sthref1320">Fetching Bulk Data with a Cursor</a><br /><br />Entradas relacionadas:<br /><a href="http://databasetutorial.blogspot.com/2007/06/plsql-tutorial.html">PL/SQL Tutorial</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/cursor-plsql-notacion-selectinto.html">Cursores PL/SQL notación SELECT..INTO</a><br /><a href="http://databasetutorial.blogspot.com/2007/05/create-or-replace-procedure.html">Cursores PL/SQL notación FOR...LOOP</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql.html">Cursores PL/SQL notación CURSOR...FOR...LOOP</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql_13.html">Cursores PL/SQL notación OPEN...FETCH...CLOSE</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql_16.html">Cursores PL/SQL notación FETCH...BULK COLLECT INTO...</a></span>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-3192562119738599305.post-6087882164653925602007-10-10T18:05:00.000-07:002007-12-18T02:28:15.209-08:00Cursores PL/SQL notación OPEN...FETCH...CLOSE<span style="font-family:georgia;font-size:100%;color:#000000;">Esta notación de cursores PL/SQL nos brinda flexibilidad agregando a cambio cierta complejidad. Declaramos el cursor y la sentencia SELECT asociada en la línea 4. Esta declaración es independiente de la ejecución de la sentencia y del recupero de sus filas. La variable Registro es declarada en forma explícita en la línea 8. <strong>Abrimos explícitamente el cursor en la línea 10 con la instrucción OPEN. Este es el momento donde se seleccionan la filas que luego seán recuperadas. Si se agregan filas a la tabla antes de ser recuperadas no serán mostradas. La foto de los datos a recuperar se toma en un punto en el tiempo y los cambios posteriores no son reflejados.</strong> Recuperamos las filas seleccionadas con la instrucción FETCH de la línea 12. Cada FETCH recupera una fila. Luego preguntamos en la línea 13 si no se encontraron filas para procesar, si es así salimos el ciclo de procesamiento de filas. Mostramos los valores de cada fila recuperada en la línea 16. Cerramos el cursor en forma explícita en la línea 21.</span><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> conn fernandez/fernandez<br />Conectado.<br /> <br />SQL> create or replace<br /> 2 PROCEDURE CURSOR_OPEN_FETCH<br /> 3 IS<br /> 4 <strong>CURSOR</strong> <span style="color:Green;">CursorVistas</span><br /> 5 <strong>IS</strong> (SELECT OWNER, VIEW_NAME<br /> 6 FROM ALL_VIEWS<br /> 7 WHERE OWNER = 'SYS'<br /> 8 AND VIEW_NAME LIKE 'ALL_TAB%');<br /> 9 <span style="color:Green;">Registro</span> CursorVistas%ROWTYPE;<br />10 BEGIN<br />11 <strong>OPEN</strong> <span style="color:Green;">CursorVistas</span>;<br />12 LOOP<br />13 <strong>FETCH</strong> <span style="color:Green;">CursorVistas</span> INTO <span style="color:Green;">Registro</span>;<br />14 IF <span style="color:Green;">CursorVistas</span><strong>%NOTFOUND</strong> THEN<br />15 EXIT;<br />16 END IF;<br />17 DBMS_OUTPUT.PUT_LINE('ESQUEMA: ' ||<br />18 <span style="color:Green;">Registro.OWNER</span> ||<br />19 ' VISTA: ' ||<br />20 <span style="color:Green;">Registro.VIEW_NAME</span>);<br />21 END LOOP;<br />22 <strong>CLOSE</strong> <span style="color:Green;">CursorVistas;</span><br />23 END CURSOR_OPEN_FETCH;<br />24 /<br /> <br />Procedimiento creado.<br /></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Verificamos la existencia del procedimiento creado.</span><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> DESCRIBE CURSOR_OPEN_FETCH<br />PROCEDURE cursor_open_fetch<br /></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Verificamos si están habilitados los mensajes por pantalla. Si no lo están, los habilitamos.<span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> SHOW SERVEROUTPUT<br />serveroutput OFF<br /> <br />SQL> SET SERVEROUTPUT ON<br /></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Ejecutamos el procedimiento y vemos los mensajes en pantalla asociados a la recuperación de cada fila del cursor.</span><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> EXECUTE CURSOR_OPEN_FETCH<br />ESQUEMA: SYS VISTA: ALL_TABLES<br />ESQUEMA: SYS VISTA: ALL_TAB_COLS<br />ESQUEMA: SYS VISTA: ALL_TAB_COLUMNS<br />ESQUEMA: SYS VISTA: ALL_TAB_COL_STATISTICS<br />ESQUEMA: SYS VISTA: ALL_TAB_COMMENTS<br />ESQUEMA: SYS VISTA: ALL_TAB_HISTOGRAMS<br />ESQUEMA: SYS VISTA: ALL_TAB_MODIFICATIONS<br />ESQUEMA: SYS VISTA: ALL_TAB_PARTITIONS<br />ESQUEMA: SYS VISTA: ALL_TAB_PRIVS<br />ESQUEMA: SYS VISTA: ALL_TAB_PRIVS_MADE<br />ESQUEMA: SYS VISTA: ALL_TAB_PRIVS_RECD<br />ESQUEMA: SYS VISTA: ALL_TAB_STATISTICS<br />ESQUEMA: SYS VISTA: ALL_TAB_STATS_HISTORY<br />ESQUEMA: SYS VISTA: ALL_TAB_SUBPARTITIONS<br /> <br />Procedimiento PL/SQL terminado correctamente.<br /> <br />SQL></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;"> Podemos leer este tema en la documentación estandar de Oracle:<br /><br />Manual: Oracle Database PL/SQL User's Guide and Reference<br />Capítulo: 6 Performing SQL Operations from PL/SQL<br />Título: Managing Cursors in PL/SQL<br />Subtítulo: <a href="http://download-east.oracle.com/docs/cd/B19306_01/appdev.102/b14261/sqloperations.htm#sthref1293">Explicit Cursors</a><br /><br />También podemos leer una descripción amplia sobre cómo decide el motor de la base de datos qué datos mostrar al ejecutar el OPEN del cursor en la documentación estandar de Oracle:<br /><br />Manual: Oracle Database Concepts<br />Capítulo: 13 Data Concurrency and Consistency<br />Título: How Oracle Manages Data Concurrency and Consistency <br />Subtítulo: <a href="http://download-east.oracle.com/docs/cd/B19306_01/server.102/b14220/consist.htm#sthref1955">Statement-Level Read Consistency</a><br /><br />Entradas relacionadas:<br /><a href="http://databasetutorial.blogspot.com/2007/06/plsql-tutorial.html">PL/SQL Tutorial</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/cursor-plsql-notacion-selectinto.html">Cursores PL/SQL notación SELECT..INTO</a><br /><a href="http://databasetutorial.blogspot.com/2007/05/create-or-replace-procedure.html">Cursores PL/SQL notación FOR...LOOP</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql.html">Cursores PL/SQL notación CURSOR...FOR...LOOP</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql_13.html">Cursores PL/SQL notación OPEN...FETCH...CLOSE</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql_16.html">Cursores PL/SQL notación FETCH...BULK COLLECT INTO...</a></span>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3192562119738599305.post-15287402555864884322007-10-09T19:35:00.000-07:002007-12-18T02:28:39.920-08:00Cursores PL/SQL notación CURSOR...FOR...LOOP<span style="font-family:georgia;font-size:100%;color:#000000;">Esta notación de cursores PL/SQL agrega a la notación mas simple FOR...LOOP la posibilidad de definir la sentencia SELECT asociada al cursor en forma independiente del recupero de las filas. Podemos reutilizar el cursor definido ganando flexibilidad y facilidad de mantenimiento. Nuevamente la variable <b>Registro</b> es automática creándose y destruyéndose en forma implicita. Sólo es referenciable dentro del contexto del FOR...LOOP...END LOOP. Los miembros o campos de la variable <b>Registro</b> están definidos por las columnas detalladas en la lista de la sentencia SELECT asociada al cursor (OWNER y VIEW_NAME).<br /></span><br /><span style="font-family:courier new;font-size:80%;color:#3333ff;"><pre>SQL> create or replace<br /> 2 PROCEDURE CURSOR_FOR_LOOP<br /> 3 IS<br /> 4 <b>CURSOR</b> <span style="color:Green;">CursorVistas</span><br /> 5 <b>IS</b> (SELECT OWNER, VIEW_NAME<br /> 6 FROM ALL_VIEWS<br /> 7 WHERE OWNER = 'SYS'<br /> 8 AND VIEW_NAME LIKE 'ALL_TAB%');<br /> 9 BEGIN<br />10 <b>FOR</b> <span style="color:Green;">Registro</span> <b>IN</b> <span style="color:Green;">CursorVistas</span><br />11 <b>LOOP</b><br />12 DBMS_OUTPUT.PUT_LINE('ESQUEMA: ' ||<br />13 <span style="color:Green;">Registro.OWNER</span> ||<br />14 ' VISTA: ' ||<br />15 <span style="color:Green;">Registro.VIEW_NAME</span>);<br />16 <b>END LOOP;</b><br />17 END CURSOR_FOR_LOOP;<br />18 /<br /> <br />Procedimiento creado.<br /></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Verificamos con el comando DESCRIBE si el procedimiento fue creado, luego lo ejecutamos.</span><span style="font-family:courier new;font-size:85%;color:#3333ff;"><pre>SQL> DESCRIBE CURSOR_FOR_LOOP<br />PROCEDURE CURSOR_FOR_LOOP<br /> <br />SQL> EXECUTE CURSOR_FOR_LOOP;<br /> <br />Procedimiento PL/SQL terminado correctamente.<br /></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Vemos que el procedimiento no muestra los mensajes en pantalla, habilitamos la salida de mensajes y volvemos a ejecutar el procedimiento.</span><span style="font-family:courier new;font-size:85%;color:#3333ff;"><pre>SQL> SET SERVEROUTPUT ON<br /> <br />SQL> EXECUTE CURSOR_FOR_LOOP;<br />ESQUEMA: SYS VISTA: ALL_TABLES<br />ESQUEMA: SYS VISTA: ALL_TAB_COLS<br />ESQUEMA: SYS VISTA: ALL_TAB_COLUMNS<br />ESQUEMA: SYS VISTA: ALL_TAB_COL_STATISTICS<br />ESQUEMA: SYS VISTA: ALL_TAB_COMMENTS<br />ESQUEMA: SYS VISTA: ALL_TAB_HISTOGRAMS<br />ESQUEMA: SYS VISTA: ALL_TAB_MODIFICATIONS<br />ESQUEMA: SYS VISTA: ALL_TAB_PARTITIONS<br />ESQUEMA: SYS VISTA: ALL_TAB_PRIVS<br />ESQUEMA: SYS VISTA: ALL_TAB_PRIVS_MADE<br />ESQUEMA: SYS VISTA: ALL_TAB_PRIVS_RECD<br />ESQUEMA: SYS VISTA: ALL_TAB_STATISTICS<br />ESQUEMA: SYS VISTA: ALL_TAB_STATS_HISTORY<br />ESQUEMA: SYS VISTA: ALL_TAB_SUBPARTITIONS<br /> <br />Procedimiento PL/SQL terminado correctamente.<br /> <br />SQL></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Esta notación de cursores nos permite definir el cursor una vez y ejecutarlo múltiples veces. Se facilita al motor de la base de datos la tarea de reconocer la ejecución frecuente de la misma sentencia SELECT, simplificando la reutilización y ahorrando recursos. <br /><br />También podemos leer este tema en la documentación estandar de Oracle:<br /><br />Manual: Oracle Database PL/SQL User's Guide and Reference<br />Capítulo: 6 Performing SQL Operations from PL/SQL<br />Título: Querying Data with PL/SQL<br />Subtítulo: <a href="http://download-east.oracle.com/docs/cd/B19306_01/appdev.102/b14261/sqloperations.htm#sthref1363">Querying Data with PL/SQL: Explicit Cursor FOR Loops</a><br /><br />Entradas relacionadas:<br /><a href="http://databasetutorial.blogspot.com/2007/06/plsql-tutorial.html">PL/SQL Tutorial</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/cursor-plsql-notacion-selectinto.html">Cursores PL/SQL notación SELECT..INTO</a><br /><a href="http://databasetutorial.blogspot.com/2007/05/create-or-replace-procedure.html">Cursores PL/SQL notación FOR...LOOP</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql.html">Cursores PL/SQL notación CURSOR...FOR...LOOP</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql_13.html">Cursores PL/SQL notación OPEN...FETCH...CLOSE</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql_16.html">Cursores PL/SQL notación FETCH...BULK COLLECT INTO...</a></span>Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-3192562119738599305.post-24711880773454919092007-10-08T18:57:00.000-07:002007-12-18T02:29:05.291-08:00Cursores PL/SQL notación FOR...LOOP<span style="font-family:georgia;font-size:100%;color:#000000;">La selección mas simple de múltiples filas de una tabla se define mediante la notación FOR...LOOP...END LOOP.<br />El primer paso que mostramos es la creación de un procedimiento almacenado o stored procedure de nombre FOR_LOOP. La variable <b>Registro</b> es automática, sólo existe dentro del alcance del FOR...LOOP...END LOOP. No puede ser referenciada fuera del mismo. Los miembros o campos de la variable <b>Registro</b> están definidos por las columnas detalladas en la lista de la sentencia SELECT asociada al cursor (OWNER y VIEW_NAME).<br /></span><span style="font-family:courier new;color:#3333ff;font-size:80%;"><pre>SQL> create or replace<br /> 2 PROCEDURE FOR_LOOP<br /> 3 IS<br /> 4 BEGIN<br /> 5 <b>FOR</b> <span style="color:Green;">Registro</span> <b>IN</b> (SELECT OWNER, VIEW_NAME<br /> 6 FROM ALL_VIEWS<br /> 7 WHERE OWNER = 'SYS'<br /> 8 AND VIEW_NAME LIKE 'ALL_TAB%')<br /> 9 <b>LOOP</b><br />10 DBMS_OUTPUT.PUT_LINE('ESQUEMA: ' ||<br />11 <span style="color:Green;">Registro.OWNER</span> ||<br />12 ' VISTA: ' ||<br />13 <span style="color:Green;">Registro.VIEW_NAME</span>);<br />14 <b>END LOOP;</b><br />15 END FOR_LOOP;<br />16 /<br /> <br />Procedimiento creado.</pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Luego verificamos si el procedimiento fue efectivamente creado con el comando DESCRIBE.</span><span style="font-family:courier new;color:#3333ff;font-size:80%;"><br /><pre>SQL> DESCRIBE FOR_LOOP<br />PROCEDURE FOR_LOOP<br /></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Una vez confirmada su existencia habilitamos la salida de mensajes en pantalla y ejecutamos el procedimiento con el comando EXECUTE.</span><br /><span style="font-family:courier new;color:#3333ff;font-size:80%;"><pre>SQL> SET SERVEROUTPUT ON<br /> <br />SQL> EXECUTE FOR_LOOP;<br />ESQUEMA: SYS VISTA: ALL_TABLES<br />ESQUEMA: SYS VISTA: ALL_TAB_COLS<br />ESQUEMA: SYS VISTA: ALL_TAB_COLUMNS<br />ESQUEMA: SYS VISTA: ALL_TAB_COL_STATISTICS<br />ESQUEMA: SYS VISTA: ALL_TAB_COMMENTS<br />ESQUEMA: SYS VISTA: ALL_TAB_HISTOGRAMS<br />ESQUEMA: SYS VISTA: ALL_TAB_MODIFICATIONS<br />ESQUEMA: SYS VISTA: ALL_TAB_PARTITIONS<br />ESQUEMA: SYS VISTA: ALL_TAB_PRIVS<br />ESQUEMA: SYS VISTA: ALL_TAB_PRIVS_MADE<br />ESQUEMA: SYS VISTA: ALL_TAB_PRIVS_RECD<br />ESQUEMA: SYS VISTA: ALL_TAB_STATISTICS<br />ESQUEMA: SYS VISTA: ALL_TAB_STATS_HISTORY<br />ESQUEMA: SYS VISTA: ALL_TAB_SUBPARTITIONS<br /> <br />Procedimiento PL/SQL terminado correctamente.<br /> <br />SQL></pre></span><span style="font-family:georgia;font-size:100%;color:#000000;">Esta forma de recupero de filas es la mas simple pero la menos flexible de las notaciones de PL/SQL para cursores. Inmediatamente después de definida la sentencia SELECT implementamos el ciclo de procesamiento de las filas recuperadas. Esta proximidad nos facilita la lectura del código fuente pero nos reduce las opciones.<br /><br />También podemos leer este tema en la documentación estandar de Oracle:<br /><br />Manual: Oracle Database PL/SQL User's Guide and Reference<br />Capítulo: 6 Performing SQL Operations from PL/SQL<br />Título: Querying Data with PL/SQL<br />Subtítulo: <a href="http://download-east.oracle.com/docs/cd/B19306_01/appdev.102/b14261/sqloperations.htm#sthref1360">Querying Data with PL/SQL: Implicit Cursor FOR Loop</a><br /><br />Entradas relacionadas:<br /><a href="http://databasetutorial.blogspot.com/2007/06/plsql-tutorial.html">PL/SQL Tutorial</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/cursor-plsql-notacion-selectinto.html">Cursores PL/SQL notación SELECT..INTO</a><br /><a href="http://databasetutorial.blogspot.com/2007/05/create-or-replace-procedure.html">Cursores PL/SQL notación FOR...LOOP</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql.html">Cursores PL/SQL notación CURSOR...FOR...LOOP</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql_13.html">Cursores PL/SQL notación OPEN...FETCH...CLOSE</a><br /><a href="http://databasetutorial.blogspot.com/2007/06/seleccionando-filas-con-cursores-plsql_16.html">Cursores PL/SQL notación FETCH...BULK COLLECT INTO...</a></span>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3192562119738599305.post-19858927458809295532007-10-07T22:00:00.000-07:002007-12-18T02:29:39.532-08:00Primer manual de Oracle para inexpertos<span style="font-size:100%;">Es muy importante que siendo aprendices tomemos pronto contacto con el manual <strong>Oracle Database Concepts</strong>. Comprende una extensa guía general sobre la que se apoya el resto de la documentación oficial. Es la base de todo conocimiento de desarrollo y administración de bases de datos Oracle.<br /><br />- Nos contará el ABC de la base de datos y los conceptos fundamentales.<br />- No nos dirá cómo codificar aplicaciones ni cómo realizar tareas de administración.<br /><br />Podemos encontrar el manual en el sitio de la Oracle Technology Network. El acceso aunque gratuito <strong>requiere una suscripción por única vez</strong>.<br /><br /><a href="http://download-east.oracle.com/docs/cd/B19306_01/server.102/b14220/toc.htm">Acceso al manual Oracle Database Concepts en OTN</a><br /><a href="http://www.oracle.com/pls/db102/homepage">Acceso a la biblioteca de documentación en OTN</a><br /><br />Hemos sido espectadores de situaciones como ésta que habrían sido evitadas con una simple lectura previa al alcance la mano:<br /><br /></span><span style="font-family:courier new;font-size:85%;">Gerente de IT: ¿Qué sucede Pérez? Los usuarios de todas las sucursales acusan lentitud general en el módulo de ventas y facturación...<br />DBA Senior: No sabemos. No tocamos nada. Habrá sido la implementación de la última versión del sistema de gestión comercial.<br />... horas después...<br />DBA Senior: Señor Gerente, DESCUBRIMOS que si no agregamos un índice a las columnas que componen la clave foránea de cierta tabla las consultas recorren toda la tabla de principio a fin generando una altísima transferencia de entrada/salida a la memoria y a los discos...</span>Unknownnoreply@blogger.com6