15 octubre 2012

Superar la limitación de los N caracteres con el paquete DBMS_OUTPUT.PUT_LINE


 Si hablamos de versiones de de Oracle Database anteriores a 10g R2 
Oracle facilitó el paquete DBMS_OUTPUT para los desarrolladores de PL/SQL. La función PUT_LINE() es la característica más utilizada por los desarrolladores Oracle. PUT_LINE se usa para mostrar resultados en la pantalla o para mostrar resultados en el spool y sacarlo a un archivo. Pero esta función tiene un límite solo permite 255 caracteres y si te pasas se provoca una excepción PL/SQL.
declare
   v_str varchar2(1000); 
begin 
   for i in 1..275 
   loop 
      v_str := v_str || TO_CHAR(i); 
   end loop; 
   dbms_output.put_line( v_str);
end;
/
Si ejecutamos el código en un motor de base de datos anterior a Oracle 10g R2, nos devuelve el error: 

ORA-20000: ORU-10028: line length overflow, limit of 255 chars per line.

Para solventar este problema podremos crear un procedimiento almacenado que sobrescriba el comportamiento de esta función de tal manera que divida la información en paquetes de 255 caracteres y la una en la salida.

create or replace procedure rafa.custom_output(in_string in varchar2 )
is 
   out_string_in long default in_string; 
   str_len number; 
   loop_count number default 0; 
begin 
   str_len := length(out_string_in);
   while loop_count < str_len
   loop 
      dbms_output.put_line( substr( out_string_in, loop_count +1, 255 ) ); 
      loop_count := loop_count +255; 
   end loop; 
end;

Si ejecutamos este código en nuestro motor de versión anterior a Oracle 10g R2, no dará el error.

declare
   v_str varchar2(1000); 
begin 
   for i in 1..275 
   loop 
      v_str := v_str || TO_CHAR(i); 
   end loop; 
   rafa.custom_output( v_str);
end; 
/
Si hablamos de versiones de de Oracle Database posteriores a 10g R2 Oracle amplió el tamaño de los caracteres a 32,767 bytes (el mismo límite superior que tiene otro paquete UTL_FILE). Este código ya no da ningún error para versiones del motor 10g R2 y 11g.

set linesize 64
BEGIN
DBMS_OUTPUT.PUT_LINE( RPAD('x',255,'x') );
END;
/

Si ejecutamos este código en un motor Oracle 10g R2 o superior, dará el siguiente error:

BEGIN
DBMS_OUTPUT.PUT_LINE( RPAD('$',32767,'$') || 'incluso mas...' );
END;
/
BEGIN
*
ERROR at line 1:
ORA-20000: ORU-10028: line length overflow, limit of 32767
bytes per line
ORA-06512: at "SYS.DBMS_OUTPUT", line 32
ORA-06512: at "SYS.DBMS_OUTPUT", line 91
ORA-06512: at "SYS.DBMS_OUTPUT", line 112
ORA-06512: at line 2

SQL> set serveroutput on size unlimited 
SQL> show serveroutput
serveroutput ON SIZE UNLIMITED FORMAT WORD_WRAPPED
Para ver que la opción ilimitada funciona, intentaremos sacar una salida superior al límite del buffer para esta opción (1000000 bytes). El siguiente ejemplo mostrará 1048544 bytes.

SQL> set lines 100
 
SQL> BEGIN
  2     FOR i IN 1 .. ((1000000/32767)+1) LOOP
  3        DBMS_OUTPUT.PUT_LINE( RPAD('$',32767,'$') );
  4     END LOOP;
  5  END;
  6  /
 
PL/SQL procedure successfully completed.
Eso si si abusamos de este código saldrá este error entre otro, así que cuidado con los experimentos, amiguitos: 
ORA-04030: out of process memory when trying to allocate 4120 bytes.

14 octubre 2012

TIP: SQL y fechas; Aquellos maravillosos años..



Si recordáis aquellas consultas megalíticas de los informes que nos hacían hacer con Oracle Reports o con otra herramienta arcana, cuando te pedían informes por trimestre (o “Quarters” si tenías un analista del tipo “Tom Cruise-Wannabe”), me echaba a temblar. Aquí hay una serie de consultas que a lo mejor sirven para algo, nunca se sabe.


  • Número de mes en el trimestre

SQL>select trunc(to_char(sysdate,'mm')/to_char(sysdate,'Q')) fecha from dual;

  •  Número de semana en el trimestre

SQL>select 1 + TRUNC((sysdate - TRUNC(sysdate, 'Q'))/7) fecha from dual; 

  • Número de día en el trimestre

