Mostrando entradas con la etiqueta Bases de datos Oracle. Mostrar todas las entradas
Mostrando entradas con la etiqueta Bases de datos Oracle. Mostrar todas las entradas

lunes 30 de junio de 2008

Integridad virtual con la base de datos Oracle 11g

Nuevas funcionalidades en Oracle 11g, las columnas virtualesSupongamos que nuestra base de datos Oracle dispone de una tabla en la que uno de los campos está formado realmente por muchos subcampos concatenados. El campo en cuestión no puede modificarse pero necesitamos validar y forzar la integridad de uno de los subcampos. Por ejemplo, queremos que los valores devueltos al ejecutar la función SUBSTR(column,7,3) sobre dicho campo estén incluidos en una tabla de referencia o lookup table.

Para versiones de la base de datos Oracle anteriores a la 11g no existía una forma sencilla de lidiar con este tipo de requerimientos ya que la validación de valores almacenados en una determinada columna, obligaba a que dichos valores estuvieran almacenados en una columna real que apuntase a la tabla de referencia o lookup table. En nuestro problema no existe una columna real sino que estamos tratando con una función aplicada sobre una columna. En la versión de la base de datos Oracle 10g y anteriores, lo más razonable sería crear una columna adicional en la tabla y, mediante el uso de un trigger PL/SQL, insertar o actualizar el valor retornado por la función SUBSTR en dicha nueva columna. A continuación podéis ver el código correspondiente a la implementación de esta solución:

SQL > CREATE TABLE tabla_referencia
2 ( validacion VARCHAR2(3) PRIMARY KEY,
3 descripcion VARCHAR2(20) );
Table created.

SQL > CREATE TABLE tabla_datos
2 ( concatenados VARCHAR2(20) PRIMARY KEY,
3 validacion VARCHAR2(3) NOT NULL
4 REFERENCES tabla_referencia );
Table created.

SQL > CREATE TRIGGER tabla_datos
2 BEFORE INSERT OR UPDATE
3 OF concatenados ON tabla_datos
4 FOR EACH ROW
5 BEGIN
6 :new.validacion :=
7 SUBSTR(:new.concatenados,7,3);
8 END;
9 /
Trigger created.

SQL > INSERT INTO tabla_referencia (validacion)
2 VALUES ('XYZ');
1 row created.

SQL > INSERT INTO tabla_datos (concatenados)
VALUES ('123456XYZ012345');
1 row created.

SQL > INSERT INTO tabla_datos (concatenados)
2 VALUES ('123456ABC012345');
INSERT INTO .....
*
ERROR at line 2:
ORA-02291: Integrity constraint violated -
parent key not found

La solución propuesta conlleva un par de problemas. El primero se trata de un obvio problema de almacenamiento ya que estamos almacenando el mismo dato dos veces, una vez en el campo concatenados y otra en el campo validacion. El segundo problema viene dado por la necesidad de utilizar un trigger PL/SQL y las implicaciones que este hecho puede conllevar sobre el rendimiento y los tiempos de ejecución de los comandos INSERT y UPDATE.

El caso es que con la versión de la base de datos Oracle 11g podemos forzar la integridad utilizando funciones aplicadas sobre una determinada columna; es decir, no estamos limitados a utilizar columnas reales, podemos utilizar una columna virtual. Así pues, con Oracle 11g podemos prescindir del trigger PL/SQL. Veamos a continuación como debemos crear nuestra tabla de datos para conseguir que esto ocurra:

SQL > CREATE TABLE tabla_datos
2 ( concatenados VARCHAR2(20) PRIMARY KEY,
3 validacion AS
4 (SUBSTR(concatenados,7,3))
5 REFERENCES tabla_referencia );
Table created.

En este caso hemos definido una columna virtual con el nombre concatenados, columna que es simplemente un metadato, que no se almacena de manera redundante, y que no requiere de un trigger PL/SQL para su mantenimiento, pero que si que se puede utilizar para forzar la integridad de nuestra base de datos cruzando dicho valor con los valores almacenados en nuestra tabla de referencia o lookup table.

Artículos relacionados: Oracle lanza la versión 11g de su base de datos.

jueves 28 de febrero de 2008

Nueva wiki oficial de Oracle

PL/SQL: La wiki oficial de Oracle en PLSQLDesde 1990, el sitio web Oracle Technology Network (OTN), ha proporcionado a la comunidad de usuarios de las bases de datos Oracle un medio informal de comunicarse e intercambiar conocimientos entre ellos mismos, así como con empleados de Oracle. En 2005, esta fórmula fue mejorada al surgir una gran colección de blogs oficiales de Oracle. No obstante, esto recursos contienen fundamentalmente información proporcionada por Oracle.

Ahora, por primera vez, clientes y asociados pueden complementar este contenido oficial con el que ha generado la comunidad OTN, todo gracias a la nueva wiki de Oracle. Para los que no sepan que es una wiki, diré que se trata de un software que permite a los usuarios editar, crear y enlazar páginas web de una manera muy sencilla. En esencia se trata de un sistema de gestión de contenidos muy fácil de usar y que facilita el cruce de enlaces y la publicación de artículos en colaboración con otros usuarios.

