Archivo de la categoría: SQL

Generar SH1 de una cadena

El paquete DBMS_CRYPTO nos permite, entre otras cosas, generar SHA1 de cualquier cadena de texto. Admite los siguientes parámetros:

  • src – [RAW|BLOB|CLOB]: datos a los que aplicar el hash
  • typ – PLS_INTEGER: algoritmo hash a aplicar

Los algoritmos disponibles son los siguientes:

  • HASH_MD4 := 1
  • HASH_MD5 := 2
  • HASH_SH1 := 3

Nota: Es necesario conceder permisos de ejecución sobre este paquete a los usuarios que vayan a utilizarlo.

select DBMS_CRYPTO.hash(utl_raw.cast_to_raw ('Hello'),
                        3) "hash_Hello",
       DBMS_CRYPTO.hash(utl_raw.cast_to_raw ('HELLO'),
                        3) "hash_HELLO"
from dual;
hash_Hello
----------------------------------------
F7FF9E8B7BB2E09B70935A5D785E0CC5D9D0ABF0

hash_HELLO
----------------------------------------
C65F99F8C5376ADADDDC46D5CBCF5762F9E55EB7

Ver también Generar el HASH de una cadena

Generar el HASH de una cadena

El paquete DBMS_UTILITY nos permite, entre otras cosas, generar resúmenes HASH de cualquier cadena de texto. Admite los siguientes parámetros:

  • name – VARCHAR2: cadena a la que se aplica la función hash
  • base – NUMBER: valor mínimo que devolverá la función hash
  • hash_size – NUMBER: valor máximo que devolverá la función hash
select DBMS_UTILITY.get_hash_value('Hello',
                                   1,
                                   power(2,16)-1
                                  ) "hash_Hello",
       DBMS_UTILITY.get_hash_value('HELLO',
                                   1,
                                   power(2,16)-1
                                  ) "hash_HELLO"
from dual;
hash_Hello hash_HELLO
---------- ----------
     11511      16086

Ver también Generar SH1 de una cadena

Generar valores RANDOM

El paquete DBMS_RANDOM nos permite, entre otras cosas, generar números aleatorios indicando un rango. Admite los siguientes parámetros, que pueden ser omitidos:

  • low – NUMBER: valor mínimo que devolverá la función random
  • high – NUMBER: valor máximo que devolverá la función random
select DBMS_RANDOM.value nums_decim,
       DBMS_RANDOM.value(1000,
                         9999) nums_range_decim,
       trunc(DBMS_RANDOM.value(1000,
                               9999)) nums_range_int
from dual;
NUMS_DECIM NUMS_RANGE_DECIM NUMS_RANGE_INT
---------- ---------------- --------------
0,76259616 8188,26474538092           3750

Búsquedas en columnas de tipo LONG

Cuando intentamos hacer una búsqueda sobre una columna de tipo LONG del mismo modo que operamos con columnas VARCHAR nos encontramos con el problema de que no se comportan del mismo modo. Y el problema es más común de lo que a priori se puede esperar dado que varias vistas del diccionario de datos utilizan este tipo de datos para almacenar información bastante interesante.

En el caso que nos ocupa, estábamos intentando localizar el uso de una columna concreta en cualquier vista accesible por el usuario. Cuando intentamos consultar la vista ALL_VIEWS para localizar en ALL_VIEWS.TEXT las menciones a la columna que buscábamos nos encontramos con el siguiente error:

select *
from all_views
where upper(text) like '%TABLE_NAME%';
ORA-00932: inconsistent datatypes: expected NUMBER got LONG

La función UPPER() es una más de las muchas que no se pueden aplicar sobre tipos de datos LONG.

Después de darle varias vueltas al problema, e intentar distintas alternativas, logramos escribir un pequeño bloque anónimo que nos permitía realizar la búsqueda:

-- Search in LONG datatype columns
set serveroutput on;
begin
    for i in (select * from all_views) loop
        if instr(upper(i.text), 'TABLE_NAME') > 0 then
            dbms_output.put_line('View: '||i.view_name);
        end if;
    end loop;
end;

Y por fin obtuvimos el resultado esperado:

View: ALL_ALL_TABLES
View: ALL_TAB_COLUMNS
View: ALL_TAB_COL_STATISTICS
...
...
...

ORA-00060 Deadlock – Index on Foreign Key (FK) Constraint

Los errores por ORA-00060 Deadlock, como siempre nos recuerdan desde soporte de Oracle, no son errores de la base de datos propiamente dichos. Son errores provocados por una mala implementación de código.

El propio soporte de Oracle ofrece bastantes recursos y documentación para localizar el origen de estos errores y poder aplicar las correcciones necesarias a nuestra base de datos.