SQL>select trunc(sysdate-trunc(sysdate,'Q'))+1 fecha from dual;

  • ¿La fecha seleccionada es fin de semana?

Tenemos esta tabla Prueba con estos datos y queremos saber si la fecha de inicio cae en fin de semana.
ID
DESCP
FEC_INICIO
FEC_FIN
1
prueba1
02/10/2012

2
prueba2
03/10/2012

3
prueba3
04/10/2012

4
prueba4
06/10/2012


Esta consulta nos devuelve el indicador de fila que se ha creado en fin de semana: 

SQL>Select id FROM rafa.pruebas WHERE MOD(TO_CHAR(fec_inicio, 'J'), 7) + 1 IN (6, 7);
               

 


11 octubre 2012

¿Hay vida más allá de Oracle? Qué memoria dispongo, en cada plataforma



En una instalación de base de datos Oracle nos podemos encontrar multitud de plataformas de lo más heterogéneas, siempre es bueno tener a mano, los comandos para saber de que memoria disponemos, he aquí un resumen.



Según me encuentre con mas sistemas iré ampliando la tabla.

 

04 octubre 2012

Oracle Streams: Montar un “Audit Vault”, para un país de recortes

 
iNTRODUCCIÓN:
Casi todos los miembros del mundillo de los administradores de bases de datos Oracle sabemos más o menos habilitar la auditoría. Pero para contentar, a esos seres siniestros, que son los auditores (entre lo que me he encontrado) Uno de los principales requerimientos es que la colección de datos resultantes de la auditoría han de ser almacenados, pero no en la base datos original.
Si estás en una organización con dinero suficiente para abordar un proyecto de auditoría que cuente con Oracle Audit Vault, deja de seguir leyendo el resto del artículo, no te quiero hacer perder el tiempo.


Se puede llegar a tener una infraestructura similar a la del Oracle Audit Vault usando Oracle Streams, este artículo está basado en una nota de Oracle Support: Nota.316893.1: DML and DDL Auditing Using Streams.

Oracle Audit Vault, ya utiliza esta tecnología por debajo, para realizar la descarga de los datos de auditoria y cargarlos en su propio repositorio de base de datos.


Oracle Streams proporciona un conjunto de elementos destinados a facilitar la captura, la puesta en escena, y el consumo de los eventos dentro de la Base de datos Oracle. Estos eventos incluyen tanto los mensajes en cola en una cola de base de datos por las aplicaciones, así como modificaciones, (ya sean datos o la estructura o ambos) a los objetos de base de datos, como tablas o procedimientos.

Figura:  Flujo de información enOracle Stream.

SQL Injection, This shit happens!!


Adaptación de la tira encontrada en este blog: http://www.kelloggsdba.blogspot.com.es/2012/05/sanity-lost-data-sanitization.html

24 septiembre 2012

Ajustes de Rendimiento:Detectar sentencias SQL no compartidas en la Shared Pool




Una de las maneras de aumentar la escalabilidad y el rendimiento de una aplicación, consiste en velar el número de sentencias SQL que están en la Shared Pool, región de la SGA (Área Global del Sistema) es una estructura básica de memoria de Oracle que sirve para facilitar la transferencia de información entre usuarios y también almacena la información estructural de la base de datos, más frecuentemente requerida. En versiones anteriores a 10g de la vista V_$SQL_SHARED_CURSOR ayudaría a diagnosticar por qué sentencias SQL no estaban siendo compartidas. En la versión 10g de a base de datos hay un evento llamado CURSORTRACE que ayuda en el mismo.
 
Área de SQL Compartido, Shared SQL Pool
En esta zona se encuentran las sentencias SQL que han sido analizadas. El análisis sintáctico de las sentencias SQL lleva su tiempo y Oracle mantiene las estructuras asociadas a cada sentencia SQL analizada durante el tiempo que pueda para ver si puede reutilizarlas.
Antes de analizar una sentencia SQL, Oracle mira a ver si encuentra otra sentencia exactamente igual en la zona de SQL compartido. Si es así, no la analiza y pasa directamente a ejecutar la que mantiene en memoria. De esta manera se premia la uniformidad en la programación de las aplicaciones. La igualdad se entiende que es lexicográfica, espacios en blanco y variables incluidas.
La base de datos Oracle asigna memoria a la shared pool cuando una nueva instrucción sql se analiza. El tamaño de esta memoria depende de la complejidad de la instrucción. Si toda la shared pool ya ha sido asignada la base de datos Oracle puede liberar elementos de la shared pool hasta que haya suficiente espacio libre para nuevas sentencias. Al liberar un elemento de la shared pool el sql asociado debe ser recompilado y reasignado a otra área de sql compartida la próxima vez que se ejecute.