Con la nueva wiki de Oracle, puedes crear tu propio contenido web, incluyendo trucos y guías de instalación, documentación de proyectos, notas técnicas o cualquier otra cosa que capture tu imaginación. También se pueden publicar comentarios en las páginas web creadas por otros miembros de la comunidad Oracle. Sin duda, este mundo paralelo de Oracle formado por clientes y asociados, se trata del complemento perfecto al sitio web OTN y a los foros de discusión y blogs de Oracle.

Otro hecho que quiero mencionar es la existencia del llamado Oracle ACE Program, los Oracle ACEs son profesionales conocidos por sus profundos conocimientos en aplicaciones Oracle y por sus credenciales como entusiastas defensores de la comunidad Oracle. Los candidatos a ser Oracle ACEs pueden ser nominados por cualquier miembro de comunidad de aplicaciones y tecnología Oracle. Para poder acceder al programa Oracle ACE, los candidatos deben ser expertos que pongan su conocimiento de las aplicaciones Oracle al alcance de todos, a través de blogs, podcasts, conferencias, artículos o foros.

A continuación os pongo algunos enlaces a sitios de la Oracle Technology Network que no he mencionado anteriormente en el contenido de este artículo:

Notas de prensa
Descargas de software gratis
Documentación
Centros de tecnología
Podcasts
Artículos técnicos
Foros de discusión
Calendario de eventos para desarrolladores

miércoles 30 de enero de 2008

Cambios de rendimiento en una sentencia SQL al activar el trazado

PLSQL y SQL esperando a que windows arranqueHace unos días un lector del blog me enviaba un email contándome un "extraño" problema de rendimiento que tenía con una sentencia SQL. Dicha sentencia SQL tardaba mucho tiempo en devolver resultados y, tras activar la utilidad de trazado SQL (SQL_TRACE=TRUE), el problema desaparecía y la respuesta de la sentencia SQL era inmediata.

La verdad es que el fenómeno no es tan extraño una vez que se conoce la causa. Cuando se activa el trazado haciendo SQL_TRACE=TRUE, lo que ocurre es que la sesión Oracle utiliza una nueva área de SQL compartido. Esto supone que el parsing (ver artículo sobre las fases durante el procesamiento de una sentencia SQL) de cualquier sentencia SQL que se ejecute después de activar el trazado vuelva a tener lugar o que, de existir una versión de dicha sentencia SQL ya parseada en la nueva área de SQL compartido, dicha versión no coincida con la versión existente cuando el trazado no está activo. Esto causa que, cuando la sentencia SQL utiliza variables (bind variables), puesto que los valores reales de dichas variables son tomados en el momento del parsing, muy probablemente, los planes de ejecución de la misma sentencia SQL sean diferentes antes y después de activar el trazado al haberse generado utilizando valores de variable distintos.

Veamos un ejemplo:

SQL> CREATE TABLE pepe AS
2 SELECT CASE WHEN rownum = 1 THEN 1
3 ELSE 0 END pepe_id, all_tab_columns.*
4 FROM all_tab_columns
5 /
Table created.

SQL> CREATE INDEX pepe_idx ON pepe(pepe_id);
Index created.

SQL> BEGIN
2 DBMS_STATS.gather_table_stats (
3 user, 'T', method_opt=>
4 'for all'||
5 'indexed columns'||
6 'size 254' );
7 END;
8 /
PL/SQL procedure successfully completed.

De esta manera habremos generado la tabla pepe que contendrá un registro con la columna pepe_id=1, mientras que para el resto de registros (digamos que alrededor de 75.000), dicha columna tomará el valor de 0.

Resulta pues evidente que, si utilizamos el optimizador basado en costes, al haber generado los correspondientes histogramas con el procedimiento PL/SQL gather_table_stats, los planes de ejecución serán muy diferentes si ejecutamos SELECT * FROM pepe WHERE pepe_id=1, sentencia SQL para la cual el optimizador elegirá utilizar el índice pepe_idx, o si, por contra, ejecutamos SELECT * FROM pepe WHERE pepe_id=0, sentencia para la cual el optimizador elegirá realizar un full scan de la tabla.

Pero que ocurriría si utilizamos una variable a la hora de ejecutar la sentencia SQL, es decir, si ejecutamos SELECT * FROM pepe WHERE pepe_id=:id. En este caso la respuesta varía dependiendo de cual es la versión de la base de datos Oracle que estemos utilizando:

Base de datos Oracle 8i Release 3 y anteriores

En este caso el optimizador dispone de la siguiente información, la columna pepe_id puede tomar dos valores (1 y 0) y, además, la tabla tiene unos 75.000 registros. Debido al hecho de que la columna puede tomar sólo dos valores, el viejo optimizador de la versión 8i supondrá que para cualquier valor de la variable :id, la consulta SELECT asociada devolverá aproximadamente la mitad de los registros de la tabla. Así que, la decisión más probable del optimizador será realizar un escaneado completo (full scan) de la tabla.

Bases de datos Oracle 9i y 10g

Si disponemos de una versión de la base de datos Oracle incluida entre las versiones 9i y 10g, entonces la base de datos Oracle esperará a que se suministre el valor de la variable, antes de que el optimizador decida cual es el mejor plan de ejecución. Esto es lo que se conoce como bind variable peeking (que traducido vendría a ser algo como echar un vistazo a la variable). Así pues, en lo que se refiere a nuestra consulta SELECT, el optimizador elegirá bien realizar un full scan (:id=0), o bien utilizar el índice pepe_idx (:id=1), dependiendo del valor de la variable :id.