En el caso que me ocupa, se detectó una disminución del rendimiento de una base de datos por interbloqueos. Es decir: ORA-00060 Deadlock.

Después de consultar distintos documentos en soporte Oracle y varias fuentes en Internet, se localizó el origen de los interbloqueos y se aplicaron las medidas correctivas pertinentes. Los pasos que seguimos fueron los siguientes.

A través de soporte Oracle se localizó el documento HOWTO “How to Identify ORA-00060 Deadlock Types Using Deadlock Graphs in Trace” (Doc ID 1507093.1). En este documento nos ayudan a interpretar las trazas que se generan cada vez que Oracle detecta un interbloqueo o deadlock. La tipología que se presentaba en nuestro caso era la siguiente:

deadlock_graph

Este extracto contiene la información más importante del fichero de traza a la hora de catalogar el deadlock y buscar la solución más conveniente. Hay que prestar especial atención a las columnas marcadas en amarillo:

deadlock_graph-particular_characteristics

Como puede apreciarse, nuestro caso correspondía a la tipología “TM SX SSX SX SSX” que indica la necesidad de generar índices para apoyar la verificación de claves externas. Especialmente cuando se producen muchas operaciones de inserción y actualización en lotes.

Bueno, pues una vez identificado el problema teníamos que buscar la solución más conveniente. ¡Manos a la obra!. En primer lugar teníamos que localizar los índices que era necesario crear y lanzar una batería de pruebas que confirmara el cese de los interbloqueos. Gracias al blog de Tom Kyte pudimos ahorrarnos el trabajo de generar una consulta que nos localizara estos índices. La consulta que nos ofrece Tom Kyte es la siguiente:

select *
from (
select table_name, constraint_name,
     cname1 || nvl2(cname2,','||cname2,null) ||
     nvl2(cname3,','||cname3,null) || nvl2(cname4,','||cname4,null) ||
     nvl2(cname5,','||cname5,null) || nvl2(cname6,','||cname6,null) ||
     nvl2(cname7,','||cname7,null) || nvl2(cname8,','||cname8,null)
            columns
  from ( select b.table_name,
                b.constraint_name,
                max(decode( position, 1, column_name, null )) cname1,
                max(decode( position, 2, column_name, null )) cname2,
                max(decode( position, 3, column_name, null )) cname3,
                max(decode( position, 4, column_name, null )) cname4,
                max(decode( position, 5, column_name, null )) cname5,
                max(decode( position, 6, column_name, null )) cname6,
                max(decode( position, 7, column_name, null )) cname7,
                max(decode( position, 8, column_name, null )) cname8,
                count(*) col_cnt
           from (select substr(table_name,1,30) table_name,
                        substr(constraint_name,1,30) constraint_name,
                        substr(column_name,1,30) column_name,
                        position
                   from user_cons_columns ) a,
                user_constraints b
          where a.constraint_name = b.constraint_name
            and b.constraint_type = 'R'
          group by b.table_name, b.constraint_name
       ) cons
 where col_cnt > ALL
         ( select count(*)
             from user_ind_columns i
            where i.table_name = cons.table_name
              and i.column_name in (cname1, cname2, cname3, cname4,
                                    cname5, cname6, cname7, cname8 )
              and i.column_position < = cons.col_cnt
            group by i.index_name
         )
);

Esta consulta devuelve todas las claves externas que no disponen de un índice de apoyo, dentro de las constraints pertenecientes al usuario conectado. La consulta se encuentra en la siguiente entrada de su blog:

Tom Kyte – Thanks for the question regarding “Rows locks from select for update clause”

Sobre estos datos, hay que seleccionar las claves externas para la que nos interesa crear índices. No siempre es bueno crear índices para claves externas, como indica Tom Kyte en otra entrada de su blog:

Tom Kyte – Thanks for the question regarding “Indexes on foreign keys”

Una vez seleccionados los índices que consideremos necesarios en cada caso, se pueden generar volviendo a lanzar la sentencia de Tom Kyte con la siguiente cláusula select:

select 'create index '||constraint_name||' on '||table_name||' ('||columns||');' script

La tarea de seleccionar los índices pasa por identificar las tablas afectadas por los interbloqueos. Gracias a los Deadlock Graphs de las trazas capturadas y con una simple consulta al diccionario de datos, podemos obtener este dato. Los datos en hexadecimal que siguen al tipo de bloqueo dentro de la traza corresponden al identificador del objeto en la base de datos. En nuestro ejemplo el dato a verificar es el siguiente:

TM-0000cc6c-00000000

Y con la siguiente consulta, obtenemos el nombre del objeto:

select *
from dba_objects
where object_id in (TO_NUMBER('0000cc6c', 'XXXXXXXX'))