El contenido de la zona de SQL compartido es:

  • Las sentencias SQL y PL/SQL (texto de la sentencia
  • Plan de ejecución de la sentencia SQL.
  • Lista de objetos referenciados.

Los pasos de procesamiento de cada petición de análisis de una sentencia SQL son:

  • Comprobar si la sentencia se encuentra en el área compartida.
  • Comprobar si los objetos son los mismos
  • Comprobar si el usuario tiene acceso a dichos objetos. En caso negativo, la sentencia es nueva, se analiza y los datos de análisis se almacenan de nuevo en la zona de SQL compartida.

Creamos un usuario Dummy para ilustrar nuestra explicación con un ejemplo:


SQL> create user dummy identified by dummy;
Usuario creado.
SQL> create table dummy.t (col1 varchar2(20) not null);
Tabla creada. 

Una vez creadas las tablas que nos servirán de ejemplo. Realizamos una consulta sobre la tabla creada, a fin de que quede reflejada en la Shared Pool, en la sesión de sqlplus #1.

Conectado a:

Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production

With the Partitioning, OLAP, Data Mining and Real Application Testing options 
SQL> select count(*) from dummy.t;
 
  COUNT(*)
----------
         0



SQL> select sql_text, hash_value,address from v$sqlarea where sql_text like 'select count(*) from dummy.t%';

SQL_TEXT                      HASH_VALUE ADDRESS
------------                  ---------- --------
select count(*) from dummy.t  2598640170 261A0758


SQL>  select * from v$sql_shared_cursor where address = '261A0758';

SQL_ID        ADDRESS  CHILD_AD CHILD_NUMBER U S O O S L F E B P I S T A B D L T

------------- -------- -------- ------------ - - - - - - - - - - - - - - - - - -

B I I R L I O E M U T N F A I T D L D B P C S C P T M B M R O P M F L P L A F L
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
R L H P B
- - - - -
fsy6q4ydf86ja 261A0758 261A04C4            0 N N N N N N N N N N N N N N N N N N

N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N
N N N N N
 
 Vemos el primer "hijo" de la sentencia SQL ejecutada

En la sesión de sqlplus #2
Conectado a:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> alter session set optimizer_mode='RULE';

Sesión modificada.

SQL> select count(*) from dummy.t;

  COUNT(*)
----------
         0

SQL>  select * from v$sql_shared_cursor where address = '261A0758';

SQL_ID        ADDRESS  CHILD_AD CHILD_NUMBER U S O O S L F E B P I S T A B D L T

------------- -------- -------- ------------ - - - - - - - - - - - - - - - - - -

B I I R L I O E M U T N F A I T D L D B P C S C P T M B M R O P M F L P L A F L
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
R L H P B
- - - - -
fsy6q4ydf86ja 261A0758 261A04C4            0 N N N N N N N N N N N N N N N N N N

N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N
N N N N N

fsy6q4ydf86ja 261A0758 2F626944            1 N N N N N N N N N N N N N N N N N N

N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N Y N N N N N N N N N
N N N N N

SQL_ID        ADDRESS  CHILD_AD CHILD_NUMBER U S O O S L F E B P I S T A B D L T

------------- -------- -------- ------------ - - - - - - - - - - - - - - - - - -

B I I R L I O E M U T N F A I T D L D B P C S C P T M B M R O P M F L P L A F L
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
R L H P B
- - - - -


SQL> alter session set events 'immediate trace name cursortrace level 612, address 2598640170';

Sesi¾n modificada.

SQL> alter session set optimizer_mode='FIRST_ROWS_1';

Session altered.

SQL> select count(*) from dummy.t;

  COUNT(*)
----------
         0

 
Esta es una porción del fichero de trazas, que arroja luz de porqué la consulta no está compartida en memoria:
*************************************************************************
PARSING SQLTEXT=select count(*) from dummy.t
SQLHASH=2d5ea7a8
Checking for already pinned child.
No valid child pinned
Parent 1EA853E8(261A0758) ready for search
kksSearchChildList outside while loop
kksCheckCursor: next child is #1
kksCheckCursor: pinning child #1 in shared mode 1eb62650 1ba90164
Compilation environment difference Failed sharing : 0
optimizer_mode = first_rows_1 rule
sqlstat_enabled = false true
SQL pgadep:0 pgapls:0 user
Failed sharing : Compilation environment mismatch
kksUnlockChild: releasing child
*************************************************************************