Y aquí es donde nos podemos encontrar con un problema de rendimiento ya que, el plan de ejecución que el optimizador utilice por primera vez, será el plan de ejecución que se almacene en el área de SQL compartido, y dicho plan será el que se utilice para ejecutar nuestra sentencia SQL SELECT * FROM pepe WHERE pepe_id=:id, independientemente del valor que demos a la variable :id con posterioridad.

Entonces ya sabemos que es lo que le pasó al lector que inspiró este artículo:

a) Alguien ejecuto la consulta en cuestión utilizando un determinado valor para la variable :id.

b) El plan de ejecución correspondiente a dicho valor de la variable se almacenó en el área de SQL compartido.

c) Nuestro lector ejecuto la misma consulta pero utilizando otro valor para la variable :id. El optimizador reutilizó el plan de ejecución almacenado en el área de SQL compartido. Dicho plan no era el más eficiente para el nuevo valor de la variable, de hecho se trataba de un plan de ejecución bastante pobre. Como resultado la consulta tardó en responder más tiempo del esperado.

d) Posteriormente nuestro lector activó el trazado (SQL_TRACE=TRUE) y volvió a ejecutar la consulta pero, en este caso, al estar el trazado activo, el plan de ejecución no se sacó del área de SQL compartido, sino que se elaboró un nuevo plan de ejecución mucho más eficiente, dando como resultado que la sentencia SQL se ejecutase de forma rápida.

Posibles soluciones

Existen algunas soluciones que nos permitirán evitar este tipo de situaciones provocadas por el bind variable peeking:

a) No utilizar variables (bind variables). En el ejemplo que he puesto es bastante evidente que no es necesario utilizar variables, simplemente podemos utilizar constantes (valores literales) en nuestra cláusula WHERE. Esta es, sin duda, la mejor solución.

b) No generar estadísticas que puedan hacer variar los planes de ejecución. Obviamente, si no hubiéramos generado los histogramas de la tabla pepe, el optimizador siempre generaría el mismo plan de ejecución con independencia del valor de la variable. A veces esto puede ser recomendable, pero si no generamos estadísticas, entonces perderemos algunas de las ventajas derivadas de utilizar el optimizador basado en costes.

c) Cambiar el valor del parámetro CURSOR_SHARING para que en vez de FORCE sea SIMILAR. Si utilizamos el valor SIMILAR evitaremos que el bind variable peeking entre en juego.

d) Actualizar nuestra base de datos Oracle a la versión 11g, ya que esta versión está dotada de una nueva funcionalidad conocida como intelligent cursor sharing (compartición de cursores inteligente). Así pues, para nuestra consulta ejemplo, el optimizador de la versión 11g es capaz de identificar que un plan de ejecución no es suficiente para todos los valores posibles de la variable :id, permitiendo que se generen varios planes de ejecución distintos para una misma sentencia SQL.

viernes 12 de octubre de 2007

Oracle lanza la base de datos Oracle 11g

SQL y PL/SQL - Oracle ha lanzado la base de datos Oracle 11gOracle ha lanzado la base de datos Oracle 11g, disponible globalmente sobre plataforma Linux, y que ha sido especialmente desarrollada para ayudar a los clientes a abordar los retos derivados de un entorno empresarial cada vez más cambiante y competitivo, la necesidad de manejar grandes volúmenes de datos y la posibilidad de ofrecer una mayor calidad de servicio y reducir a la vez los costes tecnológicos asociados.

La base de datos Oracle Database 11g proporciona nuevas funcionalidades que garantizan un alto rendimiento, una alta escalabilidad, fiabilidad y seguridad, permitiendo el uso de plataformas grid y asegurando altos niveles de calidad de servicio e incrementos de la flexibilidad de negocio reduciendo además los costes de explotación. Con la base de datos Oracle 11g los clientes pueden resolver los problema más exigentes en todas las áreas, incluyendo aplicaciones transaccionales, de inteligencia de negocio y de gestión de contenidos.

Nuevas funcionalidades de la base de datos Oracle 11g

Secure Files: gestión eficiente y segura de todo tipo de datos. Permite la gestión de todo tipo de datos, incluyendo imágenes, ficheros de texto o tipos avanzados de datos, como XML, imágenes médicas y objetos en 3D.

Information Lifecycle Management y Oracle Advanced Compression: permite realizar una gestión eficaz del ciclo de vida de los datos y reduccir los costes de almacenamiento. Oracle Database 11g ha mejorado significativamente las funcionalidades de particionamiento y compresión de datos, con importantes novedades para mejorar la gestión del almacenamiento y del ciclo de vida de la información. La base de datos Oracle 11g también dispone de funcionalidades avanzadas de compresión de datos para información estructurada y no estructurada (LOBs), consiguiendo ratios de compresión del orden de 2 ó 3.

Oracle Real Application Testing: permite reducir el tiempo, el riesgo y los costes derivados de la realización de cambios y mejoras. Esto es debido a que Oracle 11g permite realizar pruebas simulando entornos de producción con numerosos usuarios interactuando con la base de datos.

Oracle Active Data Guard: facilita el incremento del ROI de las plataformas utilizadas para recuperación de desastres. Con Oracle 11g se puede utilizar las bases de datos en modo standby. Las bases de datos en standby pueden utilizarse para generar informes, realizar backups y testear cambios y mejoras realizados en los sistemas de producción, proporcionando por lo tanto un ROI mayor cuando se invierte en sistemas para la recuperación de desastres (Disaster Recovery).

