<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6230959840389481677</id><updated>2012-02-16T00:10:57.664-08:00</updated><category term='plsql'/><category term='Google Maps'/><category term='vmware'/><category term='sqlloader'/><category term='timesten'/><category term='APEX'/><category term='9i'/><category term='plsql developer'/><category term='sqlplus'/><category term='10g'/><category term='patches'/><category term='suse'/><category term='estadísticas'/><category term='Application Server'/><category term='awk'/><category term='dbms_metadata'/><category term='sql'/><category term='soundex espanol espanhol plsql'/><category term='dblink'/><category term='unix'/><category term='11g'/><category term='noticias'/><category term='windows'/><category term='NLS'/><category term='utl_file'/><category term='Error'/><category term='índices'/><category term='Forms'/><category term='errores'/><category term='solaris'/><category term='xe'/><title type='text'>Oracle Notepad</title><subtitle type='html'>Notas sobre Oracle, PL/SQL, Linux, AWK, buenas prácticas, soluciones, y experiencia personal.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>82</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8053843071916852179</id><published>2010-09-30T04:44:00.000-07:00</published><updated>2010-10-18T05:01:54.346-07:00</updated><title type='text'>Flash Recovery Area</title><content type='html'>A partir de Oracle 10g R1 aparece un recurso muy interesante en la base de datos, destinado a guardar y organizar fácilmente todos aquellos archivos que hacen a la recuperación de la base de datos: Flash Recover Area (FRA). En ella se almacenan backups, copias de datafiles, controlfiles, archivelogs. Esta forma de manejar los archivos hace la tarea del DBA más eficiente, ya que permite recuperar una falla más rapidamente y realiza algunas funciones de limpieza automáticamente.&lt;br /&gt;Como esta opción no viene activada por defecto, es necesario alterar un par de parámetros con ALTER SYSTEM para que entre en efecto. Nada complicado. No necesitaremos reiniciar la base, a menos que no la tengamos en ARCHIVELOG mode. Este último modo es requerido.&lt;br /&gt;&lt;br /&gt;1) Colocar la base de datos en modo ARCHIVELOG (en caso que no lo esté)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new; color: rgb(0, 153, 0);"&gt;SHUTDOWN IMMEDIATE;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new; color: rgb(0, 153, 0);"&gt;STARTUP MOUNT;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new; color: rgb(0, 153, 0);"&gt;ARCHIVE LOG START;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new; color: rgb(0, 153, 0);"&gt;ALTER DATABASE ARCHIVELOG;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new; color: rgb(0, 153, 0);"&gt;ALTER DATABASE OPEN;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;2) Crear un directorio destino para los archivos via sistema operativo. En nuestro caso será: /u04/fra&lt;br /&gt;En caso de estar usando ASM es exactamente igual, crearemos un grupo llamado por ejemplo +FRA.&lt;br /&gt;&lt;br /&gt;3) Configurar los siguientes parámetros con usuario sys:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new; color: rgb(0, 153, 0);"&gt;SQL&gt; ALTER SYSTEM SET DB_RECOVERY_FILE_DEST_SIZE = 10G SCOPE=BOTH SID='*';&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new; color: rgb(0, 153, 0);"&gt;SQL&gt; ALTER SYSTEM SET DB_RECOVERY_FILE_DEST = '/disk1/fra' SCOPE=BOTH SID='*';&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;El tamaño (10G) es un ejemplo y a criterio de cada uno, dependiendo del uso que le vayamos a dar. Se recomienda reservar al menos 2 veces el tamaño actual de la base de datos.&lt;br /&gt;En próximos articulos veremos ejemplos para sacar provecho de nuestra flash recovery área.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8053843071916852179?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8053843071916852179/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8053843071916852179' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8053843071916852179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8053843071916852179'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2010/09/flash-recovery-area.html' title='Flash Recovery Area'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-3073523703853139771</id><published>2010-08-27T05:33:00.001-07:00</published><updated>2010-08-27T05:53:36.065-07:00</updated><title type='text'>Volumenes Netbackup vuelven a scratch</title><content type='html'>Esta es de Veritas Netbackup 6.5, y me consta que para versiones 5 también funciona.&lt;br /&gt;&lt;br /&gt;Solo para enmarcar el problema, tengo configurado un robot de cintas TLD para backup, que administra el respaldo diario de una base de datos Oracle y algunos file systems. El tiempo de retención de los respaldos está definido en 1 mes.&lt;br /&gt;&lt;br /&gt;Ocurrió que algunos volúmenes del pool 'Oracle' volvían al estado indefinido 'scratch' luego que expiraba el tiempo de retención, y debía reasignarlos cada vez que sucedía, volviéndolos al pool original con la Consola de Administración de Netbackup.&lt;br /&gt;&lt;br /&gt;Luego de buscar un poco e intentar alterando archivos de configuración (sin suerte), encontré en un foro de Symantec el siguiente comando online que solucionó mi problema:&lt;br /&gt;&lt;br /&gt;(con usuario root)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;nbemmcmd  -changesetting  -return_unassigned_media_to_scratch_pool no  -machinename &lt;span style="font-style: italic;"&gt;nombre_del_host&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;NBEMMCMD, Version:6.5&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Command completed successfully.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;nombre&gt;&lt;span style="font-style: italic;"&gt;nombre_del_host&lt;/span&gt; es la máquina donde el Netbackup está instalado.&lt;/nombre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-3073523703853139771?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/3073523703853139771/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=3073523703853139771' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3073523703853139771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3073523703853139771'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2010/08/volumenes-netbackup-vuelven-scratch.html' title='Volumenes Netbackup vuelven a scratch'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-26722698670114738</id><published>2010-07-22T13:21:00.001-07:00</published><updated>2010-07-28T08:15:39.938-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='solaris'/><title type='text'>Recuperando dispositivos failed en Solaris</title><content type='html'>Esta semana tuve que aprender a la fuerza algunas pisadas avanzadas con Solaris, para intentar recuperar una unidad de cinta SCSI que no era reconocida. Tanto esfuerzo y sufrimiento merece un post que espero ayude a alguien algún dia.&lt;br /&gt;&lt;br /&gt;La unidad en cuestión aparece entre los dispositivos como failed:&lt;br /&gt;&lt;br /&gt;(como usuario root en Solaris 10)&lt;br /&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-family:courier new;"&gt;cfgadm -al&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Ap_Id                          Type         Receptacle   Occupant     Condition&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;N0.IB6                         PCI-X_I/O_B  connected    configured   ok&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;N0.IB6::pci0                   io           connected    configured   ok&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;N0.IB6::pci1                   io           connected    configured   ok&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;N0.IB6::pci2                   io           connected    configured   ok&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;N0.IB6::pci3                   io           connected    configured   ok&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;N0.SB0                         CPU_V3       connected    configured   ok&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;N0.SB0::cpu0                   cpu          connected    configured   ok&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;....&lt;br /&gt;&lt;span style="font-style: italic;"&gt;(varias entradas)&lt;/span&gt;&lt;br /&gt;...&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;c4                             fc-private   connected    configured   unknown&lt;br /&gt;c4::500110a0008c4980           tape         connected    configured   unknown&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;c5                             fc-private   connected    configured   unknown&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;c5::202800a0b82ae608           disk         connected    configured   unknown&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;c6                             fc-private   connected    configured   unknown&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;c6::500110a0008c497a           med-changer  connected    configured   &lt;span style="color: rgb(255, 0, 0);"&gt;failed&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;De las dos unidades de backup (c4 y c6), la última aparece como failed, y en vez del tipo 'tape' se muestra 'med-changer'.&lt;br /&gt;&lt;br /&gt;En varias fuentes se recomienda desconfigurar el dispositivo para volverlo a configurar, pero el comando no es reconocido:&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;cfgadm -f -c unconfigure c6::500110a0008c497a&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;cfgadm: Configuration operation not supported&lt;/span&gt;&lt;/pre&gt;Cuando intentamos ver la configuración del drive, obtenemos un error de comunicación:&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;luxadm display 500110a0008c497a&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt; Error: SCSI failure. - 500110a0008c497a.&lt;/span&gt;&lt;/pre&gt;El problema responde a un desentendimiento entre el SO y el dispositivo. El hardware ha quedado en un estado inconsistente pero el SO no fue actualizado en tal sentido. Es necesario resetear el link que comunica con el dispositivo. Tener precaución con usar el siguiente comando, asegurarse de informar el id correcto!&lt;br /&gt;&lt;br /&gt;El comando mágico es:&lt;br /&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;luxadm -e forcelip /dev/cfg c6&lt;/pre&gt; Verificar con el comando &lt;span style="font-style: italic;"&gt;cfgadm -al que&lt;/span&gt; el dispositivo ya no tiene el estado 'failed'. Probar el comando &lt;span style="font-style: italic;"&gt;luxadm display&lt;/span&gt; como se muestra anteriormente, debería mostrar la configuración del dispositivo correctamente.&lt;br /&gt;&lt;br /&gt;Si esto no resuelve, los caminos alternativos son dos:&lt;br /&gt;1) Sacar el cable de datos que llega al drive y reconectarlo luego de 2 minutos, verificando el listado de dispositivos.&lt;br /&gt;2) Reiniciar el servidor (obviamente la opción menos deseada en un ambiente en producción).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-26722698670114738?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/26722698670114738/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=26722698670114738' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/26722698670114738'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/26722698670114738'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2010/07/recuperando-dispositivos-failed-en.html' title='Recuperando dispositivos failed en Solaris'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8983520102529213401</id><published>2010-06-24T09:37:00.001-07:00</published><updated>2010-07-28T08:18:03.206-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plsql developer'/><title type='text'>Keep alive en PL/SQL Developer</title><content type='html'>La mayoría de los clientes telnet y ftp tienen una función que nos permite "refrescar" nuestra conexión con un servidor y evitar que la sesión sea expirada. Comúnmente se encuentra como 'keep alive', y básicamente consiste en el envío de paquetes nulos para reiniciar el cronómetro de timeout.&lt;br /&gt;&lt;br /&gt;Son pocas las ocasiones en las cuales he necesitado de un keep alive para PL/SQL Developer, pero me ha ocurrido y para mi desgracia no he encontrado ninguna opción del estilo en la herramienta. Eso dificulta enormemente correr un debug por ejemplo, donde necesitamos avanzar paso a paso y ocasionalmente podemos demorar más que el tiempo de timeout.&lt;br /&gt;&lt;br /&gt;Comentario aparte, una de las cosas que me molestan de PL/SQL Developer es el tiempo que queda trabado cuando se ha desconectado, a veces más de 1 minuto! Pero hay que reconocer que es una herramienta excelente cuando podemos trabajar normalmente.&lt;br /&gt;&lt;br /&gt;Como PL/SQL Developer no permite más de 2 scripts ejecutando al mismo tiempo, debemos descartar la idea de crear un simple script con un loop y un SELECT para refrescar la conexión. Eso no nos permitiría por ejemplo ejecutar un debug sobre nuestra aplicación.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Workaround&lt;/span&gt;&lt;br /&gt;Aunque ignoro si Allaround Automations planea en algún momento incluir algo de ese tipo (tengo la versión 8.0 y por ahora nada), hay una forma de sortear el problema y mantener la conexión activa, permitiéndonos trabajar normalmente. La opción es 'Sessions...' bajo el menú Tools.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_fRmrOAuwnQ4/TCSpNv7ek7I/AAAAAAAAAWw/DplgU-dcqAU/s1600/plsql_auto_refresh.png"&gt;&lt;img style="float: left; margin: 0pt 10px 10px 0pt; cursor: pointer; width: 320px; height: 162px;" src="http://3.bp.blogspot.com/_fRmrOAuwnQ4/TCSpNv7ek7I/AAAAAAAAAWw/DplgU-dcqAU/s320/plsql_auto_refresh.png" alt="" id="BLOGGER_PHOTO_ID_5486696299713434546" border="0" /&gt;&lt;/a&gt;Dentro de Sessions, hay un switch para activar el auto-refresh, como se ve en la imagen a la izquierda.&lt;br /&gt;&lt;br /&gt;Si presionamos botón derecho del mouse sobre ese botón, podemos definir el intervalo de refresh entre 1 y 60 segundos. Un valor de 30 debería ser suficiente la gran mayoría de los casos.&lt;br /&gt;&lt;br /&gt;Me tardé un poco en descubrirlo pero espero que ayude a alguien.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8983520102529213401?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8983520102529213401/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8983520102529213401' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8983520102529213401'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8983520102529213401'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2010/06/keep-alive-en-plsql-developer.html' title='Keep alive en PL/SQL Developer'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_fRmrOAuwnQ4/TCSpNv7ek7I/AAAAAAAAAWw/DplgU-dcqAU/s72-c/plsql_auto_refresh.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-1233465462513220158</id><published>2010-05-27T07:31:00.000-07:00</published><updated>2010-07-28T08:18:39.092-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><title type='text'>Una de funciones analíticas</title><content type='html'>En esta oportunidad veremos una técnica interesante en SQL para relacionar valores de 2 filas consecutivas, para todos los registros de una tabla. El criterio de 'consecutividad' puede estar dado por alguna de las columnas de la tabla, como el id, la fecha de creación del registro, etc.&lt;br /&gt;&lt;br /&gt;En el ejemplo a seguir, tenemos un sistema de registro de tickets, con una tabla histórica que registra el momento en que un ticket cambió de estado.&lt;br /&gt;Lo que queremos hacer es un reporte con el tiempo total en cada uno de esos estados, con un único SQL.&lt;br /&gt;&lt;br /&gt;Esta no es una consulta trivial, ya que cada registro contiene únicamente una fecha, por tanto tendremos que asociar al menos dos de ellos para obtener un intervalo de tiempo.&lt;br /&gt;&lt;br /&gt;Para complicar un poco, lo resolveremos primero con MySQL, que tiene bastante menos potencial que Oracle para este tipo de consultas. De hecho este ejercicio surgió a partir de la necesidad de un colega trabajando con ese motor.&lt;br /&gt;&lt;br /&gt;Comenzamos creando la tabla:&lt;br /&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;CREATE TABLE status_history&lt;br /&gt;(id        INTEGER PRIMARY KEY,&lt;br /&gt;ticket_id INTEGER,&lt;br /&gt;estado    VARCHAR(1),&lt;br /&gt;fecha     DATETIME);&lt;/pre&gt;Incluyamos nuestro primer ticket, con algunos cambios de estado entre A, B y C. Los estados tienen un orden cronológico y es posible volver al mismo repetidas veces.&lt;br /&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;INSERT INTO status_history VALUES (1, 1, 'A', '2010-01-01 14:00:00');&lt;br /&gt;INSERT INTO status_history VALUES (2, 1, 'B', '2010-01-03 18:30:00');&lt;br /&gt;INSERT INTO status_history VALUES (3, 1, 'C', '2010-01-07 10:00:00');&lt;br /&gt;INSERT INTO status_history VALUES (4, 1, 'A', '2010-01-11 12:10:00');&lt;br /&gt;INSERT INTO status_history VALUES (5, 1, 'C', '2010-01-14 15:00:00');&lt;/pre&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_fRmrOAuwnQ4/S_6wt49bwAI/AAAAAAAAAWo/ew9jyeQk768/s1600/estados.png"&gt;&lt;img style="float: left; margin: 0pt 10px 10px 0pt; cursor: pointer; width: 154px; height: 145px;" src="http://3.bp.blogspot.com/_fRmrOAuwnQ4/S_6wt49bwAI/AAAAAAAAAWo/ew9jyeQk768/s320/estados.png" alt="" id="BLOGGER_PHOTO_ID_5476008499359760386" border="0" /&gt;&lt;/a&gt;&lt;/pre&gt;Como se puede ver, el ticket 1 comenzó en el estado A, luego pasó al B, al C, volvió a A y finalizó en C. Todos esos cambios registraron únicamente la fecha de inserción.&lt;br /&gt;&lt;br /&gt;Observando los datos de la tabla, deducimos que la diferencia de tiempo entre el segundo registro 'B' y el primero 'A' es tiempo transcurrido en estado 'A' (en el diagrama representado como tA&lt;span style="font-size:85%;"&gt;1&lt;/span&gt;). Lo mismo para el último intervalo tA&lt;span style="font-size:85%;"&gt;2&lt;/span&gt;. La suma de tA&lt;span style="font-size:85%;"&gt;1 y &lt;/span&gt;tA&lt;span style="font-size:85%;"&gt;2&lt;/span&gt; representará el tiempo total que el ticket estuvo en estado 'A'.&lt;br /&gt;&lt;br /&gt;La estrategia será usar un auto-join y relacionar los registros n y n+1. Para eso necesitamos tener un campo secuencial y sin huecos para que la igualdad funcione.&lt;br /&gt;Veremos primero el caso simple donde cada registro tiene un id consecutivo, y luego nos enfocaremos en resolver el problema de los huecos.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;CASO 1 - IDs continuos (1,2,3,4,5....)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En este caso, sabemos que cada id está a una distancia 1 del id siguiente (vale también para otras diferencias), por tanto haremos el join usando la condición id=id+1 para obtener las fechas y la diferencia entre ellas.&lt;pre style="color: rgb(0, 153, 0);"&gt;SELECT s1.estado,&lt;br /&gt;      TIME_TO_SEC(timediff(s2.fecha,s1.fecha))/60/60 AS total_horas&lt;br /&gt;FROM&lt;br /&gt;  status_history s1, status_history s2&lt;br /&gt;WHERE s1.id+1 = s2.id&lt;br /&gt; AND s1.ticket_id=1&lt;br /&gt; AND s2.ticket_id=1&lt;/pre&gt;&lt;br /&gt;Para manipular la diferencia entre dos fechas, usamos primero TIMEDIFF que me retorna un tipo TIME (HH:MI:SS) y luego TIME_TO_SEC para obtener la cantidad total de segundos.&lt;br /&gt;&lt;br /&gt;Obtenemos las siguientes tuplas:&lt;pre style="color: rgb(0, 153, 0);"&gt;ESTADO   TOTAL_HORAS&lt;br /&gt;======   ============&lt;br /&gt;'A'      52.50000000&lt;br /&gt;'B'      87.50000000&lt;br /&gt;'C'      98.16666667&lt;br /&gt;'A'      74.83333333&lt;/pre&gt;&lt;br /&gt;Cabe observar que aparecen dos instancias del estado A, que en el diagrama anterior equivalen a tA&lt;span style="font-size:85%;"&gt;1 y &lt;/span&gt;tA&lt;span style="font-size:85%;"&gt;2. &lt;/span&gt;Para obtener la suma total, agrupamos por estado y utilizamos la función SUM:&lt;pre style="color: rgb(0, 153, 0);"&gt;SELECT s1.estado,&lt;br /&gt;      SUM(TIME_TO_SEC(timediff(s2.fecha,s1.fecha)))/60/60 AS total_horas&lt;br /&gt;FROM&lt;br /&gt;  status_history s1, status_history s2&lt;br /&gt;WHERE s1.id+1 = s2.id&lt;br /&gt; AND s1.ticket_id=1&lt;br /&gt; AND s2.ticket_id=1&lt;br /&gt;GROUP BY estado&lt;/pre&gt;&lt;br /&gt;Finalmente el resultado deseado:&lt;pre style="color: rgb(0, 153, 0);"&gt;ESTADO   TOTAL_HORAS&lt;br /&gt;======   ============&lt;br /&gt;'A'      127.33333333&lt;br /&gt;'B'      87.50000000&lt;br /&gt;'C'      98.16666667&lt;/pre&gt;&lt;span style="font-weight: bold;"&gt;CASO 2 - IDs con huecos (1,2,4,5,9....)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En  el caso anterior, podiamos usar la condicion id=id+1, ya que estabamos seguros que siempre iba a haber una correspondencia (con excepción del último registro). En el caso de existir huecos, el id ya no nos sirve; necesitamos otro campo para ese fin. En Oracle, esto sería muy fácil con la columna virtual rownum, pero en MySQL no tenemos ese recurso.&lt;br /&gt;Hay sin embargo, una forma de simular rownum, y es usando una variable declarada en SQL, inicializada en un select interior. La sintáxis no queda muy amigable pero sirve al propósito de este problema, que es tener un campo secuencial sin huecos.&lt;br /&gt;&lt;br /&gt;Columna virtual ROWNUM con MySQL:&lt;pre style="color: rgb(0, 153, 0);"&gt;SELECT @rownum:=@rownum+1 rownum, t.*&lt;br /&gt;FROM (SELECT @rownum:=0) r, &amp;lt;tabla o consulta&amp;gt; t&lt;/pre&gt;Ahora si, podemos sustituir las tablas por este código y usar rownum como condición.&lt;br /&gt;&lt;br /&gt;Para probarlo con nuestro ejemplo, agregaremos un nuevo cambio de estado, con id=8:&lt;pre style="color: rgb(0, 153, 0);"&gt;INSERT INTO status_history VALUES (8, 1, 'D', '2010-01-16 11:00:00');&lt;/pre&gt;La secuencia de ids ahora es 1, 2, 3, 4, 5, 8&lt;br /&gt;&lt;br /&gt;La consulta queda así:&lt;pre style="color: rgb(0, 153, 0);"&gt;SELECT t1.estado,&lt;br /&gt;      SUM(TIME_TO_SEC(timediff(t2.fecha,t1.fecha)))/60/60 AS total_horas&lt;br /&gt;FROM&lt;br /&gt;  (SELECT @rownum1:=@rownum1+1 rownum, s1.*&lt;br /&gt;   FROM (SELECT @rownum1:=0) r, status_history s1&lt;br /&gt;   WHERE s1.ticket_id=1&lt;br /&gt;   ORDER BY s1.id) t1,&lt;br /&gt;  (SELECT @rownum2:=@rownum2+1 rownum, s2.*&lt;br /&gt;   FROM (SELECT @rownum2:=0) r, status_history s2&lt;br /&gt;   WHERE s2.ticket_id=1   &lt;br /&gt;   ORDER BY s2.id) t2&lt;br /&gt;WHERE t1.rownum+1 = t2.rownum&lt;br /&gt;GROUP BY t1.estado&lt;/pre&gt;Observar que cada subconsulta utiliza su propia variable @rownum, ya que si usarámos la misma, se incrementaría incorrectamente.&lt;br /&gt;&lt;br /&gt;El resultado es:&lt;pre style="color: rgb(0, 153, 0);"&gt;ESTADO   TOTAL_HORAS&lt;br /&gt;======   ============&lt;br /&gt;'A'      127.33333333&lt;br /&gt;'B'      87.50000000&lt;br /&gt;'C'      142.16666667&lt;/pre&gt;Solamente el tiempo de 'C' se vio incrementado con el pasaje de 'C' a 'D'. El estado 'D' no tiene tiempo asociado por tratarse del estado terminal.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;La solución Oracle con Funciones Analíticas&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Como parte final, voy a mostrar cómo el poder de las funciones analíticas de Oracle nos permiten resolver este tipo de problemas con elegancia y sencillez, sin preocuparnos con auto-joins, rownums ni huecos.&lt;br /&gt;La función ideal en este caso es LEAD. Esta función nos retorna el siguiente registro basado en algun criterio de ordenación, que puede ser una columna.&lt;br /&gt;&lt;br /&gt;Todo se resume a usar lo siguiente:&lt;pre style="color: rgb(0, 153, 0);"&gt;LEAD(fecha, 1, NULL) OVER(ORDER BY id)&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;El primer parámetro es la columna que voy a retornar, en mi caso, fecha.&lt;/li&gt;&lt;li&gt;El segundo  es el offset que quiero recuperar, es decir cuantas filas 'adelante'.&lt;/li&gt;&lt;li&gt;El tercer parámetro es el valor que quiero mostrar si no hay siguiente registro.&lt;/li&gt;&lt;li&gt;Por último, la columna que voy a usar como criterio de ordenación.&lt;/li&gt;&lt;/ul&gt;La consulta principal que me da tiempos por estado (no agrupados) queda así:&lt;pre style="color: rgb(0, 153, 0);"&gt;SELECT t1.estado,&lt;br /&gt;      (&lt;span style="color: rgb(0, 0, 153);"&gt;LEAD(fecha, 1, NULL) OVER(ORDER BY id)&lt;/span&gt; - t1.fecha)*24 AS total_horas&lt;br /&gt;FROM   status_history t1&lt;br /&gt;ORDER BY id&lt;/pre&gt;Estoy restando la fecha del registro siguiente (recuperado por LEAD) al registro actual. Ver que tampoco necesito funciones de fechas, basta con restarlas y multiplicar por 24.&lt;br /&gt;&lt;br /&gt;Solo resta agrupar los estados y sumar los totales en una query exterior:&lt;pre style="color: rgb(0, 153, 0);"&gt;SELECT estado,&lt;br /&gt;      SUM(total_horas) total_horas&lt;br /&gt;FROM   (&lt;span style="color: rgb(0, 0, 153);"&gt;SELECT t1.estado,&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;               (LEAD(fecha, 1, NULL) OVER(ORDER BY id) - t1.fecha)*24 total_horas&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;        FROM   status_history t1&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;        ORDER  BY id&lt;/span&gt;)&lt;br /&gt;WHERE total_horas IS NOT NULL&lt;br /&gt;GROUP  BY estado&lt;br /&gt;ORDER BY estado;&lt;/pre&gt;El resultado es el mismo, lo pueden comprobar si se animan a hacer la prueba.&lt;br /&gt;Tengo que agregar la condición total_horas IS NOT NULL, ya que el uso de LEAD no impide que se muestre el estado D con tiempo NULL (recuerdan el tercer parámetro de LEAD?), que en este caso no nos interesa.&lt;br /&gt;&lt;br /&gt;Como comentario, la función LAG nos retorna el registro anterior usando la misma sintáxis que LEAD.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Conclusiones&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Las funciones analíticas son útiles y necesarias en muchos casos. Además de ser más eficientes, nos permiten resolver rápidamente el problema de agrupar y buscar relaciones entre filas de una misma consulta.&lt;br /&gt;&lt;br /&gt;Bases de datos como MySQL no poseen estos recursos, y nos obligan a resolver el problema con mucho más esfuerzo. Esperemos que con la nueva adquisición, Oracle piense en incorporarlas, para beneficio de quienes trabajan con SQL :)&lt;br /&gt;&lt;br /&gt;Mientras tanto, en el caso de MySQL lo importante es determinar cuál es la condición que define la consecutividad entre dos registros, teniendo cuidado si hay huecos. En el caso de Oracle, simplemente es encontrar una relación de precedencia entre dos registros.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://www.oracle.com/pls/db102/to_URL?remark=ranked&amp;amp;urlname=http:%2F%2Fdownload.oracle.com%2Fdocs%2Fcd%2FB19306_01%2Fserver.102%2Fb14200%2Ffunctions001.htm%23SQLRF06174"&gt;Documentación de Funciones Analíticas en Oracle 10g&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-1233465462513220158?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/1233465462513220158/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=1233465462513220158' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1233465462513220158'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1233465462513220158'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2010/05/una-de-funciones-analiticas.html' title='Una de funciones analíticas'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_fRmrOAuwnQ4/S_6wt49bwAI/AAAAAAAAAWo/ew9jyeQk768/s72-c/estados.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-5218044242829721846</id><published>2010-04-13T12:04:00.001-07:00</published><updated>2010-06-25T06:22:42.660-07:00</updated><title type='text'>Listar parámetros indocumentados en Oracle</title><content type='html'>Los parámetros indocumentados son controles especiales que modifican el comportamiento de la base de datos, y pueden generar problemas inesperados si no se usan adecuadamente. Deben ser utilizados bajo recomendación específica del soporte. Se identifican fácilmente ya que comienzan típicamente con "_".&lt;br /&gt;&lt;br /&gt;El comando de SQL*Plus &lt;span style="font-style: italic;"&gt;show parameters&lt;/span&gt; lista los valores estandar de la base de datos, y también de aquellos parámetros indocumentados que hemos alterado explícitamente.&lt;br /&gt;&lt;br /&gt;Hay veces que queremos experimentar con algún parámetro indocumentado (no en producción obviamente!), pero necesitamos conocer el valor por defecto que está actualmente en uso. Aquí va una consulta para listarlos, con el valor actual para la sesión y para la instancia.&lt;pre style="color: rgb(0, 153, 0);"&gt;set linesize 131&lt;br /&gt;set pages 9999&lt;br /&gt;col parameter for A30&lt;br /&gt;col description for A40&lt;br /&gt;col "Session Value" for A15&lt;br /&gt;col "Instance Value" for A15&lt;br /&gt;&lt;br /&gt;SELECT&lt;br /&gt;a.ksppinm  "Parameter",&lt;br /&gt;a.ksppdesc "Description",&lt;br /&gt;b.ksppstvl "Session Value",&lt;br /&gt;c.ksppstvl "Instance Value"&lt;br /&gt;FROM&lt;br /&gt;x$ksppi a,&lt;br /&gt;x$ksppcv b,&lt;br /&gt;x$ksppsv c&lt;br /&gt;WHERE&lt;br /&gt;a.indx = b.indx&lt;br /&gt;AND&lt;br /&gt;a.indx = c.indx&lt;br /&gt;AND&lt;br /&gt;a.ksppinm LIKE '/_%' escape '/'&lt;br /&gt;/&lt;/pre&gt;Para volver al valor estándar del parámetro, bastará eliminarlo del archivo de parámetros de la base, si lo hemos incluído en el mismo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-5218044242829721846?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/5218044242829721846/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=5218044242829721846' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/5218044242829721846'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/5218044242829721846'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2010/04/listar-parametros-indocumentados.html' title='Listar parámetros indocumentados en Oracle'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-6909300549818525575</id><published>2010-03-04T08:42:00.001-08:00</published><updated>2010-07-28T08:19:16.739-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><title type='text'>Expresiones regulares en SQL con 9i</title><content type='html'>Recibi un comentario de Laura con el siguiente problema:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic; color: rgb(0, 0, 153);"&gt;"Estoy empezando a trabajar con oracle 9.2 y tengo que cambiar una sentencia que esta en mysql con expresiones regulares a oracle. La sentencia que tengo que pasar a oracle es la siguiente:&lt;br /&gt;&lt;br /&gt;SELECT * FROM prueba WHERE (content REGEXP ?) AND (codigo = ?) AND (nombre = ?) GROUP BY mes&lt;br /&gt;&lt;br /&gt;¿Alguien sabe como puedo pasar a oracle esta sentecia?"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A diferencia de sus sucesores, Oracle 9i tiene un limitado manejo de expresiones regulares, el cual está mas enfocado hacia PL/SQL y no tanto a SQL.&lt;br /&gt;&lt;br /&gt;El paquete para usar expresiones regulares se llama owa_pattern. Basta hacer un&lt;span style="font-style: italic;"&gt; desc owa_pattern&lt;/span&gt; en la consola para saber si tenemos acceso a el, en caso contrario solicitarlo al administrador.&lt;br /&gt;&lt;br /&gt;La función para buscar ocurrencias en expresiones regulares dentro del paquete es:&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;match([cadena_entrada]&lt;valor_entrada&gt;,[expresion_regular]&lt;expresion_regular&gt;)&lt;/expresion_regular&gt;&lt;/valor_entrada&gt;&lt;/span&gt;&lt;br /&gt;Retorna un valor booleano true o false. Y aquí está el dilema, en SQL no existe el tipo booleano como tal, por tanto el motor no 'entiende' que es lo que la función está retornando.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; select owa_pattern.match('ejemplo','ej*o') from dual;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ORA-06552: PL/SQL: Statement ignored&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ORA-06553: PLS-382: expression is of wrong type&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;La solución es crear una función traductora en PL/SQL para convertir el booleano retornado por &lt;span style="font-style: italic;"&gt;match&lt;/span&gt; a un tipo válido en SQL, como por ejemplo un numérico. Este wrapper recibirá los mismos parámetros que la función original.&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;create or replace function sql_match (exp varchar2, regexp varchar2) return number as&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  begin&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;    if (owa_pattern.match(exp, regexp)) then&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;      return 1;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;    else&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;      return 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;    end if;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  end;&lt;/span&gt;&lt;/pre&gt;La ponemos en práctica:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; select sql_match('ejemplo','ej.*o') res from dual;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;       RES&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;----------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;         1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ahora esta función es perfectamente compatible con SQL, y podemos usarla para evaluar el valor de una columna:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ejemplo&lt;/span&gt;: nombres de empleados&lt;span style="font-style: italic;"&gt;&lt;/span&gt; que comienzan con 'A'&lt;br /&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;SQL&gt; select ename, sql_match(ename,'^A.*') res from scott.emp;&lt;br /&gt;&lt;br /&gt;ENAME             RES&lt;br /&gt;---------- ----------&lt;br /&gt;SMITH               0&lt;br /&gt;ALLEN               1&lt;br /&gt;WARD                0&lt;br /&gt;JONES               0&lt;br /&gt;MARTIN              0&lt;br /&gt;BLAKE               0&lt;br /&gt;CLARK               0&lt;br /&gt;SCOTT               0&lt;br /&gt;KING                0&lt;br /&gt;TURNER              0&lt;br /&gt;ADAMS               1&lt;br /&gt;JAMES               0&lt;br /&gt;FORD                0&lt;br /&gt;MILLER              0&lt;br /&gt;&lt;br /&gt;14 rows selected&lt;/pre&gt;Ahora si, volviendo al problema original planteado por nuestra amiga, la traducción de MySQL a Oracle 9i queda:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SELECT * FROM  prueba WHERE (sql_match(content, ?)=1) AND (codigo = ?) AND (nombre = ?) GROUP  BY mes&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Como dije anteriormente, en versiones a partir de 10g, contamos con expresiones regulares prontas para SQL, como la función REGEXP_LIKE. El ejemplo aplicado a este caso quedaría de esta forma:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SELECT * FROM  prueba WHERE regexp_like(content, ?) AND (codigo = ?) AND (nombre = ?)  GROUP  BY mes&lt;/span&gt;&lt;br /&gt;&lt;span class="style6"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-6909300549818525575?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/6909300549818525575/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=6909300549818525575' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6909300549818525575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6909300549818525575'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2010/03/expresiones-regulares-en-sql-con-9i.html' title='Expresiones regulares en SQL con 9i'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-4583928547589525214</id><published>2010-02-10T06:14:00.000-08:00</published><updated>2010-03-30T10:54:50.927-07:00</updated><title type='text'>Critical Patch Update Febrero 2010</title><content type='html'>Oracle lanzó el último 4 de febrero una actualización de seguridad para sus servidores Weblogic, para las siguientes versiones:&lt;br /&gt;&lt;br /&gt;&lt;table cellborder="0" style="margin-left: 0.25in;" summary="BEA Weblogic" class="texta" border="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;• Oracle WebLogic Server 11gR1 releases (10.3.1 and 10.3.2)&lt;/td&gt;     &lt;td&gt;     &lt;br /&gt;&lt;/td&gt;   &lt;/tr&gt;    &lt;tr&gt;     &lt;td&gt; • Oracle WebLogic Server 10gR3 release (10.3.0)&lt;/td&gt;     &lt;td&gt;     &lt;br /&gt;&lt;/td&gt;   &lt;/tr&gt;    &lt;tr&gt;     &lt;td&gt; • Oracle WebLogic Server 10.0  through MP2&lt;/td&gt;     &lt;td&gt;     &lt;br /&gt;&lt;/td&gt;   &lt;/tr&gt;   &lt;tr&gt;     &lt;td&gt; • Oracle WebLogic Server 9.0, 9.1, 9.2  through MP3&lt;/td&gt;     &lt;td&gt;     &lt;br /&gt;&lt;/td&gt;   &lt;/tr&gt;   &lt;tr&gt;     &lt;td&gt; • Oracle WebLogic Server 8.1  through SP6 &lt;/td&gt;     &lt;td&gt;     &lt;br /&gt;&lt;/td&gt;   &lt;/tr&gt;   &lt;tr&gt;     &lt;td&gt; • Oracle WebLogic Server 7.0  through SP7 &lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;Este patch es de extrema urgencia y se recomienda la inmediata aplicación. Tal es así que se adelantó a la próxima actualización agendada (Oracle lanza patches regulares a mitad de abril, julio, octubre y enero). En este caso, una vulnerabilidad en el manejador de nodos permite acceder sin autenticación.&lt;br /&gt;&lt;br /&gt;Recientemente un experto de seguridad había afirmado en una convención de hackers que 9 de cada 10 bases de datos Oracle podían ser violadas externamente, gracias a una vulnerabilidad que el encontró (aunque no mostró pruebas de hechos concretados). Este experto inglés, autor de varios libros, ya ha detectado decenas de bugs en productos como SQL Server, DB2, Informix, y Oracle. La companía aparentemente no se manifestó sobre este asunto.&lt;br /&gt;&lt;br /&gt;Cierto o no, lo más recomendable es estar precavidos.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a target="_blank" href="http://www.oracle.com/technology/deploy/security/alerts.htm"&gt;http://www.oracle.com/technology/deploy/security/alerts.htm&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://www.reuters.com/article/idUSTRE6125FB20100203?type=technologyNews"&gt;http://www.reuters.com/article/idUSTRE6125FB20100203?type=technologyNews&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-4583928547589525214?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/4583928547589525214/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=4583928547589525214' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4583928547589525214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4583928547589525214'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2010/02/critical-patch-update-febrero-2010.html' title='Critical Patch Update Febrero 2010'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-4791230074705589734</id><published>2010-01-07T05:31:00.000-08:00</published><updated>2010-03-30T10:57:17.158-07:00</updated><title type='text'>Como exportar e importar código PL/SQL en 9i</title><content type='html'>A medida que avanzan las versiones de Oracle, cada vez se hace más dificil encontrar documentación para las anteriores, como por ejemplo 8i y 9i. Por suerte existen los blogs :)&lt;br /&gt;&lt;br /&gt;En tiempos de 9i, EXP e IMP eran las opciones para exportar e importar objetos en nuestro esquema de base de datos. A partir de Oracle 10g, existen versiones mejoradas como Data Pump, que repasaré en otro momento.&lt;br /&gt;&lt;br /&gt;Una duda que surge es como exportar e importar el código PL/SQL de un esquema para tener un rápido backup/restore. Hay algunos métodos que se basan en capturar el código en archivos (&lt;a target="_blank" href="http://oraclenotepad.blogspot.com/2007/09/un-extractor-de-ddl-en-archivos.html"&gt;ver artículo&lt;/a&gt;), pero a la hora de llevar el código rápidamente con sus grants, todo de una vez a otro lugar, lo mejor es usar EXP/IMP.&lt;br /&gt;&lt;br /&gt;Primero que nada, debemos saber que EXP/IMP trabaja en cuatro modos:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;TABLE&lt;/li&gt;&lt;li&gt;TABLESPACE&lt;/li&gt;&lt;li&gt;USER&lt;/li&gt;&lt;li&gt;FULL&lt;/li&gt;&lt;/ul&gt;Si queremos exportar el código, deberemos usar el modo USER, o FULL (todos los esquemas).&lt;br /&gt;Ahora, la única forma que IMP recupera el código PL/SQL, es importando en modo FULL. Aunque parezca extraño, el modo de importación USER no lo recupera aunque hayamos hecho el export de la misma forma.&lt;br /&gt;&lt;br /&gt;Hecha esta aclaración, pasamos a los comandos.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Generar el dump&lt;/span&gt; (en modo USER)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;exp usuario/password file=/tmp/bck_usuario.dmp owner=usuario rows=N triggers=Y indexes=N constraints=N log=/tmp/bck_usuario.log&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Importar el código&lt;/span&gt; (en modo FULL)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;imp usuario/password file=/tmp/bck_usuario.dmp full=Y ignore=y indexes=N log=/tmp/bck_usuario.log&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Observaciones&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Junto con el export, estamos capturando las tablas también (sin datos). El método EXP no es muy flexible en este sentido. Sin embargo a la hora de importar, con el parámetro ignore=Y, estaremos ignorando los intentos de creación si las mismas ya existen en el destino.&lt;/li&gt;&lt;li&gt;Como IMP únicamente puede utilizarse en modo FULL, si queremos traer solamente un usuario entonces deberemos generar un export de tipo USER.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-4791230074705589734?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/4791230074705589734/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=4791230074705589734' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4791230074705589734'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4791230074705589734'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2010/01/como-exportar-e-importar-codigo-plsql.html' title='Como exportar e importar código PL/SQL en 9i'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-3686071550808912136</id><published>2009-12-18T10:36:00.000-08:00</published><updated>2009-12-18T12:06:56.762-08:00</updated><title type='text'>Cómo saber el tipo de Linux para instalar Oracle</title><content type='html'>Alguna vez caímos en paracaídas a instalar algún producto Oracle en un servidor Linux desconocido, e inmediatamente surgieron las siguientes interrogantes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;¿Qué versión de Linux es?&lt;/li&gt;&lt;li&gt;¿Cuál es la versión de Oracle que debo descargar?&lt;/li&gt;&lt;/ul&gt;La base de datos Oracle es el producto más portable del mercado, y por eso dispone de muchas versiones dependiendo de la arquitectura y versión del sistema operativo. Todo dba se ha visto confundido más de una vez al momento de elegir la versión correcta.&lt;br /&gt;&lt;br /&gt;Por ese motivo recomiendo tener esta guía-ayuda a mano, para no perder tiempo a la hora de elegir.&lt;br /&gt;&lt;br /&gt;Mapea los códigos informados por el comando Unix &lt;span style="font-style: italic;"&gt;uname&lt;/span&gt;, con la versión de Linux que aparece en la página de downloads de Oracle. Basta con observar los últimos tres identificadores que aparecen en la salida del comando.&lt;br /&gt;Por ejemplo para el primer caso sería &lt;span style="font-family:monospace;"&gt;"&lt;/span&gt;&lt;code class="km"&gt;i686 i686 i386".&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Espero que ayude!&lt;br /&gt;&lt;br /&gt;&lt;code class="km"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$ uname -a &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linux hostname 2.6.9-34.0.2.ELsmp #1 SMP Fri Jun 30 10:33:58 EDT 2006 &lt;span style="font-weight: bold;"&gt;i686 i686 i386&lt;/span&gt; GNU/Linux  &lt;/span&gt;&lt;br /&gt;--&gt; &lt;/code&gt;&lt;u&gt;Linux x86:&lt;/u&gt;  (32-bit OS)&lt;br /&gt;&lt;br /&gt;&lt;code class="km"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$ uname -a  &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linux hostname 2.6.9-55.ELsmp #1 SMP Fri Apr 20 16:36:54 EDT 2007 &lt;span style="font-weight: bold;"&gt;x86_64 x86_64 x86_64&lt;/span&gt; GNU/Linux  &lt;/span&gt;&lt;br /&gt;--&gt; &lt;/code&gt;&lt;u&gt;Linux x86_64:&lt;/u&gt;  (64-bit OS)&lt;br /&gt;&lt;br /&gt;&lt;code class="km"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$ uname -a  &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linux hostname 2.6.9-42.EL #1 SMP Wed Jul 12 23:25:09 EDT 2006 &lt;span style="font-weight: bold;"&gt;ia64 ia64 ia64&lt;/span&gt; GNU/Linux &lt;/span&gt;&lt;br /&gt;--&gt; &lt;/code&gt;&lt;u&gt;Linux Itanium:&lt;/u&gt; (64-bit OS)&lt;br /&gt;&lt;br /&gt;&lt;code class="km"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$ uname -a  &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linux hostname 2.6.9-34.0.1.0.11.EL #1 SMP Mon Dec 4 16:10:42 PST 2006 &lt;span style="font-weight: bold;"&gt;ppc64 ppc64 ppc64&lt;/span&gt; GNU/Linux&lt;/span&gt;&lt;br /&gt;--&gt; &lt;/code&gt;&lt;u&gt;IBM Power Based Linux:&lt;/u&gt; (64-bit OS)&lt;br /&gt;&lt;br /&gt;&lt;code class="km"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$ uname -a  &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linux hostname 2.6.16.46-0.12-default #1 SMP Thu May 17 14:00:09 UTC 2007 &lt;span style="font-weight: bold;"&gt;s390x s390x s390x&lt;/span&gt; GNU/Linux&lt;/span&gt;&lt;br /&gt;--&gt; &lt;/code&gt;&lt;u&gt;IBM zSeries Based Linux:&lt;/u&gt; (64-bit OS)&lt;br /&gt;&lt;br /&gt;&lt;code class="km"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$ uname -a  &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linux hostname 2.4.21-50.EL #1 SMP Tue May 8 17:10:38 EDT 2007 &lt;span style="font-weight: bold;"&gt;s390 s390 s390 &lt;/span&gt;GNU/Linux&lt;/span&gt;&lt;br /&gt;--&gt; &lt;/code&gt;&lt;u&gt;IBM S/390 Based Linux (31-bit):&lt;/u&gt; (31-bit OS)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-3686071550808912136?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/3686071550808912136/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=3686071550808912136' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3686071550808912136'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3686071550808912136'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/12/como-saber-el-tipo-de-linux-que-tenemos.html' title='Cómo saber el tipo de Linux para instalar Oracle'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7307847423454890656</id><published>2009-11-28T10:03:00.000-08:00</published><updated>2009-11-30T03:47:08.406-08:00</updated><title type='text'>Controlando el horario de verano en Solaris</title><content type='html'>Solaris 10 viene configurado con cambios de horario de verano automáticos (daylight saving time), basados en archivos de configuración predefinidos con fecha y hora del cambio para cada región.&lt;br /&gt;&lt;br /&gt;Puede ocurrir, como ya pasó este año en Brasil, que el gobierno decida adelantar dicha fecha y entonces eso puede darnos algunos problemas si nos toma desprevenidos. Dependiendo del negocio, algunos sistemas en producción no deben alterar el horario, otros en cambio, deben hacerlo estrictamente según la normativa oficial.&lt;br /&gt;&lt;br /&gt;Para tener el control, tenemos que verificar la configuración en Solaris y ver que está de acuerdo con nuestros intereses.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;COMO ALTERAR LA CONFIGURACION DEL HORARIO DE VERANO&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 102);"&gt;Paso 1 &lt;/span&gt;- Verificar la zona horaria del servidor&lt;br /&gt;&lt;br /&gt;Siempre logueados como root, leemos el archivo TIMEZONE y buscamos el valor de la variable TZ al final del archivo:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;cat /etc/TIMEZONE&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;TZ=Brazil/East&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 102); font-weight: bold;"&gt;Paso 2 &lt;/span&gt;- Hacer un backup de la configuración actual (por si acaso)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;cd /usr/share/lib/zoneinfo&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;cd Brazil&lt;/span&gt;   (o la region que corresponda según el Paso 1)&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;cp East East.backup&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Tal vez encontremos un archivo .zic, con alguna configuración que alguien ya hizo, es aconsejable darle una ojeada y ver si nos sirve para el paso 4.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 102); font-weight: bold;"&gt;Paso 3 &lt;/span&gt;- Verificar configuración del horario de verano, según el TZ encontrado en el Paso 1&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;zdump -v Brazil/East | grep 20&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Encontraremos varias líneas del tipo:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Brazil/East  Sat Nov 28 18:31:41 2009 UTC = Sat Nov 28 15:31:41 2009 BRT isdst=0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Para cada año aparece la correspondencia entre el horario universal (UTC) y el de nuestra región. El ultimo parámetro indica si se trata de horario de verano (isdst=1) o no (isdst=0), por tanto debe haber una diferencia de una hora menos cuando isdst=1.&lt;br /&gt;O sea que si vivimos en UMT-3, en el horario de verano vamos a tener UMT-2.&lt;br /&gt;&lt;br /&gt;Verificar se coincide con el cambio oficial programado para el corriente año.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 102);"&gt;Paso 4 &lt;/span&gt;- Crear un archivo .zic con las alteraciones deseadas&lt;br /&gt;&lt;br /&gt;Crear un nuevo archivo llamado config_dst.zic con el siguiente contenido:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Rule Brazil 2009 only - Oct 11 00:00 1 S&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt; Rule Brazil 2009 only - Oct 20 00:00 0 -&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Zone Brazil/East -3:00 Brazil BR%sT&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;(fin del archivo)&lt;br /&gt;&lt;br /&gt;En el paso 2 decíamos que tal vez ya exista un archivo .zic en el directorio, en ese caso podemos agregar nuestras líneas arriba del todo, así no perdemos el resto de las configuraciones. Cuidado que no existan otras definiciones para el mismo año, porque pueden entrar en conflicto.&lt;br /&gt;&lt;br /&gt;La última línea indica el nombre del archivo de configuración, y en que directorio se encuentra. También define el indicador de horario de verano que veremos cuando usemos el comando &lt;span style="font-style: italic;"&gt;date&lt;/span&gt;. Este será BRST en horario de verano, y BRT en horario normal.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 102); font-weight: bold;"&gt;Paso 5 &lt;/span&gt;- Aplicar la nueva configuración con el comando zic&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;zic config_dst.zic&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:georgia;" &gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102); font-weight: bold;"&gt;Paso 6&lt;/span&gt; - Esperar la fecha prevista y verificar&lt;br /&gt;&lt;br /&gt;Con el comando &lt;span style="font-style: italic;"&gt;date&lt;/span&gt;, verificaremos que el horario es el correcto y el tipo de horario es BRST, tal como lo configuramos.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7307847423454890656?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7307847423454890656/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7307847423454890656' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7307847423454890656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7307847423454890656'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/11/como-controlar-el-horario-de-verano-en.html' title='Controlando el horario de verano en Solaris'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-6733776696194049857</id><published>2009-10-07T07:49:00.000-07:00</published><updated>2009-10-13T08:14:07.048-07:00</updated><title type='text'>Adiós Metalink</title><content type='html'>El tradicional site de soporte oficial de Oracle, más conocido como Metalink (&lt;a href="http://metalink.oracle.com/" target="_blank"&gt;metalink.oracle.com&lt;/a&gt;) será retirado definitivamente el próximo 6 de noviembre, y dejará al nuevo sistema basado en Adobe Flash 9, 'My Oracle Support', como única opción para los clientes.&lt;br /&gt;&lt;br /&gt;Personalmente gusto más del viejo Metalink desarrollado con &lt;a href="http://apex.oracle.com/" target="_blank"&gt;APEX&lt;/a&gt;, el html es sin dudas más estable y sencillo de navegar que la abrumadora interfase de ventanas deslizantes y tiptools de My Oracle Support. No menos importante es el hecho de que Flash no funciona en muchos dispositivos móviles y algunos browsers,  además de obligar a los usuarios a bajar  software adicional. No me molesta que esté allí, siempre y cuando sea opcional.&lt;br /&gt;&lt;br /&gt;Múltiples encuestas en páginas y blogs de Oracle muestran que la mayoría prefieren a Metalink. Yo mismo he colocado mi opinión en varias encuestas de Oracle, cada vez que me ha sido ofrecida la oportunidad. Pero parece que los planes de la corporación son inamovibles y Metalink dejará de existir para el lamento y experiencia de soporte de muchos.&lt;br /&gt;&lt;br /&gt;Actualmente Oracle tiene abierta una encuesta para expresar nuestra opinión, en este &lt;a href="http://www.zoomerang.com/Survey/?p=WEB227VX2FG7GT" target="_blank"&gt;link&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Más información:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://metalink.oracle.com/" target="_blank"&gt;Nota ID 841061.1 en Metalink&lt;/a&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;a href="http://www.oracle.com/support/training-schedule.html?msgid=8237387"&gt;Trainings My Oracle Support&lt;/a&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-6733776696194049857?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/6733776696194049857/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=6733776696194049857' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6733776696194049857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6733776696194049857'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/10/adios-metalink.html' title='Adiós Metalink'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-6404213647814792275</id><published>2009-09-04T06:47:00.000-07:00</published><updated>2009-09-11T11:12:02.771-07:00</updated><title type='text'>Oracle Critical Patch Octubre 2009</title><content type='html'>El set de patches de seguridad que Oracle había programado para publicar el 13 de Octubre de 2009, fue re agendado para el 20 del mismo mes, es decir una semana después.&lt;br /&gt;&lt;br /&gt;Llama la atención el motivo que la corporación dio al respecto: "ya que muchos clientes con responsabilidad de aplicar los patches en sus respectivas empresas van a estar asistiendo al Oracle Open World (11 al 15 de Octubre)".&lt;br /&gt;&lt;br /&gt;Es ese un motivo justificado para retrasar un patch de seguridad? Porque un mínimo porcentaje de clientes va a asistir a tal evento? Acaso todos tienen que aplicarlos al mismo tiempo?&lt;br /&gt;&lt;br /&gt;En realidad quien estará con toda la atención centrada en el evento, será naturalmente la propia compañia, incluyendo algunos responsables por los patches. Pero eso de argumentar que por causa de algunos clientes los retrasamos, no parece lógico ni justo. &lt;br /&gt;Hacen parecer que los patches críticos no son tan críticos.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-6404213647814792275?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/6404213647814792275/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=6404213647814792275' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6404213647814792275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6404213647814792275'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/09/oracle-critical-patch-octubre-2009.html' title='Oracle Critical Patch Octubre 2009'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-4624078177450674599</id><published>2009-08-13T07:00:00.000-07:00</published><updated>2009-09-04T07:15:37.106-07:00</updated><title type='text'>Cómo ocultar el código de PL/SQL</title><content type='html'>Puede el código PL/SQL ser escondido y protegido de miradas ajenas, cuando lo implantamos en alguna base de datos externa?&lt;br /&gt;&lt;br /&gt;La respuesta a esta inquietud es SI, gracias a un ejecutable y un paquete disponible que nos permite compilar un procedimiento almacenado y hacer que el fuente quede confuso para quien intenta leerlo.&lt;br /&gt;Oracle se refiere a este método como ofuscamiento "obfuscation" (el término ofuscar en el diccionario tiene el significado de "oscurecer, encubrir").&lt;br /&gt;&lt;br /&gt;Es curioso como todavía hay empresas que desarrollan software de altísimo valor corporativo y lo implantan expuestamente en bases de datos de usuarios finales que en muchos casos tiene acceso la propia competencia. No todos en la industria son mal intencionados, pero es mejor prevenir que lamentar perder margen de ventaja.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Qué encriptar&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Todo lo que sea código almacenado: procedimientos, funciones, paquetes y tipos. La excepción son los triggers, el método no los soporta, sin embargo una solución es pasar la lógica a un procedure encriptado y llamarlo desde el trigger.&lt;br /&gt;&lt;br /&gt;Una recomendación: usemos un criterio, no seamos paranoicos.&lt;br /&gt;Muy a menudo, parte de nuestro código tiene que ser compartido con otros proveedores, necesitamos disponibilizar ciertos objetos para que otros puedan construir sus propios programas a partir de ellos. No necesitamos ofuscar todos los paquetes de nuestra base de datos, solamente aquellos que tengan lógica de negocio sensible de la compañia, como algoritmos, paquetes financieros, lógica de procesamiento, mantenimiento de cuentas, paquetes de seguridad, etc.&lt;br /&gt;&lt;br /&gt;Oracle no recomienda usar este método para encriptar contraseñas, ya que si abrimos el archivo generado, podremos ver identificadores y reconocer algunas palabras que están presentes en el código original.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Antes de comenzar&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Es importante tener en cuenta que estaremos escondiendo el código de miradas ajenas y hasta de la nuestra, ya que una vez que el código está encriptado en la base de datos, no hay forma ni usuario que pueda recuperarlo. Para realizar modificaciones, hay que hacerlas sobre la versión de texto original. La recomendación es usar un manejador de versiones como repositorio de código, y luego adoptar la práctica de encriptar antes de recompilar.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Cómo encriptar&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Existe un ejecutable en $ORACLE_HOME/bin que se llama &lt;span style="font-weight: bold;"&gt;wrap&lt;/span&gt;.&lt;br /&gt;Llamándolo desde la consola, y pasándole el nombre de un script en el parámetro &lt;span style="font-style: italic;"&gt;iname&lt;/span&gt;, nos retorna un archivo de texto &lt;span style="font-weight: bold;"&gt;.plb&lt;/span&gt; con código interno, el cual podremos compilar en SQL*plus para crear el objeto almacenado "ofuscado".&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ejemplo 1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Vamos a encriptar un procedimiento. Para ello ya tenemos el código del mismo en un archivo ob_proc.sql, copiado en el servidor de la base de datos. Todo lo que tenemos que hacer es entrar en la consola y ejecutar:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;wrap edebug=wrap_new_sql iname=ob_proc.sql&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;PL/SQL Wrapper: Release 9.2.0.8.0- Production on Thu Aug 13 11:38:30 2009&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;  &lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Copyright (c) Oracle Corporation 1993, 2001.  All Rights Reserved.&lt;/span&gt;  &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;br /&gt;&lt;br /&gt;Processing ob_proc.sql to ob_proc.plb&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Nota: El flag &lt;span style="font-style: italic;"&gt;edebug&lt;/span&gt;=wrap_new_sql es necesario para poder soportar el nuevo compilador de sql y corregir un bug existente con algunas sintaxis de sql avanzado.&lt;br /&gt;&lt;br /&gt;La salida, es el archivo encriptado ob_proc.plb, el cual ahora podemos compilar en SQL*plus.&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; @ob_proc.plb&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);font-family:courier new;" &gt;Procedure created.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Esta pronto. El código del procedure en la base de datos no es más legible, ya sea usando cualquier programa de desarrollo, paquete Oracle o vista del diccionario. Sin embargo es perfectamente ejecutable como cualquier otro procedimiento.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ejemplo 2&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ahora encriptaremos un paquete. Se realiza en forma similar, recordando que el paquete se compone por especificación y opcionalmente un cuerpo. Si bien podemos encriptar ambos, se recomienda en la mayoria de los casos encriptar únicamente el cuerpo. Después de todo, es donde reside la lógica que queremos proteger. La especificación es útil muchas veces para consultar la firma de las funciones que están siendo expuestas, y es amable disponibilizarlas para el uso común.&lt;br /&gt;&lt;br /&gt;Teniendo el cuerpo de nuestro paquete preparado en el archivo ob_pack_body.sql, ejecutamos:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;wrap edebug=wrap_new_sql iname=ob_pack_body.sql&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;PL/SQL Wrapper: Release 9.2.0.8.0- Production on Thu Aug 13 11:38:30 2009&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;  &lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Copyright (c) Oracle Corporation 1993, 2001.  All Rights Reserved.&lt;/span&gt;  &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;br /&gt;&lt;br /&gt;Processing ob_pack_&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;body&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;.sql to ob_pack_body.plb&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Si nuestro paquete ya estaba compilado en la base de datos, recordemos que únicamente necesitamos recompilar el cuerpo. De lo contrario, tendremos que compilar la especificación primero.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; @ob_pack_body.plb&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Package body created.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;El código de especificación del paquete, que ya estaba compilado, continúa siendo visible, mientras que el body ahora fue ocultado y no está más disponible a la vista de todos.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/09/un-extractor-de-ddl-en-archivos.html"&gt;Cómo extraer código en archivos separados&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-4624078177450674599?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/4624078177450674599/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=4624078177450674599' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4624078177450674599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4624078177450674599'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/08/como-ocultar-el-codigo-de-plsql.html' title='Cómo ocultar el código de PL/SQL'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-5256508487612012944</id><published>2009-07-15T07:06:00.000-07:00</published><updated>2009-07-29T08:07:06.846-07:00</updated><title type='text'>¿Cuál es el mejor tipo de datos para almacenar IPs?</title><content type='html'>&lt;span class="threadText" id="textNode_21641822"&gt;Esta pregunta surgió en  &lt;a target="_blank" href="http://wiki.oracle.com/thread"&gt;Oracle Wiki&lt;/a&gt;, foro en el cual habitualmente colaboro.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic; color: rgb(51, 51, 51);"&gt;"Alguien ha desarrollado un tipo datos para IP address? Necesito que sea indexable y que pueda usar operaciones OR, AND, XOR para chequear que una IP está en el rango especificado por la red y la máscara."&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="threadText" id="textNode_21675989"&gt;Como todos sabemos, las IPs están compuestas por 4 números que van de 0 a 255, separados por un punto. Esta es una representación posible, la que hace fácil su lectura en términos de redes. Sin embargo, también podemos convertirlas a notación binaria, decimal o hexadecimal.&lt;br /&gt;El modo que elijamos va a beneficiar un aspecto u otro, dependiendo el uso y  aplicación que le estemos dando a la información.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Algunas formas de almacenar IPs&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="threadText" id="textNode_21675989"&gt;Hay muchas maneras de almacenar direcciones IP, y nos bastan los tipos de datos nativos que Oracle ofrece. Estas son las más comunmente utilizadas.&lt;/span&gt;&lt;br /&gt;&lt;span class="threadText" id="textNode_21675989"&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 102, 0);"&gt;Notación de Punto&lt;/span&gt;&lt;br /&gt;Es el típico formato de IP que todos conocemos.&lt;br /&gt;Ejemplo: 127.0.0.1&lt;br /&gt;Tipo de datos sugerido: VARCHAR2(15)&lt;br /&gt;Ventajas: Fácil de leer y recuperar, listo para reportes relacionados a redes.&lt;br /&gt;Desventajas: Requiere convertir para poder usar algebra booleana, no muy eficiente en uso de espacio.&lt;/span&gt;&lt;br /&gt;&lt;span class="threadText" id="textNode_21675989"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="threadText" id="textNode_21675989"&gt;&lt;span style="font-weight: bold; color: rgb(0, 102, 0);"&gt;Hexadecimal&lt;/span&gt;&lt;br /&gt;Es el formato de IP removiendo los puntos y pasando cada componente a hexa.&lt;br /&gt;Ejemplo: &lt;/span&gt;&lt;span class="threadText" id="textNode_21675989"&gt;7F000001&lt;/span&gt;&lt;br /&gt;&lt;span class="threadText" id="textNode_21675989"&gt; Tipo de datos sugerido: VARCHAR2(8)&lt;br /&gt;Ventajas: Fácil de convertir a los otros modos, ocupa menos espacio que la notación de punto.&lt;br /&gt;Desventajas: Requiere conversión para lectura y operaciones booleanas. &lt;/span&gt;&lt;br /&gt;&lt;span class="threadText" id="textNode_21675989"&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 102, 0);"&gt;Decimal&lt;/span&gt;&lt;br /&gt;Es la representación interna de la IP. Usado comúnmente por browsers y algunos protocolos, es la representacion decimal del formato binario.&lt;br /&gt;Ejemplo: &lt;/span&gt;&lt;span class="threadText" id="textNode_21675989"&gt;2130706433 (equivale a 127.0.0.1&lt;/span&gt;)&lt;br /&gt;&lt;span class="threadText" id="textNode_21675989"&gt;Tipo de datos sugerido: NUMBER(10)&lt;br /&gt;Ventajas: Uso de espacio muy optimizado, no requiere conversión para integración con algunos protocolos.&lt;br /&gt;Desventajas: Es el formato más ilegible (para los humanos) de todos.&lt;br /&gt;&lt;/span&gt;&lt;span class="threadText" id="textNode_21675989"&gt;&lt;br /&gt;Nota: Multiplicando cada componente de la IP por una potencia 2^24, 2^16, 2^8 y 2^0, y  sumándolos, obtendremos el entero equivalente.&lt;/span&gt; Hagan la prueba de convertir una IP y colocarla en el browser!&lt;br /&gt;&lt;span class="threadText" id="textNode_21675989"&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 102, 0);"&gt;Notación Binaria&lt;/span&gt;&lt;br /&gt;Podemos tomar cada número de la representación por punto y convertirlo a un octeto binario, completando con ceros a la izquierda.&lt;br /&gt;Ejemplo: 01111111000000000000000000000001 (equivale a 127.0.0.1)&lt;br /&gt;Tipo de datos sugerido: VARCHAR2(32)&lt;br /&gt;Ventajas: Listo para algebra booleana, fácil de convertir a otros formatos.&lt;br /&gt;Desventajas: Poco legible, uso muy ineficiente de espacio.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Conclusiones&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;No hay formatos malos ni buenos, sólo que algunas opciones serán mejores que otras a la hora de decidir. La idea es minimizar ese impacto según  nuestro objetivo.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="threadText" id="textNode_21675989"&gt;El formato de punto es óptimo para lectura de redes, ya que con una vista rápida es posible consultar direcciones IP directamente y presentar reportes sin esfuerzo. Por su claridad, es recomendado para el desarrollo de aplicaciones que registran logs de tráfico.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="threadText" id="textNode_21675989"&gt;El formato hexadecimal es una variante para intentar mejorar el uso de espacio, ya que solamente 8 bytes serán necesarios. Por otro lado, requiere convertir cada par para obtener el formato tradicional y hacerlo más comprensible en lenguaje de red.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="threadText" id="textNode_21675989"&gt;El formato decimal es el más eficaz utilizador de espacio. En sistemas real-time, BD embebidas y dispositivos móviles, el uso de espacio es crítico. Nótese que el tipo sugerido ocuparía 6 bytes en el peor caso, contra 8 fijos que utiliza la forma hexadecimal.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="threadText" id="textNode_21675989"&gt;Finalmente el formato binario es para programadores 'perezosos' que validan IPs, ya que no requieren convertirla. Por ejemplo, si nuestra IP es de tipo A, B o C, realizando la operación (IP) AND (Netmask correspondiente), obtenemos el ID de red. Del lado de las desventajas, ademas de ser difícil de leer, es un gran derrochador de espacio.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-5256508487612012944?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/5256508487612012944/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=5256508487612012944' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/5256508487612012944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/5256508487612012944'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/07/cual-es-el-mejor-tipo-de-datos-para.html' title='¿Cuál es el mejor tipo de datos para almacenar IPs?'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-1340386578960561886</id><published>2009-07-09T13:20:00.000-07:00</published><updated>2009-07-09T13:37:54.923-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='awk'/><title type='text'>Obtener totales con AWK</title><content type='html'>Hay veces que en nuestro trabajo somos solicitados para realizar un spool de datos (millones de registros) y finalmente informar el total de algunas  columnas.&lt;br /&gt;&lt;br /&gt;Dependiendo de varios factores como cantidad de registros, distribución de datos y relacionamientos, la ejecución de la consulta primaria para obtener los datos, puede ser costosa. Un tiempo similar puede tomarnos ejecutar la consulta para obtener el total de las columnas.&lt;br /&gt;&lt;br /&gt;Sin embargo, el uso de AWK para obtener los totales, puede ser un diferencial. La performance de esta operación en el archivo de texto sobre la ejecución de un SQL (usando sum y group by) puede ser abismal (de muchos minutos a un par de segundos).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ejemplo:&lt;/span&gt; como obtener el total de la segunda y tercera columna de un archivo spool con millones de registros y valores separados por pipe '|'.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0); font-family: courier new;"&gt;awk -F"|" '{sum1+=$2; sum2+=$3} END {print sum1,sum2}' archivo_entrada.csv&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Primero en la sección de procesamiento línea a línea (primer par de { })  sumamos los valores de la segunda y tercera columna ($2, $3) en dos variables, sum1 y sum2, por defecto inicializadas en cero.&lt;br /&gt;&lt;br /&gt;Finalmente la sección de ejecución final muestra los valores de sum1 y sum2.&lt;br /&gt;&lt;br /&gt;Si deseamos usar otro delimitador bastará con alterar el parámetro -F.&lt;br /&gt;&lt;br /&gt;Así de simple y eficiente.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-1340386578960561886?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/1340386578960561886/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=1340386578960561886' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1340386578960561886'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1340386578960561886'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/07/obtener-totales-con-awk.html' title='Obtener totales con AWK'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-281069855309593374</id><published>2009-06-12T06:49:00.000-07:00</published><updated>2009-07-10T11:07:35.299-07:00</updated><title type='text'>Ordenar resultados por el alfabeto castellano</title><content type='html'>Por defecto, el ordenamiento de una consulta SQL en Oracle se rige según el valor numérico del mapa de caracteres, típicamente: A, B, C, D, E , F, G, H, I, etc. Este método, que recibe el nombre de ordenamiento binario, es el método más eficiente y conveniente para ordenar palabras en inglés.&lt;br /&gt;&lt;br /&gt;Sin embargo, las palabras del castellano no serán ordenadas correctamente, y muchas veces debemos respetar las reglas de nuestro idioma (aunque la popularidad del inglés diga lo contrario).&lt;br /&gt;&lt;br /&gt;Por ejemplo si listamos países, &lt;span style="font-style: italic;"&gt;Chile&lt;/span&gt; aparecerá antes que &lt;span style="font-style: italic;"&gt;Colombia&lt;/span&gt;, cuando la letra 'CH' debería suceder a la 'C'. Similarmente la palabra &lt;span style="font-style: italic;"&gt;lluvia&lt;/span&gt; aparecerá antes que &lt;span style="font-style: italic;"&gt;lotería&lt;/span&gt;, cuando no deberíamos ignorar que la 'LL' viene despues de la 'L'. Finalmente recordar que nuestra original letra eñe no puede ser dejada de lado.&lt;br /&gt;&lt;br /&gt;Para ordenar usando las letras del español, bastará con alterar la sesión:&lt;br /&gt;&lt;br /&gt;ALTER SESSION SET nls_sort=spanish_m;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ejemplo&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; select * from paises order by nombre;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;NOMBRE&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;------------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Camerún&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Canadá&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Chile&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Colombia&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Congo&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Cuba&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; alter session set nls_sort=spanish_m;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Session altered&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;NOMBRE&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;------------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Camerún&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Canadá&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Colombia&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Congo&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Cuba&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Chile&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Acentos y mayúsculas&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Otro efecto de setear el parametro nls_sort, es que suprimimos la precedencia de palabras acentuadas sobre no acentuadas, así como de mayúsculas sobre minúsculas.&lt;br /&gt;&lt;br /&gt;Ejemplo 1: Problema de acentos&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; select letra from mialfabeto order by letra;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;LETRA&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;--------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;a&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;b&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;c&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;á&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; alter session set nls_sort=spanish_m;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; select letra from mialfabeto order by letra;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt; LETRA&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt; --------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt; a&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;á&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;b&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt; c&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ejemplo 2: Problema de  mayúsculas que anteceden a minúsculas&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; select texto from conceptos order by texto;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;TEXTO&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;--------------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Integrado&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;distribuído&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;integración&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; alter session set nls_sort=spanish_m;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; select texto from conceptos order by texto;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;TEXTO&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;--------------&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;distribuído&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;integración&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Integrado&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-281069855309593374?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/281069855309593374/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=281069855309593374' title='4 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/281069855309593374'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/281069855309593374'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/06/ordenar-resultados-por-el-alfabeto.html' title='Ordenar resultados por el alfabeto castellano'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-647432638521616688</id><published>2009-05-13T10:58:00.000-07:00</published><updated>2009-05-13T13:12:34.986-07:00</updated><title type='text'>Un autodeadlock y algunas buenas prácticas</title><content type='html'>Este deadlock sí que es eficiente. Poco esfuerzo de programación, una única sesión y una espera de un recurso siendo usado por... uno mismo!&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;CREATE OR REPLACE PROCEDURE deadlock_suicida AS&lt;br /&gt;BEGIN&lt;br /&gt;EXECUTE IMMEDIATE 'DROP PROCEDURE deadlock_suicida';&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;Procedure created.&lt;br /&gt;&lt;br /&gt;SQL&gt; exec deadlock_suicida;&lt;/pre&gt;&lt;span style="font-weight: bold;"&gt;Deadlock más comunes&lt;/span&gt;&lt;br /&gt;El ejemplo de deadlock más frecuente es cuando dos sesiones intentan modificar registros de una tabla que la otra sesión está usando, y terminan esperando a que la otra libere primero. Un deadlock no ocurre cuando las operaciones son de lectura, sino que tienen que existir operaciones que produzcan locks implícitos o explícitos.&lt;br /&gt;&lt;br /&gt;Los deadlocks no son un defecto de Oracle, sino que reponden a carencias de conocimiento de los mecanismos de locking por parte del programador, que no ha tenido en cuenta que su código es ejecutado por múltiples sesiones que comparten los recursos concurrentemente.&lt;br /&gt;&lt;br /&gt;Afortunadamente, este problema es automáticamente manejado por Oracle. Cuando dos sesiones se encuentran bloqueadas, una de ellas recibirá el mítico ORA-00060 y será finalizada con rollback. La otra sesión sufrió algunos minutos de bloqueo, pero continuará ejecutando normalmente. Como es una duda común, aclaro que no existe manera alguna de ajustar el tiempo de timeout.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Cómo evitar deadlocks&lt;/span&gt;&lt;br /&gt;No existen recetas infalibles para evitar los deadlocks sino buenas prácticas. Deshacerse de un deadlock puede ser muy trabajoso si no se conoce a fondo la aplicación que lo produjo. Requiere de un análisis exaustivo para determinar como los registros son obtenidos y liberados por una sesión, y luego extrapolar sobre un escenario de dos sesiones concurrentes. El primer paso de análisis, debe ser analizar la sesión generada en el directorio de dump de usuarios ubicada en &lt;span style="font-style: italic;"&gt;$ORACLE_HOME/admin&lt;sid&gt;/udump&lt;/sid&gt;&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Algunas buenas prácticas&lt;/span&gt;&lt;br /&gt;1) Si no se trata del mismo código que generó deadlock, tratar que los registros de todas las tablas sean obtenidos en el mismo orden por las aplicaciones en conflicto.&lt;br /&gt;2) Intentar obtener los recursos más exclusivos que van ser modificados en primer lugar. Puede usarse SELECT FOR UPDATE para este propósito. Es aconsejable tener nuestra aplicación bien tuneada de forma de no bloquear a todas las otras sesiones y enlentecer nuestro sistema.&lt;br /&gt;3) Por ejemplo si vamos a actualizar tablas padre-hijo, podemos tomar la práctica de siempre bloquear primero la tabla padre.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-647432638521616688?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/647432638521616688/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=647432638521616688' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/647432638521616688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/647432638521616688'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/05/un-autodeadlock-y-algunas-buenas.html' title='Un autodeadlock y algunas buenas prácticas'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8928702940581980345</id><published>2009-04-01T07:58:00.000-07:00</published><updated>2009-04-22T09:08:06.887-07:00</updated><title type='text'>Cómo crear una vista parametrizada</title><content type='html'>En Oracle, podemos crear vistas que retornen resultados dependientes de parámetros previamente seteados. La forma de lograr esto es usando un feature llamado Application Contexts.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;¿Qué es un contexto de aplicación?&lt;/span&gt;&lt;br /&gt;El contexto de aplicación es simplemente un espacio en memoria que nos permite almacenar valores para luego utilizarlos en SQL o PL/SQL, como cualquier otra variable definida en el entorno. De forma transparente, mis objetos pueden ser 'manipulados' externamente sin necesidad que mis aplicaciones o procesos batch se enteren.&lt;br /&gt;El contexto puede ser definido tanto localmente (privado para cada sesión) como globalmente, compartiendo sus valores para todas las sesiones de la instancia.&lt;br /&gt;Para poder alterar los valores del contexto, debemos crear un paquete especialmente autorizado para ese fin. Esto es un requerimiento por razones de seguridad.&lt;br /&gt;También necesitamos tener el permiso especial de sistema &lt;span style="font-style: italic;"&gt;CREATE ANY CONTEXT&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ejemplo &lt;/span&gt;&lt;span style="font-size:85%;"&gt; (con sqlplus)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Vamos a ver un sencillo ejemplo de cómo implementar una vista parametrizada con contextos de aplicación, usando el popular usuario SCOTT. El parámetro para la vista en este caso será el número de departamento.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;&lt;span style="font-weight: bold;"&gt;Paso 1:&lt;/span&gt; Crear el contexto de aplicación&lt;/span&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;CREATE CONTEXT app_ctx_scott USING pk_scott_app_context&lt;br /&gt;/&lt;/pre&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;&lt;span style="font-weight: bold;"&gt;Paso 2:&lt;/span&gt; Crear el paquete para manipular el contexto&lt;/span&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;CREATE OR REPLACE PACKAGE pk_scott_app_context AS&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  -- El contexto tendra un unico valor deptno&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  PROCEDURE set_dept (p_deptno IN NUMBER);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;END;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;CREATE OR REPLACE PACKAGE BODY pk_scott_app_context AS&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  PROCEDURE set_dept (p_deptno IN NUMBER) IS&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  BEGIN&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;    DBMS_SESSION.SET_CONTEXT('app_ctx_scott', 'deptno', p_deptno);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  END;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;END;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;/&lt;/span&gt;&lt;/pre&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;&lt;span style="font-weight: bold;"&gt;Paso 3&lt;/span&gt;: Setear el parámetro de contexto deptno para el departamento de ventas&lt;/span&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;BEGIN&lt;br /&gt;pk_scott_app_context.set_dept(30);&lt;br /&gt;END;&lt;br /&gt;/&lt;/pre&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;&lt;span style="font-weight: bold;"&gt;Paso 4&lt;/span&gt;: Crear la vista parametrizada&lt;/span&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;CREATE VIEW empleados AS&lt;br /&gt;SELECT e.empno, e.ename, e.job, d.dname&lt;br /&gt;FROM emp e, dept d&lt;br /&gt;WHERE e.deptno=d.deptno&lt;br /&gt;AND d.deptno = sys_context('app_ctx_scott','deptno');&lt;/pre&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;&lt;span style="font-weight: bold;"&gt;Paso 5&lt;/span&gt;: Obtener los resultados consultando la vista&lt;br /&gt;&lt;/span&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;SELECT * FROM empleados;&lt;br /&gt;&lt;br /&gt;     EMPNO ENAME      JOB       DNAME&lt;br /&gt;---------- ---------- --------- --------------&lt;br /&gt;      7499 ALLEN      SALESMAN  SALES&lt;br /&gt;      7521 WARD       SALESMAN  SALES&lt;br /&gt;      7654 MARTIN     SALESMAN  SALES&lt;br /&gt;      7698 BLAKE      MANAGER   SALES&lt;br /&gt;      7844 TURNER     SALESMAN  SALES&lt;br /&gt;      7900 JAMES      CLERK     SALES&lt;br /&gt;&lt;br /&gt;6 rows selected.&lt;/pre&gt;Actualmente la vista retorna los empleados del departamento de ventas, ya que así está definida la variable en el contexto. Ahora cambiaré el valor del parámetro para que la vista retorne resultados únicamente del departamento contable:&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;BEGIN&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  pk_scott_app_context.set_dept(10);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;END;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SELECT * FROM empleados;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;     EMPNO ENAME      JOB       DNAME&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;---------- ---------- --------- --------------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;      7782 CLARK      MANAGER   ACCOUNTING&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;      7839 KING       PRESIDENT ACCOUNTING&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;      7934 MILLER     CLERK     ACCOUNTING&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;3 rows selected.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;De la misma forma podemos aplicar esta técnica a procedimientos y funciones, pudiendo manipular valores utilizados internamente o inclusive introduciendo fragmentos de código como sql dinámico.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8928702940581980345?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8928702940581980345/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8928702940581980345' title='14 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8928702940581980345'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8928702940581980345'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/04/como-crear-una-vista-parametrizada.html' title='Cómo crear una vista parametrizada'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-1557088925552394812</id><published>2009-03-01T01:58:00.000-08:00</published><updated>2009-07-20T06:13:48.348-07:00</updated><title type='text'>Migrando MySQL a Oracle con SQL Developer</title><content type='html'>Siempre que evaluamos herramientas para realizar esta tarea, debemos considerar variables tales como rapidez, facilidad de uso y bajo costo, entre otras.&lt;br /&gt;&lt;br /&gt;SQL Developer es un producto de costo cero, que permite migrar bases de datos MySQL, Access o SQL Server a Oracle con facilidad.&lt;br /&gt;En este tutorial mostraré el método de migración rápida de esquemas MySQL 5 a Oracle XE, usando la versión 1.5.3 de SQL Developer.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Preparación del software&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;- Descargar e instalar SQL Developer:&lt;br /&gt;La última versión está disponible en &lt;a target="_blank" href="http://otn.oracle.com/"&gt;OTN&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;- Instalar el plugin de conexión a MySQL:&lt;br /&gt;Para esto, desde SQL Developer vamos al menú &lt;span style="font-style: italic;"&gt;Ayuda&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;Verificar Actualizaciones&lt;/span&gt;, marcamos la casilla &lt;span style="font-style: italic;"&gt;Third Party SQL Developer Extensions&lt;/span&gt; y descargamos el driver de conexión a MySQL. SQL Developer deberá ser reiniciado para que el plugin entre en efecto.&lt;br /&gt;&lt;br /&gt;NOTA: Si la extensión no nos aparece entre las opciones de descarga, es posible que ya la tengamos instalada. Para verificar, vamos al menú &lt;span style="font-style: italic;"&gt;Ayuda&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;Sobre&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;Extensiones&lt;/span&gt;, y verificamos que exista la entrada &lt;span style="font-style: italic;"&gt;MySQL JDBC Driver&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Crear un usuario para la migración&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;El usuario de migración será el encargado del proceso de colección, transformación, carga y movimiento de datos, y necesitará un conjunto de permisos especiales para tales efectos.&lt;br /&gt;&lt;br /&gt;Para crearlo, loguearse a sqlplus con el usuario system y ejecutar:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;CREATE USER migration IDENTIFIED BY xxxxxxx DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp;&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;GRANT &lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;create session, create view, &lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;resource, create user, create role, alter any trigger TO migration WITH ADMIN OPTION;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Crear las conexiones&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;- Crear una conexión para el esquema MySQL a ser migrado:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_fRmrOAuwnQ4/SaQfXhTWxTI/AAAAAAAAAQ8/IU52HBCfpbw/s1600-h/Conexion+MySQL.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 154px; height: 98px;" src="http://3.bp.blogspot.com/_fRmrOAuwnQ4/SaQfXhTWxTI/AAAAAAAAAQ8/IU52HBCfpbw/s320/Conexion+MySQL.png" alt="" id="BLOGGER_PHOTO_ID_5306400749886293298" border="0" /&gt;&lt;/a&gt;Colocar un nombre para la conexión y el usuario MySQL (en el ejemplo será root, sin password). Luego seleccionar la lengueta &lt;span style="font-style: italic;"&gt;MySQL&lt;/span&gt; para verificar el hostname (o IP del servidor) y el puerto (generalmente 3306).&lt;br /&gt;Testeando la conexión, el resultado debe ser SUCCESS para poder continuar, de lo contrario revisar el nombre del servidor Apache, usuario de MySQL, y el puerto configurado en el archivo de configuración &lt;span style="font-style: italic;"&gt;my.ini&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;- Crear una conexión Oracle para el repositorio y el esquema destino:&lt;br /&gt;Similarmente como hicimos para MySQL, crearemos una conexión para nuestra base de datos destino, en este caso XE. Configurar el usuario system con su password, y el nombre del servicio de nuestra base de datos.&lt;br /&gt;Como en el caso anterior, testear la conexión.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_fRmrOAuwnQ4/SaQgA9xhUkI/AAAAAAAAARE/sKfR-06MBCI/s1600-h/conexiones.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 110px; height: 66px;" src="http://3.bp.blogspot.com/_fRmrOAuwnQ4/SaQgA9xhUkI/AAAAAAAAARE/sKfR-06MBCI/s320/conexiones.png" alt="" id="BLOGGER_PHOTO_ID_5306401461903643202" border="0" /&gt;&lt;/a&gt;Una vez creadas, ambas conexiones deberán aparecer en el menú vertical izquierdo. A partir de ellas podremos ver la definición de los objetos antes y después de la migración.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Es recomendado crear una conexión separada para el repositorio y otra para el esquema destino, la idea es que sean usuarios diferentes. Para simplificar este ejemplo utilizaremos el mismo esquema para las dos cosas.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Crear un repositorio&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SQL Developer usa un Repositorio para almacenar los packages y datos temporales para validar y convertir nuestros objetos. Si no lo creamos, el propio wizard lo creará automáticamente, pero siempre es mejor crearlo antes y verificar que quede todo correctamente instalado.&lt;br /&gt;&lt;br /&gt;Vamos a la conexión de Oracle recientemente creada y con el botón derecho seleccionamos &lt;span style="font-style: italic;"&gt;Migration Repository&lt;/span&gt;, y &lt;span style="font-style: italic;"&gt;Associate Migration Repository&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;En un minuto, el repositorio estará creado. Podemos verificar que las tablas y vistas del repositorio fueron creadas, con prefijo 'MD_' y 'MGV_' respectivamente. También es bueno verificar que estén correctamente compilados los 4 packages: MD_META, MIGRATION, MIGRATION_REPORT y MIGRATION_TRANSFORMER.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Iniciar el asistente&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Teniendo todos los pasos previos completados, comenzamos con la migración propiamente dicha. Vamos al menú &lt;span style="font-style: italic;"&gt;Migration&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;Quick Migrate&lt;/span&gt;, y seleccionamos la conexión de la base de datos orígen, es decir MySQL.&lt;br /&gt;&lt;br /&gt;Si nuestra conexión fue correctamente creada, nos pide la conexión destino. Colocaremos la conexión XE creada para recibir los objetos.&lt;br /&gt;&lt;br /&gt;El siguiente paso es la verificación del repositorio a utilizar. Debe aparecer el mensaje OK: Using [nombre conexión].&lt;br /&gt;&lt;br /&gt;El cuarto paso es la verificación de pre-requisitos. Luego de presionar el botón &lt;span style="font-style: italic;"&gt;Verify,&lt;/span&gt;&lt;span&gt; una serie de tests son ejecutados, que incluyen conectividad de fuente y destino, y permisos de usuario&lt;/span&gt;. Cualquier observación que Oracle levante aquí tiene que ser resuelta antes de poder avanzar.&lt;br /&gt;&lt;br /&gt;Luego que todos los pasos de la verificación de pre-requisitos resultan en SUCCESS, seguimos.&lt;br /&gt;&lt;br /&gt;Finalmente debemos elegir el tipo de migración entre MIGRATE TABLES ONLY, MIGRATE TABLES AND DATA o MIGRATE EVERYTHING.&lt;br /&gt;&lt;br /&gt;En este caso elegiremos migrate everything: usuarios, tablas, datos, índices, constraints, vistas y código.&lt;br /&gt;&lt;br /&gt;Al presionar &lt;span style="font-style: italic;"&gt;Finalizar&lt;/span&gt;, cruzaremos los dedos.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Resultados de la migración&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SafrszMDxPI/AAAAAAAAARM/GdCqT6JX5sE/s1600-h/migration_summary.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 217px; height: 197px;" src="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SafrszMDxPI/AAAAAAAAARM/GdCqT6JX5sE/s320/migration_summary.png" alt="" id="BLOGGER_PHOTO_ID_5307469840766977266" border="0" /&gt;&lt;/a&gt;La imagen a la izquierda muestra el resultado final de cada stream que realiza el movimiento final en paralelo, junto con la cantidad de filas migradas y errores sucedidos. Es de esperar no encontrar ningún error aquí!&lt;br /&gt;&lt;br /&gt;Durante la migración, podemos pasar todas las etapas sin inconvenientes - el caso ideal- o, podemos encontrar errores generalmente durante la ejecución (Build) de los DDLs generados, lo cual abortará el proceso. La verdad verdadera, es que no siempre lograremos terminar exitosamente la migración en la primera vez, solamente si la base de datos orígen respeta ciertas reglas en su definición.&lt;br /&gt;&lt;br /&gt;Diversos errores de conversión pueden ocurrir desde que MySQL permite amplia libertad de declaraciones fuera del ANSI/ISO SQL standard (además de que MySQL tiene varias extensiones propias de SQL).&lt;br /&gt;&lt;br /&gt;En caso de error, debemos leer el dump de la ejecución y analizar de qué tipo fue la falla. Una vez resuelto, eliminaremos los objetos 'sucios' creados y volveremos a ejecutar el asistente.&lt;br /&gt;&lt;br /&gt;Este proceso de ensayo y error puede ser repetitivo, y es común en la gran mayoría de las migraciones hasta que finalmente logramos una ejecución limpia. Necesitaremos paciencia y perseverancia para llegar al objetivo.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Tareas post-migración&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Luego de la migración, es recomendable verificar que cada tabla fue copiada correctamente. Revisar como fueron transformados los datos, verificar los campos de tipo fecha o numéricos, o aquellos que tienen valores por defecto.&lt;br /&gt;&lt;br /&gt;Es también un buen momento para reforzar la integridad del esquema con todas aquellas mejoras que Oracle ofrece como foreign keys, constraints, e índices. MySQL es poco restrictivo en ese aspecto, y eso puede generar vicios para algunos programadores.&lt;br /&gt;Por ejemplo, el hecho de que las fks y transacciones son soportadas solamente en tablas InnoDB. En este tipo de escenarios, son las aplicaciones las responsables de forzar la integridad de datos, y no siempre logran ese objetivo.&lt;br /&gt;&lt;br /&gt;Cuidado con aplicar restricciones sin analizar previamente su impacto, ya que podremos estar causando un mal peor del que queremos remediar. Un ejemplo común, es el uso de claves referenciales fantasma (o dummy) en la aplicación (sin clave valida en la tabla padre). Tendremos que evaluar si al incorporar las constraints estaremos afectando a la aplicación que tiene implementada esta práctica.&lt;br /&gt;&lt;br /&gt;Una crítica que encuentro oportuna, es el hecho de que para cada campo autonumérico de MySQL, SQL Developer crea una secuencia y un trigger asociado para 'simular' la asignación automática. Entiendo que está a favor de la transparencia, pero por otro lado me parece una decisión interesante en términos de diseño, y me gustaría que fuera un feature opcional para tener el control.&lt;br /&gt;&lt;br /&gt;De la misma forma, otra opción 'indeseada' para mí, es que genera un trigger por cada campo enumerado ('1', '2', '3',..) para asegurar la asignación de valores. Desearía que SQL Developer creara check constraints en lugar de estos triggers que sólo empeoran la performance y limitan la escalabilidad de las aplicaciones.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;En conclusión&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Por la facilidad de operación, visibilidad gráfica de cada una de las etapas, bajo costo -y pese a las críticas que he expuesto-, recomiendo que le den una oportunidad a SQL Developer como opción para migrar de MySQL a Oracle.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-1557088925552394812?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/1557088925552394812/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=1557088925552394812' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1557088925552394812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1557088925552394812'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/03/migrando-mysql-oracle-con-sql-developer.html' title='Migrando MySQL a Oracle con SQL Developer'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_fRmrOAuwnQ4/SaQfXhTWxTI/AAAAAAAAAQ8/IU52HBCfpbw/s72-c/Conexion+MySQL.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-4393696238814708483</id><published>2009-02-20T06:14:00.000-08:00</published><updated>2009-05-28T05:35:05.132-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='noticias'/><category scheme='http://www.blogger.com/atom/ns#' term='vmware'/><title type='text'>Soporte Oracle con VMWare?</title><content type='html'>Oracle establece según nota 249212.1 en Metalink, que no ha certificado ningún producto corriendo virtualizado con VMWare. Si un problema determinado ocurre y el usuario necesita soporte oficial Oracle, deberá probar que el error no se debe a estar corriendo bajo VMWare, por ejemplo reproduciéndolo bajo el sistema operativo nativo. Esto sin dudas complica la existencia de los administradores de sistemas y bases de datos, que deben preparar ambientes no virtualizados para intentar capturar el mismo error. En ese proceso, el tiempo puede ser el peor enemigo.&lt;br /&gt;&lt;br /&gt;Desde el punto de vista del soporte, es entendible y sensato que Oracle no se responsabilice por productos de terceros como VMWare. Desde el punto de vista comercial, es una posición conveniente en tiempos que Oracle invierte en marketing para su propia infraestructura de virtualización (Oracle VM). Los potenciales compradores de una solución virtual deberán evaluar con especial cuidado el soporte que tendrán corriendo una base de datos Oracle virtualizada. Una decisión segura llevará a adoptar el producto que le garanta la máxima cobertura de soporte.&lt;br /&gt;&lt;br /&gt;VMWare, INC comienza a experimentar un crecimiento desacelerado y ya tiene sus primeras bajas en la guerra de la competencia con Microsoft, Citrix, Sun, Oracle y otros. Su presidente ejecutivo fue recientemente demitido, y para empeorar los pronósticos, viene de recibir un duro golpe en el mercado de valores luego que sus acciones cayeran un 11%, pese al crecimiento del 53% en sus ganancias en el último balance de 2008.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-4393696238814708483?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/4393696238814708483/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=4393696238814708483' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4393696238814708483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4393696238814708483'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/02/soporte-oracle-con-vmware.html' title='Soporte Oracle con VMWare?'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-1340651592335704837</id><published>2009-02-05T10:00:00.000-08:00</published><updated>2009-02-20T07:14:03.708-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='errores'/><category scheme='http://www.blogger.com/atom/ns#' term='timesten'/><title type='text'>Error al crear usuario en Oracle TimesTen</title><content type='html'>Cuando se intenta crear un usuario en Oracle TimesTen, se obtiene el siguiente error:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Command&gt; create user lferTTadmin identified by '$ql450';&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;15007: Access control not enabled&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;The command failed.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Durante la instalación de TimesTen 7.0, se preguntó al usuario si se deseaba activar el access control (Do you want to enable Access Control? Yes/No). Si No fue la respuesta, aún hay una forma de poder activarlo 'post instalación'.&lt;br /&gt;&lt;br /&gt;Abrir una consola del sistema operativo, y ejecutar:&lt;br /&gt;ttmodinstall -enableAccessControl&lt;br /&gt;&lt;br /&gt;Luego de eso, el control de acceso estará habilitado.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;C:\Windows\system32&gt;ttmodinstall -enableAccessControl&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;C:\Windows\system32&gt;"C:\TimesTen\tt70_32\perl\bin\perl.exe" "C:\TimesTen\tt70_32&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;\bin\ttmodinstall" -enableAccessControl&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Would you like to enable access control for this instance? [ no ] yes&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;NOTE: The daemon must be stopped before enabling access control.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Would you like to stop the daemon? [ yes ] yes&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;The TimesTen Data Manager 7.0 service is stopping...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;The TimesTen Data Manager 7.0 service was stopped successfully.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Patching successful ...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Restarting the daemon ...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;The TimesTen Data Manager 7.0 service is starting.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;The TimesTen Data Manager 7.0 service was started successfully.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Access control is now enabled for this TimesTen instance.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;C:\Windows\system32&gt;ttisql TT_test&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Copyright (c) 1996-2008, Oracle.  All rights reserved.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Type ? or "help" for help, type "exit" to quit ttIsql.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;All commands must end with a semicolon character.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;connect "DSN=TT_dns_prod04";&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Connection successful: DSN=TT_test;UID=sixbell;DataStore=C:\Users\dashboard\Deskto&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;p\temp\TT_store;DatabaseCharacterSet=AL32UTF8;ConnectionCharacterSet=AL32UTF8;DR&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;IVER=C:\TimesTen\tt70_32\bin\ttdv70.dll;TypeMode=0;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;(Default setting AutoCommit=1)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Command&gt; create user lferTTadmin identified by '&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;$ql450&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;';&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Command&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-1340651592335704837?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/1340651592335704837/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=1340651592335704837' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1340651592335704837'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1340651592335704837'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/02/error-al-crear-usuario-en-oracle.html' title='Error al crear usuario en Oracle TimesTen'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-1628617553040785006</id><published>2009-02-05T06:22:00.000-08:00</published><updated>2009-02-20T07:14:36.244-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='patches'/><category scheme='http://www.blogger.com/atom/ns#' term='noticias'/><title type='text'>Parches de seguridad Enero 2009</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_fRmrOAuwnQ4/SYr2jotSX8I/AAAAAAAAAQ0/kwXyt-y_cbo/s1600-h/alerta.JPG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 77px; height: 112px;" src="http://1.bp.blogspot.com/_fRmrOAuwnQ4/SYr2jotSX8I/AAAAAAAAAQ0/kwXyt-y_cbo/s320/alerta.JPG" alt="" id="BLOGGER_PHOTO_ID_5299319003638030274" border="0" /&gt;&lt;/a&gt;Oracle ha anunciado un nuevo Critical Patch Update que afectan a la mayoría de sus productos, y recomienda fuertemente su aplicación en todos los ambientes, para evitar la explotación de accesos indebidos.&lt;br /&gt;El set incluye 20 patches para Oracle Database (a partir de 9i Release 2),  4 para Application Server (a partir de 10g Release 2), 1 para Collaboration Suite (a partir de 10g), 4 para Applications Suite (a partir de 11i), 1 para Enterprise Manager (a partir de 10g Release 4), 6 para PeopleSoft y JDEdwards Suite (a partir de 8.9), y 5 para BEA Products Suite (a partir de 7.0).&lt;br /&gt;Desde luego que el download está disponible en OTN.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también&lt;/span&gt;&lt;br /&gt;&lt;a target="_blank" href="http://www.oracle.com/technology/deploy/security/critical-patch-updates/cpujan2009.html"&gt;Critical Patch Update Advisory - January 2009&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-1628617553040785006?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/1628617553040785006/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=1628617553040785006' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1628617553040785006'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1628617553040785006'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/02/parches-de-seguridad-enero-2009.html' title='Parches de seguridad Enero 2009'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_fRmrOAuwnQ4/SYr2jotSX8I/AAAAAAAAAQ0/kwXyt-y_cbo/s72-c/alerta.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8104881277791489483</id><published>2009-02-04T11:38:00.000-08:00</published><updated>2009-02-20T07:16:15.235-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqlplus'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>Cómo pasar valores desde sqlplus hacia Unix Shell</title><content type='html'>Hay ocasiones que dentro de un Shell script queremos consultar la base de datos  y con dicho resultado continuar la lógica de nuestro Shell. Como logramos transferir ese resultado?&lt;br /&gt;&lt;br /&gt;Tenemos dos enfoques para capturar valores luego de terminada la ejecución de Sql*plus: por archivos y por variables.&lt;br /&gt;&lt;br /&gt;La primera de ellas es básicamente escribir la salida de la consulta en un archivo y luego levantarlo desde Shell. Este caso es ideal cuando nuestra consulta retorna varios registros, ya que el archivo servirá de entrada a algún comando Unix para procesarlo línea a línea. No veremos un ejemplo ya que es muy sencillo, basta usar el comando SPOOL de Sqlplus para obtener la salida en un archivo de texto. Si bien no es del todo prolijo, funciona.&lt;br /&gt;&lt;br /&gt;Y cuando digo prolijidad, me refiero a que en realidad generar un archivo temporal no es necesario, ya que podemos recoger el resultado de la consulta en una variable Shell directamente. Lo veremos con algunos ejemplos hechos para Solaris 10. Algunos parámetros pueden variar respecto a Linux, se recomienda acudir a la documentación de man.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Resultado único&lt;/span&gt;&lt;br /&gt;Supongamos que quiero leer el contenido de la columna Dummy de la tabla Dual y continuar manipulando el valor en Shell.&lt;br /&gt;&lt;br /&gt;Unix lo hace fácil: ejecutamos la consulta en una sesión sqlplus, y la salida es capturada en la variable 'resultado'. Para simplificar este ejemplo no he considerado si hubo algún error en la consulta, pero puede agregarse lógica que trate mensajes de error dentro de la variable asignada.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;#!/bin/ksh&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;resultado=`sqlplus -s 'scott/tiger' &amp;lt;&amp;lt; EOF&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;set serveroutput on&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;set feedback off&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;set head off&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;select * from dual;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;exit;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;EOF`&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;echo "El resultado es: $resultado"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;Varios resultados en una línea&lt;/span&gt;&lt;br /&gt;Qué hacer si necesitamos retornar más de un valor? Continuamos retornando una única línea, pero separamos los valores dentro del sql con algún caracter que no ocurra dentro de cada resultado, por ejemplo punto y coma. En el ejemplo obtenemos el usuario conectado, la fecha actual y el valor de la columna de Dual, todo al mismo tiempo.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;#!/bin/ksh&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;resultado=`sqlplus -s 'scott/tiger' &amp;lt;&amp;lt; EOF&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;set serveroutput on&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;set feedback off&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;set head off&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;select user||';'||sysdate&lt;/span&gt;&lt;span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;||';'||&lt;/span&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;dummy from dual;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;exit;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;EOF`&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;echo "Los valores son: $resultado"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;El valor retornado es: &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SCOTT;05-FEB-09;X&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Con el comando cut separamos los valores fácilmente:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;echo $resultado | cut -d';' -f1&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SCOTT&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt; echo $resultado | cut -d';' -f2&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;05-FEB-09&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;echo $resultado | cut -d';' -f3&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;X&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Resultados multilínea&lt;/span&gt;&lt;br /&gt;Si queremos obtener varias líneas en lugar de una sola, también podemos hacerlo de esta forma. Como en el caso anterior, si tenemos múltiples valores por línea es aconsejable usar separadores, ya que los espacios no son buenos a la hora de identificar strings que contengan espacios. Además, evitamos los incómodos espacios entre líneas al optimizar el tamaño de cada cadena y no llegar al fin de cada línea.&lt;br /&gt;&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;#!/bin/ksh&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;resultado=`sqlplus -s 'scott/tiger' &amp;lt;&amp;lt; EOF&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;set serveroutput on&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;set feedback off&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;set head off&lt;br /&gt;set linesize 131&lt;br /&gt;set pagesize 9999&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;select empno||';'||ename||';'||job||';'||mgr||';'||deptno from emp;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;exit;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;EOF`&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;echo "El resultado es: $resultado"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;La salida en este caso es:&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;br /&gt;7369;SMITH;CLERK;7902;20&lt;br /&gt;7499;ALLEN;SALESMAN;7698;30&lt;br /&gt;7521;WARD;SALESMAN;7698;30&lt;br /&gt;7566;JONES;MANAGER;7839;20&lt;br /&gt;7654;MARTIN;SALESMAN;7698;30&lt;br /&gt;7698;BLAKE;MANAGER;7839;30&lt;br /&gt;7782;CLARK;MANAGER;7839;10&lt;br /&gt;7788;SCOTT;ANALYST;7566;20&lt;br /&gt;7839;KING;PRESIDENT;;10&lt;br /&gt;7844;TURNER;SALESMAN;7698;30&lt;br /&gt;7876;ADAMS;CLERK;7788;20&lt;br /&gt;7900;JAMES;CLERK;7698;30&lt;br /&gt;7902;FORD;ANALYST;7566;20&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ahora, podemos usar cualquier comando para tratar las líneas. Uno de mis favoritos es awk, ya que nos provee de muchas funciones para tratamiento de cada tipo.&lt;br /&gt;Para que awk consuma cada línea de la variable como si fuese un archivo, debemos incluir comillas dobles, de otro modo lo considerará como una única gran línea.&lt;br /&gt;En el siguiente ejemplo, vemos como awk toma línea a línea e imprime un texto anexo.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-family:courier new;"&gt;echo "$resultado" | awk -F";" 'BEGIN {$cnt=1}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;         {print "Linea "$cnt, $0; $cnt=$cnt+1;}'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;La salida generada por awk es:&lt;br /&gt;&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 1 7369;SMITH;CLERK;7902;20&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 2 7499;ALLEN;SALESMAN;7698;30&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 3 7521;WARD;SALESMAN;7698;30&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 4 7566;JONES;MANAGER;7839;20&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 5 7654;MARTIN;SALESMAN;7698;30&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 6 7698;BLAKE;MANAGER;7839;30&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 7 7782;CLARK;MANAGER;7839;10&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 8 7788;SCOTT;ANALYST;7566;20&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 9 7839;KING;PRESIDENT;;10&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 10 7844;TURNER;SALESMAN;7698;30&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 11 7876;ADAMS;CLERK;7788;20&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 12 7900;JAMES;CLERK;7698;30&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 13 7902;FORD;ANALYST;7566;20&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Linea 14 7934;MILLER;CLERK;7782;10&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8104881277791489483?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8104881277791489483/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8104881277791489483' title='11 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8104881277791489483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8104881277791489483'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/02/como-pasar-variables-desde-sqlplus.html' title='Cómo pasar valores desde sqlplus hacia Unix Shell'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8996506741420408122</id><published>2009-01-30T04:34:00.000-08:00</published><updated>2010-03-04T11:17:44.746-08:00</updated><title type='text'>Oracle SQL Developer Data Modelling</title><content type='html'>Los días de Oracle Designer 10g están acabando. El cásico paquete de diseño, versionamiento e implementación de bases de datos, tiene un jóven sustituyo acorde a los tiempos que corren: Oracle SQL Developer Data Modelling.&lt;br /&gt;&lt;br /&gt;Esta herramienta de nombre largo es totalmente gratuita, profesional y actualizada, y su propósito es quitarle el mercado que otros diseñadores compactos como Erwin o Toad Data Modeller han ganado.&lt;br /&gt;&lt;br /&gt;Es que la comunidad Oracle reclamaba hace tiempo las limitaciones de la interfase, que no había cambiado mucho desde la versión Designer/2000 de 1995, y prácticamente nada desde 9i para 10g. También, la ubicación geográfica de los desarrolladores era un problema cada vez que debían movilizarse lejos del repositorio que Designer require para funcionar.&lt;br /&gt;&lt;br /&gt;Oracle SQL Developer Data Modelling simplemente se copia en una carpeta y no require de instalador. Es un producto separado disponible para Windows, Linux y Mac.&lt;br /&gt;Se conecta con Oracle a partir de 9.2.0.1, no necesita un repositorio (aunque opcionalmente puede usarlo), y tiene una abanico de funcionalidades muy atractivas: modelos lógico, relacional y físico, ingeniería reversa desde otras bases de datos, diseño multidimensional de tablas para DW, data flow diagrams, modelado gráfico de Oracle types, importación desde otros modeladores como Erwin, importación por ODBC, exportación a ddl, csv, xml, Oracle AW.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_fRmrOAuwnQ4/SYMQlfWmbqI/AAAAAAAAAQk/yIGWicvxAYk/s1600-h/logical_model.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 246px; height: 147px;" src="http://1.bp.blogspot.com/_fRmrOAuwnQ4/SYMQlfWmbqI/AAAAAAAAAQk/yIGWicvxAYk/s320/logical_model.png" alt="" id="BLOGGER_PHOTO_ID_5297095822975332002" border="0" /&gt;&lt;/a&gt;Mi impresión sobre la herramienta es muy buena. Luego de un buen tiempo cuesta dejar algunos condicionamientos que el arcaico Designer impone para sobrellevar los problemas de su propia interfase, pero parece que con Oracle Data Modelling no son más necesarios.&lt;br /&gt;Uno típico era el nombrado automático de objetos, Designer tenía una forma particular de autogenerar nombres de FKs e índices, y cada vez que se generaban había que arreglarlos. Oracle Data Modeller permite personalizar todos esos prefijos que usa para ingeniería reversa, o pasaje de modelo lógico para relacional, facilitando el cumplimiento de estándares. Por ejemplo para la generación de índices puedo definir los nombres como: IX_{table}_{seq nr}.&lt;br /&gt;El Navigator (pequeño cuadro que muestra un mapa de todo el modelo) es otra buena innovación que aporta navegabilidad en diagramas grandes.&lt;br /&gt;Es posible especificar una vasta variedad de propiedades sobre cada objeto, para mejorar la generación de código final y evitar tener que repasar los ddls generados.&lt;br /&gt;También cuenta con un analizador de impacto, para anticipar la propagación de cambios en el modelo existente.&lt;br /&gt;La integración por ODBC permite conectarse con prácticamente cualquier base de datos existente.&lt;br /&gt;La mayor ventaja de todas es, como dije anteriormente, que es gratuito.&lt;br /&gt;&lt;br /&gt;Oracle SQL Developer Data Modeller está proyectado para ser lanzado sobre el correr de 2009, pero una serie de versiones beta están siendo publicadas por Oracle para evaluación.&lt;br /&gt;La versión &lt;span style="font-style: italic;"&gt;Early Adopter Release 2&lt;/span&gt; (Nov 2008) está disponible para descargar en OTN.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;ACTUALIZACIÓN &lt;/span&gt;(01-mar-2010): Este articulo comenta sobre la versión Early Adopter R2, la cual no está más disponible. Las versiones comerciales actuales no son gratuitas. Ver comentarios abajo.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a target="_blank" href="http://www.oracle.com/technology/products/database/sql_developer/index.html"&gt;OTN SQL Developer&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8996506741420408122?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8996506741420408122/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8996506741420408122' title='3 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8996506741420408122'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8996506741420408122'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/01/oracle-sql-developer-data-modelling.html' title='Oracle SQL Developer Data Modelling'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_fRmrOAuwnQ4/SYMQlfWmbqI/AAAAAAAAAQk/yIGWicvxAYk/s72-c/logical_model.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7762491943086355369</id><published>2009-01-23T16:04:00.000-08:00</published><updated>2009-02-16T11:53:44.051-08:00</updated><title type='text'>Combinando Microsoft Access y Oracle</title><content type='html'>En este artículo vamos a ver cómo implementar una sencilla aplicación que trabaje sobre una base de datos Oracle para actualizar datos y generar reportes, usando una herramienta amigable y disponible en la mayoría de los ordenadores como lo es Microsoft Access.&lt;br /&gt;&lt;br /&gt;La idea es, en 5 pasos crear una interfase gráfica con poco esfuerzo y en tiempo récord, que facilite la vida de muchos usuarios no IT, como por ejemplo la del Gerente del Area de Recursos Humanos que necesita ingresar y consultar datos en su base de datos corporativa. No hay necesidad de comprar un caro producto comercial para esa sencilla tarea.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Qué software necesitamos para desarrollar y ejecutar nuestra aplicación?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;- Microsoft Access, sino nuestro archivo .mdb no será reconocido por Windows.&lt;br /&gt;- Un cliente Oracle, para que podamos conectarnos con la base de datos de destino.&lt;br /&gt;&lt;br /&gt;(Este software debe estar presente ya sea en la máquina que usemos para desarrollar como en la máquina destino donde va a correr el programa. No está en el alcance de este artículo explicar cómo instalar estos dos productos)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Los 5 pasos para crear la aplicación&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;1) Crear una conexión ODBC&lt;br /&gt;2) Vincular en Access las tablas de Oracle&lt;br /&gt;3) Crear las relaciones existentes&lt;br /&gt;4) Crear formularios&lt;br /&gt;5) Crear un reporte&lt;br /&gt;&lt;br /&gt;Nuestra aplicación va a conectarse y trabajar con el usuario Oracle SCOTT. Uso Windows Vista, Access 2003 y un cliente Oracle 10g.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_fRmrOAuwnQ4/SXnwHF1x_cI/AAAAAAAAAPE/930COjXH-1U/s1600-h/brasilusa.gif"&gt;&lt;img style="margin: 0pt 4px 4px 0pt; float: left; cursor: pointer; width: 32px; height: 11px;" src="http://1.bp.blogspot.com/_fRmrOAuwnQ4/SXnwHF1x_cI/AAAAAAAAAPE/930COjXH-1U/s320/brasilusa.gif" alt="" id="BLOGGER_PHOTO_ID_5294526841568230850" border="0" /&gt;&lt;/a&gt;Disculpas por las pantallas de Access en portugués y Windows en inglés, pero es todo lo que tengo en la vuelta :)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;Paso 1. Crear una conexión ODBC&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;- En Windows, vamos al menú Start, Control Panel, Administrative Tools y seleccionamos Data Sources (ODBC)&lt;br /&gt;&lt;br /&gt;- En la lengueta User DNS, presionar sobre el botón Add. Una lista de drivers disponibles aparecerá en un diálogo. Tenemos dos posiblidades para seleccionar, una es elegir el driver de Microsoft para Oracle (&lt;span style="font-style: italic;"&gt;Microsoft OD&lt;/span&gt;&lt;span style="font-style: italic;"&gt;BC for Oracle&lt;/span&gt;), y la otra es elegir el driver de Oracle que fue instalado con nuestro cliente (en mi caso &lt;span style="font-style: italic;"&gt;Oracle em OraClient10g&lt;/span&gt;). Recomiendo usar el driver nativo de Oracle, ya que está optimizado y se corresponde exactamente con la versión de Oracle que acabamos de instalar.&lt;br /&gt;&lt;br /&gt;Nota: En caso que se nos presente un mensaje de error con el título &lt;span style="font-style: italic;"&gt;SQORAS32&lt;/span&gt; y el mensaje &lt;span style="font-style: italic;"&gt;An unsupported operation was attemped&lt;/span&gt;, se trata de un bug conocido cuya solución está en Metalink nota &lt;span style="font-family:verdana;"&gt;NR.5699495. &lt;/span&gt;Será necesario descargar un patch y reemplazar algunos archivos en el cliente Oracle. Es aplicable a los clientes 10.2.0.3.0 a 10.2.0.3.7 y las instrucciones vienen con el propio patch.&lt;br /&gt;&lt;img src="file:///C:/Users/sixbell/AppData/Local/Temp/moz-screenshot-2.jpg" alt="" /&gt;&lt;br /&gt;- En el diálogo siguiente, ingresar los datos de la conexión:&lt;br /&gt;&lt;br /&gt;Data Source Name: Un nombre cualquiera describiendo nuestra conexión&lt;br /&gt;Description: Opcional, puede ser nada&lt;br /&gt;TNS Service Name: El mismo nombre de servicio en nuestro tnsnames.ora&lt;br /&gt;User ID: Opcional, puede ser nada&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_fRmrOAuwnQ4/SXnUUZLhOXI/AAAAAAAAAOc/ezebNsNEKGk/s1600-h/crear_odbc.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 133px; height: 87px;" src="http://2.bp.blogspot.com/_fRmrOAuwnQ4/SXnUUZLhOXI/AAAAAAAAAOc/ezebNsNEKGk/s320/crear_odbc.png" alt="" id="BLOGGER_PHOTO_ID_5294496283772402034" border="0" /&gt;&lt;/a&gt;- Pulsar Test Connection para ver si nuestra conexión funciona. Aparecerá un diálogo solicitando usuario y password. En mi caso colocaré scott/tiger. El mensaje &lt;span style="font-style: italic;"&gt;Connection successfull&lt;/span&gt; deberá aparecer para poder continuar. De lo contrario, revisar el tnsnames.ora que está siendo usado, para ver si tiene la entrada correcta.&lt;br /&gt;&lt;br /&gt;&lt;nombre&gt;&lt;br /&gt;&lt;br /&gt;Luego de sele&lt;/nombre&gt;&lt;nombre&gt;ccionar OK en el diálogo principal, nuestra conexión ODBC está lista para ser usada.&lt;br /&gt;&lt;br /&gt;&lt;/nombre&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;Paso 2. Vincular en Access las tablas de Oracle&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;- Ingresar a Access y comenzar un nuevo proyecto en blanco.&lt;br /&gt;&lt;br /&gt;- En el menú &lt;span style="font-style: italic;"&gt;Insertar&lt;/span&gt;, elegir &lt;span style="font-style: italic;"&gt;Tabla&lt;/span&gt; y &lt;span style="font-style: italic;"&gt;Vinculación de tabla&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SXoVWoW59sI/AAAAAAAAAP0/UYoWmlBhZJQ/s1600-h/select_odbc.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 134px; height: 97px;" src="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SXoVWoW59sI/AAAAAAAAAP0/UYoWmlBhZJQ/s320/select_odbc.png" alt="" id="BLOGGER_PHOTO_ID_5294567790462236354" border="0" /&gt;&lt;/a&gt;Aparecerá un diálogo como los de seleccionar archivos, pero tenemos primero que elegir en el combo &lt;span style="font-style: italic;"&gt;T&lt;/span&gt;&lt;span style="font-style: italic;"&gt;ipos de archivos&lt;/span&gt;, la opción &lt;span style="font-style: italic;"&gt;Bases de datos (ODBC)&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SXnWxDUswnI/AAAAAAAAAOs/ZZ-NhRQ9DBw/s1600-h/select_ds.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 138px; height: 119px;" src="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SXnWxDUswnI/AAAAAAAAAOs/ZZ-NhRQ9DBw/s320/select_ds.png" alt="" id="BLOGGER_PHOTO_ID_5294498975144788594" border="0" /&gt;&lt;/a&gt; Luego se muestra el diálogo &lt;span style="font-style: italic;"&gt;Select Data Source&lt;/span&gt;. Ir a la lengueta &lt;span style="font-style: italic;"&gt;Machine Data Source&lt;/span&gt; y seleccionar la conexión ODBC creada en el paso anterior.&lt;br /&gt;&lt;br /&gt;Somos solicitados para informar el usuario y password de la base de datos Oracle (scott/tiger).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;nombre&gt;Observar que se nos presentan todas las tablas y vistas a las que el usuario scott tiene acceso. Buscar aquellas que comienzan con el prefijo SCOTT y ma&lt;/nombre&gt;&lt;nombre&gt;rcarlas: BONUS, DEPT, EMP y SALGRADE. Seleccionar OK.&lt;br /&gt;&lt;br /&gt;- Access solicitará indicar para algunas tablas las columnas identificadoras (clave primaria). Eso dependerá si la clave está definida en la tabla original o no. En este caso vamos a ignorar el aviso simplemente seleccionando OK.&lt;br /&gt;&lt;br /&gt;Listo! Las tablas ya están vinculadas en nuestro proyecto. Notar que el nombre de cada tabla asignado automáticamente por Access es [ESQUEMA]_[TABLA].&lt;br /&gt;&lt;/nombre&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;Paso 3. Crear las relaciones&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Cuando se vinculan las tablas del esquema Oracle en Access, no se crean los relacionamientos entre ellas. Es necesario hacerlo manualmente, si queremos mantener el esquema de integridad de datos.&lt;br /&gt;&lt;br /&gt;- En el menú &lt;span style="font-style: italic;"&gt;Herramientas&lt;/span&gt;, ingresar a &lt;span style="font-style: italic;"&gt;Relaciones&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;- En el diálogo &lt;span style="font-style: italic;"&gt;Mostrar Tabla&lt;/span&gt;, en la lengueta &lt;span style="font-style: italic;"&gt;Tablas&lt;/span&gt;, seleccionar (presionando Control) las tablas SCOTT_EMP y SCOTT_DEPT, y pulsar Agregar. Las tablas se mostrarán en la ventana de relacionamientos, y podremos cerrar el diálogo &lt;span style="font-style: italic;"&gt;Mostrar &lt;/span&gt;&lt;span style="font-style: italic;"&gt;Tabla&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;- Si la tabla SCOTT_EMP no muestra todas las columnas, agrandarla desde el vértice inferior derecho para permitir ver la columna DEPT.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_fRmrOAuwnQ4/SXntg1H7vNI/AAAAAAAAAO0/NoIZqAFCb3Q/s1600-h/crear_relacion.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 193px; height: 100px;" src="http://2.bp.blogspot.com/_fRmrOAuwnQ4/SXntg1H7vNI/AAAAAAAAAO0/NoIZqAFCb3Q/s320/crear_relacion.png" alt="" id="BLOGGER_PHOTO_ID_5294523985222679762" border="0" /&gt;&lt;/a&gt;- Hacer click (izquierdo) sobre la columna DEPT de SCOTT_EMP, y sin soltar, arrastrar la columna hacia la tabla SCOTT_EMP, soltándola encima de la columna DEPTNO (clave primaria de SCOTT_DEPT). Un nuevo diálogo se abrirá con la definición de la relación.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_fRmrOAuwnQ4/SXnxnrvjQUI/AAAAAAAAAPM/uRjssRz8NWc/s1600-h/relacion_empdept.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 123px; height: 87px;" src="http://1.bp.blogspot.com/_fRmrOAuwnQ4/SXnxnrvjQUI/AAAAAAAAAPM/uRjssRz8NWc/s320/relacion_empdept.png" alt="" id="BLOGGER_PHOTO_ID_5294528501010088258" border="0" /&gt;&lt;/a&gt;- Podemos elegir las propiedades de esta relación, en nuestro caso vamos a dejar todo por defecto, que corresponde a un INNER JOIN entre ambas tablas. Luego de seleccionar &lt;span style="font-style: italic;"&gt;Crear&lt;/span&gt;, la relación entre las tablas será visible gráficamente en pantalla.&lt;br /&gt;&lt;br /&gt;- Cerrar la ventana de relacionamientos y elegir Sí para guardar los cambios.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;Paso 4. Crear formulario&lt;/span&gt;s&lt;br /&gt;&lt;br /&gt;Tenemos las tablas vinculadas en Access con sus relaciones, y de aquí para adelante depende exclusivamente de nuestros conocimientos en Access. Da lo mismo que sean tablas propias de Access o de Oracle con las que estemos trabajando. Podemos inclusive usar este mismo esquema para importar datos desde una tabla Oracle a una local.&lt;br /&gt;&lt;br /&gt;Crearemos un formulario para cada una de las tablas importadas, para facilitar el mantenimiento de los datos a través de la aplicación. Explicaré cómo hacer los formularios de DEPT y EMP; para el resto es el mismo procedimiento.&lt;br /&gt;&lt;br /&gt;- En la ventana principal de Access, ir a &lt;span style="font-style: italic;"&gt;Formularios&lt;/span&gt;, y elegir la opción &lt;span style="font-style: italic;"&gt;Crear formulario usando el asistente&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;- Seleccionar la tabla SCOTT_DEPT en el combo de tablas, y seleccionar todos los campos disponibles (DEPTNO, DNAME, LOC) con el botón &gt;&gt;.&lt;br /&gt;&lt;br /&gt;- Presionar &lt;span style="font-style: italic;"&gt;Avanzar&lt;/span&gt;, elegir el layout 'Columna', el estilo 'Standard', y escribir el título 'Departamentos'. Seleccionar Concluir.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SX2vgrg2lsI/AAAAAAAAAQE/BMSn4_eIbxE/s1600-h/mantener_deptos.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 154px; height: 65px;" src="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SX2vgrg2lsI/AAAAAAAAAQE/BMSn4_eIbxE/s320/mantener_deptos.png" alt="" id="BLOGGER_PHOTO_ID_5295581712828765890" border="0" /&gt;&lt;/a&gt;Ya está listo nuestro primer formulario donde daremos mantenimiento a los departamentos. Por defecto, todas las operaciones sobre los datos están permitidas. Si se desea, se pueden alterar las propiedades del formulario activando la caja &lt;span style="font-style: italic;"&gt;Propiedades&lt;/span&gt; con Alt-Enter.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ahora vamos al formulario de empleados.&lt;br /&gt;&lt;br /&gt;- En la ventana &lt;span style="font-style: italic;"&gt;Formularios&lt;/span&gt;, elegir la opción  &lt;span style="font-style: italic;"&gt;Crear formula&lt;/span&gt;&lt;span style="font-style: italic;"&gt;rio usando el asistente&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;- Seleccionar la tabla SCOTT_EMP en el combo de tablas, y seleccionar todos los campos disponibles con el botón &gt;&gt;.&lt;br /&gt;&lt;br /&gt;- Elegir las mismas opciones que en la tabla anterior, excepto por el título 'Empleados'.&lt;br /&gt;&lt;br /&gt;Nuestro formulario de empleados está listo, pero vamos a introducir una pequeña mejora: que el campo DEPTNO en lugar de un texto numérico sea un combo list con los nombres de los departamentos, para evitar tener que recordar los números.&lt;br /&gt;&lt;br /&gt;- Seleccionar en el menú &lt;span style="font-style: italic;"&gt;Exibir&lt;/span&gt;, el modo &lt;span style="font-style: italic;"&gt;Diseño&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;- Marcar el campo DEPTNO en el formulario y borrarlo (pulsando la tecla DELETE)&lt;br /&gt;&lt;br /&gt;- En la caja de herramientas (lateral izquierda) seleccionar la herramienta &lt;span style="font-style: italic;"&gt;Caja de Combinación&lt;/span&gt;.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_fRmrOAuwnQ4/SXn2KkhYQ-I/AAAAAAAAAPU/hfD-qOeAP3Y/s1600-h/cajacombinacion.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 23px; height: 22px;" src="http://2.bp.blogspot.com/_fRmrOAuwnQ4/SXn2KkhYQ-I/AAAAAAAAAPU/hfD-qOeAP3Y/s320/cajacombinacion.png" alt="" id="BLOGGER_PHOTO_ID_5294533498413532130" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;- Crear el combo en el lugar que eliminamos el campo numérico DEPTNO. Se abrirá automáticamente el asistente para caja de combinación.&lt;br /&gt;&lt;br /&gt;- Seleccionar la primera opción para que el combo busque los datos a partir de una consulta y Avanzar.&lt;br /&gt;&lt;br /&gt;- Elegir la tabla SCOTT_DEPT y Avanzar.&lt;br /&gt;&lt;br /&gt;- Seleccionar los campos DEPTNO y DNAME y Avanzar&lt;br /&gt;&lt;br /&gt;- Definir el orden de ordenamiento por DNAME (creciente) y Avanzar.&lt;br /&gt;&lt;br /&gt;- Dejar seleccionado el checkbox &lt;span style="font-style: italic;"&gt;Ocultar llave primaria&lt;/span&gt; y Avanzar.&lt;br /&gt;&lt;br /&gt;- Dejar la opción &lt;span style="font-style: italic;"&gt;Recordar valor para uso posterior&lt;/span&gt; y Avanzar.&lt;br /&gt;&lt;br /&gt;- Si se desea modificar el rótulo, puede alterarse para 'DEPTO'.&lt;br /&gt;&lt;br /&gt;- Concluir la operación.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SX2yD7NB4zI/AAAAAAAAAQM/R3Wk9AH5ed8/s1600-h/mantener_emps.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 127px; height: 97px;" src="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SX2yD7NB4zI/AAAAAAAAAQM/R3Wk9AH5ed8/s320/mantener_emps.png" alt="" id="BLOGGER_PHOTO_ID_5295584517359264562" border="0" /&gt;&lt;/a&gt;Terminamos nuestro formulario para dar mantenimiento a los empleados.&lt;br /&gt;Ahora nuestro campo de departamentos se actualiza dinámicamente a partir de la tabla SCOTT_DEPT.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Repetir el proceso de creación de formularios para el resto de las tablas, y estaremos aptos para insertar, borrar y alterar registros completamente a través de nuestra interface gráfica.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;Paso 5. Crear un reporte&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Crearemos un reporte para mostrar un ejemplo más de funcionalidad.&lt;br /&gt;&lt;br /&gt;- En la ventana principal de Access, ir a &lt;span style="font-style: italic;"&gt;Consultas&lt;/span&gt;, y elegir la opción &lt;span style="font-style: italic;"&gt;Crear consulta en modo diseño&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;- Cerrar la ventana Mostrar tabla y en el menú &lt;span style="font-style: italic;"&gt;Exibir&lt;/span&gt; seleccionar &lt;span style="font-style: italic;"&gt;SQL&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;- Escribir la query:&lt;pre style="color: rgb(0, 153, 0);"&gt;SELECT e.empno, e.ename, e.job, e.sal, d.dname, s.grade&lt;br /&gt;FROM scott_emp e, scott_dept d, scott_salgrade s&lt;br /&gt;WHERE e.deptno=d.deptno&lt;br /&gt;AND e.sal BETWEEN s.losal AND s.hisal&lt;/pre&gt;- Cerrar la ventana query y elegir Sí para grabar los cambios, asignando un nombre cualquiera para la consulta, por ejemplo 'Salarios de empleados por departamento'.&lt;br /&gt;&lt;br /&gt;- En la ventana principal de Access, ir a &lt;span style="font-style: italic;"&gt;Reportes&lt;/span&gt;, y elegir la opción &lt;span style="font-style: italic;"&gt;Crear reporte usando el asistente&lt;/span&gt;.&lt;br /&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SXoNo5iI0gI/AAAAAAAAAPc/0lJlqX4yILk/s1600-h/crear_reporte.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 107px; height: 67px;" src="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SXoNo5iI0gI/AAAAAAAAAPc/0lJlqX4yILk/s320/crear_reporte.png" alt="" id="BLOGGER_PHOTO_ID_5294559308217373186" border="0" /&gt;&lt;/a&gt;&lt;/pre&gt;- En el combo &lt;span style="font-style: italic;"&gt;Tablas/Consultas&lt;/span&gt; elegir la consulta creada anteriormente, junto con todas las columnas disponibles en la consulta, y avanzar.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_fRmrOAuwnQ4/SXoOvJvtcyI/AAAAAAAAAPk/rlZ2fqC6_7M/s1600-h/agrupar_reporte.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 108px; height: 76px;" src="http://2.bp.blogspot.com/_fRmrOAuwnQ4/SXoOvJvtcyI/AAAAAAAAAPk/rlZ2fqC6_7M/s320/agrupar_reporte.png" alt="" id="BLOGGER_PHOTO_ID_5294560515160109858" border="0" /&gt;&lt;/a&gt;- En el nivel de agrupamiento, elegir DNAME (el departamento) y avanzar.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;- Elegir el ordenamiento por orden de ENAME creciente.&lt;br /&gt;&lt;br /&gt;- Dejar las opciones de diseño por defecto y avanzar hasta visualizar el reporte.&lt;br /&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_fRmrOAuwnQ4/SXoeywllVqI/AAAAAAAAAP8/0fLubkmkJgo/s1600-h/reporte_visualizar.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 138px; height: 114px;" src="http://1.bp.blogspot.com/_fRmrOAuwnQ4/SXoeywllVqI/AAAAAAAAAP8/0fLubkmkJgo/s320/reporte_visualizar.png" alt="" id="BLOGGER_PHOTO_ID_5294578169312269986" border="0" /&gt;&lt;/a&gt;&lt;/pre&gt;Nuestro reporte está listo luego de 10 minutos con cero esfuerzo de programación, simplemente usando una sencilla consulta SQL.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Combinamos la facilidad de uso y versatilidad de una herramienta como Access con el potencial de una base de datos Oracle. Las posibilidades dependen sólo de nuestra voluntad, pudiendo integrar planillas de Excel, gráficos, multimedia y mejorar la lógica de nuestra aplicacion con VBA (Visual Basic for Applications).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7762491943086355369?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7762491943086355369/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7762491943086355369' title='9 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7762491943086355369'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7762491943086355369'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/07/como-combinar-microsoft-access-y-oracle.html' title='Combinando Microsoft Access y Oracle'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_fRmrOAuwnQ4/SXnwHF1x_cI/AAAAAAAAAPE/930COjXH-1U/s72-c/brasilusa.gif' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7675600847618083079</id><published>2009-01-13T11:40:00.000-08:00</published><updated>2009-01-26T05:54:43.706-08:00</updated><title type='text'>Aumento en esperas log file sync</title><content type='html'>Se puede encontrar mucha "blografía" acerca de este clásico problema de tunning. El parámetro &lt;span style="font-style: italic;"&gt;log file sync&lt;/span&gt; está directamente ligado al proceso interno LGWR que sincroniza el log buffer (área de memoria interna de la SGA) y redo logs en disco (archivos donde son registradas las transacciones), y puede revelar uno de dos problemas (sino ambos): un sub dimensionamiento de la base de datos, o un uso inadecuado de transacciones en las aplicaciones que la usan. En ambos casos, el tiempo de respuesta puede experimentar picos, deteriorando la performance.&lt;br /&gt;&lt;br /&gt;Si bien alguien puede pensar en alterar la configuración de Oracle, es mejor comenzar a buscar posibles sesiones ofensoras. Es sabido que el 98% de las veces las aplicaciones son las principales reponsables de los problemas de performance que los usuarios reclaman, y no la base de datos.&lt;br /&gt;&lt;br /&gt;Recientemente tuve la oportunidad de ver un problema de performance en una base de datos de producción que comenzó a paralizarse sin explicación aparente. He notado una tendencia de los gerentes a pensar los problemas de performance como problemas de hardware, y que adquiriendo nuevos recursos el problema se soluciona. Hay veces que puede ser cierto -no descartemos posibilidades- pero antes de cualquier conjetura y gasto innecesario, un poco de investigación es primordial. Para eso, Oracle nos provee de algunas herramientas de gran utilidad para identificar comportamientos anómalos, y causas que los producen.&lt;br /&gt;&lt;br /&gt;En este caso, fue la herramienta de diagnóstico Statspack quien reveló la verdadera causa del problema.&lt;br /&gt;&lt;br /&gt;El primer indicio en el reporte fue en los &lt;span style="font-style: italic;"&gt;Top 5 wait events&lt;/span&gt;: &lt;span style="font-style: italic;"&gt;log file sync&lt;/span&gt; al tope de la lista.&lt;br /&gt;Eso significaba que Oracle estaba generando mucho más REDO, y tan agresivamente que la instancia dificilmente podía procesarlo. Existen errores típicos de programación PL/SQL que generan más REDO del necesario, uno de ellos es el clásico commit dentro del loop:&lt;pre style="color: rgb(0, 153, 0);"&gt;FOR rec IN (SELECT * from TABLA_GRANDE)&lt;br /&gt;-- ..&lt;br /&gt;-- procesamiento del registro rec&lt;br /&gt;COMMIT;&lt;br /&gt;END LOOP;&lt;br /&gt;-- no es aquí donde debería hacerse el commit?&lt;br /&gt;&lt;/pre&gt;Analisando los resultados del reporte de Statspack, se pudo percibir que no había habido un aumento de COMMITs sino de ROLLBACKs.&lt;br /&gt;&lt;br /&gt;Extracto de Statspack:&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;Load Profile&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;-----------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;/span&gt;&lt;br /&gt;&lt;resumido&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;% Blocks changed per Read:    4.09    Recursive Call %:                92.21&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;Rollback per transaction %:   64.85 &lt;/span&gt;  &lt;span style="color: rgb(0, 153, 0);"&gt;    Rows per Sort:                14.35&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;....&lt;mas&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;user commits          &lt;/span&gt;                        &lt;span style="color: rgb(255, 0, 0);"&gt;96,732&lt;/span&gt;          &lt;span style="color: rgb(0, 153, 0);"&gt;161.2          0.4&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;user rollbacks     &lt;/span&gt;                          &lt;span style="color: rgb(255, 0, 0);"&gt;175,390&lt;/span&gt;         &lt;span style="color: rgb(0, 153, 0);"&gt; 292.3          0.6&lt;/span&gt;&lt;br /&gt;...&lt;br /&gt;&lt;/mas&gt;&lt;/resumido&gt;&lt;/pre&gt;El valor de &lt;span style="font-style: italic;"&gt;Rollback per transaction&lt;/span&gt; indicaba que un 65% de las transacciones terminaban con rollback, y más adelante en el reporte se pudo verificar que efectivamente estaban ocurriendo el doble de rollbacks que de commits. Esto no es normal prácticamente en ninguna base de datos, al menos las que yo he visto. Leyendo reportes anteriores con actividad normal, se comprobó que efectivamente estos números había cambiado (anteriormente el índice era 4%). Sirvió para enfocarse y analisar de lleno las aplicaciones que se estaban ejecutando, y descubrir a través de las sesiones activas que una de ellas generaba mucho rollback. Sucede que el código que esta ejecutaba, había sido levemente modificado (3 líneas) y estaba generando errores inesperados con rollbacks de forma indiscriminada.&lt;br /&gt;&lt;br /&gt;Las bases de datos deben servir a las aplicaciones y usuarios de la mejor forma posible. Para eso, no basta simplemente con ampliar recursos de memoria ni ajustar parámetros. Las aplicaciones que afectan la base de datos deben ser las primeras en ser tuneadas, y ni que hablar testeadas antes de colocarlas en producción. Ya sea en proyectos de desarrollo o mantenimiento, los tests de carga son necesarios. Un test de funcionalidad con una única sesión no revela este tipo de defectos, los cuales sumados en un ambiente real se convierten en un gran problema.&lt;br /&gt;&lt;br /&gt;Haciendo las veces de investigador, el DBA debe recurrir a las herramientas de diagnóstico como Statspack, tkprof y vistas dinámicas. En caso de tratarse de código malicioso, será fundamental trabajar en equipo junto a los desarrolladores para hallar la fuente del problema.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7675600847618083079?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7675600847618083079/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7675600847618083079' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7675600847618083079'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7675600847618083079'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/01/como-disminuir-valores-de-log-file-sync.html' title='Aumento en esperas log file sync'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-4644160618416934301</id><published>2009-01-09T05:41:00.000-08:00</published><updated>2009-01-09T05:45:18.828-08:00</updated><title type='text'>Iniciar OEM 9i en Linux</title><content type='html'>Cómo iniciar el Oracle Enterprise Manager (OEM) 9i en Linux:&lt;br /&gt;&lt;br /&gt;cd $ORACLE_HOME&lt;br /&gt;oemapp console&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-4644160618416934301?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/4644160618416934301/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=4644160618416934301' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4644160618416934301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4644160618416934301'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/01/iniciar-oem-9i-en-linux.html' title='Iniciar OEM 9i en Linux'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-4105527232538391818</id><published>2008-12-31T05:35:00.000-08:00</published><updated>2009-01-27T06:20:19.520-08:00</updated><title type='text'>Indices de función para mejorar un LIKE</title><content type='html'>Los índices de función son útiles para poder indizar condiciones aplicadas sobre columnas y así acceder rapidamente a los registros.&lt;br /&gt;Un ejemplo típico es el uso de UPPER(columna) o el abuso de LIKE:&lt;br /&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;SELECT *&lt;br /&gt;FROM my_big_table&lt;br /&gt;WHERE  buffer LIKE 'FF-%';&lt;/pre&gt;En este caso, una barrida por toda la tabla (FULL SCAN TABLE) es inevitable.&lt;br /&gt;&lt;br /&gt;El uso de LIKE compromete el uso de índices resultando en baja peformance.&lt;br /&gt;Usando la función substr podemos evitar el LIKE en este caso, ya que tenemos siempre le mismo prefijo 'FF-'. Para mejorar la ejecución, podemos crear un índice de función como el que sigue:&lt;pre style="color: rgb(0, 153, 0);"&gt;CREATE INDEX ix_my_big_table_01 ON my_big_table(substr(buffer,1,4));&lt;/pre&gt;La consulta quedaría entonces de esta forma:&lt;pre style="color: rgb(0, 153, 0);"&gt;SELECT *&lt;br /&gt;FROM my_big_table&lt;br /&gt;WHERE substr(buffer,1,4) = 'FF-';&lt;/pre&gt;Para que el índice sea tenido en cuenta por Oracle, necesitamos contar con el privilegio de sistema QUERY REWRITE y dos parámetros de la base seteados: query_rewrite_enabled=TRUE y query_rewrite_integrity=TRUSTED&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/05/indices-cmo-y-cundo.html"&gt;Mis índices no funcionan!&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/11/indices-condicionales.html"&gt;&lt;span style="text-decoration: underline;"&gt;Indices condicionales&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/03/11g-y-sus-ndices-invisibles.html"&gt;11g y sus índices invisibles&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-4105527232538391818?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/4105527232538391818/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=4105527232538391818' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4105527232538391818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4105527232538391818'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2009/01/indices-de-funcin.html' title='Indices de función para mejorar un LIKE'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-4778094076923980602</id><published>2008-11-29T10:44:00.000-08:00</published><updated>2008-12-13T11:44:06.560-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='soundex espanol espanhol plsql'/><title type='text'>Limpieza de archivos en Unix</title><content type='html'>Para eliminar archivos de log inutiles y evitar que nuestro file system se llene con el paso del tiempo, podemos usar el comando find y programarlo para executar diariamente en la crontab de root:&lt;pre style="color: rgb(0, 153, 0);"&gt;find &lt;directorio_log&gt; -name "*.dmp.log" -mtime +2 -exec rm -f {} \;&lt;/directorio_log&gt;&lt;/pre&gt;En el ejemplo estamos eliminando archivos temporales de log de mas de 2 días atras.&lt;br /&gt;&lt;br /&gt;Asimismo, la misma idea puede usarse para compactar archivos con cierta antiguedad:&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;find &lt;/span&gt;&lt;directorio_log&gt;&lt;span style="color: rgb(0, 153, 0);"&gt; -name "*.dmp"  -exec gzip {} \;&lt;span style="font-family:Georgia,serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/directorio_log&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Vea también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/07/esta-es-una-tarea-sencilla-en-linux.html"&gt;Cómo obtener la fecha de ayer en Unix&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/05/comandos-tiles-para-unix.html"&gt;Comandos útiles para Unix Shell&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/03/verificar-archivos-y-carpetas-en-shell.html"&gt;Cómo verificar archivos y directorios en Shell&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-4778094076923980602?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/4778094076923980602/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=4778094076923980602' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4778094076923980602'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4778094076923980602'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/11/limpieza-de-archivos-unix.html' title='Limpieza de archivos en Unix'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-6143001420385794156</id><published>2008-10-28T06:21:00.001-07:00</published><updated>2008-10-28T07:27:39.000-07:00</updated><title type='text'>Cómo saber si Pro*C está instalado en Oracle</title><content type='html'>Pro*C es un producto que por ser pre-compilador no viene incluído en el sistema operativo, sino en la propia distruibución de Oracle. Es un componente opcional que se puede instalar utilizando el Oracle Universal Installer bajo el check [Oracle 9i Develpment Kit]/[Oracle Programmer]/Pro*C.&lt;br /&gt;Generalmente utilizamos Pro*C en conjunción con algún compilador C nativo como &lt;span style="font-weight:bold;"&gt;gcc&lt;/span&gt;, el cual a diferencia del primero, es incluído por el sistema operativo.&lt;br /&gt;&lt;br /&gt;Pro*C, cuando instalado en nuestra base de datos Oracle, es un ejecutable:&lt;br /&gt;&lt;br /&gt;$ORACLE_HOME/bin/proc&lt;br /&gt;&lt;br /&gt;Si el ejecutable no se encuentra en esa ubicación, verificar si efectivamente el componente no fue incluído en la instalación.&lt;br /&gt;&lt;br /&gt;Ejecutar el OUI y confirmar si entre las opciones instaladas el componente no aparece listado.&lt;br /&gt;&lt;br /&gt;Cuando operamos bases de datos remotamente, a veces no tenemos ambiente gráfico, y por razones de conectividad, ni siquiera nos es posible exportar el OUI hacia nuestra consola. Deberemos entonces leer el log de instalación de la base de datos (disponible en $ORACLE_BASE/oraInventory/logs), y buscar si una entrada como la siguiente es listada:&lt;br /&gt;&lt;br /&gt;Pro*C/C++ 9.2.0.4&lt;br /&gt;&lt;br /&gt;En caso de no encontrarlo, entonces deberemos instalarlo usando el OUI.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-6143001420385794156?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/6143001420385794156/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=6143001420385794156' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6143001420385794156'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6143001420385794156'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/10/cmo-saber-si-proc-est-instalado-en.html' title='Cómo saber si Pro*C está instalado en Oracle'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8328057027570452229</id><published>2008-10-27T07:33:00.000-07:00</published><updated>2008-10-28T07:32:32.655-07:00</updated><title type='text'>Application Express y su supervivencia</title><content type='html'>Application Express, también conocida como APEX o HTMLDB, es la herramienta de desarrollo web de Oracle, que viene incluída en Oracle XE lista para usar. A la fecha de hoy, la versión actual (3.1.2) puede descargarse gratuitamente e instalarse en todas las versiones de la base de datos a partir de 9i R2 (incluída la versión XE, también gratuita). Aplication Express es un recurso gratuito tanto para desarrollar como para implantar, inclusive en ambientes de producción, no require de licencias ni posee limitaciones de distribución.&lt;br /&gt;&lt;br /&gt;Desde 2004 a la fecha, la tecnología que ofrece APEX continúa siendo avanzada y cada vez más rica en recursos (AJAX, Web Services, CSS, Javascript, XML, etc) y es realmente rápido lograr resultados profesionales, pudiendo optar por diversas opciones de arquitectura y no teniendo que pagar absolutamente nada por ello.&lt;br /&gt;¿Porqué entonces APEX continúa en las sombras, no logrando transformarse en una verdadera opción para los desarrolladores open source?&lt;br /&gt;&lt;br /&gt;Pese a los esfuerzos de Oracle por disponibilizar gratuitamente esta tecnología, APEX nació con limitantes importantes que impiden su crecimiento en popularidad: no abundan los servicios de hosting gratuito de workspaces de Aplication Express (como sí los hay para otras tecnologías de libre uso). El libre licenciamiento de Oracle Aplication Express no es debidamente acompañado con los permisos de utilización del servidor de aplicaciones y base de datos requerida para estos fines. Para las empresas, no es redituable ofrecer APEX, ya que deben soportar las licencias de una de las bases de datos más costosas del mercado. APEX termina confinado a la privacidad de las intranets corporativas y los computadores portátiles.&lt;br /&gt;&lt;br /&gt;¿No sería una buena movida estratégica para Oracle crear un nuevo tipo de licenciamiento que permita implantar bases de datos con mayor capacidad para ofrecer exclusivamente servicios de hosting APEX? ¿Porqué no difundir un producto, cuyo desarrollo le significa una cara inversión (300,000 líneas de código) destinada a satisfacer las necesidades de unos pocos? Con una comunidad tan reducida, el crecimiento de APEX es bastante más lento de lo que podría ser, dejándolo en amplia desventaja respecto a otros lenguajes de desarrollo web. Sin ir más lejos, fue así que Forms perdió vigencia, y cada vez más de estas aplicaciones emigran hacia otros lenguajes web, aún perdiendo parte de la seguridad y consistencia que Forms ofrece.&lt;br /&gt;&lt;br /&gt;No se ven planes para intentar captar un público más amplio, sino de terminar de cerrar sus filas reclutando a los desarrolladores de Forms: la próxima versión de Aplication Express incluirá un convertidor de Forms a APEX, con lo que se facilitará la migración de estas aplicaciones a lo que será su sustituto inevitable.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Vea también:&lt;/span&gt;&lt;br /&gt;&lt;a target="_blank" href="http://www.oracle.com/technology/products/database/application_express/download.html"&gt;Descargue la última versión de APEX&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/04/como-saber-si-aplication-express-apex.html"&gt;Cómo saber si tiene APEX instalado&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8328057027570452229?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8328057027570452229/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8328057027570452229' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8328057027570452229'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8328057027570452229'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/10/aplication-express-contra-el-tiempo.html' title='Application Express y su supervivencia'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7829647914731978948</id><published>2008-09-19T13:08:00.000-07:00</published><updated>2008-09-19T13:47:56.381-07:00</updated><title type='text'>Generando planillas Excel con SQL*plus</title><content type='html'>Una de los comandos más interesantes de SQL*plus para formatear salida, es MARKUP HTML. Con el, podemos generar código HTML listo para mostrar en cualquier navegador.&lt;br /&gt;&lt;br /&gt;Sintaxis básica:  SET MARKUP HTML ON&lt;br /&gt;&lt;br /&gt;Ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; set markup html on&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; spool tablas.html&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; select table_name, tablespace_name from user_tables;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; spool off&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_fRmrOAuwnQ4/SNQJknC-hzI/AAAAAAAAAEk/lcPePRaf3mw/s1600-h/tables.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://3.bp.blogspot.com/_fRmrOAuwnQ4/SNQJknC-hzI/AAAAAAAAAEk/lcPePRaf3mw/s320/tables.jpg" alt="" id="BLOGGER_PHOTO_ID_5247829990354880306" border="0" /&gt;&lt;/a&gt;Visto en el navegador, nuestra consulta de dos columnas es formateada como una tabla HTML.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Lista para Excel&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_fRmrOAuwnQ4/SNQOAj7hwhI/AAAAAAAAAEs/EatOn0_XaBE/s1600-h/excel.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://2.bp.blogspot.com/_fRmrOAuwnQ4/SNQOAj7hwhI/AAAAAAAAAEs/EatOn0_XaBE/s320/excel.jpg" alt="" id="BLOGGER_PHOTO_ID_5247834868601176594" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Lo interesante de Excel es que permite ver páginas html como si fuesen planillas. Cuando abrimos el spool generado por SQL*plus, tenemos una planilla Excel pronta para usar.&lt;br /&gt;&lt;br /&gt;Como el formato HTML generalmente es abierto por otras aplicaciones, podemos generar el spool como .xlt, y de esta forma abriremos el archivo en Excel con solamente dos clicks.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7829647914731978948?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7829647914731978948/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7829647914731978948' title='6 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7829647914731978948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7829647914731978948'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/09/generando-spools-para-excel-con-sqlplus.html' title='Generando planillas Excel con SQL*plus'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_fRmrOAuwnQ4/SNQJknC-hzI/AAAAAAAAAEk/lcPePRaf3mw/s72-c/tables.jpg' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8899345121087769020</id><published>2008-08-01T23:01:00.000-07:00</published><updated>2008-08-02T00:11:19.971-07:00</updated><title type='text'>¿Cómo generar una fecha aleatoria con PL/SQL?</title><content type='html'>El paquete dbms_random implementa algunas funciones aleatorias de utilidad para generar números y strings aleatorios, pero no para fechas. Para generar fechas al azar, podemos sumar o restar una cantidad X de días a sysdate. El único detalle respecta al período de fechas que queremos obtener, debemos ajustar adecuadamente el número de días que se van a sumar/restar a la fecha actual.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;CREATE TABLE fechas_aleatorias (fecha DATE);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;DECLARE&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  sd    NUMBER;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  dias  NUMBER := 5000;  --max de dias que se suman/restan a sysdate&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  dia   DATE;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;BEGIN&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  -- inicializacion del seed&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  SELECT to_char(systimestamp,'FF') INTO sd FROM dual;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  dbms_random.initialize(sd);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  FOR i in 1..10000&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  LOOP&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;    dia := &lt;/span&gt;&lt;span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;TRUNC(sysdate) + dbms_random.value(-dias,dias);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;    INSERT INTO fechas_aleatorias VALUES (dia);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  END LOOP;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;END;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8899345121087769020?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8899345121087769020/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8899345121087769020' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8899345121087769020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8899345121087769020'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/08/cmo-generar-una-fecha-aleatoria-con.html' title='¿Cómo generar una fecha aleatoria con PL/SQL?'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-319681440910115175</id><published>2008-07-26T10:12:00.000-07:00</published><updated>2008-07-26T06:36:41.462-07:00</updated><title type='text'>ORA-01440: column to be modified must be empty to decrease precision or scale</title><content type='html'>&lt;pre style="color: rgb(0, 153, 0);"&gt;ALTER TABLE t1 MODIFY (nr_pct NUMBER(5,2))&lt;br /&gt;ORA-01440: column to be modified must be empty to decrease precision or scale&lt;/pre&gt;¿Cómo disminuir la longitud de una columna con datos y evitar el error?&lt;br /&gt;&lt;br /&gt;Para este ejemplo, vamos a agregar una dificultad extra: la columna que queremos alterar es NOT NULL.&lt;br /&gt;&lt;br /&gt;Presento dos formas de hacer esto con SQL*Plus:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;FORMA 1: VACIANDO Y ALTERANDO&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Respaldar la columna&lt;pre style="color: rgb(0, 153, 0);"&gt;CREATE TABLE t1_backup AS SELECT id, nr_pct FROM t1;&lt;/pre&gt;Setear la columna como NULL&lt;pre style="color: rgb(0, 153, 0);"&gt;ALTER TABLE t1 MODIFY (nr_pct NULL);&lt;/pre&gt;Vaciar la columna&lt;pre style="color: rgb(0, 153, 0);"&gt;UPDATE t1 SET nr_pct=NULL;&lt;/pre&gt;Ajustar el tamaño deseado de la columna&lt;pre style="color: rgb(0, 153, 0);"&gt;ALTER TABLE t1 MODIFY (nr_pct NUMBER(5,2));&lt;/pre&gt;Cargar los datos a la columna&lt;pre style="color: rgb(0, 153, 0);"&gt;UPDATE t1 SET nr_pct=(SELECT nr_pct FROM t1_backup WHERE t1_backup.id=t1.id);&lt;/pre&gt;Alterar la columna como NOT NULL nuevamente&lt;pre style="color: rgb(0, 153, 0);"&gt;ALTER TABLE t1 MODIFY (nr_pct NOT NULL);&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ventajas: &lt;/span&gt;Se preserva el orden de las columnas dentro de la tabla&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Desventajas:&lt;/span&gt; Poco performante para tablas muy grandes (millones de datos), requiere crear un índice en la tabla t1_backup para mejorar la performance&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;FORMA 2: AGREGANDO COLUMNA EXTRA&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Agregar una columna a la tabla con el tamaño deseado&lt;pre style="color: rgb(0, 153, 0);"&gt;ALTER TABLE t1 ADD (nr_pct_2 NUMBER(5,2) NOT NULL);&lt;/pre&gt;Cargar la nueva columna con los datos&lt;pre style="color: rgb(0, 153, 0);"&gt;UPDATE t1 SET nr_pct_2=nr_pct;&lt;/pre&gt;Eliminar la columna original&lt;pre style="color: rgb(0, 153, 0);"&gt;ALTER TABLE t1 DROP COLUMN nr_pct;&lt;/pre&gt;Renombrar la nueva columna al nombre de la original&lt;pre style="color: rgb(0, 153, 0);"&gt;ALTER TABLE t1 RENAME COLUMN nr_pct_2 TO nr_pct;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ventajas:&lt;/span&gt; Más performante que la forma 1 para tablas grandes. Más fácil de realizar.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Desventajas:&lt;/span&gt; Sólo aplicable a 9i o superiores (8i no posibilita el renombrado de columnas).&lt;br /&gt;La columna alterada quedará al final de la tabla si hacemos un DESC.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-319681440910115175?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/319681440910115175/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=319681440910115175' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/319681440910115175'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/319681440910115175'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/03/ora-01440-column-to-be-modified-must-be.html' title='ORA-01440: column to be modified must be empty to decrease precision or scale'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-4750289153756819766</id><published>2008-07-08T11:33:00.000-07:00</published><updated>2008-12-02T07:00:29.011-08:00</updated><title type='text'>Cómo obtener la fecha de ayer en Unix</title><content type='html'>Esta es una tarea sencilla en Linux. Para obtener el día anterior simplemente usamos la función date:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;date --date='1 day ago'&lt;/span&gt;&lt;/pre&gt;Esta funcionalidad tiene varias formas que flexibilizan la tarea de obtener una fecha relativa del pasado o futuro. Sólo basta dar una mirada a la documentación del man.&lt;br /&gt;&lt;br /&gt;El problema ocurre si usamos Sun Solaris: este sistema operativo no tiene una modalidad similar para date. Es frustrante para un programador casual como yo, que algo tan sencillo no esté implementado en el sistema operativo.&lt;br /&gt;He visto algunos foros en la web donde se pueden encontrar diversos Shell scripts para calcular el día anterior dependiendo de si el mes tiene 30 o 31 dias, si es febrero, si es año bisiesto... y la verdad que la idea de tanta complejidad para algo tan simple no me satisface.&lt;br /&gt;&lt;br /&gt;Una solución directa y fácil de obtener el dia anterior en Solaris es la siguiente: agendar en el programa &lt;span style="font-weight: bold;"&gt;crontab&lt;/span&gt; (o scheduler unix de preferencia) a las 23:59, un simple script que escriba la hora actual en un archivo:&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;#/bin/ksh&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;br /&gt;date &gt; ayer.txt&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Listo!&lt;br /&gt;Ahora nuestros scripts pueden acceder en forma simple a la fecha de ayer.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Vea también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/11/limpieza-de-archivos-unix.html"&gt;Limpieza de archivos en Unix&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/05/comandos-tiles-para-unix.html"&gt;Comandos útiles para Unix Shell&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/03/verificar-archivos-y-carpetas-en-shell.html"&gt;Cómo verificar archivos y directorios en Shell&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-4750289153756819766?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/4750289153756819766/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=4750289153756819766' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4750289153756819766'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4750289153756819766'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/07/esta-es-una-tarea-sencilla-en-linux.html' title='Cómo obtener la fecha de ayer en Unix'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-470928283649461887</id><published>2008-07-02T10:52:00.000-07:00</published><updated>2008-07-07T05:26:13.717-07:00</updated><title type='text'>Obtener la versión de la base desde PL/SQL</title><content type='html'>Si queremos obtener cual es la versión de la base de datos para hacer una u otra cosa, podemos utilizar el paquete dbms_utility y el procedimiento db_version. El mismo nos retorna un string del tipo 9.2.0.4, el cual podemos consultar para programar a conveniencia.&lt;pre&gt;&lt;span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  dbms_utility.db_version(v_version, v_compatible);&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;Una observación: cuidado con utilizar comandos que no existan en otras versiones, para eso está la compilación condicional introducida en Oracle 9i R2. Esta es una simple función que nos retorna la versión de la base actual que nos puede servir por ejemplo para reconstruir nombres de archivos que dependan de la versión utilizada.&lt;br /&gt;&lt;br /&gt;El siguiente bloque PL/SQL es un ejemplo de como puede utilizarse db_version para condicionar la programación.&lt;pre&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;DECLARE&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   v_version        varchar2 (32); &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   v_compatible     varchar2 (32); &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;BEGIN  &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  dbms_utility.db_version(v_version, v_compatible);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  if substr(v_version,1,2) = '10' then &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;     ...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  elsif substr(v_version,1,1) = '9' then &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;     ...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  elsif substr(v_version,1,3) = '8.1' then&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;     ...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  elsif substr(v_version,1,3) = '8.0' then&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;     ...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  elsif substr(v_version,1,1) = '7' then   &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;     ...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  end if; &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  ...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;END;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/03/compilacin-condicional-tambin-en-9i.html"&gt;Compilación condicional en 9i&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-470928283649461887?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/470928283649461887/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=470928283649461887' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/470928283649461887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/470928283649461887'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/07/obtener-la-versin-de-la-base-desde.html' title='Obtener la versión de la base desde PL/SQL'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-3836187663988593889</id><published>2008-06-12T10:10:00.000-07:00</published><updated>2008-07-26T06:48:51.735-07:00</updated><title type='text'>SP2-1503 Unable to initialize Oracle call interface</title><content type='html'>&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SP2-1503: No es posible inicializar la interfase de llamada Oracle&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SP2-0152: ORACLE tal vez no esté funcionando adecuadamente&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Acabamos de instalar nuestro cliente Oracle en Windows Vista, configuramos el tnsnames.ora y al querer conectarnos a la base de datos obtenemos el error.&lt;br /&gt;&lt;br /&gt;Una posible explicación, es que acabamos de instalar un cliente Oracle para Windows XP, y este no es compatible con Vista. Es necesario descargar e instalar el software de Oracle específico para ese sistema operativo.&lt;br /&gt;&lt;br /&gt;Si se ejecuta PL/SQL Developer en estas condiciones, luego de pedir los datos para el login retorna un error nada explicativo: un pequeño popup con mensaje vacío y un botón OK.&lt;br /&gt;&lt;br /&gt;Un consejo: al obtener errores de conexión con herramientas externas, testear la conexión a través de SQL*Plus, para poder ver errores directos del sistema operativo o de la base de datos. Si SQL*Plus se conecta exitosamente, entonces el problema estará en la herramienta.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-3836187663988593889?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/3836187663988593889/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=3836187663988593889' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3836187663988593889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3836187663988593889'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/06/sp2-1503-no-es-posible-inicializar-la.html' title='SP2-1503 Unable to initialize Oracle call interface'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-307954975095993979</id><published>2008-06-04T06:23:00.000-07:00</published><updated>2008-06-12T10:10:39.602-07:00</updated><title type='text'>Cómo saber la última fecha en que un tablespace fue alterado</title><content type='html'>El único lugar donde encontramos esta información es el &lt;span style="FONT-WEIGHT: bold"&gt;alert log file&lt;/span&gt;. Este archivo registra todas las alertas críticas de la base de datos, incluídas las alteraciónes de tablespaces. Es solo mirar este archivo, hacer una búsqueda por el nombre del tablespace y obtendremos todos los datos de la modificación.&lt;br /&gt;&lt;br /&gt;El alert log se localiza en la ruta: &lt;span style="COLOR: rgb(0,153,0)"&gt;${ORACLE_HOME}/admin/${ORACLE_SID}/bdump&lt;/span&gt;&lt;br /&gt;y su nombre generalmente es: &lt;span style="COLOR: rgb(0,153,0)"&gt;alert_${ORACLE_SID}.log&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;La restricción, es que únicamente el administrador de la base de datos tendrá acceso a este archivo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-307954975095993979?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/307954975095993979/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=307954975095993979' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/307954975095993979'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/307954975095993979'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/06/cmo-saber-la-ltima-fecha-en-que-un.html' title='Cómo saber la última fecha en que un tablespace fue alterado'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-6916733912965153910</id><published>2008-05-30T08:11:00.001-07:00</published><updated>2008-12-02T06:58:57.541-08:00</updated><title type='text'>Comandos útiles para Unix Shell</title><content type='html'>Agrego algunos comandos útiles en Unix que siempre vienen bien tenerlos a mano. Hacen parte de las tareas diarias, en la creación de nuevos scripts de respaldo o en el tratamiento de logs que generan nuestros processos.&lt;br /&gt;&lt;br /&gt;En este caso uso Solaris y ksh, pero dependiendo de la versión de Unix/Linux que estén utilizando, pueden variar algunos parámetros. De todas formas, la idea sirve para adaptarlos en cada ambiente.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Eliminar archivos gz con mas de 3 días de antiguedad&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;find . -mtime +3 -name "*.gz" -exec rm {} \;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ejecutar un shell en background e independiente de la conexión&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;nohup ./shell.sh &amp;amp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;(este script retorna un número de job, puede ser útil anotarlo)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Para traer el proceso nuevamente al foreground&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;fg %(num_job)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;(el número de job fue el que se retornó al colocar el proceso en background)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Colocar los nombres de archivo de un listado en un array&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ARQS=$(cd ${DIR_RECARGA} ; ls *.dat)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Correr un script de sqlplus en Shell&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;#!/bin/ksh&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;sqlplus -s /nolog &lt;&lt; !EOF&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;@$ORACLE_HOME/scripts/mi_script.sql&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;exit;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;!EOF&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Compactar todos los archivos de un directorio&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;#!/usr/sbin/ksh&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;for arch in `ls -t &lt;diretorio&gt;/*.arch  tail +5`&lt;/diretorio&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;do&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;gzip $arch&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;done&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Ver y buscar en el contenido de un archivo comprimido con gzip&lt;/strong&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-family:courier new;"&gt;gzcat&lt;/span&gt; - CAT para archivos gz&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-family:courier new;"&gt;gzgrep&lt;/span&gt; - GREP para archivos gz&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/03/verificar-archivos-y-carpetas-en-shell.html"&gt;Cómo verificar archivos y directorios en Shell&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/11/limpieza-de-archivos-unix.html"&gt;Limpieza de archivos en Unix&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/07/esta-es-una-tarea-sencilla-en-linux.html"&gt;Cómo obtener la fecha de ayer en Unix&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-6916733912965153910?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/6916733912965153910/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=6916733912965153910' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6916733912965153910'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6916733912965153910'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/05/comandos-tiles-para-unix.html' title='Comandos útiles para Unix Shell'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-6665199847251992996</id><published>2008-04-20T04:51:00.000-07:00</published><updated>2008-04-21T18:48:03.234-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plsql'/><category scheme='http://www.blogger.com/atom/ns#' term='APEX'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Maps'/><title type='text'>Incorporando Google Maps en Application Express</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_fRmrOAuwnQ4/SAvD8wuVfEI/AAAAAAAAAEY/L0K6F5KcOD4/s1600-h/copa2.JPG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://2.bp.blogspot.com/_fRmrOAuwnQ4/SAvD8wuVfEI/AAAAAAAAAEY/L0K6F5KcOD4/s320/copa2.JPG" alt="" id="BLOGGER_PHOTO_ID_5191458444113443906" border="0" /&gt;&lt;/a&gt;Diversos artículos pueden encontrarse en Internet acerca de cómo implementar Google Maps en Oracle Application Express (APEX). Desafortunadamente, la mayoría son algo básicos y se limitan a cargar un mapa dentro de una región, no teniendo interacción con la base de datos.&lt;br /&gt;&lt;br /&gt;En esta oportunidad vamos a ver cómo lograr desplegar puntos dinámicamente en un mapa a partir de datos almacenados en una tabla. Únicamente se requieren conocimientos básicos de Application Express, PL/SQL y Javascript.&lt;br /&gt;&lt;br /&gt;Este artículo no pretende mostrar todas las funcionalidades de la API de Google Maps, sino resolver el problema puntual de programación en Oracle, luego dependerá de las habilidades Javascript del programador y de conocer a fondo las funcionalidades que brinda esta maravillosa API.&lt;br /&gt;&lt;br /&gt;Aclarado esto, comencemos, paso a paso:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 102);"&gt;Paso 1: Tener APEX instalado.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Lo primero es tener APEX instalado, ya que necesitaremos la URL o IP para el paso siguiente.&lt;br /&gt;Si tienen duda si APEX está instalado en su base de datos, ver mi &lt;a href="http://oraclenotepad.blogspot.com/2008/04/como-saber-si-aplication-express-apex.html" target="_blank"&gt;artículo anterior&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 102);"&gt;Paso 2: Solicitar una clave en Google Maps.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ir a la siguiente direccion &lt;a href="http://code.google.com/apis/maps/signup.html" target="_blank"&gt;http://code.google.com/apis/maps/signup.html&lt;/a&gt; para registrarnos y solicitar un código que necesitamos para utilizar el servicio. En el registro, se debe ingresar la URL donde será utilizado Google Maps.&lt;br /&gt;Por ejemplo, si utilizamos el espacio gratuito &lt;a href="http://apex.oracle.com/" target="_blank"&gt;Oracle Apex&lt;/a&gt;, podemos ingresar la URL http://apex.oracle.com/pls/otn/. Si se está desarrollando localmente (en la propia máquina) puede registrarse la IP 127.0.0.1, pero cuando finalice el desarrollo habrá que solicitar una nueva clave con la URL de producción, ya que Google requiere que el sitio registrado sea de acceso público.&lt;br /&gt;El código generado será referenciado desde de nuestro script.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 102);"&gt;Paso 3: Manos a la obra con APEX!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En nuestra nueva aplicación APEX, como primer paso debemos crear una página.&lt;br /&gt;&lt;br /&gt;Editar los atributos de la página y en el campo &lt;span style="font-style: italic;" class="htmldbLabelOptional"&gt;Enfoque de Cursor&lt;/span&gt; elegir el valor &lt;span style="font-style: italic;"&gt;No enfocar Cursor.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En la sección Cabecera HTML, incluir el siguiente script reemplazando nuestra clave:&lt;br /&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;&amp;lt;script src="http://maps.google.com/maps?file=api&amp;amp;v=2&amp;amp;key=&lt;span style="color: rgb(51, 51, 255);"&gt;la_clave_de_google_maps&lt;/span&gt;&amp;amp;hl=es"&lt;br /&gt;type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;&lt;br /&gt;//&amp;lt;![CDATA[&lt;br /&gt;&lt;br /&gt;function load() {&lt;br /&gt;if (GBrowserIsCompatible()) {&lt;br /&gt;miMapa();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//]]&amp;gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;En este  javascript acabamos de definir la función load, la cual invoca a otra función miMapa que crearemos más adelante.&lt;br /&gt;&lt;br /&gt;En el campo &lt;span style="font-style: italic;"&gt;Atributo de Cuerpo HTML de Página&lt;/span&gt;, pegar lo siguiente:&lt;pre style="color: rgb(0, 153, 0);"&gt;onload="load();" onunload="GUnload();"&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;Se puede observar cómo en el evento onload de la página, se invoca a la función load incluída en el cabezal HTML. &lt;/li&gt;&lt;/ul&gt;Hecho esto, guardar los cambios efectuados en la página&lt;br /&gt;&lt;br /&gt;Ahora queda la parte interesante, ya que deberemos crear la función miMapa la cual ejecutará la carga del mapa y los puntos de referencia extraídos de la base de datos. Esta función será generada por un bloque anónimo PL/SQL el cual será ejecutado cada vez que se carga la página. &lt;br /&gt;Para este ejemplo, se creó una sencilla tabla &lt;span style="font-style: italic;"&gt;geopuntos&lt;/span&gt; la cual contiene información sobre los puntos de referencia que queremos desplegar en nuestro mapa:&lt;pre style="color: rgb(0, 153, 0);"&gt;id_punto     NUMBER(10)    NOT NULL&lt;br /&gt;latitud      NUMBER(18,15) NOT NULL&lt;br /&gt;longitud     NUMBER(18,15) NOT NULL&lt;br /&gt;descripcion  VARCHAR2(100)&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;La precisión de &lt;span style="font-style: italic;"&gt;latitud&lt;/span&gt; y &lt;span style="font-style: italic;"&gt;longitud&lt;/span&gt; es adecuada para almacenar coordenadas geográficas de Google Maps.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Cada punto puede enriquecerse con valiosa información como pueden ser imagenes, archivos, links, íconos, etc.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Se recomienda consultar la extensa documentación y tutoriales que brinda Google Maps en su sitio oficial.&lt;/li&gt;&lt;/ul&gt;En el área &lt;span style="font-style: italic;"&gt;Presentación de Página&lt;/span&gt;, en la sección &lt;span style="font-style: italic;"&gt;Procesos&lt;/span&gt;, vamos a implementar nuestro bloque PL/SQL.&lt;br&gt;Crear un nuevo proceso de tipo &lt;span style="font-style: italic;"&gt;PL/SQL&lt;/span&gt;. En &lt;span style="font-style: italic;"&gt;Atributos de Proceso&lt;/span&gt;, ingresar un nombre cualquiera y elegir el valor &lt;span style="font-style: italic;"&gt;En Carga, Antes de Cabecera&lt;/span&gt;. Clickear siguiente, y en &lt;span style="font-style: italic;"&gt;Proceso&lt;/span&gt;, incluir el siguiente bloque:&lt;pre style="color: rgb(0, 153, 0);"&gt;DECLARE&lt;br /&gt;  CURSOR cr_puntos IS select * from puntosmapa;&lt;br /&gt;BEGIN&lt;br /&gt;  htp.p('&amp;lt;script type="text/javascript"&amp;gt;');&lt;br /&gt;  htp.p('//&amp;lt;![CDATA[');  &lt;br /&gt;  &lt;br /&gt;  htp.p('function crearMarca(lat, long, descr) {');&lt;br /&gt;  htp.p('  var point = new GLatLng(lat,long);');&lt;br /&gt;  htp.p('  var opts = {title: descr};');&lt;br /&gt;  htp.p('  var mark = new GMarker(point,opts);');&lt;br /&gt;  htp.p('  GEvent.addListener(mark, "click", function() {&lt;br /&gt;    mark.openInfoWindowHtml(''&amp;lt;div&amp;gt;&amp;lt;B&amp;gt;'' + descr + ''&amp;lt;/B&amp;gt;&amp;lt;BR&amp;gt;Lat: '' + lat +&lt;br /&gt; ''&amp;lt;BR&amp;gt;Long: '' + long + ''&amp;lt;/div&amp;gt;'');');&lt;br /&gt;  htp.p('  });');&lt;br /&gt;  htp.p('return mark;');&lt;br /&gt;  htp.p('}');  &lt;br /&gt;  &lt;br /&gt;  htp.p('function miMapa() {'); &lt;br /&gt;  htp.p('var map = new GMap2(document.getElementById("map"));');&lt;br /&gt;  htp.p('map.addControl(new GLargeMapControl());');&lt;br /&gt;  htp.p('map.addControl(new GMapTypeControl());');&lt;br /&gt;  htp.p('map.addControl(new GOverviewMapControl());');&lt;br /&gt;  htp.p('map.setCenter(new GLatLng(-22.915739,-43.22912), 11);');&lt;br /&gt;  &lt;br /&gt;  -- Recorro cursor e inserto los puntos&lt;br /&gt;  FOR punto IN cr_puntos&lt;br /&gt;  LOOP&lt;br /&gt;    htp.p('var marker = crearMarca('||to_char(punto.latitud,'999.9999999999999999')||','&lt;br /&gt;   ||to_char(punto.longitud,'999.9999999999999999')||',"'||punto.descripcion||'");');  &lt;br /&gt;    htp.p('map.addOverlay(marker);');&lt;br /&gt;  END LOOP;&lt;br /&gt;  htp.p('}');&lt;br /&gt;&lt;br /&gt;  htp.p('//]]&amp;gt;');&lt;br /&gt;  htp.p('&amp;lt;/script&amp;gt;');&lt;br /&gt;END;&lt;/pre&gt;Este es el punto neurálgico de la aplicación: en este bloque PL/SQL resolvemos la consulta a la base de datos, la creación del mapa y la publicación de los puntos de referencia extraídos. Notar el uso de la función PL/SQL &lt;span style="font-weight: bold;"&gt;htp.p()&lt;/span&gt;. Así como DBMS_OUTPUT.PUT_LINE envia caracteres a la salida en pantalla, la función htp.p envía cadenas de caracteres al browser, y esto precisamente lo que necesitamos ya que queremos construir el Javascript a ser ejecutado en la carga de la página.&lt;br /&gt;&lt;br /&gt;Puede apreciarse la sencillez de esta implementación, en pocas líneas crea un mapa, lo centra y carga los controles básicos de zoom. La particularidad en este caso es el FOR LOOP, el cual recorre un cursor definido sobre la tabla, y en cada iteración define una marca "publicando" Javascript en tiempo de ejecución. Cada referencia tiene un toolTip cuando se pasa el mouse por encima y un globo informativo que se abre al clickear sobre la marca.&lt;br /&gt;&lt;br /&gt;Y eso es todo, tras guardar el proceso (dejar el resto de los valores por defecto), hemos concluído nuestra aplicación y ya está lista para ser explorada.&lt;br /&gt;&lt;br /&gt;Las posibilidades son muchísimas, y cuanto más se profundice sobre la documentación, mejores funcionalidades podremos lograr en APEX.&lt;br&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 102);"&gt;Un ejemplo implementado&lt;/span&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SAu79QuVfBI/AAAAAAAAAEA/m61b7IqCx24/s1600-h/gmapsdemo.JPG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://4.bp.blogspot.com/_fRmrOAuwnQ4/SAu79QuVfBI/AAAAAAAAAEA/m61b7IqCx24/s320/gmapsdemo.JPG" alt="" id="BLOGGER_PHOTO_ID_5191449656610356242" border="0" /&gt;&lt;/a&gt;&lt;/pre&gt;Hice una página de demostración para que puedan ver que realmente funciona. En mi tabla de datos tengo almacenados puntos turísticos de Río de Janeiro, ciudad donde vivo actualmente :)&lt;br /&gt;&lt;br /&gt;Para saber las coordenadas geográficas de un punto en la tierra, podemos utilizar por ejemplo la página &lt;a href="http://itouchmap.com/latlong.html" target="_blank"&gt;Geocoder&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Hay mucha documentación y ejemplos disponibles sobre Google Maps y realmente es fácil comenzar a obtener resultados sorprendentes.&lt;br /&gt;&lt;br /&gt;Finalmente le agradezco a Emilio Le Mener por su ayuda en Javascript.&lt;br /&gt;&lt;br /&gt;Espero que este tutorial haya sido útil y puedan comenzar a implementar sin problemas con APEX y Google Maps.&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;img id="BLOGGER_PHOTO_ID_5175489526410503874" style="cursor: pointer;" alt="" src="http://2.bp.blogspot.com/_fRmrOAuwnQ4/R9MITahz5sI/AAAAAAAAADA/L9NY7wTm94I/s320/execute.jpg" border="0" /&gt; &lt;a href="http://apex.oracle.com/pls/otn/f?p=45998:1:4251456126624924:::::" target="_blank"&gt;Probar la Demo&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;/div&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://code.google.com/apis/maps/index.html" target="_blank"&gt;Google Maps API&lt;/a&gt;&lt;br /&gt;&lt;a href="http://doug.ricket.com/gdd2007/" target="_blank"&gt;Presentación Google Developer Day 2007&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-6665199847251992996?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/6665199847251992996/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=6665199847251992996' title='6 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6665199847251992996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6665199847251992996'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/04/google-maps-en-application-express.html' title='Incorporando Google Maps en Application Express'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_fRmrOAuwnQ4/SAvD8wuVfEI/AAAAAAAAAEY/L0K6F5KcOD4/s72-c/copa2.JPG' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-1198931205682747280</id><published>2008-04-18T00:05:00.000-07:00</published><updated>2008-04-17T20:04:30.123-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='APEX'/><title type='text'>Cómo saber si Application Express (APEX) está instalado en Oracle</title><content type='html'>Ejecutar la siguiente consulta en SQL*Plus con un usuario con rol DBA:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SELECT username &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;FROM dba_users &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;WHERE username IN ('FLOWS_010500','FLOWS_010600','FLOWS_020000','FLOWS_020200','FLOWS_030000', 'FLOWS_030100');&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Si no se obtiene ninguna fila como resultado, entonces APEX no está instalado en la base de datos.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-1198931205682747280?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/1198931205682747280/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=1198931205682747280' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1198931205682747280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1198931205682747280'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/04/como-saber-si-aplication-express-apex.html' title='Cómo saber si Application Express (APEX) está instalado en Oracle'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7556479463904541640</id><published>2008-04-17T08:34:00.000-07:00</published><updated>2008-04-17T20:05:19.623-07:00</updated><title type='text'>Oracle libera parches de seguridad para todos sus productos</title><content type='html'>&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://1.bp.blogspot.com/_fRmrOAuwnQ4/SAd1mt_1q1I/AAAAAAAAAD4/puXJg32_8n0/s320/alerta.JPG" alt="" id="BLOGGER_PHOTO_ID_5190246403610618706" border="0" /&gt;&lt;br /&gt;Este 15 de abril fueron publicados parches para todos los productos Oracle, incluída la base de datos, la E-Business Suite, PeopleSoft, Siebel y otros productos.&lt;br /&gt;&lt;br /&gt;En cuanto a la base de datos, los parches abarcan todas las versiones desde la 9.0.1.5, y algunos de los bugs que se corrigen son vulnerabilidades graves que permiten explotar la base de datos aún sin autenticación.  Este es un asunto de alerta máxima y no debe demorarse la decisión de actualizar el software. La instalación es sencilla y no toma mucho tiempo.&lt;br /&gt;&lt;br /&gt;El Application Server 9&lt;i&gt;i&lt;/i&gt; Release 1, version 1.0.2.2 también presenta vulnerabilidades en el JInitiator 1.3.1.14, Enterprise Manager 1.0.2.2 y el Enterprise Portal 9.0.4.3. Estos agujeros de seguridad pueden ser explotados sin necesidad de usuario y password.&lt;br /&gt;&lt;br /&gt;Oracle recomienda fuertemente descargar estos patches (gratuitamente desde otn.oracle.com) y aplicarlos en todos los casos para evitar riesgos ya detectados.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://www.oracle.com/technology/deploy/security/critical-patch-updates/cpuapr2008.html" target="_blank"&gt;Oracle Critical Patch Update Advisory - April 2008&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7556479463904541640?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7556479463904541640/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7556479463904541640' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7556479463904541640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7556479463904541640'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/04/oracle-libera-parches-de-seguridad-para.html' title='Oracle libera parches de seguridad para todos sus productos'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_fRmrOAuwnQ4/SAd1mt_1q1I/AAAAAAAAAD4/puXJg32_8n0/s72-c/alerta.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8643097908149921395</id><published>2008-04-14T05:27:00.000-07:00</published><updated>2008-04-16T09:18:29.751-07:00</updated><title type='text'>Sobre comentarios y cómo actualizar secuencias</title><content type='html'>Cuando se realizan cargas de datos en un esquema existente y no se utilizan las secuencias para asignar los valores de llave primaria, se produce un desfasaje entre identificadores y secuencias. Este desfasaje provoca errores de clave primaria ya existente, ya que los valores retornados por &lt;span style="font-style: italic;"&gt;secuencia.NEXTVAL&lt;/span&gt;, pueden ya existir entre los datos recientemente cargados.&lt;br /&gt;&lt;br /&gt;Para sincronizar las secuencias con los identificadores de llave primaria, puede crearse un script en PL/SQL el cual recree cada secuencia con su valor inicial según el máximo identificador de la tabla correspondiente. Para poder realizarlo, necesitamos primero tener una correspondencia entre tabla y secuencia, y en Oracle esta dependencia no existe.&lt;br /&gt;&lt;br /&gt;Es una buena oportunidad para comenzar a adoptar la buena práctica de usar comentarios sobre los objetos. Muchas veces, en el proceso de creación de una base de datos, este paso es omitido y considerado innecesario. Supuestamente, los objetos "no necesitan describirse" fundamentandose que el diseño es lo suficientemente explicativo y que los creadores van a permanecer durante todo el proceso de desarrollo, por lo tanto no vale la pena perder el tiempo describiendo los objetos. ¿No suenan estas excusas a holgazanería?&lt;br /&gt;&lt;br /&gt;No se deben ignorar estas máximas: 1) el diseñador puede abandonar el proyecto y 2) las personas externas que miren nuestro modelo pueden no entenderlo.&lt;br /&gt;&lt;br /&gt;En este sentido, el responsable de base de datos debe velar porque el modelo quede totalmente documentado y que no dependa de las personas que lo crearon, en definitiva que sea lo más auto-explicativo posible.&lt;br /&gt;&lt;br /&gt;Alguien puede considerar suficiente que la base de datos quede documentada en un diagrama o en un documento de word, pero ningún esfuerzo es suficiente si podemos hacer un poquito más. Los comentarios sobre objetos son almacenados en el diccionario de datos, y van a ser exportados junto a los objetos cuando estos sean migrados. Sin necesidad de consultar diagramas ni documentos, un programador en apuros puede hacer un DESCRIBE sobre una tabla en SQLPLUS, y saber al instante donde encontrar el dato que busca. Los comentarios ayudan a disipar las dudas más rapido, sobre todo a los que no están empapados en el modelo.&lt;br /&gt;&lt;br /&gt;Ahora si, volviendo al tema inicial, ¿cómo relacionamos una tabla con su secuencia? Precisamente, colocando en el comentario de la columna, el nombre de la secuencia que se utiliza:&lt;pre style="color: rgb(0, 153, 0);"&gt;SQL&gt; COMMENT ON cuentas.id_cuenta IS 'CUENTA_SEQ'&lt;/pre&gt;Acabamos de documentar que para la columna &lt;span style="font-style: italic;"&gt;id_cuenta&lt;/span&gt; de la tabla &lt;span style="font-style: italic;"&gt;cuentas&lt;/span&gt;, se debe utilizar la secuencia &lt;span style="font-style: italic;"&gt;cuenta_seq&lt;/span&gt;. Observar que el efecto de esta sentencia es únicamente documental, todavía puede utilizarse la secuencia de la forma que se desee. La ventaja es que ahora podemos consultar la tabla de diccionario dba_col_comments en nuestros scripts.&lt;br /&gt;&lt;br /&gt;A modo de nota, mencionamos que tambien se pueden comentar tablas y vistas, con esta sintaxis:&lt;pre style="color: rgb(0, 153, 0);"&gt;SQL&gt; COMMENT ON cuentas 'Tabla para el registro de cuentas corrientes personales'&lt;/pre&gt;Finalmente, el bloque que actualiza las secuencias utilizando los comentarios sobre columnas.&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;DECLARE&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  v_max NUMBER(10) := 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  v_desc varchar2(50);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;BEGIN&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  FOR t IN (SELECT u.table_name,&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;                   u.column_name,&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;                   uc.comments secuencia&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;            FROM   user_tab_columns  u,&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;                   user_col_comments uc&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;            WHERE  u.table_name = uc.table_name&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;            AND    u.column_name = uc.column_name&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;            AND    u.column_name LIKE 'ID_%')&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  LOOP&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;      EXECUTE IMMEDIATE 'SELECT MAX(' || t.column_name || ') FROM ' ||&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;t.table_name INTO v_max;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;      dbms_output.put_line('Eliminando secuencia '|| t.secuencia||'...');&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;      v_desc := 'DROP SEQUENCE '|| t.secuencia;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;      EXECUTE IMMEDIATE 'DROP SEQUENCE '|| t.secuencia;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;      EXECUTE IMMEDIATE 'CREATE SEQUENCE '|| t.secuencia ||' INCREMENT BY&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;1 START WITH '|| (NVL(v_max,0) + 1);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;      dbms_output.put_line('Secuencia '|| t.secuencia||' recreada.');&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  END LOOP;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;END;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Se requiere el permiso CREATE SEQUENCE por parte del ejecutor de este script.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8643097908149921395?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8643097908149921395/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8643097908149921395' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8643097908149921395'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8643097908149921395'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/04/sobre-comentarios-y-cmo-actualizar.html' title='Sobre comentarios y cómo actualizar secuencias'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-2908038626679953520</id><published>2008-03-28T13:31:00.000-07:00</published><updated>2008-04-01T14:29:28.602-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plsql'/><category scheme='http://www.blogger.com/atom/ns#' term='11g'/><title type='text'>Más control sobre los LOOPS en 11g</title><content type='html'>En PL/SQL podemos salir de un LOOP con la cláusula EXIT. Típicamente la usamos para la condición de salida de un loop:&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt; BEGIN&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   LOOP&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;       ...  &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;       &lt;span style="color: rgb(51, 51, 255);"&gt;EXIT&lt;/span&gt; WHEN &lt;/span&gt;&lt;span style="font-style: italic; color: rgb(0, 153, 0);"&gt;condición&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt; ;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   END LOOP;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   -- el &lt;/span&gt;&lt;span style="font-style: italic; color: rgb(0, 153, 0);"&gt;control sigue aquí&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   ...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt; END;&lt;/span&gt;&lt;/pre&gt;Ahora la versión 11g agrega un nivel intermedio: CONTINUE. Con esta sentencia, podemos cancelar la iteración del loop actual y pasar a la siguiente, sin abortar el loop. Puede usarse  simplemente &lt;span style="color: rgb(51, 51, 255);"&gt;CONTINUE&lt;/span&gt; o &lt;span style="color: rgb(51, 51, 255);"&gt;CONTINUE WHEN&lt;/span&gt;. En este último caso podremos evitar el uso de la sentencia IF para controlar la condición de salida de la iteración.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt; BEGIN&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   LOOP&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;     ...&lt;br /&gt;    &lt;span style="color: rgb(51, 51, 255);"&gt;CONTINUE WHEN&lt;/span&gt; condicion;  --vuelve al comienzo del loop&lt;br /&gt;&lt;br /&gt;    -- si condicion=true, esta parte no se ejecuta&lt;br /&gt;    ...&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   END LOOP;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;/span&gt;&lt;span style="font-style: italic; color: rgb(0, 153, 0);"&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt; END;&lt;/span&gt;&lt;/pre&gt;Los programadores conservadores se preguntarán para que necesitamos esta sentencia, despues de todo siempre nos hemos arreglado con la cláusula IF-THEN-ELSE de modo de avanzar a la siguiente iteración. Es cierto, no lo necesitamos para hacer cosas que antes no podíamos, pero sí para simplificar el código y facilitar la lectura.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Un ejemplo con CONTINUE&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Hay un caso típico donde CONTINUE puede beneficiarnos en la claridad de código para el seguimiento de la lógica: cuando tenemos que hacer múltiples validaciones antes de procesar un registro.&lt;br /&gt;&lt;br /&gt;Imaginemos nuestro loop actual (muy simplificado), en donde para cada registro hacemos una serie de validaciones antes de procesarlo. Si una validación falla, debemos pasar al siguiente registro. Para resolver esta lógica, debemos crear una estructura anidada de IFs, ya que debo evitar que se ejecute el resto del loop si una de las validaciones falla.&lt;pre style="color: rgb(0, 153, 0);"&gt;LOOP&lt;br /&gt; EXIT WHEN &lt;span style="font-style: italic;"&gt;condicion&lt;/span&gt;;&lt;br /&gt; i:=i+1;&lt;br /&gt;&lt;br /&gt; a := valida_1(T(i));&lt;br /&gt; IF (a) THEN&lt;br /&gt;   b := valida_2(a);&lt;br /&gt;   IF (b) THEN&lt;br /&gt;     ....&lt;br /&gt;     IF (c) THEN&lt;br /&gt;&lt;br /&gt;        .....&lt;br /&gt;                procesoRegistro(T(i));&lt;br /&gt;        .....&lt;br /&gt;     END IF;&lt;br /&gt;   ELSE&lt;br /&gt;       ....&lt;br /&gt;   END IF;&lt;br /&gt; END IF;&lt;br /&gt;&lt;br /&gt;END LOOP;&lt;/pre&gt;Notemos como los múltiples IF anidados complican el seguimiento del flujo. Muchas veces nos hemos visto complicados al tener que agregar una nueva validación, ya que hay que agregar un nuevo IF al loop, y debo mantener consistentes los correspondientes END IF que deshacen el nivel de anidación.&lt;br /&gt;&lt;br /&gt;Ahora veamos como puede verse mejorado con CONTINUE-WHEN:&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;LOOP&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  EXIT WHEN &lt;span style="font-style: italic;"&gt;condicion&lt;/span&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  i:=i+1;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  a := valida_1(T(i));&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  CONTINUE WHEN NOT a;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  b := valida_2(a);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  CONTINUE WHEN NOT b;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  ....&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  procesoRegistro(T(i));&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;END LOOP;&lt;/span&gt;&lt;/pre&gt;La mejoría es evidente, conseguimos disminuir la anidación (a 1) y evitar el uso repetitivo de IF/END-IF, reduciendo la posibilidad de cometer errores de lógica al momento de modificar.&lt;br /&gt;&lt;br /&gt;CONTINUE es un recurso que evidentemente solo puede utilizarse en Oracle 11g, sin embargo, siendo conscientes que nuestro código no va a compilar en versiones anteriores, es una práctica que contribuye a la legibilidad del código, con todos los beneficios que eso trae aparejado.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también sobre 11g&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/03/11g-y-sus-ndices-invisibles.html"&gt;11g y sus índices invisibles&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-2908038626679953520?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/2908038626679953520/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=2908038626679953520' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/2908038626679953520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/2908038626679953520'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/03/ms-control-sobre-los-loops-en-11g.html' title='Más control sobre los LOOPS en 11g'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-565412775676012770</id><published>2008-03-26T11:36:00.000-07:00</published><updated>2009-08-25T05:47:16.566-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='11g'/><category scheme='http://www.blogger.com/atom/ns#' term='xe'/><category scheme='http://www.blogger.com/atom/ns#' term='9i'/><category scheme='http://www.blogger.com/atom/ns#' term='10g'/><title type='text'>NATURAL JOIN antinatural</title><content type='html'>El NATURAL JOIN en Oracle tiene un bug. Las versiones 9i y 10g retornan extraños productos cartesianos cuando ejecutamos joins naturales con varias tablas a la vez.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;El join natural&lt;/strong&gt;&lt;br /&gt;Un natural join toma las columnas de igual nombre entre dos tablas y las utiliza para realizar un join. ¿Cuál es el beneficio? No hay que nombrar las columnas en el join.&lt;br /&gt;Si bien puede sonar fantástico el ahorrarnos de escribir las columnas en el JOIN, en Oracle el natural join produce resultados inesperados que veremos más adelante.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Un caso de prueba&lt;/strong&gt;&lt;br /&gt;Tengo tablas que describen clientes, órdenes, y libros. Deseo realizar una consulta joineandolas a todas.&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;p&gt;SQL&gt; desc customers&lt;br /&gt;Name      Type         Nullable Default Comments&lt;br /&gt;--------- ------------ -------- ------- --------&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;CUSTOMER#&lt;/span&gt; NUMBER(4)        &lt;br /&gt;LASTNAME  VARCHAR2(10) Y   &lt;br /&gt;FIRSTNAME VARCHAR2(10) Y   &lt;br /&gt;ADDRESS   VARCHAR2(20) Y&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;SQL&gt; desc orders&lt;br /&gt;Name Type Nullable Default Comments&lt;br /&gt;--------- --------- -------- ------- --------&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;ORDER#&lt;/span&gt; NUMBER(4)&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;CUSTOMER#&lt;/span&gt; NUMBER(4) Y&lt;br /&gt;ORDERDATE DATE Y&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;SQL&gt; desc orderitems&lt;br /&gt;Name Type Nullable Default Comments&lt;br /&gt;-------- ------------ -------- ------- --------&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;ORDER#&lt;/span&gt; NUMBER(4)&lt;br /&gt;&lt;span style="color: rgb(255, 153, 0);"&gt;ISBN&lt;/span&gt; VARCHAR2(10)&lt;br /&gt;QUANTITY NUMBER(3) Y&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;SQL&gt; desc books&lt;br /&gt;Name Type Nullable Default Comments&lt;br /&gt;------- ------------ -------- ------- --------&lt;br /&gt;&lt;span style="color: rgb(255, 153, 0);"&gt;ISBN&lt;/span&gt; VARCHAR2(10)&lt;br /&gt;TITLE VARCHAR2(30) Y&lt;br /&gt;PUBDATE DATE Y&lt;br /&gt;COST NUMBER(5,2) Y&lt;/p&gt;&lt;/span&gt; &lt;/pre&gt;&lt;span style="font-family:Courier New;"&gt;&lt;/span&gt;&lt;p&gt;A considerar:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Todas las tablas tienen primary key, foreign keys, índices y estadísticas.&lt;/li&gt;&lt;li&gt;El JOIN que deseo hacer puede verse mirando las cuatro tablas desde arriba hacia abajo.&lt;/li&gt;&lt;li&gt;Las columnas con igual color determinan las llaves del JOIN.&lt;/li&gt;&lt;li&gt;La consulta que quiero realizar es --&gt; Cuáles son los libros que ordenó JAKE LUCAS?&lt;/li&gt;&lt;/ul&gt;&lt;strong&gt;&lt;br /&gt;&lt;br&gt;Resultado con INNER JOIN&lt;/strong&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;p&gt;SQL&gt; SELECT isbn, title&lt;br /&gt; 2  FROM ((customers INNER JOIN orders USING (customer#))&lt;br /&gt; 3        INNER JOIN orderitems USING (order#))&lt;br /&gt; 4        INNER JOIN books USING (isbn)&lt;br /&gt; 5  WHERE firstname='JAKE' AND lastname='LUCAS';&lt;/p&gt;&lt;br /&gt;&lt;p&gt;ISBN       TITLE&lt;br /&gt;---------- ------------------------------&lt;br /&gt;2491748320 PAINLESS CHILD-REARING&lt;br /&gt;9247381001 HOW TO MANAGE THE MANAGER&lt;br /&gt;2491748320 PAINLESS CHILD-REARING&lt;/p&gt;&lt;br /&gt;&lt;p&gt;3 rows selected.&lt;/p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;Es el resultado esperado. El cliente JAKE LUCAS ordenó los 3 libros que aparecen. ¿Qué sucede si ejecutamos la misma consulta pero con NATURAL JOIN?&lt;/p&gt;&lt;strong&gt;Resultado con NATURAL JOIN&lt;/strong&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;p&gt;SQL&gt; SELECT isbn, title&lt;br /&gt; 2  FROM customers NATURAL JOIN orders&lt;br /&gt; 3                 NATURAL JOIN orderitems&lt;br /&gt; 4                 NATURAL JOIN books&lt;br /&gt; 5  WHERE firstname='JAKE' AND lastname='LUCAS';&lt;/p&gt;&lt;br /&gt;&lt;p&gt;ISBN       TITLE&lt;br /&gt;---------- ------------------------------&lt;br /&gt;1059831198 BODYBUILD IN 10 MINUTES A DAY&lt;br /&gt;0401140733 REVENGE OF MICKEY&lt;br /&gt;0401140733 REVENGE OF MICKEY&lt;br /&gt;0401140733 REVENGE OF MICKEY&lt;br /&gt;0401140733 REVENGE OF MICKEY&lt;br /&gt;8843172113 DATABASE IMPLEMENTATION&lt;br /&gt;8843172113 DATABASE IMPLEMENTATION&lt;br /&gt;8843172113 DATABASE IMPLEMENTATION&lt;br /&gt;3437212490 COOKING WITH MUSHROOMS&lt;br /&gt;3437212490 COOKING WITH MUSHROOMS&lt;br /&gt;3437212490 COOKING WITH MUSHROOMS&lt;br /&gt;3957136468 HOLY GRAIL OF ORACLE&lt;br /&gt;1915762492 HANDCRANKED COMPUTERS&lt;br /&gt;...&lt;/p&gt;&lt;br /&gt;&lt;p&gt;64 rows selected.&lt;/p&gt;&lt;/span&gt;&lt;/pre&gt;Retorna 64 filas, algo inquietante. Se estará realizando un producto cartesiano? Veamos el plan de ejecución:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;p&gt;SQL&gt; set autotrace traceonly explain&lt;/p&gt;&lt;br /&gt;&lt;p&gt;SQL&gt; SELECT isbn, title&lt;br /&gt; 2  FROM customers NATURAL JOIN orders&lt;br /&gt; 3                 NATURAL JOIN orderitems&lt;br /&gt; 4                 NATURAL JOIN books&lt;br /&gt; 5  WHERE firstname='JAKE' AND lastname='LUCAS';&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;  0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=9 Card=3 Bytes=204)&lt;br /&gt;  1    0   NESTED LOOPS (Cost=9 Card=3 Bytes=204)&lt;br /&gt;  2    1     MERGE JOIN (CARTESIAN) (Cost=9 Card=1 Bytes=57)&lt;br /&gt;  3    2       MERGE JOIN (Cost=6 Card=1 Bytes=22)&lt;br /&gt;  4    3         TABLE ACCESS (BY INDEX ROWID) OF 'CUSTOMERS' (TABLE) (Cost=2 Card=1 Bytes=18)&lt;br /&gt;  5    4           INDEX (FULL SCAN) OF 'SYS_C00138991' (INDEX (UNIQUE)) (Cost=1 Card=20)&lt;br /&gt;  6    3         SORT (JOIN) (Cost=4 Card=21 Bytes=84)&lt;br /&gt;  7    6           TABLE ACCESS (FULL) OF 'ORDERS' (TABLE) (Cost=3 Card=21 Bytes=84)&lt;br /&gt;  8    2       BUFFER (SORT) (Cost=6 Card=14 Bytes=490)&lt;br /&gt;  9    8         TABLE ACCESS (FULL) OF 'BOOKS' (TABLE) (Cost=3 Card=14 Bytes=490)&lt;br /&gt; 10    1     INDEX (RANGE SCAN) OF 'IX_BOOKS' (INDEX) (Cost=0 Card=2 Bytes=22)&lt;/span&gt;&lt;/pre&gt;Efectivamente, en la línea 2 del plan aparece un MERGE JOIN (CARTESIAN) lo cual nos indica que un producto cartesiano se llevó a cabo. Esto no debería suceder, ya que las columnas para vincular las tablas con NATURAL JOIN existen y fueron verificadas en la primera consulta, usando la cláusula USING del INNER JOIN.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Resultado con NATURAL JOIN variando las columnas del SELECT &lt;/strong&gt;&lt;br /&gt;Si bien lo anterior no era normal, es aún más desconcertante lo que obtenemos si variamos las columnas del select: ¡La cantidad de registros también varía!&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;p&gt;SQL&gt; SELECT #order&lt;br /&gt; 2  FROM customers NATURAL JOIN orders&lt;br /&gt; 3                 NATURAL JOIN orderitems&lt;br /&gt; 4                 NATURAL JOIN books&lt;br /&gt; 5  WHERE firstname='JAKE' AND lastname='LUCAS';&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;42 rows selected.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;SQL&gt; SELECT isbn&lt;br /&gt; 2  FROM customers NATURAL JOIN orders&lt;br /&gt; 3                 NATURAL JOIN orderitems&lt;br /&gt; 4                 NATURAL JOIN books&lt;br /&gt; 5  WHERE firstname='JAKE' AND lastname='LUCAS';&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;64 rows selected.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;SQL&gt; SELECT #customer&lt;br /&gt; 2  FROM customers NATURAL JOIN orders&lt;br /&gt; 3                 NATURAL JOIN orderitems&lt;br /&gt; 4                 NATURAL JOIN books&lt;br /&gt; 5  WHERE firstname='JAKE' AND lastname='LUCAS';&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;896 rows selected.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;SQL&gt; SELECT *&lt;br /&gt; 2  FROM customers NATURAL JOIN orders&lt;br /&gt; 3                 NATURAL JOIN orderitems&lt;br /&gt; 4                 NATURAL JOIN books&lt;br /&gt; 5  WHERE firstname='JAKE' AND lastname='LUCAS';&lt;/p&gt;&lt;br /&gt;&lt;p&gt;3 rows selected.&lt;/p&gt;&lt;/span&gt;&lt;/pre&gt;Unicamente cuando seleccionamos todas las columnas (*) obtenemos el resultado correcto. ¡Esto debería desalentar a cualquiera a usar NATURAL JOIN!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Bug reconocido&lt;/strong&gt;&lt;br /&gt;La verdad es, que este bug fue reconocido por Oracle en Metalink, el soporte oficial. Lo que se informa es, que este bug se cree que será eliminado a partir de la versión 11.2. Pero eso deja de ser un problema si eliminamos hoy el uso de natural join, definitivamente.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Nunca usar NATURAL JOIN&lt;/strong&gt;&lt;br /&gt;El NATURAL JOIN es una sentencia que está en Oracle desde la versión 9i en un esfuerzo por cumplir con el estándar ANSI SQL. Dentro del propio mundo del SQL, el NATURAL JOIN es uno accesorio inútil como pocos, del cual podemos prescindir totalmente.&lt;br /&gt;&lt;br /&gt;Más allá de que no funcione correctamente en este RDBMS, imaginemos que si lo hace y que en Oracle 11g R2 tenemos este bug solucionado: el NATURAL JOIN funciona de maravillas.&lt;br /&gt;Ahora, que sucede con mi procedimiento si el día de mañana agregan una columna de nombre &lt;em&gt;quantity&lt;/em&gt; a mi tabla &lt;em&gt;books&lt;/em&gt;?&lt;br /&gt;Como &lt;em&gt;orderitems&lt;/em&gt; ya tiene una columna &lt;em&gt;quantity&lt;/em&gt;, se van a retornar resultados inesperados, ya que la tablas se van a combinar por &lt;em&gt;isbn&lt;/em&gt; y &lt;em&gt;quantity &lt;/em&gt;(lo cual no tiene sentido). Un cambio leve en las estructuras modifica el comportamiento de mi JOIN, y en este caso no se trata de un bug sino que sería bastante lógico que lo hiciera.&lt;br /&gt;&lt;br /&gt;Usar NATURAL JOIN también nos quita claridad en nuestro código: cada vez que lo encontramos en una consulta compleja perdemos tiempo buscando en las estructuras cuáles son las columnas que coinciden.&lt;br /&gt;&lt;br /&gt;Lo mejor en todos los casos es usar INNER JOIN o la notación original de Oracle por medio de comparadores en el where. Con ellas el programador explicita cuáles son las columnas que deben entrar en juego, sin dejar nada librado al azar.&lt;br /&gt;&lt;br /&gt;En conclusión, usar NATURAL JOIN es totalmente desaconsejado en SQL, ya que nuestras consultas dependen del nombrado de columnas en las tablas relacionadas. Un cambio insignificante como agregar una columna a una tabla, podría hacer dejar de funcionar mi aplicación.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-565412775676012770?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/565412775676012770/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=565412775676012770' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/565412775676012770'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/565412775676012770'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/03/natural-join-antinatural.html' title='NATURAL JOIN antinatural'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7485380932227986378</id><published>2008-03-24T14:51:00.000-07:00</published><updated>2010-11-09T06:16:39.941-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plsql'/><category scheme='http://www.blogger.com/atom/ns#' term='11g'/><category scheme='http://www.blogger.com/atom/ns#' term='9i'/><category scheme='http://www.blogger.com/atom/ns#' term='10g'/><title type='text'>Compilación condicional en 9i</title><content type='html'>La compilación condicional permite indicarle al compilador los fragmentos de código que debe compilar en nuestro código PL/SQL, según las condiciones que el programador indique utilizando directivas especiales.&lt;br /&gt;Ahora, si bien la compilación condicional fue presentada con Oracle 10g R2, es también posible contar con esta característica en 9i y 10g R1, gracias a la aplicación de patches sobre la base de datos.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;En Oracle 10g y superiores&lt;/strong&gt;&lt;br /&gt;En Oracle 10g ya viene incorporada a partir de la versión 10.1.0.4 y activada por defecto, por lo tanto no debemos hacer nada para comenzar a utilizarla.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;En Oracle 9i&lt;/strong&gt;&lt;br /&gt;Para contar con ella en 9i debemos primero tener instalado el patch 9.2.0.6 o superiores. Luego debemos setear el parámetro indocumentado &lt;span style="color: rgb(51, 51, 255);font-family:courier new;" &gt;_plsql_conditional_compilation=TRUE&lt;/span&gt;. Este parámetro es del sistema y no es dinámico, por lo que el DBA deberá incluirlo en el archivo de parámetros y reiniciar la instancia.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;En Oracle 8i e inferiores&lt;/strong&gt;&lt;br /&gt;No es soportada.&lt;br /&gt;&lt;br /&gt;Una aplicación de la compilación condicional bastante práctica y recomendable es la de hacer que nuestro código PL/SQL compile en cualquier versión, utilizando los features propios de cada release cuando es posible, todo en el mismo código. Esta buena práctica tiene dos objetivos: el primero es hacer que nuestro paquete sea reutilizable (por ejemplo un paquete de funciones de uso general), y el segundo que nuestro código no genere un impacto al momento de migrar de versión.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Un ejemplo de compilación condicional&lt;/strong&gt;&lt;br /&gt;En este ejemplo, vamos a realizar un procedimiento para hacer un spool de determinadas cuentas que cumplen con un patrón en su código.&lt;br /&gt;La compilación condicional va a entrar en juego con la siguiente lógica:&lt;br /&gt;-Si la base es 9i, ejecuta SQL con invocaciones a SUBSTR para verificar el patrón&lt;br /&gt;-Si la base es 10g, ejecuta SQL con expresiones regulares ya que es más performante&lt;br /&gt;&lt;br /&gt;Primero compilamos la especificación de nuestro paquete, que no tiene nada de especial:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;CREATE OR REPLACE PACKAGE pack_cuentas&lt;br /&gt;IS&lt;br /&gt;PROCEDURE list_cuentas (p_max IN VARCHAR2);&lt;br /&gt;END pack_cuentas;&lt;br /&gt;/&lt;/span&gt;&lt;/pre&gt;Luego compilamos el body, donde está la diferencia:&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;CREATE OR REPLACE PACKAGE BODY pack_cuentas&lt;br /&gt;IS&lt;br /&gt;PROCEDURE spool_cuentas (p_max IN VARCHAR2)&lt;br /&gt;IS&lt;br /&gt;BEGIN&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);"&gt; $IF DBMS_DB_VERSION.VER_LE_9&lt;br /&gt; $THEN&lt;/span&gt;&lt;br /&gt;    FOR cue IN (SELECT cod_cuenta FROM cuentas&lt;br /&gt;             WHERE activo='Y' AND SUBSTR(cod_cuenta,1,2)='00'&lt;br /&gt;             AND SUBSTR(cod_cuenta,3,1) IN ('0','1','2','3')&lt;br /&gt;             AND SUBSTR(cod_cuenta,LENGTH(cod_cuenta),-1)='A')&lt;br /&gt;    LOOP&lt;br /&gt;       dbms_output.put_line(cue.cod_cuenta);&lt;br /&gt;    END LOOP;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);"&gt; $ELSE&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;    DECLARE&lt;br /&gt;       v_pattern VARCHAR2(20) := '00[0-'||p_max||']-[0-9]+A';&lt;br /&gt;    BEGIN&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;       FOR cue IN (SELECT cod_cuenta FROM cuentas&lt;br /&gt;             WHERE activo='Y' AND REGEXP_LIKE (cod_cuenta,v_pattern))&lt;br /&gt;       LOOP&lt;br /&gt;          dbms_output.put_line(cue.cod_cuenta);&lt;br /&gt;       END LOOP;  &lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;    END;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt; &lt;span style="color: rgb(51, 51, 255);"&gt;$END&lt;/span&gt;&lt;br /&gt;END spool_cuentas;&lt;br /&gt;END pack_cuentas;&lt;br /&gt;/&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Las directivas especiales $IF $THEN $END hacen el truco. Observar que este mismo código se compila en Oracle 9i, donde la función REGEXP_LIKE retornaría un error de compilación, sin embargo el fragmento de código que no corresponde a la versión ni siquiera es analizado por el compilador.&lt;br /&gt;Para poder diferenciar entre las versiones 9i, 9iR1, 9iR2, 10g, 10gR1 y 10gR2 contamos con el paquete DBMS_DB_VERSION, el cual incluye constantes para cada versión, como la que usé en el ejemplo. En el ejemplo usamos la constante &lt;em&gt;ver_le_9&lt;/em&gt; que implica 'menor o igual a 9i', pero también tenemos para indicar menor o igual a un release como por ejemplo &lt;em&gt;ver_le_9_2&lt;/em&gt;.&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;CREATE OR REPLACE package dbms_db_version is&lt;br /&gt;version constant pls_integer := 10; -- RDBMS version number&lt;br /&gt;release constant pls_integer := 2; -- RDBMS release number&lt;br /&gt;ver_le_9_1 constant boolean := FALSE;&lt;br /&gt;ver_le_9_2 constant boolean := FALSE;&lt;br /&gt;ver_le_9 constant boolean := FALSE;&lt;br /&gt;ver_le_10_1 constant boolean := FALSE;&lt;br /&gt;ver_le_10_2 constant boolean := TRUE;&lt;br /&gt;ver_le_10 constant boolean := TRUE;&lt;br /&gt;end dbms_db_version;&lt;br /&gt;/&lt;/span&gt;&lt;/pre&gt;Ahora podremos arreglar nuestro viejo código para que sea más eficiente en las versiones siguientes y que no tengamos que hacer cambios al momento de migrar, en definitiva adelantarnos y minimizar el retrabajo en el futuro.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7485380932227986378?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7485380932227986378/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7485380932227986378' title='4 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7485380932227986378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7485380932227986378'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/03/compilacin-condicional-tambin-en-9i.html' title='Compilación condicional en 9i'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-464119748010898635</id><published>2008-03-19T13:47:00.000-07:00</published><updated>2008-12-02T06:57:16.304-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>Cómo verificar archivos y directorios en Shell</title><content type='html'>Estos son mis recursos favoritos cuando necesito comandos en Unix para verificar la existencia de carpetas, saber si existen archivos o si están vacíos. Acudo a ellos comúnmente en scripts de respaldo de bases de datos, antes de invocar a RMAN o EXP bajo Linux y después de que se realizan los mismos para verificar los resultados.&lt;br /&gt;También recurro al tamaño en bytes de archivos o de todos los archivos de un directorio, especialmente para generar reportes o logs en nuestros scripts.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Verificar si una carpeta existe&lt;br /&gt;&lt;/strong&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  if [ -d &lt;em&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;archivo&lt;/span&gt;&lt;/em&gt; &lt;carpeta&gt;]&lt;br /&gt;then&lt;br /&gt;# Codigo si existe&lt;br /&gt;echo existe&lt;br /&gt;else&lt;br /&gt;# codigo si no existe&lt;br /&gt;echo no existe&lt;br /&gt;fi&lt;/carpeta&gt;&lt;/span&gt;&lt;/pre&gt;&lt;strong&gt;Verificar si un archivo de salida no esta vacío&lt;br /&gt;&lt;/strong&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  if [ -s &lt;span style="color: rgb(51, 51, 255);"&gt;&lt;em&gt;archivo&lt;/em&gt;&lt;/span&gt; &lt;archivo&gt;]&lt;br /&gt;then&lt;br /&gt;# Codigo si existe y no es vacio&lt;br /&gt;...&lt;br /&gt;else&lt;br /&gt;# codigo si no existe o es vacio&lt;br /&gt;...&lt;br /&gt;fi&lt;/archivo&gt;&lt;/span&gt;&lt;/pre&gt;&lt;strong&gt;Verificar si un archivo es escribible&lt;br /&gt;&lt;/strong&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  if [ -w &lt;span style="color: rgb(51, 51, 255);"&gt;&lt;em&gt;archivo&lt;/em&gt;&lt;/span&gt; &lt;archivo&gt;]&lt;br /&gt;then&lt;br /&gt;# Codigo si existe y es escribible&lt;br /&gt;...&lt;br /&gt;else&lt;br /&gt;# codigo si no existe o no es escribible&lt;br /&gt;...&lt;br /&gt;fi&lt;/archivo&gt;&lt;/span&gt;&lt;/pre&gt;&lt;strong&gt;Verificar la integridad de un archivo comparando checksum&lt;br /&gt;&lt;/strong&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  check1=`md5sum &lt;span style="color: rgb(51, 51, 255);"&gt;&lt;em&gt;archivo_origen&lt;/em&gt;&lt;/span&gt; | cut -d' ' -f1`&lt;br /&gt;&lt;br /&gt;check2=`md5sum &lt;span style="color: rgb(51, 51, 255);"&gt;&lt;em&gt;archivo_destino&lt;/em&gt;&lt;/span&gt; | cut -d' ' -f1`&lt;br /&gt;&lt;br /&gt;if [ $check1 -eq $check2 ]&lt;br /&gt;then&lt;br /&gt;# Integridad del archivo correcta&lt;br /&gt;...&lt;br /&gt;else&lt;br /&gt;# Error de checksum&lt;br /&gt;...&lt;br /&gt;fi&lt;/span&gt;&lt;/pre&gt;&lt;strong&gt;Obtener el tamaño de un archivo en bytes&lt;br /&gt;&lt;/strong&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  x=`du -m &lt;span style="color: rgb(51, 51, 255);"&gt;&lt;em&gt;archivo&lt;/em&gt;&lt;/span&gt; | cut -f1`&lt;/span&gt;&lt;/pre&gt;&lt;strong&gt;Sumar la cantidad total de bytes en un directorio&lt;br /&gt;&lt;/strong&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  suma=0&lt;br /&gt;for arch in *&lt;br /&gt;do&lt;br /&gt;if [ ! -d $arch ]&lt;br /&gt;then&lt;br /&gt;  tamano=`du -b $arch | cut -f1`&lt;br /&gt;  let suma=$suma+$tamano&lt;br /&gt;fi&lt;br /&gt;done&lt;br /&gt;echo $suma&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/05/comandos-tiles-para-unix.html"&gt;Comandos útiles para Unix Shell&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/11/limpieza-de-archivos-unix.html"&gt;Limpieza de archivos en Unix&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/07/esta-es-una-tarea-sencilla-en-linux.html"&gt;Cómo obtener la fecha de ayer en Unix&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-464119748010898635?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/464119748010898635/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=464119748010898635' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/464119748010898635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/464119748010898635'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/03/verificar-archivos-y-carpetas-en-shell.html' title='Cómo verificar archivos y directorios en Shell'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-3097820117578731029</id><published>2008-03-17T08:49:00.000-07:00</published><updated>2009-07-10T05:45:45.489-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='11g'/><category scheme='http://www.blogger.com/atom/ns#' term='9i'/><category scheme='http://www.blogger.com/atom/ns#' term='10g'/><category scheme='http://www.blogger.com/atom/ns#' term='estadísticas'/><title type='text'>Error ORA-942 while gathering statistics</title><content type='html'>Este error puede suceder cuando un usuario no DBA quiere setear AUTOTRACE en su sesión.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; set autotrace traceonly statistics;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Error ORA-942 while gathering statistics&lt;/span&gt; &lt;div style="color: rgb(0, 153, 0); font-family: courier new;"&gt;SP2-0611: Error enabling STATISTICS report&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Si es un usuario con acceso limitado, entonces es de esperar que no tenga grants sobre las vistas que se necesitan para leer las estadísticas generadas por AUTOTRACE. Se requieren permisos explícitos de lectura sobre las vistas &lt;span style="font-family:courier new;"&gt;v$_session&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;v_$sesstat&lt;/span&gt; y &lt;span style="font-family:courier new;"&gt;v_$statname&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Para facilitar la asignación de estos grants, el administrador cuenta con un script para crear un rol especial y así poder extenderlo a los usuarios.&lt;br /&gt;&lt;br /&gt;Script:&lt;br /&gt;&lt;span style="font-style: italic;"&gt;$ORACLE_HOME/sqlplus/admin/PLUSTRCE.SQL&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Cómo hacer para que un usuario pueda usar AUTOTRACE&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;1. En el sistema operativo, posicionarse sobre el directorio con el script.&lt;br /&gt;&lt;pre&gt;    &lt;span style="color: rgb(0, 153, 0);"&gt;lfer@linux&gt;  cd $ORACLE_HOME/sqlplus/admin&lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;2. Loguearse a SQL*plus con usuario 'AS SYSDBA'&lt;br /&gt;&lt;br /&gt;3. Ejecutar en SQL*plus:&lt;br /&gt;&lt;pre&gt;    &lt;span style="color: rgb(0, 153, 0);"&gt;SQL&gt; @PLUSTRCE.SQL&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;4. Otorgar el rol PLUSTRACE a todos los usuarios que deseen usar AUTOTRACE:&lt;br /&gt;&lt;pre&gt;    &lt;span style="color: rgb(0, 153, 0);"&gt;grant PLUSTRACE to &lt;/span&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;USUARIO&lt;/span&gt;;&lt;/pre&gt;&lt;strong&gt;Ver también:&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14357/ch8.htm#i1037226" target="_blank"&gt;SQLPlus Users Guide and Reference 10g sobre AUTOTRACE&lt;/a&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/03/error-ora-942-while-gathering.html"&gt;&lt;br /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-3097820117578731029?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/3097820117578731029/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=3097820117578731029' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3097820117578731029'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3097820117578731029'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/03/error-ora-942-while-gathering.html' title='Error ORA-942 while gathering statistics'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7547813763606410110</id><published>2008-03-12T10:52:00.000-07:00</published><updated>2009-06-12T08:55:10.882-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plsql'/><title type='text'>Cómo detectar caracteres extraños o no imprimibles</title><content type='html'>Muchas veces pasa que en las cargas de datos aparecen caracteres de control como ^M o símbolos ilegibles, y no es deseable que estos queden almacenados entre los datos.&lt;br /&gt;¿Cómo detectar si existen?&lt;br /&gt;Es sencillo si contamos con expresiones regulares en SQL como en 10g, pero en 9i igualmente podemos crear una función que recorra cada uno de los caracteres y verifique si pertenece al juego de caracteres 'raros'.&lt;br /&gt;Mostraré una sencilla implementación compatible con todas las versiones de Oracle, pero puede considerarse utilizar compilación condicional para utilizar expresiones regulares cuando sea posible.&lt;br /&gt;El código:&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;CREATE OR REPLACE FUNCTION buscar_no_imprimible(v_cadena VARCHAR2) &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;RETURN BOOLEAN IS&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  v_ret BOOLEAN := FALSE;    &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  v_iter NUMBER := 1;    &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  v_ascii_min NUMBER := 33;  &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  v_ascii_max NUMBER := 126;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;BEGIN&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  WHILE (v_iter &lt;= LENGTH(v_cadena))&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;    LOOP&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;      IF ASCII(SUBSTR(v_cadena,v_iter,1))&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;      NOT BETWEEN v_ascii_min AND v_ascii_max THEN&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;        v_ret := TRUE;&lt;br /&gt;     EXIT;   &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;      END IF;        &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;      v_iter := v_iter + 1; &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;    END LOOP;    &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;    RETURN(v_ret);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;END buscar_no_imprimible;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:Courier New;" &gt;&lt;/span&gt;&lt;/pre&gt;Esta función booleana retorna TRUE si la cadena contiene un caracter no imprimible, FALSE en caso contrario. Una rápida mirada en una tabla &lt;a href="http://www.techonthenet.com/ascii/chart.php" target="_blank"&gt;ASCII&lt;/a&gt; nos dice que el rango de caracteres válidos se encuentra entre las representaciones decimales 33 y 126.&lt;br /&gt;&lt;br /&gt;Si se desean considerar caracteres del &lt;a href="http://www.cdrummond.qc.ca/cegep/informat/Professeurs/Alain/files/ascii.htm" target="_blank"&gt;ASCII extendido&lt;/a&gt; (por ejemplo vocales con acentos), bastará ajustar el procedimiento para incluir el conjunto de caracteres deseado.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Ver también:&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2009/06/ordenar-resultados-por-el-alfabeto.html" target="_blank"&gt;Como eliminar acentos del español&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/09/limpiando-cdigo-ddl.html"&gt;Limpiando código DDL&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/03/compilacin-condicional-tambin-en-9i.html"&gt;Compilación condicional en 9i&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7547813763606410110?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7547813763606410110/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7547813763606410110' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7547813763606410110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7547813763606410110'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/03/cmo-detectar-caracteres-extraos-o-no.html' title='Cómo detectar caracteres extraños o no imprimibles'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-1372073584381281763</id><published>2008-03-10T13:40:00.000-07:00</published><updated>2009-01-27T06:16:24.908-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='11g'/><category scheme='http://www.blogger.com/atom/ns#' term='índices'/><title type='text'>11g y sus Indices Invisibles</title><content type='html'>Los índices invisibles, disponibles a partir de Oracle Database 11g, según la documentación oficial, son una interesante alternativa al borrado de índices o seteo como "unusable". Pero, son realmente una alternativa?&lt;br /&gt;&lt;br /&gt;Según la información disponible, un índice invisible será ignorado por el optimizador al momento de evaluar los planes de ejecución, excepto que alteremos el valor de sesión (o sistema) &lt;span style="font-family:courier new;"&gt;OPTIMIZER_USE_INVISIBLE_INDEXES&lt;/span&gt;, pero en todo momento el estado del índice será válido. Esto nos brinda múltiples beneficios.&lt;br /&gt;&lt;br /&gt;Un índice &lt;em&gt;unusable&lt;/em&gt; es una forma sutil de dejar un índice "fuera de combate" temporalmente en TODAS las sesiones. Un índice &lt;em&gt;invisible&lt;/em&gt; en cambio es selectivo: podemos verlo (y usarlo) en una sesión mientras en el resto hacer como si no existiera.&lt;br /&gt;&lt;br /&gt;Un índice invisible puede volverse visible sin costo alguno, con una simple alteración del mismo: &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ALTER INDEX mi_indice VISIBLE&lt;/span&gt;. Un índice unusable en cambio deberá ser reconstruído, ya que las actualizaciones de datos sobre la tabla no fueron reflejadas mientras el índice estuvo en ese estado. Los índices invisibles SIEMPRE son mantenidos por Oracle con cada sentencia DML.&lt;br /&gt;&lt;br /&gt;Con estas afirmaciones, podemos concluir que los índices invisibles llegan para sustituir el estado unusable?&lt;br /&gt;-No! El propósito es completamente diferente. Los índices invisibles evitarán, por ejemplo, que tengamos que 'inutilizar' índices para poder realizar performance tunning, pero en ciertos casos particulares seguiremos necesitando del estado unusable.&lt;br /&gt;&lt;br /&gt;Cuando tengamos que realizar una carga masiva de datos, tendremos que alterar el índice como 'unusable' para realizar la carga eficientemente, ya que la invisibilidad de índices de 11g tiene efecto sobre las consultas SELECT y no sobre las demás sentencias DML. El índice invisible continúa aportando overhead en cada modificación de datos.&lt;br /&gt;&lt;br /&gt;La clave de este novedoso estado está en el testeo de consultas. Por ejemplo, podremos probar cómo se comporta un SELECT con determinado índice, sin que el ambiente en el cual lo estoy probando se entere. Creo un índice invisible, seteo el valor de &lt;span style="font-family:courier new;"&gt;OPTIMIZER_USE_INVISIBLE_INDEXES&lt;/span&gt; como true, y testeo mi aplicación sin efectos colaterales.&lt;br /&gt;&lt;br /&gt;Otro beneficio, razonando en forma opuesta: ¿Cómo funcionaría mi aplicación si determinado índice no existiera?&lt;br /&gt;Alteramos el índice como 'invisible', seteamos en nuestra sesión &lt;span style="font-family:courier new;"&gt;OPTIMIZER_USE_INVISIBLE_INDEXES = false&lt;/span&gt;, y probamos. Los demás usuarios mientras tanto, continuarán utilizándolo normalmente.&lt;br /&gt;&lt;br /&gt;Los índices invisibles son un valor agregado a nuestra forma de trabajo, ya que nos proveen transparencia sobre el ambiente. Nos permiten realizar nuestras pruebas tranquilos de que nadie está siendo afectado.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Ver también:&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/11/indices-condicionales.html"&gt;Indices condicionales&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/05/indices-cmo-y-cundo.html"&gt;Mis índices no funcionan!&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2009/01/indices-de-funcin.html"&gt;Indices de función para mejorar un LIKE&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B28359_01/server.111/b28310/indexes003.htm#BABDHCJD" target="_blank"&gt;Oracle Database Administrator's Guide 11g sobre índices invisibles&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-1372073584381281763?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/1372073584381281763/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=1372073584381281763' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1372073584381281763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1372073584381281763'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/03/11g-y-sus-ndices-invisibles.html' title='11g y sus Indices Invisibles'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-3303211077234446376</id><published>2008-03-08T13:19:00.000-08:00</published><updated>2010-03-04T05:01:15.695-08:00</updated><title type='text'>SOUNDEX en español</title><content type='html'>SOUNDEX, función que ha estado presente en Oracle desde tiempos remotos, es un clásico algoritmo fonético utilizado para indexar nombres propios, asociando palabras que suenen igual a pesar de ocasionales diferencias en su escritura.&lt;br /&gt;&lt;br /&gt;El algoritmo SOUNDEX fue concebido teniendo en cuenta la fonética del idioma inglés, por lo que hace inadecuado su uso en el castellano. Realizando adaptaciones propias del idioma español, implementé una versión en PL/SQL llamada SOUNDESP, la cual respeta la esencia del algoritmo original.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Los pasos básicos son:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Retener la primera letra de la cadena. Tener en cuenta las letras dobles como CH y LL.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Remover todas las ocurrencias de las letras siguientes a partir de la segunda posición: a, e, i, o, u, h, w, y (cuando suena como vocal i )&lt;/li&gt;&lt;li&gt;Asignar números a las siguientes letras (luego de la primera): &lt;ul&gt;&lt;li&gt;b, f, p, v = 1&lt;/li&gt;&lt;li&gt;c, g, j, k, q, s, x, z = 2&lt;/li&gt;&lt;li&gt;d, t = 3&lt;/li&gt;&lt;li&gt;l = 4&lt;/li&gt;&lt;li&gt;m, n = 5&lt;/li&gt;&lt;li&gt;r = 6&lt;/li&gt;&lt;li&gt;ll, y, ch = 7&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Si hay números consecutivos, dejar solamente uno en la serie.&lt;/li&gt;&lt;li&gt;Retornar los cuatro primeros caracteres, si son menos de cuatro completar con ceros.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;SOUNDESP es un proyecto abierto y es bienvenido cualquier comentario para mejorar su implementación.&lt;br /&gt;Ultima versión: 1.8 (04-MAR-2010)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div style="text-align: center;"&gt;&lt;img id="BLOGGER_PHOTO_ID_5175490114821023442" style="cursor: pointer;" alt="" src="http://3.bp.blogspot.com/_fRmrOAuwnQ4/R9MI1qhz5tI/AAAAAAAAADI/-IEgBrozxZA/s320/code.jpg" /&gt; &lt;a href="http://apex.oracle.com/pls/otn/wwv_flow_file_mgr.get_file?p_security_group_id=7043734611203540596&amp;amp;p_fname=pkg_snd_1.8.pck" target="_blank"&gt;Descargar Código&lt;/a&gt; &lt;img id="BLOGGER_PHOTO_ID_5175489526410503874" style="cursor: pointer;" alt="" src="http://2.bp.blogspot.com/_fRmrOAuwnQ4/R9MITahz5sI/AAAAAAAAADA/L9NY7wTm94I/s320/execute.jpg" border="0" /&gt; &lt;a href="http://apex.oracle.com/pls/otn/f?p=18527:1:2128667202552406:::::" target="_blank"&gt;Probar SOUNDESP&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-size:78%;"&gt;NOTA: Para descargar el código correctamente, haga click derecho y elija Guardar destino&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Soundex" target="_blank"&gt;Wikipedia: Soundex&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-3303211077234446376?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/3303211077234446376/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=3303211077234446376' title='37 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3303211077234446376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3303211077234446376'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/03/soundex-en-espaol.html' title='SOUNDEX en español'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_fRmrOAuwnQ4/R9MI1qhz5tI/AAAAAAAAADI/-IEgBrozxZA/s72-c/code.jpg' height='72' width='72'/><thr:total>37</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8527475554639066943</id><published>2008-02-27T11:36:00.000-08:00</published><updated>2008-03-24T09:57:56.908-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><title type='text'>Insertar o actualizar un único registro con MERGE</title><content type='html'>La versión 9i de Oracle Database traía entre sus SQL features, la incorporación de una novedosa sentencia: MERGE (ahora parte del estándar ANSI SQL). Hasta ese momento, cuando se quería insertar/actualizar datos en una tabla no vacía, había que revisar si el registro ya existía para aplicar INSERT o UPDATE, manejándolo proceduralmente con algún lenguaje, por ejemplo PL/SQL. Con MERGE, podemos tener esta lógica en una única sentencia SQL, simplificando el código y haciendo la tarea más performante.&lt;br /&gt;&lt;br /&gt;La sintáxis de MERGE está pensada para que la fuente de datos que se va a insertar sea una tabla o una consulta, de esta manera:&lt;br /&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0)"&gt;&lt;span style="font-family:courier new;"&gt;MERGE INTO &lt;em&gt;&lt;span style="color:#3333ff;"&gt;[tabla_destino]&lt;/span&gt;&lt;span style="COLOR: rgb(51,51,255)"&gt;&lt;tabla_destino&gt;&lt;/tabla_destino&gt;&lt;/span&gt;&lt;/em&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;USING (&lt;span style="COLOR: rgb(51,51,255)"&gt;&lt;em&gt;[tabla o vista o consulta]&lt;/em&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0)"&gt;&lt;span style="font-family:courier new;"&gt;ON &lt;em&gt;(&lt;span style="COLOR: rgb(51,51,255)"&gt;[condición de existencia de registro]&lt;/span&gt;&lt;span style="COLOR: rgb(51,51,255)"&gt;&lt;condicion&gt;&lt;/condicion&gt;&lt;/span&gt;&lt;/em&gt;)&lt;br /&gt;WHEN MATCHED THEN &lt;span style="COLOR: rgb(51,51,255)"&gt;&lt;span style="FONT-STYLE: italic"&gt;[sentencia de actualización]&lt;/span&gt;&lt;em&gt;&lt;span style="COLOR: rgb(51,51,255)"&gt;&lt;sentencia&gt;&lt;/sentencia&gt;&lt;/span&gt;&lt;/em&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;WHEN NOT MATCHED THEN &lt;span style="COLOR: rgb(51,51,255)"&gt;&lt;span style="FONT-STYLE: italic"&gt;[sentencia de inserción]&lt;/span&gt;&lt;em&gt;&lt;span style="COLOR: rgb(51,51,255)"&gt;&lt;sentencia&gt;&lt;/sentencia&gt;&lt;/span&gt;&lt;/em&gt;&lt;/span&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0)"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;Hay veces en que los datos a incorporar no provienen de ninguna tabla, sino que se trata de un único registro enviado por una aplicación (por ejemplo a través de parámetros). &lt;/span&gt;Una forma de poder aprovechar la sentencia MERGE en estos casos, es construir una consulta sobre la tabla DUAL que convenientemente contiene una única fila.&lt;br /&gt;&lt;br /&gt;Partiendo de una tabla &lt;em&gt;tabla_destino&lt;/em&gt;, donde queremos hacer la actualización de datos:&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;CREATE TABLE tabla_destino (a int, b varchar2(10));&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Si:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Nuestro registro a insertar es {&lt;em&gt;param_a&lt;/em&gt;, &lt;em&gt;param_b&lt;/em&gt;}&lt;/li&gt;&lt;li&gt;La condición de que el registro exista es la columna a &lt;/li&gt;&lt;li&gt;Lo que queremos actualizar en caso que el registro exista es la columna b&lt;/li&gt;&lt;/ul&gt;...entonces usamos MERGE así:&lt;br /&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;1 &lt;/span&gt;MERGE INTO tabla_destino td&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;2 &lt;/span&gt;USING (SELECT &lt;span style="COLOR: rgb(51,51,255)"&gt;param_a&lt;/span&gt; a FROM dual) d&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;3 &lt;/span&gt;ON (td.a = d.a)&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;4 &lt;/span&gt;WHEN MATCHED THEN UPDATE SET td.b = &lt;span style="COLOR: rgb(51,51,255)"&gt;param_b&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;5 &lt;/span&gt;WHEN NOT MATCHED THEN INSERT (td.a, td.b) VALUES (&lt;span style="COLOR: rgb(51,51,255)"&gt;param_a&lt;/span&gt;, &lt;span style="COLOR: rgb(51,51,255)"&gt;param_b&lt;/span&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En la línea 2 (USING) seleccionamos el registro que participa en la condición que el registro exista.&lt;br /&gt;En la línea 3 especificamos cual es la condición que se considera que el registro ya existe&lt;br /&gt;Las líneas 4 y 5 actualizan o insertan el registro.&lt;br /&gt;&lt;br /&gt;Una forma simplificada y equivalente es la siguiente:&lt;br /&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;MERGE INTO tabla_destino td&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;USING dual d&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;ON (td.a = &lt;span style="COLOR: rgb(51,51,255)"&gt;param_a&lt;/span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;WHEN MATCHED THEN UPDATE SET td.b = &lt;span style="COLOR: rgb(51,51,255)"&gt;param_b&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;WHEN NOT MATCHED THEN INSERT (td.a, td.b) VALUES (&lt;span style="COLOR: rgb(51,51,255)"&gt;param_a&lt;/span&gt;, &lt;span style="COLOR: rgb(51,51,255)"&gt;param_b&lt;/span&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Se recomienda el uso de MERGE ya que es una única sentencia SQL y naturalmente está optimizada por el motor. Hay excepciones como es el caso de cargas masivas de datos con millones de registros donde la performance de MERGE se degrada. Allí existen otras alternativas más eficientes.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Ver también:&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B10501_01/server.920/a96540/statements_915a.htm#SQLRF01606" target="_blank"&gt;Oracle 9i SQL Reference para MERGE&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_9016.htm#sthref9516" target="_blank"&gt;Oracle 10g SQL Reference para MERGE&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/10/update-condicional.html" target="_blank"&gt;Update condicional&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8527475554639066943?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8527475554639066943/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8527475554639066943' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8527475554639066943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8527475554639066943'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/02/insertar-o-actualizar-un-nico-registro.html' title='Insertar o actualizar un único registro con MERGE'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-248388662330352783</id><published>2008-01-29T04:39:00.000-08:00</published><updated>2008-04-17T08:46:44.970-07:00</updated><title type='text'>Patches críticos en enero para todos los productos Oracle</title><content type='html'>Acaban de ser liberados patches críticos que afectan a los principales productos de Oracle: Oracle Database (9i, 10g, 11g), Application Server (9, 10g R2 y R3), Collaboration Suite 10g, E-Bussiness Suite (11i y 12) y People Soft PeopleTools.&lt;br /&gt;&lt;br /&gt;Se recomienda fuertemente que estos patches sean aplicados, ya que reducen potencialmente las posibilidades de ataques recientemente descubiertos.&lt;br /&gt;&lt;br /&gt;En la Base de Datos se descubrieron 8 puntos vulnerables en los siguientes módulos: XML DB, Advanced Queuing, Oracle Spatial, Ultra-Search y en el propio CORE de 11g.&lt;br /&gt;En el Application Server los fixes afectan al J-Initiator, BPEL, Forms y JDeveloper 10g, Internet Directory.&lt;br /&gt;Como parte de la Collaboration Suite, Oracle Ultra-Search 10.1.2 es el único componente afectado.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Vea también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://www.oracle.com/technology/deploy/security/critical-patch-updates/cpujan2008.html" target="_blank"&gt;Oracle Critical Patch Update Advisory - January 2008&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/04/oracle-libera-parches-de-seguridad-para.html"&gt;Oracle Critical Patch Update Advisory - April 2008&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-248388662330352783?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/248388662330352783/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=248388662330352783' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/248388662330352783'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/248388662330352783'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/01/patches-crticos-en-enero-para-todos-los.html' title='Patches críticos en enero para todos los productos Oracle'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-5408104848981339990</id><published>2008-01-28T12:33:00.000-08:00</published><updated>2008-03-10T16:58:37.118-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='10g'/><title type='text'>Sesiones de Búsqueda</title><content type='html'>Ya lo habiamos comentado hace algún tiempo, la consola de administración de Oracle 10g en español tiene algunos errores de traducción. Este es de los más feos que he visto.&lt;br /&gt;&lt;br /&gt;Si en algún momento buscan el listado de sesiones de usuario en la instancia, no esperen hallarla con un nombre coherente.&lt;br /&gt;&lt;br /&gt;Les doy una pista, en la version en inglés se llega a a través del link &lt;span style="font-weight: bold;"&gt;Session Search&lt;/span&gt;...&lt;br /&gt;&lt;br /&gt;Espera encontrar Búsqueda de Sesiones? Mal!, no realice la traducción correctamente. Hallará en su lugar &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;Sesiones de Búsqueda&lt;/span&gt;.&lt;/span&gt; Una preciosura. Ahora ejecute una búqueda en blanco para obtener el listado.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_fRmrOAuwnQ4/R55BcirY7pI/AAAAAAAAACQ/DzTTQECs1Us/s1600-h/sesionesBusqueda.JPG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_fRmrOAuwnQ4/R55BcirY7pI/AAAAAAAAACQ/DzTTQECs1Us/s320/sesionesBusqueda.JPG" alt="" id="BLOGGER_PHOTO_ID_5160634181614104210" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-5408104848981339990?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/5408104848981339990/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=5408104848981339990' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/5408104848981339990'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/5408104848981339990'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2008/01/sesiones-de-bsqueda.html' title='Sesiones de Búsqueda'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_fRmrOAuwnQ4/R55BcirY7pI/AAAAAAAAACQ/DzTTQECs1Us/s72-c/sesionesBusqueda.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8007735236960807531</id><published>2007-12-26T06:06:00.000-08:00</published><updated>2007-12-26T08:47:04.646-08:00</updated><title type='text'>Cadena de conexión con jdbc thin</title><content type='html'>Error al testear la conexión JDBC en JDeveloper 10g -&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;Test Failed: La dirección URL de Oracle especificada no es válida&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Un método que puede solucionar este frustrante mensaje es usar la cadena de conexión que incluye la descripción del servicio (tal como la colocaríamos en el archivo tnsnames.ora)&lt;br /&gt;&lt;br /&gt;jdbc:oracle:thin:@&lt;span style="color: rgb(0, 0, 153);"&gt;(DESCRIPTION =&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;    (ADDRESS = (PROTOCOL = TCP)(HOST = host)(PORT = puerto))&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;    (CONNECT_DATA =&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;      (SERVER = DEDICATED)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;      (SERVICE_NAME = base_de_datos)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;    )&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;  )&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;Si todavía no funciona es recomendable probar la conexión a través de un cliente Oracle, configurando el archivo tnsnames.ora. Al momento que esta conexión logre conectarnos, solo resta reemplazar en la cadena de conexión la misma descripción del servicio del tnsnames (arriba en azul). &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8007735236960807531?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8007735236960807531/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8007735236960807531' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8007735236960807531'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8007735236960807531'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/12/cadena-de-conexin-con-jdbc-thin.html' title='Cadena de conexión con jdbc thin'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-2625368578269019250</id><published>2007-11-11T17:18:00.000-08:00</published><updated>2009-01-27T06:15:48.047-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='índices'/><title type='text'>Indices condicionales</title><content type='html'>Hay tablas para las cuales desearíamos hacer valer condiciones de unicidad pero dadas ciertas condiciones que dependan de alguna otra columna de control.&lt;br /&gt;&lt;br /&gt;Un ejemplo típico se da cuando mantenemos registros en una tabla con borrado lógico, y queremos que exista un único registro activo (pudiendo aparecer el registro inactivo más de una vez). Digamos que tenemos la tabla t, con dos columnas fundamentales: un id y un indicador de registro activo Y/N. Queremos que el Id sea único solamente cuando Activo es 'Y'.&lt;br /&gt;&lt;br /&gt;Muchas veces, mucha gente se enfrenta a este problema y lo resuelve creando una tabla auxiliar para hacer valer la condición de unicidad, almacenando en ella las tuplas que no desea repetir para lograr dicho control.&lt;br /&gt;Todo el problema que implica crear (y mantener) estructuras adicionales y la complejidad de manipulación, puede evitarse utilizando lo que siempre estuvo al alcance: el índice de función y la sentencia CASE.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; CREATE TABLE t (sec int primary key, id VARCHAR2(100), activo VARCHAR2(1));&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Table created.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; CREATE UNIQUE INDEX ix_t ON t&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;2 (CASE WHEN activo='Y' THEN id ELSE NULL END);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Index created.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; insert into t values (1, 'COD1', 'Y');&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;1 row created.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; insert into t values (2, 'COD1', 'N');&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;1 row created.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; insert into t values (3, 'COD1', 'N');&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;1 row created.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; insert into t values (4, 'COD1', 'Y');&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;insert into t values (4, 'COD1', 'Y')&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;*&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ERROR at line 1:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ORA-00001: unique constraint (LFER.IX_T) violated&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Los índices condicionales no son un feature especial de Oracle, sino que se construyen utilizando los mismos índices de función que usualmente definimos para todas las filas de una tabla. En este caso, la función CASE permite que el índice único se construya sobre un subconjunto de registros, aquellos en el que el campo activo sea igual a 'Y'.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Ver también:&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/strong&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/03/11g-y-sus-ndices-invisibles.html"&gt;11g y sus índices invisibles&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/05/indices-cmo-y-cundo.html"&gt;Mis índices no funcionan!&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2009/01/indices-de-funcin.html"&gt;Indices de función para mejorar un LIKE&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-2625368578269019250?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/2625368578269019250/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=2625368578269019250' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/2625368578269019250'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/2625368578269019250'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/11/indices-condicionales.html' title='Indices condicionales'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-2084426688960669773</id><published>2007-11-07T16:18:00.000-08:00</published><updated>2008-03-28T15:11:21.378-07:00</updated><title type='text'>Variables en SQL*Plus (parte 3)</title><content type='html'>Anteriormente repasando variables en sqlplus, vimos las variables de sustitución y las de usuario. Finalmente llegamos al tercer tipo de variables, las variables bind (o de ligadura).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;VARIABLES BIND&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Estas variables tienen la particularidad de poder ser definidas en sqlplus, usadas y/o asignadas dentro de nuestros programas PL/SQL y luego leídas una vez terminado el programa. Son convenientes cuando queremos tener comunicación entre scripts de sqlplus y bloques de PL/SQL.&lt;br /&gt;&lt;br /&gt;Las variables bind se definen así:&lt;br /&gt;&lt;pre class="CE"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;SQL&gt; VARIABLE bvar NUMBER;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-size:85%;"&gt;Pueden utilizarse también los tipos: CHAR,  VARCHAR2, NCHAR, NVARCHAR2, CLOB, BINARY_FLOAT y BINARY_DOUBLE&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Si queremos asignar estas variables antes de llamar a nuestros bloques de PL/SQL, debemos hacerlo desde otro bloque BEGIN END o simplemente usando la sentencia EXEC:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; exec :bvar := 1;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;PL/SQL procedure successfully completed.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Las variables dentro de PL/SQL se referencian con dos puntos seguidos del nombre de la misma.&lt;br /&gt;Ahora ya podemos utilizar las variables dentro de nuestro programa e imprimir el resultado:&lt;pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;SQL&gt; BEGIN&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   2   IF :bvar = 1 THEN&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   3     :bvar := 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   4   ELSE&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   5     :bvar := 1;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   6   END IF;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   7 END;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;   8 /&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;PL/SQL procedure successfully completed.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; print bvar&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;      BVAR&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;----------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;         0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; /&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;PL/SQL procedure successfully completed.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; print bvar&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;      BVAR&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;----------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;         1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Y con esto culminamos el repaso de variables en sql*plus. Finalmente como complemento, veremos brevemente los cursores referenciales los cuales son también útiles para recuperar un conjunto de tuplas de nuestros SELECTs, desde los programas PL/SQL hacia fuera, y así tener más flexibilidad.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;REFCURSORS&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Se declaran como una variable bind, pero colocamos REFCURSOR en el tipo:&lt;br /&gt;&lt;br /&gt;&lt;pre class="CE"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;SQL&gt; VARIABLE rcur REFCURSOR;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; begin&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  2     OPEN :rcur FOR select segment_name, segment_type from user_segments orde&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;r by segment_name;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  3  end;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  4   /&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;PL/SQL procedure successfully completed.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A diferencia de los cursores tradicionales que se abren y requieren del uso de fetch, al abrir estos cursores bind ya son ejecutados automáticamente y sus resultados almacenados en memoria. Solo resta imprimir las filas de la consulta.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;SQL&gt; print rcur&lt;/span&gt;&lt;pre style="color: rgb(0, 153, 0);"&gt;SEGMENT_NAME         SEGMENT_TYPE&lt;br /&gt;-------------------- ------------------&lt;br /&gt;ACUMULADO_CONSUMO    TABLE&lt;br /&gt;BIG_TABLE            TABLE&lt;br /&gt;BIG_TABLE_PK         INDEX&lt;br /&gt;CLIENTE              TABLE&lt;br /&gt;COUNTRY_C_ID_PK      INDEX&lt;br /&gt;CUENTA               TABLE&lt;br /&gt;INVENTARIO           TABLE&lt;br /&gt;IX_T                 INDEX&lt;br /&gt;MUNICIPIO            TABLE&lt;br /&gt;NDX_ACUMCON          INDEX&lt;br /&gt;NDX_ACUMRENT         INDEX&lt;br /&gt;NDX_ACUMTERC         INDEX&lt;br /&gt;NDX_ACUMVAR          INDEX&lt;br /&gt;&lt;br /&gt;13 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0); font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/06/variables-en-sqlplus-parte-1.html"&gt;Variables en SQLPlus (parte 1)&lt;/a&gt; - Sobre variables de usuario&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/07/sqlplus-variables-parte-2.html"&gt;Variables en SQLPlus (parte 2)&lt;/a&gt; - Sobre variables de sustitución&lt;a href="http://oraclenotepad.blogspot.com/2007/06/variables-en-sqlplus-parte-1.html"&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-2084426688960669773?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/2084426688960669773/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=2084426688960669773' title='3 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/2084426688960669773'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/2084426688960669773'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/11/variables-en-sqlplus-parte-3.html' title='Variables en SQL*Plus (parte 3)'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7210759851063859899</id><published>2007-10-23T20:25:00.000-07:00</published><updated>2008-03-24T09:53:30.703-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='índices'/><title type='text'>Update condicional</title><content type='html'>¿Cómo se hace un UPDATE condicional?&lt;br /&gt;Digamos que tengo una tabla t con 3 campos a, b y c. Quiero que si a=1 entonces actualizo b con algún valor, y si a=2 actualizo c.&lt;br /&gt;&lt;br /&gt;Alguien de mi equipo se encontró con este dilema y lo resolvió de forma muy práctica: dos updates, uno para actualizar aquellos valores con a=1 y otro para actualizar los a=2:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;1&lt;/span&gt; BEGIN&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;2&lt;/span&gt;    &amp;nbsp;&amp;nbsp; UPDATE t&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;3&lt;/span&gt;     &amp;nbsp;&amp;nbsp;SET b=valor&lt;/span&gt;&lt;valor&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;4&lt;/span&gt;     &amp;nbsp;&amp;nbsp;WHERE a=1;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;5&lt;/span&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;6&lt;/span&gt;     &amp;nbsp;&amp;nbsp;UPDATE t&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;7&lt;/span&gt;     &amp;nbsp;&amp;nbsp;SET c=valor&lt;/span&gt;&lt;valor&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;8&lt;/span&gt;     &amp;nbsp;&amp;nbsp;WHERE a=2;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;9&lt;/span&gt; END;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Otro programador del equipo refutó: -¿Para qué ejecutar dos sentencias update si en su lugar puedes hacer una?&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;1&lt;/span&gt; BEGIN&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;2&lt;/span&gt;     &amp;nbsp;&amp;nbsp;UPDATE t&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;3&lt;/span&gt;     &amp;nbsp;&amp;nbsp;SET B=DECODE(A,1,valor&lt;/span&gt;&lt;valor&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;,2,B),&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;4&lt;/span&gt;         &amp;nbsp;&amp;nbsp;C=DECODE(A,1,C,2,&lt;/span&gt;&lt;valor&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;valor); &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;span style="color: rgb(0, 0, 0);"&gt;5&lt;/span&gt; END;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;¿Quién de los dos tiene razón?&lt;br /&gt;&lt;br /&gt;...depende.&lt;br /&gt;&lt;br /&gt;Dado que la performance parece ser lo que está en juego, la primera pregunta que cabe es:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;¿Tiene la tabla t índice por A?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Si la tabla no tiene índice, entonces debemos recorrerla por entero. En el primer caso recorremos la tabla dos veces, mientras que en el segundo caso lo hacemos solamente una! Por lo tanto la primera solución en este escenario es el doble más lenta que la segunda. Al notar que la tabla no tiene índices, el Programador 2 asiente la cabeza con orgullo.&lt;br /&gt;&lt;br /&gt;Inmediatamente el Programador 1 reacciona: -Pongámosle un índice a t entonces!&lt;br /&gt;(el ávido Programador 1 sabe que la segunda consulta no puede sacar provecho de un índice por lo que su victoria parece asegurada)&lt;br /&gt;&lt;br /&gt;Y aquí es donde cabe la segunda pregunta:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;¿Cuál es la cardinalidad de la columna A?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Sería de gran ayuda saber si 1 y 2 son la mayoría de los valores que hay en la columna A, porque en ese caso, el optimizador va a continuar prefiriendo recorrer toda la tabla! Si la cardinalidad de los valores 1 y 2 son un porcentaje muy bajo del  conjunto de valores distintos, entonces recién ahí los índices van a ser utilizados.&lt;br /&gt;Cuando el índice es utilizado por la escasa presencia de valores 1 y 2, los tiempos de la primer solución es abismalmente inferior.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Conclusión:&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;La solución 1 es mejor si existen índice sobre la columna A y su cardinalidad es alta (hay pocos 1 y 2 en A).&lt;br /&gt;La solución 2 es mejor si no existen índices o existe y la cardinalidad de A es baja para valores 1 y 2 (significa que 1 y 2 ocurren mucho en A).&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0); font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/05/indices-cmo-y-cundo.html"&gt;Mis índices no funcionan!&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/11/indices-condicionales.html&lt;br /&gt;"&gt;Indices condicionales&lt;/a&gt;&lt;br /&gt;&lt;/valor&gt;&lt;/valor&gt;&lt;/valor&gt;&lt;/valor&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7210759851063859899?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7210759851063859899/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7210759851063859899' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7210759851063859899'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7210759851063859899'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/10/update-condicional.html' title='Update condicional'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-3356234165272848002</id><published>2007-09-26T20:56:00.000-07:00</published><updated>2007-09-29T14:41:15.250-07:00</updated><title type='text'>tic... toc</title><content type='html'>¿Cómo medir la performance de un código PL/SQL?&lt;br /&gt;&lt;br /&gt;Muchas veces nos encontramos con procedimientos lentos de muchos pasos, con llamadas a funciones, DML, loops con cursores, etc, que queremos revisar. Como SYSDATE nos da la cantidad de segundos como mínima fracción de tiempo, no nos es de mucha ayuda consultarlo entre líneas.&lt;br /&gt;&lt;br /&gt;Afortunadamente, contamos con la función &lt;span style="color: rgb(0, 0, 153);"&gt;get_time&lt;/span&gt; del paquete dbms_utility para obtener la cantidad de centésimas de segundo que transcurren entre dos instantes de tiempo.&lt;br /&gt;A sabiendas de eso, debemos incluir los statements que invoquen a dichas funciones y desplieguen los tiempos por la salida, en un archivo o en una tabla de logs.&lt;br /&gt;&lt;br /&gt;Con el fin de evitar duplicar código y además aprovechar las bondades del cacheado de paquetes en el área de memoria compartida, creé un pequeño paquete exclusivo para ese fin. El mismo tiene dos procedimientos: tic y toc. Cuando quiero comenzar a medir tiempo, invoco a tic (con un mensaje para registro). Cuando quiero medir el tiempo transcurrido, uso toc. Así de simple.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;CREATE OR REPLACE PACKAGE crono AS&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  &amp;nbsp;&amp;nbsp;v_tic PLS_INTEGER;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  &amp;nbsp;&amp;nbsp;evento VARCHAR2(100);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  &amp;nbsp;&amp;nbsp;PROCEDURE tic(p_evento IN VARCHAR2);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  &amp;nbsp;&amp;nbsp;PROCEDURE toc;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;END crono;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;/&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-size:85%;" &gt;&lt;span style="font-family:courier new;"&gt;CREATE OR REPLACE PACKAGE BODY crono AS&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &amp;nbsp;PROCEDURE tic(p_evento IN VARCHAR2) IS&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &amp;nbsp;BEGIN&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &amp;nbsp;&amp;nbsp;&amp;nbsp;v_tic := DBMS_UTILITY.GET_TIME;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &amp;nbsp;&amp;nbsp;&amp;nbsp;evento := p_evento;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &amp;nbsp;&amp;nbsp;&amp;nbsp;DBMS_OUTPUT.PUT_LINE('TIC: '||p_evento);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &amp;nbsp;END tic;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &amp;nbsp;PROCEDURE toc IS&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &amp;nbsp;&amp;nbsp;&amp;nbsp;df pls_integer := CAST(DBMS_UTILITY.GET_TIME AS PLS_INTEGER)-v_tic;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &amp;nbsp;BEGIN&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &amp;nbsp;&amp;nbsp;&amp;nbsp;DBMS_OUTPUT.PUT_LINE('TOC: '||evento||', T(1/100s): '||df);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &amp;nbsp;END toc;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;END crono;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;/&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;El uso del paquete en un procedimiento cualquiera puede ser:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-size:85%;" &gt;&lt;span style="font-family:courier new;"&gt;SQL&gt;   begin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  2       &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;crono.tic('creo tabla');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  3       &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;execute immediate 'CREATE TABLE testtable as select * from user_tables';&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  4       &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;crono.toc;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  5       &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;crono.tic('elimino registros');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  6       &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;execute immediate 'delete from testtable';&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  7       &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;crono.toc;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  8       &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;crono.tic('hago commit');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  9       &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;commit;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; 10       &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;crono.toc;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; 11       &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;crono.tic('borro tabla');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; 12       &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;execute immediate 'drop table testtable';&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; 13       &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;crono.toc;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; 14    end;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; 15  /&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;TIC: creo tabla&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;TOC: creo tabla, T(1/100s): 34&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;TIC: elimino registros&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;TOC: elimino registros, T(1/100s): 5&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;TIC: hago commit&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;TOC: hago commit, T(1/100s): 0&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;TIC: borro tabla&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;TOC: borro tabla, T(1/100s): 50&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Procedimiento PL/SQL terminado correctamente.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Aunque esta experiencia es meramente demostrativa, notar que no pasó un segundo en toda la operación, y sin embargo estamos sacando medidas ilustrativas en cada paso.&lt;br /&gt;&lt;br /&gt;Es una solución práctica y disponible para todos los usuarios de la base de datos, solo tengo que dar permisos de ejecución a los desarrolladores o a public. Un sinónimo puede simplificar aún más las llamadas a tic y a toc.&lt;br /&gt;&lt;br /&gt;Pese al mínimo costo de salida por pantalla, una desventaja es que el procedimiento tiene que terminar exitosamente para obtener las medidas (dbms_output despliega su salida al final del proceso). Una alternativa para lograr la medición "&lt;span style="font-style: italic;"&gt;on the run&lt;/span&gt;", es implementar la salida a archivo (utilizando el paquete utl_file), o insertando en una tabla (con una transacción autónoma para no hacer commit sobre la actual).&lt;br /&gt;Estos cambios pueden ser transparentes al usuario, ya que solo hará falta recompilar el body del paquete. Las ventajas están a la vista.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/09/un-extractor-de-ddl-en-archivos.html"&gt;Uso del paquete utl_file&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-3356234165272848002?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/3356234165272848002/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=3356234165272848002' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3356234165272848002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3356234165272848002'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/09/tic-toc.html' title='tic... toc'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7398921275509521847</id><published>2007-09-22T08:39:00.000-07:00</published><updated>2007-09-23T21:11:12.765-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqlplus'/><category scheme='http://www.blogger.com/atom/ns#' term='xe'/><title type='text'>El usuario Scott no existe</title><content type='html'>&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:85%;"  &gt;SQL&gt; conn scott/tiger&lt;br /&gt;ERROR:&lt;br /&gt;ORA-01017: invalid username/password; logon denied&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Dependiendo de las opciones de instalación o de los schemas que vengan instalados en algunas versiones del RDBMS (como Express por ejemplo), el usuario más famoso de Oracle, Scott, puede no estar presente. Citando el caso de Oracle XE, el único schema de ejemplo que viene instalado por defecto es HR.&lt;br /&gt;&lt;br /&gt;Pero no hay problema, lo único que tenemos que hacer para instalar el schema Scott con sus clásicas tablas EMP, DEPT, BONUS y SALGRADE, es correr (en SQL*Plus) el script &lt;span style="font-weight: bold;"&gt;utlsampl.sql&lt;/span&gt; que está en el directorio del servidor $ORACLE_HOME/RDBMS/ADMIN.&lt;br /&gt;&lt;br /&gt;Como se requiere de ciertos permisos especiales que un usuario corriente no tiene, deberá hacerlo el DBA.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7398921275509521847?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7398921275509521847/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7398921275509521847' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7398921275509521847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7398921275509521847'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/09/el-usuario-scott-no-existe.html' title='El usuario Scott no existe'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7667551215916231498</id><published>2007-09-10T16:35:00.000-07:00</published><updated>2008-03-31T15:25:07.568-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='awk'/><title type='text'>Limpiando código DDL</title><content type='html'>A propósito de &lt;a href="http://oraclenotepad.blogspot.com/2007/09/un-extractor-de-ddl-en-archivos.html"&gt;Un extractor de DDL en archivos separados&lt;/a&gt;, me preguntaron como evitaba exportar las cláusulas de storage en Oracle 9i, únicamente dejando las que especifican los tablespaces.&lt;br /&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-size:85%;" &gt;&lt;span style="font-family:courier new;"&gt;SQL&gt; create table t (a int);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Tabla creada.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;SQL&gt; select dbms_metadata.get_ddl('TABLE','T') from dual;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;DBMS_METADATA.GET_DDL('TABLE','T')&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--------------------------------------------------------------------------------&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;CREATE TABLE "LFERNANDEZ"."T"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;( "A" NUMBER(*,0)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;TABLESPACE "USERS"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En la práctica, las cláusulas PCTFREE, PCTUSED, STORAGE, etc, no son útiles durante el desarrollo, sino cuando los objetos son puestos en producción. Es allí donde el detalle en la especificación de storage de los objetos tiene un impacto significativo sobre la performance de los sistemas.&lt;br /&gt;&lt;br /&gt;Repasando las opciones disponibles en el paquete dbms_metadata y en particular la función &lt;span style="FONT-STYLE: italic"&gt;set_transform_param&lt;/span&gt;, encontramos que podemos generar el código de asignación de tablespace pasando el valor 'TABLESPACE' como segundo parámetro y TRUE como tercero:&lt;br /&gt;&lt;span style="font-family:Courier;"&gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;dbms_metadata.set_transform_param&lt;/span&gt;(&lt;/span&gt;&lt;span style="COLOR: rgb(0,0,0);font-family:courier new;" &gt;dbms_metadata.session_transform&lt;/span&gt;&lt;span style="font-family:Courier;"&gt;, '&lt;span style="COLOR: rgb(0,0,153)"&gt;TABLESPACE&lt;/span&gt;', &lt;span style="COLOR: rgb(0,0,153)"&gt;TRUE&lt;/span&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Desafortunadamente, esta opción es ignorada cuando deshabilitamos todo el STORAGE usando:&lt;br /&gt;&lt;span style="font-family:Courier;"&gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;dbms_metadata.set_transform_param&lt;/span&gt;(&lt;/span&gt;&lt;span style="COLOR: rgb(0,0,0);font-family:courier new;" &gt;dbms_metadata.session_transform&lt;/span&gt;&lt;span style="font-family:Courier;"&gt;, '&lt;span style="COLOR: rgb(0,0,153)"&gt;STORAGE&lt;/span&gt;', &lt;span style="COLOR: rgb(0,0,153)"&gt;FALSE&lt;/span&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;&lt;br /&gt;&lt;/span&gt;No tenemos alternativa entre: o generar todo el storage, o no generarlo en absoluto.&lt;br /&gt;&lt;br /&gt;El camino que termina siendo elegido es el de generar todo el código DDL y luego procesar el archivo de salida con algun lenguaje con buenas cualidades para procesamiento de texto, como por ejemplo Perl o AWK.&lt;br /&gt;&lt;br /&gt;No se requiere ser experto en AWK para programar un sencillo script que elimine algunas líneas de archivos:&lt;br /&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-size:85%;" &gt;&lt;span style="font-family:courier new;"&gt;BEGIN {flag=2}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;/^ *) PCTFREE/{print ")"; flag=0}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;/^ *PCTFREE/{flag=0}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;flag==0 &amp;amp;&amp;amp; $0~/)$/{flag=1}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;flag==2&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;flag==1{flag=2}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Esta es la idea: uso flags para saber cuándo imprimir y cuándo no, luego tengo dos posibles comienzos con PCTFREE, el primero para tablas comunes y el segundo para el caso de tablas particionadas (las cuales no son antecedidas por un paréntesis).&lt;br /&gt;&lt;br /&gt;Cualquier comentario para mejorar la performance de este script será bienvenido!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Ver también&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/09/un-extractor-de-ddl-en-archivos.html"&gt;Un extractor de DDL en archivos separados&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7667551215916231498?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7667551215916231498/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7667551215916231498' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7667551215916231498'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7667551215916231498'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/09/limpiando-cdigo-ddl.html' title='Limpiando código DDL'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8654975430636316437</id><published>2007-08-15T14:04:00.000-07:00</published><updated>2007-08-15T15:03:52.746-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NLS'/><category scheme='http://www.blogger.com/atom/ns#' term='estadísticas'/><title type='text'>ORA-00600 y recolección de estadísticas con ORA-00933</title><content type='html'>Esto puede ocurrir en 9iR2:&lt;br /&gt;Al ejecutar un select se produce el error inesperado&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ORA-00600: internal error code, arguments: [19004], [], [], [], [], [], [], []&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Esto puede solucionarse generando las estadísticas nuevamente, invocando el paquete dbms_stats.&lt;br /&gt;Al hacer eso, puede ocurrir lo siguiente al ejecutar el procedimiento dbms_stats.gather_table_stats (o gather_schema_stats):&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; exec dbms_stats.gather_table_stats(user,tabname =&gt; 'FACTURADOR', estimate_percent =&gt; 20, block_sample =&gt; false, method_opt =&gt; 'FOR TABLE FOR ALL COLUMNS SIZE 1', cascade =&gt; true);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ERROR at line 1:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ORA-00933: SQL command not properly ended&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ORA-06512: at "SYS.DBMS_STATS", line 9375&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ORA-06512: at "SYS.DBMS_STATS", line 9389&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ORA-06512: at line 1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Verificar que este error no se produce si invocamos el procedimiento con cascade =&gt; false.&lt;br /&gt;&lt;br /&gt;Esto esta relacionado con los parámetros de NLS como NLS_LANGUAGE, NLS_SORT y NLS_NUMERIC_CHARACTERS. Seguramente no tienen valores por defecto, los cuales se necesitan para la generación de histogramas.&lt;br /&gt;&lt;br /&gt;La vista NLS_SESSION_PARAMETERS nos muestra cuáles son los valores actuales que se están usando (recordar que éstos tienen prioridad sobre los parámetros de la base de datos).&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; select PARAMETER, VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER IN ('NLS_LANGUAGE', 'NLS_SORT', 'NLS_NUMERIC_CHARACTERS'); NLS_SESSION_PARAMETERS;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;PARAMETER VALUE&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;------------------------- --------------------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;NLS_LANGUAGE AMERICAN&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;NLS_NUMERIC_CHARACTERS .,&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;NLS_SORT BINARY&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Si alguno de estos parámetros no aparece como arriba, será necesario hacer un ALTER SESSION para arreglarlo y probar generar las estadísticas nuevamente con cascade =&gt; true.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8654975430636316437?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8654975430636316437/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8654975430636316437' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8654975430636316437'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8654975430636316437'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/08/recoleccin-de-estadisticas-falla-con.html' title='ORA-00600 y recolección de estadísticas con ORA-00933'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7076363305320917493</id><published>2007-08-08T14:09:00.000-07:00</published><updated>2007-09-22T14:36:38.810-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plsql'/><category scheme='http://www.blogger.com/atom/ns#' term='utl_file'/><category scheme='http://www.blogger.com/atom/ns#' term='dbms_metadata'/><title type='text'>Un extractor de DDL en archivos separados</title><content type='html'>¿Cómo podemos exportar todos los objetos de un esquema de Oracle en archivos separados para poder versionar el código?&lt;br /&gt;&lt;br /&gt;Algunos de los IDEs comerciales más populares de PL/SQL traen esta funcionalidad (lo pueden investigar), pero me parece una buena oportunidad para poder hacerlo sin ayuda de ninguna otra herramienta que... PL/SQL!&lt;br /&gt;&lt;br /&gt;Para poder cristalizar esto, ejecutaremos desde sql*plus bloques de PL/SQL que realicen lo siguiente:&lt;br /&gt;1) Extraer el DDL de los objetos - usaremos el paquete dbms_metadata para obtener el DDL de tablas, indices y constraints. También leeremos la tabla de diccionario user_source para capturar el código de procedimientos, funciones, paquetes y triggers.&lt;br /&gt;2) Escribir en archivos el código generado - invocaremos al paquete utl_file para crear archivos en el sistema operativo.&lt;br /&gt;&lt;br /&gt;Este procedimiento funciona en 9i, XE o 10g, tanto para Unix como para Windows. Los archivos quedarán alojados localmente en el servidor (el usuario necesitará los debidos permisos de acceso a estas carpetas).&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(102, 102, 102);font-size:85%;" &gt;Nota para usuarios Oracle XE:&lt;br /&gt;El paquete utl_file no vine compilado por defecto luego de la instalación. Es necesario ejecutar el siguiente script con el usuario sys:&lt;br /&gt;$ORACLE_HOME/RDBMS/ADMIN/utlfile.sql&lt;br /&gt;Luego deberán revisar con el usuario sys si quedaron paquetes, vistas o procedimientos mal compilados y en tal caso, recompilarlos.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Bueno, ahora sí vamos con los pasos necesarios:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Paso 1) &lt;/span&gt;Crear directorios en el servidor - El usuario de la base de datos necesita privilegios para crear directories de Oracle, de lo contrario deberá solicitar al DBA que lo haga por él o le indique alguno que pueda usar. De lo contrario no vamos a poder escribir con el paquete utl_file.&lt;br /&gt;&lt;br /&gt;Las carpetas deben existir en el sistema operativo y luego crearse los objetos directorios en Oracle, asociados a las mismas.&lt;br /&gt;Por ejemplo, luego de crear las carpetas en Unix, ejecutar lo siguiente en SQL*Plus:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0)" &gt;&lt;span style="font-family:courier new;"&gt;create directory tables as '/oracle/source/tables';&lt;br /&gt;create directory indexes as '/oracle/source/indexes';&lt;br /&gt;create directory procedures as '/oracle/source/procedures';&lt;br /&gt;create directory functions as '/oracle/source/functions';&lt;br /&gt;create directory packages as '/oracle/source/packages';&lt;br /&gt;create directory packagebodies as '/oracle/source/packagebodies';&lt;br /&gt;create directory triggers as '/oracle/source/triggers';&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;... etc&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Paso 2)&lt;/span&gt; Especificar la información a ser generada con dbms_metadata - La función set_transform_param nos permite especificar que detalle de código debe generarse.&lt;br /&gt;&lt;br /&gt;Ejecutar lo siguiente en SQL*Plus:&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0)" &gt;&lt;span style="font-family:courier new;"&gt;dbms_metadata.set_transform_param(dbms_metadata.session_transform,'CONSTRAINTS_AS_ALTER', TRUE ) ;&lt;br /&gt;dbms_metadata.set_transform_param(dbms_metadata.session_transform,'SQLTERMINATOR', TRUE ) ;&lt;br /&gt;dbms_metadata.set_transform_param(dbms_metadata.session_transform,'SEGMENT_ATTRIBUTES', FALSE) ;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Paso 3) &lt;/span&gt;Finalmente, la parte interesante... codificar el PL/SQL! :)&lt;br /&gt;&lt;br /&gt;Voy a mostrar dos ejemplos: el primero para capturar tablas y el segundo para capturar los procedimientos. El caso para capturar DDL de índices es completamente análogo al de tablas, y el caso de funciones, paquetes, body de paquetes, triggers es similar al de procedimientos.&lt;br /&gt;&lt;br /&gt;Ejecutar en SQL*Plus:&lt;br /&gt;&lt;br /&gt;1 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;DECLARE&lt;/span&gt;&lt;br /&gt;2 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;f UTL_FILE.FILE_TYPE;&lt;/span&gt;&lt;br /&gt;3 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;BEGIN&lt;/span&gt;&lt;br /&gt;4 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;FOR cr IN (SELECT table_name FROM USER_tables)&lt;/span&gt;&lt;br /&gt;5 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;LOOP&lt;/span&gt;&lt;br /&gt;6 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;f := UTL_FILE.FOPEN ('TABLES', cr.table_name || '.sql', 'w');&lt;/span&gt;&lt;br /&gt;7 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UTL_FILE.PUT_LINE (f, dbms_metadata.get_ddl('TABLE', cr.table_name));&lt;/span&gt;&lt;br /&gt;8 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UTL_FILE.FCLOSE (f);&lt;/span&gt;&lt;br /&gt;9 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;END LOOP;&lt;/span&gt;&lt;br /&gt;10 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;END;&lt;/span&gt;&lt;br /&gt;11 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Las líneas 2, 6, 7 y 8 muestran el uso del paquete utl_file en cuatro pasos básicos: se declara el manejador, se abre el archivo para escritura, se escribe y finalmente se cierra.&lt;br /&gt;&lt;br /&gt;La línea 4 selecciona todas las tablas de mi esquema.&lt;br /&gt;&lt;br /&gt;En cada iteración del FOR, abrimos un nuevo archivo (línea 6) con el nombre de la tabla, y escribimos en el directorio TABLES (creado en el paso 1) la salida del procedimiento dbms_metadata.get_ddl.&lt;br /&gt;&lt;br /&gt;Para capturar los índices es análogo, cambiando en la línea 7 el parámetro 'TABLE' por 'INDEX' y cambiando también si se desea el directorio destino en la línea 6.&lt;br /&gt;&lt;br /&gt;Ahora veremos el caso de escribir código de usuario leyendo la tabla user_source. Tenemos que tener algunos detalles en cuenta, ya que la tabla user_source contiene todo el código separado una línea por fila. Vamos a ir escribiendo el archivo de a una línea y tendremos que saber cuándo termina un procedimiento y comienza otro.&lt;br /&gt;&lt;br /&gt;1 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;DECLARE&lt;/span&gt;&lt;br /&gt;2 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;f UTL_FILE.FILE_TYPE;&lt;/span&gt;&lt;br /&gt;3 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;nombre VARCHAR2(4000) := NULL;&lt;/span&gt;&lt;br /&gt;4 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;BEGIN&lt;/span&gt;&lt;br /&gt;5 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;FOR cr IN (SELECT name, text FROM USER_SOURCE WHERE type='PROCEDURE' order by name, line)&lt;/span&gt;&lt;br /&gt;6 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;LOOP&lt;/span&gt;&lt;br /&gt;7 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IF (nombre IS NULL OR cr.name &lt;&gt; nombre) THEN&lt;/span&gt;&lt;br /&gt;8 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IF (nombre IS NOT NULL) THEN&lt;/span&gt;&lt;br /&gt;9 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UTL_FILE.FCLOSE (f);&lt;/span&gt;&lt;br /&gt;10 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;END IF;&lt;/span&gt;&lt;br /&gt;11 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;f := UTL_FILE.FOPEN ('TABLES', cr.name || '.sql', 'w');&lt;/span&gt;&lt;br /&gt;12 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UTL_FILE.PUT (f, 'CREATE OR REPLACE ');&lt;/span&gt;&lt;br /&gt;13 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;END IF;&lt;/span&gt;&lt;br /&gt;14 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nombre := cr.name;&lt;/span&gt;&lt;br /&gt;15 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UTL_FILE.PUT (f, cr.text);&lt;/span&gt;&lt;br /&gt;16 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;END LOOP;&lt;/span&gt;&lt;br /&gt;17 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;IF UTL_FILE.IS_OPEN(f) THEN&lt;/span&gt;&lt;br /&gt;18 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UTL_FILE.FCLOSE (f);&lt;/span&gt;&lt;br /&gt;19 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;END IF;&lt;/span&gt;&lt;br /&gt;20 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;END;&lt;/span&gt;&lt;br /&gt;21 &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En la línea 3 declaramos una variable para leer cada línea.&lt;br /&gt;&lt;br /&gt;En la línea 5 seleccionamos el tipo de objeto que queremos capturar. De la misma forma lo haremos para triggers, funciones, paquetes, etc.&lt;br /&gt;&lt;br /&gt;Las líneas 7 y 8 tratan la apertura y cierre del archivo cuando se termina un procedimiento; al ir leyendo línea a línea de todos los objetos, necesito saber cuando comienza y cuando termina un procedimiento para podeer abrir un nuevo archivo de escritura.&lt;br /&gt;&lt;br /&gt;En la línea 12 le agrego 'CREATE OR REPLACE' ya que la tabla user_source no almacena esta directiva.&lt;br /&gt;&lt;br /&gt;Observar en las líneas 12 y 15 que utilizamos la función PUT en lugar de PUT_LINE. Esto es porque las líneas de user_source ya almacenan un retorno de carro por lo cual no es necesario que use PUT_LINE, de lo contrario voy a escribir un renglón por medio.&lt;br /&gt;&lt;br /&gt;Con estos ejemplos es posible capturar todo el código de nuestro esquema y almacenar cada objeto en un archivo diferente. Traté de simplificar el código a costa de sacrificar algunas buenas prácticas, pero en este caso no quise complicar la visibilidad de la idea por lo cual cada uno podrá mejorar su propia implementación.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7076363305320917493?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7076363305320917493/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7076363305320917493' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7076363305320917493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7076363305320917493'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/09/un-extractor-de-ddl-en-archivos.html' title='Un extractor de DDL en archivos separados'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-113569780748710392</id><published>2007-07-27T17:41:00.000-07:00</published><updated>2007-07-29T17:40:20.372-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqlloader'/><title type='text'>SQL*loader: Field in data file exceeds maximum length</title><content type='html'>Recientemente tuve la oportunidad de ver un problema en un aplicativo que utilizaba SQL*loader 9.2.0.6 para cargar datos de un archivo plano en una tabla, originalmente separados por pipes y eventualmente conteniendo espacios en uno de sus campos.&lt;br /&gt;&lt;br /&gt;Un ejemplo del contenido del archivo de datos (los pipes van en color para identificar mejor los campos):&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;A1&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;3&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;02/12/98&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;NN&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;A Suspender      &lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;34&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;A2&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;2&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;02/12/98&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;NS&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;No se presentó, fue enviado nuevo aviso &amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  (**)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;27&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;A3&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;3&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;02/14/98&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;NS&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;|&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;9&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;(**)  La segunda línea de datos contiene luego del texto y antes del siguiente pipe, más de 300 espacios.&lt;br /&gt;&lt;br /&gt;El control file utilizado para cargar ese archivo tenía esta forma:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-size:100%;" &gt;&lt;span style="font-family:courier new;"&gt;LOAD DATA&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;APPEND&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;INTO TABLE TMP_SUSPENSION&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;FIELDS TERMINATED BY '|'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;TRAILING NULLCOLS&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;(&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div  style="color: rgb(0, 153, 0);font-family:courier new;" id="mb_0"&gt;&lt;span style="font-size:100%;"&gt;  SEGMENTO,&lt;br /&gt;SUBSEGMENTO,&lt;br /&gt;FECHA_VENCIMIENTO  DATE  "MM/DD/RRRR",&lt;br /&gt;ESTADO_FINANCIERO,&lt;br /&gt;DESCRIPCION  "TRIM(:DESCRIPCION)",&lt;br /&gt;CODIGO_CATEGORIA&lt;br /&gt;)&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Al campo descripcion se le había agregado la función TRIM para eliminar los espacios sobrantes, sin embargo, cuando se ejecutaba SQL*loader la carga fallaba y parecía ignorar la función:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Record 2: Rejected - Error on table TMP_SUSPENSION, column DESCRIPCION.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Field in data file exceeds maximum length&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Table TMP_SUSPENSION: &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  2 Rows successfully loaded.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  1 Row not loaded due to data errors.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  0 Rows not loaded because all WHEN clauses were failed.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;  0 Rows not loaded because all fields were null.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;La causa del problema no era que la función TRIM no estaba cortando los caracteres ni que el campo en la tabla no tuviera el largo suficiente, sino la mala práctica de no especificar tipos en los campos del control file. Recordemos que los campos que se definen allí no corresponden a la tabla destino sino a lo que se espera encontrar en el archivo fuente. Cuando no se especifica tipo y largo en un campo del control file, Oracle asume el largo por defecto de 255 caracteres. Por lo tanto, al encontrar mas de 255 caracteres entre dos pipes, se rechaza el mismo antes siquiera de aplicar la función TRIM. De la forma que opera SQL*loader, primero se validan los tipos en el archivo, y luego se aplican las funciones sobre los campos identificados.&lt;br /&gt;&lt;br /&gt;Como solución, se arregló el control file para que tuviera el tipo especificado de un largo mayor al previsto. Esta simple buena práctica es muy recomendada para SQL*loader.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-size:100%;" &gt;&lt;span style="font-family:courier new;"&gt;LOAD DATA&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;APPEND&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;INTO TABLE TMP_SUSPENSION&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;FIELDS TERMINATED BY '|'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;TRAILING NULLCOLS&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;(&lt;/span&gt;&lt;br /&gt;&lt;/span&gt; &lt;div  style="color: rgb(0, 153, 0);font-family:courier new;" id="mb_0"&gt;&lt;span style="font-size:100%;"&gt;  SEGMENTO,&lt;br /&gt;SUBSEGMENTO,&lt;br /&gt;FECHA_VENCIMIENTO  DATE  "MM/DD/RRRR",&lt;br /&gt;ESTADO_FINANCIERO,&lt;br /&gt;DESCRIPCION  &lt;span style="color: rgb(51, 51, 255);"&gt;CHAR(600)&lt;/span&gt; "TRIM(:DESCRIPCION)",&lt;br /&gt;CODIGO_CATEGORIA&lt;br /&gt;)&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-113569780748710392?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/113569780748710392/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=113569780748710392' title='4 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/113569780748710392'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/113569780748710392'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/07/sqlloader-field-in-data-file-exceeds.html' title='SQL*loader: Field in data file exceeds maximum length'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-4280678299666265143</id><published>2007-07-22T16:34:00.000-07:00</published><updated>2008-03-31T15:20:46.049-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='estadísticas'/><title type='text'>Sobre estimar estadísticas</title><content type='html'>¿Que estadísticas conviene tomar? ¿La tabla completa o un porcentaje? ¿Qué valor uso?&lt;br /&gt;&lt;br /&gt;Esta pregunta es relevante para tablas muy grandes o esquemas populosos donde el tiempo de extracción debe ser optimizado. Obviamente tomar las estadísticas completas es lo mejor por precisión, y en esquemas chicos nos puede llevar algunos pocos minutos, pero algunas veces tenemos ciertos requerimientos de performance o de utilización de recursos ya que es una operación que realiza mucha lectura física (I/O).&lt;br /&gt;&lt;br /&gt;Para tablas y esquemas grandes yo suelo tomar un 20 por ciento para el estimado, con lo cual logro mis estadísticas de 'buena calidad'.&lt;br /&gt;&lt;br /&gt;Oracle recomienda utilizar el paquete dbms_stats para tomar estadísticas de tablas, índices, esquemas o de la base de datos completa. Extrae más información que el comando ANALIZE además de otras características que podemos aprovechar como el procesamiento en paralelo.&lt;br /&gt;&lt;br /&gt;Se puede invocar de la siguiente manera:&lt;br /&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;SQL&gt; exec dbms_stats.gather_table_stats(ownname =&gt; user, tabname =&gt; 'OUT_TAB',estimate_percent =&gt; 20, method_opt =&gt; 'FOR TABLE FOR ALL COLUMNS FOR ALL INDEXES', cascade =&gt; true );&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Algo interesante de este paquete es que nos permite que Oracle calcule un valor adecuado para el porcentaje estimado, y esto se hace poniendo &lt;span style="COLOR: rgb(0,0,153)"&gt;dbms_stats.auto_sample_size&lt;/span&gt; en &lt;span style="FONT-WEIGHT: bold"&gt;estimate_percent&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Si hacemos la prueba podemos constatar observando la tabla user_tables que Oracle toma las estadísticas al 100 por ciento hasta cierta cantidad de filas que depende de cada base de datos.&lt;br /&gt;&lt;br /&gt;A tales efectos puede utilizarse una consulta como ésta:&lt;br /&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;SELECT table_name, num_rows, sample_size, TRUNC(sample_size*100/DECODE(num_rows,0,1,num_rows),2) porciento &lt;/span&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(0,153,0);font-family:courier new;" &gt;FROM user_tables;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A modo de ejemplo, en una base 9iR2 sobre un esquema de desarrollo con estadísticas estimadas al 20 por ciento, constaté que hasta las tablas con 12500 filas Oracle tomaba el 100 por ciento de las filas como muestra, y luego a partir de ahí tomaba el 20 por ciento que yo le había solicitado.&lt;br /&gt;&lt;br /&gt;También se puede comprobar que usando dbms_stats.auto_sample_size para ciertos casos, Oracle calcula la tabla entera sin importar la cantidad de tuplas.&lt;br /&gt;Esto se debe a que Oracle utiliza un algoritmo para obtener un número adecuado: parte de cierta cantidad de tuplas y en base a ellas analiza si la calidad de las mismas es buena, si no lo es toma un nuevo porcentaje y así sucesivamente hasta llegar a un 25 por ciento, luego del cual decide computar la tabla entera.&lt;br /&gt;Cuando esto sucede, los requerimientos de performance o recursos utilizados sufren; por este motivo en esos casos es mejor especificar el valor fijo.&lt;br /&gt;&lt;br /&gt;&lt;span style="FONT-WEIGHT: bold"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/08/recoleccin-de-estadisticas-falla-con.html"&gt;Falla de estadísticas con ORA-00933&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/03/error-ora-942-while-gathering.html"&gt;Error ORA-942 while gathering statistics&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-4280678299666265143?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/4280678299666265143/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=4280678299666265143' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4280678299666265143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4280678299666265143'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/07/estimando-estadsticas.html' title='Sobre estimar estadísticas'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-1828768602156325434</id><published>2007-07-19T01:00:00.000-07:00</published><updated>2007-11-12T19:57:53.395-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqlplus'/><title type='text'>Variables en SQL*Plus (parte 2)</title><content type='html'>Hay tres tipos de variables en SQL*Plus y suelen causar confusión.&lt;br /&gt;Estas son: variables de usuario, variables de sustitución y bind variables.&lt;br /&gt;En la primera parte repasamos las variables de usuario, aquí van las variables de sustitución.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153); font-weight: bold;"&gt;VARIABLES DE SUSTITUCION&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Las variables de sustitución suelen confundirse con las variables de usuario, más que nada porque ambas aparecen precedidas con un símbolo ampersand (&amp;amp;). Sin embargo, hay diferencias en la definición, asignación y uso de las mismas. Como vimos en la &lt;a href="http://oraclenotepad.blogspot.com/2007/06/variables-en-sqlplus-parte-1.html"&gt;parte 1&lt;/a&gt;,  las variables de usuario se declaran explícitamente con el comando DEFINE, tienen asociado un tipo y se les pueden asignar valores en el script como lo hacemos en cualquier lenguaje de programación.&lt;br /&gt;Las variables de sustitución en cambio cuando están indefinidas, solicitan el valor al usuario cada vez que aparecen, y el resultado es exactamente el mismo que si escribiéramos el texto directamente.&lt;br /&gt;&lt;br /&gt;Por ejemplo: en la siguiente consulta en cada aparición de variable será solicitado el valor al usuario. Notar que aunque la variable COLUMNA aparece repetida, se trata como si fueran diferentes y el valor es solicitado dos veces.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SELECT &amp;amp;COLUMNA, SUM(MONTO)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;FROM cuentas&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;GROUP BY &amp;COLUMNA;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Enter value for columna: ID_&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;REGION&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;old   1: SELECT   &amp;amp;COLUMNA,&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;new   1: SELECT   ID_REGION,&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Enter value for columna: ID_REGION&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;old   1: GROUP BY   &amp;amp;COLUMNA&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;new   1: GROUP BY   ID_REGION&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Mientras que el símbolo ampersand (&amp;amp;) declara una variable sustitución temporal (vale una única vez), dos ampersand consecutivos (&amp;amp;&amp;amp;) definen una variable permanente evitando que el usuario deba ingresar nuevamente su valor.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Scripts parametrizados&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Las variables de sustitución se utilizan típicamente para pasar parámetros a un script a través del comando START. Dentro del script las variables deben enumerarse secuencialmente &amp;amp;1, &amp;amp;2, etc, y éstas serán asignadas en orden con los parámetros pasados en la llamada.&lt;br /&gt;&lt;br /&gt;Ejemplo: Un script al que le pasamos dos parámetros, un nombre de columna y la tabla&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt; programa.sql&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);font-family:courier new;" &gt;SET VERIFY OFF&lt;br /&gt;SELECT &amp;amp;&amp;amp;1, SUM(MONTO)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);font-family:courier new;" &gt;FROM &amp;amp;2&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;GROUP BY &amp;1;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;La llamada se realiza de la siguiente manera:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;SQL&gt; START programa.sql  &lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;ID_&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;REGION &lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;cuentas&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;La variable 1 tiene dos ampersand por lo que queda definida a la primera vez que aparece.&lt;/li&gt;&lt;li&gt;SET VERIFY OFF elimina el feedback de old y new values.&lt;/li&gt;&lt;li&gt;Las variables de sustitución 1 y 2, al igual que las variables de usuario también se pueden borrar de memoria, por ejemplo incluyendo la línea UNDEFINE 1 en el script.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/06/variables-en-sqlplus-parte-1.html"&gt;Variables en SQLPlus (parte 1)&lt;/a&gt; - Sobre variables de usuario&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/11/variables-en-sqlplus-parte-3.html"&gt;Variables en SQLPlus (parte 3)&lt;/a&gt; - Sobre variables bind&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-1828768602156325434?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/1828768602156325434/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=1828768602156325434' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1828768602156325434'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1828768602156325434'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/07/sqlplus-variables-parte-2.html' title='Variables en SQL*Plus (parte 2)'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7528618812924509335</id><published>2007-07-14T09:37:00.001-07:00</published><updated>2007-07-22T17:47:08.301-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='11g'/><title type='text'>Announcing Oracle Database 11g</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;p&gt;&lt;object height="350" width="425"&gt;&lt;param value="http://youtube.com/v/OiZKUx4qlx8" name="movie"&gt;&lt;embed type="application/x-shockwave-flash" src="http://youtube.com/v/OiZKUx4qlx8" height="350" width="425"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;&lt;p&gt;Institucional de la 11g lanzada el pasado 11 de julio en NY.&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7528618812924509335?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7528618812924509335/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7528618812924509335' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7528618812924509335'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7528618812924509335'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/07/announcing-oracle-database-11g.html' title='Announcing Oracle Database 11g'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-1666828735881562218</id><published>2007-07-09T05:56:00.000-07:00</published><updated>2009-01-14T04:18:19.643-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NLS'/><title type='text'>Cuestiones de idioma</title><content type='html'>Muchas veces necesitamos comparar días de la semana con SQL y usamos funciones como to_char, to_date, next_day, y otras. Con ellas podemos obtener el número de día de la semana o el nombre del día o del mes, y usarlo para evaluar alguna condición.&lt;br /&gt;En estos casos deberíamos preguntarnos si nuestro código depende del idioma en que esté configurado el cliente o la base de datos. Si esto es así, estamos haciendo que nuestras aplicaciones sean dependientes del lugar donde se instalen y eso no es deseable.&lt;br /&gt;&lt;br /&gt;Recordando que el orden de preferencia para los seteos del NLS es (de mayor a menor):&lt;br /&gt;&lt;br /&gt;1) NLS explicitado en función&lt;br /&gt;2) NLS configurado en la sesión del usuario&lt;br /&gt;3) NLS configurado en el cliente&lt;br /&gt;4) NLS configurado en la base de datos&lt;br /&gt;&lt;br /&gt;...es una buena práctica 'asegurar' el idioma o territorio a utilizar cuando usamos una función que depende de eso.&lt;br /&gt;Yo prefiero que mis aplicaciones dependan lo menos posible de una configuración especial en el cliente o la base de datos, por lo tanto siempre voy a aplicar 1) o 2).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ejemplo 1: &lt;/span&gt;Días de la semana como palabra&lt;br /&gt;&lt;br /&gt;Obtener las marcas de los empleados los días sábados:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SELECT timestamp, employee&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;FROM timer_tbl&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;WHERE to_char(timestamp,'DAY') = 'SATURDAY';&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En este caso la consulta depende del idioma que este configurado en la sesión o en el cliente o la base de datos, y rezaría para que siempre fuera inglés!&lt;br /&gt;Para evitar ese tipo de ligaduras con la región, puedo pasarle a to_char el parámetro NLS_DATE_LANGUAGE y entonces estoy en el caso 1) de máxima prioridad.&lt;br /&gt;Me quedaría:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SELECT timestamp, employee&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;FROM timer_tbl&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;WHERE to_char(timestamp,'DAY','NLS_DATE_LANGUAGE=AMERICAN') = 'SATURDAY';&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ejemplo 2:&lt;/span&gt; Número de día de la semana&lt;br /&gt;&lt;br /&gt;En algunos territorios como Portugal y USA el primer día de la semana es el domingo, en otros como España y América del Sur es el lunes. Esto hace que la máscara 'D' de to_char sea diferente según el territorio.&lt;br /&gt;El número de dia de la semana depende del parámetro NLS_TERRITORY, pero no lo puedo pasar como tercer parámetro, no es válido.&lt;br /&gt;&lt;br /&gt;En ese caso tendré que alterar la sesión para asegurar que el día 1 sea el lunes para mi aplicación.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;alter session set nls_territory='AMERICA';&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; select to_char(sysdate,'D DAY') HOY from dual;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;HOY&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;-----------------------------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;1 MONDAY&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/10/acentos-del-idioma-espaol.html"&gt;Acentos del idioma español&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-1666828735881562218?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/1666828735881562218/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=1666828735881562218' title='7 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1666828735881562218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/1666828735881562218'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/07/las-funciones-de-fecha.html' title='Cuestiones de idioma'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8980155252507604556</id><published>2007-07-05T17:16:00.000-07:00</published><updated>2007-07-29T17:43:38.659-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><title type='text'>INNER/OUTER JOINS (ANSI)</title><content type='html'>La pregunta del día es:&lt;br /&gt;&lt;span style="font-style: italic;"&gt;¿Usando la notación ANSI para joins, es lo mismo agregar condiciónes adicionales al where que en la cláusula ON?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Si no se entendió la pregunta, pongamos un ejemplo...&lt;br /&gt;&lt;br /&gt;¿Son estas dos consultas equivalentes?&lt;br /&gt;&lt;pre class="CE"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;SELECT d.departamento_id, e.nombre&lt;/span&gt;&lt;br /&gt;&lt;a style="color: rgb(0, 153, 0);" name="2091499"&gt;&lt;/a&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  FROM (departamento d INNER JOIN empleado e&lt;/span&gt;&lt;br /&gt;&lt;a style="color: rgb(0, 153, 0);" name="2091500"&gt;&lt;/a&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;    ON d.departamento_id = e.departamento_id)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;WHERE d.seccion_id = e.seccion;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;SELECT d.departamento_id, e.nombre&lt;/span&gt;&lt;br /&gt;&lt;a style="color: rgb(0, 153, 0);" name="2091499"&gt;&lt;/a&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  FROM (departamento d INNER JOIN empleado e&lt;/span&gt;&lt;br /&gt;&lt;a style="color: rgb(0, 153, 0);" name="2091500"&gt;&lt;/a&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;    ON d.departamento_id = e.departamento_id&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  AND d.seccion_id = e.seccion);&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Pensemos esto, si usáramos el viejo estilo de join (la sintaxis ANSI apareció en 9i), tendríamos todas las condiciones juntas, sin orden específico ni marca especial del tipo "esta condición es de join".&lt;br /&gt;Sucede que luego que Oracle parsea la consulta, analiza todas las condiciones que haya encontrado y según lo que tiene sobre la mesa decide cuál aplicar primero. El hecho de que nosotros incluyamos una condición en el ON o en el where, no va a ser un factor decisivo sino una diferencia &lt;span style="font-style: italic;"&gt;documental&lt;/span&gt; que aporta claridad. Hagamos sino la prueba de dejar una de las cláusulas de un join compuesto fuera del ON, y colocarla en el where.... el resultado va a ser el mismo!&lt;br /&gt;&lt;br /&gt;Si alguien quiere pruebas experimentales concretas podrá comparar los planes de ejecución y notar que internamente es exactamente lo mismo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8980155252507604556?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8980155252507604556/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8980155252507604556' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8980155252507604556'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8980155252507604556'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/07/innerouter-joins-ansi.html' title='INNER/OUTER JOINS (ANSI)'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8086192908426835623</id><published>2007-06-28T15:59:00.000-07:00</published><updated>2007-11-12T19:56:50.760-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqlplus'/><title type='text'>Variables en SQL*Plus (parte 1)</title><content type='html'>Hay tres tipos de variables en SQL*Plus y suelen causar confusión.&lt;br /&gt;Estas son: variables de usuario, variables de sustitución y bind variables.&lt;br /&gt;&lt;br /&gt;Aquí va la primera de ellas, más adelante revisaremos las otras.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;VARIABLES DE USUARIO&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Se definen de la siguiente forma:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; DEFINE u_nombre = ANDREA&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Para confirmar que haya quedado el valor guardado:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; DEFINE u_nombre&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;br /&gt;DEFINE U_VAR1        = "ANDREA" (CHAR)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;El comando DEFINE nos permite definir una cadena que se sustituye textualmente en cualquier lugar de nuestro script. Aunque quede definida como CHAR realmente no importa el tipo ya que la sustitución se realiza antes de ejecutar el código y además no incluye comillas. Por estas razones podemos jugar a 'construir' el script con nuestras variables de sustitución.&lt;br /&gt;Si queremos usar la entrada como varchar le agregamos las comillas nosotros, si queremos usarla como número no las agregamos. Para recuperar el contenido de la variable usamos el símbolo ampersand.&lt;br /&gt;&lt;br /&gt;Por ejemplo:         &lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SELECT * FROM empleado WHERE nombre = '&amp;amp;u_var1';&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ahora definimos una variable con un número...&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; define u_edad = 25&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;br /&gt;SQL&gt; define u_edad&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;DEFINE U_EDAD          = "25" (CHAR)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;...y la usamos de la misma forma pero sin incluir las comillas:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SELECT * FROM empleado WHERE edad = &amp;amp;u_edad;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Las variables de usuario son útiles cuando se define su valor al comienzo de un script y luego se utiliza varias veces en el código.&lt;br /&gt;&lt;br /&gt;No solamente podemos sustituir valores de columnas, sino también nombres de columnas o de tablas, incluso fragmentos de código, permitiéndonos hacer scripts más dinámicos.&lt;br /&gt;&lt;br /&gt;Un uso interesante es 'aceptar' el nombre de una columna y luego usarla para parametrizar el ORDER BY de un reporte.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/07/sqlplus-variables-parte-2.html"&gt;Variables en SQLPlus (parte 2)&lt;/a&gt; - Sobre variables de sustitución&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/11/variables-en-sqlplus-parte-3.html"&gt;Variables en SQLPlus (parte 3)&lt;/a&gt; - Sobre variables bind&lt;br /&gt;&lt;variable&gt;&lt;/variable&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8086192908426835623?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8086192908426835623/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8086192908426835623' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8086192908426835623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8086192908426835623'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/06/variables-en-sqlplus-parte-1.html' title='Variables en SQL*Plus (parte 1)'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-5845232377009424217</id><published>2007-06-27T11:24:00.001-07:00</published><updated>2007-07-08T13:40:58.692-07:00</updated><title type='text'>Webstats</title><content type='html'>La gente de &lt;a href="http://motigo.com/" target="_blank"&gt;MOTIGO Webstats&lt;/a&gt; toma las estadísticas de este blog, pero estoy comenzando a sospechar que sus números no son muy confiables...&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_fRmrOAuwnQ4/RoKtpsLe-BI/AAAAAAAAABo/FU2sHtMB1eI/s1600-h/STATS3.JPG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_fRmrOAuwnQ4/RoKtpsLe-BI/AAAAAAAAABo/FU2sHtMB1eI/s320/STATS3.JPG" alt="" id="BLOGGER_PHOTO_ID_5080814261372909586" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-5845232377009424217?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/5845232377009424217/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=5845232377009424217' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/5845232377009424217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/5845232377009424217'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/06/webstats.html' title='Webstats'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_fRmrOAuwnQ4/RoKtpsLe-BI/AAAAAAAAABo/FU2sHtMB1eI/s72-c/STATS3.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7524546646799385418</id><published>2007-06-27T08:32:00.000-07:00</published><updated>2007-07-08T13:54:50.882-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xe'/><category scheme='http://www.blogger.com/atom/ns#' term='9i'/><category scheme='http://www.blogger.com/atom/ns#' term='10g'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>For Export/Import</title><content type='html'>La base de datos Oracle XE es cada vez más popular en los hogares de los programadores que en sus trabajos utilizan la 9i.&lt;br /&gt;Estos exportan un schema versión 9i y lo llevan a su flamante XE portátil. Hasta aquí no hay mayores dificultades, la instalación y configuración de XE se realiza sola, y basta leer el manual Database Utilities para aprender a utilizar exp e imp.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Un archivo dump de 9i se puede importar en 10g sin especificar ningún parámetro extra.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;El asunto es que para migrar de la casa al trabajo con el programa &lt;span style="font-weight: bold;"&gt;exp &lt;/span&gt;tendremos un problema: el archivo dump generado por XE (10g) no se puede importar en 9i, no es compatible.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);font-size:85%;" &gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Import: Release 9.2.0.1.0 - Production on Thu Jun 28 18:31:04 2007&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;IMP-00010: not a valid export file, header failed verification&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;IMP-00000: Import terminated unsuccessfully&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;La solución es exportar desde XE con un &lt;span style="font-weight: bold;"&gt;exp&lt;/span&gt; 9i. El imp y el exp deben tener la misma versión cuando exportamos algo que es mas nuevo que la base destino.&lt;br /&gt;&lt;br /&gt;No es necesario que instalemos una base de datos 9i en casa, alcanza simplemente con instalar el cliente el cual se puede descargar gratuitamente desde:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.oracle.com/technology/software/products/oracle9i/htdocs/winsoft.html" target="_blank"&gt;http://www.oracle.com/technology/software/products/oracle9i/htdocs/winsoft.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Luego de instalar el cliente 9i, hay que tener en cuenta que éste utiliza su propio tnsnames.ora, por lo que vamos a tener que anexarle el servicio de nuestra base de datos XE. Finalmente ejecutaremos el export del cliente (por ejemplo: c:\oracle\ora92\bin\exp.exe) y exportaremos un archivo dump versión 9i que podrá ser importado sin problemas por otra 9i.&lt;br /&gt;&lt;br /&gt;Un detalle: tendremos que ser cuidadosos al ejecutar programas como sql*plus, exp, imp. Cada uno de ellos referirán a sus respectivos tnsnames.ora donde están registradas nuestras bases de datos. Como al iniciarse siempre muestran la versión, sabremos cual tnsnames.ora están leyendo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7524546646799385418?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/7524546646799385418/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=7524546646799385418' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7524546646799385418'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7524546646799385418'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/06/for-exportimport.html' title='For Export/Import'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-84203494133301423</id><published>2007-05-23T10:43:00.000-07:00</published><updated>2007-06-28T15:10:31.939-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dblink'/><category scheme='http://www.blogger.com/atom/ns#' term='9i'/><category scheme='http://www.blogger.com/atom/ns#' term='10g'/><title type='text'>Problemas de TNS utilizando DB links</title><content type='html'>Al definir un DB link y utilizarlo en una consulta, puede obtenerse el error:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-size:85%;" &gt;&lt;span style="font-family:courier new;"&gt;ORA-12154: TNS:could not resolve service name&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Nada parece estar mal:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Creamos el link utilizando el service name del tnsnames.ora&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Verificamos que la entrada del servicio está justamente definida en el archivo&lt;/li&gt;&lt;li&gt;Revisamos que nuestra aplicación esté leyendo el tnsnames.ora correcto (en caso de que tengamos muchos)&lt;/li&gt;&lt;li&gt;El usuario y password del link funciona OK si me conecto por SQL*plus&lt;/li&gt;&lt;li&gt;Tengo el privilegio CREATE DATABASE LINK&lt;/li&gt;&lt;li&gt;Al crear el link me retorna que el link fue creado exitosamente&lt;/li&gt;&lt;/ul&gt;Al utilizar el link en un select, se obtiene el error mencionado.&lt;br /&gt;La razón por la cual no encuentra el conector aún la desconozco, pero la forma de evitar el problema es:&lt;br /&gt;&lt;br /&gt;En lugar de definir el link de esta forma:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;create public database link uat1prod_lnk&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;connect to oramain&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;identified by "0racl3"&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;using 'produat1db';&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;donde 'produat1db' es el nombre del service name para ese conector (tnsnames.ora)&lt;br /&gt;&lt;br /&gt;Definirlo así:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;create public database link uat1prod_lnk&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;connect to oramain&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;identified by "0racl3"&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;using '(description=(address=(protocol=TCP)&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;br /&gt;(host=192.168.3.32)(port=1521))(connect_data=(sid=uat1db)))';&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;donde 'uat1db' es el nombre del SID de la base de datos.&lt;br /&gt;&lt;br /&gt;Crearlo de esta manera corrige el problema. Otra ventaja adicional que me proporciona esta sintaxis, es independencia del tnsnames.ora.&lt;br /&gt;Lo único que necesito es: IP, puerto y SID de la base de datos.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-84203494133301423?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/84203494133301423/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=84203494133301423' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/84203494133301423'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/84203494133301423'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/05/problemas-de-tns-utilizando-db-links.html' title='Problemas de TNS utilizando DB links'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-6243830179458139672</id><published>2007-05-18T15:16:00.000-07:00</published><updated>2007-06-28T15:11:29.507-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plsql'/><title type='text'>PL/SQL haragán</title><content type='html'>¿Es PL/SQL 'lazy' para las comparaciones?&lt;br /&gt;Es decir, para evaluar (expr_a AND expr_b): si expr_a es falso entonces ¿se evalúa expr_b?&lt;br /&gt;Asimismo en (expr_a OR expr_b): si expr_a es verdadero entonces ¿se evalúa expr_b?&lt;br /&gt;&lt;br /&gt;Opción 1 -&gt; consultar la documentación&lt;br /&gt;&lt;br /&gt;Opción 2 -&gt; ¡probarlo!&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt; SQL&gt; create table t (a int);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;Table created.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;SQL&gt; create or replace function fA return boolean is&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;2 begin&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;3 insert into t values (1);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;4 return (false);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;5 end;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;6 /&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;Function created.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;SQL&gt; begin&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;2 if ((1=0) and fA) = true then&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;3 NULL;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;4 end if ;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;5 end;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;6 /&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;PL/SQL procedure successfully completed.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;SQL&gt; select * from t;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;no rows selected&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;Conclusión:  PL/SQL es lazy. No se insertó un 1 en la tabla ya que la función fA nunca se ejecutó.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;¿Y en SQL? ¿Son las condiciones 'lazy' en las consultas?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;Lo podemos probar con otra función similar:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;SQL&gt;&lt;br /&gt;create or replace function fB return number is&lt;br /&gt;2  begin&lt;br /&gt;3  insert into t values (0);&lt;br /&gt;4  return (0);&lt;br /&gt;5  end;&lt;br /&gt;6  /&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;En SQL no podría ejecutar esta función, ya que realiza una operación DML:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;SQL&gt; select * from dual where ((0 = 0) and (fB = 0));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-14551: cannot perform a DML operation inside a query&lt;br /&gt;ORA-06512: at "EMP01.FB", line 3&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;Sin embargo, si cambiamos AND por OR, notamos que la función fB ni siquiera es evaluada:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:100%;"  &gt;&lt;br /&gt;SQL&gt; select * from dual where ((0 = 0) or (fB = 0));&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;D&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;-&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;X&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;1 row selected.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;Conclusión:  SQL también es lazy. Si se hubiera evaluado la segunda expresión, entonces tendría que haber retornado el error ORA-14551.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-6243830179458139672?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/6243830179458139672/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=6243830179458139672' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6243830179458139672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/6243830179458139672'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/05/plsql-haragn.html' title='PL/SQL haragán'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-5903495972191109535</id><published>2007-05-13T15:49:00.000-07:00</published><updated>2008-03-10T18:39:23.035-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Error'/><category scheme='http://www.blogger.com/atom/ns#' term='Forms'/><category scheme='http://www.blogger.com/atom/ns#' term='Application Server'/><title type='text'>ORA-01461: can bind a LONG value only for insert into a LONG column</title><content type='html'>Este error puede ocurrir cuando se insertan o actualizan valores en la base de datos desde alguna aplicación cliente, como por ejemplo Forms o Java.&lt;br /&gt;&lt;br /&gt;En mi caso utilizando Forms 10g, intentaba grabar en un textbox dos mil y tantos caracteres en un formulario, obteniendo repetidamente el error. El campo de formulario estaba definido como CHAR de largo máximo 4000 y la columna de la base de datos era VARCHAR2 de 4000; entonces donde estaba el problema?&lt;br /&gt;&lt;br /&gt;El problema resultó en el character set de la base de datos y el character set del cliente.&lt;br /&gt;La base de datos tenía un character set multibyte, es decir que puede utilizar varios bytes para representar un caracter. El cliente (en mi caso Application Server 10g), estaba configurado con un character set single byte. Resultado: el cliente enviaba 1 byte y la base de datos almacenaba en 3 bytes. Al enviar más de la tercera parte del tamaño de la columna, se produce un bug (conocido) de Oracle en el cual supone que se le está enviando un LONG. Forms internamente utiliza bind variables para las operaciones en formularios, por lo tanto se produce el error: can bind a LONG value only for insert into a LONG column.&lt;br /&gt;&lt;br /&gt;La solución simple es modificar el character set del cliente, en este caso del Application Server. Particularmete elegí el mismo que tenía la base de datos (UTF8).&lt;br /&gt;&lt;br /&gt;Los datos quedan intactos, y el cambio en mi cliente es transparente. Ahora cliente y servidor tienen los mismos juegos de caracteres, y evita que se produzcan las conversiones que llevan al bug mencionado.&lt;br /&gt;&lt;br /&gt;Para conocer el character set de la base de datos, puede ejecutarse esta consulta desde SQL*Plus:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:85%;"  lang="EN-US" &gt;SELECT VALUE FROM v$nls_parameters WHERE PARAMETER = 'NLS_CHARACTERSET';&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-5903495972191109535?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/5903495972191109535/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=5903495972191109535' title='6 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/5903495972191109535'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/5903495972191109535'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/05/ora-01461-can-bind-long-value-only-for.html' title='ORA-01461: can bind a LONG value only for insert into a LONG column'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8360575590826585223</id><published>2007-05-10T18:00:00.000-07:00</published><updated>2007-06-28T15:25:11.865-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqlplus'/><title type='text'>Manipulando HTML en SQL*plus</title><content type='html'>"Necesito ejecutar unos scripts SQL*plus que insertan líneas de código HTML en una tabla. El problema que tengo es que cuando los valores contienen el símbolo ampersand, Oracle me pide que ingrese un valor. Como hago para evitarlo?"&lt;br /&gt;&lt;br /&gt;Es común en HTML encontrar cosas del estilo:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;lt;font&amp;gt;&amp;lt;B&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;Acentuaci&lt;span style="color: rgb(255, 0, 0);"&gt;&amp;amp;oacute;&lt;/span&gt;n en la Gram&lt;span style="color: rgb(255, 0, 0);"&gt;&amp;amp;aacute;&lt;/span&gt;tica&amp;lt;/B&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;lt;/font&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;" &gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;El símbolo ampersand en SQL*Plus suele utilizarse como prefijo standard para indicar un parámetro de sustitución en un script.&lt;br /&gt;&lt;br /&gt;Por lo pronto, el único problema que presenta el código HTML en SQL*Plus, es para escribir el símbolo ampersand   (suerte que las comillas en HTML son dobles y no simples).&lt;br /&gt;&lt;br /&gt;Bueno, quien me planteó el problema no tenía intenciones de modificar los scripts originales, así que le di una solución simple: crear un script que invoque a los demás (con @), y que al comienzo del mismo contenga la línea:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-family:courier new;font-size:85%;"  &gt;SET DEFINE OFF&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Este comando de SQL*Plus inhibe los parámetros de entrada en SQL*Plus, por lo cual va a ignorar todos los ampersand que encuentre.&lt;br /&gt;&lt;br /&gt;Aunque la solución es simple y funciona para muchos casos, es también un poco drástica: inhibe los verdaderos parámetros que contengan nuestros scripts. Si nuestro script recibe un parámetro &amp;valor y en la siguiente línea inserta un texto que contiene un &amp;amp;aacute, entonces no hay manera de que SQL*plus se de cuenta cuál es parámetro y cuál no.  Conclusión: los scripts deben alterarse.&lt;br /&gt;&lt;br /&gt;Para esto hay algunas alternativas. La primera, es cambiar el prefijo para definir parámetros, por algún símbolo que no sea caracter especial en HTML, por ejemplo, el símbolo de pesos.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);font-size:85%;" &gt;&lt;span style="font-family:courier new;"&gt;SET DEFINE $&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;De esta manera los ampersand de HTML serán ignorados.&lt;br /&gt;&lt;br /&gt;Una segunda alternativa, es utilizar el caracter de escape (por defecto '\') para 'escapar' los ampersand del código. Luego los ampersand no son incluídos al guardarse en la tabla. Por lo general tendremos muchos menos parámetros que símbolos ampersand, así que particularmente elijo la primer opción.&lt;br /&gt;&lt;br /&gt;En conclusión, cambiar el prefijo de parámetros con SET DEFINE $ es la opción más flexible. Usar SET DEFINE OFF al comienzo del script nos inhibe los parámetros, pero si no los necesitamos, se transforma en la opción más sencilla de implementar.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8360575590826585223?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8360575590826585223/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8360575590826585223' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8360575590826585223'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8360575590826585223'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/05/manipulando-html-en-sqlplus.html' title='Manipulando HTML en SQL*plus'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-3078596456063045727</id><published>2007-05-08T19:47:00.000-07:00</published><updated>2007-05-13T14:15:28.418-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Error'/><category scheme='http://www.blogger.com/atom/ns#' term='10g'/><category scheme='http://www.blogger.com/atom/ns#' term='suse'/><title type='text'>Fallo de librerías en instalación de 10g en SUSE 10</title><content type='html'>Talvez hayan tenido este mismo problema.&lt;br /&gt;Mientras se instala Oracle 10.2.0 en Suse 10.0 64 bits, el Oracle Universal Installer falla en un 64% de completado, cuando esta enlazando las librerías. Al mirar el log generado, aparece lo siguiente:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;INFO: ./x86_64-suse-linux/bin/ld: skipping incompatible /usr/lib64/gcc-lib/x86_64-suse-linux/3.3.3/../../../libpthread.so when searching for -lpthread&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;/usr/lib64/gcc-lib/x86_64-suse-linux/3.3.3/../../../../x86_64-suse-linux/bin/ld: skipping incompatible /usr/lib64/gcc-lib/x86_64-suse-linux/3.3.3/../../../libpthread.a when searching for -lpthread&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;/usr/lib64/gcc-lib/x86_64-suse-linux/3.3.3/../../../../x86_64-suse-linux/bin/ld: cannot find -lpthread&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;INFO: collect2:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;INFO: ld returned 1 exit status&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;INFO: make: *** [ctxhx] Error 1&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;El problema que ocurre es que Oracle necesita algunas librerías de 32 bits, y la instalación por defecto de Suse tiene todas de 64 bits. Es necesario instalar las de 32 también.&lt;br /&gt;&lt;br /&gt;Son estas dos:&lt;br /&gt;&lt;br /&gt;1) &lt;span style="font-family:courier new;"&gt;glibc-devel-2.3.4-2.25 (i386)&lt;br /&gt;&lt;/span&gt;2) &lt;span style="font-family:courier new;"&gt;compat-libstdc++-33-3.2.3-47 (x86_64)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Si alguna de estas librerías están disponibles en versiones más nuevas, no hay que elegirlas, hay que instalar exactamente las versiones que acabo de marcar. Las versiones más nuevas no funcionan.&lt;br /&gt;Asimismo, si ya estuviera instalada la librería compat-libstdc++-33-3.2.3-47.3, hay que desinstalarla e instalar la que sirve: compat-libstdc++-33-3.2.3-47.&lt;br /&gt;&lt;br /&gt;Realmente es el único problema serio que me encontré, el resto mas o menos está en el manual de instalación.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-3078596456063045727?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/3078596456063045727/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=3078596456063045727' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3078596456063045727'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/3078596456063045727'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/05/fallo-de-libreras-en-instalacin-de-10g.html' title='Fallo de librerías en instalación de 10g en SUSE 10'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-8680778033351011990</id><published>2007-05-01T11:41:00.000-07:00</published><updated>2009-01-27T06:15:19.268-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='índices'/><title type='text'>Mis índices no funcionan!</title><content type='html'>TOP FIVE de casos en los cuales los índices que creamos 'inexplicablemente' no funcionan.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1 - CONSULTAS QUE RETORNAN MUCHOS REGISTROS&lt;br /&gt;&lt;/strong&gt;Este es el caso de consultas que retornan muchos registros. Según algunos autores, más del 5 por ciento del total de filas, pero en realidad es un porcentaje variable que se infiere según varios parámetros como cardinalidad, cantidad de filas, distribución en los bloques, etc.&lt;br /&gt;En este tipo de operaciones, es mas 'barato' para la base de datos leer toda la tabla y colocarla en memoria que acceder a cada registro mediante el índice. Hay que pensar que un índice es una estructura más que debemos consultar para recién llegar al bloque que contiene nuestro registro y traerlo a memoria, por lo cual ese acceso trae un costo adicional para cada fila. Si consultamos pocos registros, el incremento del costo por acceso a índice se absorve con el tiempo total. Si consultamos muchos registros, el overhead incrementa significativamente el tiempo de completitud.&lt;br /&gt;No está mal que los índices no se usen y se produzcan 'FULL SCAN TABLES'. El optimizador sabe los elementos que tiene a mano, cual es el costo asociado a cada método y por lo tanto puede resolver qué usar en cada caso.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2 - SE APLICAN FUNCIONES SOBRE COLUMNAS INDIZADAS&lt;/strong&gt;&lt;br /&gt;Cuando aplicamos cualquier función en una columna indizada en la cláusula WHERE, el índice deja de tener efecto.&lt;br /&gt;Un simple TRUNC sobre una columna fecha, o un TRIM en una columna VARCHAR2, deja sin efecto a los índices sobre esas columnas.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);font-family:courier new;font-size:85%;"  &gt;SELECT nombre, TRIM(apellido)&lt;br /&gt;FROM socios&lt;br /&gt;WHERE SUBSTR(codigo,1,3) = 'DNO'&lt;br /&gt;AND apellido &gt; 'P';&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;En este ejemplo, un índice sobre la columna codigo no es de utilidad, ya que la función SUBSTR lo anula. Un índice sobre apellido puede funcionar sin problemas ya que no hay ninguna función aplicada en la cláusula WHERE (notar que sí la hay en el SELECT).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;3 - CONVERSIONES IMPLICITAS&lt;/strong&gt;&lt;br /&gt;Por efecto del punto anterior, hay algunos casos en los que no tenemos en cuenta los tipos de datos en el WHERE, y dejamos que se realicen conversiones automáticas. Esto no es preferible, ya que Oracle siempre convierte la columna al tipo del elemento comparante.&lt;br /&gt;&lt;br /&gt;Por ejemplo, una consulta como la siguiente:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);font-family:courier new;font-size:85%;"  &gt;SELECT nombre, TRIM(apellido)&lt;br /&gt;FROM socios&lt;br /&gt;WHERE nro_socio = '2232';&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;internamente es traducida como:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);font-family:courier new;font-size:85%;"  &gt;SELECT nombre, TRIM(apellido)&lt;br /&gt;FROM socios&lt;br /&gt;WHERE TO_CHAR(nro_socio) = '2232';&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;...y el índice sobre nro_socio es inhibido por la conversión implícita.&lt;br /&gt;&lt;br /&gt;Siempre es bueno convertir explícitamente este tipo de condiciones en donde intervienen diferentes tipos de datos.&lt;br /&gt;Otras opciones son: estandarizar el almacen de datos (por ejemplo guardando siempre los valores con la función aplicada), usar una función de conversión sobre el literal en lugar de en la columna, crear índices de función.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;4 - EL PREDICADO NO ES ADECUADO&lt;/strong&gt;&lt;br /&gt;Por ejemplo, tenemos un índice compuesto formado por las columnas a, b y c. Se quiere realizar una consulta incluyendo en la cláusula WHERE la condicion b (por la cual queremos usar el índice), pero no se incluye a!&lt;br /&gt;Aunque la columna a almacene el mismo valor para todos los registros, Oracle necesita explícitamente todas las columnas previas entre las condiciones del where. De la misma manera, si queremos usar una condición sobre c, deberíamos incluir en el where condiciones para a y b.&lt;br /&gt;Otro caso es cuando tenemos un OR en el where, un distinto != sobre la columna indizada, o cuando la columna aparece a ambos lados de una expresión.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;5 - ESTADISTICAS OBSOLETAS (O INEXISTENTES)&lt;/strong&gt;&lt;br /&gt;El optimizador de la base de datos construye un plan de ejecución basado enteramente en estadísticas de los objetos, almacenadas en el dicionario de datos. La recolección de estas estadísticas debe realizarse periódicamente para que Oracle sepa cuales son los costos asociados a cada método de acceso a los registros, y de esta manera elegir la mejor opción. Al insertar y eliminar datos frecuentemente, cambian las estadísticas quedan obsoletas, y esto puede traer aparejado un impacto directo en la performance de las consultas.&lt;br /&gt;&lt;br /&gt;Pueden recolectarse estadísticas de todo el esquema, de una tabla, de un índice, del sistema. El paquete DBMS_STATS contiene procedimientos para hacer el trabajo: GATHER_TABLE_STATS, GATHER_INDEX_STATS, GATHER_SCHEMA_STATS.&lt;br /&gt;Para saber si una tabla tiene estadísticas, puede utilizarse la siguiente consulta:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);font-family:courier new;font-size:85%;"  &gt;SELECT last_analyzed&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);font-family:courier new;font-size:85%;"  &gt;FROM user_tables&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);font-family:courier new;font-size:85%;"  &gt;WHERE table_name = 'mi_tabla'&lt;tabla&gt;;&lt;/tabla&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);font-family:Courier New;font-size:85%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;Retorna la fecha de la ultima recolección. Si retorna NULL, entonces nunca fueron recolectadas.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0); font-weight: bold;"&gt;Ver también:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2007/11/indices-condicionales.html"&gt;Indices condicionales&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2008/03/11g-y-sus-ndices-invisibles.html%20"&gt;11g y sus índices invisibles&lt;/a&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/2009/01/indices-de-funcin.html"&gt;Indices de función para mejorar un LIKE&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-8680778033351011990?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/8680778033351011990/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=8680778033351011990' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8680778033351011990'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/8680778033351011990'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/05/indices-cmo-y-cundo.html' title='Mis índices no funcionan!'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-4709517589731069071</id><published>2007-04-27T19:42:00.000-07:00</published><updated>2008-02-27T15:21:45.552-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqlplus'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>Generar spools de más de 2 Gigas en Unix</title><content type='html'>Hay varias formas de generar spools con sql*plus desde Unix. La forma tradicional (o directa) es escribir un shell que realice lo siguiente:&lt;br /&gt;&lt;br /&gt;-Invocar a sql*plus en modo embebido.&lt;br /&gt;-Ejecutar &lt;em&gt;spool &lt;ruta_archivo&gt;&lt;/ruta_archivo&gt;&lt;/em&gt;y realizar el SELECT.&lt;br /&gt;-Finalizar el spool con &lt;em&gt;spool off&lt;/em&gt;.&lt;br /&gt;-Salir de sql*plus.&lt;br /&gt;-Zipear el archivo de salida (recomendado si se va a enviar a otro destino).&lt;br /&gt;&lt;br /&gt;El problema que presenta este enfoque, es que en algunos sistemas operativos, puede existir el límite de 2 Gigas de tamaño para archivos, por lo cual nuestro script va a alimentar el spool hasta llegar a ese tamaño. En el mejor de los casos obtendremos un error inesperado, en otros quizás ni nos enteremos.&lt;br /&gt;&lt;br /&gt;Una forma alternativa (y para mi gusto más elegante), es utilizar el comando mknod de Unix. Este comando, entre otras funcionalidades, permite crear un pipe de tipo FIFO que funciona como un dispositivo que utiliza el buffer del sistema operativo y permite conectarlo directamente con un programa de compresion como &lt;em&gt;compress&lt;/em&gt; o &lt;em&gt;gzip&lt;/em&gt;, evitando que se utilice el file system para almacenar el spool sin comprimir. También es posible conectarlo al programa &lt;em&gt;split&lt;/em&gt; para que particione el archivo zip, si este continúa superando el límite máximo.&lt;br /&gt;&lt;br /&gt;Básicamente las tareas que hay que escribir en nuestro shell son:&lt;br /&gt;-Borrar pipes que hayan quedado anteriormente (puede fallar si ya existe)&lt;br /&gt;-Crear el pipe&lt;br /&gt;-Asignarle permisos al pipe (con chmod)&lt;br /&gt;-Invocar en modo background al programa destino (el que va a recibir el spool). Este podría ser gzip, split, o ambos.&lt;br /&gt;-Invocar a sqlplus para que realice el spool&lt;br /&gt;-Borrar el pipe creado&lt;br /&gt;&lt;br /&gt;Resulta poco intuitivo llamar primero al programa destino y luego en definitiva a quien genera el spool, pero veamos un ejemplo de código para visualizarlo más claramente.&lt;br /&gt;&lt;br /&gt;Este script genera un spool y lo comprime en un zip:&lt;br /&gt;&lt;br /&gt;&lt;pre style="color: rgb(0, 102, 0);"&gt;&lt;span style="font-size:90;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;1&lt;/span&gt;   #/bin/ksh&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;2&lt;/span&gt;   rm -f ${ARCHIVO}.pipe 2&gt;/dev/null&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;3&lt;/span&gt;   mknod ${ARCHIVO}.pipe p&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;4&lt;/span&gt;   gzip -f &lt; ${ARCHIVO}.pipe &gt; ${ARCHIVO}.txt.Z 2&gt;&gt;${LOG} &amp;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;5&lt;/span&gt;   sqlplus -s usuario/password &amp;lt;&amp;lt; EOF &amp;gt; /dev/null 2&gt;&gt; ${LOG}&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;6&lt;/span&gt;   spool ${ARCHIVO}.pipe&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;7&lt;/span&gt;   select * FROM user_objects;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;8&lt;/span&gt;   spool off&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;9&lt;/span&gt;   EOF&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;10&lt;/span&gt;  rm -f ${ARCHIVO}.pipe 2&gt;/dev/null&lt;/eof&gt;&lt;/span&gt;&lt;/pre&gt;En la línea 2 eliminamos pipes que hayan podido quedar abiertos por alguna razón. De quedar abierto obtendríamos un error al crearlo de nuevo.&lt;br /&gt;&lt;br /&gt;En la línea 3 creamos el pipe. El primer parámetro es el nombre y el segundo el tipo (p=FIFO)&lt;br /&gt;&lt;br /&gt;En la línea 4 invocamos en background a gzip (notar el &amp; al final de la linea). El pipe (el cual todavia no ha recibido datos), va a ser la entrada fuente para gzip. Luego gzip generará el archivo .txt.Z.&lt;br /&gt;&lt;br /&gt;Las líneas 5 a 8 llaman a sqlplus y comienzan a enviar datos al pipe, luego gzip que esta corriendo en background zipea el contenido, sin necesidad de tener un archivo en file system.&lt;br /&gt;&lt;br /&gt;La línea 10 elimina el pipe.&lt;br /&gt;&lt;br /&gt;Si el archivo final .zip alcanza los 2 Gigas, vamos a tener el mismo problema, gzip no va a poder crearlo. Es necesario adicionarle el comando split y conectarlo con gzip para que parta el archivo en secciones de un tamaño manejable.&lt;br /&gt;Para eso, hay que insertar entre la línea 3 y la 4 del script anterior, la siguiente línea:&lt;span style="font-size:90;"&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:90;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;3.5&lt;/span&gt; &lt;span style="color: rgb(0, 102, 0);"&gt;split -b 1024m &lt; ${ARCHIVO}.txt.Z &amp;&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Conviene leer primero la línea 4 y luego la 3.5, observar que ambos programas corren en background.&lt;br /&gt;&lt;br /&gt;La línea 4 es la misma, genera el archivo .txt.Z.&lt;br /&gt;&lt;br /&gt;La línea 3.5 recibe el archivo .txt.Z y lo divide en archivos de 1024 megas, el tamaño es en bytes (especificado por el parámetro b).&lt;br /&gt;&lt;br /&gt;Sea cual sea el programa que genera el archivo, siempre podemos manipular con pipes los datos sin llegar a utilizar un archivo del file system para pasos intermedios.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Próximos pasos: &lt;/strong&gt;&lt;br /&gt;&lt;a href="http://www.freebsd.org/cgi/man.cgi?query=mknod&amp;amp;apropos=0&amp;sektion=0&amp;amp;manpath=FreeBSD+6.2-RELEASE&amp;format=html" target="_blank"&gt;MAN del comando mknod&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.freebsd.org/cgi/man.cgi?query=split&amp;amp;apropos=0&amp;sektion=0&amp;amp;manpath=FreeBSD+6.2-RELEASE&amp;amp;format=html" target="_blank"&gt;MAN del comando split&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-4709517589731069071?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/4709517589731069071/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=4709517589731069071' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4709517589731069071'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/4709517589731069071'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/04/generar-spools-de-ms-de-2-gigas-con.html' title='Generar spools de más de 2 Gigas en Unix'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-338913111570374974</id><published>2007-04-23T21:02:00.000-07:00</published><updated>2007-05-13T15:47:18.349-07:00</updated><title type='text'>Ahí Vamos!</title><content type='html'>Como siempre, hay una primera vez para todo!!&lt;br /&gt;La idea de comenzar mi blog surgió por sugerencia de mi amigo Fede Brubacher, adicto a RoR y otras tecnologías emergentes. El, que me conoce y ha sufrido mi orientación a las bases de datos relacionales, movilizó algunos mecanismos de motivación y curiosidad, con el simple hecho de mostrarme su trabajo:&lt;br /&gt;&lt;a href="http://fbrubacher.com/"&gt;http://fbrubacher.com/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;"Dedicado a aquellos que con sus blogs técnicos me ahorraron tantas horas de trabajo y esfuerzo"&lt;br /&gt;&lt;br /&gt;Sin más reflexiones doy por inagurado este blog.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-338913111570374974?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oraclenotepad.blogspot.com/feeds/338913111570374974/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6230959840389481677&amp;postID=338913111570374974' title='1 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/338913111570374974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/338913111570374974'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/04/primer-contacto.html' title='Ahí Vamos!'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6230959840389481677.post-7400820694612030782</id><published>2007-04-23T14:42:00.000-07:00</published><updated>2010-07-23T12:46:44.696-07:00</updated><title type='text'>Sobre el autor</title><content type='html'>&lt;span style="color: rgb(0, 0, 0); font-weight: bold;"&gt;Lucio Fernández Herrera&lt;/span&gt;&lt;br /&gt;Ingeniero de Sistemas (UDELAR, 2003)&lt;br /&gt;Oracle OCP DBA Certified (2006)&lt;br /&gt;Consultor Oracle Independiente&lt;br /&gt;&lt;br /&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://4.bp.blogspot.com/_fRmrOAuwnQ4/Rv7AEc9aLmI/AAAAAAAAACA/-FaflTMVmlI/s320/oracle_certprof.gif" alt="OCP DBA Certification logo" id="BLOGGER_PHOTO_ID_5115737409465232994" /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;br /&gt;Perfil Técnico&lt;/span&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold; color: rgb(0, 153, 0);"&gt;:: Bases de datos ::&lt;/span&gt;&lt;br /&gt;Oracle databases 8i/9i/10g/11g, MySQL, SQL Server&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 153, 0);"&gt;:: Tecnologías ::&lt;/span&gt;&lt;br /&gt;Unix, PL/SQL, Apex, Forms, PHP, J2EE, Data Guard, Sun Cluster&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 153, 0);"&gt;:: Áreas de especialización ::&lt;/span&gt;&lt;br /&gt;Desarrollo, análisis y diseño, administración, datawarehousing&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 96px; height: 156px;" src="http://3.bp.blogspot.com/_fRmrOAuwnQ4/RvWNic9aLlI/AAAAAAAAAB4/E8Gh0zdGvsM/s320/fotoLatu.JPG" alt="" id="BLOGGER_PHOTO_ID_5113148574977896018" border="0" /&gt;Me desempeño como DBA y especialista en PL/SQL en proyectos con J2EE. Actualmente resido en Rio de Janeiro, Brasil.&lt;br /&gt;Realizo consultorías de instalación, configuración, administración, tunning, entre otras diversas áreas de desarrollo.&lt;br /&gt;En los diversos proyectos en los cuales he participado aparece un común denominador que es la falta de un conocimiento avanzado de la herramienta que se está utilizando. Esto lleva a efectos tales como la mala performance de los sistemas y sobreesfuerzo de las personas. En ese sentido el propósito de este blog es dar mi pequeño aporte para aliviar y ayudar a que el trabajo sea cada vez más efectivo.&lt;br /&gt;En la carrera de bases de datos, mi motivación es seguir creciendo y aprendiendo día a día en un campo que debe soportar y acompañar a las nuevas tecnologías.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://oraclenotepad.blogspot.com/p/contacto.html"&gt;Contactar al autor&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6230959840389481677-7400820694612030782?l=oraclenotepad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7400820694612030782'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6230959840389481677/posts/default/7400820694612030782'/><link rel='alternate' type='text/html' href='http://oraclenotepad.blogspot.com/2007/09/sobre-el-autor.html' title='Sobre el autor'/><author><name>lfer</name><uri>http://www.blogger.com/profile/00618392085702183279</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_fRmrOAuwnQ4/Rv7AEc9aLmI/AAAAAAAAACA/-FaflTMVmlI/s72-c/oracle_certprof.gif' height='72' width='72'/></entry></feed>
