tag:blogger.com,1999:blog-62309598403894816772024-03-12T17:34:50.074-07:00Oracle NotepadNotas sobre Oracle, PL/SQL, Linux, AWK, buenas prácticas, soluciones, y experiencia personal.lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.comBlogger82125tag:blogger.com,1999:blog-6230959840389481677.post-80538430719168521792010-09-30T04:44:00.000-07:002010-10-18T05:01:54.346-07:00Flash Recovery AreaA 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.<br />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.<br /><br />1) Colocar la base de datos en modo ARCHIVELOG (en caso que no lo esté)<br /><br /><span style="font-family: courier new; color: rgb(0, 153, 0);">SHUTDOWN IMMEDIATE;</span><br /><span style="font-family: courier new; color: rgb(0, 153, 0);">STARTUP MOUNT;</span><br /><span style="font-family: courier new; color: rgb(0, 153, 0);">ARCHIVE LOG START;</span><br /><span style="font-family: courier new; color: rgb(0, 153, 0);">ALTER DATABASE ARCHIVELOG;</span><br /><span style="font-family: courier new; color: rgb(0, 153, 0);">ALTER DATABASE OPEN;</span><br /><br />2) Crear un directorio destino para los archivos via sistema operativo. En nuestro caso será: /u04/fra<br />En caso de estar usando ASM es exactamente igual, crearemos un grupo llamado por ejemplo +FRA.<br /><br />3) Configurar los siguientes parámetros con usuario sys:<br /><br /><span style="font-family: courier new; color: rgb(0, 153, 0);">SQL> ALTER SYSTEM SET DB_RECOVERY_FILE_DEST_SIZE = 10G SCOPE=BOTH SID='*';</span><br /><span style="font-family: courier new; color: rgb(0, 153, 0);">SQL> ALTER SYSTEM SET DB_RECOVERY_FILE_DEST = '/disk1/fra' SCOPE=BOTH SID='*';</span><br /><br />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.<br />En próximos articulos veremos ejemplos para sacar provecho de nuestra flash recovery área.lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com2tag:blogger.com,1999:blog-6230959840389481677.post-30735237038531397712010-08-27T05:33:00.001-07:002010-08-27T05:53:36.065-07:00Volumenes Netbackup vuelven a scratchEsta es de Veritas Netbackup 6.5, y me consta que para versiones 5 también funciona.<br /><br />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.<br /><br />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.<br /><br />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:<br /><br />(con usuario root)<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >nbemmcmd -changesetting -return_unassigned_media_to_scratch_pool no -machinename <span style="font-style: italic;">nombre_del_host</span></span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >NBEMMCMD, Version:6.5</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Command completed successfully.</span><br /><br /><nombre><span style="font-style: italic;">nombre_del_host</span> es la máquina donde el Netbackup está instalado.</nombre>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com0tag:blogger.com,1999:blog-6230959840389481677.post-267226986701147382010-07-22T13:21:00.001-07:002010-07-28T08:15:39.938-07:00Recuperando dispositivos failed en SolarisEsta 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.<br /><br />La unidad en cuestión aparece entre los dispositivos como failed:<br /><br />(como usuario root en Solaris 10)<br /><pre style="color: rgb(0, 153, 0);"><span style="font-size:100%;"><span style="font-family:courier new;">cfgadm -al</span><br /><span style="font-family:courier new;">Ap_Id Type Receptacle Occupant Condition</span><br /><span style="font-family:courier new;">N0.IB6 PCI-X_I/O_B connected configured ok</span><br /><span style="font-family:courier new;">N0.IB6::pci0 io connected configured ok</span><br /><span style="font-family:courier new;">N0.IB6::pci1 io connected configured ok</span><br /><span style="font-family:courier new;">N0.IB6::pci2 io connected configured ok</span><br /><span style="font-family:courier new;">N0.IB6::pci3 io connected configured ok</span><br /><span style="font-family:courier new;">N0.SB0 CPU_V3 connected configured ok</span><br /><span style="font-family:courier new;">N0.SB0::cpu0 cpu connected configured ok</span><br /><span style="font-family:courier new;">....<br /><span style="font-style: italic;">(varias entradas)</span><br />...<br /></span><span style="font-family:courier new;">c4 fc-private connected configured unknown<br />c4::500110a0008c4980 tape connected configured unknown</span><br /><span style="font-family:courier new;">c5 fc-private connected configured unknown</span><br /><span style="font-family:courier new;">c5::202800a0b82ae608 disk connected configured unknown</span><br /><span style="font-family:courier new;">c6 fc-private connected configured unknown</span><br /><span style="font-family:courier new;">c6::500110a0008c497a med-changer connected configured <span style="color: rgb(255, 0, 0);">failed</span></span></span></pre>De las dos unidades de backup (c4 y c6), la última aparece como failed, y en vez del tipo 'tape' se muestra 'med-changer'.<br /><br />En varias fuentes se recomienda desconfigurar el dispositivo para volverlo a configurar, pero el comando no es reconocido:<pre><span style="color: rgb(0, 153, 0);font-family:courier new;" >cfgadm -f -c unconfigure c6::500110a0008c497a</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >cfgadm: Configuration operation not supported</span></pre>Cuando intentamos ver la configuración del drive, obtenemos un error de comunicación:<pre><span style="color: rgb(0, 153, 0);">luxadm display 500110a0008c497a</span><br /><span style="color: rgb(0, 153, 0);"> Error: SCSI failure. - 500110a0008c497a.</span></pre>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!<br /><br />El comando mágico es:<br /><pre style="color: rgb(0, 153, 0);">luxadm -e forcelip /dev/cfg c6</pre> Verificar con el comando <span style="font-style: italic;">cfgadm -al que</span> el dispositivo ya no tiene el estado 'failed'. Probar el comando <span style="font-style: italic;">luxadm display</span> como se muestra anteriormente, debería mostrar la configuración del dispositivo correctamente.<br /><br />Si esto no resuelve, los caminos alternativos son dos:<br />1) Sacar el cable de datos que llega al drive y reconectarlo luego de 2 minutos, verificando el listado de dispositivos.<br />2) Reiniciar el servidor (obviamente la opción menos deseada en un ambiente en producción).lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com0tag:blogger.com,1999:blog-6230959840389481677.post-89835201025292134012010-06-24T09:37:00.001-07:002010-07-28T08:18:03.206-07:00Keep alive en PL/SQL DeveloperLa 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.<br /><br />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.<br /><br />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.<br /><br />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.<br /><br /><span style="font-weight: bold;">Workaround</span><br />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.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgS-RN8L9wutfW4Xbm2w0KFNMvlIYO3bu1wA_hW4IYgC3iTsgojVbm7fEkVvLHG-IcWYq2dQ0KIw6o6302yENgrjo3eDn1uYnq4r04NMZwkEPbBK_apF3iuRxWU-bjcq58LuHq-Z6P28-g/s1600/plsql_auto_refresh.png"><img style="float: left; margin: 0pt 10px 10px 0pt; cursor: pointer; width: 320px; height: 162px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgS-RN8L9wutfW4Xbm2w0KFNMvlIYO3bu1wA_hW4IYgC3iTsgojVbm7fEkVvLHG-IcWYq2dQ0KIw6o6302yENgrjo3eDn1uYnq4r04NMZwkEPbBK_apF3iuRxWU-bjcq58LuHq-Z6P28-g/s320/plsql_auto_refresh.png" alt="" id="BLOGGER_PHOTO_ID_5486696299713434546" border="0" /></a>Dentro de Sessions, hay un switch para activar el auto-refresh, como se ve en la imagen a la izquierda.<br /><br />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.<br /><br />Me tardé un poco en descubrirlo pero espero que ayude a alguien.lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com3tag:blogger.com,1999:blog-6230959840389481677.post-12334654625132201582010-05-27T07:31:00.000-07:002010-07-28T08:18:39.092-07:00Una de funciones analíticasEn 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.<br /><br />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.<br />Lo que queremos hacer es un reporte con el tiempo total en cada uno de esos estados, con un único SQL.<br /><br />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.<br /><br />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.<br /><br />Comenzamos creando la tabla:<br /><pre style="color: rgb(0, 153, 0);">CREATE TABLE status_history<br />(id INTEGER PRIMARY KEY,<br />ticket_id INTEGER,<br />estado VARCHAR(1),<br />fecha DATETIME);</pre>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.<br /><pre style="color: rgb(0, 153, 0);">INSERT INTO status_history VALUES (1, 1, 'A', '2010-01-01 14:00:00');<br />INSERT INTO status_history VALUES (2, 1, 'B', '2010-01-03 18:30:00');<br />INSERT INTO status_history VALUES (3, 1, 'C', '2010-01-07 10:00:00');<br />INSERT INTO status_history VALUES (4, 1, 'A', '2010-01-11 12:10:00');<br />INSERT INTO status_history VALUES (5, 1, 'C', '2010-01-14 15:00:00');</pre><pre style="color: rgb(0, 153, 0);"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCOkp9bgQzaupefDwdNXEm_UMIuraoyO6naeBIW3eFxoTTkiiKKHwgxD7MCrQyvgM9_MldTTreAUEhWjJu3lB6cMtqd_X-TPf0ws75kZcCIMz7bT2XWwyuXzJerRQbU-y4EfVS82iaETw/s1600/estados.png"><img style="float: left; margin: 0pt 10px 10px 0pt; cursor: pointer; width: 154px; height: 145px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCOkp9bgQzaupefDwdNXEm_UMIuraoyO6naeBIW3eFxoTTkiiKKHwgxD7MCrQyvgM9_MldTTreAUEhWjJu3lB6cMtqd_X-TPf0ws75kZcCIMz7bT2XWwyuXzJerRQbU-y4EfVS82iaETw/s320/estados.png" alt="" id="BLOGGER_PHOTO_ID_5476008499359760386" border="0" /></a></pre>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.<br /><br />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<span style="font-size:85%;">1</span>). Lo mismo para el último intervalo tA<span style="font-size:85%;">2</span>. La suma de tA<span style="font-size:85%;">1 y </span>tA<span style="font-size:85%;">2</span> representará el tiempo total que el ticket estuvo en estado 'A'.<br /><br />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.<br />Veremos primero el caso simple donde cada registro tiene un id consecutivo, y luego nos enfocaremos en resolver el problema de los huecos.<br /><br /><span style="font-weight: bold;">CASO 1 - IDs continuos (1,2,3,4,5....)</span><br /><br />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.<pre style="color: rgb(0, 153, 0);">SELECT s1.estado,<br /> TIME_TO_SEC(timediff(s2.fecha,s1.fecha))/60/60 AS total_horas<br />FROM<br /> status_history s1, status_history s2<br />WHERE s1.id+1 = s2.id<br /> AND s1.ticket_id=1<br /> AND s2.ticket_id=1</pre><br />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.<br /><br />Obtenemos las siguientes tuplas:<pre style="color: rgb(0, 153, 0);">ESTADO TOTAL_HORAS<br />====== ============<br />'A' 52.50000000<br />'B' 87.50000000<br />'C' 98.16666667<br />'A' 74.83333333</pre><br />Cabe observar que aparecen dos instancias del estado A, que en el diagrama anterior equivalen a tA<span style="font-size:85%;">1 y </span>tA<span style="font-size:85%;">2. </span>Para obtener la suma total, agrupamos por estado y utilizamos la función SUM:<pre style="color: rgb(0, 153, 0);">SELECT s1.estado,<br /> SUM(TIME_TO_SEC(timediff(s2.fecha,s1.fecha)))/60/60 AS total_horas<br />FROM<br /> status_history s1, status_history s2<br />WHERE s1.id+1 = s2.id<br /> AND s1.ticket_id=1<br /> AND s2.ticket_id=1<br />GROUP BY estado</pre><br />Finalmente el resultado deseado:<pre style="color: rgb(0, 153, 0);">ESTADO TOTAL_HORAS<br />====== ============<br />'A' 127.33333333<br />'B' 87.50000000<br />'C' 98.16666667</pre><span style="font-weight: bold;">CASO 2 - IDs con huecos (1,2,4,5,9....)</span><br /><br />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.<br />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.<br /><br />Columna virtual ROWNUM con MySQL:<pre style="color: rgb(0, 153, 0);">SELECT @rownum:=@rownum+1 rownum, t.*<br />FROM (SELECT @rownum:=0) r, <tabla o consulta> t</pre>Ahora si, podemos sustituir las tablas por este código y usar rownum como condición.<br /><br />Para probarlo con nuestro ejemplo, agregaremos un nuevo cambio de estado, con id=8:<pre style="color: rgb(0, 153, 0);">INSERT INTO status_history VALUES (8, 1, 'D', '2010-01-16 11:00:00');</pre>La secuencia de ids ahora es 1, 2, 3, 4, 5, 8<br /><br />La consulta queda así:<pre style="color: rgb(0, 153, 0);">SELECT t1.estado,<br /> SUM(TIME_TO_SEC(timediff(t2.fecha,t1.fecha)))/60/60 AS total_horas<br />FROM<br /> (SELECT @rownum1:=@rownum1+1 rownum, s1.*<br /> FROM (SELECT @rownum1:=0) r, status_history s1<br /> WHERE s1.ticket_id=1<br /> ORDER BY s1.id) t1,<br /> (SELECT @rownum2:=@rownum2+1 rownum, s2.*<br /> FROM (SELECT @rownum2:=0) r, status_history s2<br /> WHERE s2.ticket_id=1 <br /> ORDER BY s2.id) t2<br />WHERE t1.rownum+1 = t2.rownum<br />GROUP BY t1.estado</pre>Observar que cada subconsulta utiliza su propia variable @rownum, ya que si usarámos la misma, se incrementaría incorrectamente.<br /><br />El resultado es:<pre style="color: rgb(0, 153, 0);">ESTADO TOTAL_HORAS<br />====== ============<br />'A' 127.33333333<br />'B' 87.50000000<br />'C' 142.16666667</pre>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.<br /><br /><span style="font-weight: bold;">La solución Oracle con Funciones Analíticas</span><br /><br />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.<br />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.<br /><br />Todo se resume a usar lo siguiente:<pre style="color: rgb(0, 153, 0);">LEAD(fecha, 1, NULL) OVER(ORDER BY id)</pre><ul><li>El primer parámetro es la columna que voy a retornar, en mi caso, fecha.</li><li>El segundo es el offset que quiero recuperar, es decir cuantas filas 'adelante'.</li><li>El tercer parámetro es el valor que quiero mostrar si no hay siguiente registro.</li><li>Por último, la columna que voy a usar como criterio de ordenación.</li></ul>La consulta principal que me da tiempos por estado (no agrupados) queda así:<pre style="color: rgb(0, 153, 0);">SELECT t1.estado,<br /> (<span style="color: rgb(0, 0, 153);">LEAD(fecha, 1, NULL) OVER(ORDER BY id)</span> - t1.fecha)*24 AS total_horas<br />FROM status_history t1<br />ORDER BY id</pre>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.<br /><br />Solo resta agrupar los estados y sumar los totales en una query exterior:<pre style="color: rgb(0, 153, 0);">SELECT estado,<br /> SUM(total_horas) total_horas<br />FROM (<span style="color: rgb(0, 0, 153);">SELECT t1.estado,</span><br /><span style="color: rgb(0, 0, 153);"> (LEAD(fecha, 1, NULL) OVER(ORDER BY id) - t1.fecha)*24 total_horas</span><br /><span style="color: rgb(0, 0, 153);"> FROM status_history t1</span><br /><span style="color: rgb(0, 0, 153);"> ORDER BY id</span>)<br />WHERE total_horas IS NOT NULL<br />GROUP BY estado<br />ORDER BY estado;</pre>El resultado es el mismo, lo pueden comprobar si se animan a hacer la prueba.<br />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.<br /><br />Como comentario, la función LAG nos retorna el registro anterior usando la misma sintáxis que LEAD.<br /><br /><span style="font-weight: bold;">Conclusiones</span><br /><br />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.<br /><br />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 :)<br /><br />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.<br /><br /><span style="font-weight: bold;">Ver también:</span><br /><a href="http://www.oracle.com/pls/db102/to_URL?remark=ranked&urlname=http:%2F%2Fdownload.oracle.com%2Fdocs%2Fcd%2FB19306_01%2Fserver.102%2Fb14200%2Ffunctions001.htm%23SQLRF06174">Documentación de Funciones Analíticas en Oracle 10g</a>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com0tag:blogger.com,1999:blog-6230959840389481677.post-52180442428297218462010-04-13T12:04:00.001-07:002010-06-25T06:22:42.660-07:00Listar parámetros indocumentados en OracleLos 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 "_".<br /><br />El comando de SQL*Plus <span style="font-style: italic;">show parameters</span> lista los valores estandar de la base de datos, y también de aquellos parámetros indocumentados que hemos alterado explícitamente.<br /><br />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.<pre style="color: rgb(0, 153, 0);">set linesize 131<br />set pages 9999<br />col parameter for A30<br />col description for A40<br />col "Session Value" for A15<br />col "Instance Value" for A15<br /><br />SELECT<br />a.ksppinm "Parameter",<br />a.ksppdesc "Description",<br />b.ksppstvl "Session Value",<br />c.ksppstvl "Instance Value"<br />FROM<br />x$ksppi a,<br />x$ksppcv b,<br />x$ksppsv c<br />WHERE<br />a.indx = b.indx<br />AND<br />a.indx = c.indx<br />AND<br />a.ksppinm LIKE '/_%' escape '/'<br />/</pre>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.lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com0tag:blogger.com,1999:blog-6230959840389481677.post-69093005498185255752010-03-04T08:42:00.001-08:002010-07-28T08:19:16.739-07:00Expresiones regulares en SQL con 9iRecibi un comentario de Laura con el siguiente problema:<br /><br /><span style="font-style: italic; color: rgb(0, 0, 153);">"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:<br /><br />SELECT * FROM prueba WHERE (content REGEXP ?) AND (codigo = ?) AND (nombre = ?) GROUP BY mes<br /><br />¿Alguien sabe como puedo pasar a oracle esta sentecia?"</span><br /><br />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.<br /><br />El paquete para usar expresiones regulares se llama owa_pattern. Basta hacer un<span style="font-style: italic;"> desc owa_pattern</span> en la consola para saber si tenemos acceso a el, en caso contrario solicitarlo al administrador.<br /><br />La función para buscar ocurrencias en expresiones regulares dentro del paquete es:<br /><span style="color: rgb(51, 51, 255);">match([cadena_entrada]<valor_entrada>,[expresion_regular]<expresion_regular>)</expresion_regular></valor_entrada></span><br />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.<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >SQL> select owa_pattern.match('ejemplo','ej*o') from dual;</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > </span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >ORA-06552: PL/SQL: Statement ignored</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >ORA-06553: PLS-382: expression is of wrong type</span><br /><br />La solución es crear una función traductora en PL/SQL para convertir el booleano retornado por <span style="font-style: italic;">match</span> 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.<pre><span style="color: rgb(0, 153, 0);font-family:courier new;" >create or replace function sql_match (exp varchar2, regexp varchar2) return number as</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > begin</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > if (owa_pattern.match(exp, regexp)) then</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > return 1;</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > else</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > return 0;</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > end if;</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > end;</span></pre>La ponemos en práctica:<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >SQL> select sql_match('ejemplo','ej.*o') res from dual;</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > </span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > RES</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >----------</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > 1</span><br /><br />Ahora esta función es perfectamente compatible con SQL, y podemos usarla para evaluar el valor de una columna:<br /><br /><span style="font-weight: bold;">Ejemplo</span>: nombres de empleados<span style="font-style: italic;"></span> que comienzan con 'A'<br /><pre style="color: rgb(0, 153, 0);">SQL> select ename, sql_match(ename,'^A.*') res from scott.emp;<br /><br />ENAME RES<br />---------- ----------<br />SMITH 0<br />ALLEN 1<br />WARD 0<br />JONES 0<br />MARTIN 0<br />BLAKE 0<br />CLARK 0<br />SCOTT 0<br />KING 0<br />TURNER 0<br />ADAMS 1<br />JAMES 0<br />FORD 0<br />MILLER 0<br /><br />14 rows selected</pre>Ahora si, volviendo al problema original planteado por nuestra amiga, la traducción de MySQL a Oracle 9i queda:<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >SELECT * FROM prueba WHERE (sql_match(content, ?)=1) AND (codigo = ?) AND (nombre = ?) GROUP BY mes</span><br /><br /><br />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:<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >SELECT * FROM prueba WHERE regexp_like(content, ?) AND (codigo = ?) AND (nombre = ?) GROUP BY mes</span><br /><span class="style6"><span style="font-family:Courier New,Courier,mono;"></span></span>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com0tag:blogger.com,1999:blog-6230959840389481677.post-45839285475895252142010-02-10T06:14:00.000-08:002010-03-30T10:54:50.927-07:00Critical Patch Update Febrero 2010Oracle lanzó el último 4 de febrero una actualización de seguridad para sus servidores Weblogic, para las siguientes versiones:<br /><br /><table cellborder="0" style="margin-left: 0.25in;" summary="BEA Weblogic" class="texta" border="0"><tbody><tr><td>• Oracle WebLogic Server 11gR1 releases (10.3.1 and 10.3.2)</td> <td> <br /></td> </tr> <tr> <td> • Oracle WebLogic Server 10gR3 release (10.3.0)</td> <td> <br /></td> </tr> <tr> <td> • Oracle WebLogic Server 10.0 through MP2</td> <td> <br /></td> </tr> <tr> <td> • Oracle WebLogic Server 9.0, 9.1, 9.2 through MP3</td> <td> <br /></td> </tr> <tr> <td> • Oracle WebLogic Server 8.1 through SP6 </td> <td> <br /></td> </tr> <tr> <td> • Oracle WebLogic Server 7.0 through SP7 </td></tr></tbody></table><br />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.<br /><br />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.<br /><br />Cierto o no, lo más recomendable es estar precavidos.<br /><br /><span style="font-weight: bold;">Ver también:</span><br /><a target="_blank" href="http://www.oracle.com/technology/deploy/security/alerts.htm">http://www.oracle.com/technology/deploy/security/alerts.htm</a><br /><a target="_blank" href="http://www.reuters.com/article/idUSTRE6125FB20100203?type=technologyNews">http://www.reuters.com/article/idUSTRE6125FB20100203?type=technologyNews</a>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com0tag:blogger.com,1999:blog-6230959840389481677.post-47912300747055897342010-01-07T05:31:00.000-08:002010-03-30T10:57:17.158-07:00Como exportar e importar código PL/SQL en 9iA 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 :)<br /><br />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.<br /><br />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 (<a target="_blank" href="http://oraclenotepad.blogspot.com/2007/09/un-extractor-de-ddl-en-archivos.html">ver artículo</a>), 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.<br /><br />Primero que nada, debemos saber que EXP/IMP trabaja en cuatro modos:<br /><ul><li>TABLE</li><li>TABLESPACE</li><li>USER</li><li>FULL</li></ul>Si queremos exportar el código, deberemos usar el modo USER, o FULL (todos los esquemas).<br />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.<br /><br />Hecha esta aclaración, pasamos a los comandos.<br /><br /><span style="font-weight: bold;">Generar el dump</span> (en modo USER)<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >exp usuario/password file=/tmp/bck_usuario.dmp owner=usuario rows=N triggers=Y indexes=N constraints=N log=/tmp/bck_usuario.log</span><br /><br /><span style="font-weight: bold;">Importar el código</span> (en modo FULL)<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >imp usuario/password file=/tmp/bck_usuario.dmp full=Y ignore=y indexes=N log=/tmp/bck_usuario.log</span><br /><br /><span style="font-weight: bold;">Observaciones</span><br /><ul><li>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.</li><li>Como IMP únicamente puede utilizarse en modo FULL, si queremos traer solamente un usuario entonces deberemos generar un export de tipo USER.</li></ul>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com0tag:blogger.com,1999:blog-6230959840389481677.post-36860715508089121362009-12-18T10:36:00.000-08:002009-12-18T12:06:56.762-08:00Cómo saber el tipo de Linux para instalar OracleAlguna vez caímos en paracaídas a instalar algún producto Oracle en un servidor Linux desconocido, e inmediatamente surgieron las siguientes interrogantes:<br /><ul><li>¿Qué versión de Linux es?</li><li>¿Cuál es la versión de Oracle que debo descargar?</li></ul>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.<br /><br />Por ese motivo recomiendo tener esta guía-ayuda a mano, para no perder tiempo a la hora de elegir.<br /><br />Mapea los códigos informados por el comando Unix <span style="font-style: italic;">uname</span>, 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.<br />Por ejemplo para el primer caso sería <span style="font-family:monospace;">"</span><code class="km">i686 i686 i386".</code><br /><br />Espero que ayude!<br /><br /><code class="km"><span style="color: rgb(0, 153, 0);">$ uname -a </span><br /><span style="color: rgb(0, 153, 0);">Linux hostname 2.6.9-34.0.2.ELsmp #1 SMP Fri Jun 30 10:33:58 EDT 2006 <span style="font-weight: bold;">i686 i686 i386</span> GNU/Linux </span><br />--> </code><u>Linux x86:</u> (32-bit OS)<br /><br /><code class="km"><span style="color: rgb(0, 153, 0);">$ uname -a </span><br /><span style="color: rgb(0, 153, 0);">Linux hostname 2.6.9-55.ELsmp #1 SMP Fri Apr 20 16:36:54 EDT 2007 <span style="font-weight: bold;">x86_64 x86_64 x86_64</span> GNU/Linux </span><br />--> </code><u>Linux x86_64:</u> (64-bit OS)<br /><br /><code class="km"><span style="color: rgb(0, 153, 0);">$ uname -a </span><br /><span style="color: rgb(0, 153, 0);">Linux hostname 2.6.9-42.EL #1 SMP Wed Jul 12 23:25:09 EDT 2006 <span style="font-weight: bold;">ia64 ia64 ia64</span> GNU/Linux </span><br />--> </code><u>Linux Itanium:</u> (64-bit OS)<br /><br /><code class="km"><span style="color: rgb(0, 153, 0);">$ uname -a </span><br /><span style="color: rgb(0, 153, 0);">Linux hostname 2.6.9-34.0.1.0.11.EL #1 SMP Mon Dec 4 16:10:42 PST 2006 <span style="font-weight: bold;">ppc64 ppc64 ppc64</span> GNU/Linux</span><br />--> </code><u>IBM Power Based Linux:</u> (64-bit OS)<br /><br /><code class="km"><span style="color: rgb(0, 153, 0);">$ uname -a </span><br /><span style="color: rgb(0, 153, 0);">Linux hostname 2.6.16.46-0.12-default #1 SMP Thu May 17 14:00:09 UTC 2007 <span style="font-weight: bold;">s390x s390x s390x</span> GNU/Linux</span><br />--> </code><u>IBM zSeries Based Linux:</u> (64-bit OS)<br /><br /><code class="km"><span style="color: rgb(0, 153, 0);">$ uname -a </span><br /><span style="color: rgb(0, 153, 0);">Linux hostname 2.4.21-50.EL #1 SMP Tue May 8 17:10:38 EDT 2007 <span style="font-weight: bold;">s390 s390 s390 </span>GNU/Linux</span><br />--> </code><u>IBM S/390 Based Linux (31-bit):</u> (31-bit OS)lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com0tag:blogger.com,1999:blog-6230959840389481677.post-73078474234548906562009-11-28T10:03:00.000-08:002009-11-30T03:47:08.406-08:00Controlando el horario de verano en SolarisSolaris 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.<br /><br />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.<br /><br />Para tener el control, tenemos que verificar la configuración en Solaris y ver que está de acuerdo con nuestros intereses.<br /><br /><span style="font-weight: bold;"><br />COMO ALTERAR LA CONFIGURACION DEL HORARIO DE VERANO</span><br /><br /><span style="font-weight: bold; color: rgb(0, 0, 102);">Paso 1 </span>- Verificar la zona horaria del servidor<br /><br />Siempre logueados como root, leemos el archivo TIMEZONE y buscamos el valor de la variable TZ al final del archivo:<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >cat /etc/TIMEZONE</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >TZ=Brazil/East</span><br /><br /><br /><span style="color: rgb(0, 0, 102); font-weight: bold;">Paso 2 </span>- Hacer un backup de la configuración actual (por si acaso)<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >cd /usr/share/lib/zoneinfo</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >cd Brazil</span> (o la region que corresponda según el Paso 1)<br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >cp East East.backup</span><br /><br />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.<br /><br /><br /><span style="color: rgb(0, 0, 102); font-weight: bold;">Paso 3 </span>- Verificar configuración del horario de verano, según el TZ encontrado en el Paso 1<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >zdump -v Brazil/East | grep 20</span><br /><br />Encontraremos varias líneas del tipo:<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Brazil/East Sat Nov 28 18:31:41 2009 UTC = Sat Nov 28 15:31:41 2009 BRT isdst=0</span><br /><br />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.<br />O sea que si vivimos en UMT-3, en el horario de verano vamos a tener UMT-2.<br /><br />Verificar se coincide con el cambio oficial programado para el corriente año.<br /><br /><br /><span style="font-weight: bold; color: rgb(0, 0, 102);">Paso 4 </span>- Crear un archivo .zic con las alteraciones deseadas<br /><br />Crear un nuevo archivo llamado config_dst.zic con el siguiente contenido:<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Rule Brazil 2009 only - Oct 11 00:00 1 S</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > Rule Brazil 2009 only - Oct 20 00:00 0 -</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Zone Brazil/East -3:00 Brazil BR%sT</span><br /><br />(fin del archivo)<br /><br />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.<br /><br />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 <span style="font-style: italic;">date</span>. Este será BRST en horario de verano, y BRT en horario normal.<br /><br /><br /><span style="color: rgb(0, 0, 102); font-weight: bold;">Paso 5 </span>- Aplicar la nueva configuración con el comando zic<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >zic config_dst.zic</span><br /><br /><br /><span style="color: rgb(0, 153, 0);font-family:georgia;" ></span><span style="color: rgb(0, 0, 102); font-weight: bold;">Paso 6</span> - Esperar la fecha prevista y verificar<br /><br />Con el comando <span style="font-style: italic;">date</span>, verificaremos que el horario es el correcto y el tipo de horario es BRST, tal como lo configuramos.lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com0tag:blogger.com,1999:blog-6230959840389481677.post-67337766961940498572009-10-07T07:49:00.000-07:002009-10-13T08:14:07.048-07:00Adiós MetalinkEl tradicional site de soporte oficial de Oracle, más conocido como Metalink (<a href="http://metalink.oracle.com/" target="_blank">metalink.oracle.com</a>) 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.<br /><br />Personalmente gusto más del viejo Metalink desarrollado con <a href="http://apex.oracle.com/" target="_blank">APEX</a>, 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.<br /><br />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.<br /><br />Actualmente Oracle tiene abierta una encuesta para expresar nuestra opinión, en este <a href="http://www.zoomerang.com/Survey/?p=WEB227VX2FG7GT" target="_blank">link</a>.<br /><br /><span style="font-weight: bold;">Más información:</span><br /><a href="http://metalink.oracle.com/" target="_blank">Nota ID 841061.1 en Metalink</a><b><br /></b><a href="http://www.oracle.com/support/training-schedule.html?msgid=8237387">Trainings My Oracle Support</a><b><br /></b>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com2tag:blogger.com,1999:blog-6230959840389481677.post-64042136478147922752009-09-04T06:47:00.000-07:002009-09-11T11:12:02.771-07:00Oracle Critical Patch Octubre 2009El 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.<br /><br />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)".<br /><br />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?<br /><br />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. <br />Hacen parecer que los patches críticos no son tan críticos.lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com0tag:blogger.com,1999:blog-6230959840389481677.post-46240781774506745992009-08-13T07:00:00.000-07:002009-09-04T07:15:37.106-07:00Cómo ocultar el código de PL/SQLPuede el código PL/SQL ser escondido y protegido de miradas ajenas, cuando lo implantamos en alguna base de datos externa?<br /><br />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.<br />Oracle se refiere a este método como ofuscamiento "obfuscation" (el término ofuscar en el diccionario tiene el significado de "oscurecer, encubrir").<br /><br />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.<br /><br /><span style="font-weight: bold;">Qué encriptar</span><br /><br />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.<br /><br />Una recomendación: usemos un criterio, no seamos paranoicos.<br />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.<br /><br />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.<br /><br /><span style="font-weight: bold;">Antes de comenzar</span><br /><br />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.<br /><br /><span style="font-weight: bold;">Cómo encriptar</span><br /><br />Existe un ejecutable en $ORACLE_HOME/bin que se llama <span style="font-weight: bold;">wrap</span>.<br />Llamándolo desde la consola, y pasándole el nombre de un script en el parámetro <span style="font-style: italic;">iname</span>, nos retorna un archivo de texto <span style="font-weight: bold;">.plb</span> con código interno, el cual podremos compilar en SQL*plus para crear el objeto almacenado "ofuscado".<br /><br /><span style="font-weight: bold;">Ejemplo 1</span><br /><br />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:<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >wrap edebug=wrap_new_sql iname=ob_proc.sql</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >PL/SQL Wrapper: Release 9.2.0.8.0- Production on Thu Aug 13 11:38:30 2009</span><span style="font-family:courier new;"> </span><span style="color: rgb(0, 153, 0);font-family:courier new;" >Copyright (c) Oracle Corporation 1993, 2001. All Rights Reserved.</span> <span style="color: rgb(0, 153, 0);font-family:courier new;" ><br /><br />Processing ob_proc.sql to ob_proc.plb</span><br /><br />Nota: El flag <span style="font-style: italic;">edebug</span>=wrap_new_sql es necesario para poder soportar el nuevo compilador de sql y corregir un bug existente con algunas sintaxis de sql avanzado.<br /><br />La salida, es el archivo encriptado ob_proc.plb, el cual ahora podemos compilar en SQL*plus.<br /><span style="color: rgb(0, 102, 0);"><br /></span><span style="color: rgb(0, 153, 0);font-family:courier new;" >SQL> @ob_proc.plb</span><br /><br /><span style="color: rgb(0, 102, 0);font-family:courier new;" >Procedure created.</span><br /><br />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.<br /><br /><span style="font-weight: bold;">Ejemplo 2</span><br /><br />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.<br /><br />Teniendo el cuerpo de nuestro paquete preparado en el archivo ob_pack_body.sql, ejecutamos:<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >wrap edebug=wrap_new_sql iname=ob_pack_body.sql</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >PL/SQL Wrapper: Release 9.2.0.8.0- Production on Thu Aug 13 11:38:30 2009</span><span style="font-family:courier new;"> </span><span style="color: rgb(0, 153, 0);font-family:courier new;" >Copyright (c) Oracle Corporation 1993, 2001. All Rights Reserved.</span> <span style="color: rgb(0, 153, 0);font-family:courier new;" ><br /><br />Processing ob_pack_</span><span style="color: rgb(0, 153, 0);font-family:courier new;" >body</span><span style="color: rgb(0, 153, 0);font-family:courier new;" >.sql to ob_pack_body.plb</span><br /><br />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.<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >SQL> @ob_pack_body.plb</span><br /><br /><span style="color: rgb(0, 102, 0);"><span style="color: rgb(0, 153, 0);font-family:courier new;" >Package body created.</span><br /><br /></span>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.<br /><br /><span style="font-weight: bold;">Ver también:</span><br /><a href="http://oraclenotepad.blogspot.com/2007/09/un-extractor-de-ddl-en-archivos.html">Cómo extraer código en archivos separados</a>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com1tag:blogger.com,1999:blog-6230959840389481677.post-52565084876120129442009-07-15T07:06:00.000-07:002009-07-29T08:07:06.846-07:00¿Cuál es el mejor tipo de datos para almacenar IPs?<span class="threadText" id="textNode_21641822">Esta pregunta surgió en <a target="_blank" href="http://wiki.oracle.com/thread">Oracle Wiki</a>, foro en el cual habitualmente colaboro.<br /><br /><span style="font-style: italic; color: rgb(51, 51, 51);">"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."</span><br /><br /></span><span class="threadText" id="textNode_21675989">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.<br />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.<br /><br /><span style="font-weight: bold;">Algunas formas de almacenar IPs</span><br /><br /></span><span class="threadText" id="textNode_21675989">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.</span><br /><span class="threadText" id="textNode_21675989"><br /><span style="font-weight: bold; color: rgb(0, 102, 0);">Notación de Punto</span><br />Es el típico formato de IP que todos conocemos.<br />Ejemplo: 127.0.0.1<br />Tipo de datos sugerido: VARCHAR2(15)<br />Ventajas: Fácil de leer y recuperar, listo para reportes relacionados a redes.<br />Desventajas: Requiere convertir para poder usar algebra booleana, no muy eficiente en uso de espacio.</span><br /><span class="threadText" id="textNode_21675989"><br /></span><span class="threadText" id="textNode_21675989"><span style="font-weight: bold; color: rgb(0, 102, 0);">Hexadecimal</span><br />Es el formato de IP removiendo los puntos y pasando cada componente a hexa.<br />Ejemplo: </span><span class="threadText" id="textNode_21675989">7F000001</span><br /><span class="threadText" id="textNode_21675989"> Tipo de datos sugerido: VARCHAR2(8)<br />Ventajas: Fácil de convertir a los otros modos, ocupa menos espacio que la notación de punto.<br />Desventajas: Requiere conversión para lectura y operaciones booleanas. </span><br /><span class="threadText" id="textNode_21675989"><br /><span style="font-weight: bold; color: rgb(0, 102, 0);">Decimal</span><br />Es la representación interna de la IP. Usado comúnmente por browsers y algunos protocolos, es la representacion decimal del formato binario.<br />Ejemplo: </span><span class="threadText" id="textNode_21675989">2130706433 (equivale a 127.0.0.1</span>)<br /><span class="threadText" id="textNode_21675989">Tipo de datos sugerido: NUMBER(10)<br />Ventajas: Uso de espacio muy optimizado, no requiere conversión para integración con algunos protocolos.<br />Desventajas: Es el formato más ilegible (para los humanos) de todos.<br /></span><span class="threadText" id="textNode_21675989"><br />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.</span> Hagan la prueba de convertir una IP y colocarla en el browser!<br /><span class="threadText" id="textNode_21675989"><br /><span style="font-weight: bold; color: rgb(0, 102, 0);">Notación Binaria</span><br />Podemos tomar cada número de la representación por punto y convertirlo a un octeto binario, completando con ceros a la izquierda.<br />Ejemplo: 01111111000000000000000000000001 (equivale a 127.0.0.1)<br />Tipo de datos sugerido: VARCHAR2(32)<br />Ventajas: Listo para algebra booleana, fácil de convertir a otros formatos.<br />Desventajas: Poco legible, uso muy ineficiente de espacio.<br /><br /><br /><span style="font-weight: bold;">Conclusiones</span><br /></span><br />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.<br /><ul><li><span class="threadText" id="textNode_21675989">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.<br /></span></li><li><span class="threadText" id="textNode_21675989">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.</span></li><li><span class="threadText" id="textNode_21675989">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.</span></li><li><span class="threadText" id="textNode_21675989">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.</span></li></ul>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com1tag:blogger.com,1999:blog-6230959840389481677.post-13403865789605618862009-07-09T13:20:00.000-07:002009-07-09T13:37:54.923-07:00Obtener totales con AWKHay veces que en nuestro trabajo somos solicitados para realizar un spool de datos (millones de registros) y finalmente informar el total de algunas columnas.<br /><br />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.<br /><br />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).<br /><br /><span style="font-weight: bold;">Ejemplo:</span> como obtener el total de la segunda y tercera columna de un archivo spool con millones de registros y valores separados por pipe '|'.<br /><br /><span style="color: rgb(0, 153, 0); font-family: courier new;">awk -F"|" '{sum1+=$2; sum2+=$3} END {print sum1,sum2}' archivo_entrada.csv</span><br /><br />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.<br /><br />Finalmente la sección de ejecución final muestra los valores de sum1 y sum2.<br /><br />Si deseamos usar otro delimitador bastará con alterar el parámetro -F.<br /><br />Así de simple y eficiente.lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com0tag:blogger.com,1999:blog-6230959840389481677.post-2810698553095933742009-06-12T06:49:00.000-07:002009-07-10T11:07:35.299-07:00Ordenar resultados por el alfabeto castellanoPor 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.<br /><br />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).<br /><br />Por ejemplo si listamos países, <span style="font-style: italic;">Chile</span> aparecerá antes que <span style="font-style: italic;">Colombia</span>, cuando la letra 'CH' debería suceder a la 'C'. Similarmente la palabra <span style="font-style: italic;">lluvia</span> aparecerá antes que <span style="font-style: italic;">lotería</span>, 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.<br /><br />Para ordenar usando las letras del español, bastará con alterar la sesión:<br /><br />ALTER SESSION SET nls_sort=spanish_m;<br /><br /><span style="font-weight: bold;">Ejemplo</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >SQL> select * from paises order by nombre;</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > </span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >NOMBRE</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >------------</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Camerún</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Canadá</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Chile</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Colombia</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Congo</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Cuba</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >SQL> alter session set nls_sort=spanish_m;</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > </span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Session altered</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >NOMBRE</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >------------</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Camerún</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Canadá</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Colombia</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Congo</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Cuba</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Chile<br /></span><br /><span style="font-weight: bold;">Acentos y mayúsculas</span><br /><br />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.<br /><br />Ejemplo 1: Problema de acentos<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >SQL> select letra from mialfabeto order by letra;</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >LETRA</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >--------</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >a</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >b</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >c</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >á</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >SQL> alter session set nls_sort=spanish_m;<br /><br /></span><span style="color: rgb(0, 153, 0);font-family:courier new;" >SQL> select letra from mialfabeto order by letra;</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > LETRA</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > --------</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > a</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >á</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >b</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > c</span><br /><br />Ejemplo 2: Problema de mayúsculas que anteceden a minúsculas<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >SQL> select texto from conceptos order by texto;</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > </span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >TEXTO</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >--------------</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Integrado</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >distribuído</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >integración</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >SQL> alter session set nls_sort=spanish_m;<br /><br /></span><span style="color: rgb(0, 153, 0);font-family:courier new;" >SQL> select texto from conceptos order by texto;</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" ><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" ></span></span><span style="color: rgb(0, 153, 0);font-family:courier new;" >TEXTO</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >--------------</span><span style="color: rgb(0, 153, 0);font-family:courier new;" ><span style="color: rgb(0, 153, 0);font-family:courier new;" ></span><span style="color: rgb(0, 153, 0);font-family:courier new;" ></span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >distribuído</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >integración</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Integrado</span><br /></span>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com4tag:blogger.com,1999:blog-6230959840389481677.post-6474326385216166882009-05-13T10:58:00.000-07:002009-05-13T13:12:34.986-07:00Un autodeadlock y algunas buenas prácticasEste 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!<br /><span style="font-weight: bold;"></span><br /><pre style="color: rgb(0, 153, 0);">CREATE OR REPLACE PROCEDURE deadlock_suicida AS<br />BEGIN<br />EXECUTE IMMEDIATE 'DROP PROCEDURE deadlock_suicida';<br />END;<br />/<br /><br />Procedure created.<br /><br />SQL> exec deadlock_suicida;</pre><span style="font-weight: bold;">Deadlock más comunes</span><br />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.<br /><br />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.<br /><br />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.<br /><br /><span style="font-weight: bold;">Cómo evitar deadlocks</span><br />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 <span style="font-style: italic;">$ORACLE_HOME/admin<sid>/udump</sid></span>.<br /><br /><span style="font-weight: bold;">Algunas buenas prácticas</span><br />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.<br />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.<br />3) Por ejemplo si vamos a actualizar tablas padre-hijo, podemos tomar la práctica de siempre bloquear primero la tabla padre.lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com1tag:blogger.com,1999:blog-6230959840389481677.post-89287029405819803452009-04-01T07:58:00.000-07:002009-04-22T09:08:06.887-07:00Cómo crear una vista parametrizadaEn 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.<br /><br /><span style="font-weight: bold;">¿Qué es un contexto de aplicación?</span><br />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.<br />El contexto puede ser definido tanto localmente (privado para cada sesión) como globalmente, compartiendo sus valores para todas las sesiones de la instancia.<br />Para poder alterar los valores del contexto, debemos crear un paquete especialmente autorizado para ese fin. Esto es un requerimiento por razones de seguridad.<br />También necesitamos tener el permiso especial de sistema <span style="font-style: italic;">CREATE ANY CONTEXT</span>.<br /><br /><span style="font-weight: bold;">Ejemplo </span><span style="font-size:85%;"> (con sqlplus)</span><br /><br />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.<br /><br /><span style="color: rgb(0, 0, 153);"><span style="font-weight: bold;">Paso 1:</span> Crear el contexto de aplicación</span><pre style="color: rgb(0, 153, 0);">CREATE CONTEXT app_ctx_scott USING pk_scott_app_context<br />/</pre><span style="color: rgb(0, 0, 153);"><span style="font-weight: bold;">Paso 2:</span> Crear el paquete para manipular el contexto</span><pre><span style="color: rgb(0, 153, 0);">CREATE OR REPLACE PACKAGE pk_scott_app_context AS</span><br /><span style="color: rgb(0, 153, 0);"> -- El contexto tendra un unico valor deptno</span><br /><span style="color: rgb(0, 153, 0);"> PROCEDURE set_dept (p_deptno IN NUMBER);</span><br /><span style="color: rgb(0, 153, 0);">END;</span><br /><span style="color: rgb(0, 153, 0);">/</span><br /><br /><span style="color: rgb(0, 153, 0);">CREATE OR REPLACE PACKAGE BODY pk_scott_app_context AS</span><br /><span style="color: rgb(0, 153, 0);"> PROCEDURE set_dept (p_deptno IN NUMBER) IS</span><br /><span style="color: rgb(0, 153, 0);"> BEGIN</span><br /><span style="color: rgb(0, 153, 0);"> DBMS_SESSION.SET_CONTEXT('app_ctx_scott', 'deptno', p_deptno);</span><br /><span style="color: rgb(0, 153, 0);"> END;</span><br /><span style="color: rgb(0, 153, 0);">END;</span><br /><span style="color: rgb(0, 153, 0);">/</span></pre><span style="color: rgb(0, 0, 153);"><span style="font-weight: bold;">Paso 3</span>: Setear el parámetro de contexto deptno para el departamento de ventas</span><pre style="color: rgb(0, 153, 0);">BEGIN<br />pk_scott_app_context.set_dept(30);<br />END;<br />/</pre><span style="color: rgb(0, 0, 153);"><span style="font-weight: bold;">Paso 4</span>: Crear la vista parametrizada</span><pre style="color: rgb(0, 153, 0);">CREATE VIEW empleados AS<br />SELECT e.empno, e.ename, e.job, d.dname<br />FROM emp e, dept d<br />WHERE e.deptno=d.deptno<br />AND d.deptno = sys_context('app_ctx_scott','deptno');</pre><span style="color: rgb(0, 0, 153);"><span style="font-weight: bold;">Paso 5</span>: Obtener los resultados consultando la vista<br /></span><pre style="color: rgb(0, 153, 0);">SELECT * FROM empleados;<br /><br /> EMPNO ENAME JOB DNAME<br />---------- ---------- --------- --------------<br /> 7499 ALLEN SALESMAN SALES<br /> 7521 WARD SALESMAN SALES<br /> 7654 MARTIN SALESMAN SALES<br /> 7698 BLAKE MANAGER SALES<br /> 7844 TURNER SALESMAN SALES<br /> 7900 JAMES CLERK SALES<br /><br />6 rows selected.</pre>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:<pre><span style="color: rgb(0, 153, 0);">BEGIN</span><br /><span style="color: rgb(0, 153, 0);"> pk_scott_app_context.set_dept(10);</span><br /><span style="color: rgb(0, 153, 0);">END;</span><br /><span style="color: rgb(0, 153, 0);">/</span><br /><br /><span><span style="color: rgb(0, 153, 0);">PL/SQL procedure successfully completed.<br /><br />SELECT * FROM empleados;</span></span><br /><br /><span style="color: rgb(0, 153, 0);"> EMPNO ENAME JOB DNAME</span><br /><span style="color: rgb(0, 153, 0);">---------- ---------- --------- --------------</span><br /><span style="color: rgb(0, 153, 0);"> 7782 CLARK MANAGER ACCOUNTING</span><br /><span style="color: rgb(0, 153, 0);"> 7839 KING PRESIDENT ACCOUNTING</span><br /><span style="color: rgb(0, 153, 0);"> 7934 MILLER CLERK ACCOUNTING</span><br /><br /><span style="color: rgb(0, 153, 0);">3 rows selected.</span></pre><br />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.lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com16tag:blogger.com,1999:blog-6230959840389481677.post-15570889255523948122009-03-01T01:58:00.000-08:002009-07-20T06:13:48.348-07:00Migrando MySQL a Oracle con SQL DeveloperSiempre que evaluamos herramientas para realizar esta tarea, debemos considerar variables tales como rapidez, facilidad de uso y bajo costo, entre otras.<br /><br />SQL Developer es un producto de costo cero, que permite migrar bases de datos MySQL, Access o SQL Server a Oracle con facilidad.<br />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.<br /><br /><span style="font-weight: bold;">Preparación del software</span><br /><br />- Descargar e instalar SQL Developer:<br />La última versión está disponible en <a target="_blank" href="http://otn.oracle.com/">OTN</a>.<br /><br />- Instalar el plugin de conexión a MySQL:<br />Para esto, desde SQL Developer vamos al menú <span style="font-style: italic;">Ayuda</span>, <span style="font-style: italic;">Verificar Actualizaciones</span>, marcamos la casilla <span style="font-style: italic;">Third Party SQL Developer Extensions</span> y descargamos el driver de conexión a MySQL. SQL Developer deberá ser reiniciado para que el plugin entre en efecto.<br /><br />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ú <span style="font-style: italic;">Ayuda</span>, <span style="font-style: italic;">Sobre</span>, <span style="font-style: italic;">Extensiones</span>, y verificamos que exista la entrada <span style="font-style: italic;">MySQL JDBC Driver</span>.<br /><br /><span style="font-weight: bold;">Crear un usuario para la migración</span><br /><br />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.<br /><br />Para crearlo, loguearse a sqlplus con el usuario system y ejecutar:<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >CREATE USER migration IDENTIFIED BY xxxxxxx DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp;</span> <span style="color: rgb(0, 153, 0);font-family:courier new;" >GRANT </span><span style="color: rgb(0, 153, 0);font-family:courier new;" >create session, create view, </span><span style="color: rgb(0, 153, 0);font-family:courier new;" >resource, create user, create role, alter any trigger TO migration WITH ADMIN OPTION;</span><br /><br /><span style="font-weight: bold;">Crear las conexiones</span><br /><br />- Crear una conexión para el esquema MySQL a ser migrado:<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEig1uszcypP93cq3XGkxWs798YRBc4ZyI-IFJ9gi8NS_yjY1uISasb5C6xGrRujpIAEl2BYjhzEACVM9rFcCHJn5Nzqi9JQsxul8OWr_mztNzv8ej4BrVLTb_BNxlBDVSUpZSZ0i56sTDQ/s1600-h/Conexion+MySQL.png"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 154px; height: 98px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEig1uszcypP93cq3XGkxWs798YRBc4ZyI-IFJ9gi8NS_yjY1uISasb5C6xGrRujpIAEl2BYjhzEACVM9rFcCHJn5Nzqi9JQsxul8OWr_mztNzv8ej4BrVLTb_BNxlBDVSUpZSZ0i56sTDQ/s320/Conexion+MySQL.png" alt="" id="BLOGGER_PHOTO_ID_5306400749886293298" border="0" /></a>Colocar un nombre para la conexión y el usuario MySQL (en el ejemplo será root, sin password). Luego seleccionar la lengueta <span style="font-style: italic;">MySQL</span> para verificar el hostname (o IP del servidor) y el puerto (generalmente 3306).<br />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 <span style="font-style: italic;">my.ini</span>.<br /><br />- Crear una conexión Oracle para el repositorio y el esquema destino:<br />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.<br />Como en el caso anterior, testear la conexión.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEip1K6Sy5izY8NdXsHUUFWSWkeAvPghLIToz5KasGnQNyNEyhz00seVWihrzR6eklxkMz1i6P6gBq-_1n_iotbvNYSPj4cqRyuAO8x3nXg7H42rg7JSMPoOUcy-d8riCwk8SygCRptcy1s/s1600-h/conexiones.png"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 110px; height: 66px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEip1K6Sy5izY8NdXsHUUFWSWkeAvPghLIToz5KasGnQNyNEyhz00seVWihrzR6eklxkMz1i6P6gBq-_1n_iotbvNYSPj4cqRyuAO8x3nXg7H42rg7JSMPoOUcy-d8riCwk8SygCRptcy1s/s320/conexiones.png" alt="" id="BLOGGER_PHOTO_ID_5306401461903643202" border="0" /></a>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.<br /><br /><br /><br />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.<br /><br /><span style="font-weight: bold;">Crear un repositorio</span><br /><br />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.<br /><br />Vamos a la conexión de Oracle recientemente creada y con el botón derecho seleccionamos <span style="font-style: italic;">Migration Repository</span>, y <span style="font-style: italic;">Associate Migration Repository</span>.<br /><br />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.<br /><br /><span style="font-weight: bold;">Iniciar el asistente</span><br /><br />Teniendo todos los pasos previos completados, comenzamos con la migración propiamente dicha. Vamos al menú <span style="font-style: italic;">Migration</span>, <span style="font-style: italic;">Quick Migrate</span>, y seleccionamos la conexión de la base de datos orígen, es decir MySQL.<br /><br />Si nuestra conexión fue correctamente creada, nos pide la conexión destino. Colocaremos la conexión XE creada para recibir los objetos.<br /><br />El siguiente paso es la verificación del repositorio a utilizar. Debe aparecer el mensaje OK: Using [nombre conexión].<br /><br />El cuarto paso es la verificación de pre-requisitos. Luego de presionar el botón <span style="font-style: italic;">Verify,</span><span> una serie de tests son ejecutados, que incluyen conectividad de fuente y destino, y permisos de usuario</span>. Cualquier observación que Oracle levante aquí tiene que ser resuelta antes de poder avanzar.<br /><br />Luego que todos los pasos de la verificación de pre-requisitos resultan en SUCCESS, seguimos.<br /><br />Finalmente debemos elegir el tipo de migración entre MIGRATE TABLES ONLY, MIGRATE TABLES AND DATA o MIGRATE EVERYTHING.<br /><br />En este caso elegiremos migrate everything: usuarios, tablas, datos, índices, constraints, vistas y código.<br /><br />Al presionar <span style="font-style: italic;">Finalizar</span>, cruzaremos los dedos.<br /><br /><span style="font-weight: bold;">Resultados de la migración</span><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt7XanQzj2Lt8kmCo8rlCDljGBBrRiTED1l3DjrgBKwYFIx8ZaiCGSCgK2vz_CSRQTWr6OY9Mjs6O3srTn_gluvbDTOlMY96xYxj48TNpIwYNI_sh7VEjQFyg2Rt-YjQjauQZyqkFckVU/s1600-h/migration_summary.png"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 217px; height: 197px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt7XanQzj2Lt8kmCo8rlCDljGBBrRiTED1l3DjrgBKwYFIx8ZaiCGSCgK2vz_CSRQTWr6OY9Mjs6O3srTn_gluvbDTOlMY96xYxj48TNpIwYNI_sh7VEjQFyg2Rt-YjQjauQZyqkFckVU/s320/migration_summary.png" alt="" id="BLOGGER_PHOTO_ID_5307469840766977266" border="0" /></a>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í!<br /><br />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.<br /><br />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).<br /><br />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.<br /><br />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.<br /><br /><span style="font-weight: bold;">Tareas post-migración</span><br /><br />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.<br /><br />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.<br />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.<br /><br />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.<br /><br />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.<br /><br />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.<br /><br /><span style="font-weight: bold;">En conclusión</span><br /><br />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.lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com1tag:blogger.com,1999:blog-6230959840389481677.post-43936962388147084832009-02-20T06:14:00.000-08:002009-05-28T05:35:05.132-07:00Soporte Oracle con VMWare?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.<br /><br />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.<br /><br />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.lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com1tag:blogger.com,1999:blog-6230959840389481677.post-13406515923357048372009-02-05T10:00:00.000-08:002009-02-20T07:14:03.708-08:00Error al crear usuario en Oracle TimesTenCuando se intenta crear un usuario en Oracle TimesTen, se obtiene el siguiente error:<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Command> create user lferTTadmin identified by '$ql450';</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >15007: Access control not enabled</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >The command failed.</span><br /><br />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'.<br /><br />Abrir una consola del sistema operativo, y ejecutar:<br />ttmodinstall -enableAccessControl<br /><br />Luego de eso, el control de acceso estará habilitado.<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >C:\Windows\system32>ttmodinstall -enableAccessControl</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >C:\Windows\system32>"C:\TimesTen\tt70_32\perl\bin\perl.exe" "C:\TimesTen\tt70_32</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >\bin\ttmodinstall" -enableAccessControl</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Would you like to enable access control for this instance? [ no ] yes</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >NOTE: The daemon must be stopped before enabling access control.</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Would you like to stop the daemon? [ yes ] yes</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >The TimesTen Data Manager 7.0 service is stopping...</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >The TimesTen Data Manager 7.0 service was stopped successfully.</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Patching successful ...</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Restarting the daemon ...</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >The TimesTen Data Manager 7.0 service is starting.</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >The TimesTen Data Manager 7.0 service was started successfully.</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Access control is now enabled for this TimesTen instance.</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >C:\Windows\system32>ttisql TT_test</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Copyright (c) 1996-2008, Oracle. All rights reserved.</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Type ? or "help" for help, type "exit" to quit ttIsql.</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >All commands must end with a semicolon character.</span><br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >connect "DSN=TT_dns_prod04";</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Connection successful: DSN=TT_test;UID=sixbell;DataStore=C:\Users\dashboard\Deskto</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >p\temp\TT_store;DatabaseCharacterSet=AL32UTF8;ConnectionCharacterSet=AL32UTF8;DR</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >IVER=C:\TimesTen\tt70_32\bin\ttdv70.dll;TypeMode=0;</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >(Default setting AutoCommit=1)</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Command> create user lferTTadmin identified by '</span><span style="color: rgb(0, 153, 0);font-family:courier new;" >$ql450</span><span style="color: rgb(0, 153, 0);font-family:courier new;" >';</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >Command></span>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com1tag:blogger.com,1999:blog-6230959840389481677.post-16286175530407850062009-02-05T06:22:00.000-08:002009-02-20T07:14:36.244-08:00Parches de seguridad Enero 2009<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEico896Jn_1hg4HZh2KRd5EASi5hzw1sIMQwHCqOO8PeBNWizV917u_KDJiluATfdgICM7a2xy3vTiDvvZmDK5PrPFr-MK12lF4s2DVfVnyPMC45u8w6Up2FINVBbwdBJl3L4ZiOzLiEkA/s1600-h/alerta.JPG"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 77px; height: 112px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEico896Jn_1hg4HZh2KRd5EASi5hzw1sIMQwHCqOO8PeBNWizV917u_KDJiluATfdgICM7a2xy3vTiDvvZmDK5PrPFr-MK12lF4s2DVfVnyPMC45u8w6Up2FINVBbwdBJl3L4ZiOzLiEkA/s320/alerta.JPG" alt="" id="BLOGGER_PHOTO_ID_5299319003638030274" border="0" /></a>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.<br />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).<br />Desde luego que el download está disponible en OTN.<br /><br /><span style="font-weight: bold;">Ver también</span><br /><a target="_blank" href="http://www.oracle.com/technology/deploy/security/critical-patch-updates/cpujan2009.html">Critical Patch Update Advisory - January 2009</a>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com0tag:blogger.com,1999:blog-6230959840389481677.post-81048812777914894832009-02-04T11:38:00.000-08:002009-02-20T07:16:15.235-08:00Cómo pasar valores desde sqlplus hacia Unix ShellHay 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?<br /><br />Tenemos dos enfoques para capturar valores luego de terminada la ejecución de Sql*plus: por archivos y por variables.<br /><br />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.<br /><br />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.<br /><br /><span style="font-weight: bold;">Resultado único</span><br />Supongamos que quiero leer el contenido de la columna Dummy de la tabla Dual y continuar manipulando el valor en Shell.<br /><br />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.<br /><pre><br /><span style="color: rgb(0, 153, 0);">#!/bin/ksh</span><br /><span style="color: rgb(0, 153, 0);">resultado=`sqlplus -s 'scott/tiger' << EOF<br /><span style="color: rgb(0, 153, 0);">set serveroutput on</span><br /><span style="color: rgb(0, 153, 0);">set feedback off</span><br /><span style="color: rgb(0, 153, 0);">set head off</span><br /><span style="color: rgb(0, 153, 0);">select * from dual;</span><br /><span style="color: rgb(0, 153, 0);">exit;</span><br /><span style="color: rgb(0, 153, 0);">EOF`</span><br /><br /><span style="color: rgb(0, 153, 0);">echo "El resultado es: $resultado"</span></span></pre><span style="font-family:monospace;"><br /></span><span style="font-weight: bold;">Varios resultados en una línea</span><br />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.<br /><pre><span style="color: rgb(0, 153, 0);">#!/bin/ksh</span><br /><span style="color: rgb(0, 153, 0);">resultado=`sqlplus -s 'scott/tiger' << EOF<br /><span style="color: rgb(0, 153, 0);">set serveroutput on</span><br /><span style="color: rgb(0, 153, 0);">set feedback off</span><br /><span style="color: rgb(0, 153, 0);">set head off</span><br /><span style="color: rgb(0, 153, 0);">select user||';'||sysdate</span><span><span style="color: rgb(0, 153, 0);">||';'||</span></span><span style="color: rgb(0, 153, 0);">dummy from dual;</span><br /><span style="color: rgb(0, 153, 0);">exit;</span><br /><span style="color: rgb(0, 153, 0);">EOF`</span><br /><br /><span style="color: rgb(0, 153, 0);">echo "Los valores son: $resultado"</span></span></pre>El valor retornado es: <span style="color: rgb(0, 153, 0);font-family:courier new;" >SCOTT;05-FEB-09;X</span><br /><br />Con el comando cut separamos los valores fácilmente:<br /><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >echo $resultado | cut -d';' -f1</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >SCOTT</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" > echo $resultado | cut -d';' -f2</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >05-FEB-09</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >echo $resultado | cut -d';' -f3</span><br /><span style="color: rgb(0, 153, 0);font-family:courier new;" >X</span><br /><br /><span style="font-weight: bold;">Resultados multilínea</span><br />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.<br /><span style="font-family:monospace;"><br /><span style="color: rgb(0, 153, 0);">#!/bin/ksh</span><br /><span style="color: rgb(0, 153, 0);">resultado=`sqlplus -s 'scott/tiger' << EOF<br /><span style="color: rgb(0, 153, 0);">set serveroutput on</span><br /><span style="color: rgb(0, 153, 0);">set feedback off</span><br /><span style="color: rgb(0, 153, 0);">set head off<br />set linesize 131<br />set pagesize 9999<br /></span><br /><span style="color: rgb(0, 153, 0);">select empno||';'||ename||';'||job||';'||mgr||';'||deptno from emp;</span><br /><span style="color: rgb(0, 153, 0);">exit;</span><br /><span style="color: rgb(0, 153, 0);">EOF`</span><br /><br />echo "El resultado es: $resultado"</span></span><br /><br />La salida en este caso es:<br /><span style="color: rgb(0, 153, 0);font-family:courier new;" ><br />7369;SMITH;CLERK;7902;20<br />7499;ALLEN;SALESMAN;7698;30<br />7521;WARD;SALESMAN;7698;30<br />7566;JONES;MANAGER;7839;20<br />7654;MARTIN;SALESMAN;7698;30<br />7698;BLAKE;MANAGER;7839;30<br />7782;CLARK;MANAGER;7839;10<br />7788;SCOTT;ANALYST;7566;20<br />7839;KING;PRESIDENT;;10<br />7844;TURNER;SALESMAN;7698;30<br />7876;ADAMS;CLERK;7788;20<br />7900;JAMES;CLERK;7698;30<br />7902;FORD;ANALYST;7566;20</span><br /><br />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.<br />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.<br />En el siguiente ejemplo, vemos como awk toma línea a línea e imprime un texto anexo.<br /><br /><span style="color: rgb(0, 153, 0);"><span style="font-family:courier new;">echo "$resultado" | awk -F";" 'BEGIN {$cnt=1}</span><br /><span style="font-family:courier new;"> {print "Linea "$cnt, $0; $cnt=$cnt+1;}'</span></span><br /><br />La salida generada por awk es:<br /><span style="font-family:monospace;"><br /><span style="color: rgb(0, 153, 0);">Linea 1 7369;SMITH;CLERK;7902;20</span><br /><span style="color: rgb(0, 153, 0);">Linea 2 7499;ALLEN;SALESMAN;7698;30</span><br /><span style="color: rgb(0, 153, 0);">Linea 3 7521;WARD;SALESMAN;7698;30</span><br /><span style="color: rgb(0, 153, 0);">Linea 4 7566;JONES;MANAGER;7839;20</span><br /><span style="color: rgb(0, 153, 0);">Linea 5 7654;MARTIN;SALESMAN;7698;30</span><br /><span style="color: rgb(0, 153, 0);">Linea 6 7698;BLAKE;MANAGER;7839;30</span><br /><span style="color: rgb(0, 153, 0);">Linea 7 7782;CLARK;MANAGER;7839;10</span><br /><span style="color: rgb(0, 153, 0);">Linea 8 7788;SCOTT;ANALYST;7566;20</span><br /><span style="color: rgb(0, 153, 0);">Linea 9 7839;KING;PRESIDENT;;10</span><br /><span style="color: rgb(0, 153, 0);">Linea 10 7844;TURNER;SALESMAN;7698;30</span><br /><span style="color: rgb(0, 153, 0);">Linea 11 7876;ADAMS;CLERK;7788;20</span><br /><span style="color: rgb(0, 153, 0);">Linea 12 7900;JAMES;CLERK;7698;30</span><br /><span style="color: rgb(0, 153, 0);">Linea 13 7902;FORD;ANALYST;7566;20</span><br /><span style="color: rgb(0, 153, 0);">Linea 14 7934;MILLER;CLERK;7782;10</span><br /></span>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com13tag:blogger.com,1999:blog-6230959840389481677.post-89965067414204081222009-01-30T04:34:00.000-08:002010-03-04T11:17:44.746-08:00Oracle SQL Developer Data ModellingLos 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.<br /><br />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.<br /><br />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.<br /><br />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.<br />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.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ963Pu20S1czISV3iYfCORJYlnr8FTAI0WhQLM5NCPQ2yKx2FnASVso-ZYiKbaWh5W04GWKPjJfx6tQuOyzD3ZE81SGjenGhcMVJNyf_51JllFrSN_8HApGYxgqywc5KcZtUZbu0-gcw/s1600-h/logical_model.png"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 246px; height: 147px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ963Pu20S1czISV3iYfCORJYlnr8FTAI0WhQLM5NCPQ2yKx2FnASVso-ZYiKbaWh5W04GWKPjJfx6tQuOyzD3ZE81SGjenGhcMVJNyf_51JllFrSN_8HApGYxgqywc5KcZtUZbu0-gcw/s320/logical_model.png" alt="" id="BLOGGER_PHOTO_ID_5297095822975332002" border="0" /></a>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.<br />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}.<br />El Navigator (pequeño cuadro que muestra un mapa de todo el modelo) es otra buena innovación que aporta navegabilidad en diagramas grandes.<br />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.<br />También cuenta con un analizador de impacto, para anticipar la propagación de cambios en el modelo existente.<br />La integración por ODBC permite conectarse con prácticamente cualquier base de datos existente.<br />La mayor ventaja de todas es, como dije anteriormente, que es gratuito.<br /><br />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.<br />La versión <span style="font-style: italic;">Early Adopter Release 2</span> (Nov 2008) está disponible para descargar en OTN.<br /><br /><span style="font-weight: bold;">ACTUALIZACIÓN </span>(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.<br /><br /><span style="font-weight: bold;">Ver también:</span><br /><a target="_blank" href="http://www.oracle.com/technology/products/database/sql_developer/index.html">OTN SQL Developer</a>lferhttp://www.blogger.com/profile/00618392085702183279noreply@blogger.com2