Oracle Total Recall: permite realizar un rastreo eficaz de cambios, asegurando el cumplimiento de normativas de seguridad. Esta funcionalidad permite el acceso transparente a los datos en momentos del tiempo configurados por el administrador. Oracle Total Recall permite la realización sencilla de operaciones de rastreo de cambios, auditoría de modificaciones y análisis para el cumplimiento de regulaciones de seguridad.

Arquitectura de máxima disponibilidad: Oracle Database 11g ofrece importantes innovaciones para garantizar la disponibilidad de los datos ante paradas planificadas y no planificadas. Se incluyen mejoras para conseguir mejorar los tiempos de backup y recuperación de grandes volúmenes de información y funcionalidades de hot patching que mejoran la disponibilidad de los sistemas al permitir la aplicación de parches sin necesidad de apagar las bases de datos. El nuevo asistente Data Recovery Advisor ayuda a los administradores a reducir de manera muy significativa los tiempos de parada ofreciendo la automatización de muchas de las tareas asociadas, incluyendo tanto el diagnóstico de los problemas, como el establecimiento del plan de recuperación.

Transparent Encryption: encriptación transparente de los datos. Con la base de datos Oracle 11g aumentan las funcionalidades de encriptación de datos, incluyendo no sólo la encriptación de columnas, sino también la encriptación de tablas completas, índices y tipos de dato LOB. Todo esto implica una seguridad de los datos mayor.

Gestión de datos XML: Oracle Database 11g mejora de manera significativa las funcionalidades de XML DB. Se incluye el soporte a formatos binarios de XML (Binary XML), una importante innovación que proporciona un rendimiento superior en la gestión de datos XML.

Cubos OLAP embebidos: Oracle Database 11g también proporciona innovaciones importantes en el ámbito de los Data Warehouses. Los cubos OLAP ahora pueden comportarse como vistas materializadas en la base de datos, permitiendo que los desarrolladores puedan utilizar el estándar SQL y PLSQL para realizar operaciones de consulta, pero manteniendo los beneficios en el rendimiento derivados del uso de cubos OLAP.

Mejoras en los pool de conexiones y en los caches de de resultados: la base de datos Oracle 11g ofrece nuevas funcionalidades para mejorar los tiempos de respuesta de las aplicaciones, mejorando el rendimiento. Con Connection Pooling y Query Results Cache los resultados de las queries más utilizadas son almacenadas y reutilizadas de manera transparente a las aplicaciones, mejorando los tiempos de respuesta y haciendo un uso más efectivo de los recursos hardware disponibles.

Mejoras en la automatización y en la auto administración de la base de datos: Con el objetivo de reducir los costes de gestión de los sistemas e incrementar el rendimiento, la disponibilidad, la escalabilidad y la seguridad, la base de datos Oracle 11g introduce un conjunto de nuevas funcionalidades en la línea de la auto administración de la base de datos. Estas funcionalides incluyen asistentes para reducir las tareas de particionamiento y ciclo de vida de los datos, funcionalidades que permiten la optimización automática de sentencias SQL y PL/SQL o un nuevo Support Workbench que proporciona una interfaz simple para visualizar incidencias en la base de datos.

Facilita el desarrollo de aplicaciones: Oracle Database 11g incorpora un nuevo compilador Java just-in-time que proporciona un alto rendimiento en la ejecución de procedimientos almacenados en Java sin necesidad de utilizar compiladores de terceros. También se incorpora una integración nativa con Visual Studio 2005 para facilitar el desarrollo de aplicaciones .NET con Oracle.

martes 26 de junio de 2007

Identificación de tablas y columnas en una base de datos Oracle mediante sentencias SQL

Nuevo teclado de programación PL/SQL y PLSQL
Una vez que una tabla Oracle es creada utilizando el comando CREATE TABLE, la estructura de dicha tabla se puede visualizar utilizando las siguientes vistas (views) del sistema de la base de datos Oracle:

DBA_TABLES: Muestra la información de la tabla a nivel de cabecera.

DBA_TAB_COLUMNS: Muestra la información de la tabla a nivel de columna.

DBA_TAB_PRIVS: Muestra los privilegios de acceso a las tablas de los usuarios de la base de datos.

DBA_COL_PRIVS: Muestra los privilegios de acceso a nivel de columna. Es bastante raro tener la necesidad de definir permisos a nivel de columna, de hecho, yo nunca me he encontrado con una bases de datos Oracle que utilice esta funcionalidad.

A nivel de cabecera, es decir en la vista DBA_TABLES, se muestran datos como el esquema al que pertenece la tabla (columna OWNER), el nombre de la tabla (TABLE_NAME), el nombre del tablespace donde se almacenan físicamente los datos contenidos en la tabla (TABLESPACE_NAME), el número de registros que contiene la tabla (NUM_ROWS), el número de bloques que ocupan físicamente los datos (BLOCKS), así como otros datos estadísticos que son utilizados por el optimizador basado en costes para generar los planes de ejecución de las sentencias SQL o PL/SQL.

La vista DBA_TABLES se puede unir con la vista DBA_TAB_COLUMNS mediante los campos OWNER y TABLE_NAME. Esta última vista del sistema Oracle nos dará información acerca los nombres de las columnas (COLUMN_NAME), del tipo de dato SQL almacenado en dicha columna (DATA_TYPE), de las dimensiones o tamaño de la columna (DATA_LENGTH) y nos indicará si la columna permite o no tomar valores nulos o en blanco (NULLABLE). También esta vista del sistema Oracle muestra algunos valores estadísticos que son utilizados por el optimizador.

La vista DBA_TAB_PRIVS se une de igual manera que la DBA_TAB_COLUMNS con la vista DBA_TABLES. Las columnas fundamentales de la vista DBA_TAB_PRIVS dan información sobre cual es el usuario al que se le concede un permiso determinado (GRANTEE), cual es el usuario que concedió el permiso (GRANTOR), cual es el permiso concedido (PRIVILEGE) y si el usuario al que se le concede el permiso puede a su vez conceder dicho permiso a otros usuarios (GRANTABLE).

Los permiso de acceso que se pueden asignar a una tabla son los siguientes: ALL, ALTER, DELETE, INDEX, INSERT, REFERENCES, SELECT y UPDATE. Además, cuando se dan permisos de acceso al usuario genérico PUBLIC, automáticamente se están asignando dichos permisos a todos los usuarios de la base de datos Oracle.

A continuación encontraréis un par de ejemplos de sentencias SQL con los que podréis:

1) Encontrar el nombre de una tabla de la que sólo recordáis parte del nombre (la sentencia SQL del ejemplo devolverá todas las tablas y el esquema al que pertenecen que contienen en su nombre la secuencia de caracteres EMPL):

SELECT DISTINCT owner, table_name
  FROM dba_tables
 WHERE table_name LIKE '%EMPL%'

2) Encontrar las tablas que tienen una columna con un nombre específico (la sentencia SQL del ejemplo devolverá todas las tablas que tienen una columna cuyo nombre es CLIENTE):

SELECT DISTINCT owner, table_name, column_name
  FROM dba_tab_columns
 WHERE column_name = 'CLIENTE'

3) Listar los permisos que un determinado usuario tiene sobre un esquema de la base de datos determinado (la sentencia SQL del ejemplo mostraría los permisos del usuario JLOPEZ sobre el esquema FACTURAS):

SELECT owner, table_name, grantee,
       privilege, grantable
  FROM dba_tab_privs
 WHERE owner = 'FACTURAS'
   AND grantee = 'JLOPEZ'

Nota: Es importante utilizar mayúsculas a la hora de tratar de identificar un esquema, una tabla, una columna o un usuario, ya que sus nombres son almacenados con este formato.

Aclaración: Es posible que un determinado usuario tenga acceso a una tabla y no aparezca ningún registro para dicho usuario en la tabla DBA_TAB_PRIVS. Estas son algunas de las posibles razones por las que esto puede ocurrir:

1) La tabla en cuestión puede estar habilitada para ser accedida públicamente, es decir, tiene permisos de acceso el usuario genérico PUBLIC.

2) Un determinado rol (ROLE) tiene permiso de acceso sobre la tabla y el usuario en cuestión pertenece a dicho rol (ver tablas DBA_ROLES y DBA_ROLE_PRIVS).

3) El usuario tiene acceso a nivel de sistema para acceder a cualquier tabla, es decir, se le ha asignado el privilegio SELECT ANY TABLE (ver tabla DBA_SYS_PRIVS). También existen privilegios equivalentes para las acciones de CREATE, ALTER, BACKUP, DELETE, DROP, INSERT, LOCK y UPDATE.

viernes 20 de abril de 2007

Fases durante el procesamiento de una sentencia SQL

Procesamiento de una sentencia SQL en PLSQL o PL/SQL
Durante el procesamiento de una sentencia SQL, ya sea mediante un script o un programa PL/SQL, se distinguen cuatro fases: análisis de la sintaxis (parsing), análisis de las variables (binding), ejecución (executing) y recuperación de datos (fetching).

Fase de parsing

Durante esta fase el servidor de la base de datos Oracle realiza las siguientes acciones:

- Busca la sentencia SQL en la memoria compartida (shared pool).

- Chequea la sintaxis de la sentencia siguiendo las especificaciones y la gramática del lenguaje SQL.

- Chequea la semántica, asegurando que los objetos Oracle referenciados en la sentencia SQL son válidos (existen en la base de datos) y satisfacen las restricciones de seguridad (es decir, el usuario que ejecuta la sentencia tienen los permisos adecuados sobre dichos objetos).

- Determina si el proceso que lanza la sentencia SQL tiene los permisos apropiados para ejecutarlo.

- Si la sentencia SQL incluye una vista (view) o una subquery (una subquery no es más que una sentencia SELECT que está dentro de otra sentencia SQL), transforma dicha sentencia en una sentencia SQL equivalente e intenta simplificar la sentencia resultante.

- Determina y almacena el plan de ejecución o, si es posible, utiliza un plan de ejecución existente.

Fase de binding

En esta fase el servidor de la base de datos Oracle:

- Identifica las variables (bind variables) en la sentencia SQL.

- Asigna o reasigna un valor a cada variable.

Esto quiere decir que en el momento de la optimización del plan de ejecución, el servidor Oracle no conoce los valores de estas variables. Esto facilita una re-ejecución de la sentencia más rápida, ya que no es necesario volver a hacer el parsing de la sentencia. No obstante, está el inconveniente de que el optimizador puede seleccionar un plan de ejecución equivocado causando problemas de rendimiento, cosa que no ocurriría si en vez de variables utilizamos constantes y, además, se recolectan estadísticas. El tema de la recolección de estadísticas para mejorar el rendimiento de las bases de datos Oracle requiere un análisis muy detallado y, quizás, escriba algún artículo más adelante sobre el tema.

Fase de ejecución

En la fase de ejecución el servidor Oracle realiza las siguientes acciones:

- Ejecuta la sentencia SQL siguiendo el plan de ejecución determinado durante la fase de parsing.

- Realiza las operaciones de entrada/salida necesarias para la ejecución de las sentencias de manipulación de datos (DML) y ordena los datos en caso de ser necesario.

Fase de fetching

Durante esta fase el servidor Oracle devuelve los registros de una sentencia SELECT. En cada iteración (fetch) el servidor devuelve múltiples registros. El número de registros que Oracle devuelve por iteración es configurable, es decir, se puede cambiar el tamaño de la matriz de registros. En SQL*Plus podemos cambiar dicho tamaño utilizando el comando SET ARRAYSIZE. Por ejemplo:

   SQL> show arraysize
   arraysize 15
   SQL> set arraysize 1

Tras la ejecución de este comando, el servidor Oracle procesará un solo registro en cada iteración (fetch). El valor por defecto es quince.

jueves 22 de marzo de 2007

Tablas temporales en las bases de datos Oracle

Además de las tablas de la base de datos permanentes, Oracle permite la creación de tablas temporales para mantener datos propios y exclusivos a una sesión Oracle determinada. Estos datos permanecerán en el sistema sólo durante el tiempo que dure la transacción o sesión involucrada. No obstante, al igual que para las tablas permanentes, la definición de las tablas temporales se almacena en las tablas del sistema.

La sentencia CREATE GLOBAL TEMPORARY TABLE crea una tabla temporal Oracle cuya temporalidad puede ser definida a nivel de transacción (los datos existen mientras se realiza la transacción) o a nivel de sesión (los datos existen mientras dura la sesión). Los datos en una tabla temporal son propios y privativos de la sesión Oracle que la está utilizando. Una sesión Oracle determinada puede ver y modificar los datos que durante dicha sesión se insertaron en la tabla temporal, pero estos datos no son accesibles desde otra sesión diferente. Como es lógico, la sentencia LOCK no tiene efecto sobre las tablas temporales ya que cada sesión hace uso de sus propios datos.

Así pues, los datos almacenados en una tabla temporal son visibles sólo para la sesión de Oracle que inserta datos dentro de dicha tabla. Para especificar si los datos de una tabla temporal son por sesión o por transacción, a la hora de crear la definición de la tabla, utilizaremos la cláusula ON COMMIT DELETE ROWS para indicar que la temporalidad es a nivel de transacción, o la cláusula ON COMMIT PRESERVE ROWS si queremos que la temporalidad sea a nivel de sesión.

A continuación podéis ver un ejemplo de comando SQL para crear una tabla temporal en Oracle:

CREATE GLOBAL TEMPORARY TABLE temp_listado 
(
nombre VARCHAR2(40),
fecha_nacimiento DATE
)
ON COMMIT PRESERVE ROWS

Asimismo, si ejecutamos una sentencia TRUNCATE sobre una tabla temporal, los datos que se truncarán serán los de la propia sesión desde la que se ejecuta la sentencia. Los datos que hayan podido ser insertados desde otras sesiones que estén utilizando la misma tabla, no se verán afectados por la sentencia TRUNCATE.

Los datos de una tabla temporal Oracle se borran automáticamente en el caso de que la sesión termine, bien porque el usuario desconecte, bien porque la sesión termine de una de manera anormal al producirse algún tipo de fallo.

Las tablas temporales admiten la creación de índices con el comando CREATE INDEX. Dichos índices, como las tablas, son también temporales, y los datos en el índice permanecen en el sistema mientras la tabla temporal existe.

También se pueden crear views y triggers sobre tablas temporales.

Asignación de segmentos

Para las tablas temporales, Oracle no asigna un segmento justo en el momento en que son creadas, cosa que si que hace para las tablas permanentes. Por contra, los segmentos son asignados cuando se realiza el primer INSERT.

Ser pueden ejecutar sentencias DDL (ALTER TABLE, DROP TABLE, CREATE INDEX,...) sobre las tablas temporales, pero sólo si no hay ninguna sesión que la esté utilizando en ese momento y que ya haya realizado un INSERT sobre la misma. La tabla temporal queda liberada en el momento en que la sesión termina, o cuando se ejecuta una sentencia COMMIT o ABORT si la temporalidad de la tabla fue definida a nivel de transacción.

De igual forma, los segmentos se liberan al final de la transacción o al final de la sesión según se haya definido la temporalidad de la tabla.

Artículos relacionados: Tablas externas en PLSQL.

martes 22 de agosto de 2006

Objetos bloqueados en una base de datos (2)

Esta nota es continuación del anterior post Objetos bloqueados en una base de datos (1). Así pretendo dejar zanjado el tema referente a la información almacenada en la tabla V$LOCK.

Tipos de locks y las columnas ID1 e ID2

En nuestro ejemplo de la nota anterior, ya sabemos que nuestro lock es un lock producido por una sentencia DML (sentencias de manipulación de datos como select, insert, update, delete, etc), pero esto es porque fuimos nosotros los que ejecutamos la sentencia bloqueadora. Evidentemente este no va a ser siempre el caso, ya que una base de datos es normalmente compartida por multitud de usuarios. Afortunadamente, podemos encontrar la información que necesitamos en la tabla V$LOCK.

El primer lugar donde mirar es en la columna TYPE. Existen docenas de tipos de locks pero la mayoría son tipos de locks del sistema. Los locks del sistema aparecen normalmente durante un tiempo muy breve y generalmente no es muy útil intentar poner a punto (tunear) la librería caché y otros elementos del sistema mirando en V$LOCK.

Sólo hay tres tipos de locks de usuario, TX, TM y UL.

   1. UL son locks definidos por el usuario (user-defined lock), estos locks se definen utilizando el paquete DBMS_LOCKS.
   2. Los TX son locks de transacciones sobre registros, el bloqueo se produce una vez por cada transacción que cambia el dato, sin importar cuantos objetos son cambiados durante la transacción. Las columnas ID1 e ID2 apuntan al segmento de rollback y a las entradas de la tabla de transaciones para dicha transacción.
   3. Los TM son locks producidos por sentencias DML. El bloqueo se produce una vez para cada objeto que está siendo actualizado. La columna ID1 identifica el objeto que está siendo modificado.

Nivel de exclusividad de un lock

Se puede optener más información sobre los locks TM y TX con sólo mirar a las columnas LMODE y REQUEST, ambas utilizan el mismo formato numérico para indicar el nivel de exclusividad de un lock: desde 0 para indicar que no hay lock, hasta 6 para indicar que se trata de un lock exclusivo. Cualquier sesión debe tener un lock TX exclusivo para poder actualizar datos; LMODE valdrá 6. Si no se puede conseguir un lock exclusivo porque alguno de los registros que se quieren cambiar está ya bloqueado por otra sesión, entonces se solicitará un lock TX exclusivo; LMODE valdrá 0, ya que no hay un lock todavía, y REQUEST valdrá 6. Esto se puede ver en los registros de la tabla V$LOCK de nuestra nota anterior y que vuelvo a poner a continuación:

ADDR     KADDR    SID TY ID1    ID2    LMODE REQUEST CTIME BLOCK
-------- -------- --- -- ------ ------ ----- ------- ----- ----------
B765B198 B765B1AC 779 TM 6442   0      3     0       11543 0
01AA2F00 01AA2FCC 611 TX 262160 653305 6     0       54537 1
B6047A58 B6047A68 699 TX 262160 653305 0     6       54535 0
...............................................................

En estos registros se puede ver que las columnas ID1 e ID2 de la sesión 2, que es la que está solicitando el lock TX (LMODE=0, REQUEST=6), apuntan al segmento de rollback y a las entradas de la transacción de la sesión 1. Esto es lo que nos permite determinar que sesión 1 bloquea a la sesión 2.

Es posible también ver locks TX en modo 4, modo compartido. Si un bloqueo que contiene registros que van a ser actualizados no tiene ninguna entrada ITL (Interested Transaction List), entonces las sesión toma un lock TX en modo 4 mientras espera por la entrada ITL. Si un objeto queda frecuentemente bloqueado por locks TX modo 4, esto signfica que probablemente haya que incrementar el INITRANS para dicho objeto.

Los locks TM son generalmente requeridos y tomados en modos 3 (shared-row exclusive) y 6. Las sentencias DDL (sentencias de definición de objetos como revoke, grant, alter, etc) requieren un lock TM exclusivo (6), aunque hay excepciones como el comando CREATE TABLE que no requiere ningún lock TM, ya que no necesita bloquear ningún objeto porque el objeto en cuestión todavía no existe. Las sentencias DML (sentencias de manipulación de datos como select, insert, update, delete, etc), sin embargo, requieren un lock TM en modo 3, así se puede ver en los registros que seleccionamos anteriormente en la tabla V$LOCK (ver arriba), los locks TM son de nivel 3 y por tanto son locks generados por sentencias DML.

Como identificar el objeto bloqueado

Ahora que sabemos que cada lock TM apunta a un objeto bloqueado, podemos utilizar la columna ID1 para identificar el objeto.

SQL> select object_name from dba_objects
2* where object_id=6442;

OBJECT_NAME
-------------
TEST_BLOQUEO

A veces conocer el objeto bloqueado es suficiente pero es posible obtener más información todavía. Veamoslo a continuación.

Como identificar el registro bloqueado

Podemos obtener esta información desde la tabla V$SESSION mirando los datos de la sesión bloqueada. En nuestro ejemplo la sesión bloqueada era la que tenía SID 699.

SQL> select row_wait_obj#, row_wait_file#,
2* row_wait_block#, row_wait_row#
3* from v$session where sid=699 ;

ROW_WAIT_OBJ# ROW_WAIT_FILE# ROW_WAIT_BLOCK# ROW_WAIT_ROW#
------------- -------------- --------------- -------------
         6442             16          111507             0

Estos cuatro datos son los cuatro componentes de un ROWID extendido. Ahora podemos utilizar el paquete DBMS_ROWID, en concreto la función ROWID_CREATE, para obtener el ROWID real:

SQL> select do.object_name,
2* dbms_rowid.rowid_create ( 1,
3* ROW_WAIT_OBJ#, ROW_WAIT_FILE#,
4* ROW_WAIT_BLOCK#, ROW_WAIT_ROW# )
5* from v$session s, dba_objects do
6* where sid=699
7* and s.ROW_WAIT_OBJ# = do.OBJECT_ID ;

OBJECT_NAME    DBMS_ROWID.ROWID_C
-------------- ------------------
TEST_BLOQUEO   ABAxnHBBWACCv0yACC

Y, por supuesto, esto nos permite obtener la información del registro directamente:

SQL> select * from test_bloqueo
2* where rowid='ABAxnHBBWACCv0yACC' ;

TEST BLOQUEO
---- ----------
1    Hola

Conclusión

Hemos visto que podemos identificar que sesión está bloqueando a otra, el objeto que está bloqueado y hasta el registro individual que está bloqueado. Espero que hayáis aprendido un poquito acerca del uso que podemos dar a la tabla V$LOCK.

lunes 31 de julio de 2006

Objetos bloqueados en una base de datos (1)

Todo el mundo ha intentado hacer alguna llamada desde su móvil y dicha llamada no ha podido realizarse por problemas de congestión, es decir, otros usuarios han copado los canales disponibles y nosotros no hemos podido tomar posesión de ninguno de ellos. Algo parecido puede ocurrir con las sesiones de Oracle (o de cualquier otra base de datos), ya que una sesión puede bloquear (mantener un "lock") un objeto de la base de datos (tabla, columna, etc) evitando que otra sesión pueda utilizarlo.

En esta nota voy a contar como identificar que sesión es la causante del bloqueo y en posteriores mensajes iré más allá y contaré como identificar el objeto que está bloqueado.

Creación de un “lock”

Para empezar voy a simular una situación de bloqueo entre dos sesiones:

Sesión 1

SQL> create table test_bloqueo (test number, bloqueo varchar2(10));

Table created.

SQL> insert into test_bloqueo values (1, 'Hola');

1 row created.

SQL> insert into test_bloqueo values (2, 'Adios');

1 row created.

SQL> select * from test_bloqueo;

TEST BLOQUEO
---- ----------
1    Hola
2    Adios

2 rows selected.

SQL> commit;

A continuación creamos el "lock" sobre la tabla:

SQL> select * from test_bloqueo for update;

La claúsula “for update” es la encargada de creae el "lock" sobre la tabla test_bloqueo.

Sesión 2

Ahora intentaremos actualizar un registro de la tabla test_bloqueo:

SQL> update test_bloqueo set test=3 where bloqueo='Hola';

Este último comando se quedará colgado bloqueado por el "lock" establecido en la primera sesión.

¿Cómo identificar la sesión bloqueadora?

Oracle proporciona una vista (view), DBA_BLOCKERS, que muestra la lista de SIDs de todas las sesiones que están bloqueando algún objeto. El problema es que consultar esta “view” es, según mi experiencia, bastante más lento que consultar directamente la tabla V$LOCK, y además dicha “view” da mucha menos información que la mencionada tabla.

Esto es lo que podemos ver consultando la tabla:

SQL> select * from v$lock ;

ADDR     KADDR    SID TY ID1    ID2    LMODE REQUEST CTIME BLOCK
-------- -------- --- -- ------ ------ ----- ------- ----- ----------
B765B198 B765B1AC 779 TM 6442   0      3     0       11543 0
01AA2F00 01AA2FCC 611 TX 262160 653305 6     0       54537 1
B6047A58 B6047A68 699 TX 262160 653305 0     6       54535 0
...............................................................

Si una sesión mantiene un “lock” que está bloqueando a otra sesión entonces la columna BLOCK mostrará el valor 1. Además, se puede determinar que sesión está siendo bloqueada si comparamos las columnas ID1 e ID2, ambas columnas mostrarán los mismos valores tanto para la sesión bloqueadora como para la sesión bloqueada. Ésta última además, puesto que esta solicitando un “lock” que no puede conseguir, tendrá un valor en la columna REQUEST mayor que cero.

Los resultados del comando “select” nos muestran que la sesión con SID 611 esta bloqueando la sesión con SID 699. La sesión 611 se corresponde con la sesión 1 de nuestro ejemplo y la 699 se corresponde con la sesión 2.

Pero existe una solución todavía más eficaz para determinar que sesión bloquea a cual. Podemos construir el siguiente comando “select”:

SQL> select lock1.sid, ' BLOQUEA ', lock2.sid
2 from v$lock lock1, v$lock lock2
3 where lock1.block =1 and lock2.request > 0
4 and lock1.id1=lock2.id1
5 and lock1.id2=lock2.id2;

       SID 'BLOQUEA'        SID
---------- --------- ----------
       611 BLOQUEA          699

1 row selected.

Incluso podemos construir una “query” mucho mas legible:

SQL> select s1.username '@' s1.machine
2 ' ( SID=' s1.sid ' ) esta bloqueando '
3 s2.username '@' s2.machine ' ( SID=' s2.sid ' ) ' AS bloqueos
4 from v$lock l1, v$session s1, v$lock l2, v$session s2
5 where s1.sid=l1.sid and s2.sid=l2.sid
6 and l1.BLOCK=1 and l2.request > 0
7 and l1.id1 = l2.id1
8 and l2.id2 = l2.id2 ;

BLOQUEOS
----------------------------------------------------------------
TEST@chessy ( SID=611 ) esta bloqueando TEST@chessy ( SID=699 )

1 row selected.

Con esto doy por terminada esta primera nota acerca de los bloqueos en una base de datos. No obstante, todavía hay más informacion útil en la tabla V$LOCK pero esto será objeto de otra nota en mi blog.