Java y Oracle 11g

PEDRO SALGUEIRO GÓMEZ 2011 Tabla de contenidos I. NOCIONES SOBRE BASES DE DATOS Y ORACLE 6 1. Arquitectura de Oracle D

Views 288 Downloads 1 File size 4MB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

PEDRO SALGUEIRO GÓMEZ 2011

Tabla de contenidos I. NOCIONES SOBRE BASES DE DATOS Y ORACLE 6 1. Arquitectura de Oracle Database 11g....................................... 6 1.1. Fundamentos de bases de datos. ........................................ 6 1.2. Fundamentos de bases de datos relacionales....................... 8 1.3. Bases de datos e instancias en Oracle. .............................. 11 1.4. Organización de las bases de datos en Oracle. .................. 11 1.5. Selección de arquitecturas y opciones. ............................. 14 2. Instalar Oracle Database 11g y crear una base de datos .......... 15 2.1. Descripción de la licencia y opciones de instalación. ........ 15 3. Servidor de Oracle ................................................................ 24 3.1. Elementos del servidor Oracle ......................................... 24 3.2. Conexiones. .................................................................... 24 3.3. Estructura de las bases de datos Oracle. ........................... 25 3.4. Instancia de la base de datos. ........................................... 25 3.5. Procesamiento de instrucciones SQL. .............................. 26 3.6. Archivos de inicialización ............................................... 27 3.7. Arranque y parada de la base de datos.............................. 28 4. Introducción a los conceptos del sistema Oracle .................... 29 4.1. Almacenamiento. ............................................................ 29 4.2. Transacciones. ................................................................ 29 4.3. Usuarios. ........................................................................ 29 4.4. Pérdidas de información. ................................................. 30 4.5. Copias de seguridad. ....................................................... 30 4.6. Bases de datos distribuidas. ............................................. 30 4.7. Herramientas de Oracle. .................................................. 30

II. LENGUAJE DE CONSULTAS.................................. 32 1. SQL para Oracle ................................................................... 32 1.1. Introducción. .................................................................. 32 1.2. Código SQL y normas de escritura. ................................. 32 1.3. SQL*Plus. ...................................................................... 32 1.4. Versión gráfica de SQL*Plus. ......................................... 33 1.5. iSQL*Plus. ..................................................................... 33 2. Estructura del lenguaje SQL.................................................. 34 2.1. Tipos de datos................................................................. 34 2.2. Operadores ..................................................................... 35 2.3. Funciones predefinidas.................................................... 36 3. Trabajando con objetos ......................................................... 52 3.1. Introducción. .................................................................. 52 3.2. Diccionario de datos de Oracle. ....................................... 52 3.3. Crear y usar bases de datos. ............................................. 53 3.4. Crear y usar tablespaces. ................................................. 53 3.5. Crear esquemas............................................................... 55 3.6. Crear y usar tablas. ......................................................... 56 3.7. Restricciones. ................................................................. 59 3.8. Crear y eliminar índices. ................................................. 61 3.9. Uso de tablas particionadas. ............................................ 64 3.10. Clústeres (o cubos)........................................................ 67 3.11. Secuencias. ................................................................... 68 3.12. Sinónimos..................................................................... 69 3.13. Inserción de registros. ................................................... 69 3.14. Actualización de registros. ............................................ 72 3.15. Eliminación de registros. ............................................... 72 3.16. Combinar registros con MERGE. .................................. 73 4. Consultas de selección .......................................................... 73 4.1. Consultas básicas. ........................................................... 74 4.2. Alias............................................................................... 74 4.3. Ordenar los registros. ...................................................... 74 4.4. Consultas con predicado.................................................. 75 4.5. Recuperación de valores calculados. ................................ 75 4.6. La cláusula «WHERE». .................................................. 76 4.7. Consultas que incluyen nulos .......................................... 76 5. Criterios de selección............................................................ 76 5.1. Operadores lógicos. ........................................................ 76 5.2. Intervalos de valores. ...................................................... 77 5.3. El operador «Like». ........................................................ 77 5.4. El operador «In». ............................................................ 77 6. Consultas sobre más de una tabla .......................................... 78

6.1. Reunión de una tabla consigo misma. .............................. 78 6.2. Consultas de unión internas. ............................................ 78 6.3. Consultas con operaciones de conjuntos........................... 80 7. Agrupaciones ....................................................................... 80 7.1. Funciones de agregado. ................................................... 80 7.2. La cláusula «Group by». ................................................. 83 8. Subconsultas ........................................................................ 84 8.1. Introducción. .................................................................. 84 8.2. Recuperación de datos con subconsulta............................ 84 8.3. Subconsultas correlacionadas. ......................................... 85 8.4. Subconsultas con operador de comparación distinto de «IN». .................................................................................... 85 8.5. Funciones de agregado en subconsultas. .......................... 86 8.6. Subconsultas con «EXISTS». .......................................... 86 8.7. Expresiones de columna con subconsultas ....................... 86 8.8. Subconsultas como origen de registros para «FROM». ..... 87 9. Vistas ................................................................................... 87 9.1. Introducción. .................................................................. 87 9.2. Crear y consultar vistas. .................................................. 88 9.3. Ejecución de comandos DML sobre vistas. ...................... 88 9.4. Estabilidad de una vista. .................................................. 88 9.5. Mostrar la lista de vistas. ................................................. 89 9.6. Borrar vistas. .................................................................. 89 10. Comandos internos en SQL*PLUS e iSQL*Plus .................. 89 10.1. Variables de sustitución................................................. 89 10.2. Comando «SET». .......................................................... 90 10.3. Encabezado y pie de informe. ........................................ 90 10.4. Comando «COLUMN». ................................................ 91 10.5. Comando «BREAK». .................................................... 91 10.6. Comando «COMPUTE». .............................................. 92 10.7. Guardar consultas en ficheros. ....................................... 93 10.8. Redirigir la salida de SQL*Plus con «SPOOL». ............. 93 11. Consultas avanzadas ........................................................... 94 11.1. Consultas con «ROWNUM». ........................................ 94 11.2. Consultas con «ROWID». ............................................. 94 11.3. Consultas con «RANK». ............................................... 95 11.4. Consultas sobre estructuras jerárquicas. ......................... 96 11.5. Consultas de agrupación avanzada. ................................ 98

III. PL/SQL.................................................................... 101 1. Estructura del lenguaje PL/SQL .......................................... 101 1.1. Fundamentos de PL/SQL. ............................................. 101 1.2. Estructuras de control en PL/SQL. ................................. 102 2. Bloques PL/SQL................................................................. 103 2.1. Introducción. ................................................................ 103 2.2. Estructura de un Bloque. ............................................... 103 2.3. Sección de declaración de variables. .............................. 104 2.4. El paquete «DBMS_OUTPUT». .................................... 105 2.5. Asignación de variables................................................. 106 3. Excepciones en PL/SQL. .................................................... 107 3.1. Manejo de excepciones. ................................................ 107 3.2. Excepciones predefinidas. ............................................. 107 3.3. Excepciones definidas por el usuario. ............................ 108 3.4. Uso de «SQLCODE» y «SQLERRM». .......................... 109 3.5. Excepciones personalizadas en PL/SQL. ........................ 109 3.6. Propagación de excepciones en PL/SQL. ....................... 109 4. Cursores ............................................................................. 109 4.1. Cursores implícitos. ...................................................... 110 4.2. Cursores explícitos. ....................................................... 110 4.3. Cursores con parámetros. .............................................. 111 4.4. Cursores de actualización. ............................................. 111 5. Subprogramas en PL/SQL................................................... 112 5.1. Permisos requeridos. ..................................................... 112 5.2. Procedimientos, funciones y paquetes. ........................... 113 5.3. Procedimientos almacenados. ........................................ 113 5.4. Funciones en PL/SQL. .................................................. 114 5.5. Subprogramas en bloques procedimentales. ................... 115 5.6. Depurando procedimientos. ........................................... 115

5.7. Paquetes en PL/SQL. .................................................... 115 5.8. Viendo el código fuente de objetos procedimentales....... 118 5.9. Compilando procedimientos, funciones y paquetes......... 118 6. Transacciones..................................................................... 119 6.1. Estado de los datos durante la transacción. ..................... 119 6.2. Control de transacciones en PL/SQL. ............................ 119 6.3. Puntos de ruptura. ......................................................... 120 6.4. Transacciones autónomas .............................................. 120 7. Triggers ............................................................................. 121 7.1. Permisos requeridos. ..................................................... 121 7.2. Tipos de triggers. .......................................................... 121 7.3. Triggers asociados a tablas. ........................................... 122 7.4. Triggers para eventos DDL. .......................................... 125 7.5. Triggers para eventos del sistema. ................................. 128 7.6. Triggers de sustitución. ................................................. 129 7.7. Activar y desactivar triggers. ......................................... 130 8. Tipos de datos complejos y operaciones masivas. ................ 131 8.1. Registros (RECORD).................................................... 131 8.2. Arrays asociativos (TABLE). ........................................ 132 8.3. Arrays variables (VARRAY)......................................... 133 8.4. Acceso masivo a los datos (BULK COLLECT). ............ 135 8.5. Funciones en línea. ....................................................... 136 8.6. Instrucción «FORALL». ............................................... 137 8.7. Objetos grandes (LOB). ................................................ 138 9. SQL Dinámico ................................................................... 142 9.1. Sentencias DML con SQL dinámico. ............................. 143 9.2. Cursores con SQL dinámico. ......................................... 143 9.3. Un ejemplo de cómo usar y cómo no usar SQL dinámico.146 10. PL/SQL y Java ................................................................. 146 10.1. Creación de Objetos Java en la base de datos ORACLE.146 10.2. Ejecución de programas Java con PL/SQL ................... 147 10.3. Correspondencia de tipos entre Java y Oracle. .............. 148 10.4. Paso de cursores Oracle a métodos de Java. ................. 148 10.5. Paso de objetos Oracle a métodos de Java. ................... 149 10.6. Paso de arrays desde un programa Java a un procedimiento almacenado de Oracle. .................................. 150 10.7. Cómo pasar y retornar un array de objetos a través de un procedimiento almacenado................................................... 151 10.8. Paquete «DBMS_JAVA». ........................................... 152

IV. CARACTERÍSTICAS DE ORACLE GRID........... 155

4.5. Cómo usar grupos de políticas. ...................................... 187 5. Trabajando con espacios de tabla......................................... 187 5.1. Tablespaces y la estructura de las bases de datos. ........... 188 5.2. Planificando el uso de nuestro tablespace. ...................... 196 6. Usar SQL*Loader para cargar datos .................................... 197 6.1. El fichero de control...................................................... 197 6.2. Comienzo de la carga. ................................................... 199 6.3. Sobre la sintaxis del fichero de control. .......................... 201 6.4. Administración de la carga de datos. .............................. 202 6.5. Ajustar la carga de datos. .............................................. 203 6.6. Funcionalidades adicionales. ......................................... 205 7. Importar y exportar con «Data Pump» ................................. 205 7.1. Creando un directorio.................................................... 205 7.2. Opciones de «Data Pump Export». ................................ 206 7.3. Iniciando una tarea de «Data Pump Export». .................. 207 7.4. Opciones para «Data Pump Import». ............................. 210 7.5. Iniciando una tarea de «Data Pump Import». .................. 211 8. Acceso a datos remotos ....................................................... 214 8.1. Enlaces de base de datos. .............................................. 214 8.2. Usando sinónimos para transparencia de localización. .... 218 8.3. Usando la pseudo-columna «USER» en vistas................ 219 8.4. Enlaces dinámicos: usando el comando de copia de SQL*Plus............................................................................ 220 8.5. Conectándose a una base de datos remota. ..................... 221 9. Vistas materializadas. ......................................................... 222 9.1. Funcionalidad. .............................................................. 222 9.2. Permisos requeridos. ..................................................... 222 9.3. Solo-lectura contra actualizable. .................................... 223 9.4. Sintaxis de creación de vistas materializadas. ................. 223 9.5. Usando vistas materializadas para modificar rutas de ejecución de consultas. ........................................................ 227 9.6. Usando «DBMS_ADVISOR». ...................................... 228 9.7. Refrescando vista materializadas. .................................. 229 9.8. Sintaxis para crear registros de vista materializada. ........ 233 9.9. Modificando vistas materializadas y registros. ............... 234 9.10. Eliminando vistas materializadas y registros. ............... 234 10. Oracle Text....................................................................... 235 10.1. Añadiendo texto a la base de datos. .............................. 235 10.2. Consultas de texto e índices de texto. ........................... 235 10.3. Conjuntos de índices. .................................................. 243 11. Uso de tablas externas....................................................... 244 11.1. Accediendo a datos externos. ....................................... 244 11.2. Creando una tabla externa. .......................................... 245 11.3. Modificación de tablas externas. .................................. 250 11.4. Limitaciones, beneficios y usos potenciales de las tablas externas............................................................................... 251 12. Consultas flashback .......................................................... 252 12.1. Ejemplo de consulta flashback basada en el tiempo. ..... 253 12.2. Guardando los datos. ................................................... 254 12.3. Ejemplo de consulta flashback basada en SCN. ............ 254 12.4. ¿Qué ocurre si falla una consulta flashback? ................ 255 12.5. ¿Qué SCN está asociado con cada registro? ................. 255 12.6. Consultas de versión flashback. ................................... 256 12.7. Planificación de las consultas flashback. ...................... 257 13. Tablas y bases de datos flashback ...................................... 258 13.1. El comando «FLASHBACK TABLE». ........................ 258 13.2. El comando «FLASHBACK DATABASE». ................ 260

1. Arquitectura de Rejilla ........................................................ 155 1.1. Hardware y elementos de configuración del sistema operativo. ............................................................................ 155 1.2. Añadiendo servidores a la rejilla. ................................... 157 1.3. Compartir datos entre la rejilla. ..................................... 157 1.4. Administración de la rejilla. .......................................... 158 1.5. Lanzar OEM. ................................................................ 159 2. Oracle Real Application Clusters ........................................ 160 2.1. Pasos de preinstalación. ................................................ 160 2.2. Instalación de RAC. ...................................................... 161 2.3. Inicia y parar instancias RAC. ....................................... 163 2.4. Transparencia de sobrefallos de aplicación..................... 164 2.5. Añadir nodos e instancias a un clúster. ........................... 165 2.6. Administración de registro y servicios del clúster. .......... 165 3. Seguridad en Oracle ........................................................... 166 3.1. Creación de usuarios. .................................................... 166 3.2. Eliminación de usuarios. ............................................... 167 V. SOPORTE DE OBJETOS Y XML ........................... 263 3.3. Gestión de contraseñas. ................................................. 167 3.4. Perfiles de usuario......................................................... 169 1. Modelo objeto-relacional de Oracle ..................................... 263 3.5. Cuentas de base de datos sobre cuentas del sistema 1.1. Tipos abstractos de datos (clases y objetos). ................... 263 operativo. ............................................................................ 170 1.2. Seguridad para tipos de datos abstractos. ....................... 264 3.6. Usuarios globales.......................................................... 170 1.3. Herencia de clases. ........................................................ 266 3.7. Usuarios con permisos especiales: SYSOPER y SYSDBA.171 1.4. Métodos. ...................................................................... 266 3.8. Roles estándar. ............................................................. 172 1.5. Tablas relacionales de objetos. ...................................... 269 3.9. Permisos del sistema. .................................................... 173 1.6. Tipos referencia (REF). ................................................. 271 3.10. Metadatos sobre permisos y usuarios. .......................... 181 1.7. Tablas anidadas y arrays variables. ................................ 272 4. Bases de datos virtuales privadas. ........................................ 181 1.8. Vistas de objeto. ........................................................... 274 4.1. Cómo implementar VPD a nivel de tabla. ...................... 182 1.9. Trabajando con tipos SQL desde aplicaciones JDBC. ..... 275 4.2. Cómo implementar VPD a nivel de columna. ................. 186 1.10. Crear y usar clases de objetos Java personalizadas para 4.3. Cómo desactivar VPD. .................................................. 186 objetos Oracle. .................................................................... 275 4.4. Contenido del paquete «SYS.DBMS_RLS». .................. 186 2. Documentos XML en Oracle............................................... 281

2.1. «XMLType». ................................................................ 281 2.2. Mapeado de «XMLType» dado un esquema XML. ........ 281 2.3. Crear tablas/columnas «XMLType». ............................. 282 2.4. Operaciones con columnas XMLType. .......................... 283 2.5. Validar los documentos XML sobre un esquema. ........... 286 2.6. Indexar elementos «XMLType». ................................... 287 2.7. SQLX, generar XML de los datos relacionales. .............. 287 2.8. Vistas «XMLType». ..................................................... 291

VI. PROCEDIMIENTOS DE GESTIÓN DE LA BASE DE DATOS. ................................................................... 294 1. Diccionario de datos de Oracle............................................ 294 1.1. Las vistas «DICTIONARY» (DICT) y «DICT_COLUMNS». ......................................................... 294 1.2. Cosas que podemos seleccionar de: tablas (y columnas), vistas, sinónimos y secuencias. ............................................ 295 1.3. Papelera: USER_RECYCLEBIN y DBA_RECYCLEBIN300 1.4. Restricciones y comentarios. ......................................... 300 1.5. Índices y clústeres. ........................................................ 303 1.6. Tipos de datos abstractos, estructuras ORDBMS y LOB's.306

1.7. Enlaces de base de datos y vistas materializadas. ............ 308 1.8. Triggers, procedimientos, funciones y paquetes.............. 310 1.9. Dimensiones. ................................................................ 311 1.10. Asignación y uso de espacio, incluyendo particiones y subparticiones. .................................................................... 312 1.11. Usuarios y permisos. ................................................... 316 1.12. Roles. ......................................................................... 318 1.13. Auditoría. ................................................................... 318 1.14. Supervisión: las tablas de rendimiento dinámico V$. .... 320 2. Administración de la base de datos. ..................................... 325 2.1. Creación de una base de datos. ...................................... 325 2.2. Iniciación y parado de la base de datos........................... 326 2.3. Tamaño y gestión de las áreas de memoria. .................... 326 2.4. Asignar y gestionar espacio para objetos. ....................... 327 3. Auditoría de Seguridad ....................................................... 329 3.1. Auditando conexiones. .................................................. 330 3.2. Auditando Acciones ...................................................... 330 3.3. Auditando objetos. ........................................................ 331 3.4. Protegiendo los registros de auditoría............................. 331

I. NOCIONES SOBRE BASES DE DATOS Y ORACLE 1. Arquitectura de Oracle Database 11g Oracle Database 11g es una actualización significativa de Oracle. Se han añadido nuevas funcionalidades para los programadores, administradores de base de datos, y los usuarios finales tienen un mayor control sobre el almacenamiento, procesamiento y recuperación de los datos. 1.1. Fundamentos de bases de datos. Todas las bases de datos relacionales manejan una serie de conceptos, los cuales pueden ser implementados de forma diferente en cada base de datos. ▪ Dato. Es un conjunto de caracteres con algún significado; que pueden ser numéricos, alfabéticos, o alfanuméricos. ▪ Información. Es un conjunto ordenado de datos, los cuales son manejados según la necesidad del usuario. Para que un conjunto de datos pueda ser procesado eficientemente y pueda dar lugar a información, primero se deben guardar lógicamente en archivos. ▪ Base de datos (BD).Es un conjunto de información relacionada que se organiza y estructura de alguna manera en archivos. En ese sentido, cualquier conjunto de fichas organizadas y guardadas en un archivador constituye una base de datos. En nuestro entorno cotidiano existen muchos ejemplos de bases de datos: registros de bibliotecas (con información sobre libros, lectores, préstamos, etc.), registros de empresas (con información sobre empleados, ocupaciones, productos, etc.), el censo de una población (con la información personal de sus habitantes), etc. La utilidad y eficacia de una base de datos depende de la forma en que se estructura la información que contiene. Por ejemplo, en un archivador, la información se distribuye en un número determinado de fichas que poseen la misma estructura.

▪ Registro. Es cada una de las fichas o filas de que consta una base de datos. ▪ Campos o atributos. Son cada una de las características diferenciadas que definen un registro. Cada registro o ficha está constituido por una serie de apartados en los que se introduce una determinada información (Nombre, Apellidos, Fecha, Dirección, Ocupación, etc.). ▪ Archivo o fichero. Es la unión de todos los registros con la misma estructura. ▪ Sistema Gestor de Base de Datos (SGBD). Es una colección de rutinas o programas interrelacionados, que permiten crear y manipular una base de datos. El objetivo primordial de un sistema gestor es proporcionar un entorno que sea a la vez conveniente y eficiente para ser utilizado al extraer, almacenar y manipular información de la base de datos. Todas las peticiones de acceso a la base de datos se manejan centralizadamente por medio del SGBD, por lo que este paquete funciona como una interfaz entre los usuarios y la base de datos. ▪ Esquema de base de datos. Es la estructura por la que está formada la base de datos. Se especifica por medio de un conjunto de definiciones que se expresa mediante un lenguaje especial llamado lenguaje de definición de datos (DDL). ▪ Administrador de base de datos (DBA). Es la persona o equipo de personas profesionales responsables del control y manejo del sistema de base de datos, generalmente tiene(n) experiencia en SGBD, diseño de bases de datos, Sistemas operativos, comunicación de datos, hardware y programación. 1.1.1. Objetivos de los sistemas de bases de datos. Los sistemas de base de datos se diseñan para manejar grandes cantidades de información. La manipulación de los datos involucra dos aspectos: Oracle /6

- la definición de estructuras para el almacenamiento de la información, y - aportar mecanismos para la manipulación de la información. Además, un sistema de base de datos debe de tener implementados mecanismos de seguridad que garanticen la integridad de la información, bien ante caídas del sistema o bien ante intentos de accesos no autorizados. Por tanto, el objetivo principal de un sistema de base de datos es minimizar los siguientes aspectos: ▪ Redundancia e inconsistencia de datos. Puesto que los archivos que mantienen almacenada la información son creados por diferentes tipos de aplicaciones, existe la posibilidad de que si no se controla detalladamente el almacenamiento, se pueda originar un duplicado de información (que la misma información esté en más de un soporte). Esto aumenta los costes de almacenamiento y acceso a los datos, además de que puede originar la inconsistencia de los datos. ▪ Dificultad para tener acceso a los datos. Un sistema de base de datos debe contemplar un entorno de datos que le facilite al usuario el manejo de los mismos. Supóngase un banco, y que uno de los gerentes necesita averiguar los nombres de todos los clientes que viven en una zona con el código postal 78733. El gerente pide al departamento de procesamiento de datos que genere la lista correspondiente. Si esta situación no fue prevista en el diseño del sistema, obtener tal lista se convertirá en una tarea difícil. ▪ Aislamiento de los datos. Puesto que los datos están repartidos en varios archivos, y éstos pueden tener diferentes formatos, es difícil crear nuevos programas para obtener los datos apropiados. ▪ Anomalías del acceso concurrente. Para mejorar el funcionamiento global del sistema y obtener un tiempo de respuesta más rápido, muchos sistemas permiten que múltiples usuarios actualicen los datos simultáneamente. En un entorno así, la interacción de actualizaciones concurrentes puede dar por resultado datos inconsistentes. Para prevenir esta posibilidad debe mantenerse alguna forma de supervisión en el sistema. ▪ Problemas de seguridad. La información de toda empresa es importante, aunque unos datos lo son más que otros; por tal motivo se debe considerar el control de acceso a los mismos. No todos los usuarios podrán visualizar determinada información; y por tal motivo, para que un sistema de base de datos sea confiable, debe mantener un grado de seguridad que garantice la autentificación y protección de los datos. ▪ Problemas de integridad. Los valores de datos almacenados en la base de datos deben satisfacer cierto tipo de restricciones de consistencia. Estas restricciones se hacen cumplir en el sistema añadiendo códigos apropiados en los diversos programas. 1.1.2. Abstracción de la información. Una base de datos es en esencia una colección de archivos relacionados entre sí, de la cual los usuarios pueden extraer información sin que tengan que conocer la estructura interna de los archivos. Un objetivo importante de un sistema de base de datos es proporcionar a los usuarios una visión abstracta de los datos; es decir, el sistema debe esconder ciertos detalles de cómo se almacenan y mantienen los datos. Sin embargo, para que el sistema sea manejable, los datos se deben extraer eficientemente. Existen diferentes niveles de abstracción para simplificar la interacción de los usuarios con el sistema: ▪ Nivel físico. Es la representación del nivel más bajo de abstracción; en éste se describe en detalle la forma en cómo se almacenan los datos en los dispositivos de almacenamiento (por ejemplo, mediante índices para el acceso aleatorio a los datos). ▪ Nivel conceptual. El siguiente nivel más alto de abstracción describe qué datos son almacenados realmente en la BD y las relaciones que existen entre los mismos. Describe completamente la base de datos en términos de su estructura de diseño. El nivel conceptual de abstracción lo usan los administradores de BD, quienes deben decidir qué información se va a guardar en la base de datos. Consta de las siguientes definiciones: 1) Definición de los datos: se describen el tipo de dato y sus características. 2) Relaciones entre datos: se definen las relaciones entre datos, para enlazar tipos de registros relacionados, para su procesamiento posterior. ▪ Nivel de visión. Es el nivel más alto de abstracción; es lo que el usuario final puede visualizar del sistema terminado. Sólo describe una parte de la BD según el usuario acreditado para verla. El sistema puede proporcionar muchas visiones para la misma BD. La interrelación entre estos tres niveles de abstracción se ilustra en la siguiente figura.

Oracle /7

Nivel de visión Vista 1

...

Vista n

Nivel conceptual

Nivel físico

1.2. Fundamentos de bases de datos relacionales. El modelo relacional de base de datos nace en 1970, cuando Edgar Codd escribe el artículo "A relational model of data for large strared data tanks". Es a partir de 1980 cuando aparecen los primeros gestores de base de datos cuyo modelo de datos subyacente es el relacional. El modelo relacional conecta registros mediante los valores que éstos contienen. De hecho, Codd propone una estructura tabular (correspondiente a tablas) para representar los datos. Es decir, si queremos representar toda la información contenida en los registros de un archivo podemos utilizar una estructura de tabla. Por ejemplo, la información del censo de una población podemos representarla de la siguiente manera: DNI 23444325 65335544 11143442 66442444

Nombre Pedro Salgueiro José Martínez Esther López José Martínez

Fecha 20/12/64 14/07/72 03/07/72 12/10/65

Ocupación Profesor Carpintero Carpintero Ingeniero

Como vemos, los registros se disponen por filas mientras que los campos se disponen por columnas. Esta forma particular de estructurar y representar la información se conoce como base de datos relacionales. Cada tabla es la representación física de una entidad o una relación, y se corresponde con un archivo o fichero de la BD. Cada fila de la tabla se corresponde con un registro, llamado también intensión o tupla. Cada columna se corresponde con los valores de un campo o atributo, siendo éstos una característica distinguible de una entidad o relación. Cada atributo tiene asignado un dominio, del cual tomará valores. 1.2.1. Campos clave. Las BD relacionales se basan en un concepto fundamental: cada registro de la tabla debe ser único, no pudiendo haber registros repetidos. Para asegurar esta circunstancia surge el concepto de clave, como aquel campo de la tabla cuyo valor para cada registro es único. En el ejemplo anterior podemos comprobar que existen nombres, apellidos y ocupaciones repetidas en varios registros; por lo tanto, ninguno de estos tres campos puede ser clave. Vemos que ninguna fecha se repite, pero nada nos asegura que no podamos insertar dos personas que hayan nacido en la misma fecha. Sin embargo, el DNI de una persona, por su propia definición, suele ser un valor único. Por tanto el campo DNI sí puede ser la clave para esta tabla. Puede darse el caso de que en una tabla ninguno de los campos sea clave. Entonces podemos probar a unir varios campos, de forma que el valor conjunto de estos campos sea único. En el ejemplo anterior podemos considerar que el valor conjunto de los campos Nombre y Fecha no suele repetirse (es decir, no suele haber dos personas con el mismo nombre y que han nacido en la misma fecha). En ese caso, los campos (Nombre, Fecha) constituyen una clave compuesta. Si en una tabla no existen claves simples ni claves compuestas, debemos inventarnos un nuevo campo que actúe como clave. Este nuevo campo normalmente es un código o identificador numérico que se va asignando a cada nuevo registro que se introduce en la tabla, de forma que nunca se repita. 1.2.2. Operaciones básicas sobre registros. Con los registros de una tabla podemos realizar las siguientes operaciones básicas: ▪ Añadir un registro. Cuando añadimos un nuevo registro en la tabla debemos comprobar que el valor (o conjunto de valores) de la clave no esté repetido. Si otro registro posee esa clave no podrá añadirse el nuevo registro. Oracle /8

▪ Borrar un registro. No existen limitaciones para borrar un registro. (Normalmente los registros no se borran inmediatamente de la tabla, sino que internamente se les pone una marca para indicar que están pendientes de borrado.) ▪ Actualizar un registro. Se puede modificar la información de un registro en cualquiera de sus campos, excepto en los campos que pertenezcan a la clave. 1.2.3. Operaciones relacionales sobre tablas. Podemos realizar las siguientes operaciones sobre una tabla: ▪ La Intersección ( ∩ ). Sólo es aplicable sobre tablas con esquemas similares. Produce una nueva tabla con el mismo esquema, y que contendrá los registros comunes en ambas tablas.

▪ La Unión ( U ). También es sólo aplicable a tablas con el mismo esquema. Produce una nueva tabla con el mismo esquema y con todos los registros de ambas tablas (excluyendo los registros repetidos).

▪ Diferencia ( – ). Produce una nueva tabla con aquellos registros de la primera tabla que no pertenecen a la segunda tabla.

1.2.4. Operaciones propias del modelo relacional. ▪ Selección ( ∑). Se aplica sobre una tabla, y produce una nueva tabla con aquellos registros que en algunos atributos cumplen una condición determinada.

▪ Proyección ( ∏ ). Aplicada sobre una tabla, produce una nueva tabla con todos los registros de la original, pero cuyo esquema contiene sólo alguno de los atributos de la tabla original.

▪ Join ( ). Se aplica sobre dos tablas de distinto esquema pero con atributos comunes (o compatibles). Genera una nueva tabla que es el producto cartesiano de ambas tablas, seleccionando los registros con valores idénticos en los atributos comunes y eliminando las columnas repetidas.

1.2.5. Relaciones entre tablas. La gran potencia y eficacia de las bases de datos relacionales se comprueba cuando debemos asociar la información contenida en dos o más tablas relacionadas. Consideremos el ejemplo de una concesionaria de automóviles, la cual posee una base de datos con información sobre sus clientes y los coches que vende. En este caso, la base de datos se compone de dos tablas: una con los datos de los clientes, y otra con los datos de coches disponibles.

Oracle /9

TABLA DE CLIENTES DNI Nombre Apellidos 11252111 PEDRO PÉREZ 12323253 JUAN GARCÍA 56344323 LUÍSA GÓMEZ

TABLA DE COCHES Matrícula Marca M-2345-AF OPEL M-3443-HW CITROEN M-1278-HZ CITROEN

TipoPago CONTADO PLAZOS PLAZOS

Precio 1200000 2100000 1500000

La clave de la tabla CLIENTES es el campo DNI, y la clave de la tabla COCHES es Matrícula. Aunque estas dos tablas recogen toda la información disponible sobre clientes y coches, no tenemos manera de saber qué clientes han comprado qué coches. Es necesario relacionar estas dos tablas asociando cada registro de un cliente al registro del coche que ha comprado. Para relacionar tablas en una BD relacional surge el concepto de clave foránea. En una de las tablas se añade un nuevo campo que se corresponda con la clave de la otra tabla, actuando el nuevo campo como clave foránea. Existen cuatro tipos de relaciones posibles: ▪ Relación uno a uno. Sólo podemos asociar un registro de la primera tabla con un registro de la segunda tabla y viceversa. En nuestro ejemplo significa que un cliente sólo puede comprar un coche y un coche sólo puede ser comprado por un cliente. Esta relación se resuelve introduciendo en una de las tablas, como clave foránea, la clave de la otra tabla. Tenemos dos soluciones para nuestro ejemplo. CLIENTES

COCHES

CLIENTES

1 DNI Nombre Apellidos TipoPago Matrícula

COCHES 1

Matrícula Marca Precio

DNI Nombre Apellidos TipoPago

1

1

Matrícula Marca Precio DNI

▪ Relación uno a varios. Podemos asociar un registro de la primera tabla con varios registros de la segunda tabla, y un registro de la segunda tabla con un registro de la primera. En nuestro ejemplo significa que un cliente puede comprar varios coches, pero un coche sólo puede ser comprado por un cliente. Se resuelve introduciendo en la segunda tabla la clave de la primera. CLIENTES

COCHES 1

DNI Nombre Apellidos TipoPago



Matrícula Marca Precio DNI

▪ Relación varios a uno. Esta relación es análoga a la anterior. Indica que un cliente sólo puede comprar un coche, pero un coche puede ser comprado por varios clientes. Se resuelve introduciendo en la primera tabla la clave de la segunda. CLIENTES

COCHES 1

DNI Nombre Apellidos TipoPago Matrícula

Matrícula Marca Precio



▪ Relación varios a varios. Podemos asociar un registro de la primera tabla con varios registros de la segunda tabla, y podemos asociar un registro de la segunda tabla con varios de la primera. En nuestro ejemplo significa que un cliente puede comprar varios coches y un coche puede ser comprado por varios clientes. Esta relación se resuelve creando una nueva tabla cuya estructura hereda los campos claves de las tablas relacionadas. Para nuestro ejemplo crearemos la tabla COMPRA, con dos campos: - DNI, clave foránea heredada de la tabla CLIENTES. - Matrícula, clave foránea heredada de la tabla COCHES. La clave de esta nueva tabla puede estar formada por ambos campos, y por lo tanto será compuesta; o bien podemos añadir una nuevo campo que actúe de clave. Oracle /10

CLIENTES DNI Nombre Apellidos TipoPago Matrícula

COMPRA 1



DNI Matrícula

COCHES 1 

Matrícula Marca Precio

1.2.6. Reglas de integridad. En las bases de datos relacionales deben cumplirse dos reglas fundamentales: 1ª REGLA (Integridad de entidad): dice que ningún campo que forme parte de una clave puede carecer de valor (o dicho en términos informáticos; no puede tomar el valor nulo). Esto es así porque si un registro careciese de valor en su clave ya no podría ser identificado de forma única. 2ª REGLA (Integridad referencial): dice que no se puede introducir en una clave foránea valores que no pertenezcan a la clave de la tabla referenciada. No tiene sentido indicar en una tabla referenciada valores que no existen en la tabla principal. En el ejemplo de la concesionaria, no tiene sentido indicar que un coche ha sido comprado por un cliente del cual no se tiene ninguna referencia. La segunda regla puede crear problemas en el caso de que se quieran borrar registros con una clave foránea referenciada por otra tabla. Para evitar este problema existen tres soluciones: - Prohibir el borrado. Por ejemplo, no se podrán eliminar clientes que hayan comprado algún coche. - Borrar en cascada. Por ejemplo, si se elimina un cliente, se eliminarán también todos los coches comprados por ese cliente. - Nulificar. Por ejemplo, si se elimina un cliente, en los registros de coches vendidos a ese cliente se pondrá un valor nulo en el DNI. 1.2.7. Diseño de una base de datos relacional. Una vez establecido un problema, para diseñar una BD relacional debemos seguir los siguientes pasos: 1) Decidir cuántas tablas necesitamos. Normalmente se creará una tabla por cada entidad que podamos distinguir en nuestro problema. 2) Definir la estructura de cada tabla. De qué campos se componen y cuál es la clave. 3) Determinar para cada campo su tipo de dato, su tamaño (si es necesario) y los valores posibles que podemos asignarle (su dominio). 4) Establecer las relaciones entre las tablas. Para ello debemos incluir en cada tabla las claves foráneas que sean necesarias o crear nuevas tablas. Una vez diseñada la base de datos podemos crearla utilizando un gestor de base de datos. 1.3. Bases de datos e instancias en Oracle. Una base de datos de Oracle es una colección de datos en uno o más archivos. La base de datos de Oracle contiene estructuras físicas y lógicas. Durante el desarrollo de una aplicación podemos crear estructuras como tablas e índices para almacenar filas y acelerar su recuperación. Podemos crear sinónimos para los nombres de objetos, vistas de objetos en varias bases de datos y podemos restringir el acceso a los objetos. También podemos usar tablas externas para acceder a ficheros fuera de la base de datos como si las filas en los ficheros fuesen filas de tablas. Una instancia de Oracle comprende un área de memoria llamada Área Global del Sistema (SGA) y los procesos de fondo que interactúan entre el SGA y los ficheros de la base de datos en disco. En una "Real Application Cluster de Oracle" (RAC) más de una instancia será usada sobre la misma base de datos; las instancias generalmente estarán sobre servidores independientes conectados mediante una interconexión de alta velocidad. 1.4. Organización de las bases de datos en Oracle. Dentro de una base de datos de Oracle, la estructura básica en la tabla. Oracle Database 11g soporta muchos tipos de tablas, incluyendo las siguientes: ■ Tablas relacionales. Usando los tipos de datos soportados por Oracle, podemos crear tablas para almacenar las filas insertadas y manipularlas en nuestras aplicaciones. Las tablas tienen definiciones de columna, y podemos añadir o quitar columnas según los requerimientos de nuestras aplicaciones. ■ Tablas objeto-relacionales. Para tomar ventajas de funcionalidades como la herencia de tipo, podemos Oracle /11

usar las capacidades objeto-relacional de Oracle. Podemos definir nuestros propios tipos de datos y usarlos como base para la definición de columnas, tablas de objetos, tablas anidadas, arrays variables y más. ■ Tablas organizadas por índice. Podemos crear una tabla que almacene sus datos como una estructura de índice, permitiendo que los datos sean ordenados dentro de la tabla. ■ Tablas externas. Los datos almacenados en ficheros planos pueden ser tratados como tablas que los usuarios pueden consultar directamente y relacionar con otras tablas en consultas. Podemos usar tablas externas para acceder a grandes volúmenes de datos sin tener que cargarlos en nuestra base de datos. Oracle también soporta tipos de datos BFILE, un puntero a un fichero binario externo. ■ Tablas particionadas. Podemos dividir una tabla en varias particiones, lo cual permite controlar independientemente cada parte de la tabla. Podemos añadir una nueva partición a una tabla, separar particiones existentes, y administrar una partición a parte de otra partición de la tabla. El particionado puede simplificar o mejorar el rendimiento de actividades de mantenimiento y de las consultas de usuario. Podemos particionar tablas según rangos de valores, según una lista de valores, según códigos de valores de columna, o según una combinación de estas opciones. ■ Vistas materializadas. Una vista materializada en una réplica de los datos recuperados por una consulta. Las consultas de usuario pueden ser redireccionadas a las vistas materializadas para evitar tablas largas durante la ejecución (el optimizador rescribirá las consultas automáticamente). Podemos establecer y controlar tareas de refresco para obtener los datos actualizados en las vistas materializadas según las necesidades del negocio. ■ Tablas temporales. Podemos usar tablas temporales globales para crear una tabla en la cual varios usuarios puedan insertar registros. Cada usuario sólo verá sus filas en la tabla. ■ Tablas de clúster (o cubo).Si dos tablas son normalmente consultadas conjuntamente, podemos almacenarlas físicamente juntas a través de una estructura llamada clúster (o cubo). ■ Tablas eliminadas. Desde Oracle Database 10g, podemos recuperar rápidamente tablas eliminadas mediante el comando DROP. Podemos recuperar varias tablas de una vez o recuperar toda la base de datos en un momento concreto. Oracle soporta consultas de deshacer, las cuales retornan versiones anteriores de filas en una tabla existente. Como soporte en el acceso a las tablas, podemos usar vistas que realicen combinaciones y agregaciones, limitar las filas retornadas, o modificar las columnas mostradas. Las vistas pueden ser de sólo lectura o modificables, y pueden referenciar tablas locales y remotas. Las tablas remotas pueden ser accedidas a través de enlaces a bases de datos. Podemos usar sinónimos para enmascarar la localización física de las tablas. Para controlar los accesos a las tablas, Oracle soporta muchos tipos de índices, incluyendo los siguientes: ■ Índices B*-tree. Un índice B*-tree es el tipo estándar de índices disponibles en Oracle, y es muy usado para seleccionar filas por un criterio de equivalencia o una criterio de rango. ■ Índices Bitmap. Para columnas que tienen pocos valores únicos, un índice bitmap puede mejorar el rendimiento de las consultas. Los índices bitmap deberían usarse sólo cuando los datos se cargan por lotes (como en muchos depósitos de datos o aplicaciones de informes). ■ Índices de clave inversa. Si hay problemas de contención de E/S durante la inserción de valores secuenciales, Oracle puede invertir dinámicamente los valores de índice antes de almacenarlos. ■ Índices basados en funciones. En vez de indexar una columna, como Nombre, podemos indexar una columna basada en una función, como UPPER(Nombre). Este tipo de índice tiene opciones adicionales del optimizador de Oracle cuando seleccionamos una ruta de ejecución. ■ Índices particionados. Podemos particionar índices para soportar tablas particionadas o para simplificar la gestión de índices. Los índices particionados pueden ser locales para cada partición de la tabla o pueden aplicarse globalmente a todas las filas de la tabla. ■ Índices de texto. Podemos indexar valores de texto para soportar capacidades de búsqueda avanzada, como palabras derivadas o búsqueda de frases. Los índices de texto son conjuntos de tablas e índices mantenidos por Oracle para soportar requerimientos de búsqueda de texto complejos. Oracle Database 11g ofrece facilidades para indexar texto que simplifique su administración y mantenimiento. 1.4.1. Almacenando los datos. Toda la estructura lógica de una base de datos debe ser almacenada en algún sitio dentro de la base de datos. Oracle mantiene una diccionario de datos que registra los metadatos acerca de cada objeto (el propietario del objeto, una definición, privilegios relacionados, y cosas así). Para los objetos que requieren un espacio de almacenamiento físico de sí mismos, Oracle reserva espacio dentro de un tablespace. Oracle /12

Tablespaces. Un tablespace consiste de uno o más ficheros de datos; un fichero de datos puede ser parte de un y solo un único tablespace. Oracle Database 11g crea al menos dos tablespaces para cada base de datos (SYSTEM y SYSAUX) para soportar las necesidades de administración interna. Podemos usar el «Administrador de Ficheros de Oracle» (OMF) para simplificar la creación y mantenimiento de ficheros de datos. A partir de Oracle Database 10g, podemos crear un tipo especial de tablespace, llamado "bigfile tablespace", que puede tener muchos miles de terabytes de tamaño. Con OMF, la administración de bigfiles hace la gestión de tablespace completamente transparente para el administrador de base de datos (DBA); el DBA puede administrar los tablespace como una unidad sin preocuparse sobre el tamaño y estructura de los ficheros de datos subyacentes. Si un tablespace esta designado como un tablespace temporal, el tablespace mismo es permanente; sólo los segmentos guardados en el tablespace son temporales. Oracle usa tablespaces temporales para soportar operaciones de ordenación en la creación de índices y procesos de combinación. Los segmentos temporales no deberían ser almacenados en el mismo tablespace como objetos permanentes. Los tablespaces pueden ser gestionados en diccionario o gestionados localmente. En una gestión de diccionario, el espacio gestionado es registrado en el diccionario de datos. En una gestión local (por defecto en Oracle Database 11g), Oracle mantiene una mapa de cada fichero de datos del tablespace para rastrear la disponibilidad de espacio. En el diccionario de datos sólo se gestionan cuotas, lo cual reduce dramáticamente la contención de tablas del diccionario de datos. Gestión automática de almacenamiento. La gestión automática de almacenamiento (ASM), disponible desde Oracle Database 10g, automatiza el diseño de ficheros de datos y otros ficheros del nivel del sistema operativo usados por la base de datos, distribuyéndolos entre los disco disponibles. Cuando un nuevo disco es añadido a la instancia ASM, los ficheros de datos son automáticamente redistribuidos a través de todos los discos en los grupos de discos definidos para optimizar el rendimiento. Las características de multiplexión de una instancia ASM minimizan la posibilidad de pérdida de datos y es generalmente más efectiva que un esquema manual que pone ficheros críticos y ficheros de respaldo en diferentes unidades físicas. Gestión automática de deshacer. Para soportar nuestras transacciones, Oracle puede crear y administrar dinámicamente segmento de deshacer, los cuales ayudan a mantener imágenes prioritarias de los bloques y filas cambiadas. Los usuarios que consultaron previamente las filas que hemos cambiado todavía verán las filas tal como existían cuando las consultaron. La gestión automática de deshacer (AUM) permite a Oracle administrar los segmentos de deshacer directamente sin la intervención del administrador de base de datos. El uso de AUM también simplifica el uso de consultas de flashback. Desde Oracle Database 10g, podemos ejecutar consultas de versiones pasadas para ver diferentes versiones de un fichero tal como ha cambiado durante un intervalo de tiempo específico. Datos eliminados. El concepto de cajón de reciclado introducido con Oracle Database 10g afecta a las exigencias de espacio requerido para nuestros tablespaces y ficheros de datos. En Oracle Database 11g el comportamiento por defecto para el borrado de una tabla es que la tabla retenga su espacio asignado; podemos ver este espacio usando la vista RECYCLEBIN del diccionario de datos. Si creamos y borramos una tabla dos veces, habrá dos copias de la tabla en el cajón de reciclado. Aunque esta arquitectura simplifica enormemente las recuperaciones de borrados de tablas accidentales, puede incrementar considerablemente el espacio usado por la base de datos. Se usa el comando PURGE para quitar entradas antiguas del cajón de reciclado. 1.4.2. Guardando los datos. Podemos controlar completamente el acceso a nuestros datos. Podemos conceder privilegios a otros usuarios para realizar funciones específicas (como seleccionar, insertar y más) sobre nuestros objetos. Podemos adquirir privilegios desde roles, los cuales son entonces concedidos a los usuarios, agrupando así privilegios dentro de conjuntos manejables. Oracle soporta un nivel muy detallado de permisos; podemos controlar qué filas son accesibles y, durante auditoría, qué filas desencadenan eventos de auditoria que sean registrados. Cuando se usa la opción Base de datos Privada Virtual (VPD), las consultas de usuario sobre tablas son siempre limitadas independientemente del método a través del cual se accede a las tablas. Desde Oracle Database 10g, VPD ha ido más lejos incluyendo enmascaramiento de columna para columnas que contienen datos sensibles. Oracle /13

Además del acceso seguro a los datos, podemos auditar actividades en la base de datos. Los eventos auditables incluyen acciones privilegiadas (como crear usuarios), cambios en las estructuras de datos y accesos a filas y tablas específicas. 1.4.3. Soporte para programación. Oracle soporta una gran cantidad de métodos de acceso a programación. El lenguaje SQL es la clave para cualquier esfuerzo de programación de aplicaciones. Otros métodos de acceso incluyen los siguientes: ■ PL/SQL.PL/SQL es un componente crítico en la implementación de muchas aplicaciones. Podemos usar PL/SQL para crear procedimientos y funciones almacenados, y entonces podemos llamar a las funciones dentro de consultas. Los procedimientos y funciones pueden ser recolectados dentro de paquetes. También podemos crear desencadenadores (triggers), los cuales dicen a la base de datos qué pasos deben seguir con varios eventos que ocurren dentro de la base de datos. Los triggers pueden ocurrir durante eventos de la base de datos (como el acceso inicial a la base de datos), cambios en la estructura (como un intento de borrar tablas), o cambios en las filas. En cada caso, se usa PL/SQL para controlar el comportamiento de la base de datos o aplicaciones en los cuales ocurren los eventos. ■ SQL Dinámico. Podemos generar SQL en tiempo de ejecución y pasarlo a procedimientos que lo ejecuten a través de SQL Dinámico. ■ SQL*Plus. SQL*Plus proporciona una interfaz sencilla para las bases de datos de Oracle. SQL*Plus puede soportar requerimientos de informes rudimentarios, pero es mejor conocerlo para soportar scripting. Proporciona una interfaz consistente para recuperar datos del diccionario de datos y crear objetos en la base de datos. ■ Java y JDBC. Oracle da soporte a Java y JDBC, permitiéndonos usar Java en lugar de PL/SQL en muchas operaciones. Podemos todavía escribir procedimientos almacenados basados en Java. Las capacidades del Java de Oracle han sido ampliadas y realzadas con cada nueva versión. ■ XML. Podemos usar interfaces y tipos XML de Oracle para soportar la inserción y recuperación de datos en formato XML. ■ SQL y PL/SQL orientados a objetos. Podemos usar Oracle para crear y acceder a estructuras orientadas a objetos, incluyendo tipos de datos definidos por el usuario, métodos, objetos grandes (LOB's), tablas de objetos, y tablas anidadas. ■ Data Pump. «Data Pump Import» y «Data Pump Export», ambos introducidos en Oracle Database 10g, realzan enormemente la manejabilidad y rendimiento de las recientes utilidades de importación y exportación. Podemos usar Data Pump para extraer rápidamente datos y moverlos a diferentes bases de datos mientras alteramos el esquema y cambiamos las filas. ■ SQL*Loader. Podemos usar SQL*Loader para cargar rápidamente ficheros planos dentro de tablas de Oracle. Un único fichero plano puede ser cargado dentro de varias tablas durante la misma carga, y la carga puede ser paralela. ■ Programas y procedimientos externos. Podemos embeber SQL dentro de programas externos, o podemos crear librerías de procedimientos que más tarde sean enlazadas a Oracle. ■ UTL_MAIL. Un paquete introducido en Oracle Database 10g, UTL_MAIL, permite a programadores de aplicaciones PL/SQL enviar correos electrónicos sin tener que conocer la pila de protocolos SMTP subyacente. 1.5. Selección de arquitecturas y opciones. Oracle proporciona un completo conjunto de herramientas para programar aplicaciones basadas en Oracle Database 11g. Podemos usar el servidor de aplicaciones de Oracle como la capa intermedia para aplicaciones de tres capas que acceden a Oracle Database 11g. Muchas de las funcionalidades introducidas con Oracle Database 11g estarán disponibles independientemente de la arquitectura de aplicación seleccionada. Estas funcionalidades incluyen administración de base de datos como gestión automática de almacenamiento, tuneado automático, y redimensionado automático de las áreas de memoria en el SGA. Oracle proporciona un conjunto de procedimientos que ayudan a gestionar la planificación de refresco de las vistas materializadas. Por ejemplo, podemos ejecutar un procedimiento que genere una descripción de los problemas con los refrescos y las configuraciones que nos impiden usar las opciones más rápidas posibles. Podemos usar otro procedimiento de Oracle para generar recomendaciones para tunear estructuras de vistas materializadas según un conjunto de consultas previstas. Algunas nuevas funcionalidades pueden contener pequeños cambios que pueden tener un gran impacto en Oracle /14

nuestra aplicación o nuestro código. Por ejemplo, desde Oracle Database 10g están disponibles búsquedas usando expresiones regulares.

2. Instalar Oracle Database 11g y crear una base de datos El software de instalación del Oracle se hace más fácil de usar con cada nueva versión; basta con abrir la caja de cedés y comenzar la instalación enseguida. Si queremos experimentar con alguna nueva funcionalidad de base de datos, se requiere una mayor planificación para realizar una instalación sucesiva sin tener que hacer todo el trabajo de reinstalación. En este capítulo veremos lo básico de una instalación de Oracle usando el Instalador Universal de Oracle (OUI), así como una plantilla básica para hacer una instalación manual de la base de datos usando el comando CREATE. Las siguientes cosas deben ser resueltas antes de empezar la instalación: • Decidir un nombre de base de datos local, y en qué dominio será contenido la base de datos. Estos nombres serán asignados en los parámetros de inicialización DB_NAME y DB_DOMAIN. • Para el primer proyecto que usará la base de datos, estimar el número de tablas e índices, así como su tamaño, para planificar el espacio en disco además del requerido por los tablespace SYSTEM y el software y herramientas asociadas de Oracle. • Planificar la ubicación de los ficheros de datos físicos en el disco del servidor para maximizar el rendimiento y recuperación. En general, cuantos más disco físicos mejor. Si un RAID o Almacén de red son usados por los ficheros de datos, podemos usar el «Administrador de Ficheros de Oracle» para gestionar la ubicación de los ficheros de datos. Desde Oracle Database 10g podemos usar almacenamiento automático (ASM) para simplificar la gestión de almacenamiento. • Revisar y comprender los parámetros básicos de inicialización. • Seleccionar el juego de caracteres de la base de datos, con un juego de caracteres alternativos. Aunque podemos dejar el juego de caracteres por defecto aplicado durante la instalación, podemos necesitar considerar dónde estarán localizados los usuarios de la base de datos y sus requerimientos de idioma. El juego de caracteres puede ser cambiado después de la instalación sólo si el nuevo juego de caracteres es un superjuego del existente. • Decidir el mejor tamaño por defecto de los bloques de base de datos. El tamaño por defecto definido por DB_BLOCK_SIZE no puede ser cambiado después sin reinstalar la base de datos. Oracle puede soportar varios tamaños de bloques dentro de una única base de datos. • Planificar el almacenamiento de objetos de usuarios distintos de SYSTEM en tablespaces distintos de SYSTEM. Hay que asegurarse de que todos los usuarios no administrativos son asignados a un tablespace distinto de SYSTEM por defecto. • Planificar la implementación del «Administrador Automático de Deshacer» para que sea fácil la administración de transacciones que rehagan al información. • Planificar una estrategia de copias de seguridad y recuperación. Decidir cómo la base de datos necesita ser recuperada, y cuánto a menudo. Planificar usar más de un método para recuperar la base de datos. 2.1. Descripción de la licencia y opciones de instalación. Una instalación inicial acertada del software es el primer paso. Independientemente de la plataforma de software y hardware sobre la cual decidamos instalar Oracle, los tipos de instalaciones que podemos realizar son los mismos. Aunque puede haber cambios en cada versión del producto, normalmente se incluyen los siguientes tipos de instalación: • Edición Empresarial. Es la versión más extensa, con más funcionalidades. Incluye funciones como Flashback Database y permite añadir piezas adicionales de funcionalidades con licencia, como Oracle Spatial, Oracle OLAP, Oracle Label Security, y Oracle Data Mining. • Edición Estándar. Esta versión proporciona un buen subconjunto de funcionalidades, incluyendo las que necesitan la mayoría de negocios. • Edición Personal. Esta versión permite el desarrollo de aplicaciones que se ejecutarán sobre la Edición Estándar o Empresarial. Esta edición no puede ser usada en un entorno de producción. Desde Oracle Database 10g, la licencia de Oracle es sólo para un nombre de usuario o una CPU, y no hay la opción de licenciar a usuarios concurrentes. Por lo tanto, el DBA debería usar el parámetro de inicialización LICENSE_MAX_USERS para especificar el número máximo de usuarios que pueden ser creados en la base de datos. Como resultado, los parámetros LICENSE_MAX_SESSIONS y LICENSE_SESSIONS_WARNING están obsoletos en Oracle Database 11g. Oracle /15

Además, el servidor de Oracle puede ser instalado durante una instalación del lado servidor o cliente. Sin embargo, es recomendable que esta instalación sea realizada después de que una instalación básica sea completada. 2.1.1. Usando OUI para instalar el software de Oracle. Usaremos el «Instalador Universal de Oracle» (OUI) para instalar y administrar todos los componentes de Oracle tanto en el lado servidor como el lado cliente. Podemos también desinstalar cualquier producto de Oracle desde la pantalla inicial del OUI. Durante la instalación del servidor, podemos elegir la versión de Oracle Database 11g: Empresarial, Estándar, o una de las otras opciones disponibles para nuestra plataforma. Es recomendable crear una base de datos de arranque cuando nos lo soliciten durante la instalación. Crear la base de datos de arranque es un buen modo de asegurarnos de que el entorno servidor se instale correctamente, así como repasar cualquier nueva funcionalidad de Oracle Database 11g. La base de datos de arranque también puede ser un buen candidato como repositorio tanto del «Administrador Empresarial de Oracle» (OEM) como del «Administrador de Recuperación». El flujo exacto del proceso de instalación puede cambiar dependiendo de nuestro entorno operativo y la versión de Oracle. Nota. En entornos UNIX, necesitamos asignar un valor apropiado a la variable de entorno y habilitar xhost previamente para empezar el OUI a través del script runInstaller.

DISPLAY

En general, los pasos serán como sigue: 1) En la pantalla de apertura, elegir entre instalar un producto o desinstalar un producto previamente instalado. 2) Especificar la ubicación del fichero fuente para el producto que queremos instalar y el directorio inicial dentro del cual el software de Oracle será instalado. El instalador debería presentarnos valores por defecto. En general, los valores por defecto para los archivos fuente de software deberían ser válidos, mientras que otros pueden necesitar ser cambiados. 3) Seleccionar un producto a instalar. Nuestras opciones incluirán la base de datos y el cliente. Si seleccionamos la opción "database", el OUI instalará un base de datos de arranque preconfigurada, opciones de producto, herramientas administrativas, servicios de red, utilidades, herramientas de desarrollo, precompiladores, y software cliente básico. Para la primera instalación, deberíamos usar la opción "database" para crear la base de datos de arranque. 4) Elegir el tipo de instalación: Edición Empresarial, Edición Estándar, o personalizada. 5) Si se ha elegido la opción "database" en el paso 3, ahora se nos pedirá que confirmemos la creación de la base de datos de arranque. 6) Se nos pedirá que elijamos entre configuraciones de base de datos estándar: propósito general, procesamiento de transacciones, o depósitos de datos (warehouse). 7) Para la base de datos de arranque, elegir las opciones de configuración. Estas opciones incluyen el nombre global de la base de datos, el nombre de la instancia, el juego de caracteres, y si incluimos o no esquemas de ejemplo. 8) Especificar una única contraseña que será usada por todos los esquemas precargados en la base de datos de arranque, o contraseñas independientes para cada cuenta. 9) Especificar la opción de almacenamiento que se usará. Si estamos usando ficheros del sistema, especificar los directorios a usar. Otras opciones incluyen administración automática de almacenamiento y dispositivos raw. 10) Nos solicitarán finalizar la selección de opciones de administración y servicios previos para aceptar la configuración e inicio de la instalación. Durante la instalación del software, el «Asistente de Configuración de la Base de datos» (DBCA) nos solicita los parámetros necesarios de tamaño y configura nuestra base de datos (comenzando en el paso 6). Los pasos de instalación en la siguiente sesión asumen que hemos completado la instalación del software y creado una base de datos de arranque. Ahora crearemos y configuraremos una segunda base de datos sobre el mismo servidor con DBCA. Nota. Desde Oracle 10g, DBCA puede configurar nodos en un entorno de «Real Application Clusters».

Oracle /16

2.1.2. Usando el DBCA para crear una base de datos. En UNIX, podemos comenzar el «Asistente de Configuración de la Base de datos» (DBCA) ejecutando el fichero dbca ubicado en el directorio $ORACLE_HOME/bin. Debemos configurar la variable de entorno DISPLAY y asignar xhost antes de empezar el DBCA. En Windows, el DBCA está ubicado en el submenú "Herramienta de configuración y migración" del menú "Oracle". En las subsecciones que siguen, se indican pautas y guías para la mayor parte de las pantallas durante la creación de la base de datos. Opciones del DBCA. Después de una pantalla de bienvenida inicial, se nos presenta una selección de cuatro opciones: ■ Crear una base de datos. Esta opción crea una nueva base de datos desde el principio, usando una plantilla como punto de partida. ■ Configurar opciones de base de datos en una base de datos . Esta opción permite cambiar alguno de los parámetros de sistema para una instalación de base de datos existente, como cambiar desde un servidor dedicado a un servidor compartido. ■ Eliminar una base de datos. Esta opción elimina todos los ficheros de datos y ficheros de control asociados con la base de datos. Necesitamos la contraseña del usuario SYS o SYSTEM para ejecutar esta opción. ■ Administrar plantillas. Esta opción permite añadir, modificar o eliminar plantillas. Durante una sesión del DBCA, una vez que todos los parámetros de base de datos han sido reunidos, tenemos la opción de guardar las asignaciones como una plantilla. En muchos casos, las plantillas predefinidas que Oracle proporciona no son exactamente perfectas para nuestro entorno, y ahorra tiempo poder guardar nuestras opciones como una plantilla para una futura sesión del DBCA. Seleccionando una plantilla de base de datos. La figura siguiente muestra la lista de plantillas disponibles. Si creamos una plantilla en una sesión previa del DBCA aparecerá en esta pantalla también.

Las plantillas a seleccionar son las siguientes: ■ Base de datos personalizada. Se usa esta opción si hemos realizado muchas instalaciones y sabemos de antemano los valores para todas las opciones que necesita la base de datos. Esta opción es mejor si creamos una nueva plantilla desde el principio o tenemos exigencias muy específicas para la configuración de nuestra base de datos. ■ Depósito de datos. Esta plantilla es para entornos de base de datos donde los usuarios realizan numerosas y complejas consultas que reúnen muchas tablas para informes, pronósticos y analíticas. Oracle /17

■ Propósito general. Si todavía no estamos seguros de para qué se empleará nuestra base de datos, o si tenemos que soportar a usuarios con requerimientos de procesamiento analítico y transaccional, se recomienda esta plantilla. ■ Procesamiento de transacciones. En entornos donde el número de usuarios es alto, las transacciones son pesadas pero cortas, y la mayor parte de la actividad es crear y actualizar, se recomienda esta plantilla. Para continuar con la instalación, seleccionaremos la plantilla de propósito general. Esta plantilla combina las funcionalidades de los depósitos de datos y un entorno OLTP en una única base de datos. Identificación de la base de datos. En el siguiente paso del DBCA, debemos identificar el nombre de la instancia junto con el nombre de la base de datos global. Nota. Si el nombre de la base de datos global necesita ser cambiado en el futuro, debemos usar el comando ALTER DATABASE para cambiarlo, además de cambiarlo en el fichero de parámetros de inicialización. El nombre de la base de datos global es almacenado en el diccionario de datos cuando se crea la base de datos. A menos que tengamos un dominio existente, debemos usar el nombre de dominio por defecto .world. Deberíamos comprobar con el administrador de sistemas si deberíamos usar un nombre específico para la base de datos global. Credenciales de la base de datos. La siguiente figura muestra la pantalla de asignación de contraseñas para las cuentas de usuario SYS y SYSTEM. Después de la instalación, debemos asegurarnos de crear al menos una cuenta con privilegios DBA para no tener que usar los usuarios SYS o SYSTEM en las tareas administrativas del día a día.

En esta pantalla, podemos también indicar que esta instancia sea incluida como un nodo administrativo en un entorno de Administración Empresarial de Oracle (OEM) existente, o podemos especificar esta instancia como un repositorio OEM. Si especificamos esta instancia como un repositorio OEM, es extremadamente recomendable que este nodo sea usado sólo para este propósito. Opciones de almacenamiento. Las bases de datos pueden usar una cantidad de métodos diferentes para almacenar ficheros de datos, ficheros de control y ficheros de deshacer. La siguiente figura muestra la pantalla donde podemos seleccionar el mecanismo de almacenamiento.

Oracle /18

Si tenemos la posibilidad de dedicar otra instancia de base de datos para administrar espacio de disco, debemos elegir la opción ASM. Si estamos en un entorno «Real Application Clusters» y no tenemos un sistema de ficheros de clúster disponible (como un OCFS) debemos elegir la opción "Raw Devices". Localización de ficheros. En la pantalla mostrada a continuación es donde podemos seleccionar la localización de los archivos de datos, ficheros de control, ficheros de deshacer, así como las localizaciones de archivado, copias de seguridad y recuperación.

Un concepto nuevo desde Oracle Database 10g es el Área de Recuperación Flash. Ésta es una localización del disco dedicada, separada de la localización de los ficheros operacionales de la base de datos, que contiene Oracle /19

ficheros de respaldo del Administrador de Respaldo (RMAN). Es altamente recomendado usar el Área de Recuperación Flash para que el RMAN pueda administrar más fácilmente copias de respaldo y operaciones de recuperación. Deberemos asegurarnos de que el Área de Recuperación Flash tenga el espacio necesario para al menos dos copias de todos los ficheros de datos, copias de respaldo incrementales, ficheros de control, SPFILE's, y ficheros de deshacer que están todavía en el disco. Podemos también habilitar el modo ARCHIVELOG, para especificar la localización o localizaciones de los ficheros de deshacer. Se recomienda dejar el archivado deshabilitado hasta que la base de datos se instale, porque habilitarlo incrementa el tiempo de creación de la base de datos. Los parámetros para el modo ARCHIVELOG pueden ser modificados fácilmente en el fichero init.ora o el SPFILE inmediatamente después de que la base de datos esté activa y ejecutándose. Componentes de la base de datos. En el siguiente paso de la sesión del DBCA, se nos pregunta acerca de la instalación de los esquemas de ejemplo. En bases de datos de no-producción, es altamente recomendable instalar esquemas de ejemplo; muchos tutoriales y guías de estudio se apoyan en los esquemas de ejemplo de la base de datos. Son también útiles en esto los ejemplos demostrativos de casi todos los tipos de datos y constructores disponibles en la base de datos, rangos de mapas de índices para tablas en clústeres y tipos de objetos. Parámetros de inicialización. La siguiente pantalla mostrada a continuación permite al DBA ajustar los parámetros clave de inicialización para la base de datos.

La figura muestra la ficha de Memoria. Si seleccionamos típica (Typical) o si seleccionamos personalizada (Custom) con administración automática de la memoria compartida, Oracle hará asunciones acerca de la memoria que podemos usar para el SGA y los procesos de segundo plano. Incluso usando por defecto muchos de los parámetros en una configuración típica, todavía podemos especificar qué cantidad de la memoria física del servidor debería usar Oracle, dependiendo de cuánta memoria es usada por el sistema operativo y si cualquier otra aplicación está siendo ejecutada en este servidor junto con Oracle. El valor para Java Pool debe ser al menos el tamaño de una fracción de la base de datos, entre 4 MB o 16 MB, pero al menos se recomiendan 20MB. Las últimas pantallas de esta sección del DBCA permiten especificar el tamaño de bloque por defecto de la base de datos, el número total de procesos que serán simultáneamente conectados, el modo de conexión usado, y el juego de caracteres para la base de datos. Almacenamiento de la base de datos. En la pantalla de almacenamiento de base de datos del DBCA, podemos ver y revisar la localización de los Oracle /20

ficheros de control, ficheros de datos y ficheros de deshacer, así como multiplexar los ficheros de control y crear grupos de ficheros de deshacer. Los nombres y localizaciones de los ficheros de control en esta pantalla determinan el valor de CONTROL_FILES en el fichero de parámetros de inicialización. Opciones de creación. En la siguiente figura se muestra la pantalla para crear la base de datos. Además, podemos usar la información proporcionada en las pantallas previas y guardarla en una plantilla. Ante la duda guardar como una plantilla; el almacenamiento requerido para guardar sólo una plantilla es mínimo y puede fácilmente ser suprimido más tarde por nueva ejecución del DBCA.

Antes de que la base de datos sea creada, se presenta un resumen de nuestra plantilla, y tenemos la opción de guardar este informe como un archivo HTML para propósitos de documentación. Completando la instalación. Después de pulsar el botón "OK" de la pantalla de resumen, el DBCA realiza las tareas necesarias para crear la base de datos e iniciar la instancia. Se ejecuta un conjunto de scripts estándar cuando la base de datos inicia por primera vez; esto incluye el script que crea los esquemas de ejemplo, más cualquier script personalizado especificado previamente. El conjunto estándar de scripts varía dependiendo de las opciones seleccionadas a través del DBCA. Una vez completados los scripts de inicialización y creación, se presenta una pantalla de resumen, mostrando la localización de los ficheros de registro de esta instalación. Es recomendable revisar estos ficheros de registro para asegurarnos que no ocurrieron errores durante la instalación. Deberíamos también guardar este fichero de registro con otra documentación de esta base de datos; esto puede ser útil para futuras instalaciones. La base de datos de Oracle recién creada es activada y ejecutada. Tenemos la opción de desbloquear otras cuentas creadas durante esta instalación y asignarles contraseñas. 2.1.3. Creación manual de una base de datos. El DBCA puede soportar requerimientos de instalación complejos. Por ejemplo, si necesitamos crear la misma base de datos en varios servidores, podemos usar el DBCA para crear y ejecutar plantillas. Podemos crear manualmente una base de datos en vez de usar el DBCA. Oracle proporciona un script de ejemplo de creación de base de datos que puede ser personalizado para una instalación manual. A continuación se describen los pasos necesarios para crear una base de datos manualmente. Algunos de estos pasos son dependientes del sistema operativo o de la plataforma. Por ejemplo, bajo Windows necesitamos ejecutar la utilidad oradim para crear el proceso de fondo de Oracle y para asignar los valores de Oracle /21

registro relevantes. 1) Decidir una estructura de directorios para la base de datos; se recomienda cumplir con los estándares de la arquitectura flexible óptima de Oracle cuando pongamos nuestros ficheros en el disco. 2) Seleccionar una SID (identificador de instancia) de Oracle para distinguir esta instancia de otras que se estén ejecutando en el servidor. Frecuentemente será el mismo que el nombre de la base de datos especificado en el parámetro de inicialización DB_NAME. En la línea de comandos de Windows escribir lo siguiente: SET ORACLE_SID=rjbdb

Bajo UNIX, debemos usar:

EXPORT ORACLE_SID=rjbdb

o bien:

SETENV ORACLE_SID=rjbdb

dependiendo de la consola de comandos por defecto. 3) Establecer un método de autentificación para conectar los permisos de usuario a la base de datos. Se usa la utilidad de línea de comando orapwd para crear un fichero de contraseñas si queremos que Oracle autentifique los permisos de usuario; se debe asignar el parámetro de inicialización REMOTE_LOGIN_PASSWORDFILE a EXCLUSIVE. Si estamos usando la autentificación del sistema operativo, no es necesario un fichero de contraseña; y por lo tanto debemos asignar REMOTE_LOGIN_PASSWORDFILE a NONE. 4) Crear un fichero de parámetros de inicialización y ponerlo en la localización por defecto para nuestra plataforma, al menos inicialmente para la instalación. Bajo UNIX, el lugar por defecto es $ORACLE_HOME/dbs; bajo Windows, es $ORACLE_HOME\database. A continuación se muestra un fichero de inicialización de ejemplo: # Cache and I/O DB_BLOCK_SIZE=4096 DB_CACHE_SIZE=20971520 # Cursors and Library Cache CURSOR_SHARING=SIMILAR OPEN_CURSORS=300 # Diagnostics and Statistics BACKGROUND_DUMP_DEST=/u01/oracle11g/admin/rjbdb/bdump CORE_DUMP_DEST=/u01/oracle11g/admin/rjbdb/cdump TIMED_STATISTICS=TRUE USER_DUMP_DEST=/u01/oracle11g/admin/rjbdb/udump # Control File Configuration CONTROL_FILES=("/u01/oracle11g/prod/rjbdb/control01.ctl", "/u02/oracle11g/prod/rjbdb/control02.ctl", "/u03/oracle11g/prod/rjbdb/control03.ctl") # Archive LOG_ARCHIVE_DEST_1='LOCATION=/u06/oracle11g/oradata/rjbdb/archive' # New log archive format. If compatibility 10.0 and up, # this is enforced. LOG_ARCHIVE_FORMAT=%t_%s_%r.dbf # The following parameter is deprecated in 10iR1 # LOG_ARCHIVE_START=TRUE # Shared Server # Starts shared server if set > 0. SHARED_SERVERS=2 # Uncomment and use first DISPATCHERS parameter # below when your listener is # configured for SSL # (listener.ora and sqlnet.ora) # DISPATCHERS = "(PROTOCOL=TCPS)(SER=MODOSE)", # "(PROTOCOL=TCPS)(PRE=oracle.aurora.server.SGiopServer)" DISPATCHERS="(PROTOCOL=TCP)(SER=MODOSE)",

Oracle /22

"(PROTOCOL=TCP)(PRE=oracle.aurora.server.SGiopServer)", (PROTOCOL=TCP) # Miscellaneous COMPATIBLE=10.0.0 DB_NAME=rjbdb # Distributed, Replication and Snapshot DB_DOMAIN=rjbdba.com REMOTE_LOGIN_PASSWORDFILE=EXCLUSIVE # Network Registration INSTANCE_NAME=rjbdb # Pools JAVA_POOL_SIZE=31457280 LARGE_POOL_SIZE=1048576 SHARED_POOL_SIZE=52428800 # Processes and Sessions PROCESSES=150 # Redo Log and Recovery FAST_START_MTTR_TARGET=300 # Resource Manager RESOURCE_MANAGER_PLAN=SYSTEM_PLAN # Sort, Hash Joins, Bitmap Indexes SORT_AREA_SIZE=524288 # Automatic Undo Management UNDO_MANAGEMENT=AUTO UNDO_TABLESPACE=undotbs

5) Conectar la instancia usando SQL*Plus:

SQLPLUS /NOLOG CONNECT SYS/contraseña AS SYSDBA

Nótese que aunque la instancia misma ya exista, no hay mucho que podamos hacer puesto que aún no hemos creado la base de datos. 6) Crear un fichero de parámetros de servidor (SPFILE). Si el fichero de inicialización está en la localización por defecto, el siguiente comando creará el SPFILE: CREATE spfile FROM pfile;

7) Empezar la instancia usando el siguiente comando: STARTUP NOMOUNT

Nótese que porque no tenemos una base de datos creada aún, ésta es la única opción que podemos emplear. 8) Ejecutar el comando CREATE DATABASE. A continuación hay un ejemplo: CREATE DATABASE rjbdb USER SYS IDENTIFIED BY paris703 USER SYSTEM IDENTIFIED BY tyler12 LOGFILE GROUP 1 ('/u02/oracle11g/oradata/rjbdb/redo01.log') SIZE 100M, GROUP 2 ('/u04/oracle11g/oradata/rjbdb/redo02.log') SIZE 100M, GROUP 3 ('/u06/oracle11g/oradata/rjbdb/redo03.log') SIZE 100M MAXLOGFILES 6 MAXLOGMEMBERS 5 MAXLOGHISTORY 1 MAXDATAFILES 100 MAXINSTANCES 1 CHARACTER SET US7ASCII NATIONAL CHARACTER SET AL16UTF16 DATAFILE '/u01/oracle11g/oradata/rjbdb/system01.dbf' SIZE 325M REUSE EXTENT MANAGEMENT LOCAL SYSAUX DATAFILE '/u01/oracle11g/oradata/rjbdb/sysaux01.dbf'SIZE 325M REUSE

Oracle /23

DEFAULT TABLESPACE tbs_1 DEFAULT TEMPORARY TABLESPACE tempts1 TEMPFILE '/u01/oracle11g/oradata/rjbdb/temp01.dbf'SIZE 20M REUSE UNDO TABLESPACE undotbs DATAFILE '/u02/oracle11g/oradata/rjbdb/undotbs01.dbf' SIZE 200M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED;

Hay varias cosas a destacar en este ejemplo. Se explicitan las contraseñas de SYS y SYSTEM; si no se especifican aquí, tendrán como valores por defecto "change_on_install" y "manager" respectivamente. Los grupos de archivos de deshacer tienen sólo un miembro cada uno; una vez que nuestra base de datos esté en producción deberíamos multiplexarlos. Ya que especificamos un tablespace de deshacer con el parámetro UNDO_TABLESPACE en el archivo de parámetros de inicialización, necesitamos crear este tablespace aquí; sino, la instancia no se iniciará. Después de que se cree la base de datos, es montada y abierta para su uso. 9) Crear tablespaces adicionales para los usuarios, índices y aplicaciones. 10) Construir las vistas del diccionario de datos con los scripts proporcionados catalog.sql y catproc.sql. El script catalog.sql crea vistas a partir de las tablas del diccionario de datos, vistas de rendimiento dinámico, y sinónimos públicos para muchas de las vistas. A las vistas del grupo PUBLIC se les concede sólo acceso de lectura. El script catproc.sql establece PL/SQL. 11) Crear respaldo de la base de datos usando copias en frío o el Administrador de Respaldo. En el caso de que la base de datos falle en las etapas iniciales del despliegue, tendremos una base de datos completa y ejecutándose para restaurar, y más probablemente no tendremos que recrear la base de datos desde el principio.

3. Servidor de Oracle Un servidor Oracle es el software que permite una administración y desarrollo de bases de datos de Oracle. Tiene tres posibilidades de ejecución: ▪ Local o basada en host. El servidor se ejecuta en la misma máquina en la que se conectan los clientes. La versión personal de Oracle Database produce servidores de este tipo. ▪ Cliente-Servidor. Enfoque más típico. El servidor reside en un ordenador distinto respecto al que los usuarios van a usar para conectarse a la base de datos. ▪ Cliente-Servidor de Aplicaciones-Servidor. Los usuarios acceden a un servidor de aplicaciones (Oracle Application Server) que, a su vez, accede al servidor Oracle. Los tres elementos (cliente, servidor de aplicaciones, servidor Oracle) pueden estar en tres máquinas distintas. 3.1. Elementos del servidor Oracle El servidor Oracle está formado por dos elementos: ▪ La instancia de la base de datos. Consta de datos (llamados estructuras de memoria) y de procesos en memoria (procesos background) necesarios para dar servicio a los usuarios de la base de datos. Puede haber más de una instancia si se distribuye la base de datos en más de una máquina. Cada instancia abre una y sólo una base de datos. ▪ Ficheros en disco. Representan la base de datos en sí. Consta de: - Estructuras lógicas: Tablespaces, objetos del esquema de usuario. - Estructuras físicas: Los ficheros de datos almacenados en disco. Los ficheros de datos (asociados a los tablespaces), los ficheros deshacer y los ficheros de control. 3.2. Conexiones. Para establecer una sesión con la base de datos, el usuario necesita conectarse con la instancia de la base de datos. Normalmente esto significa arrancar una herramienta cliente como SQL*Plus o la Consola de Administración de Oracle, o ejecutar una aplicación de desarrollo de bases de datos (como Oracle Forms); entonces se ejecuta un proceso de usuario. Cuando esto ocurre, en el servidor se establece un proceso de servidor. Este proceso es el encargado de comunicar al usuario con la instancia Oracle en nombre del proceso de usuario. Cada vez que el usuario ejecuta instrucciones SQL, éstas son transmitidas a la instancia Oracle por el proceso servidor. De este modo una conexión es un camino entre un proceso de usuario y un servidor Oracle. Cada sesión es una conexión de un usuario con el servidor Oracle. Un usuario puede establecer múltiples sesiones (si se conecta desde diferentes herramientas y máquinas). Oracle /24

3.3. Estructura de las bases de datos Oracle. Desde el punto de vista de Oracle, una base de datos es una colección de datos tratados como una única unidad. Una base de datos Oracle contiene tres tipos de ficheros: ▪ Archivos de datos (.DBF). Contiene los datos actuales de la base de datos así como el diccionario de datos. ▪ Archivos rehacer o redo logs (.LOG). Almacenan datos de transacciones recuperables en caso de error grave. ▪ Archivos de control (.CTL). Necesarios para mantener la integridad de la base de datos. Además se utilizan otros archivos de forma auxiliar ▪ Archivos de parámetros (.ora). Que definen algunas características de una instancia Oracle. ▪ Archivos de contraseñas. Que sirven para autentificar a los usuarios. ▪ Copias de archivos rehacer. Utilizadas para la recuperación de datos. 3.4. Instancia de la base de datos. La instancia de la base de datos es uno de los dos elementos de cualquier base de datos Oracle. Sirve para gestionar los datos de la base de datos y proporcionar servicio a los usuarios que acceden a la misma.

Está compuesta de: ▪ Estructuras en memoria. ▪ Procesos en segundo plano (background). En las estructuras en memoria nos encontramos con: ▪ SGA, la abreviatura de System Global Area o Área Global de Sistema. Está situada al inicio de los datos de la instancia y contiene los datos e información de control de la instancia. Está formada por las siguientes estructuras: - Shared pool, o fondo común compartido. Almacena las últimas instrucciones SQL y PL/SQL ejecutadas. Posee dos estructuras internas: la caché de instrucciones (Library cache), que almacena las últimas instrucciones SQL y PL/SQL ejecutadas; y la caché del diccionario de datos, que almacena las últimas definiciones de la base de datos utilizadas (tablas, índices, privilegios, usuarios,...). Cada vez que una instrucción utiliza un nombre de la base de datos (tabla, índice,...) se comprueba en el diccionario de datos y se almacena en este caché. De este modo la siguiente vez no hace falta acceder al diccionario de datos real. - Caché buffer de la base de datos. Almacena los últimos bloques de datos accedidos por los usuarios. - Buffer de archivo deshacer. Almacena los últimos cambios realizados a los bloques de datos de la base de datos. - Large pool. Opcional. Se utiliza como memoria de sesión y para realizar operaciones de backup. - Java pool. Opcional. Se utiliza como caché de los comandos Java. Oracle /25

- Otras estructuras ▪ PGA, la abreviatura de Program Global Area o Zona global de los programas. En ella se almacenan los datos correspondientes a un proceso (sólo un proceso puede utilizar esta área). Incluye: - Áreas de ordenación. Para acelerar las tareas de ordenación de datos. - Información de sesión. Usuario, privilegios,... - Estado del cursor. Tareas SQL actualmente en ejecución. - Espacio de pila. Variables y otros datos. En Oracle los procesos pueden ser de estos tipos: ▪ Proceso de usuario. Lanzado por el usuario para pedir interacción con la base de datos. ▪ Proceso de servidor. Hacen de enlace entre los procesos de usuarios y el servidor Oracle. Se utilizan como manejadores de los procesos de usuario. Los comandos de usuario se envían a estos procesos que se encargan de solicitar peticiones a la base de datos mediante el interfaz de programas de Oracle (OPI, Oracle Program Interface). ▪ Procesos en segundo plano (background). Cada instancia de Oracle arranca una serie de procesos de fondo. Los procesos obligatorios son: - DBWR (DataBase WRiter). Proceso encargado de escribir en los ficheros de datos los buffers más antiguos de la memoria, para que la base de datos vaya almacenando los cambios. - LGWR (LoG WRiter). Escribe los datos a los ficheros deshacer (redo) desdela caché de archivos deshacer. - CKPT. Actualiza todas las cabeceras de los ficheros de datos para que aparezca la nueva disposición de datos. Esto ocurre cuando se genera un punto de comprobación. - SMON (System MONitor). Permite recuperar la instancia de la base de datos en caso de caída fatal (cuando el sistema falla por ejemplo). - PMON (Process MONitor). Es el encargado de gestionar adecuadamente los procesos que fallan. Ante caídas de procesos, PMON se encarga de restaurarlos datos adecuadamente. - SQL *Net Listener. Es el encargado de encaminar por una red solicitudes de un cliente a un servidor de base de datos Oracle. Este proceso escuchador (listener) está tanto en el cliente como en el servidor. Puede encaminar solicitudes que se dirigen a varias instancias. 3.5. Procesamiento de instrucciones SQL. Para poder ejecutar SQL sobre la base de datos, hay que conectarse con la instancia Oracle de la base de datos, lo cual requiere la comunicación entre un proceso cliente y el servidor (el proceso cliente puede ser una instancia de SQL*Plus, por ejemplo). Los componentes utilizados por Oracle para procesar el SQL dependen del código enviado: - Las consultas devuelven filas. - Las instrucciones DML (Lenguaje de Manipulación de Datos) graban cambios. - La instrucción COMMIT asegura el proceso de la transacción. Pero de manera general los pasos en ese proceso son: 1) El usuario abre la herramienta que permite el envío de peticiones SQL (por ejemplo SQL*Plus). 2) El usuario introduce su nombre de usuario y contraseña. 3) Oracle consulta el diccionario de datos para verificar la existencia del usuario y para validar su permiso de conexión. Si lo tiene, se produce la conexión. 4) El usuario escribe la instrucción SQL (por ejemplo, una instrucción de modificación). 5) Oracle traduce la instrucción con el analizador de instrucciones (devolvería un error si la instrucción no es válida). 6) Oracle traduce los nombres usados en la instrucción con la ayuda del diccionario de datos. 7) Si es una instrucción de mostrar datos (SELECT), comprueba si otros usuarios han enviado hace poco esa misma instrucción; eso lo comprueba en el caché de instrucciones de la SGA. Si la instrucción está ahí coge los resultados del búfer caché de la base de datos. 8) Si la instrucción conlleva cambios, el servidor bloquea las filas que se modificarán. 9) La base de datos graba los cambios (si los hubo) y actualiza los archivos de deshacer. 10) La base de datos graba los nuevos valores para los datos. 11) Oracle libera del bloqueo los registros. 12) El usuario recibe un mensaje de éxito.

Oracle /26

3.6. Archivos de inicialización Además de estructuras de disco y de memoria, un servidor Oracle necesita ciertos archivos para poder ejecutarse. Estos archivos se establecen durante la creación de la base de datos, y se consultarán cada vez que se arranque la base de datos, por lo que deben estar disponibles. Esta sección explica los ficheros de parámetros que utiliza la base de datos Oracle para arrancar: init.ora y spfile.ora. 3.6.1. Parámetros de inicialización y configuración. Oracle es una base de datos configurable mediante una serie de parámetros, de forma que el administrador puede optimizar los valores de esta base de datos. Estos parámetros de optimización y configuración de base de datos se almacenan en un fichero. Este fichero es el primero al que se accede al arrancar la base de datos Oracle y se denomina «init.ora». En este fichero se escriben los parámetros de configuración de Oracle; pero si en este archivo alguno de los parámetros de Oracle configurables no se encuentra, entonces tomará el valor que Oracle tenga por defecto. Existen tres tipos de parámetros en Oracle: • Parámetros "fijos": Son parámetros que una vez instalada la base de datos no se pueden volver a modificar/configurar. El juego de caracteres es un claro ejemplo. • Parámetros estáticos: Son parámetros que se pueden modificar, pero su modificación implica cerrar la base de datos y volverla a abrir para que los lea del fichero y pueda realizar el cambio. • Parámetros dinámicos: Son parámetros cuyo valor se puede cambiar sin necesidad de cerrar la base de datos, a diferencia de los estáticos. 3.6.2. Ubicación y nomenclatura del fichero «init.ora». El archivo «init.ora» se encuentra en Windows dentro del directorio ORACLE_HOME\database y en UNIX dentro del directorio ORACLE_HOME/dbs. El nombre del archivo siempre corresponderá a «initsid.ora» siendo sid el nombre de la base de datos. (Éste es el nombre que Oracle buscará al arrancar la base de datos.) 3.6.3. El fichero «spfile.ora». El fichero «init.ora» no es el único archivo de parámetros que podemos encontrar en las base de datos Oracle. A partir de la versión 9 se puede encontrar el archivo « spfile.ora». Éste es el primer archivo que va a "buscar" Oracle en su arranque de base de datos. Si no encuentra este archivo entonces irá a buscar el archivo «init.ora». Este archivo está codificado y las modificaciones en él se realizarán mediante una serie de comandos Oracle que posteriormente indicaremos. La ubicación de este archivo es la misma que la de «init.ora». 3.6.4. Cambio de los valores de los parámetros. Si queremos realizar algún cambio en algún parámetro de base de datos tenemos que diferenciar dos cosas: • Si el cambio es en «init.ora» o «spfile.ora». • Tipo de parámetro sobre el que se quiere hacer el cambio. Vamos a explicar como realizar un cambio en el fichero «init.ora», para ello tenemos que tener en cuenta el tipo de parámetro que vamos a cambiar. Como se ha indicado, existen tres tipos de parámetros; dejando a un lado los parámetros fijos (aquellos que no se pueden cambiar una vez instalada la base de datos) nos quedan los parámetros estáticos y los dinámicos. Para modificar un parámetro estático nos basta con editar el fichero «init.ora» y modificar o añadir ahí el parámetro nuevo reiniciando la base de datos para que coja estos cambios. En cuando a los parámetros dinámicos podemos cambiarlos en tiempo real sin parar la base de datos mediante la siguiente sentencia: ALTER SYSTEM SET parámetro = valor;

Este cambio pasa automáticamente a ser efectivo en la base de datos, aunque tenemos que tener en cuenta que la próxima vez que la base de datos sea iniciada se volverá a leer el fichero de parámetros « init.ora». Para realizar cualquier cambio en «spfile.ora» hay que usar el comando ALTER SYSTEM añadiendo la cláusula SCOPE con una serie de valores que detallaremos a continuación con un ejemplo. Para cambiar el parámetro shared_pool_size a 150 Megas: ALTER SYSTEM SET shared_pool_size= 150 SCOPE=spfile

En este caso hemos cambiado el parámetro y estos cambios se han recogido en el archivo permanecerá cuando sea reiniciada la base de datos. ALTER SYSTEM SET shared_pool_size= 150 SCOPE=memory

spfile,

por lo tanto

En este caso se ha cambiado el parámetro y estos cambios se han recogido solamente en memoria, esto quiere decir que se hacen efectivos al momento (si el tipo de parámetro lo permite) pero este cambio no se verá reflejado en el archivo de parámetros. ALTER SYSTEM SET shared_pool_size= 150 SCOPE=both

Oracle /27

En este caso el parámetro se cambia tanto en el spfile como en memoria. 3.7. Arranque y parada de la base de datos. Durante el arranque y parada de la BD se suceden un conjunto de eventos que llevan a la base de datos por varios estados. Para que los usuarios puedan acceder a la base de datos el administrador debe abrirla con el comando STARTUP OPEN. El siguiente es un ejemplo de apertura de una base de datos llamada Test. STARTUP OPEN Test

ORACLE instancia iniciada. Total System Global Area Fixed Size Variable Size Database Buffers Redo Bufers Database mounted. Database opened.

4512688 bytes. 39732 bytes. 4055164 bytes. 409600 bytes. 8192 bytes.

Cuando se ejecuta el comando STARTUP OPEN la base de datos pasa por tres estados ( NOMOUNT, MOUNT y OPEN) antes de estar disponible. El administrador de base de datos puede arrancar la base de datos hasta uno de los estados con el comando STARTUP: STARTUP NOMOUNT o STARTUP MOUNT. A continuación se describe cada uno de los estados por los que pasa la base de datos en el proceso de arranque.  Estado «nomount». Se arranca la base de datos en el estado «nomount» con el siguiente comando: STARTUP OPEN Test

Oracle lee el fichero init.ora, localiza los ficheros de control, crea e inicializa la SGA, y finalmente arranca todos los procesos Oracle. En este estado la instancia de base de datos está arrancada. Se deberá llevar la base de datos al estado «nomount» cuando se esté creando la base de datos o cuando se esté restaurando un fichero de control después de haberlo perdido.  Estado «mount». Se cambia la base de datos al estado «mount» con el siguiente comando: ALTER DATABASE MOUNT;

Oracle abre los ficheros de control para localizar los ficheros de datos y los redo log, pero no se realiza ninguna comprobación en ellos en este momento. La instancia monta la base de datos y la bloquea, verificando que ninguna otra instancia ha montado la misma base de datos. Hay varias razones para querer tener la base de datos en el estado «mount». En general, todas las sentencias SQL del tipo ALTER DATABASE se deben ejecutar en esta etapa. Algunas de las operaciones a realizar cuando la base de datos está montada son: • Efectuar recuperaciones. • Poner online/offline un fichero de datos. • Recolocar los ficheros de datos y redo log. • Crear un nuevo grupo o miembro redo log, o borrar un grupo o miembro redo log existente.  Estado «open». Se cambia la base de datos al estado «open» con el siguiente comando: ALTER DATABASE OPEN;

Durante esta etapa, la instancia abre la base de datos, bloquea los ficheros de datos, y abre todos los ficheros redo log. Si la instancia abre la base de datos después de una terminación anormal, o después de una caída, se ejecutará automáticamente el proceso de recuperación utilizando los ficheros redo log. Al final de esta etapa la base de datos está dispuesta para su uso normal. Para parar la base de datos se usa el comando SHUTDOWN como se puede ver en el siguiente ejemplo: SHUTDOWN

Database closed. Database dismounted. ORACLE instance shut down.

Este comando admite tres opciones: NORMAL, IMMEDIATE y ABORT.  SHUTDOWN NORMAL Oracle /28

Impide el acceso a la base de datos, espera a que todos los usuarios completen todas sus peticiones y se desconecten del servidor. Purga todos los búferes de datos y cachés de redo log, actualizando los ficheros de datos y de redo log, se eliminan los bloqueos de ficheros, se completan las transacciones en marcha, se actualizan las cabeceras de ficheros, elimina los hilos, libera los bloqueos de la base de datos por parte de la instancia, y sincroniza los ficheros de control y de datos. En resumen, la opción «normal» cierra la base de datos, la desmonta y para la instancia con cuidado y es la opción recomendada para parar la base de datos.  SHUTDOWN IMMEDIATE En ciertas ocasiones puede ser necesario parar la base de datos de modo inmediato. Si es así, las sentencias en proceso son terminadas inmediatamente, cualquier transacción no confirmada es vuelta atrás y la base de datos es parada. La única desventaja de utilizar esta opción es que Oracle no espera a que los usuarios se desconecten. Sin embargo, la base de datos será consistente y no se necesitará recuperación en el siguiente arranque.  SHUTDOWN ABORT En situaciones de emergencia, y cuando todo lo demás falla, se debe realizar una parada de este tipo. Por ejemplo, cuando un proceso de la instancia muere y la base de datos no puede pararse de modo normal o inmediato. Cuando se utiliza la opción ABORT las sentencias SQL son terminadas bruscamente, y las transacciones no confirmadas no son vueltas atrás. Parar la base de datos con la opción ABORT requiere recuperación en la siguiente vez que arranque la base de datos y esta opción debe ser utilizada sólo cuando no quede más remedio.

4. Introducción a los conceptos del sistema Oracle 4.1. Almacenamiento. Una base de datos tiene una estructura lógica (que se manipula mediante comandos) y una estructura física (la que realmente se almacena en disco). La estructura lógica de Oracle está formada por: tablespaces, segmentos, extensiones y bloques de datos. La estructura física está formada por: ficheros de datos y bloques de sistema. 4.2. Transacciones. Los cambios en la base de datos no son guardados hasta que tras una serie de instrucciones se decide llevar a cabo esos cambios. Hasta ese momento todo lo realizado se toma como provisional. Un fallo en la máquina permitiría invertir los cambios. Una transacción son varias operaciones SQL que forman una unidad de trabajo. Comienza cuando una persona se conecta y de ahí hasta que ejecuta la instrucción COMMIT (ejecutar la transacción) o ROLLBACK (anular la transacción). La anulación deja la base de datos en el estado anterior al comienzo de la transacción. Tras un COMMIT o un ROLLBACK comienza la siguiente transacción. En Oracle se admite además el uso de puntos de ruptura (savepoints) para almacenar valores intermedios y volver a cualquier de ellos si interesa. Pero esto ralentiza excesivamente el sistema. 4.3. Usuarios. Los usuarios son las personas que acceden de una forma o de otra a la base de datos. Cada usuario tiene una vista determinada de la base de datos. Hay varios conceptos sobre los usuarios a tener en cuenta. • Privilegios. Controlan el permiso que posee un usuario de ejecutar una determinada instrucción SQL. Un usuario que quiera crear una tabla, deberá tener el privilegio (o permiso) adecuado para ello. Además se pueden colocar privilegios en los objetos; es decir, un usuario propietario de una tabla puede otorgar privilegios a esa tabla (se trataría de un privilegio a nivel de objeto) para que haya otros usuarios que la puedan usar. • Rol. Son agrupaciones de privilegios que facilitan la tarea de gestionar a los usuarios. Así, cuando una serie de usuarios van a tener los mismos privilegios, se crea un rol que contenga esos privilegios y a esos usuarios se les asigna el rol. Oracle proporciona varios roles ya predefinidos, por ejemplo el rol DBA da privilegio absoluto a un usuario. • Esquemas. Están asociados a los usuarios. Agrupan los objetos lógicos que pertenecen al usuario. Es decir, es el conjunto de tablas, vistas, sinónimos, instantáneas, enlaces de base de datos, procedimientos y funciones, paquetes, etc. creados por un usuario. Cada usuario tiene su propio esquema y, en principio, un usuario no tiene acceso a los elementos de otro Oracle /29

usuario, salvo que sea un administrador o que otro usuario ceda el privilegio de utilización de uno o más de sus objetos al resto de usuarios. La primera vez que un usuario crea un objeto en la base de datos, se crea un esquema con su mismo nombre. 4.4. Pérdidas de información. Es una de las tareas y herramientas fundamentales que nos proporcionan las bases de datos. Hay posibilidad de perder datos de nuestra base de datos por alguna de estas razones: • Fallo en una instrucción. Hay instrucciones que pueden provocar la pérdida no deseada de cientos de registros en un momento (DELETE o UPDATE). En ese caso basta con ejecutar una instrucción ROLLBACK antes de que las instrucciones previas se confirmen. • Fallo en la comunicación. Si una conexión de usuario se corta anulando el proceso de usuario relacionado. En ese caso, Oracle anula los cambios de la última transacción (el resto de transacciones sí se almacena). • Caída del servidor. Puede que la instancia Oracle se deje de ejecutar. En ese caso basta con lanzar de nuevo la instancia. El proceso SMON se encargará de grabar los archivos deshacer y aplica de nuevo las transacciones confirmadas. Se anulan los cambios no confirmados. • Pérdida de datos en los archivos. Es el único caso en el que tiene que intervenir el administrador. La única posibilidad es recuperarlos de una copia de seguridad. 4.5. Copias de seguridad. Es una de las herramientas fundamentales de toda base de datos. Al hacer la copia de seguridad se nos permite recuperar la información de esa copia. Eso hace que perdamos definitivamente los datos perdidos desde la última copia de seguridad, de ahí la importancia de hacer copias a menudo. Hay una posibilidad de perder menos información y es hacer que la base de datos se ejecute en modo "Archivo Log", lo que significa que se almacena, en un archivo de datos especial, los datos que se van rechazando en los registros de deshacer por estar llenos. Lo malo de esta opción es que el servidor funciona más lento; lo bueno es que en caso de desastre se pueden recuperar los datos almacenados. Hay dos tipos de copia de seguridad: • En frío. La copia se realiza tras parar la instancia de Oracle. La copia de seguridad almacena todos los datos de la base (incluidos los archivos de control y de deshacer). • En caliente. Ya que en muchos casos no se puede parar la instancia tan fácilmente (por estar 24 horas al día funcionando). En ese caso es una indicación la que se hace a la base de datos y la copia se realiza desde el sistema operativo (copiando sin más). Otras posibilidades de copias son: • Catálogos de copia de seguridad. Que almacenan información sobre las copias realizadas, fechas, datos, estructura,... • Copias de seguridad incrementales. Un problema de las copias es que, en bases extensas, la copia tarda muchas horas. Cuando la copia termina, la información ya está desfasada. Las copias incrementales sólo almacenan los datos que han cambiado recientemente. 4.6. Bases de datos distribuidas. Se trata de una base de datos a nivel lógico (los usuarios la manejan como una base de datos normal), pero que en realidad (físicamente) está implementada en varias ubicaciones físicas, incluso en máquinas diferentes y distantes. Cada máquina ejecuta su propia instancia y conjuntos de archivos y todas se conectan en red para hacer que el usuario no tenga que cambiar su código para reflejar esta distribución. La dificultad de esta estructura suele estar aliviada por medio de instantáneas que graban momentáneamente los datos de las tablas distantes. Permiten trabajar con los datos copiados y se programan para que cada cierto tiempo recojan nuevamente los datos a fin de reflejar sus cambios. Gracias a las instantáneas no hace falta una sobrecarga excesiva de la base de datos. 4.7. Herramientas de Oracle. El software del sistema de bases de datos Oracle incorpora herramientas para realizar la mayoría de tareas comunes en una base de datos: ▪ Oracle Universal Installer. Gestor de instalaciones, controla cada nueva instalación de software Oracle a fin de que se integren de la mejor manera posible. Oracle /30

▪ SQL*plus. Programa cliente que permite conexión con el servidor Oracle para enviarle secuencias SQL y PL/SQL. ▪ iSQL*plus. Permite conexiones al servidor Oracle con la misma finalidad que el anterior pero utilizando un navegador de Internet, lo que facilita el trabajo. ▪ SQL*plus WorkSheet. Permite conexiones al servidor de Oracle, utilizando un entorno más potente (procede del Oracle Enterprise Manager). ▪ Oracle Enterprise Manager. Entorno que permite la administración y configuración completa del servidor Oracle. ▪ SQL*Loader. Permite cargar en bases de datos de Oracle información que procede de un archivo de texto. Necesaria para utilizar en las bases de datos de Oracle, información que procede de otro software. ▪ Import/Export. Para importar y exportar datos entre instancias de Oracle. De un servidor a otro por ejemplo. También se utiliza como herramienta de copia de seguridad. ▪ Servidor http de Oracle. Basado en el servidor Apache, permite opciones de documentación y sobre todo la comunicación directa a través de iSQL*Plus con el servidor Oracle sin tener necesidad de instalar software adicional. ▪ Net Manager. Permite la administración de los servicios de red a fin de configurar las conexiones hacia instancias de Oracle. ▪ Oracle Forms. Permite crear aplicaciones visuales sobre bases de datos de Oracle. ▪Oracle Reports. Asistente para la producción de informes. ▪ Oracle Designer. Herramienta CASE de Oracle, para crear esquemas en el ordenador y que el software produzca los resultados del mismo. ▪ Oracle JDeveloper. Crea aplicaciones Java pensadas para desarrollar formularios sobre datos de Oracle ▪ Oracle Developer Suite. Integra todos los componentes anteriores. ▪ Oracle AS (Application Server). Servidor de aplicaciones de Oracle. Permite compilar aplicaciones J2EE. ▪ Pro C/C++. Precompilador de C/C++ para Oracle.

Oracle /31

II. LENGUAJE DE CONSULTAS 1. SQL para Oracle 1.1. Introducción. El lenguaje de consulta estructurado SQL (Standar Query Language) es un lenguaje de base de datos normalizado, utilizado por el motor de los Gestores de Base de Datos relacionales que existen en la actualidad. SQL pretende ser un lenguaje que simula su escritura en lenguaje normal. De ahí que se le considere un lenguaje de cuarta generación. Consta de palabras especiales y de expresiones. Se trata de un lenguaje que intenta agrupar todas las funciones que se le pueden pedir a una base de datos. Las instrucciones SQL se introducen a través de una herramienta que las traduce inmediatamente a la base de datos, por lo que se ejecutan al instante. Las instrucciones SQL se colocan como parte del código de otro lenguaje anfitrión (C, Java, Pascal, Visual Basic, etc.). Estas instrucciones están separadas del resto del código de forma conveniente. Al compilar el código se utiliza un precompilador de la propia base de datos para traducir el SQL. Las aplicaciones clientes deben acceder al servidor de Oracle a través del puerto 1521. 1.2. Código SQL y normas de escritura. El código SQL consta de los siguientes elementos: - Comandos. Las distintas instrucciones que se pueden realizar desde SQL: SELECT, DML, DDL, instrucciones de transferencia, e instrucciones de control del lenguaje. - Cláusulas. Son palabras especiales que permiten modificar el funcionamiento de un comando ( WHERE, ORDER BY, etc.). - Operadores. Permiten crear expresiones complejas. - Funciones. Para conseguir valores complejos (SUM(), DATE(), etc.). - Constantes. Valores literales para las consultas, números, textos, caracteres, etc. - Datos. Obtenidos de la propia base de datos Las normas de escritura son: - En SQL no se distingue entre mayúsculas y minúsculas. Da lo mismo cómo se escriba. - El final de una instrucción lo determina el signo del punto y coma. - Los comandos SQL (SELECT, INSERT, ...) pueden ser partidos por espacios o saltos de línea antes de finalizar la instrucción. - Se pueden tabular líneas para facilitar la lectura, si fuera necesario. - Los comentarios en el código SQL comienzan por /* y terminan por */. 1.3. SQL*Plus. Para poder enviar sentencias SQL al servidor Oracle, éste incorpora la herramienta SQL*Plus. Toda instrucción SQL que el usuario escribe es verificada por este programa. Si la instrucción es válida se envía a Oracle, el cual retornará la respuesta a la instrucción; respuesta que puede ser transformada por el programa SQL*Plus para modificar su salida. Para que el programa SQL*Plus funcione en el cliente, el ordenador cliente debe haber sido configurado para poder acceder al servidor Oracle. En cualquier caso, al acceder a Oracle con este programa siempre preguntará por el nombre de usuario y contraseña. Éstos son datos que nos tiene que proporcionar el administrador de la base de datos Oracle (DBA). Para conectarnos mediante SQL*Plus podemos ir a la línea de comandos y escribir el texto sqlplus. A continuación aparecerá la pantalla siguiente:

Oracle /32

En esa pantalla se nos pregunta el nombre de usuario y contraseña para acceder a la base de datos (información que deberá indicarnos el administrador o DBA). Tras indicar esa información conectaremos con Oracle mediante SQL*Plus, y veremos aparecer el símbolo: SQL>

Tras el cual podremos comenzar a escribir nuestros comandos SQL. Ese símbolo puede cambiar por un símbolo con números 1, 2, 3, etc.; en ese caso se nos indica que la instrucción no ha terminado y la línea en la que estamos. Otra posibilidad de conexión consiste en llamar al programa SQL*Plus indicando la contraseña y base de datos a conectar. El formato es: slplus usuario/contraseña@nombreServicioBaseDeDatos

Ejemplo:

slplus usr1/[email protected]

En este caso conectamos con SQL*Plus indicando que somos el usuario usr1 con contraseña miContra y que conectamos a la base de datos inicial de la red forempa.net. El nombre de la base de datos no tiene porqué tener ese formato, habrá que conocer cómo es el nombre que representa a la base de datos como servicio de red en la red en la que estamos. 1.4. Versión gráfica de SQL*Plus. Oracle incorpora un programa gráfico para Windows para utilizar SQL*Plus. Se puede llamar a dicho programa desde las herramientas instaladas en el menú de programas de Windows, o desde la línea de programas escribiendo sqlplusw. Al llamarle aparece esta pantalla:

Como en el caso anterior, se nos solicita el nombre de usuario y contraseña. La «cadena de Host» es el nombre completo de red que recibe la instancia de la base de datos a la que queremos acceder en la red en la que nos encontramos. También podremos llamar a este entorno desde la línea de comandos utilizando la sintaxis comentada anteriormente. En este caso: slplusw usuario/contraseña@nombreServicioBaseDeDatos

Esta forma de llamar al programa permite entrar directamente sin que se nos pregunte por el nombre de usuario y contraseña. 1.5. iSQL*Plus. Es un producto ideado desde la versión 9i de Oracle. Permite acceder a las bases de datos Oracle desde un Oracle /33

navegador. Para ello necesitamos tener configurado un servidor web Oracle que permita la conexión con la base de datos. Utilizar iSQL*Plus es indicar una dirección web en un navegador, esa dirección es la de la página iSQL*Plus de acceso a la base de datos: http://host_oracle:7778/isqlplus. (El número de puerto, en este caso 7778, puede variar en cada instalación.) Desde la página de acceso se nos pedirá nombre de usuario, contraseña y nombre de la base de datos con la que conectarnos (el nombre de la base de datos es el nombre con el que se la conoce en la red). Si la conexión es válida aparece esta pantalla:

En esa pantalla, en el apartado «Introducir Sentencias», se escribe la sentencia que deseamos enviar. El botón «Ejecutar» hace que se valide y se envíe a Oracle. Se pueden almacenar sentencias SQL usando el botón «Examinar» y cargar sentencias previamente guardadas mediante «Cargar archivos de comandos».

2. Estructura del lenguaje SQL. En SQL se distinguen los siguientes tipos de instrucciones: - SELECT. Se trata del comando que permite realizar consultas sobre los datos de la base de datos. Obtiene datos de la base de datos. - DML, Data Manipulation Language (Lenguaje de manipulación de datos). Modifica filas (registros) de la base de datos. Lo forman las instrucciones INSERT, UPDATE, MERGE y DELETE. - DDL, Data Definition Language (Lenguaje de definición de datos). Permiten modificar la estructura de las tablas de la base de datos. Lo forman las instrucciones CREATE, ALTER, DROP, RENAME y TRUNCATE. - Instrucciones de transferencia. Administran las modificaciones creadas por las instrucciones DML. Lo forman las instrucciones ROLLBACK, COMMIT y SAVEPOINT. - DCL, Data Control Language (Lenguaje de control de datos). Administran los derechos y restricciones de los usuarios. Lo forman las instrucciones GRANT y REVOKE. 2.1. Tipos de datos. Como todo lenguaje de programación, SQL-Oracle posee una serie de tipos de datos. Éstos se corresponden con los tipos de datos que pueden utilizarse en Oracle al definir tablas. Oracle /34

Datos numéricos: Number(38) Number Number(m,d) ROWID

Equivale al tipo Integer, Int o SamllInt Equivale al tipo Float, Double o Real Números decimales de coma fija, con m cifras en total y d decimales Valor hexadecimal que representa la dirección única de una fila en su tabla.

Datos de carácter: Char(n)

Varchar2(n) Nchar(n) Nvarchar2(n)

Almacena n caracteres en formato ASCII (como máximo 2000). Siempre se utilizan los n caracteres indicados, incluso si la entrada de datos es inferior. Cuando se recuperen los datos es como si fuesen rellenados con espacios en blanco hasta completar la longitud del campo. Almacena n caracteres en formato ASCII (como máximo 4000). Al recuperar los datos, sólo se recupera el texto asignado con su longitud original. Almacena n caracteres en formato UNICODE. Siempre se utilizan los n caracteres indicados, incluso si la entrada de datos es inferior. Almacena n caracteres en formato UNICODE.

Datos de fecha: Date TimeStamp Interval

Almacena fechas en formato día, mes, año, hasta el nivel de segundos. Almacena fechas hasta el nivel de fracciones de segundo. Almacena intervalos de tiempo. Por ejemplo, para indicar un período de 3 años y algunos meses: CREATE TABLE tiempo (meses INTERVAL YEAR(3) TO MONTH); INSERT INTO tiempo VALUES ('3-2');

Para indicar un intervalo hasta segundos (sin decimales en segundos):

CREATE TABLE tiempo (días INTERVAL DAY(3) TO SECOND(0)); INSERT INTO tiempo VALUES('2 7:12:23'); El valor entre paréntesis indicado para YEAR y DAY establece la precisión

día respectivamente.

del año y del

Datos binarios grandes: Raw Long Raw Blob, Clob, Nclob y Bfile

Sirve para almacenar valores binarios de hasta 2000 bytes (se puede especificar el tamaño máximo entre paréntesis). Almacena hasta 2GB. Almacenan datos binarios de varios gigas (imágenes, vídeos, etc.).

Datos de texto grandes: Long Clob

Almacenan secuencias de caracteres de varios gigas.

2.2. Operadores SQL-Oracle incluye los operadores habituales: Operadores de comparación = ó != ó ^=
=

Operadores lógicos AND OR NOT

Operadores para nulos IS NULL

Significado Igual que Distinto de Menor que Menor o igual que Mayor que Mayor o igual que Significado Y lógico O lógico Complementario Significado Indica si una expresión es nula Oracle /35

IS NOT NULL

Indica si una expresión no es nula

Operadores para texto

Significado Concatenador de texto Evalúa un texto contra una expresión regular

|| LIKE

2.3. Funciones predefinidas. Todos los SGBD implementan funciones para facilitar la creación de consultas complejas. Esas funciones dependen del SGBD que utilicemos; las que aquí se comentan son algunas de las que se utilizan con Oracle. Oracle proporciona una tabla llamada DUAL con la que se permiten hacer pruebas. Esa tabla tiene un solo campo (llamado DUMMY) y una sola fila de modo que es posible hacer pruebas sobre ella. Por ejemplo, la consulta: SELECT SQRT(5) FROM DUAL;

Muestra una tabla con el contenido del cálculo SQRT(5) (la raíz cuadrada de 5). 2.3.1. Funciones de cadena. ASCII(texto)

Retorna la representación decimal (en el juego de caracteres de la base de datos) del primer caracter del texto. Retorna el caracter que se corresponde con el código dado en el juego de CHR(código) caracteres de la base de datos. CONCAT(texto1, texto2) Concatena dos textos (igual que el operador ||). Coloca la primera letra de cada palabra en mayúsculas. INITCAP(texto) Obtiene la posición en la que se encuentra el texto buscado en el texto inicial. Se INSTR(texto, textoBuscado puede empezar a buscar a partir de una posición inicial concreta e incluso indicar [,posInicial [, nAparición]]) el número de aparición del texto buscado. Ejemplo, si buscamos la letra a y ponemos 2 en nAparición, devuelve la posición de la segunda letra a del texto). Si no lo encuentra devuelve 0. Obtiene el tamaño del texto. LENGTH(texto) Convierte el texto a minúsculas. LOWER(texto) Alarga por la izquierda. Crea un nuevo texto a partir del dato con la longitud LPAD(texto,longitud,pad) especificada añadiendo por la izquierda el caracter pad especificado o espacios en blanco si no se especifica. Elimina los espacios a la izquierda que posea el texto. LTRIM(texto) REPLACE(texto, textoABuscar, Buscar el texto a buscar en un determinado texto y lo cambia por el indicado como texto de reemplazo. textoReemplazo) Alarga por la derecha. Crea un nuevo texto a partir del dato con la longitud RPAD(texto,longitud,pad) especificada añadiendo por la derecha el caracter pad especificado o espacios en blanco si no se especifica. Elimina los espacios a la derecha del texto. RTRIM(texto) Retorna la representación fonética de un texto. SOUNDEX(texto) SUBSTR(texto,n[,m]) Obtiene los m siguientes caracteres del texto a partir de la posición n (si m no se indica se cogen desde n hasta el final). TRIM(caracteres Elimina del texto los caracteres indicados. Por ejemplo TRIM('h' FROM nombre) FROM texto) elimina las haches de la columna nombre que estén a la izquierda y a la derecha. Elimina los espacios en blanco a la izquierda y la derecha del texto y los espacios TRIM(texto) dobles del interior. Convierte el texto a mayúsculas. UPPER(texto) REGEXP_INSTR, Versiones de INSTR, REPLACE y SUBSTR para expresiones regulares. REGEXP_REPLACE y REGEXP_SUBSTR

Cómo cortar y pegar cadenas. Veremos cómo aplicar las funciones LPAD, RPAD, LTRIM, RTRIM, TRIM, LENGTH, SUBSTR y INSTR para cortar y pegar cadenas de caracteres. Cada una de estas funciones realiza algo para cortar y pegar. La función más simple, LENGTH, nos dice la longitud de una cadena (cuántos caracteres tiene). Por ejemplo, LENGTH('012345') retorna el valor 6. RPAD y LPAD son muy parecidas. RPAD permite rellenar una cadena existente con un conjunto de caracteres por el lado derecho. LPAD hace lo mismo pero por el lado izquierdo. Oracle /36

Como ejemplo, la expresión RPAD('12345', 8) retorna la cadena ' 12345', en la cual se ha rellenado con tres espacios en blanco por la derecha para que el resultado ocupe 8 caracteres. Podemos también especificar el caracter de relleno. Por ejemplo, LPAD('12345',10,'.') produce la salida '12345.....', donde se ha rellenado por la izquierda con cinco puntos. Estas dos funciones son interesantes porque aplicadas a un campo de una base de datos nos pueden garantizar que todos los valores de cada fila tengan la misma longitud. Las funciones LTRIM, RTRIM y TRIM nos permiten recortar los espacios en blanco previos y posteriores de cualquier cadena de caracteres. LTRIM elimina espacios en blanco por la izquierda, RTRIM por la derecha y TRIM por ambos lados. Por ejemplo, RTRIM(' 12 34 ') produce la salida '12 34', donde se han recortado los espacios en blanco por la derecha. También podemos especificar los caracteres de recorte. Por ejemplo, LTRIM('..0.012345..','0.') produce la salida '12345..', donde se han recortado los puntos y los ceros por la izquierda. Podemos combinar LTRIM y RTRIM para recortar por ambos lados. Por ejemplo, la expresión LTRIM( RTRIM('000012345......','.'), '0')

produce la salida '12345', donde se han recortado primero los puntos por la derecha y después los ceros por la izquierda. Si los caracteres a recortar por la izquierda y la derecha son los mismos podemos usar la función TRIM. Por ejemplo, TRIM('0' FROM '00123450') produce la salida '12345', donde se han recortados los ceros por la izquierda y por la derecha. Podemos usar la función SUBSTR para extraer un trozo de una cadena. Se indica la posición inicial del trozo dentro de la cadena y opcionalmente se indica el número de caracteres del trozo. Por ejemplo, SUBSTR('Soy una cadena',6,4) produce la salida 'a ca'. Se pueden también usar número negativos en la función SUBSTR. Cuando se indica una posición inicial negativa, entonces es relativa al final de la cadena. Por ejemplo, SUBSTR('Soy una cadena",-4) produce la salida 'dena'. Ya que no se especifica la longitud en un tercer parámetro se retorna la subcadena hasta el final. El valor del tercer parámetro de la función SUBSTR debe ser siempre positivo o no especificado. Si se usa un número negativo la función retorna el valor NULL. La función INSTR permite búsquedas simples o sofisticadas dentro de una cadena por un conjunto de caracteres. Esta función no produce una nueva cadena como las funciones previas, sino que determina en qué posición dentro de la cadena está la subcadena buscada. INSTR tiene dos opciones, una dentro de la otra. Podemos especificar la posición de inicio para la búsqueda, de forma que se salta los caracteres anteriores a esa posición. Por ejemplo, INSTR('AABBAACCAADD','BB',1) busca la subcadena 'BB' dentro de la cadena 'AABBAACCAADD' a partir del primer caracter. El resultado de esta función será 3. También podemos especificar qué ocurrencia buscamos. Si la subcadena buscada se repite varias veces dentro de la cadena sobre la que se busca podemos indicar cuál subcadena buscamos. Por ejemplo, INSTR('AABBAACCAADD','AA',3,2) busca la segunda ocurrencia de la subcadena 'AA' a partir del tercer caracter. El resultado de esa función será 9. Si sólo especificamos la cadena y la subcadena, la función realiza la búsqueda de la primera ocurrencia desde el primer caracter. Por ejemplo, INSTR('AABBAACCAADD','CC') devuelve el resultado 7. Cómo trabajar a nivel de caracteres en una cadena. Las funciones LOWER, UPPER e INITCAP permiten modificar los caracteres de una cadena. La función LOWER toma como argumento una cadena y retorna otra con todos sus caracteres en minúsculas. Por ejemplo, LOWER('tEXto') produce la salida 'texto'. La función UPPER hace lo opuesto, convierte todos los caracteres a mayúsculas. Por ejemplo, LOWER('TEXto') produce la salida 'TEXTO'. Por su parte, la función INITCAP convierte a mayúsculas la letra inicial de cada palabra de la cadena. Por ejemplo, INITCAP('uno dos') produce la salida 'Uno Dos'. Aunque las funciones ASCII y CHR se usan raramente en consultas, son útiles para trabajar con caracteres no habituales o no imprimibles. CHR convierte un valor numérico a su representación de caracter ASCII. Por ejemplo CHR(70) equivale al caracter 'F'. La función ASCII realiza la operación inversa, convierte un caracter a su valor numérico equivalente. Por ejemplo, ASCII('FSOUG') produce el resultado 70, ya que sólo se tiene en cuenta el primer caracter. Cómo comparar cadenas por su parecido sonoro. La función SOUNDEX tiene la inusual habilidad de encontrar palabras que suenan como otras palabras, independientemente de cómo sean deletreadas. Podemos usar SOUNDEX sobre dos cadenas distintas y comparar el resultado para saber si suenan igual o no. Por ejemplo, la expresión de comparación SOUNDEX('a Oracle /37

evalúa a verdadero. hace determinadas asunciones sobre cómo las letras y combinaciones de letras se pronuncian normalmente en inglés, y las dos cadenas a comparar deben comenzar con la misma letra. Podemos usar SOUNDEX para encontrar erratas sobre una lista de valores y corregirlas, evitando de esta forma incoherencias entre los datos. Por ejemplo, en una tabla VENTA que contenga nombres de cliente, podemos buscar erratas en un mismo nombre de cliente con la siguiente consulta. ver tú')=SOUNDEX('abierto') SOUNDEX

SELECT V1.IdVenta, V1.Nombre, V2.IdVenta, V2.Nombre FROM VENTA V1, VENTA V2 WHERE V1.Nombre > V2.Nombre AND SOUNDEX(V1.Nombre)=SOUNDEX(V2.Nombre); dos registros de VENTA aparecen los nombres 'José Pérez' y 'Jose Perez' obtendremos

Si en en el resultado los ID's de ambos registros, lo cual nos permitirá después corregir uno de los nombres. (La condición V1.Nombre > V2.Nombre impide que obtengamos dos registros con el mismo emparejamiento.) Soporte para otros idiomas distintos del inglés. Oracle no usa sólo caracteres ingleses; puede representar datos en cualquier idioma porque implementa el soporte para lenguajes nacionales (NLS). Al usar una representación que usa más espacio que el que ocupan los caracteres ordinarios, Oracle puede representar caracteres japoneses y de otros idiomas. Las funciones NLS_SORT, NLS_INITCAP, NLS_LOWER y NLS_UPPER son versiones para NLS de las funciones ordinarias. Además de la función SUBSTR, Oracle incluye SUBSTRB (para buscar bytes en vez de caracteres), SUBSTRC (para buscar caracteres Unicode), SUBSTR2 (para buscar codepoints UCS2) y SUBSTR4 (para buscar codepoints UCS4). Soporte para expresiones regulares. Desde Oracle Database 10g, las funciones de cadena INSTR, REPLACE y SUBSTR han sido extendidas para soportar expresiones regulares con las funciones. En la siguiente sección se describen estas nuevas funciones. 2.3.2. Funciones que trabajan con expresiones regulares. Las nuevas funciones que soportan expresiones regulares son REGEXP_SUBSTR, REGEXP_INSTR, REGEXP_LIKE y REGEXP_REPLACE. Búsqueda de cadenas. Con la función REGEXP_SUBSTR podemos realizar búsquedas más sofisticadas que las realizadas con la función SUBSTR. Por ejemplo, si tenemos códigos de producto con el formato '123-ABC-456' donde cada secuencia de dígitos y letras separados por guiones tiene un significado determinado, puede ser necesario acceder sólo a la secuencia de letras de cada código de producto. Si sabemos que la secuencia de letras está siempre encapsulada entre guiones podemos usar una expresión regular para realizar la búsqueda de esta secuencia. Con la expresión REGEXP_SUBSTR('123-ABC-456', '-[^-]+-')

le decimos a Oracle que empiece buscando una subcadena que comience con un guión, a continuación la expresión [^-] le dice a Oracle que puede seguirle cualquier caracter que no sea un guión, y el signo + indica que se puede repetir una o más veces lo anterior (es decir, caracteres seguidos que no sean guiones), y por último debe incluir un guión. En este ejemplo, la función REGEXP_SUBSTR retorna '-ABC-'. Si intentásemos este mismo ejemplo usando funciones ordinarias tendríamos una expresión como la siguiente: SUBSTR( '123-ABC-456', INSTR('123-ABC-456', '-',1,1), INSTR('123-ABC-456', '-',1,2)-INSTR('123-ABC-456', '-',1,1))

Que evidentemente es menos concisa que la anterior. La tabla siguiente describe la simbología de las expresiones regulares:

Expresión Descripción Representa a cualquier caracter (excepto el salto de línea) . Indica que lo anterior se puede repetir cero o más veces * Indica que lo anterior se puede repetir una o más veces + Operador de alternancia. Se usa para indicar expresiones alternativas | Los paréntesis se usan para agrupar expresiones, de forma que sean tratadas como () una unidad Representa el final del texto o de la línea. No es un caracter sino una posición $ Representa el inicio del texto. No es un caracter sino una posición ^ Indica una A opcional. El ? es válido para hacer opcional cualquier expresión A? Oracle /38

A{3} A{3,} A{3,8} [aBcD] [C-G] [^aBcD] [.ch.] \t \r \n \a \e \f \v \x00A2 \d \w \s \D \W \S \A \Z \b \B

Tres A'es seguidas. Las llaves indican repetición exacta de expresiones Al menos tres A'es seguidas Al menos tres A'es y menos de ocho A'es Alguno de los caracteres a, B, c o D Un caracter en el rango entre C y G inclusive Cualquier carácter distinto de a, B, c o D Especifica el caracter ch español El caracter tabulador El caracter retorno de carro El caracter de nueva línea El caracter beep El caracter de escape El caracter salto de página El caracter tabulador vertical El caracter de código ASCII 0042 Un dígito del 0 al 9 Cualquier carácter alfanumérico Un espacio en blanco Cualquier caracter que no sea un dígito Cualquier caracter no alfanumérico Cualquier carácter que no sea un espacio en blanco Representa el inicio de la cadena. No un caracter sino una posición Representa el final de la cadena. No un carácter sino una posición Marca el inicio y el final de una palabra Marca la posición entre dos caracteres alfanuméricos o dos no-alfanuméricos

Además de esta simbología, Oracle soporta las siguientes clases de caracteres según las definiciones de clases de caracteres en NLS. Clase [:alnum:] [:alpha:] [:blank:] [:cntrl:] [:digit:] [:graph:] [:lower:] [:print:] [:punct:] [:space:] [:upper:] [:xdigit:]

Descripción Todos los caracteres alfanuméricos Todos los caracteres alfabéticos Todos los caracteres de espacio en blanco Todos los caracteres de control (no imprimibles) Todos los dígitos numéricos Todos los caracteres[:punct:], [:upper:], [:lower:], y [:digit:] Todos los caracteres alfabéticos en minúsculas Todos los caracteres imprimibles Todos los caracteres de puntuación Todos los caracteres de espacio (no imprimibles) Todos los caracteres alfabéticos en mayúsculas Todos los caracteres hexadecimales válidos

La función «REGEXP_SUBSTR». La función REGEXP_SUBSTR, como se ha visto, usa expresiones regulares para especificar el punto inicial y final de la subcadena retornada. La sintaxis completa de esta función es la siguiente: REGEXP_SUBSTR(source_string, pattern [, position [, occurrence [, match_parameter ]]])

Esta función retorna una cadena de tipo VARCHAR2 o CLOB con el mismo juego de caracteres que el parámetro source_string, que especifica la cadena en la que se busca. El parámetro pattern especifica la expresión regular, que puede contener un máximo de 512 bytes. El parámetro position indica dónde empieza la búsqueda dentro de source_string; el valor por defecto es 1. El parámetro ocurrence es un entero que indica que posición de la ocurrencia buscamos; su valor por defecto es 1. Se puede usar el parámetro match_parameter para cambiar el comportamiento de búsqueda de la función; los posibles valores que podemos incluir en este parámetro son: i c n

Se usa para indicar búsquedas no sensibles a mayúsculas y minúsculas. Se usa para indicar búsquedas sensibles a mayúsculas y minúsculas. Permite que el caracter punto (.) también case con el salto de línea.

Oracle /39

m

Trata la cadena de origen como una cadena de varias líneas. Oracle tratará ^ y $ como el inicio y final, respectivamente, de cada línea.

Si se especifican valores contradictorios en el parámetro match_parameter, Oracle usa el último valor. El siguiente ejemplo realiza una búsqueda insensible a mayúsculas y minúsculas: REGEXP_SUBSTR('MI LIBRO DE CONTABILIDAD: Débitos, Créditos y Facturas 1940','mi',1,1, 'i')

Siendo el resultado 'MI'. Si ahora cambiamos el modificador de búsqueda:

REGEXP_SUBSTR('MI LIBRO DE CONTABILIDAD: Débitos, Créditos y Facturas 1940', 'mi', 1, 1, 'c') resultado será el valor NULL.

El Por defecto, las búsquedas son sensibles a mayúsculas y minúsculas. En el siguiente ejemplo, se busca el segundo dígito: REGEXP_SUBSTR('MI LIBRO DE CONTABILIDAD: Débitos, Créditos y Facturas 1940', '[[:digit:]]', 1, 2)

Siendo en este caso el resultado 9. La función «REGEXP_INSTR». La función REGEXP_INSTR usa expresiones regulares para retornar el punto inicial o final de una búsqueda. La sintaxis de esta función es como sigue: REGEXP_INSTR (source_string, pattern[, position [, ocurrence[, return_option[, match_parameter ]]]])

Esta función retorna un entero que indica la posición inicial o final de la subcadena especificada por el patrón, o cero si no encuentra ninguna subcadena. Esta función es similar a la función REGEXP_SUBSTR, y añade una nueva funcionalidad. El parámetro return_option permite especificar el significado del valor devuelto por la función: • Si return_option es 0, la función retorna la posición del primer caracter de la ocurrencia. Éste es el valor por defecto. • Si return_option es 1, la función retorna la posición del caracter que sigue a la ocurrencia. Por ejemplo, la siguiente expresión retorna la posición del primer dígito dentro de una cadena: REGEXP_INSTR('MI LIBRO DE CONTABILIDAD: Débitos, Créditos y Facturas 1940','[[:digit:]]')

Siendo el resultado 56. Si queremos la posición siguiente a la de este dígito, la expresión será:

REGEXP_INSTR('MI LIBRO DE CONTABILIDAD: Débitos, Créditos y Facturas 1940', '[[:digit:]]', 1, 1, 1)

La función «REGEXP_LIKE». La función REGEXP_LIKE permite evaluar una cadena para ver si coincide con un patrón especificado con una expresión regular. Esta función retorna verdadero o falso. Por ejemplo, la expresión establece si una cadena comienza por '111': REGEXP_LIKE('111-222-333', '111+')

En este caso la función se evalúa a verdadero. El formato de esta función es:

REGEXP_LIKE(source_string, pattern, [match_parameter ])

Donde el significado de los parámetros es similar al de las funciones vistas previamente. Las funciones «REPLACE» y «REGEXP_REPLACE». La función REPLACE reemplaza una subcadena dentro de una cadena por otra. Por ejemplo, podemos reemplazar cada ocurrencia de una letra con un número. El formato para REPLACE es: REPLACE (source_string, search_string [, replace_string])

Si no especificamos un valor para el parámetro replace_string, la subcadena search_string es quitada de la cadena de origen. La cadena de origen puede ser de cualquiera de los tipos de caracteres ( CHAR, VARCHAR2, NCHAR, NVARCHAR2, CLOB o NCLOB). Por ejemplo: REPLACE('GEORGE', 'GE', 'EG')

Produce el resultado 'EGOREG', y:

REPLACE('GEORGE', 'GE', NULL) Produce el resultado 'OR'. La función REGEXP_REPLACE extiende

las capacidades de la función REPLACE de varios modos. Soporta el uso de expresiones regulares en el parámetro search_string, y además incluye los parámetros de las funciones descritas en este capítulo: position, ocurrence y match_parameter. La sintaxis para la función REGEXP_REPLACE es la siguiente: REGEXP_REPLACE(source_string, pattern[, replace_string[, position[, ocurrence[, match_parameter ]]]])

En el siguiente ejemplo se pone entre paréntesis cada terna de un número telefónico:

REGEXP_REPLACE ('111222333', '([[:digit:]]{3})([[:digit:]]{3})([[:digit:]]{3})', '(\1) (\2) (\3)')

Oracle /40

El resultado de este ejemplo es '(111) (222) (333)'. 2.3.3. Funciones numéricas. ABS(n) ACOS(n) ASIN(n) ATAN(n) ATAN2(n, m) BITAND (n, m) CEIL(n) COALESCE(n, m, …) COS(n) COSH(n) EXP(n) FLOOR(n) GREATEST(n, m, …) LEAST(n, m, …) LN(n) LOG(n) MOD(n1,n2) NANVL(n,m)

NVL(n, m) NVL2(n,m,p) POWER(valor, exponente) REMAINDER(n, m) ROUND(n, decimales) SIGN(n) SIN(n) SINH(n) SQRT(n) TAN(n) TANH(n) TRUNC(n, decimales) VSIZE(n)

Devuelve el valor absoluto del argumento. Devuelve, en radianes, el arcocoseno del argumento. Devuelve, en radianes, el arcoseno del argumento Devuelve, en radianes, el arcotangente del argumento Devuelve, en radianes, la arcotangente de dos argumentos. Realiza una operación AND a nivel de bits entre los argumentos y devuelve un entero con el resultado. Devuelve el valor entero más pequeño mayor o igual que el argumento. Retorna el primer valor no NULL de la lista de argumentos. Devuelve el coseno del argumento (el cual tiene que estar en radianes). Devuelve el coseno hiperbólico del argumento. Devuelve el valor de e elevado al argumento n. Devuelve el valor entero más grande menor o igual que el argumento Retorna el valor más grande de la lista de argumentos, o NULL si uno de ellos es nulo. Retorna el valor más pequeño de la lista de argumentos, o NULL si uno de ellos es nulo. Devuelve el logaritmo neperiano del argumento. Devuelve el logaritmo en base 10 del argumento n. Devuelve el resto de la división entera de los argumentos. Para números BINARY_FLOAT y BINARY_DOUBLE, esta función retorna el segundo argumento si el primero no es un número, sino retorna el primer argumento Retorna el segundo argumento si el primero es NULL, sino retorna el primer argumento. Retorna el tercer argumento si el primero es NULL, sino retorna el segundo argumento. Retorna el resultado de elevar un valor al exponente indicado. Retorna el resto de dividir dos números. Redondea el número al siguiente número con el número de decimales indicado más cercano. Devuelve 1 si el argumento es positivo, cero si vale cero y -1 si es negativo Devuelve el seno del argumento (el cual tiene que estar en radianes). Devuelve el seno hiperbólico del argumento. Devuelve la raíz cuadrada del argumento. Devuelve la tangente del argumento (el cual tiene que estar en radianes). Devuelve la tangente hiperbólica del argumento. Los decimales del número se cortan para devolver sólo el número de decimales indicado. Almacena el tamaño del argumento en Oracle.

Las funciones de Oracle tratan con tres clases de números: valores simples, grupos de valores y las listas de valores. Como con las funciones de cadenas, algunas funciones numéricas cambian los valores que les son aplicados, mientras que otras reportan información acerca de los valores. Un valor simple es un número procedente de: • Una expresión literal, como 544.3702. • Una variable de SQL*Plus o PL/SQL. • Una columna de una fila de base de datos. Las funciones de valores simples normalmente cambian estos valores tras algún cálculo. Un grupo de valores son todos los números de una columna de una serie de filas. Las funciones sobre grupos de valores realizan alguna operación sobre todos los números del grupo, como el promedio de los precios de venta, pero no sobre los valores individuales. (Las funciones numéricas sobre grupos de valores se denominan «funciones de agregado» y se estudiarán en el capítulo correspondiente.) Una lista de valores es una serie de número que pueden proceder de: Oracle /41

• Una lista de expresiones literales separadas por comas, como 1, 7.3, 22. • Variables de SQL*Plus o PL/SQL. • Columnas de una base de datos. Las funciones de listas de valores seleccionan uno de los valores de la lista según algún criterio. Funciones de valor simple. La mayor parte de funciones de valor simple son bastante intuitivas. La función ABS retorna el valor independientemente del signo. Por ejemplo ABS(-23) devuelve el valor 23. La función CEIL busca el valor entero más pequeño que sea más grande o igual que uno dado. Hay que prestar atención especial a su efecto sobre números negativos. La siguiente tabla muestra algunos ejemplos: CEIL(2) CEIL(1.3) CEIL(-2) CEIL(-2.3)

= = = =

2 2 -2 -2

La función FLOOR es la opuesta a CEIL. La siguiente tabla muestra algunos ejemplos: FLOOR(2) FLOOR(1.3) FLOOR(-2) FLOOR(-2.3)

= = = =

2 1 -2 -3

La función MOD permite dividir un número entre otro y retorna el resto de la división entera. Por ejemplo MOD(23,6) divide 23 entre 6 y devuelve el resto 5. El valor de MOD es cero si el divisor es cero o negativo. La siguiente tabla muestra algunos ejemplos: MOD(100,10) MOD(22,23) MOD(10,3) MOD(-30.23,7) MOD(4.1,.3) MOD(44, 1)

= = = = = =

0 22 1 -2.23 .2 0

Esta función puede ser interesante para saber si un número es par o impar. Simplemente hay que tener en cuenta que si dividimos por 2, los números pares tienen resto 0 y los impares resto 1. También es útil para normalizar cualquier número dentro de un rango. Por ejemplo, cualquier número dividido entre 10 dará como resto un valor entre 0 (inclusive) y 10 (exclusive). La función TRUNC trunca, o recorta, los dígitos de precisión de un número; mientras que la función ROUND redondea un número según el número de dígitos de precisión especificado. El formato de ambas funciones es el siguiente: ROUND(valor, precisión) TRUNC(valor, precisión)

Si no se especifica la precisión se toma por defecto precisión cero. La siguiente tabla muestra ejemplos de ambas funciones: ROUND(11, 2) ROUND(-22, 2) ROUND(33.33, 2) ROUND(55.5, 2) ROUND(66.666, 2)

= = = = =

11 -22 33.33 55.5 66.67

TRUNC(11, 2) TRUNC(-22, 2) TRUNC(33.33, 2) TRUNC(55.5, 2) TRUNC(66.666, 2)

= = = = =

11 -22 33.33 55.5 66.66

TRUNC(11, -1) TRUNC(-22, -1) TRUNC(33.33, -1) TRUNC(55.5, -1) TRUNC(66.666, -1)

= = = = =

10 -20 30 50 60

En todos estos ejemplos se redondea y trunca en base a dos decimales. Hay que tener en cuenta que el valor decimal de .5 es redondeado siempre hacia arriba, de forma que ROUND(55.5) da como resultado 56. También podemos trabajar con precisiones negativas, de forma que se mueve el punto decimal hacia la izquierda. Por ejemplo: ROUND(11, -1) = 10 ROUND(-22, -1) = -20 ROUND(33.33, -1) = 30 ROUND(55.5, -1) = 60 ROUND(66.666, -1) = 70 usar precisión -1 se redondea al

En estos ejemplos al nivel de decenas, si se usase precisión -2 se redondearía al nivel de centenas, y así sucesivamente. El redondeo con un número negativo puede ser útil para generar informes económicos, donde las sumas de poblaciones o moneda tienen que ser redondeadas hasta los millones, billones o trillones. Oracle /42

La función SIGN es la otra cara del valor absoluto. Mientras que ABS nos da la magnitud de un valor, pero no su signo, SIGN nos da el signo de un valor, pero no su magnitud. Por ejemplo, SIGN(146) nos da 1, mientras que SIGN(-30) nos da -1. Además, SIGN(0) da 0. La función SIGN es normalmente usada en conjunción con la función DECODE. Las funciones trigonométricas seno, coseno y tangente son funciones científicas y técnicas que no se usan mucho en los negocios. SIN, COS y TAN obtienen los valores correspondientes para un ángulo expresado en radianes (grados multiplicados por pi dividido por 180). Funciones de lista. Las funciones de lista trabajan sobre un grupo de valores, realizando una selección simple sobre ellos. Así, las funciones GREATEST y LEAST pueden ser usadas sobre varios valores (numéricos o de caracteres) de la siguiente forma: GREATEST('Bob', 'Jorge', 'Andrés, 'Isaías') = Isaías LEAST('Bob', 'Jorge', 'Andrés, 'Isaías') = Andrés GREATEST obtiene el mayor valor de la lista de valores,

mientras que LEAST obtiene el menor valor de la lista de valores. Para el ejemplo previo se utiliza la ordenación alfabética de caracteres para establecer qué nombre es mayor y menor. Podemos usar la función COALESCE para evaluar varios valores que pueden incluir nulos. COALESCE retorna el primer valor de una lista que no sea nulo, y si todos son nulos retorna el valor NULL. Por ejemplo: COALESCE( NULL, 4, NULL, 7) = 4

2.3.4. Funciones que trabajan con nulos. NVL(valor, sustituto) NVL2(valor,sustituto1,sustituto2) NANVL(valor, sustituto)

Si el valor es NULL, devuelve el valor sustituto; de otro modo, devuelve valor. Variante de la anterior, devuelve el valor sustituto1 si valor no es nulo. Si valor es nulo devuelve el sustituto2. Análoga a NVL para los tipos BINARY_FLOAT y BINARY_DOUBLE.

Los valores nulos no pueden ser usados para realizar cálculos. Cuando se realiza una operación entre dos valores, uno de los cuales es NULL, el resultado siempre será NULL. Debemos tener en cuenta que el valor NULL no es igual que el valor cero; más bien el valor NULL quiere decir un valor no conocido o irrelevante. Sin embargo, habrá consultas donde debamos realizar cálculos sobre campos que admiten el valor nulo. En estos casos es importante saber si un valor determinado es nulo y, en ese caso, poder sustituirlo por un valor determinado. Por ejemplo, si tenemos una tabla de ventas, que tiene una columna "cantidad" que admite valores nulos, podemos querer interpretar los valores nulos como el valor cero. Esto se puede hacer con la función NVL aplicándola de la siguiente manera: SELECT idVenta, NVL(cantidad, 0) FROM Ventas;

Esta consulta permite mostrar los id's de venta y las cantidades en cada venta. Si el valor de cantidad está a nulo se mostrará el valor cero. El formato de esta función es el siguiente: NVL(valor, sustituto)

Si el argumento valor es NULL, esta función retorna el argumento sustituto, sino retorna el propio valor. NVL funciona con cualquier tipo de datos, pero tanto valor como sustituto deben ser del mismo tipo. NVL es realmente útil en casos donde el dato es desconocido pero no irrelevante. En los casos donde debamos evaluar si el dato es nulo pero su valor concreto es irrelevante podemos usar la función NVL2. Su formato es el siguiente: NVL2 ( expr1 , expr2 , expr3 )

En NVL2, expr1 nunca será retornada; sino que, o bien expr2oexpr3serán retornados. Si expr1no es NULL, NVL2 retorna expr2. Siexpr1es NULL, NVL2 retorna expr3. El primer argumento puede ser de cualquier tipo. Los otros dos argumentos deben ser del mismo tipo excepto LONG. Desde Oracle Database 10g, podemos usar la función análoga NANVL para los tipos de datos BINARY_FLOAT y BINARY_DOUBLE. NANVL toma dos argumentos, y retorna el segundo si el primero no es un número. 2.3.5. Funciones de fecha y hora. ADD_MONTHS(fecha,n) CURRENT_DATE CURRENT_TIMESTAMP DBTIMEZONE EXTRACT(valor FROM fecha)

Añade a la fecha el número de meses indicado por n. Retorna la fecha y hora actual en la zona horaria de la sesión. Retorna la fecha y hora actual con la información de zona horaria activa. Retorna la zona horaria actual en formato UTC. Extrae un valor de una fecha concreta. El valor puede ser day (día), month (mes), year (año), etc. Oracle /43

FROM_TZ(timestamp)

Convierte un valor timestamp a otro timestamp con un valor de zona horaria. Devuelve la fecha más moderna de la lista. GREATEST(fecha1, ...) Obtiene el último día del mes al que pertenece la fecha. Devuelve un valor LAST_DAY(fecha) DATE. Devuelve la fecha más antigua de la lista. LEAST(fecha1, fecha2,...) Retorna el timestamp local en la zona horaria activa, pero sin mostrar la LOCALTIMESTAMP información de zona horaria. MONTHS_BETWEEN(fecha1,fecha2) Obtiene la diferencia en meses entre las dos fechas (puede ser decimal). NEW_TIME(fecha,esta,otra) Obtiene la fecha (y hora) en esta zona horaria. El valor del argumento esta puede ser reemplazado por una abreviatura de tres letras para la zona horaria actual. El valor del argumento otra puede ser reemplazado por una abreviatura de otra zona horaria para la cual queremos saber la fecha y hora. Los valores de zonas horarias son: AST/ADT Atlantic standard/daylight time BST/BDT Bering standard/daylight time CST/CDT Central standard/daylight time EST/EDT Eastern standard/daylight time GMT Greenwich mean time HST/HDT Alaska-Hawaii standard/daylight time MST/MDT Mountain standard/daylight time NST Newfoundland standard time PST/PDT Pacific standard/daylight time YST/YDT Yukon standard/daylight time Indica cual es el día que corresponde a añadir a la fecha el día indicado. El NEXT_DAY(fecha,día) día puede ser el texto 'Lunes', 'Martes', 'Miércoles',... (si la configuración está en español) o el número de día de la semana (1=lunes, 2=martes,...) NUMTODSINTERVAL(valor,formato) Convierte el argumento valor a un literal de intervalo de tiempo según las unidades especificadas en el segundo argumento. El argumento formato puede ser 'DAY', 'HOUR', 'MINUTE' o 'SECOND'. NUMTOYMINTERVAL(valor,formato) Convierte el argumento valor a un literal de intervalo de tiempo según las unidades especificadas en el segundo argumento. El argumento formato puede ser 'YEAR' o 'MONTH'. Redondea la fecha al valor de aplicar el formato a la fecha. El formato ROUND(fecha [,'formato']) puede ser 'YEAR', 'MONTH', 'HH24' o 'DAY'. Retorna el valor de la zona horaria de la sesión actual. SESSIONTIMEZONE Extrae de la fecha actual al valor UTC (Coordinated Universal Time). SYS_EXTRACT_UTC Obtiene la fecha y hora actuales. SYSDATE SYSTIMESTAMP Obtiene la fecha y hora actuales en formato TIMESTAMP. Da formato a una fecha según el patrón especificado en el segundo TO_CHAR(fecha,formato) argumento. Interprete un texto como una fecha según el patrón especificado en el TO_DATE(texto,formato) segundo argumento. TO_DSINTERVAL(texto) Convierte un texto de tipo CHAR, VARCHAR2, NCHAR o NVARCHAR2 a un tipo de intervalo entre días y segundos. TO_TIMESTAMP(texto) Convierte un texto de tipo CHAR, VARCHAR2, NCHAR o NVARCHAR2 a un valor de tipo TIMESTAMP. TO_TIMESTAMP_TZ(texto) Convierte un texto de tipo CHAR, VARCHAR2, NCHAR o NVARCHAR2 a un tipo TIMESTAMP con zona horaria. TO_YMINTERVAL(texto) Convierte un texto de tipo CHAR, VARCHAR2, NCHAR o NVARCHAR2 a un tipo de intervalo entre años y meses. Trunca la fecha al valor de aplicar el formato a la fecha. El formato puede TRUNC(fecha, [formato]) ser 'YEAR', 'MONTH', 'HH24' o 'DAY'. Retorna la diferencia de zona horaria correspondiente al valor del TZ_OFFSET(texto) argumento.

Una de las bazas de Oracle es su capacidad de almacenar y calcular fechas, y el número de segundos, minutos, horas, días, meses y años entre fechas. Además de las funciones de fecha básicas, Oracle soporta muchas Oracle /44

funciones de conversión y la habilidad de dar formato a fechas en cualquier manera concebible. Aritmética de fechas. El tipo de dato para fechas en Oracle es DATE. Un tipo DATE es almacenado en un formato interno especial que incluye el día, mes, año, horas, minutos y segundos de la fecha. También se puede usar el tipo TIMESTAMP para almacenar fechas hasta fracciones de segundo. SQL*Plus y SQL reconocen columnas que tienen el tipo DATE e interpretan las instrucciones donde se utilizan operaciones aritméticas con fechas como aritmética de fechas y no como aritmética matemática. Por ejemplo, sumando el valor 1 a una fecha se obtiene otra fecha con el día siguiente; y si restamos dos fechas, obtenemos la diferencia de días entre ellas. Sin embargo, dado que las fechas de Oracle incluyen horas, minutos y segundos, la aritmética de fechas puede ser difícil de interpretar; por ejemplo, podríamos decir que ¡la diferencia entre hoy y mañana es de 0,516 días! Las funciones «SYSDATE», «CURRENT_DATE» y «SYSTIMESTAMP». Oracle consulta el sistema operativo del ordenador para obtener la fecha y hora actuales. Para ello proporciona una función especial llamada SYSDATE. Podemos ver SYSDATE como una función que siempre retorna la fecha y hora actual. Por ejemplo, la siguiente consulta: SELECT SYSDATE FROM DUAL;

Retorna una fila con una columna con el valor de la fecha (día, mes y año) actual del ordenador. Una segunda función, CURRENT_DATE, recupera la fecha del sistema en la zona horaria de la sesión (podemos asignar la zona horaria dentro de nuestra sesión local, la cual puede diferir de la zona horaria de la base de datos). Otra función, SYSTIMESTAMP, recupera la fecha del sistema en un formato más completo que incluye la hora e información de zona horaria. Cómo añadir y sustraer meses a una fecha. Se puede utilizar la función ADD_MONTHS para añadir un número de meses a una fecha dada. Por ejemplo ADD_MONTHS('1/2/2011', 6) da como resultado la fecha '1/8/2011'. Esta función siempre se mantendrá dentro del margen de fechas válidas. Por ejemplo, ADD_MONTHS('31/1/2011', 1) da como resultado la fecha '28/2/2011'. Añadiendo valores negativos a esta función conseguiremos restar meses a una fecha. Por ejemplo ADD_MONTHS('31/1/2011', -4) da como resultado '30/09/10'. Las funciones «GREATEST» y «LEAST». Las funciones GREATEST y LEAST trabajan sobre expresiones de tipo fecha igual que sobre otros tipos de datos, excepto si se aplican sobre una lista de valores literales. Por ejemplo GREATEST('2/1/2011','2/2/2000') da como resultado el valor '2/2/2000', que es claramente una fecha anterior a '2/1/2011'. Esto es así porque estas funciones interpretan los literales de fechas como strings. Para que estas funciones trabajen apropiadamente sobre valores literales de fecha debemos aplicar la función TO_DATE sobre los literales. Por ejemplo GREATEST(TO_DATE('2/1/2011'),TO_DATE('2/2/2000')) da ahora como resultado el valor '2/1/2011'. La función «NEXT_DAY». NEXT_DAY computa la fecha correspondiente al día de la semana indicado (esto es, lunes, martes, miércoles, jueves, viernes, sábado o domingo) después de una fecha dada. Por ejemplo, en una tabla de citas, la cita puede ser siempre el primer viernes después de una fecha inicial dada. En este caso podemos almacenar en la tabla la fecha en que se concierta la fecha y consultar la tabla para obtener la fecha concreta de la cita, de la siguiente manera: SELECT NEXT_DAY(FechaInicial, 'Viernes') FROM Citas;

Si el valor de FechaInicial es'1/11/2011' el resultado será '04/11/2011', que se corresponde con el viernes siguiente. Sin embargo si aplicamos NEXT_DAY('4/11/2011','Viernes') obtendremos como resultado el '11/11/2011', es decir, el siguiente viernes y no el actual. Si queremos evitar esto y mostrar el propio día si coincide con el viernes, nos basta con restar un día a la fecha, obteniendo así la consulta: SELECT NEXT_DAY(FechaInicial - 1,'Viernes') FROM Citas;

La función «LAST_DAY». LAST_DAY computa la fecha del último día del mes. Podemos aplicar esta función sobre una fecha dada para obtener la fecha del último día del mismo mes. Por ejemplo, LAST_DAY('4/2/2011') retorna el valor '28/02/11'. Meses entre dos fechas. La función MONTHS_BETWEEN permite computar el número de meses de diferencia entre dos fechas. Podemos utilizar esta función, por ejemplo, para calcular la edad actual (en años) de una persona. La siguiente Oracle /45

consulta hacer esto sobre una persona nacida el '23/4/1987':

SELECT FLOOR( MONTHS_BETWEEN(SYSDATE, '23/4/1987') / 12 ) FROM DUAL;

En este caso de aplica la función FLOOR para eliminar valores decimales del resultado. Redondear y truncar cálculos sobre fechas. Si asumimos que SYSDATE retorna el valor '23/3/2011' podemos realizar una resta de fechas como: SELECT TO_DATE('28/3/2011') - SYSDATE FROM DUAL; Siendo el resultado 4,4135 en vez de 5 días. La razón de

este número con decimales es porque SYSDATE incluye horas, minutos y segundos, y Oracle los utiliza para realizar la sustracción. Para simplificar alguna de las dificultades que podemos encontrarnos usando fracciones de días, Oracle hace algunas asunciones acerca de las fechas: • Un literal de fecha, como '28/03/2011', asume por defecto la hora 12:00:00 A.M. • Una fecha introducida a través de SQL*Plus, a menos que se especifique, asume por defecto la hora

12:00:00 A.M. • SYSDATE siempre incluye la fecha y la hora, a menos que lo redondeemos explícitamente. Usar la función ROUND sobre una fecha la redondea a las 12 A.M. del mismo día si es antes del mediodía, y a las 12 A.M. del día siguiente si es después del mediodía. La función TRUNC actúa de forma similar, excepto que asigna la hora a las 12 A.M. del mismo día e incluyendo un segundo después de medianoche.

Para obtener el número redondeado de días debemos usar la siguiente consulta: SELECT TO_DATE('28/3/2011') – ROUND(SYSDATE) FROM DUAL;

Si la fecha actual es después del mediodía la diferencia será de 4 días. Formatos para «TO_DATE» y «TO_CHAR». TO_DATE y TO_CHAR son parecidos en la medida en que ambos tienen poderosas capacidades de formato. TO_DATE convierte una cadena o un número en una fecha, mientras que TO_CHAR convierte una fecha en una cadena de caracteres. Los formatos de ambas funciones son los siguientes: TO_CHAR( fecha [, formato [, parametrosNLS]]) TO_DATE( cadena [, formato [, parametrosNLS]]) argumento fecha debe ser un valor literal (con un formato argumento cadena debe ser un literal de texto, un literal

El de fecha válido) o una columna de tipo DATE. El numérico o una columna de base de datos que contenga un string o un número. En cada caso, el formato de la cadena debe corresponderse con el descrito en el argumento formato. Si se omite el argumento de formato sólo se admiten los formatos por defecto: 'día/mes/año' o 'día-mes-año'. El argumento formato es una colección de opciones que podemos combinar para indicar un patrón de fecha. El argumento parametrosNLS es un string que asigna la opción NLS_DATE_LANGUAGE a un idioma específico, en vez de usar el idioma de la sesión actual. Normalmente no es necesario usar este argumento opcional. Oracle retorna nombres de días y meses en el idioma asignado por la sesión. Como ejemplo, la expresión TO_CHAR(SYSDATE, 'DD/MM/YYYY') retorna la fecha actual en un formato como '25/06/2012'. Mientras que la expresión TO_CHAR(SYSDATE, 'DD-MON-YYYY') retorna la fecha actual en un formato como '25-JUN-2012'. En el argumento de formato podemos usar las siguientes opciones para especificar un patrón de fecha: Formato /, -:.; A.D. ó AD A.M. ó AM B.C. ó BC CC SCC D DAY DD DDD DL DS DY E

Significado Signos de puntuación que serán incorporados en TO_CHAR e ignorados en TO_DATE. Indicador de AD. Muestra A.M. o P.M., dependiendo del momento del día. Indicador de BC. Siglo (por ejemplo, 21 para 2004). Siglo, para fechas BC prefijadas con guión. Número del día de la semana: de 1 a 7. El día con nombre completo. Número del día en el mes: de 1 a 31. Número del día en el año (desde el 1 de enero): 1 a 366. La fecha en un formato largo, como por ejemplo 'Martes 14 de Junio de 2011'. La fecha en un formato corto, como por ejemplo '14/06/2011'. Las tres letras de abreviatura del día. Por ejemplo, VIE para el viernes. Abreviatura del nombre de era (para calendarios Japanese Imperial, ROC Official y Thai Buddha). Oracle /46

EE FF [1..9] FM FX HH ó HH12 HH24 I IW IY IYY IYYY J MI MM MON MONTH P.M. Q RM RR RRRR SS SSSSS TS TZD TZH TZM TZR W WW X YEAR ó SYEAR YYYY ó SYYYY Y,YYY Y YY YYY

Versión a nombre completo del formato E. Fracciones de segundo. El número que sigue a FF especifica el número de dígitos en la parte de fracciones de segundo. Suprime espacios en blanco de rastreo y administración. Sin FM, todos los meses y días son mostrados con el mismo ancho. Especifica que case el formato exacto para el argumento de texto y de formato. Hora del día: 1 a 12. Hora del día: 0 a 23. Un dígito de año del estándar ISO. Semanas en el año del estándar ISO: 1 a 53. Dos dígitos de año del estándar ISO. Tres dígitos de año del estándar ISO. Cuatro dígitos de año del estándar ISO. Valor Juliano (días desde el 31 de Diciembre de 4712 B.C.). Minutos de la hora: 0 a 59. Número del mes: 1 a 12. Abreviatura de tres letras del mes (por ejemplo, AGO para agosto). El nombre completo del mes. Análogo a A.M. Cuatrimestre del año: 1 a 4. Número romano del mes. Últimos dos dígitos del año relativo a la fecha actual. Año redondeado, aceptando dos o cuatro dígitos. Segundos del minuto: 0 a 59. Segundos desde medianoche: 0 a 86399. Formato de hora corta, para usar con DL o DS. Información de tiempo de ahorros de luz del día. Hora de la zona horaria. Minuto de la zona horaria. Región de la zona horaria. Número de semanas en un mes (desde 1 para la primera semana del mes). Número de semanas en un año (desde 1 para la primera semana del año). Carácter de raíz local. El año hablado en inglés. Por ejemplo, una fecha con el año 2011 se corresponde con 'twenty eleven'. El año con cuatro dígitos. Año con separador de miles. Último dígito del año. Dos últimos dígitos del año. Tres últimos dígitos del año.

Los formatos que sólo trabajan con TO_CHAR, pero no con TO_DATE, son los siguientes: Formato Significado TH Sufijo para un número. Por ejemplo, ddTH o DDTH produce 24th o 24TH, respectivamente. SP Sufijo para un número que fuerza la versión hablada del número en inglés. Por ejemplo, MMSP aplicado sobre junio muestra 'six' (seis en inglés). SPTH Combinación de los sufijos SP y TH. THSP Combinación de los sufijos TH y SP.

Además, podemos insertar texto literal en el formato encapsulándolo entre comillas dobles. Por ejemplo, TO_CHAR(SYSDATE,'("YYYY") YYYY') puede producir '(YYYY) 2011'. La función «NEW_TIME» para cambiar zonas horarias. La función NEW_TIME nos da la fecha y hora para otra zona horaria a la actual. El formato de esta función es el siguiente: NEW_TIME(fecha, zonaHorariaActaul, otraZonaHoraria)

El primer argumento es una fecha (y hora) de la zona horaria especificada en el segundo argumento. La fecha Oracle /47

será retornada para la zona horaria especificada en el tercer argumento. Los argumentos de zonas horarias usan una abreviatura de tres letras. Esta función puede ser útil para comparar fechas de distintas zonas horarias. Por ejemplo, para comparar un campo fecha de una tabla Amigo de la zona de Europa del Este con la zona de Hawái, podemos usar la siguiente consulta: SELECT fecha, NEW_TIME(fecha, 'EST', 'HST') FROM Amigo;

Cálculos con «TO_DATE». TO_DATE sigue las mismas convenciones de formato que TO_CHAR, con algunas restricciones. El propósito de TO_DATE es convertir un string, como 'MAY 20, 1949', en un valor de tipo DATE. Esto permite que las fechas sean usadas para cálculos. Si queremos convertir un valor como '22-MAR-04' tenemos que usar la expresión TO_DATE('22-MAR-04','DD-MON-YY'). Si omitimos el argumento de formato, el primer argumento tiene que expresar una fecha en uno de los formatos estándar para fechas (separando la fecha con guiones o barras). Supongamos que obtenemos el día de mes en un campo de base de datos expresado como un número y queremos mostrarlo en su forma de nombre completo. La función TO_CHAR permite hacer esto sobre una fecha completa, pero no sobre un número. Por tanto, podemos convertir previamente el número de mes a fecha con la función TO_CHAR. La consulta sería como sigue: SELECT TO_CHAR( TO_DATE( numeroMes , 'MM') , 'MONTH' ) FROM Tabla; este ejemplo, la función TO_DATE convierte previamente un número en una

En fecha que incluye el día 1, el mes que casa con el número y al año actual. También podemos compactar una fecha en un único número del estilo 23022011. Podemos convertir este número en una fecha mediante la expresión TO_DATE(23022011,'DDMMYYYY'), que devuelva la fecha con valor '23/2/2011'. Como regla general, la función TO_DATE fallará si no seguimos las siguientes normas: • No se permiten literales en el string a convertir. Por ejemplo 'La fecha es 12/1/2000' no es válido. • Los días no pueden ser expresados en su forma hablada. Siempre deben ser números. • Se permiten los signos de puntuación. • El formato fm no es necesario. Si se usa, se ignora. • Si se usa Month, el mes en el string debe estar en su forma hablada. Si se usa Mon, el mes debe expresarse con una abreviatura de tres letras. Las mayúsculas y minúsculas serán ignoradas. Cómo tratar con varios siglos. Si nuestra aplicación usa sólo dos dígitos para los años, podemos encontrarnos con problemas relacionados con el año 2000. Si especificamos '98' para el año '1998', e insertamos la fecha en una base de datos después del año 2000, podemos tener problemas con el valor de siglo asignado a la fecha, puesto que se le asignará el siglo 21. Es por ello importante especificar siempre los años con cuatro dígitos cuando insertemos datos. Usando la función «EXTRACT». Podemos usar la función EXTRACT en lugar de TO_CHAR para seleccionar parte de una fecha. La sintaxis de esta función es la siguiente: EXTRACT( parte FROM fecha ) Donde parte puede ser uno de los siguiente valores: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, TIMEZONE_HOUR, TIMEZONE_MINUTE, TIMEZONE_REGION o TIMEZONE_ABBR.

Por ejemplo, si queremos obtener el año actual podemos usar la expresión EXTRACT(YEAR FROM SYSDATE). Usando el tipo de dato «TIMESTAMP». El tipo de dato DATE almacena la fecha y hora hasta segundos, el tipo TIMESTAMP almacena la fecha hasta billones de segundo. Por defecto, la precisión para fracciones de segundo en un TIMESTAMP es de 6, pero puede llegarse hasta 9 decimales. Al definir una columna de tipo TIMESTAMP podemos establecer esta precisión de la siguiente forma: CREATE TABLE X1 (TSCOLTIMESTAMP(5));

Si queremos que los valores insertados especifiquen la zona horaria podemos crear la columna con la opción WITH TIME ZONE de la siguiente forma: CREATE TABLE X1 (TSCOLTIMESTAMP(5) WITH TIME ZONE);

Oracle también soporta el tipo de dato TIMESTAMP(precisión) WITH LOCAL TIMEZONE, que permite normalizar los valores a la zona horaria donde es almacenada la base de datos. También podemos usar la función SYSTIMESTAMP para obtener la fecha y horas actuales como un tipo Oracle /48

TIMESTAMP.

Además del tipo TIMESTAMP, Oracle soporta dos tipos de datos para intervalos:

INTERVALYEAR(precisión_del_año) TO MONTH INTERVAL DAY (precisión_del_día) TO SECOND (precisión_fracción_segundos) tipo INTERVAL YEAR TO MONTH almacena un periodo de tiempo en años y meses,

El donde la precisión es el número de dígitos del año (siendo por defecto 2). El tipo INTERVAL DAY TO SECOND almacena un periodo de tiempo de días, horas, minutos y segundos; la precisión para el día y segundos acepta valores entre 0 a 9. Los tipos INTERVAL son normalmente usados en análisis estadísticos de datos. 2.3.6. Funciones que retornan valores del sistema. USER UID

USERENV('atributo')

SYS_CONTEXT('namespace', 'atributo' [, len])

Pseudo-columna de SQL*Plus que devuelve un valor VARCHAR2 que contiene el nombre de usuario actual de Oracle. Esta función retorna el ID de la se sesión. El ID de usuario es un identificador único para cada usuario en una base de datos y puede seleccionarse de la vista DBA_USERS. Devuelve un valor VARCHAR2 que contiene información acerca de la sesión actual, según la opción seleccionada. Las opciones son: CLIENT_INFO, para obtener al usuario actual de la sesión. ENTRYID, para obtener el identificador de entrada. INSTANCE, para obtener el número de identificador de la instancia actual. ISDBA, para determinar si el usuario actual tiene privilegios DBA. LANG, para obtener una abreviatura del lenguaje. LANGUAGE, para obtener el lenguaje, territorio y caracteres de la sesión. SESSIONID, para obtener el identificador de sesión. TERMINAL, para obtener el identificador del sistema operativo. Esta función se considera obsoleta y se recomienda SYS_CONTEXT. Esta función permite recuperar información (como un VARCHAR2) asociada a un atributo de un espacio de nombres de la sesión actual. Por ejemplo, el espacio de nombres relacionado con el usuario actual es USERENV, el cuál describe nuestra sesión. Existen una gran diversidad de parámetros para el espacio de nombres USERENV, entre ellos: CURRENT_SCHEMA, CURRENT_SCHEMAID, HOST, INSTANCE, INSTANCE_NAME, IP_ADDRESS, ISDBA, LANG, LANGUAGE, NETWORK_PROTOCOL, NLS_CALENDAR, NLS_CURRENCY, NLS_DATE_FORMAT, NLS_SORT, NLS_DATE_LANGUAGE, NLS_TERRITORY, OS_USER, SERVER_HOST, SERVICE_NAME, SESSION_USER, SESSION_USERID, SESSIONID, SID, TERMINAL.

Oracle permite crear espacios de nombres en el contexto actual que permiten almacenar información asociada a un nombre de atributo. Se usa el comando CREATE CONTEXT para: • Crear un espacio de nombres para un contexto. • Asociar el espacio de nombres con un paquete creado externamente, el cual asigna el contexto. Podemos usar el procedimiento DBMS_SESSION.SET_CONTEXT dentro del paquete para asignar o reasignar los atributos del contexto. Para crear un espacio de nombres de contexto debemos tener el permiso del sistema CREATE ANY CONTEXT. El siguiente comando crea un espacio de nombres llamado Mi_Contexto asociado a un paquete llamado Paquete_Contexto: CREATE OR REPLACE CONTEXT Mi_Contexto USING Paquete_Contexto;

Podemos añadir un atributo (usuario) al espacio de nombres con el siguiente comando: Y

DBMS_SESSION.SET_CONTEXT('MiContexto', 'usuario', 'Pedro'); podemos recuperar el valor del atributo como un VARCHAR2 con:

Oracle /49

SYS_CONTEXT('Mi_Contexto', 'usuario')

2.3.7. Funciones de conversión. ASCIISTR BIN_TO_NUM CAST CHARTOROWID COMPOSE CONVERT DECODE DECOMPOSE HEXTORAW NUMTODSINTERVAL NUMTOYMINTERVAL RAWTOHEX RAWTONHEX ROWIDTOCHAR ROWIDTONCHAR SCN_TO_TIMESTAMP TIMESTAMP_TO_SCN TO_BINARY_DOUBLE TO_BINARY_FLOAT TO_CHAR TO_CLOB TO_DATE TO_DSINTERVAL TO_LOB TO_MULTI_BYTE TO_NCHAR TO_NCLOB TO_NUMBER TO_SINGLE_BYTE TO_TIMESTAMP TO_TIMESTAMP_TZ TO_YMINTERVAL TRANSLATE UNISTR

Traslada un string a algún juego de caracteres y retorna un string ASCII en el juego de caracteres de la base de datos. Convierte valores binarios en su equivalente numérico. Moldea un tipo predefinido o de colección a otro. Se usa normalmente con tablas anidadas y arrays variables. Cambia un string de caracteres para que actúen como un identificador de fila interno de Oracle, o RowID. Traslada un string de algún tipo de dato o un string Unicode a una forma completa normalizada. Convierte una cadena de caracteres desde un lenguaje nacional a otro. Selecciona un valor CHAR, VARCHAR2 o NUMBER de una lista según un valor. Traslada un string de algún tipo a un string Unicode después de su descomposición canónica en el mismo juego de caracteres. Cambia una cadena de caracteres de números hexadecimales en un valor binario. Convierte un número a un literal INTERVAL DAY TO SECOND. Convierte un número a un literal INTERVAL YEAR TO MONTH. Cambia un string de números binarios a un string de caracteres de números hexadecimales. Convierte un raw a un valor NVARCHAR2 que contiene su equivalente hexadecimal. Cambia un identificador de fila interno de Oracle, o RowID, a un string. Convierte un valor RowID a un valor NVARCHAR2. Convierte un número de cambio de sistema a su aproximado TIMESTAMP. Convierte un TIMESTAMP a número de cambio de sistema aproximado. Cambia un valor binario a un número de doble precisión con coma flotante. Cambia un valor binario a un número de simple precisión con coma flotante. Convierte un NUMBER o DATE a un string. Convierte un valor NCLOB a un valor CLOB. Convierte un NUMBER, CHAR o VARCHAR2a DATE. Convierte un string del tipo CHAR, VARCHAR2, NCHAR oNVARCHAR2a un valor INTERVAL DAY TO SECOND. Convierte un LONG a un LOB como parte de una inserción o selección. Convierte caracteres de un byte en un string de caracteres de varios bytes. Convierte un string, NUMBER o DATE a al formato nacional. Convierte valores CLOB a valores NCLOB. Convierte un CHAR o VARCHAR2 a número. Convierte un CHAR o VARCHAR2a bytes simples. Convierte un string a un valor TIMESTAMP. Convierte un string a un valor TIMESTAMP WITH TIME ZONE. Convierte un string del tipo CHAR, VARCHAR2, NCHAR o NVARCHAR2 a un valor INTERVAL YEAR TO MONTH. Traslada caracteres en un string dentro de diferentes caracteres. Convierte un string a Unicode.

Las funciones más usadas para conversión de datos son las siguientes: • TO_CHAR. Transforma un DATE o NUMBER en un string. • TO_DATE. Transforma un NUMBER, CHAR o VARCHAR2 en un DATE. Para trabajar con valores timestamp podemos usar TO_TIMESTAMP o TO_TIMESTAMP_TZ. • TO_NUMBER. Transforma un CHAR o VARCHAR2 en un NUMBER. Se indica el formato de la conversión utilizando estos símbolos: 9 (posición del número), 0 (posición del número mostrando ceros), $ (formato dólar), L (símbolo local de la moneda), S (hace que aparezca el símbolo del signo), D (posición del símbolo decimal, la coma), G (posición del separador de grupo, el punto). Conversión automática de tipos de datos. Oracle es capaz de convertir datos automáticamente a fin de que la expresión final tenga sentido. En ese Oracle /50

sentido son fáciles las conversiones de texto a número y viceversa. Por ejemplo: SELECT 5 + '3' FROM DUAL SELECT 5 || '3' FROM DUAL

-- El resultado es 8 -- El resultado es 53

También ocurre esto con la conversión de textos a fechas, de hecho es la forma habitual de asignar fechas. Las reglas básicas de conversión son las siguientes: • Cualquier NUMBER o DATE puede convertirse a un string. Cualquier función de cadenas puede usarse sobre una columna de tipo NUMBER o DATE. • Un valor CHAR o VARCHAR2 serán convertidos a un NUMBER si contiene un valor numérico válido. • Un valor CHAR o VARCHAR2 será convertido a un DATE sólo si contiene una fecha con un formato por defecto. Esto es válido para todas las funciones excepto para GREATEST y LEAST, las cuales tratan los valores como strings. Funciones especializadas de conversión. Oracle incluye varias funciones de conversión especializadas. Si esperamos usar SQL*Plus y Oracle simplemente para generar informes, probablemente no necesitaremos usar estas funciones. Las funciones de conversión generalmente toman un valor simple como entrada y retornan un valor simple convertido como salida. Por ejemplo, la función BIN_TO_NUM convierte valores binarios a un valor numérico decimal. Los valores de entrada son una lista de dígitos como valores binarios separados por comas. Así, BIN_TO_NUM(1,1,1,0) produce la salida 14 (=1110 en base 2). Cuando trabajamos con operaciones de recuperación flashback, podemos convertir números de cambio del sistema (SCN's) a valores timestamp mediante la función SCN_TO_TIMESTAMP; TIMESTAMP_TO_SCN retorna el SCN para un timestamp dado. Desde Oracle Database 10g, podemos usar las funciones TO_BINARY_DOUBLE y TO_BINARY_FLOAT para convertir valores entre doble y simple precisión respectivamente. Funciones de transformación. Hay dos funciones especiales que podemos usar para controlar la conversión según el tipo de entrada, en vez de simplemente realizar una transformación. Estas funciones son TRANSLATE y DECODE. TRANSLATE es una función simple que realiza una sustitución ordenada caracter a caracter dentro de un string. El formato de esta función es el siguiente: TRANSLATE( string , if, then) TRANSLATE mira cada caracter dentro

del string y comprueba el segundo argumento para ver si este caracter está también dentro del segundo argumento. Si lo encuentra guarda su posición en el segundo argumento y busca en el tercer argumento el caracter situado en la misma posición y lo envía como parte de la salida de la función. Si no encuentra el caracter del string en el segundo argumento se envía este caracter como parte de la salida de la función. Por ejemplo, la siguiente consulta SELECT

TRANSLATE(7671234,

234567890, 'BCDEFGHIJ') FROM DUAL

produce la salida 'GFG1BCD'. Aunque TRANSLATE es técnicamente una función de cadenas, podemos ver que convierte automáticamente los datos y trabaja con strings y números. Por su parte, la función DECODE puede ser considerada como una función de sustitución valor por valor. Es similar a una estructura IF-THEN-ELSE para seleccionar un valor. Su sintaxis es: DECODE ( expresiónBase, comparando1, valor1, comparando2, valor2, ... [, valorPorDefecto] )

El primer argumento es una expresión base que se compara con otras. A continuación, cada dos argumentos definen una expresión de comparación y un valor. Si la expresión base coincide con la expresión de comparación la función retorna el valor asociado. Si no coincide ninguna comparación la función retorna el último argumento (que es opcional) con un valor por defecto. Todos los posibles valores devueltos deben ser del mismo tipo o convertibles al mismo tipo. Por ejemplo, la siguiente consulta retorna el valor 2: SELECT DECODE('abc' , 'a' , 1 , 'abc', 2 , 3) FROM DUAL;

2.3.8. Funciones de selección. Oracle proporciona una estructura y varias funciones para seleccionar valores basándose en una o varias condiciones. Las funciones de selección como DECODE, GREATEST o LEAST ya han sido vistas previamente. La estructura «CASE». La estructura CASE no se trata de una estructura de control, sino de una instrucción que evalúa una expresión booleana y retorna un valor (que podemos asignar a una variable o usar en la lista de campos de un SELECT). Oracle /51

Admite dos sintaxis:

CASE expresión WHEN valor_expresion1 THEN valor_devuelto1 WHEN valor_expresion2 THEN valor_devuelto2 ELSE valor_devuelto_por_defecto END CASE WHEN valor operador1 expresion1 THEN valor_devuelto1 WHEN valor operador2 expresion2 THEN valor_devuelto2 ELSE valor_devuelto_por_defecto END

Todos los posibles valores devueltos deben ser del mismo tipo. Por ejemplo, la siguiente consulta retorna el valor 1 si la fecha actual es mayor que el '1/1/2010': SELECT CASE WHEN SYSDATE >'1/1/2010' THEN 1 ELSE 2 END FROM DUAL;

3. Trabajando con objetos 3.1. Introducción. Según los estándares actuales, una base de datos de Oracle es un conjunto de objetos pensados para gestionar datos. Estos objetos están contenidos en esquemas, y los esquemas están asociados al perfil de un usuario concreto. En el estándar SQL-Oracle existe el concepto de catálogo, que sirve para almacenar esquemas. Así, el nombre completo de un objeto vendría dado por: catálogo.esquema.objeto

Si no se indica el catálogo, se toma el catálogo por defecto. Si no se indica el esquema, se entiende que el objeto está en el esquema actual. Básicamente, existen dos tipos de comandos SQL: • Los DLL, que permiten crear y definir los objetos de bases de datos: tablas, campos e índices. CREATE Utilizado para crear nuevas tablas, campos e índices DROP Empleado para eliminar tablas e índices ALTER Utilizado para modificar las tablas agregando campos o cambiando la definición de los campos. • Los DML, que permiten generar consultas para ordenar, filtrar y extraer datos de la base de datos. SELECT Utilizado para consultar registros de la base de datos que satisfagan un criterio determinado INSERT Utilizado para cargar lotes de datos en la base de datos en una única operación. UPDATE Utilizado para modificar los valores de los campos y registros especificados DELETE Utilizado para eliminar registros de una tabla de una base de datos 3.2. Diccionario de datos de Oracle. El diccionario de datos (DD) es una parte fundamental de las bases de datos Oracle. Está formado por tablas, vistas y paquetes a los que se puede acceder para obtener información. Las tablas se crean automáticamente durante la instalación y permiten saber: - La estructura lógica y física de la base de datos. - Los usuarios de la base de datos. - Las restricciones de integridad sobre las tablas de la base de datos. - El espacio asociado a cada objeto en la base de datos y la cantidad que se está utilizando por los distintos objetos creados por los usuarios de la base de datos. El usuario SYS es el dueño del DD y tiene todos los permisos sobre cualquier objeto de la base de datos (también los de cualquier usuario). Componentes del DD son:  Tablas base: Una serie de tablas a las que el servidor de datos accede cada vez que se procesa una instrucción DDL de SQL o en algunos comandos DML.  Vistas estáticas: Decodifican y resumen la información contenida en las tablas base. Durante la creación de estas vistas se generan sinónimos públicos para proveer el acceso a los usuarios de la base de datos. Estas vistas deben ser utilizadas para las labores de administración rutinarias que necesiten información específica sobre configuración y estado de la base de datos. Tienen el nombre de estáticas porque no mantienen información relacionada con las sesiones. Se dividen en 3 categorías: Oracle /52

• Vistas con prefijo USER_: Puede utilizarlas cualquier usuario de la base de datos y se refieren a objetos poseídos por dicho usuario. Por ejemplo: SELECT * FROM USER_TABLES;

Muestra toda la información de las tablas del usuario actual. • Vistas con prefijo ALL_: Evidente, las podrá usar cualquier usuario y además añaden la columna OWNER al resto de información. Con estas vistas se puede tener acceso a la información de los objetos de los cuales el usuario es dueño además de los objetos públicos y a los que el usuario tiene acceso (por pertenecer a un grupo de seguridad o poseer ciertos privilegios). • Vistas con prefijo DBA_: Dan información sobre todos los objetos de la base de datos. Usualmente también tienen la columna OWNER. Sólo las puede utilizar el administrador o usuarios con privilegio "SELECT ANY TABLE" o pertenezca a un rol que incluya el privilegio.  Vistas dinámicas (o performance views): Incluyen información sobre las condiciones actuales de operación en la base de datos. La mayor parte son creadas durante la instalación y algunas se crean específicamente para monitorear cierta actividad. Todas se identifican por el prefijo V$. Por ejemplo, la vista dinámica V$_SESSION incluye información sobre las sesiones actuales y la vista V$SYSSTAT provee información estadística sobre el uso de la base de datos. Para obtener información general sobre las vistas del diccionario de datos se podría utilizar esta consulta: SELECT * FROM Dictionary WHERE table_name LIKE '%indicador%';

Por ejemplo, para ver todas las vistas relacionadas con tablas podríamos ejecutar:

SELECT * FROM Dictionary WHERE table_name LIKE '%TABLE%'; Algunas vistas con el prefijo USER_ (pueden verse en ALL_VIEWS) son: – USER_OBJECTS: Lista de todos los objetos pertenecientes al usuario

(tablas, vistas, paquetes, índices, triggers, sinónimos...). – USER_TABLES: Lista de todas las tablas del usuario. – USER_VIEWS: Vistas del usuario. – USER_USERS: Diversos datos sobre el usuario. – USER_UPDATABLE_COLUMNS: Columnas que pueden ser modificadas. – USER_JOBS: Tareas pertenecientes al usuario. – USER_TRIGGERS: Disparadores (triggers) del usuario. – USER_SYNONYMS: Sinónimos pertenecientes al usuario. – USER_INDEXES: Índices pertenecientes al usuario. – USER_CONSTRAINTS: Restricciones pertenecientes al usuario. – USER_TAB_PRIVS: Permisos sobre objetos con el usuario involucrado. Si se pone _COL_ en vez de _TAB_ se refiere a las columnas. Se puede distinguir entre: • USER_TAB_PRIVS_MADE: Permisos sobre los objetos del usuario. • USER_TAB_PRIVS_RECD: Permisos recibidos por el usuario. – USER_TAB_COLUMNS: Descripciones de las columnas del usuario. – USER_TAB_COMMENTS y USER_COL_COMMENTS: Comentarios sobre las tablas y columnas del usuario, si se han insertado con el comando COMMENT: COMMENT ON [TABLE|COLUMN] [.] IS '';

3.3. Crear y usar bases de datos. Para crear una base de datos debemos usar la siguiente instrucción: CREATE DATABASE MiBaseDeDatos;

Una vez creada la base de datos, debemos indicar explícitamente que queremos trabajar con ella: USE MiBaseDeDatos;

Para eliminar una base de datos existente debemos usar: DROP DATABASE MiBaseDeDatos;

3.4. Crear y usar tablespaces. Nos podemos encontrar con los siguientes tipos de tablespaces: ▪ El tablespace SYSTEM. Es el único que se crea con la base de datos (CREATE DATABASE). Este tablespace contiene: el Diccionario de Datos (incluidos los procedimientos almacenados), y el segmento de «rollback» del sistema. No debe usarse para contener datos de aplicaciones. ▪ Tablespaces TEMPORALES. Son aquellos en los que solamente puede haber objetos temporales. No se pueden crear objetos permanentes como pueden ser los índices, las tablas o los segmentos de «rollback». Oracle /53

Se utilizan para optimizar operaciones de ordenación. ▪ De tipo deshacer cambios (desde Oracle 9i). Se utilizan para gestionar poder deshacer las transacciones incompletas. ▪ Con tamaño de bloque variable (desde Oracle 9i). ▪ De tipo BigFile (desde Oracle 10g). 3.4.1. Estado del tablespace. Su estado puede ser ONLINE u OFFLINE. Existe una vista que nos da información sobre los tablespaces de nuestra base de datos. Esta vista es la siguiente: SELECT Tablespace_Name, Status FROM DBA_TABLESPACES;

Para poder realizar una copia de seguridad del tablespace, estando completamente seguros de que nadie está modificando los objetos del tablespace, es necesario establecer el modo OFFLINE. Así mismo, se actuará de igual forma para poder actualizar una aplicación que se basa en los objetos de este tablespace sin que ningún usuario pueda modificar los datos en medio de la actualización. 3.4.2. Para crear un tablespace: La sintaxis general para crear un tablespaces es: CREATE [UNDO] TABLESPACE nombre_tablespace DATAFILE Opciones_Datafile Opciones_Almacenamiento ; Donde Opciones_Datafile tiene la sintaxis: 'nombre fichero' [AUTOEXTEND OFF] 'nombre fichero' [AUTOEXTEND ON [NEXT int K | M] [MAXSIZE int K | M]] La opción AUTOEXTEND MAXSIZE es por defecto UNLIMITED si no se especifica valor. Donde Opciones_Almacenamiento tiene la sintaxis: DEFAULT [COMPRESS|NOCOMPRESS] STORAGE storage_clause MINIMUM EXTENT int {K|M} BLOCKSIZE int K LOGGING | NOLOGGING FORCE LOGGING ONLINE | OFFLINE PERMANENT | TEMPORARY EXTENT MANAGEMENT {DICTIONARY | LOCAL {AUTOALLOCATE | UNIFORM [SIZE int K | M]} } SEGMENT SPACE MANAGEMENT {MANUAL | AUTO}

3.4.3. Para aumentar el tamaño del tablespace: Se utiliza la instrucción ALTER DATABASE TABLESPACE, como por ejemplo:

ALTER DATABASE TABLESPACE prueba ADD DATAFILE 'c:\oracleexe\oradata\XE\prueba02.dbf'SIZE 50M; / ALTER DATABASE DATAFILE'/users/oradata/orcl/prueba01.dbf'RESIZE 150M;

3.4.4. Para borrar un tablespace: Se utiliza la instrucción DROP TABLESPACE nombre_tablespace; 3.4.5. Tablespaces temporales: Para crear un tablespace temporal simplemente hay que añadir la palabra TEMPORARY a la instrucción utilizada para crear tablespaces normales. CREATE TABLESPACE prueba DATAFILE '/users/oradata/orcl/prueba01.dbf' SIZE 100M TEMPORARY; / ALTER USER nombre_de_usuario TEMPORARY TABLESPACE nombre_de_tablespace; / SELECT username, temporary_tablespace FROM dba_users; / SELECT tablespace_name, contents FROM dba_tablespaces;

3.4.6. Tablespaces read-only (de solo lectura): Los tablespace de solo lectura permiten consultar los datos de los objetos, pero no se puede ni borrar ni insertar nada en ellos. La principal ventaja de un tablespace read-only es que no hace falta hacer un backup del mismo. Ejemplo de los tablespaces read-only: SQL>ALTER TABLESPACE DataCursoxy READ ONLY; Tablespace modificado. SQL>INSERT INTO tabla01 VALUES ('PRIMERA FILA');

Oracle /54

ORA-00372: el fichero 3 no puede ser modificado en este momento ORA-01110: fichero de datos 3: '/u02/oradata/CURSOxy/datacursoxy01.dbf' SQL> DROP TABLE TABLA01; Tabla borrada. SQL> alter tablespace DataCursoxy READ WRITE; Tablespace modificado. SQL>INSERT INTO tabla02 VALUES ('PRIMERA FILA'); 1 fila creada. SQL>COMMIT; Validación terminada.

3.4.7. Tablespace de Undo (deshacer): Podemos tener varios tablespaces de "undo", pero sólo uno de ellos estará activo. No se pueden crear objetos sobre un tablespace de "undo". Al cambiar de tablespace "undo" activo (con UNDO TABLESPACE), los segmentos de «rollback» que contiene el nuevo tablespace pasan a estar ONLINE, mientras que los del tablespace anterior se ponen OFFLINE. Se crean de dos formas: - Mediante CREATE DATABASE. - Mediante CREATE TABLESPACE: CREATE UNDO TABLESPACE undotbs02 DATAFILE 'c:\oraclexe\oradata\ex\undo02.dbf'SIZE 25M REUSE AUTOEXTEND ON;

Parámetros de inicialización de los espacios de tablas de deshacer: - Undo_Management (valores MANUAL/AUTO). Si AUTO se gestionará de forma automática el espacio de deshacer. No es dinámico, cuando se cambia de estado se debe re arrancar la instancia. - Undo_tablespace (MANUAL/AUTO). Se usa en entornos RAC (Real Application Clusters). 3.5. Crear esquemas. Cada esquema está asociado a un usuario de Oracle. Por tanto, para crear un esquema debemos crear primero el usuario, y también los tablespaces dónde ubicar los objetos que creemos dentro del esquema (aunque se pueden utilizar tablespaces ya existentes). Para poder realizar estos pasos es necesario iniciar la sesión en la base de datos con un usuario con permisos de administración, lo más sencillo es utilizar directamente el usuario SYSTEM. Para crear un tablespace para datos y otro para índices, usaremos CREATE TABLESPACE "APPDAT" LOGGING DATAFILE '/export/home/oracle/oradata/datafiles/APPDAT.dbf' SIZE 1024M EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO;

para los datos, con tamaño inicial de 1024 Mb, y auto extensible; y usaremos

CREATE TABLESPACE "APPIDX" LOGGING DATAFILE '/export/home/oracle/oradata/datafiles/APPIDX.dbf' SIZE 512M EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO;

para los índices, con tamaño inicial de 512 Mb, y auto extensible. Para crear el usuario que va a trabajar sobre estos tablespaces, y que será el propietario de los objetos que se creen en ellos usaremos CREATE USER "APP" PROFILE "DEFAULT" IDENTIFIED BY "APPPWD" DEFAULT TABLESPACE "APPDAT" TEMPORARY TABLESPACE "TEMP" ACCOUNT UNLOCK; no se especifica un tablespace, la BD le asignará el tablespace USERS, que es el tablespace

Si que se utiliza por defecto para los nuevos usuarios. Se puede apreciar también que no hay ninguna referencia al tablespace de índices APPIDX que hemos creado. Si queremos mantener datos e índices separados habrá que acordarse de especificar este tablespace en las sentencias de creación de índices de este usuario, sino se crearán en APPDAT: CREATE INDEX mi_indice ON mi_tabla(mi_campo) TABLESPACE APPIDX;

Sólo falta asignar al usuario los permisos necesarios para trabajar. Si se le asignan los roles "Connect" y "Resource" ya tiene los permisos mínimos, podrá conectarse y podrá realizar las operaciones más habituales de consulta, modificación y creación de objetos en su propio esquema. GRANT "CONNECT" TO "APP"; GRANT "RESOURCE" TO "APP";

En el momento que el usuario crea el primer objeto en la BD, se crea automáticamente el esquema. Oracle /55

3.6. Crear y usar tablas. 3.6.1. Crear el esquema de una tabla. Para crear una tabla se usa la siguiente instrucción SQL:

CREATE TABLE tabla ( campo1 tipo (tamaño) restricciones1 , ..., campoN tipo (tamaño) restriccionesN , CONSTRAINT nombre_restricción tipo_restricción (columnas) ... ); donde tabla es el nombre de la tabla que se va a crear; campo1...campoN

En son el nombre de los campos que se van a crear en la nueva tabla (la nueva tabla debe contener, al menos, un campo); tipo es el tipo de datos de cada campo; tamaño es el tamaño del campo según su tipo; restricciones1... restriccionesN son cláusulas de restricción opcionales que restringen el comportamiento del campo; CONSTRAINT define restricciones con nombre propio (véase la siguiente sección). Como ejemplo crearemos una tabla para almacenar empleados:

CREATE TABLE Empleado ( ID INTEGER PRIMARY KEY, Nombre VARCHAR2(25)NOT NULL, Apellidos VARCHAR2(50) NOT NULL, Fecha_Nacimiento DATE, Categoria VARCHAR2(1) DEFAULT 'A'); Se crea una nueva tabla llamada Empleado con un campo Nombre de tipo texto y longitud 25 y otro llamado Apellidos con longitud 50 que no admiten valores nulos; crea otro campo llamado Fecha_Nacimiento de tipo DATE, y el campo Categoria admite un único caracter cuyo valor por defecto es 'A'; y crea el campo ID de tipo

entero, que se establece como clave principal. 3.6.2. Crear una tabla a partir de otra. Oracle permite crear una nueva tabla al aire, basándose en una consulta sobre una tabla existente. Por ejemplo, el siguiente comando crea una versión simplificada de la tabla Empleado. CREATE TABLE Empleado_2 AS SELECT Apellidos, Categoria FROM Empleado;

La única restricción de este comando es que no trabaja con consultas que devuelven columnas del tipo LONG. Si se describe la nueva tabla (con el comando DESCRIBE), se revela que hereda la definición de sus columnas de la tabla Empleado. La nueva tabla creada será poblada con los datos procedentes de la consulta. Si deseamos filtrar los registros que se deben insertar en la nueva tabla podemos especificar condiciones con la cláusula WHERE. El siguiente comando crea una nueva tabla con columnas para los apellidos, categoría y año de nacimiento de aquellos empleados nacidos antes de 1980: CREATE TABLE Empleado_3 AS SELECT Apellidos, Categoria, EXTRACT(Year from Fecha_Nacimiento) AS AñoNacimiento FROM Empleado WHERE EXTRACT(Year from Fecha_Nacimiento) < 1980;

Si queremos crear la nueva tabla sin insertarle ningún registro basta con especificar una condición que no cumpla ninguno de los registros de la consulta. Esta forma de crear una nueva tabla permite utilizar consultas que devuelven columnas nuevas, bien como producto de funciones o por la combinación de otras columnas. Las columnas basadas en caracteres se ajustarán al tamaño necesario para contener los datos de las columnas nuevas. Las columnas numéricas que proceden de la computación de columnas de la tabla original que especifican una precisión, se simplifican al tipo NUMBER, sin especificar la precisión, en la nueva tabla. Podemos crear la nueva tabla sin generar entradas de registro de deshacer (registros cronológicos de acciones de base de datos usadas durante la recuperación de la base de datos). Se evita la generación de estas entradas usando la palabra clave NOLOGGING en el comando CREATE TABLE. Haciendo esto se aumenta el rendimiento del comando porque tendrá menos trabajo qué hacer. Sin embargo, haciendo esto la tabla no podrá ser recreada si, después de un fallo de la base de datos, se utilizan los archivos de deshacer para recuperar la base de datos. El siguiente ejemplo muestra cómo usar la palabra clave NOLOGGING. CREATE TABLE Empleado_2 NOLOGGING AS

Oracle /56

SELECT * FROM Empleado;

3.6.3. Eliminar una tabla. Para eliminar una tabla se usa la instrucción: DROP TABLE nombreTabla ;

3.6.4. Cambiar de nombre. La orden RENAME permite el cambio de nombre de cualquier objeto. Sintaxis: RENAME nombreViejo TO nombreNuevo ;

3.6.5. Borrar el contenido de tablas. La orden TRUNCATE TABLE seguida del nombre de una tabla, hace que se elimine el contenido de la tabla, pero no la tabla en sí. Incluso borra del archivo de datos el espacio ocupado por la tabla. TRUNCATE TABLE nombreTabla ;

3.6.6. Modificar el esquema de una tabla. Una vez creado el esquema de una tabla podemos modificarlo con la instrucción ALTER TABLE. Para añadir un nuevo campo a una tabla creada: ALTER TABLE Empleado ADD Tipo VARCHAR(20) NULL;

Para cambiar el tipo de datos y propiedades de una determinada columna: ALTER TABLE Empleado MODIFY (Tipo INT);

Para eliminar un campo existente de una tabla:

ALTER TABLE Empleado DROP COLUMN Tipo;

Reglas para añadir o modificar una columna. Las reglas para añadir una columna a una tabla son las siguientes: • Podemos añadir una columna directamente si no se ha especificado NOT NULL sobre ella. • Podemos añadir una columna NOT NULL en tres pasos: 1. Añadir la columna sin especificar NOT NULL. 2. Llenar esta columna con datos en cada fila. 3. Modificar la columna para ser NOT NULL. Nota. Si usamos la cláusula DEFAULT cuando añadimos una columna, Oracle actualizará cada registro con el valor por defecto cuando se añada la columna. Las reglas para modificar una columna son las siguientes: • Podemos incrementar el tamaño de una columna de caracteres. • Podemos incrementar el tamaño de una columna numérica. • Podemos incrementar o decrementar el número de lugares decimales de una columna numérica. • Podemos pasar de CHAR a VARCHAR2 y viceversa (si no se modifica el tamaño). • Podemos pasar de DATE a TIMESTAMP y viceversa. Además, si una columna tiene el valor NULL en cada registro de la tabla, podemos hacer los siguientes cambios: • Podemos cambiar el tipo de dato de la columna. • Podemos decrementar el tamaño de una columna de caracteres. • Podemos decrementar el número de dígitos de una columna numérica. Hay una notable excepción en las restricciones de cambio de tipo de dato. Oracle soporta el cambio de columnas LONG a un tipo LOB, incluso si hay datos en la columna LONG. Otros modos de eliminar columnas. Podemos eliminar varias columnas de una tabla en un único comando. Por ejemplo, el siguiente comando elimina los campos Nombre y Apellidos de la tabla Empleado: ALTER TABLE Empleado DROP (Nombre, Apellidos);

Si eliminamos columnas que son parte de la clave primaria o tienen la restricción UNIQUE, necesitaremos también usar la cláusula CASCADE CONSTRAINTS al final del comando ALTER TABLE. Cuando se elimina una columna que es la clave primaria, también se elimina su índice. Eliminar una columna es más complejo internamente que añadir o modificar una columna, ya que requiere un trabajo adicional por parte de Oracle. Quitar la columna de la lista de columnas de la tabla es más fácil. Quitar una columna hace que se oculte cuando se consulta la tabla, pero no modifica el espacio actual reservado físicamente para ella. Podemos ocultar una columna inmediatamente marcándola como UNUSED, y eliminarla permanentemente más tarde, cuando el impacto de rendimiento por tener que redimensionar todos los registros de la tabla no afecte a nuestro trabajo con la base de datos. Oracle /57

En el siguiente ejemplo se oculta una columna de la tabla Empleado:

ALTER TABLE Empleado SET UNUSED COLUMN Fecha_Nacimiento;

Al marcar la columna como "unused" no liberamos el espacio previamente asignado para cada valor de esta columna en cada registro, hasta que eliminamos las columnas no usadas de la siguiente forma: ALTER TABLE Empleado DROP UNUSED COLUMNS;

Para ver todas las tablas con columnas marcadas como no usadas, podemos consultar las vistas USER_UNUSED_COL_TABS, ALL_UNUSED_COL_TABS y DBA_UNUSED_COL_TABS. 3.6.7. Obtener el esquema de una tabla. El comando DESCRIBE permite obtener la estructura de una tabla. Ejemplo: DESCRIBE Empleado;

Y aparecerán los campos de la tabla Empleado, de forma parecida a la siguiente: Nombre ------------------------ID NOMBRE APELLIDOS FECHA_NACIMIENTO CATEGORIA

Null? -------–-----NOT NULL NOT NULL NOT NULL

Tipo ---------------------NUMBER(38,0) VARCHAR2(25) VARCHAR2(50) DATE VARCHAR2(1)

Esta instrucción no es parte del SQL estándar, pero casi es considerada así ya que casi todos los SGBD la soportan. 3.6.8. Añadir comentarios a las tablas. Se le pueden poner comentarios a las tablas y las columnas. Un comentario es un texto descriptivo utilizado para documentar la tabla. Su sintaxis es: COMMENT ON { TABLE NombreTabla | COLUMN tabla.nombreColumna } IS 'Comentario'

Para mostrar los comentarios puestos se usan las siguientes vistas del diccionario de datos mediante la instrucción SELECT: - USER_TAB_COMMENTS. Comentarios de las tablas del usuario actual. - USER_COL_COMMENTS. Comentarios de las columnas del usuario actual. - ALL_TAB_COMMENTS. Comentarios de las tablas de todos los usuarios (sólo administradores). - ALL_COL_COMMENTS. Comentarios de las columnas de todos los usuarios (sólo administradores). 3.6.9. Determinar si existe una tabla. Para determinar si ya existe una tabla en un esquema determinado podemos consultar las siguientes vistas del diccionario de datos del sistema: DBA_TABLES, USER_TABLES o ALL_TABLES. Por ejemplo, para saber si existe la tabla MITABLA dentro del esquema actual usaríamos: SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME='MITABLA'

Si esta consulta no retorna registros es que no existe dicha tabla. Si queremos obtener toda la información sobre una tabla llamada MITABLA perteneciente a un usuario llamado USER1 usaríamos: SELECT * FROM ALL_TABLES WHERE TABLE_NAME='MITABLA' AND OWNER='USER1';

Nota. En las vistas del diccionario de datos, todos los nombres de objetos se guardan en mayúsculas. 3.6.10. Uso de tablas temporales. Podemos crear tablas que existan únicamente para nuestra sesión o cuyos datos persistan sólo durante nuestra transacción. Podemos usar tablas temporales para soportar consultas de resumen especializadas o requerimientos específicos de la aplicación. Para crear una tabla temporal se usa el comando CREATE GLOBAL TEMPORARY TABLE. Cuando se crea una tabla temporal podemos especificar si sus datos deberían durar hasta que termine nuestra sesión (mediante la cláusula ON COMMIT PRESERVE ROWS) o si las filas deben ser eliminadas cuando la transacción se complete (mediante la cláusula ON COMMIT DELETE ROWS). Al contario que una tabla permanente, una tabla temporal no reserva automáticamente espacio cuando es creada. El espacio será asignado dinámicamente a mediada que las filas son insertadas. El siguiente ejemplo muestra cómo crear una tabla temporal: CREATE GLOBAL TEMPORARY TABLE FALTA_ROLLUP ( año NUMBER(4), mes VARCHAR2(9), contador NUMBER)

Oracle /58

ON COMMIT PRESERVE ROWS;

Podemos ver la duración de nuestros datos en FALTA_ROLLUP consultando la columna Duration de la vista USER_TABLES para esta tabla. En este caso, el valor de Duration es SYS$SESSION. Si hubiésemos especificado la cláusula ON COMMIT DELETE ROWS, el valor de Duration debería ser SYS$TRANSACTION. Ahora que existe la tabla FALTA_ROLLUP, podemos poblarla mediante un comando INSERT a partir de una subconsulta sobre otra tabla. Podemos entonces usar la tabla temporal como parte de una combinación con otras tablas. 3.7. Restricciones. Una restricción es una condición de obligado cumplimiento para una o más columnas de la tabla. A cada restricción se le pone un nombre (en el caso de no poner un nombre, entonces el propio Oracle lo crea a partir del nombre de tabla, columna y tipo de restricción). Los nombres de restricción no se pueden repetir para el mismo esquema, por lo que es buena idea incluir de algún modo el nombre de la tabla, los campos involucrados y el tipo de restricción en el nombre de la misma. Por ejemplo pieza_id_pk podría indicar que el campo id de la tabla pieza tiene una clave principal (PRIMARY KEY). 3.7.1. Tipos de restricciones. Oracle admite las siguientes restricciones: ▪ NULL, para indicar que el campo admite nulos (por defecto). ▪ ▪ ▪

CREATE TABLE cliente ( dni VARCHAR2(9) NULL ); /* o también */ CREATE TABLE cliente ( dni VARCHAR2(9) ); NOT NULL, para indicar que el campo no admite nulo. CREATE TABLE cliente (dni VARCHAR2(9) NOT NULL); /* o también */ CREATE TABLE cliente(dni VARCHAR2(9) CONSTRAINT cli_dni_nn NOT NULL); UNIQUE, para indicar no repetidos en el campo. CREATE TABLE cliente (dni VARCHAR2(9) UNIQUE); /* o también */ CREATE TABLE cliente(dni VARCHAR2(9) CONSTRAINT dni_u UNIQUE); PRIMARY KEY, para indicar que el campo es la clave primaria. CREATE TABLE cliente(dni VARCHAR2(9) PRIMARY KEY) ; /* o también */ CREATE TABLE cliente(dni VARCHAR2(9) CONSTRAINT cliente_pk PRIMARY KEY) ;

Se puede crear una clave primaria sobre más de un campo:

CREATE TABLE alquiler( dni VARCHAR2(9), cod_pelicula NUMBER(5), CONSTRAINT alquiler_pk PRIMARY KEY(dni, cod_pelicula) ); FOREIGN KEY ...REFERENCES, para indicar que el campo es una

▪ integridad referencial. En la siguiente instrucción,

clave foránea, y de esta forma activar la

CREATE TABLE alquiler( dni VARCHAR2(9) CONSTRAINT dni_fk REFERENCES cliente(dni), cod_pelicula NUMBER(5), CONSTRAINT pelicula_fk FOREIGN KEY (cod_pelicula) REFERENCES pelicula(cod), CONSTRAINT alquiler_pk PRIMARY KEY(dni,cod_pelicula)); Se indica que el campo dni se relaciona con la columna dni de la tabla clientes, y que se relaciona con la columna cod de la tabla película.

el campo cod_pelicula

La integridad referencial puede provocar varios problemas en cuanto a borrados. Por ello Oracle ofrece soluciones a añadir tras la cláusula REFERENCES: - ON DELETE SET NULL, coloca nulos todas las claves secundarias relacionadas con la borrada. - ON DELETE CASCADE, borra todos los registros cuya clave secundaria es igual que la clave del registro borrado. En esas cláusulas se podría sustituir la palabra DELETE por la palabra UPDATE, haciendo que el funcionamiento se refiera a cuando se modifica un registro de la tabla principal. ▪ DEFAULT valor_por_defecto, establece un valor por defecto para el campo. CREATE TABLE cliente (dni VARCHAR2(9)DEFAULT '00000000X');

▪ CHECK, establece una condición que deben cumplir los contenidos de una o más columnas. Una misma columna puede tener múltiples CHECK en su definición (se pondrían varios seguidos, sin comas). Por ejemplo: Oracle /59

CONSTRAINT

CREATE TABLE ingreso ( cod NUMBER(5) PRIMARY KEY, importe NUMBER(11,2) CONSTRAINT importe_min_ck CHECK (importe>0) CONSTRAINT importe_max_ck CHECK (importe0 AND importe=5 THEN 'aprobado' ELSE 'suspenso' END, valor;

Para esta consulta las filas resultantes se ordenarán por el orden alfabético de las palabras 'aprobado' y 'suspenso' y después por el valor de la nota, formándose así dos grupos: primero los aprobados y después los suspensos. Por último, se puede ordenar también por una expresión que no aparezca en la salida. Por ejemplo, consideremos la siguiente consulta: SELECT nombre, apellidos FROM ALUMNO ORDER BY localidad;

En esta consulta se ordenan los alumnos por el campo localidad de la tabla base ALUMNO, pero este dato no aparece en las filas de salida. Esto es sólo posible si existe una correspondencia 1 a 1 entre las filas de salida y las filas de la tabla base. 4.4. Consultas con predicado. Un predicado es una palabra clave que modifica el comportamiento de una consulta. El predicado se incluye entre la cláusula SELECT y el primer nombre del campo a recuperar. Los posibles predicados son: ALL DISTINCT

Devuelve todos los campos de la tabla. Omite los registros cuyos campos seleccionados coincidan totalmente

4.4.1. Predicado «ALL». Si no se incluye ninguno de los predicados se asume ALL. El Motor de base de datos selecciona todos los registros que cumplen las condiciones de la instrucción SQL. El predicado «ALL *» obliga al motor de la base de datos a analizar la estructura de la tabla para averiguar los campos que contiene, siendo por tanto más rápido indicar el listado de campos deseados. SELECT ALL * FROM Alumno; SELECT * FROM Alumno;

-- es equivalente a:

4.4.2. Predicado «DISTINCT». El predicado DISTINCT omite los registros que contienen datos duplicados en los campos seleccionados. Para que se incluya un registro en la salida de la instrucción SELECT el valor conjunto de todos los campos incluidos debe ser único. Por ejemplo, varios alumnos listados en la tabla Alumno pueden tener los mismos apellidos. Si dos registros contienen López en el campo apellidos, la siguiente instrucción SQL devuelve un único registro por apellido: SELECT DISTINCT apellidos FROM Alumno;

Debe quedar claro que el predicado DISTINCT se aplica sobre los registros de salida del SELECT y no sobre los registros de entrada del FROM. 4.5. Recuperación de valores calculados. Es posible obtener valores calculados a partir de los valores originales de la tabla. Por ejemplo, en la siguiente consulta SELECT idAlumno, idModulo, valor + 1FROM Nota;

Se obtiene un listado de todas las notas pero incrementadas en una unidad. Si el valor original es 7 en el resultado se mostrará un 8. Podemos operar también con cadenas de caracteres. En el siguiente ejemplo obtenemos el nombre y Oracle /75

apellidos de los alumnos con el formato: "apellidos, nombre".

SELECT 'Apellidos y nombre:' , apellidos ||', '|| nombre AS "Nombre completo" FROM Alumno;

En la consulta, como primer campo insertamos un valor literal de tipo cadena de caracteres, y en el segundo campo se utiliza el operador "||" para concatenar los valores de los campos apellidos y nombre de la tabla Alumno. Un resultado posible de esta consulta es: APELLIDOS Y NOMBRE: -----------------------------Apellidos y nombre: Apellidos y nombre:

Nombre completo ---------------------------------Martínez Pan, Juan López Sobrado, Marian

4.6. La cláusula «WHERE». La cláusula WHERE puede usarse para determinar qué registros de las tablas enumeradas en la cláusula FROM aparecerán en los resultados de la instrucción SELECT. Después de escribir esta cláusula se deben especificar las condiciones. Si no se emplea esta cláusula, la consulta devolverá todas las filas de la tabla. WHERE es opcional, pero cuando aparece debe ir a continuación de FROM. Algunos ejemplos son: -- Matrículas del año 2009: SELECT idMatricula, nif FROM Matricula WHERE año = 2009; -- Módulo y nota donde ha aprobado el alumno de nif '11111111A': SELECT idModulo, valor FROM Nota WHERE nif = '1111111A' AND valor >= 5;

4.7. Consultas que incluyen nulos Si un registro tiene un nulo (valor NULL) en un campo, significará que se desconoce el valor de ese campo en el registro en cuestión. Debemos tener en cuenta que las expresiones escalares de cálculo en las cuales uno de los operandos es nulo dan nulo como resultado, y las expresiones escalares de comparación en las cuales uno de los comparandos es nulo dan como resultado el valor lógico desconocido. En SQL, los nulos provocan más problemas de los que resuelven y conviene evitarlos. Supongamos que, para el siguiente ejemplo, existen registros de alumnos con valor nulo en el nombre y apellidos. Podemos obtener el nombre completo (nombre + apellidos) como una única cadena de caracteres de alumnos: SELECT nombre ||''|| apellidos FROM Alumno WHERE nombre IS NOT NULL AND apellidos IS NOT NULL

En esta consulta se concatenan el valor de nombre y el valor de apellidos, excluyendo las filas donde alguno de dichos valores sea nulo.

5. Criterios de selección En el capítulo anterior se vio la forma de recuperar los registros de las tablas, pero las formas empleadas devolvían todos los registros de las tablas. A lo largo de este capítulo se estudiarán las posibilidades de filtrar los registros con el fin de recuperar solamente aquellos que cumplan unas condiciones preestablecidas. Antes de comenzar el desarrollo de este capítulo hay que recalcar tres detalles de vital importancia: - El primero de ellos es que cada vez que se desee establecer una condición referida a un campo de texto la condición de búsqueda debe ir encerrada entre comillas simples. - La segunda es que no es posible establecer condiciones de búsqueda en los campos binarios grandes (tipos de imagen o memorando dentro de campos tipo LOB). - La tercera y última hace referencia a las fechas. En Oracle se deben escribir entre comillas separando el día, mes y año con guiones o barras inclinadas. El formato válido de la fecha depende del gestor de base de datos; en Oracle podemos usar los formatos'20-03-2009' y '20/03/2009'. 5.1. Operadores lógicos. Los operadores lógicos soportados por SQL son: AND, OR, IS y NOT. A excepción del último (que es unario) todos poseen la siguiente sintaxis: expresión1

operador

expresión2

En donde expresión1 y expresión2 son las condiciones a evaluar; el resultado de la operación varía en función del operador lógico. La tabla adjunta muestra los diferentes posibles resultados: Oracle /76

expresión1 Operador expresión2 Verdad Falso AND Verdad Verdad AND Falso Verdad AND Falso Falso AND Verdad Falso OR Verdad Verdad OR Falso Verdad OR Falso Falso OR

Resultado Falso Verdad Falso Falso Verdad Verdad Verdad Falso

Si a cualquiera de las anteriores condiciones le anteponemos el operador NOT el resultado de la operación será el contrario al devuelto sin el operador NOT. El operador denominado IS se emplea para comparar dos variables de tipo objeto: Objeto1 IS Objeto2. Este operador devuelve verdad si los dos objetos son iguales. (Véase el modelo objeto-relacional de Oracle.) Algunos ejemplos de uso de operadores lógicos son los siguientes: -- Matrículas realizadas entre 1991 y 1999 inclusive: SELECT * FROM Matricula WHERE año> 1990 AND año= 5 AND valor ( SELECT AVG(valor)FROM Nota );

8.6. Subconsultas con «EXISTS». EXISTS es el cuantificador existencial y puede aplicarse sobre subconsultas. La expresión « EXISTS (SELECT … FROM …)» da como resultado un valor verdadero si y solo si el resultado de evaluar la subconsulta no es el conjunto vacío; en otras palabras, si existe al menos un registro en el resultado. Por ejemplo, la siguiente consulta obtiene el id de matrícula de alumnos del curso 2004-05 que tienen alguna falta en el módulo 5. SELECT idMatricula FROM Matricula WHERE año = 2004 AND EXISTS ( SELECT 1 FROM Falta WHERE Falta.idMatricula AND Falta.idModulo=5 )

Nota. En una subconsulta evaluada mediante el operador EXISTS la lista de columnas que devuelve el comando SELECT carece de importancia. Por ello es habitual que este tipo de consultas retornen una única columna con una expresión literal. La forma negada, NOT EXISTS, es importante en cierto tipo de consultas complejas. Como primer ejemplo obtendremos el id de matrícula de alumnos del curso 2004-05 que no tienen ninguna falta en los módulos 4 y 5. SELECT idMatricula FROM Matricula WHERE año = 2004 AND NOT EXISTS ( SELECT 1 FROM Falta WHERE Falta.idMatricula AND Falta.idModulo IN (4, 5 ) )

El siguiente ejemplo es más complejo, y obtiene los id de matrícula de alumnos del curso 2004-05 que tienen faltas en todos los módulos. SELECT Matricula.idMatricula FROM Matricula WHERE Matricula.año = 2004 ANDNOT EXISTS ( SELECT 1FROM Modulo WHERE NOT EXISTS ( SELECT 1FROM Falta WHERE Falta.idMatricula = Matricula.idMatricula AND Falta.idModulo = Modulo.idModulo )) ;

Para comprender la consulta, podríamos enunciarla como: «obtener los id de matricula de alumnos del curso 2004-05 que no tengan módulos en los que no tengan faltas». 8.7. Expresiones de columna con subconsultas Es también posible usar una subconsulta en una de las expresiones de columna de una SELECT siempre y cuando la subconsulta retorne un único registro con un valor simple. Un primer ejemplo sencillo es obtener los id's de matrícula, los apellidos de alumnos y el año de curso. SELECT M.idMatricula, (SELECT apellidos FROM Alumno A WHERE A.nif=M.nif), M.año FROM Matricula M; Como alternativa al uso de la cláusula GROUP BY, podemos obtener el nif de alumnos

que han cursado.

Oracle /86

y el número de módulos

SELECT nif , (SELECT COUNT(*) FROM Nota WHERE Nota.nif=Alumno.nif) FROM Alumno;

8.8. Subconsultas como origen de registros para «FROM». En la cláusula FROM se pueden usar como orígenes de datos: - Una tabla - Una vista - Una función que devuelva una tabla - Una subconsulta Por ejemplo, podemos obtener en una subconsulta registros con el nif de un alumno, el nombre de un módulo y la nota. Podemos utilizar dicha subconsulta como origen de datos para obtener la media de notas por módulo. SELECT nombre AS "Módulo" , AVG (valor) AS "Nota media" FROM (SELECT N.nif, M.nombre, N.valor FROM Nota N JOIN Modulo M USING (idModulo)) GROUP BY nombre;

Algo más complejo es obtener un valor de nota (entre 1 y 10) y el número de alumnos que han conseguido dicha nota en el módulo 1. Para la consulta se debe forzar un registro por cada una de las notas, de forma que si ningún alumno obtuvo esa nota se muestre el valor 0. Podemos obtener el número de alumnos por nota registrada en la tabla Nota: SELECT valor, COUNT(*) FROM Nota GROUP BY valor;

Pero esta consulta no garantiza que se genere necesariamente un registro por cada valor de nota posible (por ejemplo, si ningún alumno tiene la nota 7, no se generará un registro con la nota 7). Para forzar esto necesitamos una subconsulta con todos los valores de nota y forzar un JOIN externo: SELECT valor, NVL(num, 0) FROM (SELECT 1 valor FROM DUAL UNION SELECT 2 valor FROM DUAL UNION SELECT 3 valor FROM DUAL UNION SELECT 4 valor FROM DUAL UNION SELECT 5 valor FROM DUAL UNION SELECT 6 valor FROM DUAL UNION SELECT 7 valor FROM DUAL UNION SELECT 8 valor FROM DUAL UNION SELECT 9 valor FROM DUAL UNION SELECT 10 valor FROM DUAL) T1 LEFT JOIN (SELECT valor, COUNT(*) num FROM Nota GROUP BY valor) T2 ON T1.valor = T2.valor; Aquellos valores de nota no incluidos en la tabla Nota no combinarán en el LEFT JOIN y por tanto producirán un registro con las columnas combinadas (valor y num) a valor NULL. Por ello se utiliza la función NVL(), que verifica si el campo num tiene valor NULL y en ese caso lo sustituye por un cero.

9. Vistas 9.1. Introducción. Una vista no es más que una consulta almacenada que puede ser reutilizada tantas veces como se desee. Una vista no contiene datos sino la instrucción SELECT necesaria para generarla; eso asegura que los resultados de la vista sean siempre coherentes con los datos actuales almacenados en las tablas. Por todo ello, las vistas gastan muy poco espacio de disco. Las vistas se emplean para: - Realizar consultas complejas más fácilmente, ya que permiten dividir la consulta en varias partes. - Proporcionar el contenido de tablas con datos completos. - Utilizar visiones especiales de los datos. - Ser utilizadas como tablas que resumen todos los datos. - Ser utilizadas como cursores de datos en los lenguajes procedimentales (como PL/SQL). Hay dos tipos de vistas: • Simples. Las forma una sola tabla y no contienen funciones de agrupación. Su ventaja es que permiten siempre realizar operaciones DML sobre ellas. • Complejas. Obtienen datos de varias tablas, y pueden utilizar funciones de agrupación. No siempre permiten operaciones DML. Podemos usar la cláusula ORDER BY al crear una vista, pero esto puede influir en un rendimiento negativo, Oracle /87

porque obliga a operaciones adicionales. 9.2. Crear y consultar vistas. Las sintaxis para crear una vista es la siguiente:

CREATE [OR REPLACE] [FORCE|NOFORCE] VIEW nombre_vista [(alias[, alias2...]] AS consultaSelect [WITH CHECK OPTION [CONSTRAINT restricción]] [WITH READ ONLY [CONSTRAINT restricción]]

Donde:

OR REPLACE, indica que si la vista ya existía se cambie por la actual. FORCE, crea la vista aunque los datos de la consulta SELECT no existan. alias, es la lista de alias que se establecen para las columnas devueltas por

la consulta SELECT en la que se basa esta vista. El número de alias debe coincidir con el número de columnas devueltas por SELECT. WITH CHECK OPTION, hace que sólo las filas que se muestran en la vista puedan ser añadidas (INSERT) o modificadas (UPDATE). La restricción que sigue a esta sección es el nombre que se le da a esta restricción de tipo CHECK OPTION. WITH READ ONLY, hace que la vista sea de sólo lectura. Permite grabar un nombre para esta restricción. Lo bueno de las vistas es que tras su creación se utilizan como si fueran una tabla virtual. Por ejemplo, la siguiente vista recupera el número de faltas de cada alumno en cada módulo de cada año: CREATE VIEW ResumenFaltas (nif, modulo, faltas) AS ( SELECT M.nif, Mo.nombre, COUNT(*) FROM Matricula M JOIN Falta F ON M.idMatricula=F.idMatricula JOIN Modulo Mo ON F.idModulo=Mo.idModulo ); / SELECT DISTINCT nif, modulo FROM ResumenFaltas;

9.3. Ejecución de comandos DML sobre vistas. Las instrucciones DML (INSERT, UPDATE y DELETE) ejecutadas sobre las vistas permiten añadir o modificar los datos de las tablas relacionadas en la consulta subyacente de la vista. Ahora bien, no es posible ejecutar instrucciones DML sobre vistas que: - Utilicen funciones de grupo (SUM, AVG, etc.) - Usen GROUP BY o DISTINCT. - Posean columnas con cálculos. Además no se pueden añadir datos a una vista si en las tablas referenciadas en la consulta SELECT hay campos NOT NULL que no aparecen en la consulta (es lógico ya que al añadir el dato se tendría que añadir el registro colocando el valor NULL en el campo ausente). Por ejemplo, podemos utilizar la siguiente vista para hacer inserciones de matrículas: CREATE VIEW MatriculaAmpliada AS SELECT idMatricula, nif, apellidos || ', ' || nombre AS alumno, año FROM Matricula LEFT JOIN Alumno USING (nif); / INSERT INTO MatriculaAmpliada (idMatricula, nif, año) VALUES (67, '77777777J', 2008);

Podremos también hacer inserciones sobre una vista basada en varias tablas si Oracle puede determinar los registros apropiados a insertar. En vistas multitabla Oracle determina qué tablas proporcionan su clave. Si una vista contiene muchas columnas de una tabla para identificar la clave primaria de esta tabla, se conserva la clave y Oracle puede ser capaz de insertar filas en la tabla a través de la vista. 9.4. Estabilidad de una vista. Recuérdese que los resultados de una vista son construidos al instante sobre la tabla (o tablas) subyacente cuando ejecutamos la vista. Como consecuencia, si la tabla subyacente es eliminada la validez de la vista desaparece. Intentar una consulta sobre una vista cuya tabla subyacente ha sido eliminada produce un mensaje de error. Nota. La única excepción a esta regla es usar vistas materializadas. Una vista materializada es una tabla que almacena datos que se obtendrían normalmente a través de una vista.

Oracle /88

Crear vistas basadas en una consulta que usa el asterisco para recuperar las columnas presenta un caso especial. Por ejemplo, si creamos una vista sobre la tabla Alumno: CREATE OR REPLACE VIEW Alumno_View AS SELECT * FROM Alumno;

Y entonces alteramos la tabla subyacente:

ALTER TABLE Alumno ADD (pais VARCHAR2(30));

Ocurre que, a pesar del cambio de la tabla subyacente la vista es todavía válida; pero la columna pais no será visible a través de la vista. Si después de alterar la tabla, volvemos a compilar la vista, la nueva columna se verá a través de la vista. 9.5. Mostrar la lista de vistas. La vista del diccionario de datos de Oracle USER_VIEWS permite mostrar una lista de todas las vistas que posee el usuario actual. Es decir, para saber qué vistas hay disponibles se usa: SELECT * FROM USER_VIEWS;

La columna TEXT de esa vista contiene la sentencia SQL que se utilizó para crear la vista (sentencia que es ejecutada cada vez que se invoca a la vista). 9.6. Borrar vistas. Se utiliza el comando DROP VIEW para eliminar una vista, como por ejemplo: DROP VIEW nombreDeVista;

10. Comandos internos en SQL*PLUS e iSQL*Plus Lo que se comenta en este apartado son comandos y operaciones que no pertenecen al lenguaje SQL, sino que son comandos que sirven para dar instrucciones al programa SQL*Plus o iSQL*Plus. Las operaciones que se comentan aquí son interpretadas por el cliente SQL*Plus y no por Oracle. Estas operaciones sirven sobre todo para variar la forma en la que se muestran los resultados de las consultas SQL. Hay que tener en cuenta que hay cierta diferencia entre los comandos SQL*Plus e iSQL*Plus. 10.1. Variables de sustitución. Se utilizan variables de sustitución para poder pasar parámetros a una consulta. Por ejemplo, si a menudo se realiza un listado de clientes en el que queremos mostrar los datos de un cliente identificado por su DNI, entonces se puede utilizar una variable de sustitución para el DNI, de modo que cada vez que se ejecute esa consulta se pedirá el nuevo valor de la variable. 10.1.1. Operador &. La primera forma de utilizar variables de sustitución es mediante el comando &. Este símbolo, utilizado en cualquier parte de la consulta, permite rellenar el contenido de una variable de sustitución. Por ejemplo: SELECT * FROM Piezas WHERE modelo=&mod;

Al ejecutar esa sentencia, desde el cliente SQL*Plus se nos pedirá rellenar el valor de la variable mod. Esa variable no se puede volver a usar, si se usa se nos invitará a indicar el valor que le damos. La ventaja de esta técnica está en que cada vez que ejecutemos podremos dar un valor a la variable, lo que nos permite reutilizar consultas una y otra vez para distintos valores, sin tener que rescribirla. En el caso de que la variable sea de texto, hay que colocar el símbolo & dentro de las comillas que delimitan el texto. Ejemplo: SELECT * FROM Piezas WHERE tipo='&tip';

Es decir, se trata de una macro-sustitución en la que el contenido de la variable se sustituye por su contenido antes de pasar la instrucción a Oracle, de ahí que sea necesario colocar las comillas, de otro modo Oracle indicaría que la instrucción es errónea. 10.1.2. Comando «DEFINE». Se pueden utilizar variables de sustitución que se definan como variables de usuario mediante el comando DEFINE. La sintaxis de este comando es: DEFINE variable=valor;

La variable se sobreentiende que es de tipo texto. El valor es el contenido inicial de la variable. La variable así creada tiene vigencia durante toda la sesión de usuario. Se elimina en el cierre de la sesión o si se usa el comando UNDEFINE indicando el nombre de la variable a eliminar. Para cambiar el valor de la variable se debe utilizar otra vez el comando DEFINE. La ventaja respecto al método anterior está en que la misma variable de sustitución se puede utilizar para varios SELECT. La desventaja está en que requiere tocar el código para cambiar el valor de la variable. Ejemplo: Oracle /89

DEFINE tip='TU'; SELECT * FROM piezas WHERE tipo='&tip'; SELECT * FROM existencias WHERE tipo='&tip'; En el ejemplo, los dos SELECT muestran piezas cuyo variable tip.

tipo sea

TU.

SQL*Plus no preguntará por el valor de la

El comando DEFINE sin nada más permite mostrar una lista de todas las variables definidas en ese momento. 10.1.3. Operador &&. Se trata de una mezcla entre las opciones anteriores. Cuando en una consulta se utiliza una variable de sustitución mediante dos símbolos ampersand, entonces al ejecutar la consulta se nos preguntará el valor. Pero luego ya no, la variable queda definida como si se hubiera declarado con DEFINE. El resto de veces que se utilice la variable, se usa con un solo &. El cambio de valor de la variable habrá que realizarle con DEFINE. 10.2. Comando «SET». Este comando permite cambiar el valor de las variables de entorno del programa. Su uso es: SET nombreVariable valor

Las variables más interesantes a utilizar son: Variable

Posibles valores ON y OFF ON y OFF

Explicación Repite el comando SQL antes de mostrar su resultado. ECHO Permite mostrar estadísticas sobre el tiempo de ejecución en TIMING cada consulta SQL que se ejecute (interesante para estadísticas). Hace que el encabezado con los alias de las columnas se active HEADING ON y OFF o no. Activado, trunca un texto si sobrepasa la anchura máxima. WRAP ON y OFF Permite indicar la versión con la que se comprueba la COMPATIBILITY V7, V8, NATIVE compatibilidad de los comandos. NATIVE indica que el propio servidor Oracle decide la compatibilidad. DEFINE &, caracter, ON y OFF Permite activar y desactivar la posibilidad de usar variables de sustitución. Permite indicar el carácter utilizado para la sustitución de variables. n Indica el número de filas que se muestran antes de repetir el PAGESIZE encabezado de la consulta n Indica la anchura máxima de la línea de la consulta. Si una línea LINESIZE de la consulta sobrepasa este valor, los datos pasan a la siguiente. También influye sobre los tamaños y posiciones de los encabezados y pies de los informes. valor Indica qué valor se muestra cuando hay nulos. NULL formato Permite especificar un formato que se aplicará a todos los NUMFORMAT números. (Véase formato de columnas, más adelante). valor Indica la anchura máxima utilizada para mostrar números. Si NUMWIDTH un número sobrepasa esta anchura, es redondeado. Hace que se muestren el número total de registros de la FEEDBACK n, ON y OFF consulta cuando el resultado supera los n registros. ancho LONG Anchura máxima para los campos de tipo LONG.

El comando SHOW seguido del nombre de uno de los parámetros de la tabla anterior, permite mostrar el estado actual del parámetro indicado. Si se usa SHOW ALL, entonces se muestran todos. 10.3. Encabezado y pie de informe. Los parámetros BTITLE y TTITLE permiten, respectivamente, indicar un texto de pie y de encabezado para la consulta. El formato es {B|T}TITLE {texto|ON|OFF}

El texto es lo que se desea en el encabezado o pie. Ese texto puede incluir las palabras para hacer que el texto vaya a izquierda, centro o derecha respectivamente. Se pueden indicar incluso las tres cosas a la vez: TTITLE LEFT 'informe1' RIGHT 'estudio de clientes';

LEFT, RIGHT

Se puede también usar la palabra COL seguida del número de columna en el que se desea el texto: Oracle /90

o CENTER

TTITLE COL 50 'informe1';

También se puede indicar la palabra TAB seguida de un número que representará tabulaciones, haciendo que SQL*Plus deje ese espacio en los encabezados o pies. 10.4. Comando «COLUMN». Permite especificar un formato de columna. Si se usa sin modificador (sólo COLUMNS o su abreviatura COL) se muestran las configuraciones de formato de columnas actualmente en uso. Si se añade el nombre de una columna, se indica el formato actual (si lo hay) para esa columna. 10.4.1. Añadir formato de columna. Si se usa COLUMN (o COL) con el parámetro HEADING seguido de un texto, el texto se convierte en la cabecera de la columna (sustituyendo al alias de la columna). Ese texto sólo sirve para ser mostrado, no puede formar parte de una sentencia SQL. COLUMN precio_venta HEADING 'Precio de|venta'; SELECT tipo, modelo, precio_venta FROM piezas;

En el ejemplo, la barra vertical provoca un salto de línea en el texto. El parámetro FORMAT permite indicar una máscara de formato para el texto. Se usan códigos especiales para ello. A los textos se les puede colocar una anchura máxima de columna. Eso se hace con una A seguida del número que indica esa anchura. COLUMN tipo FORMAT 'A10'; SELECT tipo, modelo, precio_venta FROM piezas;

Para los números se usan códigos de posición: Código Significado Posición para un número. Si el valor es 0 o vacío, entonces no se muestra nada 9 Posición para un número. Si el valor es 0 o vacío, entonces se muestra 0 0 Posición para el signo de dólar $ Muestra un signo menos tras el número, si el número es negativo (ejemplo: '999MI') MI Muestra el signo del número (+ ó -) en la posición en la que se coloca el signo S Muestra los números negativos entre < y > PR Muestra el signo decimal en esa posición D Muestra el signo de grupo en esa posición G Muestra el punto (separador decimal) . Muestra la coma (separador de miles) , Muestra el símbolo de moneda nacional en esa posición L Muestra el número en romano (mayúsculas) RN Muestra el número en romano (minúsculas) rn

Las fechas deben ser formateadas desde la propia instrucción SQL mediante la función tema anterior) 10.4.2. Parámetro «LIKE». Permite copiar atributos de una columna a otra:

TO_CHAR

(vista en un

COLUMN precio_venta FORMAT '9G990D00L'; COLUMN precio_compra LIKE precio_venta;

Las dos columnas tendrán el mismo formato (separador de miles, decimales y moneda tras los dos decimales) 10.4.3. Parámetro «NULL». Indica un texto que sustituirá a los valores nulos. 10.4.4. Parámetro «CLEAR». Elimina el formato de la columna. Lógicamente se pueden combinar varias acciones a la vez. 10.5. Comando «BREAK». Es uno de los comandos más poderosos. Permite realizar agrupaciones en las consultas, consiguiendo verdaderos informes (en especial si se combina con COMPUTE). Permite dividir la vista en secciones en base al valor de un campo al que se le pueden incluso quitar los duplicados. El comando: BREAK ON tipo;

Hace que las columnas con alias

tipo

no muestren los duplicados, mostrando una vez cada valor duplicado. Oracle /91

Para el buen funcionamiento de la orden, el resultado debe de estar ordenado por esa columna. Se pueden hacer varios grupos a la vez: BREAK ON tipo ON modelo; orden CLEAR BREAK elimina todos

La los BREAK anteriormente colocados. 10.5.1. Parámetro «SKIP». A la instrucción anterior se le puede añadir la palabra SKIP seguida de un número. Ese número indica las líneas que se dejan tras el valor del grupo al imprimir. Si se indica SKIP PAGE, significa que se salta una página completa cada vez que cambie el valor del grupo. 10.5.2. Parámetro «ON REPORT». Permite (en unión con COMPUTE) realizar cálculos de totales sobre el informe completo. 10.5.3. Parámetros «DUPLICATES» y «NODUPLICATES». Permiten mostrar o no los duplicados de cada sección. La opción inicial es NODUPLICATES. 10.6. Comando «COMPUTE». Permite en unión con BREAK realizar cálculos para las secciones de una consulta. Todo COMPUTE está asociado a un apartado ON de una instrucción BREAK previa. Sintaxis: COM[PUTE] [función [LAB[EL] texto] OF columna_o_Alias ON {columna_o_Alias|REPORT}

Donde:

función, es el nombre de la función de cálculo que se usa ( SUM, AVG, MIN, MAX, NUM, STD LABEL, permite indicar un texto previo al resultado del cálculo, si no se utiliza se pone

o VAR). el nombre de la

función utilizada. OF, indica el nombre de columna o alias utilizado en la instrucción SELECT a partir de la que se realiza el cálculo. ON, indica el nombre de columna o alias que define la sección sobre la que se realizará el cálculo. Este nombre debe haber sido indicado en un BREAK anterior. REPORT, indica que el cálculo se calculará para toda la consulta. Por ejemplo: CLEAR BREAK; COLUMN TIPO FORMAT A20; BREAK ON tipo SKIP PAGE ON modelo ON REPORT; COMPUTE SUM LABEL 'Total' MAX LABEL 'Máximo' OF cantidad ON tipo; COMPUTE SUM LABEL 'Total' OF cantidad ON modelo; COMPUTE SUM LABEL 'Total absoluto' OF cantidad ON REPORT; SELECT tipo, modelo, n_almacen, cantidad FROM existencias WHERE tipo='AR' OR tipo='TU' ORDER BY tipo, modelo;

Producirá el siguiente resultado:

Oracle /92

10.7. Guardar consultas en ficheros. Podemos guardar consulta en un fichero de texto y después ejecutarlo desde SQL*Plus. Debemos guardar el archivo con extensión .sql para que sea reconocido sin problemas. Para ejecutar el archivo se utiliza el comando START o la sintaxis @archivo desde la línea de comandos de SQL*Plus. Como ejemplo, supongamos el archivo siguiente: Archivo «comandos.sql» SET PAGESIZE 40 SET LINESIZE 80 SELECT IdObra, Nombre, Precio FROM Obra /

Ahora podemos ejecutarlo desde SQL*Plus mediante el comando: SQL> @comandos.sql

O bien:

SQL> START comandos.sql

10.8. Redirigir la salida de SQL*Plus con «SPOOL». Oracle permite redirigir la salida de los comandos en SQL*Plus a un fichero de texto. Esto se consigue mediante el comando SPOOL. SQL> SPOOL /tmp/mi_fichero_de_texto.lst

Deberemos tener en cuenta que: • Los datos de salida no se materializarán en el fichero de texto hasta que se ejecute el comando SPOOL OFF. • Si vamos a redirigir muchos datos tendremos que modificar la variable de entorno SERVEROUTPUT para darle un tamaño mayor. El siguiente sería un ejemplo de utilización de SPOOL donde se desea un listado en un fichero de texto con la ruta "d:\tmp\listado.lst": SQL> SET SERVEROUTPUT ON SIZE 10000

Oracle /93

SQL> SPOOL d:\tmp\listado.lst SQL> PROMPT TEST SQL> SPOOL OFF

Si ahora leemos el contenido del archivo pantalla.

listado.lst

veremos que contiene lo mismo que se mostró por

11. Consultas avanzadas 11.1. Consultas con «ROWNUM». La pseudo-columna ROWNUM devuelve un número para cada fila de salida de un SELECT, que se corresponde con el orden en que son generadas las filas a partir de los registros de origen. Por ejemplo, la consulta siguiente: SELECT ROWNUM AS "Orden", nif, apellidos FROM Alumno WHERE localidad = 'Madrid';

Produce un resultado como éste: ORDEN 1 2 3

NIF 44444444L 55555555V 33333333H

APELLIDOS López Martín Senén Rivas García Gómez

Si aplicamos una ordenación por nif:

SELECT ROWNUM AS "Orden", nif, apellidos FROM Alumno WHERE localidad = 'Madrid' ORDER BY nif;

Se producirá el siguiente resultado:

ORDEN 3 1 2

NIF 33333333H 44444444L 55555555V

APELLIDOS García Gómez López Martín Senén Rivas

Como vemos, ROWNUM no se corresponde con la ordenación establecida, sino con el orden en que fueron generadas las filas antes de su ordenación. Si deseamos establecer una secuencia desde 1 sobre el resultado de la ordenación deberemos aplicar ROWNUM sobre una subconsulta: SELECT ROWNUM AS "Orden", nif, apellidos FROM (SELECT nif, apellidos FROM Alumno WHERE localidad = 'Madrid' ORDER BY nif); Puesto que la subconsulta obtiene una lista de los alumnos ordenada por nif, el SELECT superior

obtendrá esa lista pero mostrando el orden de las filas en esa consulta. Eso permite hacer consultas del tipo top-n (los n más...). Por ejemplo, para sacar el top-10 de los alumnos con mejor nota media: SELECT nif AS "Alumno" FROM (SELECT nif, AVG(valor) FROM Nota GROUP BY nif ORDER BY AVG(valor)) WHERE ROWNUM 1

un número,

Este comportamiento se produce por el siguiente motivo: el valor de ROWNUM es asignado a cada fila luego de que pasa la fase de predicado de la consulta pero antes que la consulta pase por algún ordenamiento o agregación; además, cada valor de ROWNUM es incrementado solamente luego de ser asignado, lo que explica por qué la consulta no devuelve ninguna fila. Debido a que ROWNUM > 1 no es verdadero para la primera fila, ROWNUM nunca avanza al valor 2. 11.2. Consultas con «ROWID». El tipo de dato ROWID permite almacenar la dirección que tiene una fila dentro de su tabla. Cada vez que se inserta un registro en la base de datos Oracle genera un valor hexadecimal de tipo ROWID para el nuevo registro con el formato siguiente: OOOOOOFFFBBBBBBRRR

Donde:

Oracle /94

OOOOOO:

es el segmento de la base de datos. Todos los objetos que estén en el mismo esquema y en el mismo segmento tendrán el mismo valor. FFF: es el número de fichero del tablespace relativo que contiene la fila. BBBBBB: es el bloque de datos que contiene a la fila. El número de bloque es relativo a su fichero de datos, no al tablespace. Por lo tanto, dos filas con números de bloque iguales podrían residir en diferentes ficheros de datos del mismo tablespace. RRR: es el número de fila en el bloque. Por tanto no existen dos filas en la base de datos con el mismo ROWID. Este valor es sólo accesible mediante la pseudo-columna ROWID, por ejemplo: SELECT ROWID, nombre, apellidos FROM Alumno;

Siempre que queramos obtener una fila de la forma más rápida posible, debemos hacerlo a través de su ROWID. Un uso típico suele ser obtener un listado de ROWID's con un SELECT, y después acceder a cada una de las filas directamente con la condición del ROWID. Esta pseudo-columna también puede ser útil para consultar ciertos aspectos físicos sobre las tablas de nuestra base de datos. Por ejemplo, una forma de saber en cuántos ficheros de datos está alojada una tabla sería consultando la parte de ROWID correspondiente. SELECT COUNT(DISTINCT SUBSTR(ROWID,7,3)) "Número ficheros" FROM Alumno;

11.3. Consultas con «RANK». Podemos crear vistas sobre consultas de agrupación para realizar informes de resumen complejos sobre los datos. Las vistas de una agrupación simplifican la representación de los datos para varios niveles de agrupación dentro de nuestra aplicación. Esto hace que sea más sencillo usar funcionalidades analíticas avanzadas. Consideremos la tabla NOTA para crear un vista que proporcione la nota media de cada alumno. CREATE VIEW NOTAS_AVG AS (nif, notaMedia) SELECT nif, AVG(valor) FROM Nota GROUP BY nif;

Podemos obtener un listado de los alumnos, ordenados de mayor a menor, por los que tienen mejor nota media: SELECT * FROM NOTAS_AVG ORDER BY notaMedia DESC;

Siendo un posible resultado el siguiente:

NIF ---------------00000000A 22222222B 33333333C 44444444D 55555555E

NOTAMEDIA ----------------9,8 8 8 6,5 5,3

Este resultado muestra la clasificación de los alumnos por su nota media; el alumno de nif '00000000A' es el primero en cuanto a mejores notas. Sin mostrar esta lista, es posible determinar dónde debería estar un valor determinado de nota media en esta clasificación. Para hacer esto se usa la función RANK; la cual toma un valor como entrada y usa cláusulas adicionales (WITHIN GROUP y ORDER BY) para decirle a Oracle cómo crear la clasificación. Por ejemplo, para saber en que posición de la clasificación debería estar la nota media 7 podemos efectuar la siguiente consulta: SELECT RANK(7) WITHIN GROUP (ORDER BY notaMedia DESC) "Ranking" FROM NOTAS_AVG;

Siendo el resultado:

RANKING ---------------4

El valor 4 indica la posición en el resultado de la clasificación que debería ocupar un registro con el valor 7 en la columna NOTAMEDIA. Desde una perspectiva de porcentaje, podemos aplicar la función PERCENT_RANK para obtener la clasificación de la nota media 7: SELECT PERCENT_RANK(7) WITHIN GROUP (ORDER BY notaMedia DESC) "Ranking" FROM NOTAS_AVG;

Siendo el resultado:

Oracle /95

RANKING ---------------,6 (con el 60%).

Es decir, estaría en una posición casi intermedia Con esta técnica de usar vistas de resumen y funciones analíticas podemos crear vistas e informes que incluyan medias ponderadas, producción eficaz, porcentajes de totales, porcentajes de subtotales, y muchos cálculos similares. 11.4. Consultas sobre estructuras jerárquicas. Imaginemos una tabla de empleados definida por un código de empleado, nombre del mismo y el código del jefe: CREATE TABLE Empleado ( codigo NUMBER PRIMARY KEY, nombre NVARCHAR2(100), codigoJefe NUMBER REFERENCES Empleado(codigo));

E insertamos los siguientes registros:

CODIGO -----------1 2 3 4 5 6

NOMBRE -----------Ángel Eva Andrés Antonio Carmen Carmelo

CODIGOJEFE -----------------1 2 1 2 3

Este último código está relacionado con el código de empleado que posee el jefe en cuestión. Así definido, una consulta que muestre el nombre de un empleado y el nombre de su jefe directo, sería: SELECT E.nombre AS empleado, J.nombre AS jefe FROM Empleado EJOIN Empleado J ON (E.codigoJefe=J.codigo);

Obtendríamos por ejemplo:

EMPLEADO -------------Antonio Ángel Eva Carmen Andrés Carmelo

JEFE ---------Ángel Ángel Eva Eva Andrés

En el ejemplo se observa como un jefe puede tener otro jefe, generando una estructura jerárquica:

En este tipo de estructuras a veces se requieren consultas que muestren todos los empleados de un jefe, mostrando los mandos intermedios. Se trata de una consulta que recorre ese árbol. Este tipo de consultas posee esta sintaxis: SELECT [LEVEL,] listaDeColumnasYExpresiones FROM tabla(s)... [WHERE condiciones...] [START WITH condiciones] CONNECT BY [PRIOR] expresion1=[PRIOR] expresion2 apartado CONNECT permite indicar qué relación hay que

El seguir para recorrer el árbol. La palabra PRIOR indica hacia dónde se dirige el recorrido. Finalmente el apartado START indica la condición de inicio del recorrido (normalmente la condición que permita buscar el nodo del árbol por el que comenzamos el recorrido); es decir, sirve para indicar desde dónde comenzamos. Oracle /96

El siguiente ejemplo obtiene los jefes jerárquicos del empleado 'Andrés': SELECT nombre FROM Empleado START WITH nombre='Andrés' CONNECT BY PRIOR codigoJefe = codigo;

Y el resultado es:

NOMBRE -----------Andrés Eva Ángel

Sin embargo, la siguiente consulta:

SELECT nombre FROM Empleado START WITH nombre='Andrés' CONNECT BY codigoJefe= PRIOR codigo; Devuelve los subordinados de 'Andrés':

NOMBRE -----------Andrés Carmelo

El modificador LEVEL permite mostrar el nivel en el árbol jerárquico de cada elemento: SELECT LEVEL, nombre FROM Empleado START WITH nombre='Ángel' CONNECT BY codigoJefe= PRIOR codigo;

Y el resultado es:

Para eliminar recorridos se utilizan

LEVEL NOMBRE -------- -----------1 Ángel 2 Antonio 2 Eva 3 Carmen 3 Andrés 4 Carmelo condiciones en WHERE o en el

SELECT LEVEL, nombre FROM Empleado WHERE nombre'Eva' START WITH nombre='Ángel' CONNECT BY codigoJefe= PRIOR codigo;

propio CONNECT. De modo que:

En este ejemplo, Eva no sale en los resultados. En este otro:

SELECT LEVEL, nombre FROM Empleado START WITH nombre='Ángel' CONNECT BY codigoJefe= PRIOR codigo AND nombre'Eva';

No sale ni Eva ni sus empleados (se corta la rama entera). Usar CONNECT BY y START WITH para crear informes no es difícil, pero deben seguirse ciertas reglas básicas: • El orden de las cláusulas debe ser siempre como sigue: 1. SELECT 2. FROM 3. WHERE 4. START WITH 5. CONNECT BY 6. ORDER BY • PRIOR fuerza que los informes vayan desde la raíz hacia las hojas (si la columna previa es el padre) o de una hoja hacia la raíz (si la columna previa es el hijo). Oracle /97

• Una cláusula WHERE elimina nodos del árbol, pero no así a sus descendientes (o ancestros). • Una cualificación en CONNECT BY (particularmente un no igual) elimina nodos y sus descendientes (o ancestros). • CONNECT BY no puede ser usado con una tabla enlazada en la cláusula WHERE. 11.5. Consultas de agrupación avanzada. La expresión ROLLUP en una consulta de agrupación (GROUP BY) permite obtener los resúmenes parciales o totales sobre las funciones de agregado utilizadas en las columnas de la consulta. Por ejemplo, la siguiente consulta obtiene la nota de cada alumno en cada módulo: SELECT N.nif, M.nombre, AVG(valor) AS "Nota" FROM Nota N JOIN Modulo M ON N.idModulo=M.idModulo GROUP BY N.nif, M.nombre;

Como cada alumno tiene una única nota en cada módulo, el resultado de la función que valor. Pero si ahora añadimos:

AVG(valor)

es el mismo

SELECT N.nif, M.nombre, AVG(valor) AS "Nota media" FROM Nota N JOIN Modulo M ON N.idModulo=M.idModulo GROUP BY ROLLUP (N.nif, M.nombre);

Entonces nos añade un registro para cada alumno en el que aparece la media para ese alumno, y al final mostrará un registro con la media global de todos los alumnos. Es decir, el resultado de esa consulta podría ser algo como: NIF 11111111A 11111111A 11111111A 22222222B 22222222B 22222222B 33333333C 33333333C 33333333C

NOMBRE Modulo1 Modulo2 Modulo1 Modulo2 Modulo1 Modulo2

NOTA MEDIA 5 6 5,5 4 7 5,5 8 9 8,5 6,5

En ROLLUP se pueden unir varias columnas entre paréntesis para tratarlas como si fueran una unidad: SELECT N.nif, M.nombre, AVG(valor) AS "Nota media" FROM Nota N JOIN Modulo M ON N.idModulo=M.idModulo GROUP BY ROLLUP ( (N.nif, M.nombre) );

La diferencia respecto a la anterior es que sólo muestra un resumen global para alumno y módulo. Es decir, sólo una fila resumen donde todos los campos involucrados quedan a nulo. NIF 11111111A 11111111A 22222222B 22222222B 33333333C 33333333C

NOMBRE Modulo1 Modulo2 Modulo1 Modulo2 Modulo1 Modulo2

NOTA MEDIA 5 6 4 7 8 9 6,5

O se puede aplicar el ROLLUP sobre una única expresión del GROUP BY: SELECT N.nif, M.nombre, AVG(valor) AS "Nota media" FROM Nota N JOIN Modulo M ON N.idModulo=M.idModulo GROUP BY ROLLUP (N.nif), M.nombre;

Con lo cual se obtiene filas de resumen donde sólo los campos incluidos en ROLLUP estarán a nulo: NIF 11111111A 22222222B 33333333C

NOMBRE Modulo1 Modulo1 Modulo1 Modulo1

Oracle /98

NOTA MEDIA 5 4 8 5,6

11111111A 22222222B 33333333C

Modulo2 Modulo2 Modulo2 Modulo2

6 7 9 7,3

La expresión CUBE es muy similar a ROLLUP, sólo que ésta calcula todos los resúmenes relativos a los campos involucrados. Ejemplo: SELECT N.nif, M.nombre, AVG(valor) AS "Nota media" FROM Nota N JOIN Modulo M ON N.idModulo=M.idModulo GROUP BY CUBE (N.nif, M.nombre);

Y el resultado es:

NIF 11111111A 11111111A 11111111A 22222222B 22222222B 22222222B 33333333C 33333333C 33333333C

NOMBRE Modulo1 Modulo2 Modulo1 Modulo2 Modulo1 Modulo2 Modulo1 Modulo2

NOTA MEDIA 5 6 5,5 4 7 5,5 8 9 8,5 5,6 7,3 6,5

Es decir, calcula medias por alumno, medias por módulo, y la media total. (Obsérvese que las filas de resumen se generan para cada combinación de campos incluidos en CUBE a nulo.) GROUPING es una función que se combina con ROLLUP o CUBE y que recibe uno o más campos, e indica si la fila muestra un resumen referido a los campos en cuestión (filas de resumen con esos campos a nulo). Si la fila es un resumen de esos campos lo marca con 1, sino lo marca con 0. Por ejemplo: SELECT N.nif, M.nombre, AVG(valor), GROUPING(N.nif) AS "SW_NIF", GROUPING(M.nombre) AS "SW_MOD" FROM Nota N JOIN Modulo M ON N.idModulo=M.idModulo GROUP BY CUBE (N.nif, M.nombre);

Genera el siguiente resultado:

NIF 11111111A 11111111A 11111111A 22222222B 22222222B 22222222B 33333333C 33333333C 33333333C

NOMBRE Modulo1 Modulo2 Modulo1 Modulo2 Modulo1 Modulo2 Modulo1 Modulo2

NOTA MEDIA 5 6 5,5 4 7 5,5 8 9 8,5 5,6 7,3 6,5

SW_NIF 0 0 0 0 0 0 0 0 0 1 1 1

SW_MOD 0 0 1 0 0 1 0 0 1 0 0 1

Se utiliza sobre todo para preparar una consulta en la creación de informes. GROUPING SETS se trata de una mejora de Oracle 9i que permite realizar varias agrupaciones para la misma consulta. Sintaxis: SELECT... ... GROUP BY GROUPING SETS (listaDeCampos1) [, (lista2)...]

Las listas indican los campos por los que se realiza la agrupación. Por ejemplo, para obtener resúmenes que cuenten las faltas por alumno, módulo y año: SELECT M.nif, F.idModulo, EXTRACT(year FROM fecha) AS "Año", COUNT(*) AS "Nº faltas" FROM Matricula M JOIN Falta F ON M.idMatricula=F.idMatricula

Oracle /99

GROUP BY GROUPING SETS ((M.nif, F.idModulo), (EXTRACT(year FROM fecha)));

Se pueden combinar agrupaciones de diversas formas creando consultas como:

SELECT M.nif, F.idModulo, EXTRACT(year FROM fecha) AS "Año", COUNT(*) AS "Nº faltas" FROM Matricula M JOIN Falta F ON M.idMatricula=F.idMatricula GROUP BY M.nif, ROLLUP(F.idModulo) , CUBE(EXTRACT(year FROM fecha)) ;

Que mostraría un informe espectacular sobre las tablas anteriores. Así como:

SELECT M.nif, F.idModulo, EXTRACT(year FROM fecha) AS "Año", COUNT(*) AS "Nº faltas" FROM Matricula M JOIN Falta F ON M.idMatricula=F.idMatricula GROUP BY GROUPING SETS ( M.nif, EXTRACT(year FROM fecha) ) , GROUPING SETS ( F.idModulo, EXTRACT(year FROM fecha) );

Oracle /100

III. PL/SQL PL/SQL amplia SQL con los elementos característicos de los lenguajes de programación: variables, sentencias de control de flujo, bucles, etc.

1. Estructura del lenguaje PL/SQL Con PL/SQL vamos a poder programar las unidades procedimentales de la base de datos Oracle; éstas son: - Procedimientos almacenados - Funciones - Triggers - Scripts Pero además, PL/SQL nos permite realizar programas sobre las siguientes herramientas de Oracle: - Oracle Forms - Oracle Reports - Oracle Graphics - Oracle Aplication Server 1.1. Fundamentos de PL/SQL. Para programar en PL/SQL es necesario conocer sus fundamentos. PL/SQL no es "case-sensitive", es decir, no diferencia mayúsculas de minúsculas como otros lenguajes de programación como C o Java. Sin embargo, debemos recordar que Oracle es "case-sensitive" en la búsqueda de texto. 1.1.1. Tipos de datos. Los tipos de datos escalares que soporta PL/SQL se muestran a continuación: BINARY_DOUBLE* BINARY_FLOAT* BINARY_INTEGER BOOLEAN CHAR CHARACTER DATE DEC DECIMAL DOUBLE PRECISION

FLOAT INT INTEGER INTERVAL DAY TO SECOND INTERVAL YEAR TO MONTH LONG LONG RAW NATURAL NATURALN NCHAR

NUMBER NUMERIC NVARCHAR2 PLS_INTEGER POSITIVE POSITIVEN RAW REAL ROWID SIGNTYPE

SMALLINT STRING TIMESTAMP TIMESTAMP WITH LOCAL TIME ZONE TIMESTAMP WITH TIME ZONE UROWID VARCHAR VARCHAR2

* Desde Oracle Database 10g, los nuevos tipos de datos BINARY_FLOAT y BINARY_DOUBLE pueden mejorar el rendimiento en aplicaciones que trabajan con número intensivamente, como en procesos de datos científicos. Los tipos de datos compuestos que soporta PL/SQL se muestran a continuación: RECORD

TABLE

VARRAY

Los tipos de datos por referencia que soporta PL/SQL se muestran a continuación: REF CURSOR

REF tipo_objeto

Los tipos de datos largos (LOB) que soporta PL/SQL se muestran a continuación: BFILE

BLOB

CLOB**

NCLOB**

** Podemos convertir implícitamente de CLOB a NCLOB o de NCLOB a CLOB. Sin embargo, ya que puede ser una operación expansiva, podemos ayudarnos de las funciones TO_CLOB y TO_NCLOB. 1.1.2. Comentarios. PL/SQL soporta varios estilos de comentarios, el de línea simple y de multilínea, para lo cual son empleados ciertos caracteres especiales como son: -- Línea simple

Oracle /101

REM Línea simple /* Conjunto de Líneas */

1.2. Estructuras de control en PL/SQL. Dentro de las órdenes de un bloque PL/SQL también existen sentencias de control del flujo del programa. 1.2.1. Bloque condicional «IF». El mandato IF tiene la siguiente sintaxis: IF condición THEN -- hacer algo; [ELSE -- hacer algo;] ENDIF;

Si queremos encadenar varios bloques IF debemos hacerlo de la siguiente manera: IFV=1 THEN -- hacer algo; ELSE IFV=2THEN -- hacer algo; ELSE -- hacer algo; END IF; END IF;

1.2.2. Bloque «CASE». El mandato CASE permite seleccionar entre una secuencia de condiciones, y ejecutar una instrucción correspondiente. El comando CASE evalúa una expresión y la compara con varios valores, o evalúa varias expresiones lógicas y elige la primera verdadera. Un ejemplo del primer caso es el siguiente: n := 1; CASE n WHEN 1 THEN -- hacer algo; WHEN 2 THEN -- hacer algo; WHEN 3 THEN -- hacer algo; ELSE -- hacer algo; END CASE;

Un ejemplo del segunda caso es el siguiente: n := 1; CASE WHEN n=1 THEN -- hacer algo; WHEN n>=2 AND n=4 THEN -- hacer algo; END CASE;

1.2.3. Bucles. Otro tipo de sentencias de control del flujo de ejecución son los bucles, dentro de los cuales podemos encontrar bucles tales como LOOP, FOR y WHILE. La sintaxis de estos bloques de código es la siguiente: LOOP sentencias; ... EXIT [WHEN condición];

Oracle /102

END LOOP;

Y para la orden WHILE tenemos la siguiente sintaxis: WHILE condición LOOP sentencias; ... END LOOP;

Podemos finalizar cualquiera de los bucles con la instrucción EXIT o bien EXIT WHEN condición. 1.2.4. Bucle «FOR». El bucle FOR permite iterar sobre un rango de valores o sobre el resultado de una consulta. El siguiente ejemplo itera sobre los valores del 10 al 1 inclusive en orden decreciente:

El

FOR contador IN REVERSE 1..10 LOOP DBMS_OUTPUT.PUT_LINE( contador ); -- se imprime el valor END LOOP; siguiente ejemplo accede al campo nombre de la tabla Producto y los FOR p IN (SELECT nombre FROM Producto) LOOP DBMS_OUTPUT.PUT_LINE( p.nombre ); -- se imprime el nombre END LOOP;

imprime:

1.2.5. Instrucción «NULL». Algunas cláusulas de PL/SQL, como un bloque IF y otros, deben contener al menos una instrucción ejecutable. Se puede utilizar la instrucción NULL para completar estos bloques sin que hagan nada. Por ejemplo: IF unaCondición THEN -- hacer algo; ELSE NULL; -- no se hace nada END IF;

La instrucción NULL simplemente pasa el control a la siguiente instrucción.

2. Bloques PL/SQL. 2.1. Introducción. Un programa de PL/SQL está compuesto por bloques, teniendo al menos que estar compuesto por un bloque. Los bloques de PL/SQL pueden ser de los siguientes tipos: - Bloques anónimos: aquellos que no tienen un nombre. - Subprogramas: aquellos que se crean como funciones o procedimientos con un nombre. 2.2. Estructura de un Bloque. Los bloques PL/SQL presentan una estructura específica compuesta de tres partes bien diferenciadas: - La sección declarativa, en donde se declaran todas los tipos de datos, constantes y variables que se van a utilizar en la ejecución del bloque. - La sección de ejecución, que incluye las instrucciones a ejecutar en el bloque PL/SQL. - La sección de excepciones, en donde se definen los manejadores de errores que soportará el bloque. Cada una de las partes anteriores se delimita por una palabra reservada, de modo que un bloque PL/SQL se puede representar como sigue: DECLARE | IS | AS /*Parte declarativa*/ BEGIN /*Parte de ejecución*/ EXCEPTION /*Parte de excepciones*/ END;

De las anteriores partes, únicamente la sección de ejecución es obligatoria, quedando delimitada entre las cláusulas BEGIN y END. Veamos un ejemplo de bloque PL/SQL muy genérico. Se trata de un bloque anónimo, es decir, no lo identifica ningún nombre. Los bloques anónimos identifican su parte declarativa con la palabra reservada DECLARE. DECLARE

Oracle /103

/*Parte declarativa*/ nombre_variable DATE; BEGIN /*Parte de ejecución *Este código asigna la fecha actual a la variable identificada por "nombre_variable" */ SELECT SYSDATEINTO nombre_variable FROM DUAL; EXCEPTION /*Parte de excepciones*/ WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Se ha producido un error'); END;

2.3. Sección de declaración de variables. En esta parte se declaran las variables que va a necesitar nuestro programa. Una variable se declara asignándole un nombre o "identificador" seguido del tipo de valor que puede contener. También se declaran cursores, de gran utilidad para la consulta de datos, y excepciones definidas por el usuario. También podemos especificar si una variable será constante, si puede contener valores nulos y asignarle un valor inicial. La sintaxis genérica para la declaración de constantes y variables es: nombre_variable [CONSTANT] tipo_de_dato [NOT NULL] [:=valor_inicial]

Donde:

es el tipo de dato que va a poder almacenar la variable, éste puede ser cualquiera de los tipos soportados por Oracle. CONSTANT indica la definición de una constante, cuyo valor no puede ser modificado. Se debe incluir la inicialización de la constante en su declaración. NOT NULL impide que a una variable se le asigne el valor nulo, y por tanto debe inicializarse a un valor diferente de NULL. La inicialización puede incluir cualquier expresión legal de PL/SQL, que lógicamente debe corresponderse con el tipo del identificador definido. Los tipos escalares incluyen los definidos en SQL, más los tipos VARCHAR y BOOLEAN. Este último puede tomar los valores TRUE, FALSE y NULL, y se suele utilizar para almacenar el resultado de alguna operación lógica. También es posible definir el tipo de una variable o constante, dependiendo del tipo de otro identificador, mediante la utilización de las cláusulas %TYPE y %ROWTYPE. Mediante la primera opción se define una variable o constante escalar, y con la segunda se define una variable de tipo registro, donde el identificador puede ser otra variable de tipo registro o una tabla. Habitualmente se utiliza %TYPE para definir la variable del mismo tipo que tenga definido un campo en una tabla de la base de datos, mientras que %ROWTYPE se utiliza para declarar variables utilizando cursores. Ejemplos de estructura de un bloque anónimo. tipo_de_dato

DECLARE /* Se declara la variable de tipo VARCHAR2(15) identificada por v_location y se le asigna el valor "Granada"*/ v_location VARCHAR2(15) := 'Granada'; /*Se declara la constante de tipo NUMBER identificada por PI y se le asigna el valor 3,1416*/ PI CONSTANT NUMBER := 3.1416; /*Se declara la variable del mismo tipo que tenga el campo nombre de la tabla tabla_empleados * identificada por v_nombre y no se le asigna ningún valor */ v_nombre tabla_empleados.nombre%TYPE; /*Se declara la variable del tipo registro correspondiente a un supuesto cursor, llamado * micursor, identificada por reg_datos */ reg_datos micursor%ROWTYPE; BEGIN /*Parte de ejecución*/ EXCEPTION /*Parte de excepciones*/ END;

Ejemplo de estructura de un subprograma:

CREATE PROCEDURE simple_procedure IS

Oracle /104

/* Se declaran variables */ BEGIN /*Parte de ejecución*/ EXCEPTION /*Parte de excepciones*/ END;

2.4. El paquete «DBMS_OUTPUT». El paquete DBMS_OUTPUT permite enviar mensajes desde un bloque PL (procedimiento, función, paquete o trigger) de la base de datos. Los procedimientos PUT y PUT_LINE de este paquete permiten colocar datos en un búfer que puede ser leído por otro bloque PL, que empleará el procedimiento GET_LINE para recuperar la información. Si no se gestiona la recuperación y presentación de los datos incluidos en el búfer y si la ejecución no se realiza bajo SQL*Plus, los datos son ignorados. El principal interés de este paquete es facilitar la depuración de los programas. Los procedimientos incluidos en este paquete son los siguientes:  DBMS_OUTPUT.DISABLE Desactiva DBMS_OUTPUT y reinicializa el tamaño del búfer al valor por defecto.  DBMS_OUTPUT.ENABLE(buffer_size IN INTEGER DEFAULT 20000) Activa DBMS_OUTPUT y asigna el tamaño del búfer, que puede ser entre 1 y 1000000.  DBMS_OUTPUT.GET_LINE(line OUT VARCHAR2, status OUT INTEGER);

Retorna una única línea del búfer. El siguiente ejemplo muestra como usar este procedimiento:

DECLARE bufer VARCHAR2(100); estado INTEGER; BEGIN DBMS_OUTPUT.PUT_LINE('Esto es'); DBMS_OUTPUT.PUT_LINE('una prueba.'); DBMS_OUTPUT.GET_LINE(bufer, estado); DBMS_OUTPUT.PUT_LINE('Búfer: ' || bufer); -- Búfer: Esto es DBMS_OUTPUT.PUT_LINE('Estado: ' || TO_CHAR(estado)); -- Estado: 0 END; /  DBMS_OUTPUT.GET_LINES(lines OUT CHARARR, numlines IN OUT INTEGER);

Recupera un array de líneas desde el búfer. El siguiente ejemplo muestra cómo usar este procedimiento:

DECLARE outtab DBMS_OUTPUT.CHARARR; -- un array de líneas de texto fetchln INTEGER := 15; BEGIN outtab(1) := 'Esto es una prueba'; outtab(12) := 'de DBMS_OUTPUT.GET_LINES'; DBMS_OUTPUT.PUT_LINE('A: ' || outtab(1)); DBMS_OUTPUT.PUT_LINE('A: ' || outtab(12)); DBMS_OUTPUT.GET_LINES(outtab, fetchln); DBMS_OUTPUT.PUT_LINE(TO_CHAR(fetchln)); -- 2 FOR i IN 1 .. fetchln LOOP DBMS_OUTPUT.PUT_LINE('B: ' || outtab(i)); -- B: A: Esto es una prueba END LOOP; -- B: A: de DBMS_OUTPUT.GET_LINES END; /  DBMS_OUTPUT.GET_LINES( linesOUT DBMSOUTPUT_LINESARRAY,numlines IN OUT INTEGER);

Recupera un array de líneas desde el búfer. El siguiente ejemplo muestra cómo usar este procedimiento: DECLARE lo DBMSOUTPUT_LINESARRAY := DBMSOUTPUT_LINESARRAY(10); fetchln INTEGER := 15; BEGIN lo(1) := 'ABC'; lo.EXTEND;

Oracle /105

lo(2) := 'DEF'; lo.EXTEND; lo(3) := 'GHI'; lo.EXTEND; lo(4) := 'JKL'; lo.EXTEND; lo(5) := 'MNO'; DBMS_OUTPUT.PUT_LINE('A: ' || lo(1)); DBMS_OUTPUT.PUT_LINE('A: ' || lo(2)); DBMS_OUTPUT.PUT_LINE('A: ' || lo(3)); DBMS_OUTPUT.PUT_LINE('A: ' || lo(4)); DBMS_OUTPUT.PUT_LINE('A: ' || lo(5)); DBMS_OUTPUT.GET_LINES(lo, fetchln); DBMS_OUTPUT.PUT_LINE(TO_CHAR(fetchln)); END; /  DBMS_OUTPUT.NEW_LINE;

-- 5

Inserta una marca de fin de línea en el búfer.

 DBMS_OUTPUT.PUT_LINE(a IN VARCHAR2);

Escribe una línea de texto al búfer. Con el procedimiento GET_LINE se puede leer una línea de datos del búfer y con el procedimiento GET_LINES se puede extraer una tabla de filas. Después de leer el búfer, todas las líneas que no se han leído al hacer otra llamada a los procedimientos PUT, PUT_LINE o NEW_LINE se eliminan, con el fin de evitar cualquier posible confusión acerca de los datos. También podemos generar un mensaje hacia consola mediante DBMS_OUTPUT. En consolas de SQL*Plus, previamente debemos activar el depurador activando la variable SERVEROUTPUT. Por ejemplo: SET SERVEROUTPUT ON; BEGIN DBMS_OUTPUT.PUT_LINE ('Hola'); END;

2.5. Asignación de variables. Dentro de un bloque podemos asignar valores a las variables de varias formas: • Directamente mediante el operador de asignación (:=). Por ejemplo: DECLARE Valor NUMBER(38,2); BEGIN Valor := 213.23; END;

• A través de una consulta mediante el operador INTO. Por ejemplo:

DECLARE Nota_minima NUMBER(2,0); Nota_maxima NUMBER(2,0) BEGIN SELECT MIN(valor), MAX(valor) INTO Nota_minima, Nota_maxima FROM Nota; END;

Debemos tener en cuenta que si la consulta no retorna registros o retorna más de un registro se generará una excepción. • A través de una instrucción de borrado mediante RETURNING...INTO. Por ejemplo: DECLARE nota_V NUMBER(2,0); BEGIN DELETE FROM Nota WHERE idNota=9 RETURNING valor INTO nota_V; END;

Si queremos recuperar todos los valores de un registro podemos asignarlos a una variable de tipo registro de la siguiente forma: DECLARE

Oracle /106

fila_notaNota%ROWTYPE; BEGIN SELECT * INTO fila_nota FROM Nota WHERE idNota = 34; DBMS_OUTPUT.PUT_LINE (fila_nota.valor); -- Imprime el valor de nota del registro recuperado END;

3. Excepciones en PL/SQL. 3.1. Manejo de excepciones. En PL/SQL una advertencia o condición de error es llamada una excepción. Las excepciones se controlan dentro de su propio bloque. Cuando ocurre un error se ejecuta la porción del programa marcada por el bloque EXCEPTION, transfiriéndose el control a ese bloque de sentencias. El siguiente ejemplo muestra un bloque de excepciones que captura las excepciones NO_DATA_FOUND y ZERO_DIVIDE. Cualquier otra excepción será capturada en el bloque WHEN OTHERS THEN. DECLARE -- Declaraciones BEGIN -- Ejecución EXCEPTION WHEN NO_DATA_FOUND THEN -- Se ejecuta cuando ocurre una excepción de tipo NO_DATA_FOUND WHEN ZERO_DIVIDE THEN -- Se ejecuta cuando ocurre una excepción de tipo ZERO_DIVIDE WHEN OTHERS THEN -- Se ejecuta cuando ocurre una excepción de un tipo no tratado en los bloques anteriores END;

Si existe un bloque de excepción apropiado para el tipo de excepción se ejecuta dicho bloque. Si no existe un bloque de control de excepciones adecuado al tipo de excepción se ejecutará el bloque de excepción WHEN OTHERS THEN (¡si existe!). WHEN OTHERS debe ser el último manejador de excepciones. Las excepciones pueden ser definidas en forma interna o explícitamente por el usuario. Ejemplos de excepciones definidas en forma interna son la división por cero y la falta de memoria en tiempo de ejecución. Estas mismas condiciones excepcionales tienen sus propio tipos y pueden ser referenciadas por ellos: ZERO_DIVIDE y STORAGE_ERROR. Las excepciones definidas por el usuario deben ser lanzadas explícitamente utilizando la sentencia RAISE. 3.2. Excepciones predefinidas. PL/SQL proporciona un gran número de excepciones predefinidas que permiten controlar las condiciones de error más habituales. Las excepciones predefinidas no necesitan ser declaradas. Simplemente se utilizan cuando éstas son lanzadas por algún error determinado. La siguiente es la lista de las excepciones predeterminadas por PL/SQL y una breve descripción de cuándo son accionadas: Excepción ACCESS_INTO_NULL COLLECTION_IS_NULL CURSOR_ALREADY_OPEN

DUP_VAL_ON_INDEX

INVALID_CURSOR INVALID_NUMBER

Se ejecuta ... El programa intentó asignar valores a los atributos de un objeto no inicializado El programa intentó asignar valores a una tabla anidada aún no inicializada El programa intentó abrir un cursor que ya se encontraba abierto. Recuerde que un cursor de ciclo FOR automáticamente lo abre y ello no se debe especificar con la sentencia OPEN El programa intentó almacenar valores duplicados en una columna que se mantiene con restricción de integridad de un índice de unicidad El programa intentó efectuar una operación no válida sobre un cursor En una sentencia SQL, la conversión de una cadena de caracteres hacia un número falla cuando esa cadena no representa un número válido Oracle /107

SQLCODE -6530 -6531 -6511

-1

-1001 -1722

LOGIN_DENIED

El programa intentó conectarse a Oracle con un nombre de usuario o contraseña inválido Una sentencia SELECT INTO no devolvió valores o el NO_DATA_FOUND programa referenció un elemento no inicializado en una tabla indexada El programa efectuó una llamada a Oracle sin estar conectado NOT_LOGGED_ON PL/SQL tiene un problema interno PROGRAM_ERROR Los elementos de una asignación (el valor a asignar y la variable ROWTYPE_MISMATCH que lo contendrá) tienen tipos incompatibles. También se presenta este error cuando un parámetro pasado a un subprograma no es del tipo esperado El parámetro SELF (el primero que es pasado a un método SELF_IS_NULL MEMBER) es nulo La memoria se terminó o está corrupta STORAGE_ERROR SUBSCRIPT_BEYOND_COUNT El programa está tratando de referenciar un elemento de un arreglo indexado que se encuentra en una posición más grande que el número real de elementos de la colección SUBSCRIPT_OUTSIDE_LIMIT El programa está referenciando un elemento de un arreglo utilizando un número fuera del rango permitido (por ejemplo, el elemento "-1") SYS_INVALID_ROWID La conversión de una cadena de caracteres hacia un tipo ROWID falló porque la cadena no representa un número Se excedió el tiempo máximo de espera por un recurso en TIMEOUT_ON_RESOURCE Oracle Una sentencia SELECT INTO devuelve más de una fila TOO_MANY_ROWS Ocurrió un error aritmético, de conversión o truncamiento. Por VALUE_ERROR ejemplo, sucede cuando se intenta calzar un valor muy grande dentro de una variable más pequeña El programa intentó efectuar una división por cero ZERO_DIVIDE

-1017 100

-1012 -6501 -6504

-30625 -6500 -6533

-6532

-1410 -51 -1422 -6502

-1476

3.3. Excepciones definidas por el usuario. PL/SQL permite al usuario definir sus propias excepciones, las que deberán ser declaradas y lanzadas explícitamente utilizando la sentencia RAISE. Las excepciones deben ser declaradas en el segmento de declaración de un bloque, subprograma o paquete. Se declara una excepción como cualquier otra variable, asignándole el tipo EXCEPTION. Las mismas reglas de alcance que se aplican a las variables también se aplican sobre las excepciones. DECLARE MyExcepcion EXCEPTION; BEGIN -- Algún código que lanza la excepción personalizada EXCEPTION WHEN MyExcepcion THEN -- Se ha capturado la excepción personalizada END;

3.3.1. Reglas de alcance. Una excepción es válida dentro de su ámbito de alcance, es decir, el bloque o programa donde ha sido declarada. Las excepciones predefinidas son siempre válidas. Como las variables, una excepción declarada en un bloque es local a ese bloque y global a todos los subbloques que comprende. 3.3.2. La sentencia «RAISE». La sentencia RAISE permite lanzar una excepción en forma explícita. Es posible utilizar esta sentencia en cualquier lugar que se encuentre dentro del alcance de la excepción. DECLARE -- Declaramos una excepción identificada por VALOR_NEGATIVO VALOR_NEGATIVO EXCEPTION; valor NUMBER; BEGIN

Oracle /108

valor := -1; IF valor < 0 THEN RAISE VALOR_NEGATIVO; END IF; EXCEPTION WHEN VALOR_NEGATIVO THEN DBMS_OUTPUT.PUT_LINE('El valor no puede ser negativo'); END; la sentencia RAISE podemos lanzar una excepción definida

Con por el usuario o predefinida, siendo el comportamiento habitual lanzar excepciones definidas por el usuario. 3.4. Uso de «SQLCODE» y «SQLERRM». Al manejar una excepción es posible usar las funciones predefinidas SQLCODE y SQLERRM para aclarar al usuario la situación de error acontecida. SQLCODE devuelve el número del error de Oracle y un 0 (cero) en caso de éxito al ejecutarse una sentencia SQL. Por otra parte, SQLERRM devuelve el correspondiente mensaje de error. El siguiente ejemplo muestra el uso de estas dos funciones: DECLARE err_num NUMBER; err_msg VARCHAR2(255); result NUMBER; BEGIN SELECT 1/0 INTO result FROM DUAL; EXCEPTION WHEN OTHERS THEN err_num := SQLCODE; err_msg := SQLERRM; DBMS_OUTPUT.PUT_LINE('Error:'||TO_CHAR(err_num)); DBMS_OUTPUT.PUT_LINE(err_msg); END; También es posible entregarle a la función SQLERRM un número

ésta devolverá el mensaje asociado.

negativo que represente un error de Oracle y

DECLARE msg VARCHAR2(255); BEGIN msg := SQLERRM(-1403); DBMS_OUTPUT.PUT_LINE(MSG); END;

3.5. Excepciones personalizadas en PL/SQL. En ocasiones queremos enviar un mensaje de error personalizado al producirse una excepción PL/SQL. Para ello es necesario utilizar la instrucción RAISE_APPLICATION_ERROR. La sintaxis general es la siguiente: RAISE_APPLICATION_ERROR ( error_num , mensaje );

Siendo error_num es un entero negativo comprendido entre -20001 y -20999, y mensaje la descripción del error. 3.6. Propagación de excepciones en PL/SQL. Una de las características más interesantes de las excepciones es la propagación de las mismas. Cuando se lanza una excepción, el control se transfiere hasta la sección EXCEPTION del bloque donde se ha producido la excepción. Entonces se busca un manejador válido de la excepción ( WHEN excepción THEN, WHEN OTHERS THEN) dentro del bloque actual. En el caso de que no se encuentre ningún manejador válido el control del programa se desplaza hasta el bloque EXCEPTION del bloque que ha realizado la llamada PL/SQL.

4. Cursores PL/SQL utiliza habitualmente cursores para gestionar las instrucciones SELECT. Un cursor es un objeto que gestiona el conjunto de registros afectados por una instrucción SQL. Técnicamente, los cursores son Oracle /109

fragmentos de memoria reservados para procesar los resultados de una consulta SELECT. Podemos distinguir dos tipos de cursores: • Cursores implícitos. Este tipo de cursores se utiliza para operaciones SELECT INTO. Normalmente se usan cuando la consulta devuelve un único registro. • Cursores explícitos. Son los cursores que son declarados y controlados por el programador. Se utilizan cuando la consulta devuelve un conjunto de registros. Ocasionalmente también se utilizan en consultas que devuelven un único registro por razones de eficiencia, ya que son más rápidos. 4.1. Cursores implícitos. Los cursores implícitos se referencian con el identificador SQL, y poseen los siguientes atributos: • SQL%ROWCOUNT: cuenta el número de filas que fueron accedidas en la última operación. • SQL%FOUND: Posee el valor TRUE si el atributo SQL%ROWCOUNT es igual o mayor que uno; es decir, que se asigna a valor TRUE si se ha accedido a alguna fila. • SQL%NOTFOUND: Poseerá el valor TRUE si el atributo SQL%FOUND tiene el valor FALSE; es decir, este atributo toma el valor TRUE si en la última operación no se accedió a ninguna fila. • SQL%ISOPEN: este atributo indica si el cursor está abierto. Posee siempre el valor FALSE, porque PL/SQL cierra los cursores implícitos inmediatamente después de ser ejecutados. Como ejemplo de uso de un cursor implícito, el siguiente boque PL/SQL imprime el número de columnas que han sido borradas: DECLARE v_id NUMBER:=605; BEGIN DELETE FROM Nota WHERE idModulo =v_id; DBMS_OUTPUT.PUT_LINE( SQL%ROWCOUNT ); END; operaciones de tipo SELECT, los cursores implícitos

Para sólo pueden devolver una fila, por lo que pueden producirse determinadas excepciones. Las más comunes que se pueden encontrar son NO_DATA_FOUND y TOO_MANY_ROWS. 4.2. Cursores explícitos. Los cursores explícitos son definidos por el usuario, y a la zona de memoria del cursor se le asigna una sentencia SELECT. Para controlar un cursor explícito debemos seguir los siguientes pasos: 1) Declarar el cursor en el bloque declarativo. 2) Abrir el cursor con el comando OPEN. Con ello se cursor se sitúa sobre el primer resultado del comando asociado. 3) Leer la primera fila con el comando FETCH. 4) Comprobar si se ha leído algo, y si es necesario seguir leyendo con FETCH. 5) Cerrar el cursor con el comando CLOSE. La sintaxis de declaración de un cursor, es la siguiente: CURSOR nombreCursor IS sentencia_Select;

Donde sentencia_Select es una sentencia SELECT cualquiera, que puede incluir cualquier tipo de restricción. La sintaxis más básica de los comandos OPEN y CLOSE es: OPEN nombreCursor; CLOSE nombreCursor;

Cuando queramos recuperar información del cursor, deberemos utilizar el comando sintaxis: FETCH nombreCursor INTO variable1, variable2, ...;

FETCH

con la siguiente

Este comando devolverá la fila actual cada vez que sea ejecutado, y avanzará a la siguiente posición dentro del conjunto de filas que devuelve la consulta especificada en la declaración del cursor. El funcionamiento de los atributos de los cursores vistos anteriormente (%ROWCOUNT, %FOUND, %NOTFOUND, %ISOPEN) es similar en los cursores explícitos, solo que el valor de %ISOPEN puede ser TRUE en este caso si el cursor esta abierto. Existe un tipo de bucle específico para cursores, que ejecuta automáticamente las órdenes OPEN, CLOSE y FETCH. Se trata de una variante del bucle FOR, cuya sintaxis es la siguiente: FOR fila IN nombreCursor LOOP -- La variable "fila" es definida automáticamente del tipo de registro del cursor -- y contiene los datos del registro actual

Oracle /110

... END LOOP;

En caso de usar otro tipo de bucle, se debería especificar la condición de salida del mismo (normalmente cuando el cursor haya recorrido todas las filas); en ese caso la condición de salida será: EXIT WHEN nombreCursor%NOTFOUND;

Por ejemplo, veamos dos formas de realizar la misma acción: una mediante el bucle FOR antes mencionado, y otra mediante la forma clásica. Así, mediante la forma clásica, podríamos crear un cursor para mostrar el nif y apellidos de todos los alumnos: DECLARE CURSOR c1 ISSELECT nif, apellidos FROM Alumno; c1_record c1%ROWTYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO c1_record; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE( c1_record.nif ); DBMS_OUTPUT.PUT_LINE( c1_record.apellidos ); END LOOP; CLOSE c1; END;

Sin embargo, esto puede realizarse de forma mucho más sencilla, utilizando el bucle manera:

LOOP

de la siguiente

DECLARE CURSOR c1 ISSELECT nif, apellidos FROM Alumno; BEGIN FOR fila IN c1 LOOP DBMS_OUTPUT.PUT_LINE( fila.nif ); DBMS_OUTPUT.PUT_LINE( fila.apellidos ); END LOOP; END;

Esta misma operación puede realizarse mediante un cursor implícito: BEGIN FOR fila IN (SELECT nif, apellidos FROM Alumno ) LOOP DBMS_OUTPUT.PUT_LINE( fila.nif ); DBMS_OUTPUT.PUT_LINE( fila.apellidos ); END LOOP; END;

4.3. Cursores con parámetros. Los cursores explícitos pueden incluir parámetros para poder trabajar con datos que son establecidos después de la declaración del cursor. Por ejemplo, el siguiente código crea un cursor para recorrer los registros de alumnos de una localidad determinada: DECLARE CURSOR c1 (p_localidadVARCHAR2) IS SELECT nif, apellidos FROM Alumno WHERE localidad=p_localidad; BEGIN FOR fila IN c1 ('Madrid')LOOP DBMS_OUTPUT.PUT_LINE( fila.nif ); DBMS_OUTPUT.PUT_LINE( fila.apellidos ); END LOOP; END; valor del parámetro es pasado en este caso al referenciar el cursor en la instrucción FOR. En otros casos

El pasará el parámetro al abrir el cursor con la instrucción OPEN:

se

OPEN c1('Madrid');

4.4. Cursores de actualización. Se utilizan los cursores de actualización para determinar los registros afectados por una operación Para ello, tanto el cursor como el comando UPDATE deben utilizar la misma tabla base. Oracle /111

UPDATE.

Los cursores de actualización se declaran igual que los cursores explícitos, añadiendo la sentencia SELECT.

FOR UPDATE

al final de

CURSOR nombre_cursor IS instrucción_SELECT FOR UPDATE

Para utilizar este tipo de cursor hay que ejecutar una sentencia CURRENT OF nombre_cursor». La sintaxis es:

UPDATE

especificando la cláusula «WHERE

UPDATE nombre_tabla SET campo_1 = valor_1 [, campo_2 = valor_2] WHERE CURRENT OF nombre_cursor

El siguiente ejemplo muestra el uso de un cursor de actualización para actualizar las notas de los alumnos de Madrid, incrementándolas en una unidad: DECLARE CURSOR c1 IS SELECT idNota FROM Nota WHERE nif IN (SELECT nif FROM Alumno WHERE localidad='Madrid') FOR UPDATE; v_idNota.idNota%TYPE; BEGIN OPEN c1; FETCH c1 INTO v_id; WHILE c1%FOUND LOOP UPDATE NotaSET valor = valor + 1 WHERE CURRENT OF c1; FETCH c1 INTO v_id; END LOOP; CLOSE c1; COMMIT; END;

Cuando trabajamos con cursores de actualización debemos tener en cuenta que generan bloqueos en la base de datos.

5. Subprogramas en PL/SQL Las reglas del negocio sofisticadas y la lógica de la aplicación pueden ser almacenadas como procedimientos y funciones dentro de Oracle. Los procedimientos almacenados (grupos de SQL, PL/SQL y comandos Java) permiten mover código que fuerza las reglas del negocio de nuestras aplicaciones a la base de datos. Como resultado, el código será guardado una vez para ser usado por varias aplicaciones. Con este soporte de subprogramas almacenados por parte de Oracle, el código dentro de nuestras aplicaciones puede ser más consistente y fácil de mantener. Además, podemos agrupar subprogramas, variables y tipos personalizados de PL/SQL dentro de paquetes. Podemos experimentar beneficios de rendimiento usando procedimientos, por dos razones: • El procesamiento de reglas del negocio complejas puede ser realizadas dentro de la base de datos (y por tanto por el servidor). En el servidor cliente o en las aplicaciones de la capa intermedia, al mover procesamientos complejos desde la aplicación a la base de datos, se puede mejorar significativamente el rendimiento. • Ya que el código del procedimiento es almacenado dentro de la base de datos y es limpiamente estático, podemos beneficiarnos de reutilizar las mismas consultas dentro de la base de datos. El área compartida en el SGA almacenará las versiones parseadas de los comandos ejecutados. Por lo tanto, la segunda vez que un comando es ejecutado, se puede tomar la ventaja de que ha sido parseado previamente, aumentando así el rendimiento de la ejecución del procedimiento. Además de estas dos ventajas, nuestro esfuerzo de desarrollo puede también beneficiarse. Las reglas del negocio consolidadas dentro de la base de datos no necesitan ser escritas en cada aplicación, con lo cual se ahorra tiempo en la creación de la aplicación y se simplifica su proceso de mantenimiento. 5.1. Permisos requeridos. Para crear un objeto procedimental debemos tener el permiso de sistema CREATE PROCEDURE. Si el objeto procedimental es usado en otro esquema entonces debemos tener el permiso de sistema CREATE ANY PROCEDURE. Oracle /112

5.1.1. Ejecución de procedimientos. Una vez que el objeto procedimental ha sido creado podemos ejecutarlo. Cuando un procedimiento es ejecutado podemos confiar en los permisos sobre tablas de sus propietarios o podemos confiar en los permisos del usuario que lo está ejecutando. Cuando un procedimiento es creado usando los permisos de su definidor, un usuario que ejecute el procedimiento no necesita que le concedan permisos sobre las tablas a las que accede el procedimiento. Si un procedimiento confía en los derechos del invocador, el usuario debe tener acceso a todos los objetos accedidos por el procedimiento. Para permitir a otros usuarios ejecutar nuestros objetos procedimentales, debemos concederles el permiso EXECUTE sobre el objeto, tal como se muestra a continuación: GRANT EXECUTE ON un_precedimiento TO un_usuario;

Si no concedemos el permiso EXECUTE al usuario, debe tener el permiso de sistema EXECUTE ANY PROCEDURE para poder ejecutar el procedimiento. 5.1.2. Permisos requeridos sobre tablas. Los objetos procedimentales pueden referenciar tablas. Para que estos objetos se ejecuten apropiadamente, el propietario del procedimiento, paquete o función debe tener permisos sobre las tablas que usa. A menos que estemos usando derechos de invocador, el usuario que esté ejecutando el objeto procedimental no necesita permisos sobre las tablas subyacentes. Nota. Los permisos necesarios para procedimientos, paquetes y funciones no pueden venir de roles; deben concederse directamente por el propietario del objeto. 5.2. Procedimientos, funciones y paquetes. Los procedimientos no retornan un valor en su llamada. Las funciones pueden retornar un valor en su llamada y pueden ser usadas directamente en consultas. El valor de una función es retornado a través del uso de la palabra clave RETURN dentro de la función. Los paquetes son grupos de procedimientos, funciones, variables y comandos SQL agrupados dentro de una simple unidad. Para ejecutar un procedimiento dentro de un paquete debemos primero identificar el nombre del paquete y después el nombre del procedimiento, tal como se muestra a continuación: EXECUTE PAQUETE_LIBRO.NUEVO_LIBRO('Un libro');

Aquí, el procedimiento NUEVO_LIBRO dentro del paquete PAQUETE_LIBRO es ejecutado. Los paquetes permiten varios procedimientos que usan las mismas variables y cursores. Los procedimientos dentro de los paquetes pueden ser públicos o privados, en cuyo caso sólo son accesibles dentro del código del paquete. 5.3. Procedimientos almacenados. Un procedimiento es un subprograma que ejecuta una acción específica y que no devuelve ningún valor en su llamada. Un procedimiento tiene un nombre, un conjunto de parámetros (opcional) y un bloque de código. La sintaxis de un procedimiento almacenado es la siguiente: CREATE [OR REPLACE]PROCEDURE nombre_procedimiento [(parametro1 [IN|OUT|IN OUT] tipo, parametro2 [IN|OUT|IN OUT] tipo, ...)] IS -- Declaración de variables locales BEGIN -- Sentencias [EXCEPTION] -- Sentencias de control de excepción END [nombre_procedimiento]; uso de OR REPLACE permite sobrescribir un procedimiento existente. Si se omite, y el procedimiento

El existe, se producirá un error al ejecutar el comando CREATE PROCEDURE. La sintaxis es muy parecida a la de un bloque anónimo, salvo porque se reemplaza la sección DECLARE por la secuencia PROCEDURE ... IS en la especificación del procedimiento. Debemos especificar el tipo de datos de cada parámetro. Al especificar el tipo de dato del parámetro no debemos especificar la longitud del tipo. Los parámetros pueden ser de entrada ( IN), de salida (OUT) o de entrada salida (IN OUT). El valor por defecto es IN, y se toma ese valor en caso de que no especifiquemos nada. Por ejemplo, el siguiente procedimiento actualiza una nota para un alumno y módulo determinados: CREATE OR REPLACEPROCEDURE

Oracle /113

Actualiza_Nota(pNif IN VARCHAR2, pIdModulo NUMBER, pNota NUMBER) IS -- Declaración de variables locales BEGIN UPDATE Nota SET valor = pNota WHERE nif=pNif AND idModulo=pIdModulo; END Actualiza_Nota;

También podemos asignar un valor por defecto a los parámetros, utilizando la cláusula operador de asignación (:=) .

DEFAULT

o el

CREATE PROCEDURE Actualiza_Nota(pNif IN VARCHAR2, pIdModulo NUMBER, pNota NUMBER DEFAULT 5) ...

Los parámetros con valores por defecto deben situarse al final de la lista de parámetros. Una vez creado y compilado el procedimiento almacenado podemos ejecutarlo. Si el sistema nos indica que el procedimiento se ha creado con errores de compilación podemos ver estos errores de compilación con la orden SHOW ERRORS en SQL *Plus. Existen dos formas de pasar argumentos a un procedimiento almacenado a la hora de ejecutarlo (en realidad es válido para cualquier subprograma). Éstas son: ▪ Notación posicional: Se pasan los valores de los parámetros en el mismo orden en que el PROCEDURE los define. BEGIN Actualiza_Nota('2222222B', 1,8); COMMIT; END;

▪ Notación nominal: Se pasan los valores en cualquier orden nombrando explícitamente el parámetro. BEGIN Actualiza_Nota(pNif =>'2222222B',pIdModulo => 1, pNota =>8); COMMIT; END;

5.4. Funciones en PL/SQL. Una función es un subprograma que devuelve un valor. La sintaxis para construir funciones es la siguiente: CREATE [OR REPLACE]FUNCTION nombre_función [(parámetro1 IN tipo, parámetro2 IN tipo, ...)] RETURN Tipo_de_retorno IS result Tipo_de_retorno; BEGIN RETURN result; [EXCEPTION] -- Sentencias de control de excepción END [nombre_función]; El uso de OR REPLACE permite sobrescribir una función existente. un error el ejecutar el comando CREATE FUNCTION.

Si se omite, y la función existe, se producirá

La sintaxis de los parámetros es la misma que en los procedimientos almacenado. Como ejemplo, la siguiente función retorna la nota media de un alumno:

CREATE OR REPLACEFUNCTION fn_NotaMedia (pNif VARCHAR2)RETURN NUMBER IS result NUMBER; BEGIN SELECT AVG(valor) INTO result FROM Nota WHERE nif = pNif; RETURN result; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN 0; END ;

Si el sistema nos indica que la función se ha creado con errores de compilación podemos ver estos errores de compilación con la orden SHOW ERRORS en SQL*Plus. Una vez creada y compilada la función podemos ejecutarla de la siguiente forma: DECLARE

Oracle /114

notaMedia NUMBER; BEGIN notaMedia := fn_NotaMedia ('2222222B'); END;

Las funciones pueden utilizarse en sentencias SQL de manipulación de datos ( SELECT, DELETE) siempre y cuando no realicen operaciones de actualización en su código:

UPDATE, INSERT

y

SELECT nif, nombre, apellidos, fn_NotaMedia( nif )FROM Alumno;

5.5. Subprogramas en bloques procedimentales. Dentro de la sección declarativa de bloque anónimo, un procedimiento o una función almacenada podemos declarar subfunciones y subprocedimientos e invocarlos desde el bloque de ejecución del script. Este tipo de subprogramas son menos conocidos que los procedimientos almacenados, funciones y triggers, pero son enormemente útiles. El siguiente ejemplo declara y ejecuta utiliza una subfunción (fn_multiplica_x2) en un bloque anónimo DECLARE idx NUMBER; /* Se declara la subfunción */ FUNCTION fn_multiplica_x2(num NUMBER) RETURN NUMBER IS result NUMBER; BEGIN result := num *2; RETURN result; END fn_multiplica_x2; BEGIN FOR idx IN 1..10 LOOP DBMS_OUTPUT.PUT_LINE ('Llamada a la función ... '||TO_CHAR(fn_multiplica_x2(idx))); END LOOP; END; Nótese que se utiliza la función TO_CHAR para convertir el resultado de la función fn_multiplica_x2

(numérico)

en alfanumérico y poder mostrar el resultado por pantalla. 5.6. Depurando procedimientos. El comando SHOW ERRORS de SQL*Plus muestra todos los errores asociados con la más reciente creación de objetos procedimentales. Este comando verifica la vista USER_ERRORS del diccionario de datos para mostrar los errores asociados con la compilación del procedimiento. SHOW ERRORS muestra la línea y número de columna de cada error, así como el texto del mensaje de error. Para ver errores asociados con procedimientos creados previamente podemos consultar USER_ERRORS directamente, tal como se muestra a continuación. Este ejemplo consulta USER_ERRORS por mensajes de error encontrados durante la creación de una función Gastos_atrasados. Si un error es encontrado, las líneas en el código que provocan condiciones de error son retornadas por la consulta. SELECT

Line, /* Número de línea del error. */ Position, /* Número de columna del error dentro de la línea.*/ Text /* Texto del error.*/ FROM USER_ERRORS WHERE Name = 'GASTOS_ATRASADOS' AND TYPE = 'FUNCTION' ORDER BY Sequence; Valores válidos para la columna Type son VIEW, PROCEDURE, PACKAGE, FUNCTION, y PACKAGE BODY. Los otros dos niveles del diccionario de datos (ALL y DBA) pueden ser usados para recuperar información acerca

de errores involucrados con los objetos procedimiento. 5.7. Paquetes en PL/SQL. Un paquete es una estructura que agrupa objetos de PL/SQL compilados (procedimientos, funciones, variables, tipos, etc.) en la base de datos. Esto nos permite agrupar la funcionalidad de los procesos en programas. Oracle /115

Lo primero que debemos tener en cuenta es que los paquetes están formados por dos partes: la especificación y el cuerpo. La especificación del un paquete y su cuerpo se crean por separado. 5.7.1. Especificación de un paquete. La especificación de un paquete es la interfaz pública del paquete que será usada por las aplicaciones. En ella es posible declarar los tipos, variables, constantes, excepciones, cursores y subprogramas disponibles para su uso posterior desde fuera del paquete. En la especificación del paquete sólo se declaran los objetos (procedimientos, funciones, variables, ...), no se implementa el código. Los objetos declarados en la especificación del paquete son accesibles desde fuera del paquete por otro script de PL/SQL o programa. Haciendo una analogía con el mundo de C, la especificación es como el archivo de cabecera de un programa en C. Para crear la especificación de un paquete la sintaxis general es la siguiente: CREATE [OR REPLACE] PACKAGE NombrePaquete IS -- Declaraciones de tipos y registros públicas TYPE NombreTipo IS TipoDeDato; -- Declaraciones de variables y constantes públicas -- También podemos declarar cursores NombreConstante CONSTANT TipoDato := valor; NombreVariable TipoDato; -- Declaraciones de procedimientos y funciones públicas FUNCTION NombreFunción (Parámetro TipoDato , ...) RETURN TipoDato; PROCEDURE NombreProcedimiento (Parámetro TipoDato , ...); END NombrePaquete;

5.7.2. Cuerpo de un paquete. El cuerpo de un paquete es la implementación privada del paquete. El cuerpo del paquete debe implementar lo que se declaró inicialmente en la especificación. En el cuerpo de un paquete podemos declarar nuevos subprogramas y tipos, pero estos serán privados para el propio paquete. La sintaxis general para crear el cuerpo de un paquete es muy parecida al de la especificación, tan solo se añade la palabra clave BODY, y se implementa el código de los subprogramas. CREATE [OR REPLACE] PACKAGE BODY NombrePaquete IS -- Declaraciones de tipos y registros privados TYPE NombreTipo IS TipoDato; -- Declaraciones de variables y constantes privadas -- También podemos declarar cursores NombreConstante CONSTANT TipoDato := valor; NombreVariable TipoDato; -- Implementación de procedimientos y funciones FUNCTION NombreFunción(Parámetro TipoDato , ...)RETURN TipoDato IS -- Variables locales de la función BEGIN -- Implementación de la función END; PROCEDURE NombreProcedimiento (Parámetro TipoDato , ...) IS -- Variables locales de la función BEGIN -- Implementación de procedimiento END; END pkgName; siguiente ejemplo crea un paquete llamado PKG_ACADEMIA que incluye

El funciones y procedimientos para procesar los datos de alumnos, matrículas, notas, etc. Para crear la especificación del paquete: CREATE OR REPLACE PACKAGE PKG_ACADEMIA IS -- Declaraciones de tipos y registros públicas TYPE Reg_ResumenNotas IS RECORD (

Oracle /116

minima NUMBER(2,0), maxima NUMBER(2,0), media NUMBER(2,2)

); -- Declaraciones de variables y constantes públicas APROBADO CONSTANT INTEGER := 5; SOBRESALIENTE CONSTANT INTEGER := 10; ERROR_NO_EXISTE_ALUMNO EXCEPTION; -- Declaraciones de procedimientos y funciones públicas PROCEDURE MatriculaAlumno (pNif VARCHAR2, pNombre VARCHAR2, pApellidos VARCHAR2, pLocalidad VARCHAR2) ; FUNCTION fn_Obtener_Resumen_Notas (pNif VARCHAR2) RETURN Reg_ResumeNotas; END PKG_ACADEMIA;

Aquí sólo hemos declarado las variables y constantes, y prototipado las funciones y procedimientos públicos. Es en el cuerpo del paquete donde debemos escribir el código de los subprogramas MatriculaAlumno y fn_Obtener_Resumen_Notas. CREATE PACKAGE BODY PKG_ACADEMIA IS -- Función privada para saber si existe un alumno FUNCTION fn_Existe_Alumno (pNif VARCHAR2) RETURN INTEGER IS n INTEGER; BEGIN SELECT COUNT(*) INTO n FROM Alumno WHERE nif = pNif; RETURN n; END; -- Función pública FUNCTION fn_Obtener_Resumen_Notas (pNif VARCHAR2) RETURN Reg_ResumeNotas IS result Reg_ResumenNotas; BEGIN IF fn_Existe_Alumno(pNif) THEN SELECT MIN(valor), MAX(valor), AVG(valor) INTO result FROM Nota WHERE nif = pNif; RETURN result; ELSE RAISE ERROR_NO_EXISTE_ALUMNO; END IF; END; -- Procedimiento público PROCEDURE MatriculaAlumno (pNif VARCHAR2, pNombre VARCHAR2, pApellidos VARCHAR2, pLocalidad VARCHAR2) IS maxId INTEGER; BEGIN IF NOT fn_Existe_Alumno(pNif) THEN INSERT INTO Alumno(nif,nombre,apellidos,localidad) VALUES (pNif,pNombre,pApellidos,pLocalidad); END IF; SELECT NVL(MAX(idMatricula, 0) INTO maxId FROM Matricula; INSERT INTO Matricula(idMatricula,nif,año) VALUES (maxId+1,pNif,EXTRACT(year FROM SYSDATE)); END; END PKG_ACADEMIA;

Es posible modificar el cuerpo de un paquete sin necesidad de alterar por ello la especificación del mismo. Los paquetes pueden llegar a ser programas muy complejos y suelen almacenar gran parte de la lógica de negocio. 5.7.3. Inicialización de paquetes. Los paquetes pueden incluir código que puede ser ejecutado la primera vez que un usuario ejecuta una función o procedimiento del paquete durante cada sesión. En el siguiente ejemplo, el paquete GESTOR_LIBRO incluye en su cuerpo un comando SQL que registra el nombre del usuario y el tiempo de la primera vez que ejecuta un componente del usuario dentro de la sesión. Se incluyen dos variables dentro del paquete para Oracle /117

registrar estos valores. Al ser declaradas dentro del cuerpo del paquete no están disponibles para el público. El código de inicialización del paquete se muestra resaltado en el siguiente listado: CREATE OR REPLACE PACKAGE BODY GESTOR_LIBRO AS Nombre_usuario VARCHAR2(30); Fecha_entrada DATE; /* Definición de otros componentes del paquete */ BEGIN SELECT USER, SYSDATE INTO Nombre_Usuario, Fecha_Entrada FROM DUAL; END GESTOR_LIBRO; /

Nota. El código que será ejecutado la primera vez que un componente del paquete es ejecutado es almacenado en su propio bloque PL/SQL al final del cuerpo del paquete. No tiene su propia cláusula END, sino que usa la cláusula END del paquete. 5.8. Viendo el código fuente de objetos procedimentales. El código fuente de procedimientos, funciones y paquetes, y cuerpos de paquete puede ser consultado con las siguientes vistas del diccionario de datos: USER_SOURCE Para objetos propiedad del usuario. ALL_SOURCE Para objetos propiedad del usuario o a los cuales el usuario tiene permisos de acceso. DBA_SOURCE Para todos los objetos de la base de datos. Se selecciona información desde la vista USER_SOURCE mediante una consulta similar a la mostrada a continuación. En este ejemplo se selecciona la columna Text y se ordena por el número de línea. El nombre y tipo del objeto se utilizan para indicar qué código fuente será mostrado. El siguiente ejemplo usa un procedimiento llamado Nuevo_Libro. SELECT Text FROM USER_SOURCE WHERE Name = 'NUEVO_LIBRO' AND Type = 'PROCEDURE' ORDER BY Line;

TEXT ------------------------------------------------------------------------------------------PROCEDURE NUEVO_LIBRO (PTitulo IN VARCHAR2, PEditor IN VARCHAR2, PCategoria IN VARCHAR2) AS BEGIN INSERT INTO BOOKSHELF (Titulo, Editor, Categoria) VALUES (PTitulo, PEditor, PCategoria); DELETE FROM PEDIDO_LIBRO WHERE Titulo = PTitulo; END;

Como se ve en este ejemplo, la vista USER_SOURCE contiene un registro por cada línea del procedimiento NUEVO_LIBRO. La secuencia de la línea es mantenida por la columna Line; por lo tanto, la columna Line debería ser usada para ordenar el resultado. Valores válidos para la columna Type son PROCEDURE, FUNCTION, PACKAGE, PACKAGE BODY, JAVA SOURCE, TYPE, y TYPE BODY. 5.9. Compilando procedimientos, funciones y paquetes. Oracle compila los objetos procedimentales cuando son creados. Sin embargo, los objetos procedimentales pueden ser inválidos si los objetos de la base de datos que referencian cambian. La siguiente vez que los objetos procedimentales sean ejecutados serán recompilados por la base de datos. Podemos evitar esta compilación en tiempo de ejecución (y la degradación de rendimiento que esto puede causar) recompilando explícitamente los procedimientos, funciones y paquetes. Para recompilar un procedimiento se usa el comando ALTER PROCEDURE, tal como se muestra a continuación. La cláusula Oracle /118

COMPILE es una opción sólo válida par este comando. ALTER PROCEDURE Nuevo_Libro COMPILE;

Para recompilar un procedimiento debemos ser el propietario o tener el permiso de sistema PROCEDURE. Para recompilar una función se usa el comando ALTER FUNCTION con la cláusula COMPILE:

ALTER ANY

ALTER FUNCTION Libros_Cambiados COMPILE;

Para recompilar una función debemos ser el propietario o tener el permiso de sistema ALTER ANY PROCEDURE. Cuando recompilamos paquetes podemos recompilar tanto la especificación del paquete como su cuerpo, o bien sólo el cuerpo del paquete. Por defecto se recompilan tanto la especificación del paquete como su cuerpo. No podemos usar los comandos ALTER FUNCTION o ALTER PROCEDURE para recompilar las funciones y procedimientos dentro del paquete. Si sólo ha cambiado el código de alguna función o procedimiento dentro del paquete sólo es necesario recompilar el cuerpo del paquete. Para ello se utiliza la siguiente sintaxis: ALTER PACKAGE [usuario.]nombre_del_paquete COMPILE [DEBUG] [PACKAGE | BODY | SPECIFICATION];

Para recompilar un paquete se usa el comando precedente con la cláusula COMPILE, tal como sigue: ALTER PACKAGE GESTOR_LIBRO COMPILE;

Para recompilar un paquete debemos ser su propietario o debemos tener el permiso de sistema ALTER ANY PROCEDURE. Ya que no se especificó PACKAGE ni BODY en el ejemplo precedente, se usó por defecto PACKAGE, con lo cual se recompiló tanto la especificación como el cuerpo del paquete.

6. Transacciones No todas las operaciones SQL son transaccionales. Sólo son transaccionales las operaciones correspondientes al DML, es decir, sentencias SELECT, INSERT, UPDATE y DELETE. Una transacción comienza con la primera instrucción DML que se ejecute y finaliza con alguna de estas circunstancias: - Una operación COMMIT o ROLLBACK. - Una instrucción DDL (como ALTER TABLE, por ejemplo). - Una instrucción DCL (como GRANT). - El usuario abandona la sesión. - Se produce una caída del sistema. Hay que tener en cuenta que cualquier instrucción DDL o DCL da lugar a un COMMIT implícito; es decir, todas las instrucciones DML ejecutadas hasta ese instante pasan a ser definitivas. 6.1. Estado de los datos durante la transacción. Si se inicia una transacción usando comandos DML hay que tener en cuenta que: - Se puede volver a la instrucción anterior a la transacción cuando se desee. - Las instrucciones de consulta SELECT realizadas por el usuario que inició la transacción muestran los datos ya modificados por las instrucciones DML. - El resto de usuarios ven los datos tal cual estaban antes de la transacción; de hecho, los registros afectados por la transacción aparecen bloqueados hasta que la transacción finalice. Esos usuarios no podrán modificar los valores de dichos registros. Tras la transacción todos los usuarios ven los datos tal cual quedan tras el fin de la transacción. Los bloqueos son liberados y los puntos de ruptura borrados. 6.2. Control de transacciones en PL/SQL. Para confirmar una transacción se utiliza la sentencia COMMIT. Cuando realizamos un COMMIT los cambios se escriben en la base de datos. Para deshacer una transacción se utiliza la sentencia ROLLBACK. Cuando realizamos un ROLLBACK se deshacen todas las modificaciones realizadas por la transacción en la base de datos que todavía no han sido confirmadas, quedando la base de datos en el mismo estado que antes de iniciarse la transacción. En una transacción los datos modificados no son visibles por el resto de usuarios hasta que se confirme la transacción. Un abandono de sesión incorrecto o un problema de comunicación o de caída del sistema dan lugar a un ROLLBACK implícito. El siguiente ejemplo muestra una supuesta transacción bancaria: DECLARE importe NUMBER; ctaOrigen VARCHAR2(23);

Oracle /119

ctaDestino VARCHAR2(23); BEGIN importe := 100; ctaOrigen:= '2530 10 2000 1234567890'; ctaDestino := '2532 10 2010 0987654321'; UPDATE CUENTAS SET SALDO = SALDO – importe WHERE CUENTA = ctaOrigen; UPDATE CUENTAS SET SALDO = SALDO + importe WHERE CUENTA = ctaDestino; INSERT INTO MOVIMIENTOS (CUENTA_ORIGEN, CUENTA_DESTINO,IMPORTE, FECHA_MOVIMIENTO) VALUES (ctaOrigen, ctaDestino, importe*(-1), SYSDATE); INSERT INTO MOVIMIENTOS (CUENTA_ORIGEN, CUENTA_DESTINO,IMPORTE, FECHA_MOVIMIENTO) VALUES (ctaDestino,ctaOrigen, importe, SYSDATE); COMMIT; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Error en la transacción:'||SQLERRM); DBMS_OUTPUT.PUT_LINE('Se deshacen las modificaciones); ROLLBACK; END;

Si alguna de las tablas afectadas por la transacción tiene triggers, las operaciones que realiza el trigger están dentro del ámbito de la transacción, y son confirmadas o deshechas conjuntamente con la transacción. Durante la ejecución de una transacción, una segunda transacción no podrá ver los cambios realizados por la primera transacción hasta que éstos se confirmen. Oracle es completamente transaccional. Siempre debemos especificar si queremos deshacer o confirmar la transacción. 6.3. Puntos de ruptura. La instrucción SAVEPOINT permite establecer un punto de ruptura en una transacción. El problema de la combinación ROLLBACK/COMMIT es que un COMMIT acepta todo y un ROLLBACK anula todo. SAVEPOINT permite señalar un punto intermedio entre el inicio de la transacción y la situación actual. Su sintaxis es: ...instrucciones DML... SAVEPOINT nombreSavepoint; ....instrucciones DML...

Para regresar a un punto de ruptura concreto se utiliza el comando: ROLLBACK TO SAVEPOINT nombreSavepoint;

Cuando se vuelve a un punto marcado, las instrucciones que siguieron a esa marca se anulan definitivamente. Debemos tener en cuenta que si realizamos un ROOLBACK TO SAVEPOINT a un punto de ruptura al cual se ha aplicado un COMMIT, se producirá una excepción. Por ejemplo, el siguiente código producirá una excepción: BEGIN SAVEPOINT SP1; DELETE FROM Alumno; COMMIT; ROLLBACK TO SAVEPOINT SP1; END;

-- Se lanza una excepción.

6.4. Transacciones autónomas En ocasiones es necesario que los datos escritos por parte de una transacción sean persistentes, pero sin afectar a la persistencia del resto de la transacción. PL/SQL permite marcar un bloque con PRAGMA AUTONOMOUS_TRANSACTION. Con esta directiva marcamos un subprograma para que se comporte como una transacción diferente a la del proceso principal, llevando el control de COMMIT o ROLLBACK independientemente. Obsérvese el siguiente ejemplo. Primero creamos un procedimiento y lo marcamos con PRAGMA AUTONOMOUS_TRANSACTION. CREATE OR REPLACE PROCEDURE Grabar_Log(descripcion VARCHAR2) IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO LOG_APLICACION (CO_ERROR, DESCRIPICION, FX_ERROR) VALUES (SQ_ERROR.NEXTVAL, descripcion, SYSDATE);

Oracle /120

COMMIT; -- Este commit solo afecta a la transacción autónoma END ; Cualquier COMMIT o ROLLBACK realizado dentro un subprograma

autónomo sólo afectará a las operaciones realizadas dentro del subrpograma, y nunca a cualquier operación previa pendiente de confirmar o rechazar. A continuación utilizamos el procedimiento desde un bloque de PL/SQL: DECLARE v_idModulo.idModulo%TYPE; BEGIN v_id := 99; INSERT INTO Modulo (idModulo, nombre) VALUES (v_id, 'Modulo ' || v_id); COMMIT; EXCEPTION WHEN OTHERS THEN Grabar_Log(SQLERRM); ROLLBACK; /* Los datos grabados por "Grabar_Log" se escriben en la base de datos a pesar del ROLLBACK, * ya que el procedimiento autónomo contiene un COMMIT. * Sin embargo, dicho COMMIT no confirma la operación INSERT previa. */ END;

Es muy común que, por ejemplo, en caso de que se produzca algún tipo de error queramos insertar un registro en una tabla de log con el error que se ha producido y hacer ROLLBACK de la transacción. Pero si hacemos ROLLBACK de la transacción también lo hacemos de la inserción del log.

7. Triggers Un trigger define una acción que la base de datos debe realizar cuando ocurre algún evento. Se pueden usar los triggers para aplicar integridad referencial adicional, forzar reglas del negocio complejas, o auditar cambios en los datos. El código dentro de un trigger, llamado el cuerpo del trigger, se escribe con un bloque PL/SQL. La ejecución del trigger es transparente para el usuario. Los triggers son ejecutados por la base de datos cuando determinados tipos de comandos se ejecutan desde aplicaciones clientes. Estos comandos pueden incluir inserciones, actualizaciones y borrados, operaciones DDL y operaciones DCL. La actualización de columnas específicas puede también ser usada como eventos para lanzar triggers, así como inicios de sesión y paradas en la base de datos. 7.1. Permisos requeridos. Para crear un trigger sobre una tabla debemos ser capaces de modificar dicha tabla. Por lo tanto, debemos o bien ser propietarios de la tabla o tener el permiso ALTER para la tabla, o tener el permiso de sistema ALTER ANY TABLE. Además, debemos tener el permiso de sistema CREATE TRIGGER; para crear triggers en otros esquemas debemos tener el permiso de sistema CREATE ANY TRIGGER. El permiso CREATE TRIGGER es parte del rol RESOURCE proporcionado con Oracle. Para modificar un trigger debemos ser propietarios del mismo o tener el permiso de sistema ALTER ANY TRIGGER. Podemos también habilitar o deshabilitar triggers modificando las tablas en las que está basado, lo cual requiere que tengamos el permiso ALTER para la tabla o el permiso de sistema ALTER ANY TABLE. Para crear un trigger sobre un evento del nivel de base de datos debemos tener el permiso de sistema ADMINISTER DATABASETRIGGER. Los triggers pueden referenciar otras tablas además de la que produce el evento. Por ejemplo, si usamos triggers para auditar cambios de datos en la tabla LIBRO, entonces podemos insertar un registro dentro de una tabla diferente (como LIBRO_AUDIT) cada vez que un registro es cambiado en LIBRO. Para hacer esto necesitamos tener el permiso para insertar dentro de LIBRO_AUDIT. Nota. Los permisos necesarios para desencadenar transacciones no pueden venir de roles; deben concederse directamente por el creador del trigger. 7.2. Tipos de triggers. Se determina el tipo de un trigger por el tipo de transacción desencadenada y por el nivel en el cual el trigger es ejecutado.

Oracle /121

7.2.1. Triggers al nivel de fila. Los triggers al nivel de fila se ejecutan para cada fila afectada por un comando DML. Para el ejemplo de auditoria de la tabla LIBRO, cada fila que es cambiada en la tabla LIBRO puede ser procesada por el trigger. Los triggers a nivel de fila son los más comunes; se usan normalmente en aplicaciones de auditoría de datos. También son normalmente usados para sincronización de datos. Las vistas materializadas usan internamente triggers a nivel de fila para estos propósitos. Los triggers a nivel de fila se crean usando la cláusula FOR EACH ROW en el comando CREATE TRIGGER. 7.2.2. Triggers a nivel de comando. Los triggers a nivel de comando se ejecutan una vez por cada comando DML. Por ejemplo, si un único comando INSERT inserta 500 filas dentro de la tabla LIBRO, se lanza un trigger a nivel de fila por cada registro insertado, pero un único trigger a nivel de comando para toda la tabla. Por lo tanto, los triggers a nivel de comando no son normalmente usados para actividades relativas a los datos; son normalmente usados para forzar medidas adicionales de seguridad sobre los tipos de acciones que podemos realizar sobre una tabla. Los triggers a nivel de comando son el tipo de trigger por defecto creados mediante el comando CREATE TRIGGER. 7.2.3. Triggers «BEFORE» y «AFTER». Ya que los triggers son ejecutados por eventos, pueden ocurrir inmediatamente antes o después de estos eventos. Ya que los eventos que ejecutan los triggers incluyen comandos DML de base de datos, pueden ser ejecutados inmediatamente antes o después de inserciones, actualizaciones o borrados. Para eventos al nivel de base de datos, podemos aplicar restricciones adicionales; no podemos desencadenar un evento para que ocurra antes de un inicio de sesión o un inicio de la base de datos. Dentro del trigger podemos referenciar los antiguos y nuevos valores involucrados en un comando DML. El acceso requerido por los datos antiguos y nuevos puede determinar qué tipo de trigger necesitamos. Si necesitamos asignar un valor de columna en un registro insertado mediante nuestro trigger, entonces podemos necesitar usar un trigger BEFORE INSERT para acceder a los nuevos valores. Usando un trigger AFTER INSERT no podemos modificar los valores a insertar, ya que la fila ha sido realmente insertada dentro de la tabla. Los triggers AFTER del nivel de fila son usados frecuentemente en aplicaciones de auditoría, ya que no se lanzan hasta que la fila ha sido modificada. Las modificaciones sucedidas en la fila implican que han pasado las restricciones de integridad referencial definidas para la tabla. 7.2.4. Triggers de sustitución (INSTEAD OF). Podemos usar triggers INSTEAD OF para decirle a Oracle qué hacer en vez de las acciones que invoca el trigger. Por ejemplo, podemos usar un trigger INSTEAD OF sobre una vista para redirigir inserciones dentro de una tabla o para actualizar varias tablas que son parte de una vista. Podemos usar triggers INSTEAD OF sobre vistas de objetos o vistas relacionales. Por ejemplo, si una vista involucra un join entre dos tablas, nuestra habilidad para usar el comando UPDATE sobre los registros en la vista es limitada. Sin embargo, si usamos un trigger INSTEAD OF, podemos decirle a Oracle cómo actualizar, borrar o insertar registros en las tablas subyacentes de la vista cuando un usuario intente cambiar valores a través de la vista. El código en el trigger INSTEAD OF es ejecutado en lugar de la inserción, actualización o borrado que entremos. Nota. Podemos acceder o cambiar datos LOB dentro de triggers BEFORE e INSTEAD OF. 7.2.5. Triggers de esquema. Podemos crear triggers sobre operaciones a nivel de esquema como un CREATE TABLE, ALTER TABLE, DROP TABLE, AUDIT, RENAME, TRUNCATE, y REVOKE. Aún podemos crear un trigger BEFORE DDL. Principalmente, los triggers de nivel de esquema proporcionan dos capacidades: la prevención de operaciones DDL y proporcionar supervisión de seguridad adicional con operaciones DDL. 7.2.6. Triggers al nivel de base de datos. Podemos crear triggers que sean lanzados por eventos de base de datos, incluyendo errores, registros, desconexiones, paradas e inicios de base de datos. Podemos usar este tipo de triggers para automatizar el mantenimiento de base de datos o acciones de auditoría. Las bases de datos virtuales privadas ejecutan triggers a nivel de base de datos para establecer valores de variables del contexto de sesión. 7.3. Triggers asociados a tablas. Normalmente se habla de un trigger asociado a una tabla como un bloque PL/SQL que se ejecuta como Oracle /122

consecuencia de una determinada instrucción SQL (INSERT, UPDATE o DELETE) sobre dicha tabla. 7.3.1. Declaración de los triggers. La sintaxis para crear estos triggers es la siguiente: CREATE [OR REPLACE] TRIGGER nombre_trigger {BEFORE|AFTER|INSTEAD OF} {DELETE|INSERT|UPDATE [OF col1, col2, ..., colN] [OR {DELETE|INSERT|UPDATE [OF col1, col2, ..., colN]...]} ON nombre_tabla [FOR EACH ROW [WHEN ()]] DECLARE -- variables locales BEGIN -- Sentencias [EXCEPTION] -- Sentencias de control de excepción END nombre_trigger; uso de OR REPLACE permite sobrescribir un trigger existente.

El Si se omite, y el trigger existe, se producirá un error al ejecutar el comando CREATE TRIGGER. Los triggers pueden definirse para las operaciones INSERT, UPDATE o DELETE, y pueden ejecutarse antes o después de la operación. Los modificadores BEFORE y AFTER indican que el trigger se lanzará antes o después de ejecutarse la sentencia SQL. Si incluimos el modificador OF, el trigger solo se ejecutará cuando la sentencia SQL afecte a los campos incluidos en la lista. El alcance de estos triggers puede ser a nivel de fila o de comando. El modificador FOR EACH ROW indica que el trigger se disparará cada vez que se realizan operaciones sobre una fila de la tabla. Si se acompaña del modificador WHEN, se puede establece una restricción; el trigger solo actuará sobre las filas que satisfagan la restricción. (La cláusula WHEN sólo es válida para los triggers con nivel de fila.) 7.3.2. Orden de ejecución de los triggers. Una misma tabla puede tener varios triggers asociados. En tal caso es necesario conocer el orden en el que se van a ejecutar. Los triggers se activan al ejecutarse la sentencia SQL: • Si existe, se ejecuta el disparador de tipo BEFORE (disparador previo) con nivel de comando. • Para cada fila a la que afecte el comando: - Se ejecuta si existe, el disparador de tipo BEFORE con nivel de fila. - Se ejecuta el propio comando. - Se ejecuta si existe, el disparador de tipo AFTER (disparador posterior) con nivel de fila. • Se ejecuta, si existe, el disparador de tipo AFTER con nivel de comando. 7.3.3. Restricciones de los triggers. El cuerpo de un trigger es un bloque PL/SQL. Cualquier comando que sea legal en un bloque PL/SQL, es legal en el cuerpo de un trigger, con las siguientes restricciones: - Un trigger no puede emitir ninguna orden de control de transacciones: COMMIT, ROLLBACK o SAVEPOINT. El trigger se activa como parte de la ejecución del comando que provocó el disparo, y forma parte de la misma transacción que dicho comando. Cuando el comando que provoca el disparo es confirmado o cancelado, se confirma o cancela también el trabajo realizado por el trigger. - Por razones idénticas, ningún procedimiento o función llamado por el trigger puede emitir órdenes de control de transacciones. - El cuerpo del trigger no puede contener ninguna declaración de variables LONG o LONG RAW. 7.3.4. Utilización de las variables globales «OLD» y «NEW». Dentro del ámbito de un trigger disponemos de las variables globales OLD y NEW. Estas variables se utilizan del mismo modo que cualquier otra variable PL/SQL, con la salvedad de que no es necesario declararlas; son del tipo %ROWTYPE de la tabla asociada al trigger y contienen una copia del registro antes ( OLD) y después (NEW) de la acción SQL que ha disparado el trigger. Utilizando estas variables podemos acceder a los datos que se están insertando, actualizando o borrando. La siguiente tabla muestra los valores de OLD y NEW según el comando que dispara el trigger. Acción SQL

OLD

INSERT

No definido; todos los campos toman Valores que serán insertados cuando se complete la

NEW

Oracle /123

UPDATE DELETE

orden. valor NULL. Valores originales de la fila, antes de la Nuevos valores que serán escritos cuando se actualización. complete la orden. Valores, antes del borrado de la fila. No definidos; todos los campos toman el valor NULL.

Nota. Los registros OLD y NEW son sólo válidos dentro de los triggers con nivel de fila (con la especificación FOR EACH ROW). Por ejemplo, si queremos lanzar un trigger antes de que se actualice una nota, pero sólo si el nuevo valor es mayor que el antiguo valor, podríamos utilizar el siguiente código: CREATE OR REPLACE TRIGGER TR_Nota_01 BEFORE UPDATE ON Nota FOR EACH ROW WHEN (NEW.valor > OLD.valor) BEGIN -- Código del Trigger END ;

El siguiente ejemplo muestra un trigger que inserta automáticamente un registro de matrícula con el año actual cada vez que insertamos un nuevo alumno en la base de datos (en este ejemplo se presupone creado un objeto secuencia denominado SQ_IDMatricula):

El

CREATE OR REPLACE TRIGGER TR_Matricula_01 AFTER INSERT ON Alumno FOR EACH ROW BEGIN INSERT INTO Matricula (idMatricula, nif, año) VALUES (SQ_IDMatricula.NEXT, :NEW.nif, EXTRACT(YEAR FROM SYSDATE)); END ; trigger se ejecutará automáticamente cuando sobre la tabla Alumno se ejecute una sentencia INSERT INSERT INTO Alumno (nif, nombre, apellidos, localidad) VALUES ('66666666H', 'Juan' , 'Salgado Rey', 'Madrid');

como:

Nota. Cuando se usa NEW y OLD en el cuerpo del trigger deben ir precedidos de dos punto (:); sin embargo, cuando se usan en la cláusula WHEN de FOR EACH ROW no deben ir precedidos de dos puntos. 7.3.5. Utilización de las funciones «INSERTING», «UPDATING» y «DELETING». Dentro de un trigger en el que se disparan distintos tipos de órdenes DML, hay tres funciones booleanas que pueden emplearse para determinar de qué operación se trata. Estas funciones son INSERTING, UPDATING y DELETING. Su comportamiento es el siguiente: Función

Retorna si el comando de disparo es INSERT; FALSE en otro caso. si el comando de disparo es UPDATE; FALSE en otro caso. si el comando de disparo es DELETE; FALSE en otro caso.

INSERTING TRUE UPDATING TRUE DELETING TRUE

Como ejemplo, el siguiente trigger normaliza el precio de un libro cuando se realizan inserciones o actualizaciones en la tabla LIBRO. Si se inserta un nuevo registro redondea el precio, y se actualiza el precio conserva el antiguo valor si es mayor que el nuevo. CREATE OR REPLACE TRIGGER TR_Normaliza_Titulo BEFORE INSERT OR UPDATE OF precio ON Libro FOR EACH ROW DECLARE BEGIN IF INSERTING THEN -- se está realizando una inserción :NEW.precio := ROUND(:NEW.precio, 2); END IF; IF UPDATING AND :NEW.precio'M';

Esta vista puede ahora ser tratada de la misma manera que cualquier otra vista en la base de datos local. El acceso a esta vista puede concederse a otros usuarios, a condición de que estos usuarios tengan acceso al enlace CONEXIÓN_REMOTA. 8.1.5. Usando un enlace de base de datos para actualizaciones remotas. La sintaxis de enlaces de base de datos para actualizaciones remotas es la misma que para consultas remotas. Se añade el nombre del enlace al nombre de la tabla que se quiere actualizar. Por ejemplo, para cambiar la categoría de libros en la tabla remota Libro, podemos ejecutar el comando UPDATE tal como se muestra a continuación: UPDATE Libro@CONEXION_REMOTA SET Categoria = 'JUVENIL' WHERE Categoria = 'INFANTIL'; Podemos usar subconsultas en la parte SET

del comando UPDATE. En las subconsultas podemos referenciar tanto tablas locales como remotas aplicando la sintaxis correspondiente. Se muestra un ejemplo a continuación: UPDATE Libro@CONEXION_REMOTA /* en base de datos remota */ SET Categoria = (SELECT Categoria FROM Libro@CONEXION_REMOTA /* en base de datos remota */ WHERE Titulo = 'Alicia') WHERE Titulo = 'Pincho';

Si en la subconsulta no se usase el nombre de enlace, entonces se usaría una tabla Libro de la base de datos local. 8.2. Usando sinónimos para transparencia de localización. Durante la vida útil de una aplicación, los datos con mucha probabilidad se moverán de una base de datos a Oracle /218

otra, de un host a otro. Por lo tanto, simplificará el mantenimiento de la aplicación que la posición física exacta de un objeto de base de datos esté protegida del usuario (y de la aplicación). El mejor modo de implementar esta transparencia de localización es usando sinónimos. En vez de escribir aplicaciones (o informes de SQL*Plus) que contengan consultas especificando el propietario de las tablas, como la siguiente consulta: SELECT * FROM Empledo.Libro;

Podemos crear un sinónimo para esta tabla y entonces usar el sinónimo en la consulta, como se hace a continuación: CREATE PUBLIC SYNONYM Libro FOR Empleado.Libro; / SELECT * FROM Libro;

La lógica requerida para encontrar los datos de esta manera ha sido trasladada de la aplicación a la base de datos. Mover la lógica de localización de la tabla a la base de datos será un beneficio si alguna vez necesitamos mover la tabla de un esquema a otro. Además de ocultar el propietario de la tabla a la aplicación, podemos ocultar la localización física de los datos que usan los enlaces de base de datos y sinónimos. Usando sinónimos locales para tablas remotas creamos una capa de lógica entre la aplicación y la base de datos. Por ejemplo, el sinónimo local Libro se define a continuación para referenciar una tabla localizada en otra base de datos remota sobre un host diferente. Si la tabla se mueve en el futuro, sólo necesitaremos cambiar el enlace; el código de la aplicación, cuando usa sinónimos, no necesita ser cambiado. CREATE PUBLIC SYNONYM Libro FOR Libro@CONEXION_REMOTA;

Si la cuenta remota usada por el enlace no es la propietaria del objeto referenciado, tenemos dos opciones. Primero, podemos referenciar un sinónimo disponible en la base de datos remota: CREATE PUBLIC SYNONYM Libro FOR Libro_Syn@CONEXION_REMOTA; Aquí, Libro_Syn, en la cuenta remota, es un sinónimo

para la tabla Libro remota. Segundo, podemos incluir el nombre del propietario remoto cuando creamos el sinónimo local, tal como se muestra a continuación: CREATE PUBLIC SYNONYM Libro FOR Empleado.Libro@CONEXION_REMOTA;

Estos dos ejemplo producirán la misma funcionalidad en nuestras consultas, pero hay una diferencia entre ellas. El segundo ejemplo, que incluye el nombre del propietario, es potencialmente más difícil de mantener ya que no usa un sinónimo en la base de datos remota, y si el objeto remoto se mueve posteriormente invalidará nuestro sinónimo local. 8.3. Usando la pseudo-columna «USER» en vistas. La pseudo-columna USER es muy útil cuando usamos métodos de acceso remoto a datos. Por ejemplo, podemos querer que no todos los usuarios remotos vean todos los registros de una tabla. Para resolver este problema podemos pensar en los usuarios remotos como usuarios especiales de nuestra base de datos. Para forzar la restricción sobre los datos necesitamos crear una vista a la que puedan acceder cuentas remotas. Pero, ¿qué podemos usar en la cláusula WHERE para restringir apropiadamente los registros? La pseudocolumna USER, combinada con los nombres de usuario apropiados permite forzar estas restricciones. La pseudo-columna USER es una "columna" que retorna el usuario actual de Oracle cuando es seleccionada, pero no es una columna actual de la tabla. Así, si una columna de la tabla contiene nombres de usuarios, estos valores pueden compararse con el valor de la pseudo-columna USER para restringir los registros, tal como se muestra a continuación. En este ejemplo, se consulta la columna Nombre en cada registro. Si el valor de la primera parte de la columna Nombre es el mismo que el nombre del usuario que realizó la consulta, se retornarán estos registros. CREATE VIEW Mis_Libros AS SELECT * FROM Libro WHERE SUBSTR(Nombre, 1, INSTR(Nombre,'') - 1) = USER;

Cuando restrinjamos el acceso remoto los registros de nuestra tabla, primero debemos considerar qué columnas serían las mejores para usar en la restricción. Normalmente hay divisiones lógicas en los datos de nuestra tabla, como un departamento o una región. Por cada división diferente crearemos una cuenta de usuario en nuestra base de datos local. Para este ejemplo, añadiremos la columna Region a la tabla Libro. Ahora habilitaremos la lista de libros para varias localizaciones distribuidas en una única tabla: Oracle /219

ALTER TABLE Libro ADD (Region VARCHAR2(10));

Supongamos que tenemos 4 regiones representadas en nuestra tabla Libro, y que hemos creado una cuenta de Oracle para cada región (asumamos que las regiones se llaman NORTE, SUR, ESTE, OESTE). Para cada una de estas regiones se crea una base de datos. Podemos entonces crear enlaces de base de datos para que sean usados por los usuarios específicos en nuestra base de datos local. Por ejemplo, los miembros del departamento SUR deberían usar el enlace mostrado a continuación: CREATE DATABASE LINK Enlace_Sur CONNECT TO SUR IDENTIFIED BY contraseña USING 'HQ';

Cuando los usuarios remotos consultan a través de los enlaces (como el enlace Enlace_Sur) son registrados dentro de la base de datos HQ con el nombre de la región. Por lo tanto, el valor de la columna USER para cualquier tabla que el usuario consulte será SUR. Ahora creamos una vista de nuestra tabla base, comparando la pseudo-columna USER al valor de la columna Region: CREATE OR REPLACE VIEW Libro_Restringido AS SELECT * FROM Libro WHERE Region = USER; Un usuario que se conecte a través del enlace Enlace_Sur (y por tanto se SUR) debería sólo ser capar de ver los registros cuya región sea 'SUR'.

registra remotamente como el usuario

Este tipo de restricción también puede realizarse en la base de datos remota en vez de en la base de datos donde reside la tabla. Los usuarios de la base de datos remota pueden crear vistas sobre sus bases de datos de la siguiente forma: CREATE OR REPLACE VIEW Sur_Libros AS SELECT * FROM Libro@CONEXION_REMOTA WHERE Region = 'SUR'; este caso, la restricción sobre Region está todavía vigente, pero es administrada

En localmente. La elección entre estos dos tipos de opciones de restricción (local o remota) se basa en el número de cuentas requeridas para la restricción. Para asegurar nuestra base de datos de producción, deberíamos limitar los permisos concedidos a las cuentas usadas en enlaces de base de datos. Concediendo estos permisos a través de roles, y usando vistas (con la cláusula READ ONLY o WITH CHECK OPTION) conseguimos limitar la capacidad de estas cuentas para realizar cambios no autorizados en los datos. 8.4. Enlaces dinámicos: usando el comando de copia de SQL*Plus. El comando de copia de SQL*Plus permite que los datos sean copiados entre bases de datos (o dentro de la misma base de datos) a través de SQL*Plus. Aunque nos permite seleccionar las columnas a copiar, trabaja mejor cuando se eligen todas las columnas de la tabla. El gran beneficio de usar este comando es la habilidad de confirmar después de que cada conjunto de datos ha sido procesado. Esto a su vez genera transacciones que son de un tamaño manejable. Nota. El comando de copia no ha cambiado desde Oracle8i. Este comando quedará obsoleto en futuras versiones. Consideremos el caso de una tabla grande, como Libro. Supongamos que la tabla Libro tiene 100.000 registros que usan un espacio total de 100MB, y necesitamos hacer una copia de esta tabla en otra base de datos. La opción más sencilla consiste en crear un enlace de base de datos y entonces usar este enlace en un comando CREATE TABLE … AS SELECT, tal como se muestra a continuación: CREATE DATABASE LINK CONEXION_REMOTA CONNECT TO Empleado IDENTIFIED BY contraseña USING 'HQ'; / CREATE TABLE Libro AS SELECT * FROM Libro@CONEXION_REMOTA;

El primer comando crea el enlace de base de datos, y el segundo crea una nueva tabla basada en todos los datos de la tabla remota. Desafortunadamente, esta opción crea una transacción muy larga (todos los 100.000 registros serán insertados en la nueva tabla dentro de una única transacción), que pone una carga grande sobre las estructuras internas de Oracle llamada segmentos de rollback. Los segmentos de rollback y la funcionalidad de deshacer gestionada por el sistema almacenan la imagen previa de los datos hasta que los nuevos datos son Oracle /220

confirmados en la base de datos. Ya que esta tabla está siendo poblada en una única operación de inserción, se genera una transacción larga que puede exceder el espacio disponible en los segmentos de rollback actuales. Este fallo provocará que la creación de la tabla falle. Para romper la transacción en entradas pequeñas se usa el comando COPY de SQL*Plus, el cual tiene la siguiente sintaxis: COPY FROM [usuario_remoto/contraseña_remota@cadena_de_conexión] [TO usuario/contraseña@cadena_de_conexión] {APPEND|CREATE|INSERT|REPLACE}

nombre_tabla USING subconsulta;

Si la cuenta actual es la destinataria de los datos copiados, la palabra TO y el usuario, contraseña y cadena de conexión local no son necesarios. Si la cuenta actual es el origen de los datos copiados, la información de la conexión remota no es necesaria. Para asignar el tamaño de la transacción se usa el comando SET de SQL*Plus para dar un valor al parámetro ARRAYSIZE. Esto determina el número de registros que serán enviados en cada lote. El parámetro COPYCOMMIT le dice a SQL*Plus cuántos lotes deberían ser confirmados de cada vez. El siguiente script SQL*Plus realiza la misma copia de la tabla Libro; sin embargo, rompe la transacción simple en varias transacciones. En este ejemplo, los datos son confirmados después de cada 1.000 registros. Esto reduce el tamaño de los segmentos de rollback de la transacción de 100MB a 1MB. SET COPYCOMMIT 1 SET ARRAYSIZE 1000 COPY FROM Empleado/contraseña@HQ CREATE Libro USING SELECT * FROM Libro

Nota. Excepto para la última línea, cada línea del comando porque es un comando de SQL*Plus.

COPY

debe terminar con un guión

Hay varias opciones dentro del comando COPY: Opción

Explicación Inserta los registros dentro de la tabla destino. Automáticamente crea la tabla si no existe previamente. Crea la tabla y entonces inserta los registros. CREATE INSERT Inserta los registros dentro de la tabla destino si existe; si no existe retorna un error. Cuando se usa INSERT, deben especificarse todas las columnas en la subconsulta. REPLACE Borra la tabla destino existente y la reemplaza con una nueva tabla con los datos copiados. APPEND

La interacción proporcionada por el comando COPY puede ser confusa al principio. Después de que se complete la última confirmación, la base de datos informa al usuario del número de registros que han sido confirmados en el último lote. No informa del número total de registros confirmados (a menos que se haya confirmado un único registro). 8.5. Conectándose a una base de datos remota. Además de la conexión entre base de datos descrita previamente, podemos conectarnos directamente a una base de datos remota a través de la herramienta SQL*PLUS de Oracle. Por lo tanto, en vez de escribir: SQLPLUS usuario/contraseña

Y acceder a nuestra base de datos local, podemos ir directamente a una base de datos remota. Para hacer esto, escribiremos nuestro nombre de usuario y contraseña junto con la cadena de conexión de Oracle Net para bases de datos remotas: SQLPLUS usuario/contraseña@HQ

Este comando nos registrará directamente en la base de datos registro se muestra en la siguiente figura.

Oracle /221

HQ.

La configuración host para este tipo de

Oracle Database + Oracle Net Base de datos HQ Herramientas Oracle + Oracle Net SQLPULS usuario/contraseña@HQ

SQL>

Host Central

Host Periférico

El host periférico tiene la herramientas de Oracle (como SQL*Plus) y está ejecutando Oracle Net, y el host central ejecuta Oracle Net y una base de datos de Oracle. Puede haber o no una base de datos en el host periférico; especificando la cadena de conexión a la base de datos remota se fuerza a Oracle a ignorar cualquier base de datos local.

9. Vistas materializadas. Para aumentar el rendimiento de una aplicación, podemos hacer copias locales de tablas remotas que usan datos distribuidos, o crear tablas de resumen basadas en operaciones de agrupación. Oracle proporciona las vistas materializadas para almacenar copias de datos o agregaciones. Se pueden usar las vistas materializadas para replicar todo o parte de una única tabla, o para replicar el resultado de una consulta a través de varias tablas; el refresco de los datos replicados pueden hacerlo automáticamente la base de datos cada cierto intervalo de tiempo. 9.1. Funcionalidad. Las vistas materializadas son copias (también conocidas como réplicas) de datos, basadas en consultas. En su forma más simple, una vista materializada puede ser obtenida a partir de una tabla creada por un comando como el siguiente: CREATE TABLE Local_Libros AS SELECT * FROM Libros@REMOTE_CONNECT; este ejemplo, se crea una tabla llamada Local_Libros

En en la base de datos local y se puebla con los datos de una base de datos remota (definida por el enlace de base de datos llamado REMOTE_CONNECT). Una vez creada la tabla Local_Libros, los datos deberán ser sincronizados con la tabla maestra (Libros@REMOTE_CONNECT). También los usuarios locales deberán poder actualizar Local_Libros, sin tener que preocuparse de las complicaciones de sincronización con la tabla maestra. A pesar de estos problemas de sincronización, hay ventajas en replicar los datos de este modo. Al crear copias locales de datos remotos podemos mejorar el rendimiento de consultas distribuidas, particularmente si los datos de la tabla maestra no cambian frecuentemente. Podemos usar también el proceso de creación de la tabla local para restringir las filas retornadas, restringir las columnas retornadas o generar nuevas columnas. Ésta es una estrategia común en entornos de tomas de decisiones, en los cuales se usan consultas complejas para periódicamente reunir datos dentro de tablas resumen para usar durante análisis. Las vistas materializadas automatizan la replicación de datos y los procesos de refresco. Cuando se crea una vista materializada, se establece un intervalo de refresco para planificar refrescos y replicar datos. Se pueden evitar actualizaciones locales, y se pueden usar refrescos basados en transacciones. Este tipo de refrescos envían desde la base de datos maestra sólo aquellas filas que han cambiado. 9.2. Permisos requeridos. Para crear una vista materializada debemos tener los permisos necesarios para crear los objetos subyacentes que se usarán. 9.2.1. Permisos de sistema. Debemos tener el permiso CREATE MATERIALIZED VIEW, así como los permisos CREATE TABLE o CREATE ANY TABLE. Además debemos tener el permiso de sistema UNLIMITED TABLESPACE o una cuota de espacio suficiente en un tablespace local. Para crear una vista materializada refrescada-ante-confirmación debemos tener también el permiso de sistema ON COMMIT REFRESH sobre cualquier tabla involucrada de la que no seamos propietario, o el permiso de sistema ON COMMIT REFRESH. Las vistas materializadas de tablas remotas requieren consultas de tablas remotas; por lo tanto, debemos tener permisos para usar un enlace de base de datos que acceda a la base de datos remota. El enlace que usemos Oracle /222

puede ser público o privado. Si el enlace es privado necesitamos tener el permiso de sistema CREATE DATABASE LINK para crear el enlace de base de datos. Si estamos creando vistas materializadas para tener las ventajas de la funcionalidad de "rescritura de consulta" (en la cual el optimizador elige dinámicamente tomar los datos de la vista materializada en vez de la tabla subyacente), debemos tener el permiso QUERY REWRITE. Si las tablas están en otro esquema de usuario debemos tener el permiso GLOBAL QUERY REWRITE. Si la vista materializada es creada con la opción ON COMMIT REFRESH, debemos tener el permiso de sistema ON COMMIT REFRESH o el permiso de objeto ON COMMIT REFRESH sobre cada tabla fuera de nuestro esquema. 9.2.2. Permisos de tabla. Cuando se crean vistas materializadas podemos referenciar tablas en bases de datos remotas mediante enlaces de base de datos. La cuenta que usa el enlace en la base de datos remota debe tener acceso a las tablas y vistas usadas por el enlace. No podemos crear vistas materializadas basadas en objetos propiedad del usuario SYS. Dentro de una base de datos local, podemos conceder el permiso SELECT sobre una vista materializada a otros usuarios locales. Ya que las vistas materializadas son sólo para leer (aunque puedan ser actualizables), no son necesarios permisos adicionales. Si creamos una vista materializada actualizable, debemos conceder el permiso UPDATE tanto a la vista como a la tabla local subyacente. 9.3. Solo-lectura contra actualizable. Una vista materializada de solo lectura no puede trasladar cambios en los datos a la tabla origen en la que está basada. Una vista materializada actualizable puede enviar cambios a la tabla origen. Aunque parece que la distinción está clara, las diferencias subyacentes entre estos dos tipos de vistas materializadas no son tan simples. Una vista materializada de solo lectura se implementa como un comando CREATE TABLE AS SELECT. Cuando ocurre una transacción dentro de la tabla origen, la transacción es enviada a la vista materializada de solo lectura. Así, el método por el cual las filas en la vista materializada cambian está controlado (las filas de la vista materializada sólo cambian después de un cambio en la tabla origen). En una vista materializada actualizable hay menos control sobre el método por el cual cambian las filas en la vista. Las filas pueden ser cambiadas según los cambios en la tabla origen, o se pueden cambiar las filas directamente en la vista. Por lo tanto, necesitamos enviar filas desde la tabla origen a la vista materializada y viceversa. Ya que existen varios métodos de cambios, existen varios orígenes (mencionado como configuración de multi-origen). Durante la transferencia de registros desde la vista a la tabla origen, necesitamos decidir cómo resolver conflictos. Por ejemplo, ¿qué pasa si un registro con ID=1 es eliminado en la vista materializada, mientras que en el sitio origen se crea un registro en otra tabla que referencia (mediante integridad referencial) el registro con ID=1? No podemos eliminar el registro con ID=1 de la tabla origen mientras exista un registro "hijo" relacionado. ¿Cómo planificar la resolución de estos conflictos? Las vistas materializadas de solo lectura evitan la necesidad de resolver este tipo de conflictos ya que fuerzan que todas las transacciones ocurran en la tabla origen. Esto puede limitar nuestra funcionalidad, pero es una solución apropiada para la mayoría de necesidades de replicación. Si necesitamos una replicación multi-origen véanse las reglas de la guía de replicación avanzada. 9.4. Sintaxis de creación de vistas materializadas. La sintaxis básica de creación de vistas materializadas se muestra a continuación. CREATE MATERIALIZED VIEW [usuario.]nombre

Cabecera

[ ORGANIZATION INDEX cláusula_iot] [ { { cláusulas de atributos de segmento } | CLUSTER cluster (columna [, columna] ...) } [ {cláusula de particionado | cláusula paralelo | cláusula construcción } ] | ON PREBUILT TABLE [ {WITH | WITHOUT} REDUCED PRECISION ] ] [ USING INDEX [ { cláusulas atributos físicos | cláusula tablespace } [ cláusula atributos físicos | cláusula tablespace ] | USING NO INDEX ] [ cláusula refresco ] [ FOR UPDATE ] [{DISABLE | ENABLE} QUERY REWRITE] AS subconsulta; comando CREATE MATERIALIZED VIEW tiene cuatro secciones

El en la cual se indica el nombre de la vista materializada:

Oracle /223

Parámetros de almacenamiento

Opciones de refresco consulta

mayores. La primera sección es la cabecera,

CREATE MATERIALIZED VIEW [usuario.]nombre

La vista será creada en nuestra cuenta (esquema) a menos que especifiquemos un nombre de usuario en la cabecera. En la segunda sección se especifican los parámetros de almacenamiento. Estos parámetros se aplican a la tabla que será creada en la base de datos local. Si los datos han sido ya replicados en una tabla local podemos usar la cláusula PREBUILT TABLE para decirle a Oracle que use esta tabla como una vista materializada. Nota. Podemos especificar los parámetros de almacenamiento para ser usados por el índice que es automáticamente creado sobre la vista materializada. En la tercera sección se asignan las opciones de refresco. La sintaxis para esta cláusula es la siguiente:

{ REFRESH { { FAST | COMPLETE | FORCE } | ON { DEMAND | COMMIT } | { START WITH | NEXT } DATE | WITH { PRIMARY KEY | ROWID } | USING { DEFAULT [ MASTER | LOCAL ] ROLLBACK SEGMENT | [ MASTER | LOCAL ] ROLLBACK SEGMENT segmento_Rollback } [ DEFAULT [ MASTER | LOCAL ] ROLLBACK SEGMENT | [ MASTER | LOCAL ] ROLLBACK SEGMENT segmento_Rollback ]... } [ { FAST | COMPLETE | FORCE } | ON { DEMAND | COMMIT } | { START WITH | NEXT } DATE | WITH { PRIMARY KEY | ROWID } | USING { DEFAULT [ MASTER | LOCAL ] ROLLBACK SEGMENT | [ MASTER | LOCAL ] ROLLBACK SEGMENT segmento_Rollback } [ DEFAULT [ MASTER | LOCAL ] ROLLBACK SEGMENT | [ MASTER | LOCAL ] ROLLBACK SEGMENT segmento_Rollback ]... ]... | NEVER REFRESH } La opción REFRESH especifica el mecanismo que Oracle debe usar cuando se refresque la vista materializada. Las tres opciones disponibles son FAST, COMPLETE y FORCE. El refresco FAST está sólo disponible si Oracle

puede hacer corresponder las filas de la vista directamente con las filas de la tabla(s) origen; Oracle usa tablas llamadas registros de vistas materiaplizadas para enviar filas específicas desde la tabla origen a la vista materializada. El refresco COMPLETE trucan los datos y re-ejecuta la consulta base de la vista materializada para repoblarla. La opción FORCE le dice a Oracle que use un refresco rápido si es posible; si no es posible usa un refresco completo. Si hemos creado una vista materializada simple pero queremos usar el refresco completo, debemos especificar REFRESH COMPLETE en el comando. Dentro de esta sección podemos especificar el mecanismo usado para relacionar valores en la vista materializada con la tabla origen (si deben usarse valores RowId o la clave primaria). Por defecto, se usan las claves primarias. Si la consulta origen para la vista referencia una combinación o una única tabla podemos usar la opción ON COMMIT para controlar la replicación de los cambios. Si se usa ON COMMIT, los cambios serán enviados desde el origen a la réplica cuando los cambios son confirmados sobre la tabla origen. Si especificamos ON DEMAND, el refresco ocurrirá cuando ejecutemos manualmente un comando REFRESH. La cuarta sección es la consulta que usará la vista materializada: [ FOR UPDATE ] [{DISABLE | ENABLE} QUERY REWRITE] AS subconsulta ; especificamos FOR UPDATE, la vista será actualizable; si

Si no, será de solo lectura. La mayoría de vistas materializadas son réplicas de sólo lectura de la tabla origen. Si usamos vistas actualizables debemos tener en Oracle /224

cuenta las replicaciones en los dos sentidos y la resolución de conflictos en los datos. Nota. La consulta que forma el origen de la vista materializada no debería usar las pseudo-columnas USER y SYSDATE. El siguiente ejemplo crea una vista materializada de solo lectura llamada LOCAL_LIBRO en la base de datos local, basada en una tabla remota llamada LIBRO que es accesible mediante el enlace CONEXIÓN_REMOTA. La vista es ubicada en el tablespace USERS. CREATE MATERIALIZED VIEW LOCAL_LIBRO TABLESPACE USERS REFRESH FORCE START WITH SYSDATE NEXT SYSDATE+7 WITH PRIMARY KEY AS SELECT * FROM LIBRO@CONEXION_REMOTA;

Oracle responde con

Vista materializada creada.

El comando del ejemplo precedente creará una vista materializada de sólo lectura llamada LOCAL_LIBRO. La tabla subyacente será creada en el tablespace USERS. Podemos poner los registros de la vista materializada en tablespaces aparte de los que soportan la vista materializada. Se ha especificado la opción de refresco FORCE porque no existen registros de la vista materializada sobre la tabla origen; Oracle intentará usar un refresco rápido pero sólo usará un refresco completo hasta que se cree el registro de vista materializada. La consulta asociada a la vista especifica que toda la tabla LIBRO, sin modificaciones, será copiada a la base de datos local. A la vez que la vista materializada LOCAL_LIBRO es creada, su tabla subyacente es poblada con los datos de LIBRO. A partir de ese momento la vista materializada será refrescada cada 7 días. Los parámetros de almacenamiento no indicados serán aplicados por defecto según el tablespace USERS. El siguiente ejemplo crea una vista materializada llamada LOCAL_CATEGORIA_COUNT en una base de datos local, basada en una tabla remota llamada LIBRO en una base de datos accedida mediante el enlace CONEXION_REMOTA. CREATE MATERIALIZED VIEW LOCAL_CATEGORIA_COUNT TABLESPACE USERS REFRESH FORCE START WITH SYSDATE NEXT SYSDATE+7 AS SELECT NombreCategoria, COUNT(*) CountPorCat FROM LIBRO@CONEXION_REMOTA GROUP BY NombreCategoria;

La consulta de esta vista materializada cuenta el número de libros en cada categoría de la tabla remota LIBRO. Hay unos pocos importantes puntos a notar acerca de estos dos ejemplos previos: • La consulta de agrupación usada en la vista materializada LOCAL_CATEGORIA_COUNT podría ser realizada en SQL*Plus usando la vista LOCAL_LIBRO. Esto es, la operación de agrupación puede hacerse fuera de la vista materializada. • Ya que LOCAL_CATEGORIA_COUNT usa una cláusula GROUP BY, es una vista compleja si sólo puede usar el refresco completo. LOCAL_LIBRO, como vista simple, puede usar refresco rápido. Estas dos vistas materializadas de ejemplo referencian la misma tabla. Ya que una de las vista replica todas las columnas y filas de la tabla origen, la segunda vista puede parecer redundante. Sin embargo, algunas veces el segundo tipo de vista, la compleja, es el más utilizado de los dos. ¿Cómo es esto así? Primero, recordemos que estas vistas materializadas son usadas para servir consultas necesarias para usuarios locales. Si estos usuarios siempre realizan operaciones de agrupación en sus consultas, y las columnas de agrupación son fijas, entonces LOCAL_CATEGORIA_COUNT puede ser más útil. Segundo, si el volumen de transacción de la tabla origen LIBRO es muy alto, o la tabla LIBRO es muy pequeña, puede no haber diferencias en los tiempo de refresco rápido y completo. Las vistas materializadas más apropiadas son las que resultan más productivas para los usuarios. 9.4.1. Tipos de vistas materializadas. Las vistas materializadas mostradas en los ejemplos previos ilustran sobre dos tipos de vistas materializadas. En el primero, la vista materializada crea una copia local de datos remotos, sin agrupación. En el segundo, se aplica una agrupación. En ambas vistas podemos ampliar la consulta base para incluir combinaciones de Oracle /225

tablas. Lo diferencia importante en este caso es el uso de agrupación en el segundo ejemplo. Un tercer tipo de vista materializada es una vista materializada anidada (una vista materializada cuya definición es la base para otra vista materializada). El tipo de vista materializada tendrá importancia en nuestra habilidad de realizar refrescos rápidos. Un refresco rápido actualizará la vista sólo con las filas que han cambiado en las tablas origen desde el último refresco. Si no podemos realizar un refresco rápido, tendremos que usar un refresco completo, el cual es normalmente más expansivo en términos de tiempo y recursos. Los refrescos rápidos requieren el uso de registros de vista materializada sobre todas las tabla referenciadas en la consulta base de la vista. Si la vista contiene una agrupación, un refresco rápido será posible si la consulta contiene todo el grupo por columnas y debe haber un COUNT(*) y COUNT(columna) sobre cualquier columna del GROUP BY. Si la vista materializada contiene sólo combinaciones pero no agrupaciones, el refresco rápido es posible después de cualquier inserción, actualización o borrado sobre las tablas base. Las columnas RowID para cada tabla debe estar presente en la lista del SELECT de la consulta base, y todas las tablas referenciadas deber tener registros de vista materializada. Debido a que hay que enviar los cambios incrementales desde las tablas referenciadas a la vista materializada, los refrescos rápidos normalmente representan el camino más rápido para actualizar los datos de nuestras vistas materializadas. 9.4.2. Vistas materializadas basadas en «RowID» y en clave primaria. Podemos basar las vistas materializadas tanto sobre los valores de la clave primaria como sobre los valores de RowID de las tablas base. Debemos decidir entre estas opciones según varios factores: • Estabilidad del sistema. Si el sitio origen no es estable, entonces podemos necesitar realizar recuperaciones de base de datos que involucren a las tablas origen de la vista. Cuando se usan las utilidades de Oracle Data Pump Export e Import para realizar recuperaciones, los valores RowID de las filas cambiarán. Si el sistema requiere frecuentes exportaciones e importaciones, deberíamos usar vistas basadas en clave primaria. • Tamaño de la tabla de registro de vista materializada. Oracle permite almacenar los cambios de las tablas origen en tablas separadas llamadas registros de vista materializada. Si la clave primaria consiste de varias columnas, esta tabla de registro para una vista basada en clave primaria puede ser considerablemente más grande que la vista basada en RowID. • Integridad referencial. Para usar vistas basadas en clave primaria, debemos definir una clave primaria sobre la tabla origen. Si no podemos definir una clave primaria debemos usar vistas basadas en RowID. 9.4.3. Usando tablas predefinidas. Cuando creamos vistas materializadas podemos especificar BUILD IMMEDIATE para poblar la vista materializada inmediatamente o BUILD DEFERRED para poblar la vista más tarde (a través de un refresco). Si necesitamos gestionar con cuidado las transacciones que pueblan inicialmente la vista materializada, podemos crear una tabla que tenga la misma estructura que la vista materializada a poblar. Cuando la tabla está completamente cargada y apropiadamente indexada, se usa la cláusula ON PREBUILT TABLE del comando CREATE MATERIALIZED VIEW. La tabla y la vista materializada deben tener el mismo nombre, y la tabla debe tener las misma columnas y tipos de datos (podemos especificar precisiones reducidas para acomodar diferencias en precisión). La tabla puede contener columnas adicionales no gestionadas. Una vez que la tabla ha sido registrada como una vista materializada, podemos mantenerla mediante refrescos, y el optimizador puede usarla en operaciones de "rescritura de consultas". Para que la "rescritura de consulta" trabaje apropiadamente sobre una tabla predefinida, debemos asignar el parámetro de inicialización QUERY_REWRITE_INTEGRITY a STALE_TOLERATED o TRUSTED. 9.4.4. Indexando tablas de vistas materializadas. Cuando creamos una vista materializada, Oracle crea una tabla base local que contiene los datos que satisfacen la consulta base. Ya que los datos han sido replicados con algún propósito en mente (normalmente para mejorar el rendimiento en la base de datos o la red), es importante llevar a cabo a este objetivo después de que la vista materializada ha sido creada. La mejora del rendimiento para consultas se obtiene normalmente mediante el uso de índices. Las columnas que son frecuentemente usadas en cláusulas WHERE de consultas deberían se indexadas; si un conjunto de columnas es frecuentemente accedida en consultas, entonces se puede crear un índice concatenado sobre este conjunto de columnas. Oracle no crea automáticamente índices para vistas materializadas complejas sobre columnas en vez de la clave primaria. Necesitamos crear estos índices manualmente. Para crear índices en nuestra tabla base local se usa el comando CREATE INDEX. No se aconseja crear ninguna restricción sobre la tabla origen de la vista Oracle /226

materializada; Oracle mantiene las relaciones basadas en restricciones sobre las tablas origen. Ya que no se crea ningún índice sobre las columnas que los usuarios consultan sobre la vista materializada, deberíamos crear índices sobre la tabla local base de la vista materializada. 9.5. Usando vistas materializadas para modificar rutas de ejecución de consultas. Para una gran base de datos, una vista materializada puede ofrecer varios beneficios de rendimiento. Podemos usar vistas materializadas para influir en el optimizador a cambiar la ejecución de rutas para consultas. Esta funcionalidad, llamada "rescritura de consulta", permite al optimizador usar una vista materializada en vez de la tabla consultada por la vista materializada, aun si la vista materializada no es nombrada en la consulta. Por ejemplo, si tenemos una tabla grande VENTA, podemos crear una vista materializada que sume los datos de VENTA por región. Si un usuario consulta la tabla VENTA para obtener la suma de datos por región, Oracle puede redireccionar esta consulta para que use la vista materializada en vez de la tabla VENTA. Como resultado, podemos reducir el número de accesos a nuestras tablas grandes, mejorando así el rendimiento del sistema. Nota. Debemos especificar ENABLE QUERY REWRITE en la definición de la vista materializada para que la vista sea usada como parte de una operación de rescritura de consulta. Para usar la capacidad de rescritura de consulta efectivamente, debemos crear una dimensión que defina la jerarquía dentro de los datos de la tabla. Para ejecutar el comando CREATE DIMENSION necesitamos tener el permiso del sistema CREATE DIMENSION. Podemos crear una dimensión que soporte la jerarquía entre dos tablas de ejemplo PAIS y CONTINENTE: CREATE DIMENSION GEOGRAFIA LEVEL IdPais IS PAIS.Pais LEVEL IdContinente IS CONTINENTE.Continente HIERARCHY Pais_Rollup ( IdPais CHILD OF IdContinente JOIN KEY PAIS.Continente REFERENCES IdContinente);

Para habilitar una vista materializada para rescritura de consultas, todas las tablas origen de la vista deben estar en el esquema de la vista, y debemos tener el permiso de sistema QUERY REWRITE. Si la vista y las tablas están en esquemas separados, debemos tener el permiso de sistema GLOBAL QUERY REWRITE. En general, debemos crear vistas materializada en el mismo esquema que las tablas origen; si no es así, necesitaremos gestionar los permisos y concesiones requeridas para crear y mantener la vista materializada. Nota. Podemos habilitar o deshabilitar rescritura de consultas en el nivel de la sentencia SQL mediante los modificadores REWRITE y NOREWRITE. Cuando usamos el modificador REWRITE podemos especificar vistas materializadas que debería considerar el optimizador. Nota. Las decisiones de rescritura de consultas están basadas en el coste de las diferentes rutas de ejecución, así que nuestras estadísticas deberían mantenerse. Para que la rescritura de consultas sea posible, debemos asignar los siguientes parámetros de inicialización: • OPTIMIZER_MODE = ALL_ROWS o FIRST_ROWS • QUERY_REWRITE_ENABLED = TRUE • QUERY_REWRITE_INTEGRITY = STALE_TOLERATED, TRUSTED, o ENFORCED Por defecto, QUERY_REWRITE_INTEGRITY es asignado a ENFORCED; en este modo todas las restricciones deben ser validadas. El optimizador sólo usar refrescos de datos desde vistas materializadas y sólo usar estas relaciones que están basadas en restricciones ENABLED VALIDATED primarias, únicas, o de clave foránea. En modo TRUSTED, el optimizador garantiza que los datos en la vista materializada sean frescos y las relaciones declaradas en dimensiones y restricciones sean correctas. En modo STALE_TOLERATED, el optimizador usa vistas materializadas que son válidas pero contienen datos antiguos, así como aquellas que contienen datos frescos. Si asignamos QUERY_REWRITE_ENABLED a FORCE, el optimizador rescribirá consultas para usar vistas materializadas cuando el coste estimado de la consulta original sea bajo. Si ocurre la rescritura de consultas, el plan estimado para la consulta catalogará la vista materializada como una de los objetos accedidos, con una operación catalogada como "MAT_VIEWREWRITE ACCESS". Podemos usar el procedimiento DBMS_MVIEW.EXPLAIN_REWRITE para ver si la rescritura es posible para una consulta, y qué vistas materializadas estarían involucradas. Si la consulta no puede ser rescrita, el procedimiento documentará las razones. EXPLAIN_REWRITE tiene tres parámetros de entrada: la consulta, el Oracle /227

nombre de una vista materializada (opcional), y una sentencia identificadora (opcional). La salida de este procedimiento puede ser almacenada en una tabla. Oracle proporciona el comando CREATE TABLE para la tabla de salida en un script llamado utlxrw.sql en el directorio /rdbms/admin bajo el directorio base de Oracle. El script utlxrw.sql crea una tabla llamada REWRITE_TABLE. Se puede consultar en esta tabla el coste original, el coste de rescritura y la decisión del optimizador. La columna Message mostrará las razones de la decisión del optimizador. 9.6. Usando «DBMS_ADVISOR». Desde Oracle Database 10g, podemos usar el SQL Access Advisor (Consejero de Acceso SQL) para generar recomendaciones para la creación de índices (y tipos de índices) para mejorar el rendimiento de consultas de combinación y otras. El SQL Access Advisor puede generar recomendaciones para modificar una vista materializada y que así soporte rescritura de consultas y refrescos rápidos. Podemos ejecutar el SQL Access Advisor desde el Administrador de Oracle Enterprise o mediante el paquete DBMS_ADVISOR. Nota. Para los mejores resultados del paquete DBMS_ADVISOR, deberíamos reunir estadísticas sobre todas las tablas, índices y columnas de join para generar recomendaciones. Para usar el SQL Access Advisor, tanto desde el Administrador de Oracle Enterprise como con DBMS_ADVISOR, debemos seguir los siguientes pasos: 1. Crear una tarea. 2. Definir la carga de trabajo. 3. Generar recomendaciones 4. Ver e implementar recomendaciones. Podemos crear una tarea de dos formas: ejecutando el procedimiento DBMS_ADVISOR.CREATE_TASK o usando el procedimiento DBMS_ADVISOR.QUICK_TUNE. La carga de trabajo consiste de una o más sentencias SQL más las estadísticas y atributos que se relacionan con la sentencia. La carga de trabajo puede incluir todas las sentencias SQL de una aplicación. El SQL Access Advisor organiza las entradas en la carga de trabajo de a cuerdo a las estadísticas e importancia en el negocio. La carga de trabajo se crea usando el procedimiento DBMS_ADVISOR.CREATE_SQLWKLD. Para asociar una carga de trabajo con una tarea del consejero se usa el procedimiento DBMS_ADVISOR.ADD_SQLWKLD_REF. Si no se proporciona una carga de trabajo, el SQL Access Advisor puede generar y usar una carga de trabajo hipotética basada en las dimensiones definidas en nuestro esquema. Una vez que existe una tarea y una carga de trabajo asociada con la tarea, podemos generar recomendaciones a través del procedimiento DBMS_ADVISOR.EXECUTE_TASK. El SQL Access Advisor considerará la carga de trabajo y las estadísticas del sistema e intentará generar recomendaciones para afinar la aplicación. Podemos ver estas recomendaciones ejecutando la función DBMS_ADVISOR.GET_TASK_SCRIPT o mediante las vistas del diccionario de datos. Cada recomendación puede ser vista mediante USER_ADVISOR_RECOMMENDATIONS (existen también versiones ALL_ y DBA_). Para relacionar recomendaciones con una sentencia SQL necesitamos usar la vista USER_ADVISOR_SQLA_WK_STMTS y USER_ADVISOR_ACTIONS. Cuando se ejecuta el procedimiento GET_TASK_SCRIPT, Oracle genera un fichero SQL ejecutable que contendrá los comandos necesarios para crear, modificar o borrar los objetos recomendados. Deberíamos revisar el script generado antes de ejecutarlo, particularmente en las especificaciones del tablespace. 9.6.1. Cómo realizar un afinado rápido. Para afinar una única sentencia SQL se usa el procedimiento DBMS_ADVISOR.QUICK_TUNE. El procedimiento QUICK_TUNE tiene dos parámetros de entrada: un nombre de tarea y una sentencia SQL. Usando QUICK_TUNE escudamos al usuario de los pasos involucrados en la creación de cargas de trabajo y tareas mediante DBMS_ADVISOR. Por ejemplo, el siguiente procedimiento evalúa una consulta: EXECUTE DBMS_ADVISOR.QUICK_TUNE(DBMS_ADVISOR.SQLACCESS_ADVISOR, 'MV_TUNE','SELECT Autor FROM Libro');

Nota. El usuario que ejecute este comando necesita el permiso de sistema ADVISOR. Las

recomendaciones

generadas por QUICK_TUNE pueden verse a través de la vista pero es más fácil leerlas usando el procedimiento DBMS_ADVISOR para generar un fichero de script. La recomendación es que una vista materializada será creada para soportar esta consulta. Ya que sólo se proporcionó una consulta, no se consideran otros aspectos de la base de datos o la aplicación. USER_ADVISOR_ACTIONS,

Oracle /228

Podemos usar el procedimiento CREATE_FILE para automatizar la generación de un fichero que contenga el script necesario para implementar las recomendaciones. Primero, crear un objeto DIRECTORY para contener el fichero: CREATE DIRECTORY SCRIPTS AS 'E:\SCRIPTS'; GRANT READ ON DIRECTORY SCRIPTS TO PUBLIC; GRANT WRITE ON DIRECTORY SCRIPTS TO PUBLIC; A continuación, ejecutar el procedimiento CREATE_FILE. Tiene tres variables de entrada: el mediante GET_TASK_SCRIPT, al cual se pasa el nombre de la tarea), el directorio de salida,

fichero que será creado.

script (generado y el nombre del

EXECUTE DBMS_ADVISOR.CREATE_FILE(DBMS_ADVISOR.GET_TASK_SCRIPT('MV_TUNE'), 'SCRIPTS', 'MV_TUNE.sql'); fichero MV_TUNE.sql creado por el procedimiento CREATE_FILE contendrá comandos similares

El a los mostrados a continuación. Dependiendo de la versión específica de Oracle las recomendaciones pueden diferir. Rem Username: EMPLEADO Rem Task: MV_TUNE Rem set feedback 1 set linesize 80 set trimspool on set tab off set pagesize 60 whenever sqlerror CONTINUE CREATE MATERIALIZED VIEW "EMPLEADO"."MV$$_021F0001" REFRESH FORCE WITH ROWID ENABLE QUERY REWRITE AS SELECT PRACTICE.BOOKSHELF.ROWID C1, "EMPLEADO"."LIBRO"."AUTOR" M1 FROM EMPLEADO.LIBRO; begin dbms_stats.gather_table_stats('"PRACTICE"', '"MV$$_021F0001"',NULL,dbms_stats.auto_sample_size); end; / whenever sqlerror EXIT SQL.SQLCODE begin dbms_advisor.mark_recommendation('MV_TUNE',1,'IMPLEMENTED'); end; / El procedimiento MARK_RECOMMENDATION permite anotar la recomendación así que puede saltarse durante la generación del script. Acciones válidas para MARK_RECOMMENDATION incluyen ACCEPT, IGNORE, IMPLEMENTED y REJECT. Podemos usar el procedimiento DBMS_ADVISOR.TUNE_MVIEW para generar recomendaciones para la reconfiguración de nuestras vistas materializadas. TUNE_VIEW genera dos conjuntos de resultados de salida:

para la creación de nuevas vistas materializadas, y para remover vistas materializadas creadas previamente. El resultado final será un conjunto de vistas materializadas que pueden ser refrescadas rápidamente, reemplazando vistas materializadas que no pueden ser refrescadas rápidamente. Podemos ver la salida de TUNE_MVIEW mediante la vista del diccionario de datos USER_TUNE_MVIEW, o podemos generar su script mediante los procedimientos GET_TASK_SCRIPT y CREATE_FILE. 9.7. Refrescando vista materializadas. Los datos en una vista materializada puede ser replicados una vez (cuando la se crea la vista) o a intervalos. El comando CREATE MATERIALIZED VIEW permite asignar el intervalo de refresco, delegando la responsabilidad de la planificación y realización de los refrescos a la base de datos.

Oracle /229

9.7.1. ¿Qué cosas podemos cambiar en los refrescos? Para ver qué cosas de los refrescos y capacidades de rescritura son posibles en nuestras vistas materializadas, podemos consultar la tabla MV_CAPABILITIES_TABLE. Las capacidades pueden cambiar entre versiones, así que deberíamos reevaluar nuestras capacidades de refresco con cada actualización de Oracle. Para crear esta tabla, hay que ejecutar el script utlxmv.sql localizado en el directorio /rdbms/admin bajo el directorio base de Oracle. Las columnas de MV_CAPABILITIES_TABLE son: DESC MV_CAPABILITIES_TABLE

Name ----------------------------------------STATEMENT_ID MVOWNER MVNAME CAPABILITY_NAME POSSIBLE RELATED_TEXT RELATED_NUM MSGNO MSGTXT SEQ

Null? --------

Type ---------------VARCHAR2(30) VARCHAR2(30) VARCHAR2(30) VARCHAR2(30) CHAR(1) VARCHAR2(2000) NUMBER NUMBER(38) VARCHAR2(2000) NUMBER

Para poblar la tabla MV_CAPABILITIES_TABLE se ejecuta el procedimiento DBMS_MVIEW.EXPLAIN_MVIEW, usando el nombre de la vista materializada como valor de entrada como se muestra a continuación: EXECUTE DBMS_MVIEW.EXPLAIN_MVIEW('local_category_count');

El script de utlxmv.sql proporciona guías de interpretación de los valores de columna, tal como se muestra a continuación: CREATE TABLE MV_CAPABILITIES_TABLE (STATEMENT_ID VARCHAR(30), MVOWNER VARCHAR(30), MVNAME VARCHAR(30), CAPABILITY_NAME VARCHAR(30),

-- Client-supplied unique statement identifier -- NULL for SELECT based EXPLAIN_MVIEW -- NULL for SELECT based EXPLAIN_MVIEW -- A descriptive name of the particular -- capability: -- REWRITE -- Can do at least full text match -- rewrite -- REWRITE_PARTIAL_TEXT_MATCH -- Can do at least full and partial -- text match rewrite -- REWRITE_GENERAL -- Can do all forms of rewrite -- REFRESH -- Can do at least complete refresh -- REFRESH_FROM_LOG_AFTER_INSERT -- Can do fast refresh from an mv log -- or change capture table at least -- when update operations are -- restricted to INSERT -- REFRESH_FROM_LOG_AFTER_ANY -- can do fast refresh from an mv log -- or change capture table after any -- combination of updates -- PCT -- Can do Enhanced Update Tracking on -- the table named in the RELATED_NAME -- column. EUT is needed for fast -- refresh after partitioned -- maintenance operations on the table -- named in the RELATED_NAME column

Oracle /230

-- and to do non-stale tolerated -- rewrite when the mv is partially -- stale with respect to the table -- named in the RELATED_NAME column. -- EUT can also sometimes enable fast -- refresh of updates to the table -- named in the RELATED_NAME column -- when fast refresh from an mv log -- or change capture table is not -- possible. POSSIBLE CHARACTER(1), -- T = capability is possible -- F = capability is not possible RELATED_TEXT VARCHAR(2000), -- Owner.table.column, alias name, etc. -- related to this message. The -- specific meaning of this column -- depends on the MSGNO column. See -- the documentation for -- DBMS_MVIEW.EXPLAIN_MVIEW() for details RELATED_NUM NUMBER, -- When there is a numeric value -- associated with a row, it goes here. -- The specific meaning of this column -- depends on the MSGNO column. See -- the documentation for -- DBMS_MVIEW.EXPLAIN_MVIEW() for details MSGNO INTEGER, -- When available, QSM message # -- explaining why not possible or more -- details when enabled. MSGTXT VARCHAR(2000), -- Text associated with MSGNO. SEQ NUMBER); -- Useful in ORDER BY clause when -- selecting from this table. vez ejecutado el procedimiento EXPLAIN_MVIEW podemos consultar MV_CAPABILITIES_TABLE

Una determinar nuestras opciones.

Para

SELECT Capability_Name, Msgtxt FROM MV_CAPABILITIES_TABLE WHERE Msgtxt IS NOT NULL; la vista materializada LOCAL_LIBRO

la consulta retornará:

CAPABILITY_NAME ---------------------------------------------PCT_TABLE REFRESH_FAST_AFTER_INSERT REFRESH_FAST_AFTER_ONETAB_DML REFRESH_FAST_AFTER_ANY_DML REFRESH_FAST_PCT REWRITE_FULL_TEXT_MATCH REWRITE_PARTIAL_TEXT_MATCH REWRITE_GENERAL REWRITE_PCT PCT_TABLE_REWRITE

para

MSGTXT --------------------------------------------------------------------------relation is not a partitioned table the detail table does not have a materialized view log see the reason why REFRESH_FAST_AFTER_INSERT is disabled see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled PCT is not possible on any of the detail tables in the materialized view query rewrite is disabled on the materialized view query rewrite is disabled on the materialized view query rewrite is disabled on the materialized view general rewrite is not possible or PCT is not possible on any of the detail tables relation is not a partitioned table

Ya que no se especificó la cláusula QUERY REWRITE durante la creación de la vista materializada, las capacidades de rescritura de consultas están desactivadas para LOCAL_LIBRO. Las capacidades de refresco rápido no están soportadas porque la tabla base no tiene un registro de vista materializada. Si cambiamos nuestra vista materializada o la tabla base deberíamos regenerar los datos en MV_CAPABILITIES_TABLE para ver las nuevas capacidades. Además de la falta de registro de vista materializada hay otras restricciones que limitan nuestra habilidad de Oracle /231

usar refrescos rápidos: • La vista materializada no debe contener referencias a expresiones no repetitivas como SysDate y RowNum. • La vista materializada no debe contener referencias a tipos de datos RAW o LONG RAW. • Para vistas materializadas basadas en joins, los RowID's de todas las tablas deben ser parte de la lista del SELECT. • Si hay joins externos, todos los joins deben estar conectados por AND's, la cláusula WHERE no debe tener selecciones, y debe existir una restricción UNIQUE sobre las columnas del join de la tabla del INNER JOIN. • Para vistas materializadas basadas en agrupaciones, los registros de vista materializada deben contener todas las columnas de las tablas referenciadas, deben especificar el RowID y cláusulas INCLUDING NEW VALUES, y deben especificar la cláusula SEQUENCE. Nota. Podemos especificar una cláusula ORDER BY en el comando CREATE MATERIALIZED VIEW. La cláusula ORDER BY sólo afectará a la creación inicial de la vista; y no afectará a cualquier refresco. 9.7.2. Refrescos automáticos. Consideremos la vista materializada LOCAL_LIBRO descrita previamente. La opción de refresco, definida al crear la vista, se muestra a continuación: CREATE MATERIALIZED VIEW LOCAL_LIBRO TABLESPACE USERS REFRESH FORCE START WITH SYSDATE NEXT SYSDATE+7 WITH PRIMARY KEY AS SELECT * FROM LIBRO@CONEXION_REMOTA;

La planificación de refresco tiene tres componentes. Primero, el tipo de refresco ( FAST, COMPLETE, NEVER, o FORCE). Refrescos rápidos usan registros de vista materializada para enviar cambios de filas desde la tabla origen a la vista. Refrescos completos borran todas las filas de la vista y la repueblan. La opción FORCE le dice a Oracle que use refresco rápido si es posible, sino se usará refresco completo. La cláusula START WITH le dice a la base de datos cuándo realizar la primera replicación de la tabla origen a la tabla base local. Debemos evaluarla a un punto del tiempo en el futuro. Si no especificamos un tiempo para START WITH pero especificamos un valor NEXT, Oracle usará la cláusula NEXT para determinar el tiempo inicial. Para mantener control sobre nuestra planificación de replicación debemos especificar un valor para la cláusula START WITH. La cláusula NEXT le dice a Oracle cuánto tiempo debe esperar entre cada refresco. Ya que será aplicada a diferentes bases de tiempo en cada refresco, la cláusula NEXT especifica una expresión de fecha en vez de una fecha fija. En el ejemplo precedente la expresión es NEXT SysDate+7

Cada vez que la vista materializada es refrescada, el siguiente refresco se planificará para 7 días después. Aunque la planificación de refresco en este ejemplo es muy simple, podemos usar muchas funciones de fechas de Oracle para personalizar la planificación de refresco. Por ejemplo, si queremos refrescar cada Domingo a partir de la fecha actual, podemos asignar la cláusula NEXT a NEXT NEXT_DAY(TRUNC(SysDate), 'DOMINGO')+12/24

Para que ocurran los refrescos automáticos debemos tener al menos un proceso de refresco de fondo ejecutándose en nuestra base de datos. El proceso de refresco se ejecuta periódicamente y mira si cualquier vista materializada de la base de datos necesita ser refrescada. El número de procesos que se ejecutan en nuestra base de datos se determina en un parámetro de inicialización llamado JOB_QUEUE_PROCESSES. Este parámetro debe asignarse (en nuestro fichero de parámetros de inicialización) a un valor más grande que cero; para la mayoría de los casos, un valor de 1 debería ser suficiente. Un proceso coordinador comienza los procesos de la cola de tareas cuando es necesario. Si la base de datos no está ejecutando los procesos de la cola de tareas necesitamos usar métodos de refresco manual. 9.7.3. Refrescos manuales. Además de los refrescos automáticos, podemos realizar refrescos manuales de vistas materializadas. Esto rescribe la planificación normal de los refrescos; el nuevo valor de START WITH deberá estar basado en el momento de nuestro refresco manual. Para refrescar una única vista materializada se usa el procedimiento DBMS_MVIEW.REFRESH. Tiene dos Oracle /232

parámetros principales: el nombre de la vista materializada y el método a usar. En este método podemos especificar 'c' para un refresco completo, 'f' para un refresco rápido, 'p' para un refresco rápido usando Partition Change Tracking (PCT) y '?' para forzar refresco. Por ejemplo: EXECUTE DBMS_MVIEW.REFRESH('LOCAL_LIBRO','c');

Nota. Partition Change Tracking (PCT) ocurre cuando operaciones de mantenimiento de partición se han realizado sobre las tablas referenciadas por la vista materializada. En PCT, Oracle realiza refrescos re-computando las filas en la vista afectadas por particiones cambiadas en tablas de detalle, evitando la necesidad de refrescos completos. Si refrescamos varias vistas materializadas mediante una única ejecución de DBMS_MVIEW.REFRESH, debemos listar los nombres de todas las vistas en el primer parámetro, y sus métodos de refresco en el segundo parámetro, tal como se muestra a continuación: EXECUTE DBMS_MVIEW.REFRESH( 'LOCAL_LIBRO,LOCAL_CATEGORIA', '?c' ); Podemos usar un procedimiento independiente en el paquete DBMS_MVIEW para

refrescar todas las vistas materializadas que están planificadas para refrescarse automáticamente. Este procedimiento, llamado REFRESH_ALL, refrescará cada vista materializada independientemente. No acepta ningún parámetro: EXECUTE DBMS_MVIEW.REFRESH_ALL_MVIEWS;

Ya que las vistas serán refrescadas mediante REFRESH_ALL consecutivamente, no serán refrescadas al mismo tiempo. Por lo tanto, una base de datos o servidor que falle durante la ejecución de este procedimiento puede causar que vistas materializadas locales no estén sincronizadas con otras. Si esto ocurre, simplemente hay que volver a ejecutar este procedimiento después de que se recupere la base de datos. Como alternativa, podemos crear grupos de refresco (ver siguiente sección). Otro procedimiento, DBMS_MVIEWS.REFRESH_ALL_MVIEWS, refresca todas las vistas materializadas que tengan las siguientes propiedades: • La vista no ha sido refrescada desde los cambios más recientes a la tabla origen o vistas origen de la cuales depende. • La vista y todas las tablas o vistas origen de las cuales depende son locales. • La vista está en la vista DBA_MVIEWS. Si creamos vistas materializadas anidadas, para asegurarnos de que sean refrescadas, podemos usar el procedimiento DBMS_MVIEW.REFRESH_DEPENDENT. 9.8. Sintaxis para crear registros de vista materializada. Un registro de vista materializada en una tabla que registra los cambios en las filas de la tabla origen y el historial de replicación de vistas materializadas. El registro de cambios en las filas puede entonces usarse durante refrescos para enviar a las vistas materializadas sólo las filas que han cambiado en el origen. Varias vistas materializadas basadas en la misma tabla pueden usar el mismo registro de vista materializada. La sintaxis resumida para el comando CREATE MATERIALIZED VIEW LOG se muestra a continuación: CREATE MATERIALIZED VIEW LOG ON [esquema .] tabla [{ cláusula de atributos físicos | TABLESPACE tablespace | { LOGGING | NOLOGGING } | { CACHE | NOCACHE } } [ cláusula de atributos físicos | TABLESPACE TABLESPACE | { LOGGING | NOLOGGING } | { CACHE | NOCACHE } ]... ] [ cláusula paralelo ] [ cláusula particionado ] [WITH { OBJECT ID | PRIMARY KEY | ROWID | SEQUENCE | ( columna [, columna ] . . . ) } [, { OBJECT ID | PRIMARY KEY | ROWID | SEQUENCE | (columna [,columna ] . . . ) }] . . . ] [{ INCLUDING | EXCLUDING } NEW VALUES] ; comando CREATE MATERIALIZED VIEW LOG se ejecuta en la base de datos de la tabla origen,

El normalmente por el propietario de la tabla origen. Los registros de vista materializada no deben crearse para tablas que sólo Oracle /233

están involucradas con vistas complejas. No se especifica un nombre para el registro de vista materializada. Un registro de vista materializada para la tabla LIBRO puede crearse mediante el siguiente comando, ejecutado dentro de la cuenta del propietario de la tabla: CREATE MATERIALIZED VIEW LOG ON LIBRO WITH SEQUENCE, ROWID (Titulo, Autor, Categoria) INCLUDING NEW VALUES; cláusula WITH SEQUENCE es necesaria para soportar

La la replicación de operaciones mixtas DML a través de varias tablas base. Debido a que los registros de vista materializada pueden aumentar impredeciblemente a lo largo del tiempo en las bases de datos de producción, deberíamos considerar almacenar sus objetos asociados en tablespaces dedicados para los registros de vista materializada. 9.8.1. Permisos de sistema requeridos. Para crear el registro de vista materializada debemos tener los permisos del sistema CREATE TABLE y CREATE TRIGGER. Si estamos creando el registro de vista materializada para una cuenta de usuario que no es propietaria de la tabla origen, necesitamos tener los permisos de sistema CREATE ANY TABLE, COMMENT ANY TABLE y CREATE ANY TRIGGER, así como el permiso SELECT sobre la tabla origen de la vista. 9.9. Modificando vistas materializadas y registros. Podemos modificar los parámetros de almacenamiento, opciones de refresco, y planificación de refresco para vistas materializadas existentes. Si no estamos seguros de las opciones actuales para una instantánea, podemos consultar la vista USER_MVIEWS del diccionario de datos. El siguiente ejemplo muestra cómo modificar la opción de refresco para la vista materializada LOCAL_LIBRO usando el comando ALTER MATERIALIZED VIEW. ALTER MATERIALIZED VIEW LOCAL_LIBRO REFRESH COMPLETE; Todos los futuros refrescos de LOCAL_LIBRO refrescarán

la tabla base local por completo. Para modificar una vista materializada debemos ser propietarios de la vista o tener el permiso de sistema ALTER ANY MATERIALIZED VIEW. Para modificar un registro de vista materializada debemos ser propietarios de la tabla, tener el permiso ALTER para la tabla, o tener el permiso de sistema ALTER ANY TABLE. Si creamos una vista materializada sin las cláusulas RowID o SEQUENCE, podemos añadirlas después mediante el comando ALTER MATERIALIZED VIEW. 9.10. Eliminando vistas materializadas y registros. Para borrar una vista materializada debemos tener los permisos de sistema requeridos para borrar tanto la vista como todos los objetos relacionados. Necesitamos tener el permiso DROP MATERIALIZED VIEW si los objetos están en nuestro esquema, o el permiso DROP ANY MATERIALIZED VIEW si la vista no está en nuestro esquema. El siguiente comando borra la vista materializada LOCAL_CATEGORIA_COUNT: DROP MATERIALIZED VIEW LOCAL_CATEGORIA_COUNT;

Nota. Cuando borramos una vista materializada que fue creada sobre una tabla predefinida, la tabla todavía existirá pero la vista será borrada. Los registros de vista materializada pueden ser borrados mediante el comando DROP MATERIALIZED VIEW LOG. Una vez que el registro de vista materializada es borrado de una tabla origen, ya no se harán refrescos rápidos para las vistas materializadas simples basadas en la tabla. Un registro de vista materializada debería ser borrado cuando no haya vistas materializadas simples basadas en la tabla origen. El siguiente comando borra el registro de vista materializada que fue creado para la tabla LIBRO previamente: DROP MATERIALIZED VIEW LOG ON LIBRO;

Para borrar el registro de vista materializada debemos tener la habilidad de borrar tanto el registro de vista como sus objetos relacionados. Si somos el propietario del registro, debemos tener los permisos de sistema DROP TABLE y DROP TRIGGER. Si no somos propietarios del registro necesitamos los permisos DROP ANY TABLE y DROP ANY TRIGGER para ejecutar este comando.

Oracle /234

10. Oracle Text Como la cantidad de texto en nuestras bases de datos aumenta, se hacen más complejas las consultas sobre texto de la base de datos. En vez de realizar búsquedas de strings por comparación podemos usar las nuevas funcionalidades de búsqueda de texto (cosas como dar peso a términos en una búsqueda de varios términos, o clasificar los resultados de una búsqueda de texto). Podemos usar Oracle Text para realizar búsquedas basadas en texto. Las capacidades de búsqueda de texto incluyen caracteres comodín, búsquedas difusas, clasificación por importancia, búsquedas por proximidad, términos con peso, y expansión de palabras. 10.1. Añadiendo texto a la base de datos. Podemos añadir texto a una base de datos bien almacenando físicamente el texto en una tabla o bien almacenando punteros a ficheros externos de la base de datos. Esto es, para los libros de una estantería, podemos almacenar trozos en la base de datos o en archivos externos. Si almacenamos los trozos en archivos externos, entonces podemos almacenar los nombres del archivo en la base de datos Para almacenar trozos de libros en nuestra base de datos, podemos crear la tabla TROZO_LIBRO. Para los ejemplos de este capítulo crearemos dos tablas: TROZO_LIBRO_CONTEXT y TROZO_LIBRO_CTXCAT. Ambas tablas cargarán los mismos datos y cada una usará un tipo de índice distinto.

CREATE TABLE TROZO_LIBRO_CONTEXT ( Titulo VARCHAR2(100) PRIMARY KEY, Autor VARCHAR2(25), Fecha_Libro DATE, Trozo VARCHAR2(4000) ); / INSERT INTO TROZO_LIBRO_CONTEXT VALUES ( 'La montaña de luz', 'Emilio Salgari', '01-MAY-02', 'Una muy calurosa tarde de julio de 1843, un elefante de estatura gigantesca, trepaba fatigosamente los últimos escalones del altiplano de Pannah, uno de los más salvajes y al mismo tiempo más pintorescos de la India central.' ); / CREATE TABLE TROZO_LIBRO_CTXCAT ( Titulo VARCHAR2(100) PRIMARY KEY, Autor VARCHAR2(25), Fecha_Libro DATE, Trozo VARCHAR2(4000) ); / INSERT INTO TROZO_LIBRO_CTXCAT VALUES ( 'La montaña de luz', 'Emilio Salgari', '01-MAY-02', 'Una muy calurosa tarde de julio de 1843, un elefante de estatura gigantesca, trepaba fatigosamente los últimos escalones del altiplano de Pannah, uno de los más salvajes y al mismo tiempo más pintorescos de la India central.' ); La columna Trozo de las tablas has sido definida como VARCHAR2(4000). Para valores grandes podemos considerar el uso del tipo CLOB.

Podemos seleccionar el texto del trozo desde la base de datos con la siguiente consulta: SELECT Trozo FROM TROZO_LIBRO_CONTEXT WHERE Titulo = 'La montaña de luz';

10.2. Consultas de texto e índices de texto. Las consultas de texto son diferentes de las consultas de datos porque las palabras tienen significado, Oracle /235

relaciones con otras palabra y opuestos. Podemos querer buscar palabras que estén cerca de otras, o palabras que estén relacionadas con otras. Estas consultas deberían ser difíciles de hacer con los operadores relacionales estándar. Al extender SQL para incluir índices de texto, Oracle Text permite realizar consultas muy complejas sobre el texto. Para usar Oracle Text, necesitamos crear un índice de texto sobre la columna en la cual se almacena el texto. Un índice de texto es un término ligeramente confuso (actualmente es una colección de tablas e índices que almacenan información acerca del texto guardado en la columna). Veremos cómo crear dos tipos de índices de texto: CONTEXT y CTXCAT. Podemos usar también un tercer tipo de índice, CTXRULE, para crear una aplicación de clasificación de documentos basada en contenido. Nota. Antes de crear un índice de texto sobre una tabla debemos crear una clave primaria para la tabla, sin es que aún no existe. Podemos crear un índice de texto mediante una versión especial del comando CRATE INDEX. Para un índice CONTEXT se especifica el tipo CTXSYS.CONTEXT en la cláusula INDEXTYPE, tal como se muestra a continuación: CREATE INDEX Trozo_Context_Index ON TROZO_LIBRO_CONTEXT(Trozo) INDEXTYPE IS CTXSYS.CONTEXT;

Cuando se crea el índice de texto, Oracle crea varios índices y tablas en nuestro esquema para soportar las consultas de texto. Podemos reconstruir nuestro índice de texto mediante el comando ALTER INDEX, tal como se hace con otros índices. Podemos usar un índice CTXCAT en lugar del tipo CONTEXT: CREATE INDEX Trozo_Ctxcat_Index ON TROZO_LIBRO_CTXCAT(Trozo) INDEXTYPE IS CTXSYS.CTXCAT; tipo de índice CTXCAT soporta

El sincronización transaccional de datos entre la tabla base (TROZO_LIBRO_CTXCAT) y sus índices de texto. Con índices CONTEXT necesitamos actualizar manualmente el índice de texto después de cada cambio en la tabla base. Los índices de tipo CTXCAT no generan valores de "puntuación" durante las consultas de texto (como sí lo hacen los índices CONTEXT), pero la sintaxis de consulta es más larga. 10.2.1. Consultas de texto. Una vez creado un índice de texto sobre la columna Trozo de la tabla TROZO_LIBRO_CONTEXT, las capacidades de búsqueda de texto se incrementa dramáticamente. Ahora podemos mirar por cualquier libro que contenga la palabra 'elefante': SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'elefante') > 0;

Nota. En esta búsqueda no se tienen en cuenta las mayúsculas y minúsculas de la palabra buscada. La función CONTAINS tiene dos parámetros (el nombre de la columna y el string de búsqueda) y comprueba el índice de texto para la columna Trozo. Si la palabra "elefante" es encontrada en el texto de la columna Trozo, entonces la base de datos retorna un valor de puntuación mayor que 0. La puntuación es una valoración de cómo los registros retornados casan con el criterio especificado por la función CONTAINS. Si creamos un índice CTXCAT, debemos usar la función CATSEARCH en vez de CONTAINS. La función CATSEARCH tiene tres parámetros: el nombre de columna, el string de búsqueda, y el nombre del conjunto de índice. (Los conjuntos de índices son descritos posteriormente en este capítulo.) En este ejemplo no hay ningún conjunto de índice, así que el parámetro se asignará a NULL: SELECT Titulo FROM TROZO_LIBRO_CTXCAT WHERE CATSEARCH(Trozo, 'elefante', NULL) > 0; CATSEARCH no computa puntuaciones, pero usa la sintaxis >0

por una cuestión de compatibilidad con la función CONTAINS. Cuando una función como CONTAINS o CATSEARCH se usa en una consulta, la porción de texto de la consulta es procesada por Oracle Text. El resto de la consulta es procesado de forma regular. Los resultados de la consulta de texto y el procesamiento de la consulta normal son combinados para retorna un único conjunto de registros. Oracle /236

10.2.2. Expresiones de consultas de texto disponibles. Oracle Text sería muy limitado si sólo se dedicara a buscar coincidencia de palabras. Oracle Text ofrece muchas capacidades de búsqueda que podemos usar para personalizar nuestras consultas. Muchas de estas capacidades se aplican mediante las funciones CONTAINS y CATSEARCH, las cuales sólo operan en la cláusula WHERE de una consulta SELECT, y nunca en comandos INSERT, UPDATE o DELETE. Los operadores dentro de CONTAINS y CATSEARCH permiten realizar las siguientes búsquedas de texto: • Coincidencia exacta de palabas o frases. • Coincidencia exacta de varias palabras, usando lógica booleana para combinar búsquedas. • Búsquedas basadas en cómo unas palabras están cerca de otras. • Búsqueda de palabras que tienen la misma palabra "raíz". • Coincidencias difusas de palabras. • Búsquedas de palabras que suenan como otras. 10.2.3. Buscando la coincidencia exacta de una palabra. La siguiente consulta sobre las tablas TROZO_LIBRO retorna el título de todos los trozos que incluyen la palabra 'India': REM Método CONTAINS para índices CONTEXT: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'India') > 0;

REM Método CATSEARCH para índices CTXCAT: SELECT Titulo FROM TROZO_LIBRO_CTXCAT WHERE CATSEARCH(Trozo, 'India', NULL) > 0;

Dentro de las llamadas a la función, el operador precedente puede ser leída de la siguiente manera:

>

es denominado un operador de umbral. La consulta

Selecciona todos los valores de la columna Titulo Dentro de la tabla TROZO_LIBRO_CONTEXT Donde la puntuación para la búsqueda de texto de la columna Trozo Para una búsqueda exacta de la palabra 'India' exceda un valor de umbral cero

El análisis de umbral compara la puntuación (la puntuación interna calculada por Oracle cuando se realiza la búsqueda de texto) con el valor de umbral. Los valores de umbral para búsquedas individuales están en el rango 0 a 10. Para índices CONTEXT podemos mostrar esta puntuación como parte de la consulta. Para mostrar la puntuación del texto buscado, se usa la función SCORE, la cual tiene un único parámetro (una etiqueta que asignamos a la puntuación dentro del texto buscado): SELECT Titulo, SCORE(10) FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'India', 10) > 0 ;

TITULO -----------------------------La montaña de luz

SCORE(10) -------------3

En esta consulta, los parámetros de la función CONTAINS se modifican para incluir una etiqueta (10) para realizar la operación de búsqueda. La función SCORE muestra la puntuación de la búsqueda de texto asociada con la etiqueta. La puntuación es un cálculo interno basado en cómo el texto indexado coincide con el criterio. En índices CONTEXT podemos usar la función SCORE en la lista del SELECT o en un GROUP BY o en un ORDER BY. 10.2.4. Buscando por coincidencia exacta en varias palabras. Para buscar por varias palabras podemos usar lógica booleana (con operadores AND y OR) para combinar los resultados de varia búsquedas de texto en una única consulta. Podemos también buscar por varios términos en la misma llamada a la función y dejar que Oracle resuelva el resultado de la búsqueda. Por ejemplo, si queremos buscar por libros que tengan las palabras "India" y "elefante" en el texto del trozo, podemos usar la siguiente consulta: REM método CONTAINS para índices CONTEXT: SELECT Titulo FROM TROZO_LIBRO_CONTEXT

Oracle /237

WHERE CONTAINS(Trozo, 'India AND elefante') > 0; REM método CATSEARCH para índices CTXCAT: SELECT TITLE FROM TROZO_LIBRO_CTXCAT WHERE CATSEARCH(Trozo, 'India AND elefante', NULL) > 0;

Nota. Esta búsqueda no busca por la frase "India and elefante" sino que busca por cada palabra individual del texto. En vez de usar AND en el índice CONTEXT, podríamos usar un ampersand (&). Antes de usar este método en SQL*Plus, deberíamos asignar SET DEFINE OFF para que el caracter & no sea visto como parte del nombre de variable: SET DEFINE OFF REM método CONTAINS para índices CONTEXT: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'India & elefante') > 0; Para el índice CTXCAT, la palabra AND puede ser obviada: REM método CATSEARCH para índices CTXCAT: SELECT Titulo FROM TROZO_LIBRO_CTXCAT WHERE CATSEARCH(Trozo, 'India elefante', NULL) > 0; Usar el caracter & o la palabra AND denotas una operación AND (así fila sólo si el texto revisado incluye ambas palabra "India" y "elefante".

que la función

CONTAINS

retornará un

Cada búsqueda debe pasar los criterios de umbral definidos por las puntuaciones de búsqueda. Si queremos buscar por más de dos términos basta con añadirlos a la función CONTAINS o CATSEARCH: REM método CONTAINS para índices CONTEXT: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'India AND elefante AND julio') > 0;

REM método CATSEARCH para índices CTXCAT: SELECT Titulo FROM TROZO_LIBRO_CTXCAT WHERE CATSEARCH(REVIEW_TEXT, 'India elefante julio', NULL) > 0; Además del operador AND, podemos usar el operador OR. El símbolo de OR para horizontal ( | ), así que las dos siguientes consultas son procesadas idénticamente: REM método CONTAINS para índices CONTEXT: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'India OR elefante') > 0;

Oracle Text es la barra

SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'India | elefante') > 0; operador ACCUM proporciona otro método para combinar

El búsquedas. ACCUM suma las puntuaciones de búsquedas individuales y compara las puntuaciones acumuladas con el valor de umbral. El símbolo para ACCUM es una coman (,). Por lo tanto estas dos consultas son equivalentes: REM método CONTAINS para índices CONTEXT: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'India ACCUM elefante') > 0;

SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'India , elefante') > 0; sintaxis ACCUM es soportada por la función CATSEARCH

La pero debería no ser usada porque CATSEARCH no calcula una puntuación para comparar el valor de umbral. Podemos usar Oracle Text para restar las puntuaciones de varias búsquedas antes de comparar el resultado a la puntuación de umbral. El operador MINUS en CONTAINS resta la puntuación de la búsqueda del segundo Oracle /238

término de la puntuación de la resta del primer término. La consulta siguiente determinará la puntuación de búsqueda de 'India' y lo restará de la puntuación de búsqueda de 'casa' y entonces comparará la diferencia con la puntuación de umbral. En este ejemplo, el segundo término ('casa') no se encuentra en el texto indexado. Si el segundo término está en el texto (por ejemplo 'elefante'), entonces no retornará ninguna fila. REM método CONTAINS para índices CONTEXT: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'India MINUS casa') > 0; SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'India - casa') > 0;

Podemos usar el símbolo menos (–) en lugar del operador MINUS, tal como se ha mostrado en el ejemplo previo. Para índices CONTEXT el operador – reduce la puntuación cuando comparamos la puntuación de búsqueda total con el valor de umbral, pero no elimina la fila a considerar. Para eliminar filas basadas sobre términos de búsqueda en índices CONTEXT, se usa el caracter ~ como el operador NOT. Para índices CTXCAT, el símbolo – tiene un significado diferente al de los índices CONTEXT. Para índices CTXCAT, el signo – le dice a Oracle Text que no retorne la fila si el término de búsqueda después del guión se encuentra (como ~ en índices CONTEXT). Si el segundo término es encontrado, CATSEARCH no retornará la fila. Para consultas CATSEARCH podemos reemplazar el guion por la palabra NOT. REM método CATSEARCH para índices CTXCAT: SELECT Titulo FROM TROZO_LIBRO_CTXCAT WHERE CATSEARCH(Trozo, 'India - elefante', NULL) > 0;

SELECT Titulo FROM TROZO_LIBRO_CTXCAT WHERE CATSEARCH(Trozo, 'Indica NOT elefante', NULL) > 0;

Podemos usar paréntesis para aclarar la lógica dentro de nuestro criterio de búsqueda. Si nuestra búsqueda usa tanto AND como OR, deberíamos usar paréntesis para aclarar el camino en el cual las filas deben ser procesadas. Por ejemplo, la siguiente consulta retorna una fila si el texto de búsqueda contiene la palabra 'casa' o las palabras 'India' y 'elefante' a la vez.

La

REM método CONTAINS para índices CONTEXT: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(REVIEW_TEXT, 'casa OR (India AND elefante)') > 0; función CATSEARCH no requiere la palabra AND entre 'India' y 'elefante': REM método CATSEARCH para índices CTXCAT: SELECT Titulo FROM TROZO_LIBRO_CTXCAT WHERE CATSEARCH(REVIEW_TEXT, 'casa | (India elefante)', NULL) > 0;

Cuando evaluamos las puntuaciones de varias búsquedas mediante índices CONTEXT, podemos decirle a Oracle Text el peso de las puntuaciones de algunas búsquedas más importantes de otras. Por ejemplo, si queremos que la puntuación para 'India' sea el doble cuando comparamos el puntuación de umbral, podemos usar el asterisco (*) para indicar el factor por el cual la puntuación de búsqueda se multiplicará. La siguiente consulta doblará la puntuación de búsqueda para 'India' cuando lo evaluemos en una condición OR: SELECT Titulo, SCORE(10) FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(REVIEW_TEXT, 'India*2 OR elefante*1', 10) > 5;

Nota. El operador EQUIV trata dos términos de búsqueda como el mismo durante la puntuación de búsqueda. EQUIV (el cual puede ser reemplazado por =) puede ser útil en combinaciones con los operadores que comparan puntuaciones de búsqueda.

Oracle /239

10.2.5. Búsqueda por coincidencia de una frase. Cuando buscamos por coincidencia en una frase, debemos especificar la frase como parte del string de búsqueda. Si la frase incluye una palabra reservada (como "and", "or", o "minus"), necesitamos usar los caracteres de escape mostrados en esta sección para que la búsqueda sea ejecutada apropiadamente. Si la frase buscada incluye una palabra reservada de Oracle Text, entonces debemos usar las llaves { y } para encerrarla. La siguiente consulta busca por cualquier párrafo que incluya la frase "muy calurosa tarde". REM método CONTAINS para índices CONTEXT: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'muy calurosa tarde') > 0;

La

REM método CATSEARCH para índices CTXCAT: SELECT TITLE FROM TROZO_LIBRO_CTXCAT WHERE CATSEARCH(REVIEW_TEXT, '"muy calurosa tarde"', NULL) > 0; siguiente consulta busca la frase "transactions and finances". La palabra "and" REM método CONTAINS para índices CONTEXT: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'transactions {and} finances') > 0;

es encerrada entre llaves.

REM método CATSEARCH para índices CTXCAT: SELECT Titulo FROM TROZO_LIBRO_CTXCAT WHERE CATSEARCH(Trozo, '"transactions {and} finances"', NULL) > 0;

Podemos encerrar toda la frase entre llaves, en cuyo caso cualquier palabra reservada dentro de la frase será tratada como parte del criterio de búsqueda, tal como muestra el siguiente ejemplo: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, '{transactions and finances}') > 0;

10.2.6. Búsqueda de palabras que están cerca de otras. Podemos usar capacidades de búsqueda por proximidad para realizar una búsqueda basada en cómo términos cerrados están cerca dentro del mismo documento. Una búsqueda de proximidad retorna una puntuación alta para palabras que están cercas de las otras, y retorna una puntuación baja para palabras que están apartadas. Si las palabras continúan una a la otra, la búsqueda de proximidad retorna una puntuación de 100. Para usar búsqueda de proximidad a través de índices CONTEXT se usa la palabra NEAR, como en el siguiente ejemplo: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'elefante NEAR estatura')>0; Podemos reemplazar el operador NEAR por el símbolo de punto

y coma (;). En las consultas con índice CONTEXT podemos especificar el máximo número de palabras entre los términos buscados. Por ejemplo, para palabras dentro de una separación de 10 palabras de otra, podemos usar la cadena de búsqueda 'NEAR((India, elefante),10)'. 10.2.7. Usando caracteres comodín durante búsquedas. En los ejemplos previos de este capítulo, las consultas seleccionan valores de texto que coinciden exactamente con los criterios especificados. Podemos usar caracteres comodines para ampliar la lista de términos de búsqueda válidos durante nuestra consulta. Al igual que con las expresiones regulares usadas con el operador LIKE, hay dos caracteres comodines disponibles: % representa cualquier cadena de texto de cero o más caracteres de cualquier longitud. _ representa un carácter. La siguiente consulta busca por todas las coincidencias de texto para palabras que comienzan con los caracteres "escalo": SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'escalo%') > 0;

La siguiente consulta limita la expansión del string de texto exactamente a tres caracteres. En lugar del signo Oracle /240

%

se usan tres guiones de subrayado (_ _ _).

SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'escalo___') > 0;

Deberíamos usar caracteres comodines cuando conozcamos algunos caracteres pertenecientes al string de búsqueda. Si no estamos seguros del string de búsqueda, deberíamos usar uno de los métodos descritos en las siguientes secciones: palabras raíces, coincidencias difusas o coincidencias SOUNDEX. 10.2.8. Buscando palabras que tienen la misma raíz. En vez de usar caracteres comodines podemos usar las capacidades de expansión por raíz para ampliar la lista de strings de texto. Dada la raíz de una palabra, Oracle amplía la lista de palabras para buscar por todas las palabras que tengan la misma raíz. Unas ampliaciones simples se muestran a continuación: Raíz Raíz ampliada casa casamentera, casanova, casado trabajo trabajador, trabajoso historia historiador Como "trabajo" y "trabajador" tienen la misma raíz, una búsqueda de raíz-ampliada usando la palabra "trabajador" retornará el texto que contenga la palabra "trabajo". Para usar la ampliación de raíz con una consulta necesitamos usar el signo de dólar ( $). Dentro de string de búsqueda, el $ debe preceder inmediatamente a la palabra que queremos ampliar. La siguiente consulta busca todos los trozos que contienen una palabra con la raíz "gigante": SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(REVIEW_TEXT, '$gigante') > 0;

Cuando esta consulta se ejecuta, Oracle expande la palabra "gigante" para incluir todas las palabras con la misma raíz. 10.2.9. Búsquedas por coincidencia difusa. Una búsqueda difusa amplia el término de búsqueda para incluir palabras que son fonéticamente similares pero que no necesariamente tiene la misma raíz. Las búsquedas difusas son más útiles cuando el texto contiene erratas. Las erratas pueden estar tanto en el texto de búsqueda como en el string buscado. Por ejemplo, la siguiente consulta no retornará el título "La montaña de luz" porque no contiene la palabra "elegante". SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'elegante') > 0; embargo, sí contiene la palabra "elefante", que

Sin es muy parecida. Una búsqueda difusa retornará trozos que contenga la palabra "elefante" aunque el término de búsqueda sea "elegante". Para usar una búsqueda difusa, hay que preceder el término de búsqueda con un signo de interrogación ( ?), sin ningún espacio entre ellos. El siguiente ejemplo ilustra el uso de esta capacidad de coincidencia: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, '?elegante') > 0;

10.2.10. Búsqueda de palabras que suenan igual que otras palabras. Las búsquedas de raíz-ampliada amplían un término de búsqueda a varios términos basados en una raíz. Las búsquedas difusas amplían la búsqueda a términos basados en palabras similares en el índice de texto. Una tercera forma de ampliar términos de búsqueda, SOUNDEX, amplia la búsqueda según cómo suenan las palabras. El método de expansión SOUNDEX usa la misma lógica de coincidencia que la función SOUNDEX. Para usar la opción SOUNDEX, debemos preceder el término de búsqueda con una marca de exclamación ( !). Durante la búsqueda, Oracle evalúa los valores de SOUNDEX para los términos en el índice de texto y busca por todas las palabras que tienen el mismo valor de SOUNDEX. Como muestra la siguiente consulta, podemos buscar por los trozos que incluyan la palabra "fatigosamente" usando la coincidencia fonética "fastidiosamente": SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Texto_Revidado, '!fastidiosamente') > 0;

Podemos también anidar operadores, permitiendo realizar ampliaciones por raíz sobre los términos Oracle /241

retornados por una búsqueda difusa. En el siguiente ejemplo, se realiza una búsqueda difusa sobre la palabra "alto", y el término retornado se amplía usando raíz-ampliada:

Las

SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, '$?alto') > 0; opciones principales de búsqueda para CONTAINS

se resumen en la siguiente tabla.

Operador Descripción Devuelve un registro si uno u otro término de búsqueda exceden el umbral. OR | Equivalente a OR. Devuelve un registro si ambos términos de búsqueda exceden el umbral. AND & Equivalente a AND. Devuelve un registro si la suma de las puntuaciones de los términos de búsqueda exceden el ACCUM umbral Devuelve un registro si la puntuación de la primera búsqueda menos la puntuación de la segunda MINUS búsqueda exceden el umbral. – Equivalente a MINUS. Asigna diferentes pesos a la puntuación de búsqueda. * La puntuación se basará en cómo de cerca los términos de búsqueda están uno junto al otro en el NEAR texto buscado. ; Equivalente a NEAR. NOT Excluye el registro si el término después del NOT es encontrado. ~ Equivalente a NOT. EQUIV Trata dos términos (term1 EQUIV term2) como el mismo durante la puntación de búsqueda. = Equivalente a EQUIV. Encierra palabras reservadas como parte del término de búsqueda. {} Comodín para varios caracteres. % Comodín para un único caracter. _ Realiza expansión por raíz. $ Realiza una búsqueda difusa. ? ! Realiza una búsqueda SOUNDEX. Especifica el orden en el cual el criterio de búsqueda es evaluado. ()

Las opciones de búsqueda para CATSEARCH se resumen en la siguiente tabla. Operador Descripción Devuelve un registro si uno de los términos de búsqueda es encontrado. | Devuelve un registro si cada término de búsqueda es encontrado. AND Si hay espacios alrededor del guión, CATSEARCH retorna filas que contiene el término que precede al guión y que no contienen el término que sigue al guión. Un guión sin espacios es tratado como un caracter regular. Equivalente al guión. NOT Encierra frases. "" Especifica el orden en el cual el criterio de búsqueda es evaluado. () Comodín para varios caracteres. Puede estar al final del término o en el medio de los caracteres. *

10.2.11. Usando el operador «ABOUT». En Oracle Text podemos buscar sobre temas de documentos. La búsqueda temática está integrada con la búsqueda de términos de texto. Podemos usar el operador ABOUT para buscar términos que tienen que ver con el tema del documento en vez de términos específicos dentro del documento. Aquí hay un ejemplo: REM método CONTAINS para índices CONTEXT: SELECT Titulo FROM TROZO_LIBRO_CONTEXT WHERE CONTAINS(Trozo, 'ABOUT(Medicina)') > 0;

REM método CATSEARCH para índices CTXCAT: SELECT Titulo FROM TROZO_LIBRO_CTXCAT WHERE CATSEARCH(Trozo, 'ABOUT(Medicina)', NULL) > 0;

Oracle /242

10.2.12. Sincronización de índice. Por defecto, cuando usamos índices CONTEXT, tenemos que gestionar los contenidos del índice de texto; los índices de texto no serán actualizados cuando la tabla base sea actualizada. En cuanto un valor del campo Trozo sea actualizado, el índice de texto dejará de estar sincronizado con la tabla base. Para sincronizar el índice hay que ejecutar el procedimiento CTX_DDL. SYNC_INDEX, tal como se muestra a continuación: EXECUTE CTX_DDL.SYNC_INDEX('Trozo_Context_Index');

Nota. Debemos conceder el permiso estas actividades de mantenimiento.

EXECUTE

sobre el paquete

CTX_DDL

al usuario para soportar

Desde Oracle Database 10g, los índices CONTEXT pueden mantenerse automáticamente, en el momento de confirmación de cambios, o en intervalos especificados. Como parte del comando CREATE INDEX para un índice CONTEXT, podemos usar la cláusula SYNC: [[METADATA] SYNC (MANUAL | EVERY "intervalo" | ON COMMIT)]

Aquí hay un ejemplo:

DROP INDEX Trozo_Context_Index; / CREATE INDEX Trozo_Context_Index ON TROZO_LIBRO_CONTEXT(Trozo) INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS ('SYNC (ON COMMIT)');

10.3. Conjuntos de índices. Históricamente, los problemas con consultas de índices de texto han ocurrido cuando otros criterios son usados sobre el texto de búsqueda como parte de la cláusula WHERE. Por ejemplo, ¿cuándo se aplican cláusulas WHERE sobre columnas no de texto?, ¿antes o después de que la búsqueda de texto se complete?, y ¿cómo se ordena los resultados apropiadamente? Para mejorar las capacidades de mezclar consultas podemos usar conjuntos de índices. Los índices dentro del conjunto pueden ser sobre columnas relacionales estructuradas o columnas de texto. Para crear un conjunto de índices se usa el paquete CTX_DDL para crear el conjunto de índices y añadirle índices. Cuando creamos un índice de texto podemos entonces especificar el conjunto de índices. Por ejemplo, para crear un conjunto de índices llamado Trozos, se usa el procedimiento CREATE_INDEX_SET: EXECUTE CTX_DDL.CREATE_INDEX_SET('Trozos');

Ahora podemos añadir índices al conjunto mediante el procedimiento ADD_INDEX. Primero añadiremos unos estándar, índices no de texto: EXECUTE CTX_DDL.ADD_INDEX('Trozos', 'Autor'); EXECUTE CTX_DDL.ADD_INDEX('Trozos', 'Fecha_Libro'); Ahora crearemos un índice de texto CTXCAT. Se especifica CTXSYS.CTXCAT de índices en la cláusula PARAMETERS: CREATE INDEX Trozo_CtxCat_Indice ON TROZO_LIBRO_CTXCAT(Trozo) INDEXTYPE IS CTXSYS.CTXCAT PARAMETERS ('INDEX SET Trozos');

como tipo de índice, y el conjunto

Ahora podemos ordenar nuestros resultados por el resultado de la búsqueda del conjunto de índices combinados: SELECT * FROM TROZO_LIBRO_CTXCAT WHERE CATSEARCH( Trozo, 'julio', 'Autor=''Emilio Salgari'' ORDER BY Fecha_Libro DESC') > 0;

Para resolver esta consulta, Oracle Text usará el conjunto de índices, permitiendo ordenar los resultados apropiadamente, por fecha. Nótese que se han utilizado dos comillas seguidas para encapsular 'Emilio Salgari', para que de esta forma sean convertidas a una comilla simple cuando la consulta se ejecute. Los conjuntos de índices pueden contener hasta 99 índices de tipos NUMBER, DATE, CHAR y VARCHAR2. Las columnas en un índice del conjunto no pueden exceder de 30 bytes (así que en este ejemplo, la columna Titulo no puede ser indexada como parte del conjunto de índices). Las columnas de los índices no pueden contener valores nulos. Desde Oracle Database 10g, Oracle Text incluye una facilidad de trazado para ayudar a identificar cuellos de botella en la indexación y consultas. Se usa el procedimiento CTX_OUTPUT.ADD_TRACE para habilitar el Oracle /243

trazado. Las trazas disponibles incluyen el tiempo gastado sobre los diferentes componentes de la búsqueda, el número de octetos leídos, y el número de filas procesadas.

11. Uso de tablas externas Podemos usar la funcionalidad de tablas externas para acceder a ficheros externos como si fuesen tablas dentro de la base de datos. Cuando creamos una tabla externa podemos definir su estructura y localización dentro de Oracle. Cuando consultamos la tabla, Oracle lee la tabla externa y retorna los resultados tal como si los datos estuviesen almacenados dentro de la base de datos. Lo mejor es que, aunque los datos están fuera de la base de datos, no necesitamos conocer el proceso de carga dentro de la base de datos. Las tablas externas tiene límites (no podemos actualizar o borrar sus registros dentro de Oracle, y no podemos indexarlas). Debido a que son parte de nuestra aplicación de base de datos, tendremos que incluirlas como parte de nuestros procesos de copias de respaldo y recuperación. A pesar de estas complicaciones, las tablas externas pueden ser una potente adición a nuestra arquitectura de base de datos. Desde Oracle Database 10g, podemos usar tablas externas para descargar datos desde tablas a ficheros externos mediante dispositivos de acceso ORACLE_DATAPUMP. 11.1. Accediendo a datos externos. Para acceder a ficheros externos dentro de Oracle debemos primero usar el comando CREATE DIRECTORY para definir un objeto DIRECTORY que apunte a la ubicación de los ficheros externos. Los usuarios que accedan a los ficheros externos deben tener el permiso READ sobre el directorio. Nota. Antes de empezar, debemos verificar que el directorio externo existe y que el usuario que publicará el comando CREATE DIRECTORY tenga el permiso de sistema CREATE ANY DIRECTORY. El siguiente ejemplo crea un directorio llamado Empleado:

LIBRO_DIR

y concede accesos

READ

y

WRITE

al esquema

CREATE DIRECTORY LIBRO_DIR AS 'e:\oracle\external'; GRANT READ ON DIRECTORY LIBRO_DIR TO Empleado; GRANT WRITE ON DIRECTORY LIBRO_DIR TO Empleado; usuario Empleado puede ahora leer ficheros del directorio e:\oracle\external

El como si estuviera dentro de la base de datos. Al tener permiso de escritura, el usuario puede crear ficheros de log, descartes y "malos" dentro del directorio. La siguiente lista genera dos ficheros para datos de ejemplo (uno para LIBRO y otro para AUTOR). Nótese que el comando SPOOL no usa el nombre de directorio creado, sino que hay que especificar la ruta física del directorio. CONNECT Empleado/contraseña SET PAGESIZE 0 NEWPAGE 0 FEEDBACK OFF SPOOL e:\oracle\external\libro_dump.lst SELECT Titulo || '~' || Autor || '~' || Categoria || '~' || Valoracion || '~' FROM LIBRO ORDER BY Titulo; SPOOL OFF SPOOL e:\oracle\external\autor_dump.lst SELECT Titulo || '~' || Autor || '~' FROM AUTOR ORDER BY Titulo; SPOOL OFF

Nota. El comando SPOOL de SQL*Plus permite redirigir la salida a un fichero de texto. Los datos no se materializarán en el fichero de texto hasta que se ejecute SPOOL OFF. Además de los datos, los ficheros de salida contendrán la consulta en las líneas iniciales y una línea final con "SQL> SPOOL OFF". Para simplificar los ejemplos, deberíamos manualmente editar los ficheros al nivel del sistema operativo y eliminar estas líneas extra. Si otro usuario debe acceder a los datos de los ficheros libro_dump.lst y autor_dump.lst, debemos concederle el permiso READ sobre el directorio LIBRO_DIR: GRANT READ ON DIRECTORY LIBRO_DIR TO otroUsuario;

También los ficheros mismos deber ser legibles por el usuario de Oracle al nivel del sistema operativo. Oracle /244

11.2. Creando una tabla externa. Ahora que hay datos externos disponibles y accesibles, podemos crear una estructura de tabla que acceda a ellos. Para hacer esto necesitamos usar la cláusula ORGANIZATION EXTERNAL del comando CREATE TABLE. Dentro de esta cláusula podemos especificar la estructura de datos, tal como se hace en el fichero de control de SQL*Loader. 11.2.1. Usando el controlador ORACLE_LOADER. El siguiente listado muestra la creación de la tabla LIBRO_EXT, basada en los datos del fichero libro_dump.lst creado previamente. SET FEEDBACK ON HEADING ON NEWPAGE 1 PAGESIZE 60 CREATE TABLE LIBRO_EXT ( Titulo VARCHAR2(100), Autor VARCHAR2(20), Categoria VARCHAR2(20), Valoracion NUMBER(2) ) ORGANIZATION EXTERNAL ( TYPE ORACLE_LOADER DEFAULT DIRECTORY LIBRO_DIR ACCESS PARAMETERS ( RECORDS DELIMITED BY NEWLINE FIELDS TERMINATED BY "~" ( Titulo CHAR(100), Autor CHAR(20), Categoria CHAR(20), Valoracion INTEGER EXTERNAL (2) ) ) LOCATION ('libro_dump.lst') );

Oracle responderá con: Tabla creada.

Aunque ningún dato ha sido creado dentro de la base de datos de Oracle. De forma similar podemos crear una tabla basada en el fichero autor_dump.lst: CREATE TABLE AUTOR_EXT ( Nombre VARCHAR2(20), Nacionalidad VARCHAR2(50) ) ORGANIZATION EXTERNAL ( TYPE ORACLE_LOADER DEFAULT DIRECTORY LIBRO_DIR ACCESS PARAMETERS ( RECORDS DELIMITED BY NEWLINE FIELDS TERMINATED BY "~" ( Nombre CHAR(20), Nacionalidad CHAR(50) ) ) LOCATION ('autor_dump.lst') );

Oracle realizará sólo validación superficial cuando la tabla externa es creada. No veremos la mayor parte de los errores hasta que intentemos consultar la tabla. La sintaxis para los parámetros de acceso es muy específica, y errores menores en la definición de acceso (incluyendo el orden de la cláusula) pueden evitar que todas las filas sean accedidas. Podemos verificar el contenido de las tablas externas consultándolas y comparándolas con las tablas fuentes: SELECT Titulo FROM LIBRO; SELECT Titulo FROM LIBRO_EXT;

Podemos combinar la tabla "interna" AUTOR con su contrapartida externa, AUTOR_EXT, para verificar que no hay registros perdidos o añadidos: SELECT *

Oracle /245

FROM AUTOR A WHERE NOT EXISTS (SELECT 'X' FROM AUTOR_EXT AE WHERE A.Nombre = AE.Nombre);

Como resultado, esta consulta no retornará ningún registro. La tabla AUTOR_EXT apunta al fichero autor_dump.lst. Si modificamos los datos en el fichero, los datos consultados sobre AUTOR_EXT cambiarán. Podemos consultar la vista USER_EXTERNAL_TABLES del diccionario de datos para informarnos sobre nuestras tablas externas, incluyendo el directorio por defecto y definiciones de acceso. DESC USER_EXTERNAL_TABLES

Name ----------------------------------------TABLE_NAME TYPE_OWNER TYPE_NAME DEFAULT_DIRECTORY_OWNER DEFAULT_DIRECTORY_NAME REJECT_LIMIT ACCESS_TYPE ACCESS_PARAMETERS PROPERTY

Por ejemplo, la tabla consulta:

AUTOR_EXT

usa

LIBRO_DIR

Null? -------NOT NULL NOT NULL NOT NULL

Type -------------------VARCHAR2(30) CHAR(3) VARCHAR2(30) CHAR(3) VARCHAR2(30) VARCHAR2(40) VARCHAR2(7) VARCHAR2(4000) VARCHAR2(10)

como directorio por defecto como mostraría la siguiente

SELECT DEFAULT_DIRECTORY_NAME, ACCESS_PARAMETERS FROM USER_EXTERNAL_TABLES WHERE TABLE_NAME = 'AUTOR_EXT'; DEFAULT_DIRECTORY_NAME -----------------------------------LIBRO_DIR

ACCESS_PARAMETERS --------------------------------------------RECORDS DELIMITED BY NEWLINE FIELDS TERMINATED BY "~" (Nombre CHAR(20), Nacionalidad CHAR(50))

La vista USER_EXTERNAL_TABLES no muestra el nombre del fichero externo (o ficheros) que la tabla referencia. Para ver esta información podemos consultar la vista USER_EXTERNAL_LOCATIONS: SELECT * FROM USER_EXTERNAL_LOCATIONS; TABLE_NAME ------------------LIBRO_EXT AUTOR_EXT

LOCATION --------------------------libro_dump.lst autor_dump.lst

DIR -----SYS SYS

DIRECTORY_NAME -----------------------LIBRO_DIR LIBRO_DIR

11.2.2. Usando el controlador ORACLE_DATAPUMP. Podemos descargar datos desde tablas de la base de datos a ficheros externos mediante dispositivos de acceso ORACLE_DATAPUMP. La sintaxis para utilizar el controlador de acceso ORACLE_DATAPUMP es la siguiente: CREATE TABLE Tabla_externa ORGANIZATION EXTERNAL ( TYPE ORACLE_DATAPUMP DEFAULT DIRECTORY objeto_directory LOCATION ( 'fichero_externo' ) ) AS SELECT id, nombre, direccion FROM Cliente ;

Con esta sintaxis no estamos creando realmente una tabla en la base de datos. En realidad, estamos creando metadatos en el diccionario de datos que se pueden utilizar para acceder a datos externos. Dentro del directorio referenciado por objeto_directory se crea un archivo con el nombre fichero_externo que contendrá los datos procedentes de la consulta utilizada para crear la tabla. Esta tabla puede ser consultada de la forma habitual: SELECT * FROM Tabla_externa;

Oracle /246

Podemos mover el archivo fichero_externo a otro sistema y crear una tabla externa para leer los datos: CREATE TABLE Import_Tabla_externa ( id number, nombre VARCHAR2(50), direccion VARCHAR2(150) ) ORGANIZATION EXTERNAL ( TYPE ORACLE_DATAPUMP DEFAULT DIRECTORY objeto_directory LOCATION ('fichero_externo') );

11.2.3. Opciones de creación de tablas externas. Dentro de la cláusula ORGANIZATION EXTERNAL hay cuatro sub-cláusulas: TYPE, DEFAULT DIRECTORY, ACCESS PARAMETERS, y LOCATION. Cuando creamos una tabla externa podemos estas cláusulas para personalizar el modo cómo Oracle ve los datos externos. La cláusula «TYPE». La sintaxis para el componente TYPE es: ( [TYPE tipo_dispositivo_acceso ] propiedades_datos_externos ) [REJECT LIMIT { entero | UNLIMITED }]

Para las tablas externas, el dispositivo de acceso es la API usada para transformar los datos externos. Se usa el tipo ORACLE_LOADER para tablas externas de solo lectura, u ORACLE_DATAPUMP si queremos usar Data Pump para tablas externas de lectura/escritura. Debemos especificar el dispositivo de acceso ORACLE_DATAPUMP si usamos la cláusula AS subconsulta para descargar datos desde una base de datos y entonces los recargamos. El tipo de dispositivo de acceso ORACLE_LOADER es el de por defecto. Nota. Ya que el dispositivo de acceso es parte del software de Oracle, sólo los ficheros accesibles por la base de datos pueden ser accedidos como tablas externas. Después de la declaración de TYPE, podemos asignar un valor de límite de rechazo ( REJECT LIMIT). Por defecto, ninguna fila puede ser rechazada (cualquier problema con una fila causará que el comando SELECT retorne un error). Generaremos otra copia de los datos de LIBRO en otro fichero: SET PAGESIZE 0 NEWPAGE 0 FEEDBACK OFF SPOOL e:\oracle\external\libro_dump_2.lst SELECT Titulo || '~' || Autor || '~' || Categoria || '~' || Valoracion || '~' FROM LIBRO / SPOOL OFF

Ahora crearemos una nueva tabla que referencie este fichero, diciéndole a Oracle que se salte las dos primeras filas (SKIP 2) y que permita un error (REJECT LIMIT 1). Esto permitirá saltarse las dos primeras líneas (con el comando SELECT y la barra /) y evitar que la última línea (SQL> SPOOL OFF) provoque un error: SET FEEDBACK ON HEADING ON NEWPAGE 1 PAGESIZE 60 CREATE TABLE LIBRO_EXT_2 ( Titulo VARCHAR2(100), Autor VARCHAR2(20), Categoria VARCHAR2(20), Valoracion NUMBER(2) ) ORGANIZATION EXTERNAL ( TYPE ORACLE_LOADER DEFAULT DIRECTORY LIBRO_DIR ACCESS PARAMETERS ( RECORDS DELIMITED BY NEWLINE SKIP 2 FIELDS TERMINATED BY "~" ( Titulo CHAR(100), Autor CHAR(20), Categoria CHAR(20), Valoracion INTEGER EXTERNAL (2) ) )

Oracle /247

LOCATION ('libro_dump_2.lst') ) REJECT LIMIT 1 ;

Podemos ahora verificar el número de filas de la tabla:

SET FEEDBACK ON HEADING ON NEWPAGE 1 PAGESIZE 60 SELECT COUNT(*) FROM LIBRO_EXT_2;

La cláusula «DEFAULT DIRECTORY». La cláusula DEFAULT DIRECTORY especifica el objeto directorio que será usado para todos los ficheros de datos para los cuales no se especifica otro directorio. Si usamos varios ficheros externos localizados en varios directorios podemos nombrar uno de ellos como el directorio por defecto y especificar los otros mediante su nombre en la cláusula LOCATION. Debemos usar nombres de objetos DIRECTORY (como LIBRO_DIR) en la cláusula LOCATION, no la ruta completa del directorio. Parámetros de acceso. La cláusula ACCESS PARAMETERS le dice a Oracle cómo mapear las filas del fichero con la tabla. Su sintaxis se muestra a continuación:

Dentro de la cláusula ACCESS PARAMETERS primero le decimos a Oracle cómo crear un registro (si su longitud es fija o variable, y cómo están delimitadas las filas). Si tenemos varias filas en una única línea, podemos usar una cadena como un separador entre fila. Debido a que los datos externos pueden venir de bases de datos diferentes de Oracle, Oracle soporta varios juegos de caracteres y tamaños de strings. Como con SQL*Loader, podemos especificar una cláusula WHEN para limitar qué registros son seleccionados. En el siguiente listado se crea la tabla LIBRO_EXT_3, con una cláusula WHEN para limitar sólo a los libros con categoría 'ADULTO'. CREATE TABLE LIBRO_EXT_3 ( Titulo VARCHAR2(100), Autor VARCHAR2(20), Categoria VARCHAR2(20), Valoracion NUMBER(2) ) ORGANIZATION EXTERNAL (

Oracle /248

TYPE ORACLE_LOADER DEFAULT DIRECTORY LIBRO_DIR ACCESS PARAMETERS ( RECORDS DELIMITED BY NEWLINE LOAD WHEN Categoria = 'ADULTO' SKIP 2 FIELDS TERMINATED BY "~" ( Titulo CHAR(100), Autor CHAR(20), Categoria CHAR(20), Valoracion INTEGER EXTERNAL(2) )

) LOCATION ('libro_dump_2.lst') ) REJECT LIMIT 1 ; LIBRO_EXT_3 accede al mismo fichero 'ADULTO'.

que

LIBRO_EXT_2,

pero sólo mostrará los registros de categoría

Al igual que con SQL*Loader, podemos crear un fichero de log, un fichero "malo" y un fichero de descarte. Las filas que fallen la condición WHEN serán escritos en el archivo de descartes. Las filas que fallen las condiciones de los parámetros de acceso serán escritos en el fichero "malo", y los detalles de carga serán escritos en el fichero de log. Para todos los tres tipos de ficheros podemos especificar un objeto DIRECTORY con el nombre de archivo y así podemos escribir la salida a otro directorio distinto del directorio de entrada. Podemos especificar NODISCARDFILE, NOBADFILE y NOLOGFILE para evitar que estos ficheros sean creados. Dentro de la cláusula ACCESS PARAMETERS podemos especificar las definiciones de campo y delimitadores como: FIELDS TERMINATED BY "~" ( Titulo CHAR(100), Autor CHAR(20), Categoria CHAR(20), Valoracion INTEGER EXTERNAL(2) ) Podemos usar la cláusula MISSING FIELD VALUES ARE NULL

para asignar valores para columnas nulas, pero debemos tener cuidado cuando usemos esta opción. Por ejemplo, la tabla AUTOR tiene valores nulos en su columna Nacionalidad. La creación de la tabla externa para AUTOR_EXT se muestra a continuación: SET PAGESIZE 0 NEWPAGE 0 FEEDBACK OFF SPOOL e:\oracle\external\autor_dump.lst SELECT Nombre || '~' || Nacionalidad || '~' FROM AUTOR ORDER BY Nombre / SPOOL OFF SET FEEDBACK ON HEADING ON NEWPAGE 1 PAGESIZE 60 CREATE TABLE AUTOR_EXT ( Nombre VARCHAR2(50), Nacionalidad VARCHAR2(100) ) ORGANIZATION EXTERNAL ( TYPE ORACLE_LOADER DEFAULT DIRECTORY LIBRO_DIR ACCESS PARAMETERS ( RECORDS DELIMITED BY NEWLINE SKIP 2 FIELDS TERMINATED BY "~" MISSING FIELD VALUES ARE NULL ( Nombre CHAR(50), Nacionalidad CHAR(100) ) ) LOCATION ('autor_dump.lst') ) REJECT LIMIT 1 ;

Oracle /249

Pero esto no es correcto. Si seleccionamos los valores de Nombre de la tabla AUTOR_EXT, veremos que los valores incluyen lo siguiente: SELECT Nombre FROM AUTOR_EXT WHERE Nombre LIKE 'S%';

Nombre -----------------------------------Sax Rohmer Sheridan Le Fanu SQL> SPOOL OFF

Debido a la cláusula MISSING FIELD VALUES ARE NULL, la línea "SQL> SPOOL OFF" del final del fichero fue leída como un valor de nombre de autor, con la nacionalidad a valor NULL. Esto ilustra el problema con la codificación de excepciones en nuestras definiciones de cargado. En muchos casos, nuestra integridad de datos mejorará forzando que fallen filas (siendo enviadas al fichero "malo" o de descartes) y evaluando las filas falladas aparte. La cláusula «Location». En la cláusula LOCATION podemos especificar el fichero de datos usado como origen de datos para la tabla. Podemos nombrar varios ficheros en esta cláusula si todos existen en objetos DIRECTORY para los cuales el usuario tiene permisos READ. El siguiente ejemplo combina dos ficheros independientes de LIBRO para ilustrar la habilidad de combinar varios ficheros en una única tabla externa. CREATE TABLE LIBRO_EXT_4 ( Titulo VARCHAR2(100), Autor VARCHAR2(20), Categoria VARCHAR2(20), Valoracion NUMBER(2) ) ORGANIZATION EXTERNAL ( TYPE ORACLE_LOADER DEFAULT DIRECTORY LIBRO_DIR ACCESS PARAMETERS ( RECORDS DELIMITED BY NEWLINE SKIP 2 FIELDS TERMINATED BY "~" ( Titulo CHAR(100), Autor CHAR(20), Categoria CHAR(20), Valoracion INTEGER EXTERNAL(2) ) ) LOCATION ('libro_dump_2.lst', 'libro_dump.lst') ) REJECT LIMIT 1 ; orden de los ficheros es importante; SKIP 2 se aplica

El al primer fichero, no al segundo. 11.3. Modificación de tablas externas. Podemos modificar las definiciones de una tabla externa para cambiar el modo en cómo Oracle interpreta el fichero plano. Las opciones disponibles se detallan a continuación. Parámetros de acceso. Podemos cambiar los parámetros de acceso sin borrar y recrear la definición de la tabla externa, preservando permisos, definición del fichero y así. Por ejemplo, a continuación se muestra cómo incrementar el número de registros a saltarse en la tabla LIBRO_EXT_4: ALTER TABLE LIBRO_EXT_4 ACCESS PARAMETERS ( RECORDS DELIMITED BY NEWLINE SKIP 10 FIELDS TERMINATED BY "~" ( Titulo CHAR(100), Autor CHAR(20), Categoria CHAR(20), Valoracion INTEGER EXTERNAL(2) )

Oracle /250

);

Añadir columna. Podemos usar la cláusula ADD COLUMN del comando ALTER TABLE para añadir una columna a la tabla externa, usando la misma sintaxis usada para tablas estándar. Directorio por defecto. Podemos usar la cláusula DEFAULT DIRECTORY del comando ALTER TABLE para cambiar el directorio por defecto para los ficheros externos accedidos por la tabla. El directorio debe ser creado mediante el comando CREATE DIRECTORY. Quitar columna. Podemos usar la cláusula DROP COLUMN del comando ALTER TABLE para quitar columnas de la tabla externa, usando la misma sintaxis que la usada para tablas estándar. Los datos del fichero permanecerán sin cambiar. Localización. Podemos cambiar los ficheros accedidos por la tabla externa mediante la cláusula LOCATION del comando ALTER TABLE. Podemos usar esta opción para añadir nuevos ficheros a la lista o cambiar el orden en el cual son accedidos. Modificar columna. Podemos usar la cláusula MODIFY COLUMN del comando ALTER TABLE para modificar una columna de la tabla externa, usando la misma sintaxis que la usada para tablas estándar. Paralelismo. Podemos usar la cláusula PARALLEL del comando ALTER TABLE para cambiar el grado de paralelismo de la tabla externa, usando la misma sintaxis que la usada para tablas estándar. Proyectar columna. La cláusula PROJECT COLUMN del comando ALTER TABLE le dice a los dispositivos de acceso cómo validar filas en consultar consecutivas. Si usamos la opción PROJECT COLUMN REFERENCED, el dispositivo de acceso sólo procesará las columnas seleccionadas por la consulta. Si entonces consultamos un conjunto diferente de columnas de la tabla externa, el resultado puede ser inconsistente con los resultados de la primera consulta. Si se usa la opción PROJECT COLUMN ALL, el dispositivo de acceso procesará todas las columnas definidas sobre la tabla externa, resultando en un conjunto consistente de resultados. La opción PROJECT COLUMN REFERENCED es la de por defecto. Límite de rechazo. Podemos usar la cláusula REJECT LIMIT del comando ALTER TABLE para cambiar el número permitido de filas rechazadas por la tabla externa. Aquí hay un ejemplo: ALTER TABLE LIBRO_EXT_3 REJECT LIMIT 5;

Renombrar. Podemos usar la cláusula RENAME TO del comando ALTER TABLE para cambiar el nombre de la tabla externa, usando la misma sintaxis que para las tablas estándar. Aquí hay un ejemplo: ALTER TABLE LIBRO_EXT_3 RENAME TO BE3;

11.4. Limitaciones, beneficios y usos potenciales de las tablas externas. Las tablas externas tienen limitaciones que pueden hacerlas inapropiadas para algunas aplicaciones que procesan transacciones en línea. No podemos realizar ninguna operación de actualización o borrado sobre las tablas externas. Cuanto más dinámica es la tabla, menos apropiados son los ficheros externos. Podemos cambiar los ficheros dinámicamente con operaciones al nivel del sistema operativo. Si nuestra aplicación genera sólo inserciones, podemos escribir estos registros insertados dentro de un fichero externo en vez de una tabla de base de datos. No podemos indexar tablas externas. La falta de índices sobre tablas externas no tiene por qué ser un factor negativo en el funcionamiento de una aplicación. Las consultas a tablas externas se completan muy rápidamente, aun cuando requieran una exploración completa de la tabla con cada acceso. Nota. Para analizar tablas externas, se usa el paquete externas con el comando ANALYZE.

DBMS_STATS.

No podemos analizar tablas

No podemos especificar restricciones sobre una tabla externa. Incluso crear una restricción NOT NULL o de clave foránea falla. Al margen de estas limitaciones, las tablas externas ofreces muchas funcionalidades útiles. Podemos combinar tablas externas (con otras, o con tablas estándar). Podemos usar etiquetas para forzar al optimizador a elegir Oracle /251

varias formas de join. Como una alternativa a la carga de datos, las tablas externas ofrecen a los DBA's y programadores de aplicaciones la posibilidad de acceder a datos sin tener que soportar programas de cargas de larga duración. Ya que los ficheros pueden ser editados al nivel del sistema operativo, podemos rápidamente reemplazar los datos rápidamente sin preocuparnos de transacciones que modifiquen las tablas. Por ejemplo, podemos usar esta capacidad para crear varias tablas externas y crear una vista UNION ALL con ellas, creando una vista particionada a través de varios ficheros. Podemos entonces gestionar los datos de cada tabla separadamente al nivel del sistema operativo, reemplazando su contenido si es necesario. Ya que la tabla externa puede se consultada, podemos usar la tabla externa como origen de datos para un comando INSERT AS SELECT. Durante esta operación, Oracle intentará cargar los ficheros externos en paralelo, potencialmente mejorando el rendimiento. Para mejorar el rendimiento de las operaciones INSERT AS SELECT, deberíamos usar la etiqueta APPEND para forzar inserciones a nivel de bloque. Cuando especificamos el grado de paralelismo para las operaciones INSERT AS SELECT, Oracle inicia varios dispositivos de acceso ORACLE_LOADER para procesar los datos en paralelo. Para mejorar el funcionamiento de la carga, hay que evitar usar campos de longitud variable, campos delimitados, conversión de juego de caracteres, NULLIF, DEFAULTIF, y operaciones de conversión de tipos de datos. Desactivando la generación del fichero "malo" (con NOBADFILE) eliminamos el coste asociado con la creación del fichero y el mantenimiento el contexto de filas original. Durante la INSERT AS SELECT podemos aplicar funciones sobre los datos que se procesan. Podemos aplicar estas funciones en la sintaxis del comando INSERT AS SELECT o en la definición de la tabla externa. Esto ofrece importantes beneficios a las tablas externas (podemos centralizar la representación y requerimientos de proceso de nuestros datos, creando rutinas de traslación dentro de la definición de nuestras tablas). Durante las consultas, las tablas externas permiten seleccionar conjuntos de datos específicos (mediante la cláusula LOAD WHEN). Si tenemos varios orígenes de datos para cargas de datos de repositorio, podemos elegir qué datos estarán disponibles aún mientras los datos están fuera de la base de datos. Podemos usar esta característica para mantener aplicaciones disponibles durante cargas de datos. Estas cargas pueden ocurrir en paralelo si los ficheros externos tiene un formato de fichero FIXED. La característica de límites de acceso también permite forzar reglas de seguridad complejas concernientes al acceso de datos. Por ejemplo, podemos tomar datos sensibles desde fuera de la base de datos, en un directorio seguro. Usuarios con acceso READ al directorio deberían ser capaces de usar la tabla externa y combinarla con otras tablas; usuarios sin este acceso deberían estar limitados para insertar datos dentro de las tablas públicamente accesibles. Datos altamente seguros, o datos dinámicos poco accedidos, no tienen que ser insertados en la base de datos hasta que sea necesario. Si usamos tablas externas en nuestra arquitectura de base de datos, debemos asegurarnos de planificar copias de respaldo y recuperación para estos ficheros al igual que para el resto de la base de datos. Si los ficheros externos cambian más rápidamente que los ficheros de base de datos, podemos necesitar recuperarlos más frecuentemente para tomar todas las ventajas de las capacidades de recuperación de Oracle.

12. Consultas flashback Como parte de su modelo consistente de lectura, Oracle muestra datos que han sido confirmados en la base de datos. Desde Oracle9i, podemos consultar datos previos a una transacción que ha sido confirmada. Si accidentalmente confirmamos una actualización podemos usar esta capacidad (llamada consultas flashback) para ver los datos en el estado previo a la confirmación. Podemos usar los resultados de la consulta flashback para restaurar los datos. Para soportar consultas flashback nuestra base de datos debería usar deshacer administrado por el sistema, una funcionalidad introducida en Oracle9i para automáticamente administrar segmentos de rollback. El DBA debe crear una tablespace de deshacer, habilitar Automatic Undo Management, y establecer una ventana de tiempo de retención de deshacer. Las consultas flashback pueden ejecutarse a través de bases de datos remotas. Oracle intentará mantener cada información de deshacer en el tablespace de deshacer para soportar consultas flashback durante el periodo de retención de los datos. El tiempo de retención asignado y la cantidad de espacio disponible en el tablespace de deshacer pueden impactar significativamente nuestra habilidad para ejecutar consultas flashback.

Oracle /252

Nota. Oracle usa deshacer (undo) para dar marcha atrás a transacciones y soportar consultas flashback. Oracle usa rehacer (redo), mediante ficheros de log de rehacer en línea, para aplicar transacciones durante recuperaciones de base de datos. Las consultas flashback son herramientas muy importantes mediante los esfuerzos de recuperaciones parciales. En general, no deberíamos confiar en consultas flashback como parte del diseño de nuestras aplicación debido a su dependencia de elementos del sistema fuera del control de los programadores de la aplicación (como el número de transacciones durante un período de tiempo y el tamaño de tablespace de deshacer). Más bien deberíamos tratarlas como una opción durante los periodos de pruebas, soporte y recuperación de datos. Por ejemplo, podríamos usarlas para crear copias de tablas y puntos en el tiempo que serán usados para reconstruir los datos cambiados. Nota. Para usar algunas características de las consultas flashback, debemos tener el permiso EXECUTE sobre el paquete DBMS_FLASHBACK. La mayoría de usuarios no necesitarán permisos sobre este paquete. 12.1. Ejemplo de consulta flashback basada en el tiempo. La tabla PEDIDO_LIBRO tiene seis registros, tal como se muestra en el siguiente listado: COLUMN Titulo FORMAT A30 SELECT * FROM PEDIDO_LIBRO;

TITULO -------------------------Joe descalzo Evangelio Algo tan fuerte La hija de Galileo Longitud Una vez quitado

EDITOR -------------------Mariner Picador Pandoras Penguin Penguin Sancturay Pub

CATEGORIA ---------------Adulto-fic Adulto-fic Adulto-nf Adulto-nf Adulto-nf Adulto-nf

Llega un envío, y los registros antiguos de PEDIDO_LIBRO son suprimidos y se confirma el borrado. Lamentablemente, no todos los libros estaban en el envío, y por tanto la supresión fue inadecuada: DELETE FROM PEDIDO_LIBRO; COMMIT;

¿Cómo podemos reconstruir los registros de libros no recibidos de la tabla PEDIDO_LIBRO? Podemos realizar una recuperación de la base de datos usando Data Pump Import para restaurar la tabla, o podemos usar una recuperación física de la base de datos para recuperarla a un punto anterior a la supresión. Sin embargo, con consultas flashback podemos evitar la necesidad de realizar operaciones de recuperación. Primero debemos consultar los datos antiguos a la base de datos. Desde Oracle9i Release 2, podemos usar las cláusulas AS OF TIMESTAMP y AS OF SCN del comando SELECT para especificar cómo de lejos debería llegar Oracle para recuperar datos. Nota. En la liberación 9.0.1 de Oracle9, esta sintaxis no está soportada; es necesario ejecutar procedimientos del paquete DBMS_FLASHBACK y escribir bucles PL/SQL para realizar cualquier transacción basada en los datos. SELECT COUNT(*) FROM PEDIDO_LIBRO; COUNT(*) ------------0 SELECT COUNT(*) FROM PEDIDO_LIBRO AS OF TIMESTAMP (SYSDATE – 5/1440); COUNT(*) ------------6

Cuando ejecutamos una consulta flashback, sólo es cambiado el estado de los datos. Se usa el tiempo actual del sistema (el valor de SYSDATE), y el diccionario de datos actual. Si la estructura de la tabla ha cambiado, la consulta fallará. Oracle /253

12.2. Guardando los datos. Como se muestra en el ejemplo de PEDIDO_LIBRO, las consultas flashback son sencillas de implementar, a condición de que el administrador de deshacer de la base de datos esté correctamente configurado y la información deshacer esté disponible. ¿Pero cómo trabajar con los datos flashback? El método más simple consiste en guardar los datos en otra tabla. Hay 1440 minutos en un día, así "SYSDATE – 5/1440" dirigirá a la base de datos al estado que tenía hace 5 minutos. Nótese entonces que la consulta puede no retornar registros si han pasado más de 5 minutos desde la confirmación. CREATE TABLE PEDIDO_LIBRO_OLD AS SELECT * FROM PEDIDO_LIBRO AS OF TIMESTAMP (SYSDATE – 5/1440); / SELECT COUNT(*) FROM PEDIDO_LIBRO_OLD; COUNT(*) ------------6

Podemos verificar que los datos recuperados son correctos consultando la tabla PEDIDO_LIBRO_OLD. Y entonces podemos trabajar con estos datos para restaurar los datos perdidos, realizar actualizaciones selectivas, insertar sólo registros incorrectamente borrados, o cualquier otra operación necesaria. La nueva tabla, PEDIDO_LIBRO_OLD, no tiene índices ni restricciones de integridad referencial. Si necesitamos combinarla con otras tablas, podemos necesitar crear copias flashback de varias tablas para mantener la integridad referencial de los datos. También podemos notar que cada consulta es ejecutada en diferentes momentos del tiempo (y los tiempos relativos usados en la cláusula AS OF TIMESTAMP, por lo tanto, pueden conducir a confusión o resultados incoherentes). Podemos trabajar directamente con datos antiguos (sin embargo, confiamos en que los datos viejos estén disponibles para nuestra transacción). Es generalmente seguro crear una tabla y almacenar los datos viejos temporalmente mientras trabajamos con ellos. 12.2.1. Limitaciones en las consultas flashback basadas en el tiempo. El ejemplo precedente muestra cómo usar el tiempo del sistema durante nuestras consultas flashback. Como se indicó, este método proporciona un soporte limitados para consultas flashback multi-tablas. Para realizar consultas flashback complejas correctamente deberíamos usar el número de cambio del sistema (SCN). Oracle usa internamente el SCN, no el tiempo del sistema, para generar datos flashback, aunque especifiquemos la cláusula AS OF TIMESTAMP. El tiempo del sistema se mapea a los valores SCN cada cinco minutos; la tabla SYS.SMON_SCN_TIME registra las últimas 1440 coincidencias de SCN al tiempo del sistema. Cuando volvemos a un punto del tiempo, Oracle usa los 5 minutos más recientes que preceden al tiempo consultado. Si SMON_SCN_TIME tiene un registro para las 1:00 P.M. y otro para las 1:05 P.M., una consulta flashback para las 1:04 P.M. retornará los datos desde las 1:00 P.M. SMON_SCN_TIME sólo mantiene las más recientes 1440 entradas, así que las consultas flashback que van más allá de los últimos cinco días deben usar un acercamiento basado en SCN. Si cerramos nuestra base de datos de vez en cuando (por ejemplo, para hacer respaldos fuera de línea), los 1440 registros de SMON_SCN_TIME incluirán más de cinco días. Para ver las entradas más recientes de SYS.SMON_SCN_TIME, podemos ejecutar la siguiente consulta como SYS, conectados como SYSDBA: SELECT TO_CHAR(TIME_DP, 'DD-MON-YYYY HH24:MI:SS') FROM SYS.SMON_SCN_TIME ORDER BY TIME_DP DESC ;

12.3. Ejemplo de consulta flashback basada en SCN. Cuando realizamos consultas flashback basadas en el tiempo, realmente estamos haciendo consultas basadas en SCN; simplemente confiamos en Oracle para encontrar un SCN cerca del tiempo especificado. Si conocemos el SCN exacto podemos realizar una consulta flashback con una gran precisión. Para comenzar una consulta flashback basada en SCN, primero debemos conocer el SCN de nuestra transacción. Para obtener el último número de cambio debemos aplicar un COMMIT y entonces usar la cláusula AS OF SCN del comando SELECT. Podemos encontrar el SCN actual ejecutando la función GET_SYSTEM_CHANGE_NUMBER del paquete DBMS_FLASHBACK antes de la ejecución de nuestra transacción.

Oracle /254

Nota. Antes de ejecutar el siguiente ejemplo, tenemos que tener concedido el permiso sobre el paquete DBMS_FLASHBACK.

EXECUTE

El siguiente ejemplo muestra este proceso como parte de una transacción a través de la tabla PEDIDO_LIBRO_OLD creada y poblada previamente. Primero, el SCN actual se asigna a una variable llamada scn_flash y se muestra mediante el comando PRINT de SQL*Plus: COMMIT; VARIABLE scn_flash NUMBER; EXECUTE :scn_flash :=DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER; PRINT scn_flash scn_flash ------------529732

A continuación, se realiza un borrado y el resultado es confirmado: DELETE FROM PEDIDO_LIBRO_OLD; COMMIT;

Ahora podemos consultar los datos flashback. Aunque conocemos el valor SCN, podemos continuar usando la variable scn_flash dentro de la misma sesión: SELECT COUNT(*) FROM PEDIDO_LIBRO_OLD AS OF SCN (:scn_flash);

COUNT(*) ------------6

Podemos usar los datos flashback de PEDIDO_LIBRO_OLD, accedidos por la cláusula AS OF SCN, para poblarla con los valores antiguos: INSERT INTO PEDIDO_LIBRO_OLD SELECT * FROM PEDIDO_LIBRO_OLD AS OF SCN (:scn_flash); COMMIT;

Nota. Las operaciones DDL que modifican la estructura de la tabla invalidan los datos de deshacer para la tabla, y las capacidades flashback se limitan a antes de que el DDL fue ejecutado. 12.4. ¿Qué ocurre si falla una consulta flashback? Si no hay suficiente espacio en el tablespace de deshacer para mantener todos los datos necesarios para la consulta de flashback, la consulta fallará. Aunque el DBA cree un tablespace de deshacer grande, es posible que una serie de transacciones largas usen todo el espacio disponible. Una porción de cada consulta fallida será escrita en el registro de avisos de la base de datos. Desde la perspectiva de un usuario que intenta recuperar datos antiguos, deberíamos intentar recuperar los datos que sean tan correctos y oportunos como sea posible. A menudo podemos tener que ejecutar varias consultas flashback para determinar como de lejos podemos consultar sucesivamente los datos, y luego guardar los datos más viejos a los que podemos tener acceso y los datos más cercanos al punto en el cual el problema ocurrió. Una vez que los datos más viejos se han ido del tablespace de deshacer, no podemos usar consultas flashback para recuperarlo. Si no es posible recuperar datos que son bastante viejos para nuestras necesidades, tendremos que realizar algún tipo de la recuperación de la base de datos (recuperando la base de datos entera o recuperando tablas y tablespaces específicos mediante los métodos tradicionales de un DBA). Si este problema es habitual, deberíamos aumentar el tiempo de retención de deshacer, aumentar el espacio asignado para el tablespace deshacer, y la aplicación debería examinarse para determinar por qué se repiten transacciones cuestionables. 12.5. ¿Qué SCN está asociado con cada registro? Desde Oracle Database 10g, podemos ver el más reciente SCN asociado con cada registro de la bases de datos. Comenzaremos repoblando nuestra tabla PEDIDO_LIBRO con los datos que fueron restaurados en la tabla PEDIDO_LIBRO_OLD: DELETE FROM PEDIDO_LIBRO; INSERT INTO PEDIDO_LIBRO SELECT * FROM PEDIDO_LIBRO_OLD;

Oracle /255

COMMIT;

Ahora usaremos la pseudo-columna asociado con cada registro:

ORA_ROWSCN

introducida en Oracle Database 10g para ver el SCN

SELECT Titulo, ORA_ROWSCN FROM PEDIDO_LIBRO; TITULO -------------------------Joe descalzo Evangelio Algo tan fuerte La hija de Galileo Longitud Una vez quitado

ORA_ROWSCN -----------------553531 553531 553531 553531 553531 553531

Todos los registros son parte de la misma transacción y por tanto tienen el mismo SCN. Ahora, después de que se han realizado otras transacciones en la base de datos, insertaremos un nuevo registro: INSERT INTO PEDIDO_LIBRO VALUES ('Innumeracy','Vintage Books','Adulto-nf'); COMMIT;

Ahora que ha sido confirmado un nuevo cambio, podemos usar el SCN asociado con él: SELECT Titulo, ORA_ROWSCN FROM PEDIDO_LIBRO;

TITULO -------------------------Joe descalzo Evangelio Algo tan fuerte La hija de Galileo Longitud Una vez quitado Innumeracy

ORA_ROWSCN -----------------553531 553531 553531 553531 553531 553531 553853

¿A qué hora se hace el mapeado SCN? Podemos usar la función de fecha en la cual se hizo el cambio:

SCN_TO_TIMESTAMP

para mostrar la

SELECT SCN_TO_TIMESTAMP(555853) FROM DUAL;

SCN_TO_TIMESTAMP(555853) ------------------------------------------20-FEB-2011 03.11.28.000000000 PM

Podemos integrar estas dos consultas par ver los tiempos de la última transacción para cada registro: SELECT Titulo, SCN_TO_TIMESTAMP(ORA_ROWSCN) FROM PEDIDO_LIBRO;

12.6. Consultas de versión flashback. Desde Oracle Database 10g, podemos mostrar varias versiones de las filas que existen durante intervalos especificados. Como con los ejemplos mostrados previamente, los cambios son dependientes de SCN, así que sólo estos que son confirmados se mostrarán. Nota. Las consultas de versión flashback requieren que el DBA tenga asignado un valor distinto de cero para el parámetro de inicialización UNDO_RETENTION. Si el valor de UNDO_RETENTION es demasiado pequeño obtendremos un error ORA-30052. Para los siguientes ejemplos, eliminaremos los registros antiguos de la tabla PEDIDO_LIBRO: DELETE FROM PEDIDO_LIBRO;

Ahora repoblaremos la tabla PEDIDO_LIBRO: SELECT SYSTIMESTAMP FROM DUAL; INSERT INTO PEDIDO_LIBRO SELECT * FROM PEDIDO_LIBRO_OLD; SELECT SYSTIMESTAMP FROM DUAL;

Ahora esperamos unos pocos minutos y actualizamos todos los registros: SELECT SYSTIMESTAMP FROM DUAL; UPDATE PEDIDO_LIBRO SET Categoria = 'Adulto-f';

Oracle /256

SELECT SYSTIMESTAMP FROM DUAL;

Para ejecutar una consulta de versión flashback se usa la cláusula VERSIONS BETWEEN en el comando SELECT. Podemos especificar la fecha o el SCN. En este ejemplo, el formato para la cláusula TIMESTAMP está basado en el formato estándar de Oracle. SELECT * FROM PEDIDO_LIBRO VERSIONS BETWEEN TIMESTAMP TO_TIMESTAMP('20-FEB-04 16.00.20','DD-MON-YY HH24.MI.SS') AND TO_TIMESTAMP('20-FEB-04 16.06.20','DD-MON-YY HH24.MI.SS') ;

Cuando ejecutemos esta consulta, Oracle retornará una fila por cada versión de cada registro entre las fechas indicadas. Para las filas retornadas, podemos consultar pseudo-columnas adicionales: Pseudo-columna

Descripción El SCN cuando los primeros datos tenían los valores reflejados. Si NULL, entonces la fila fue creada antes del límite inferior de la consulta. VERSIONS_STARTTIME La fecha cuando los primeros datos tenían los valores reflejados. Si NULL, entonces la fila fue creada antes del límite inferior de la consulta. VERSIONS_ENDSCN El SCN cuando la versión de la fila expira. Si NULL, entonces la fila es actual o ha sido borrada. VERSIONS_ENDTIME La fecha cuando la versión del la fila expira. Si NULL, entonces la fila es actual o ha sido borrada. El identificador de la transacción que creó la versión de la fila. VERSIONS_XID VERSIONS_OPERATION La operación que realizó la transacción (I para INSERT, U para UPDATE, D para DELETE). VERSIONS_STARTSCN

El siguiente ejemplo muestra el uso de consultas de versión flashback. Los valores de Versions_StartSCN son NULL para las filas actuales; las filas antiguas han sido actualizadas. La fecha será diferente según la plataforma. SELECT Titulo, VERSIONS_STARTSCN, VERSIONS_OPERATION FROM PEDIDO_LIBRO VERSIONS BETWEEN TIMESTAMP TO_TIMESTAMP('20—FEB-04 16.00.20','DD-MON-YY HH24.MI.SS') AND TO_TIMESTAMP('20—FEB-04 16.06.20','DD-MON-YY HH24.MI.SS') ; TITULO -------------------------Una vez quitado Longitud La hija de Galileo Algo tan fuerte Evangelio Joe descalzo Joe descalzo Evangelio Algo tan fuerte La hija de Galileo Longitud Una vez quitado

ORA_ROWSCN -----------------568127 568127 568127 568127 568127 568127

V ---U U U U U U

La cláusula VERSIONS BETWEEN puede usarse en subconsultas de comandos DML y DDL. Desde Oracle Database 10g, podemos usar la vista FLASHBACK_TRANSACTION_QUERY del diccionario de datos para seguir los cambios hechos por cada transacción. Para una transacción dada, FLASHBACK_TRANSACTION_QUERY muestra el nombre del usuario que las ejecutó, la operación realizada, la tabla sobre la que se aplicó la transacción, el SCN y fechas iniciales y finales, y el SQL necesario para deshacer la transacción. 12.7. Planificación de las consultas flashback. Par los DBA's, las consultas flashback pueden servir para realizar operaciones de recuperaciones parciales rápidamente. Oracle /257

Si los datos pueden ser reconstruidos mediante consultas flashback, y si el volumen de datos no es aplastante, podemos ser capaces de guardar los resultados de varias consultas flashback en tablas independientes. Podemos entonces comparar los datos en las tablas mediante las opciones SQL mostradas previamente (subconsultas correlacionadas, EXISTS, NOT EXISTS, MINUS, y demás). Si no podemos evitar la necesidad de una operación de recuperación, deberíamos señalar el período de tiempo para ser usado en una operación de recuperación basada en el tiempo. Como se verá en el siguiente capítulo, Oracle Database 11g introduce opciones adicionales (los comandos FLASHBACK DATABASE y FLASHBACK TABLE). Para los programadores y administradores de aplicaciones, las consultas flashback proporcionan una herramienta importante para reconstruir datos. Las consultas flashback pueden ser particularmente críticas durante operaciones de pruebas y soporte.

13. Tablas y bases de datos flashback Desde Oracle Database 10g, podemos usar los comandos FLASHBACK TABLE y FLASHBACK DATABASE para simplificar nuestros esfuerzos de recuperación de datos. El comando FLASHBACK TABLE automatiza el proceso de restaurar una tabla entera a un estado previo. El comando FLASHBACK DATABASE recupera una base de datos entera, y necesita modificaciones sobre el estado y registro de la base de datos. 13.1. El comando «FLASHBACK TABLE». El comando FLASHBACK TABLE recupera un estado anterior de una tabla en el evento de error humano o de aplicación. Oracle no puede restaurar una tabla a un estado previo durante operaciones DDL que cambien la estructura de la tabla. Nota. La base de datos debería usar Automatic Undo Management (AUM) para trabajar con FLASHBACK TABLE. La habilidad de recuperar datos viejos está limitada a la cantidad de datos de deshacer retenidos en el tablespace de deshacer y el parámetro de inicialización UNDO_RETENTION. No se puede dar marcha atrás a un comando FLASHBACK TABLE. Sin embargo, podemos aplicar otro comando FLASHBACK TABLE y especificar un tiempo anterior al actual. Nota. Registre el SCN actual antes de la ejecución de un comando FLASHBACK TABLE. 13.1.1. Permisos requeridos. Debemos tener el permiso de objeto FLASHBACK sobre la tabla o el permiso de sistema FLASHBACK ANY TABLE. Debemos también tener los permisos de objeto SELECT, INSERT, DELETE y ALTER sobre la tabla. Debemos habilitar el movimiento de fila para todas las tablas de la lista flashback. Para dar marcha atrás una tabla antes de una operación DROP sobre tabla, sólo necesitamos tener los permisos necesarios para borrar la tabla. 13.1.2. Recuperación de tablas borradas. Consideremos la tabla AUTOR: DESCRIBE AUTOR

Name -----------------NombreAutor Comentarios

Null? -------NOT NULL

Type ---------------------------VARCHAR2(50) VARCHAR2(100)

Ahora, asumamos que la tabla es borrada accidentalmente. Esto ocurre normalmente cuando un usuario con permisos intenta borrar una tabla en el entorno de desarrollo/pruebas pero pensando en la base de datos de producción cuando ejecuta el comando: DROP TABLE AUTOR CASCADE CONSTRAINTS;

¿Cómo podemos recuperar la tabla? Desde Oracle Database 10g, una tabla borrada no desaparece completamente. Sus bloques todavía se mantienen en el tablespace, y eso todavía perjudica la cuota de espacio. Podemos ver los objetos borrados consultando la vista RECYCLEBIN del diccionario de datos. Nótese que el formato de la columna Object_Name puede diferir entre versiones. SELECT Object_Name, Original_Name, Operation, Type, Ts_Name, CreateTime FROM RECYCLEBIN;

OBJECT_NAME --------------------------RB$$48448$TABLE$0

ORIGINAL_NAME ---------------------AUTOR

OPERATION --------------DROP

Oracle /258

TYPE --------TABLE

TS_NAME ------------USERS

CREATETIME ------------------------2004-02-23:16:10:58

RB$$48449$INDEX$0

SYS_C004828

DROP

INDEX

USERS

2004-02-23:16:10:58

SELECT DropTime, DropScn, Partition_Name, Can_Undrop, Can_Purge, Related FROM RECYCLEBIN; DROPTIME ------------------------2004-02-25:14:30:23 2004-02-25:14:30:23

DROPSCN -------------720519 720516

PARTITION_NAME ------------------------

CAN ------YES NO

CAN -----YES YES

RELATED ------------48448 48448

SELECT Base_Object, Purge_Object, Space FROM RECYCLEBIN; BASE_OBJECT -----------------48448 48448

PURGE_OBJECT ---------------------48448 48449

SPACE ---------8 8

es un sinónimo público de la vista USER_RECYCLEBIN del diccionario de datos, que muestra las entradas del reciclador para el usuario actual. Los DBA's pueden ver todos los objetos borrados mediante la vista DBA_RECYCLEBIN del diccionario de datos. Como se muestra en el listado precedente, Oracle ha borrado la tabla AUTOR y su índice de clave primaria asociado. Aunque hayan sido borrados todavía están disponibles para recuperación. Nótese que el listado muestra el SCN para el comando DROP usado para borrar el objeto base. Podemos usar el comando FLASHBACK TABLE TO BEFORE DROP para recuperar la tabla desde el reciclador: RECYCLEBIN

FLASHBACK TABLE AUTOR TO BEFORE DROP;

La tabla es restaurada, junto con sus filas, índices y estadísticas. La sintaxis completa de este comando es la siguiente:

¿Qué ocurre si borramos la tabla AUTOR, la recreamos y entonces la borramos otra vez? El reciclador contendrá ambas tablas. Cada entrada en el reciclador se identificará mediante su SCN y la fecha de borrado. Nota. El comando FLASHBACK TABLE TO BEFORE DROP no recupera restricciones referenciales. Para purgar entradas antiguas del reciclador se usa el comando PURGE. Podemos purgar todos nuestros objetos borrados, todos los objetos borrados en la base de datos (si somos un DBA), todos los objetos de un tablespace específico, o todos los objetos de un usuario determinado en un tablespace específico. La sintaxis completa del comando PURGE se muestra a continuación:

Podemos usar la cláusula recupera.

RENAME

del comando

FLASHBACK TABLE

Oracle /259

para renombrar la tabla cuando se

13.1.3. Cómo recuperar a una fecha o SCN. Como se vio en el capítulo previo, podemos guardar los resultados de una consulta flashback a otra tabla mientras la tabla principal permanece inalterada. También podemos actualizar las filas de una tabla según los resultados de una consulta flashback. Podemos usar el comando FLASHBACK TABLE para transformar una tabla a una versión anterior (borrando los cambios hechos desde el punto de retroceso especificado). Durante la operación de recuperación de tabla, Oracle adquiere bloqueos DML exclusivos sobre todas las tablas especificadas. El comando es ejecutado como una única transacción a través de todas las tablas; si alguna de ellas falla, todo el comando falla. Podemos entonces recuperar una tabla a un SCN o tiempo específicos. Nota. FLASHBACK TABLE TO SCN o TO TIMESTAMP no preservan los RowID's. El siguiente comando de actualización intenta actualizar el comentario para el autor embargo, la cláusula WHERE no es especificada; todos los registros serán actualizados.

Clement Hurd.

Sin

UPDATE AUTOR SET COMMENTS = 'Ilustrador de libros para niños'; COMMIT;

En este caso, sabemos que la mayoría de la tabla tiene datos incorrectos, y sabemos que la transacción fue ejecutada incorrectamente. Para recuperar los datos correctos, podemos dar marcha atrás a la base de datos y entonces aplicar cualquier nuevo comando necesario para actualizar los datos. Primero, nos aseguraremos de conocer el SCN actual para retornar a este punto: COMMIT; VARIABLE scn_flash NUMBER; EXECUTE :scn_flash :=DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER; PRINT scn_flash SCN_FLASH -----------------720880

Ahora, recuperaremos la tabla al tiempo anterior a la actualización. Primero, habilitaremos el movimiento de filas para la tabla: ALTER TABLE AUTOR ENABLE ROW MOVEMENT;

Entonces podemos recuperar la tabla:

FLASHBACK TABLE AUTOR TO TIMESTAMP (SYSTIMESTAMP – 5/1440); Podemos usar la cláusula TO SCN si queremos especificar un SCN en vez de

una fecha. 13.1.4. Índices y estadísticas. Cuando una tabla es recuperada, sus estadísticas no son recuperadas. Los índices que existen sobre la tabla son revertidos y reflejan el estado de la tabla al punto de recuperación. Los índices borrados desde el punto de recuperación no son restaurados. Los índices creados desde el punto de recuperación continuarán existiendo y serán actualizados para reflejar los viejos datos. 13.2. El comando «FLASHBACK DATABASE». El comando FLASHBACK DATABASE retorna la base de datos a un tiempo o SCN pasado, proporcionando una alternativa rápida para realizar recuperaciones incompletas de la base de datos. Después de una operación de retroceso de base de datos, para tener acceso de escritura a la base de datos recuperada debemos volver a abrirla con un comando ALTER DATABASE OPEN RESETLOGS. Debemos tener el permiso de sistema SYSDBA para usar el comando FLASHBACK DATABASE. Nota. La base de datos debe haber sido puesta en modo flashback con el comando ALTER DATABASE FLASHBACK ON. La base de datos debe ser montada en modo exclusivo pero no estar abierta cuando este comando es ejecutado. La sintaxis para el comando FLASHBACK DATABASE es la siguiente:

Oracle /260

Podemos usar tanto la cláusula TO SCN como TO TIMESTAMP para asignar el punto al cual queremos retornar toda la base de datos. Con la cláusula TO BEFORE podemos regresar a un punto crítico (como una transacción que produce una alteración no intencionada de varias tablas). Se usa la pseudo-columna ORA_ROWSCN para ver el SCN de las transacciones que más recientemente modificaron las filas. Para usar FLASHBACK DATABASE, debemos primer modificar la base de datos mientras está montada pero no abierta. Si no ocurre esto, necesitaremos cerrar nuestra base de datos y habilitar flashback durante el proceso de inicio: STARTUP MOUNT EXCLUSIVE; ALTER DATABASE ARCHIVELOG; ALTER DATABASE FLASHBACK ON; ALTER DATABASE OPEN;

Nota. Debemos tener habilitado recuperación de medios mediante el comando ARCHIVELOG antes de ejecutar el comando ALTER DATABASE FLASHBACK ON.

ALTER DATABASE

Dos parámetros de inicialización controlan cómo los datos flashback son retenidos en la base de datos. El parámetro DB_FLASHBACK_RETENTION_TARGET asigna el límite superior (en minutos) para determinar qué lejos en el tiempo podemos recuperar la base de datos. El parámetro DB_RECOVERY_FILE_DEST asigna el tamaño del área de recuperación. Nótese que el comando FLASHBACK TABLE usa el tablespace de deshacer, mientras que el comando FLASHBACK DATABASE confía en registros de recuperación almacenados en un área de recuperación. Podemos determinar cuán lejos podemos recuperar la base de datos consultando la vista V$FLASHBACK_DATABASE_LOG. La cantidad de datos flashback retenidos en la base de datos es controlada por el parámetro de inicialización y el tamaño del área de recuperación. El siguiente listado muestra las columnas disponibles en V$FLASHBACK_DATABASE_LOG y contenidos de ejemplo: DESC V$FLASHBACK_DATABASE_LOG

Name ----------------------------------------OLDEST_FLASHBACK_SCN OLDEST_FLASHBACK_TIME RETENTION_TARGET FLASHBACK_SIZE ESTIMATED_FLASHBACK_SIZE

Null? --------

Type -----------NUMBER DATE NUMBER NUMBER NUMBER

SELECT * FROM V$FLASHBACK_DATABASE_LOG; OLDEST_FL --------------722689

OLDEST_FL --------------25-FEB-04

RETENTION_TARGET --------------------------1440

FLASHBACK_SIZE ----------------------8192000

ESTIMATED_FL ---------------------

0

Podemos verificar el estado de recuperación de la base de datos consultando V$DATABASE; la columna Flashback_On tendrá el valor YES si la recuperación está habilitada para la base de datos. SELECT CURRENT_SCN, FLASHBACK_ON FROM V$DATABASE; CURRENT_SCN --------------------723649

FLASHBACK_ON ---------------------YES

Con la base de datos abierta durante más de una hora, verificamos que los datos flashback están disponibles y entonces los recuperamos (perderemos todas las transacciones que ocurrieron durante este tiempo): SHUTDOWN; STARTUP MOUNT EXCLUSIVE; FLASHBACK DATABASE TO TIMESTAMP SYSDATE-1/24; Nótese que el comando FLASHBACK DATABASE requiere

Oracle /261

que la base de datos esté montada en modo

exclusivo. Cuando ejecutamos el comando FLASHBACK DATABASE, Oracle comprueba que todo el archivado requerido y los ficheros de registro de deshacer en línea están disponibles. Si los registros están disponibles, los ficheros de datos en línea se revierten al tiempo o SCN especificado. Si no hay suficientes datos en línea en los registros de archivado y áreas de recuperación, necesitaremos usar métodos tradicionales de recuperación de base de datos para recuperar los datos. Por ejemplo, podemos necesitar usar un método de recuperación de ficheros del sistema después de la marcha atrás de los datos. Una vez que la recuperación se ha completado, debemos abrir la base de datos usando la opción RESETLOGS para tener accesos de escritura sobre la base de datos: ALTER DATABASE OPEN RESETLOGS;

Para desactivar la opción de flashback de la base de datos hay que ejecutar el comando FLASHBACK OFF cuando la base de datos está montada pero no abierta: STARTUP MOUNT EXCLUSIVE; ALTER DATABASE FLASHBACK OFF; ALTER DATABASE OPEN;

Oracle /262

ALTER DATABASE

V. SOPORTE DE OBJETOS Y XML Esta lección trata de las características y funcionalidades XML de Oracle Database en su versión 9i. Estas características no son sólo el almacenamiento de este tipo de datos como cadenas de caracteres sino el modelado de estos tipos, su clasificación e indexado en el sistema de modelado de datos de Oracle, permitiendo peticiones y manipulación avanzada de los mismos. La naturaleza de estructura no plana de los documentos XML obliga para su modelado hacer uso de las características objeto-relacionales de la base de datos. Por eso, la primera parte de esta lección habla del modelo objeto-relacional de Oracle. La segunda parte se centra en los documentos XML en la base de datos Oracle.

1. Modelo objeto-relacional de Oracle Las bases de datos relacionales son una tecnología muy madura y eficiente, pero su principal defecto es la planaridad de su modelo. Esto ha dificultado el modelado de los tipos de datos basados en objetos, obligando a establecer capas de transformación del modelo orientado a objetos de la aplicación con el modelo relacional de la base de datos. Esto exigía una doble labor de diseño en ambos planos y el uso de código envolvente (wrapper) entre los dos modelos. Las bases de datos orientadas a objetos aparecen para cubrir esta problemática. Al ser el modelado de los elementos de este software también orientado a objetos, sólo es necesario un único diseño y el wrapper desaparece o se simplifica enormemente. Sin embargo, el nuevo modelo orientado a objetos es mucho más ineficiente y lento. La última opción en aparecer fue la objeto-relacional. Este modelo combina las ventajas de los dos anteriores: la base de datos es relacional (por lo que conserva su rapidez y eficiencia), pero permite hacer uso de nuevos elementos que modelan los objetos a esta base de datos relacional, con lo que el analista y diseñador ve un modelo orientado a objetos. Oracle Database pertenece a este último tipo desde la versión 8i. Los objetos en Oracle 9i se denominan tipos abstractos de datos y poseen funcionalidades de herencia y métodos implementados en SQL o Java. La problemática de la planaridad se resuelve con las tablas anidadas y los arrays variables (VARRAY). Las relaciones entre objetos se establecen mediante el tipo de referencia REF. El almacenamiento de conjuntos de datos no estructurado se realiza con los tipos de objetos grandes. Y la base de datos relacional se puede modelar como objetos mediante las vistas de objeto. 1.1. Tipos abstractos de datos (clases y objetos). Oracle permite modelar los objetos en filas o columnas: • Un objeto modelado en filas es una tabla (una clase), en la cual cada fila es del tipo del objeto (una instancia), y los atributos nativos del objeto son modelados como columnas. • Un objeto modelado en columnas es una fila dentro de una tabla (o instancia del tipo de objeto), y se modela haciendo corresponder sus atributos con las columnas de la tabla o atributos del objeto respectivamente. En el ejemplo siguiente se define un objeto Compra, que se modelará con su atributo id seguido de los atributos de un objeto persona: nombre y telefono, y de una referencia a la tabla anidada tabla_detalles. Además declara un método getValor, cuyo cuerpo se definirá posteriormente. -- Se define el objeto Persona con la información del comprador CREATE TYPE Persona AS OBJECT ( nombre VARCHAR2(30), telefono VARCHAR2(20) ); / -- Se define el objeto LineaDetalle, con la información del producto comprado CREATE TYPE LineaDetalle AS OBJECT ( producto VARCHAR2(30), cantidad NUMBER, precioUnitario NUMBER(12,2) ); / -- Se define un nuevo tipo de array (tabla anidada) de líneas de detalle de compra CREATE TYPE Tabla_detalles AS TABLE OF LineaDetalle;

Oracle /263

/ -- Se define el objeto Compra, con un ID, un comprador y un conjunto de líneas de detalle de compra CREATE TYPE Compra AS OBJECT ( id NUMBER, comprador Persona, detalles Tabla_detalles, MEMBER FUNCTION getValor RETURN NUMBER );

1.1.1. Modificación de los atributos de un tipo de dato. Una vez creado un tipo de dato podemos modificar su lista de atributos mediante el comando con la siguiente sintaxis:

ALTER TYPE,

ALTER TYPE nombre_del_tipo ADD ATTRIBUTE (nombre_del_atributo tipo_de_dato) CASCADE;

Por ejemplo, supongamos el tipo Telefono_TA y el tipo Telefono_Lista_TA:

CREATE OR REPLACE TYPE Telefono_TA AS OBJECT ( codigo CHAR(3), numero CHAR(8) ) NOT FINAL; / CREATE OR REPLACE TYPE Telefono_Lista_TA AS TABLE OF Telefono_TA; / Si queremos añadir un nuevo atributo al tipo Telefono_TA, al usar el siguiente comando se producirá un error: CREATE OR REPLACE TYPE Telefono_TA AS OBJECT ( Pais CHAR(3), codigo CHAR(3), numero CHAR(8) ) NOT FINAL; / El error se debe a la dependencia del tipo Telefono_TA respecto al tipo Telefono_Lista_TA. Para añadir el nuevo

atributo debemos utilizar el siguiente comando:

ALTER TYPE Telefono_TA ADD ATTRIBUTE (pais CHAR(3)) CASCADE;

1.2. Seguridad para tipos de datos abstractos. El ejemplo precedente asume que el mismo usuario es propietarios de los tipos de dato Persona, LineaDetalle y Compra. ¿Qué ocurriría si el propietario de Persona fuese diferente del propietario de Compra? Por ejemplo, supongamos que la cuenta llamada Dora es propietaria del tipo de dato Persona, y el usuario de la cuenta George intenta crear un tipo de dato Cliente_TA. George ejecuta el siguiente comando: REM Mientras está conectado como George: CREATE TYPE Cliente_TA AS OBJECT ( Identidad Persona, Direccion VARCHAR2(100) ); George no es propietario del tipo abstracto Persona,

Si siguiente mensaje:

Oracle responderá a este comando

CREATE TYPE

con el

Warning: Tipo creado con errores de compilación.

Los errores de compilación son causados por problemas al crear el método constructor (un método especial creado por Oracle para el tipo de dato). Oracle no puede resolver la referencia al tipo de dato Persona porque George no es propietario de un tipo de dato con ese nombre. George podría intentar ejecutar otra vez el comando CREATE TYPE para referenciar especialmente el tipo de dato Persona perteneciente a Dora: CREATE OR REPLACE TYPE Cliente_TA AS OBJECT ( Identidad Dora.Persona, Direccion VARCHAR2(100) ); / Warning: Tipo creado con errores de compilación.

Para ver los errores asociados con la creación del tipo de dato podemos usar el comando SHOW ERRORS: SHOW ERRORS Errores para el tipo CLIENTE: LINE/COL ERROR

Oracle /264

------------ -------------------------------------------------------------------------0/0 PL/SQL: Análisis de la unidad de compilación terminada 3/11 PLS-00201: Identificador 'DORA.PERSONA' debe ser declarado George no será capaz de crear el tipo de dato Cliente_TA a menos que Dora primero EXECUTE sobre su tipo. Esto se muestra a continuación: REM Mientras está conectado como Dora: GRANT EXECUTE ON Persona TO George; Ahora George puede crear el tipo de datos basado en el tipo Persona, propiedad de Dora.

le conceda el permiso

Usar tipos de datos de otros usuarios no es trivial. Por ejemplo, durante operaciones de inserción, debemos especificar el propietario de cada tipo o usar un sinónimo. George puede crear una tabla basada sobre el tipo Cliente_TA (el cual incluye el tipo de datos Dora.Persona), tal como se muestra a continuación:

CREATE TABLE CLIENTES ( Cliente_Id NUMBER, Cliente Cliente_TA) ; Una inserción en la tabla CLIENTES debería tener el siguiente formato: INSERT INTO CLIENTES VALUES ( 1, Cliente_TA( Persona('Juan Pérez', '111111111'), 'Calle Ronda – 25') ); Ya que George no es propietario del tipo Persona, este comando fallará. Durante la inserción, se usa el método constructor de Persona (y Dora es su propietario). Por lo tanto, debemos modificar el comando INSERT para especificar que Dora es el propietario del tipo Persona. El siguiente ejemplo muestra el comando INSERT

correcto:

INSERT INTO CLIENTES VALUES ( 1, Cliente_TA( Dora.Persona('Juan Pérez', '111111111'), 'Calle Ronda – 25') ); George puede simplificar el SQL creando un sinónimo llamado Persona: CREATE PUBLIC SYNONYM Persona FOR Dora.Persona;

Este sinónimo puede ser usado en consultas DML y DDL. Podemos usar un sinónimo para otros tipos de datos de usuario durante consultas e inserciones. Por ejemplo, George inserta clientes ahora que existe el sinónimo Persona: INSERT INTO CLIENTES VALUES ( 1, Cliente_TA( Persona('Juan Pérez', '111111111'), 'Calle Ronda – 25') );

Nota. Cuando creamos un sinónimo, Oracle no verifica la validez del objeto para el cual es creado el sinónimo. Cuando se usa CREATE SYNONYM X FOR Y, Oracle no verifica que Y sea un nombre de objeto válido o un tipo de objeto válido. La validez de este objeto es sólo verificada cuando el objeto es accedido a través del sinónimo. En una implementación solo relacional de Oracle podemos conceder el permiso EXECUTE sobre objetos como procedimientos y paquetes. Dentro de la implementación objeto-relacional de Oracle, el permiso EXECUTE se extiende para cubrir tipos de datos abstractos. El permiso EXECUTE es apropiado porque los tipos de datos abstractos pueden incluir métodos (funciones PL/SQL y procedimientos que operan sobre el tipo de datos). Si concedemos algún permiso para usar nuestro tipo de datos, estamos concediendo al usuario el permiso de ejecutar los métodos definidos dentro del tipo de datos. Aunque Dora aún no definiera ningún método sobre el tipo Persona, Oracle crea automáticamente un método constructor para acceder a los datos. Cualquier objeto (como Cliente_TA) que usa el tipo Persona usa su método constructor asociado. Incluso si no hemos creado ningún método para nuestro tipo de dato abstracto, todavía tiene procedimientos asociados con él. Podemos describir la tabla CLIENTES: DESCRIBE CLIENTES; Name ------------------CLIENTE_ID CLIENTE

Null? --------

Type ----------------NUMBER CLIENTE_TA

Podemos usar el comando SET DESCRIBE DEPTH en SQL*Plus para mostrar los atributos del tipo de dato para tablas que usan características objeto-relacional. Podemos especificar valores de profundidad de 1 a 50: SET DESCRIBE DEPTH 2 DESC CLIENTES

Oracle /265

Name ------------------CLIENTE_ID CLIENTE IDENTIDAD DIRECCION

Null? --------

Type ----------------NUMBER CLIENTE_TA PERSONA VARCHAR(100)

Name ------------------CLIENTE_ID CLIENTE IDENTIDAD NOMBRE TELEFONO DIRECCION

Null? --------

Type ----------------NUMBER CLIENTE_TA PERSONA VARCHAR2(30) VARCHAR2(20) VARCHAR(100)

SET DESCRIBE DEPTH 3 DESC CLIENTES

1.3. Herencia de clases. Oracle permite la herencia simple de clases a través del modificador UNDER. Siguiendo el ejemplo del apartado anterior, añadimos una fecha a las compras creando una subclase: CREATE TYPE CompraConFecha UNDER Compra ( fecha DATE ) NOT FINAL; La cláusula FINAL indica si se puede heredar la clase por un subtipo ( NOT FINAL) o si es una clase final y por lo tanto no se puede extender (FINAL). En este ejemplo sí es heredable. Otra cláusula es INSTANTIABLE, para indicar que la clase no es abstracta y por tanto se puede instanciar, o NOT INSTANTIABLE, para indicar que la clase es abstracta y no se puede instanciar. La cláusula TREAT es el operador de moldeo de un tipo de objeto a subtipos del mismo. Si el moldeo no es posible se devuelve NULL. El siguiente ejemplo busca en la tabla relacional TRCompraConFecha, asociada al tipo CompraConFecha, y devuelve los registros de respuesta como objetos Compra. SELECT VALUE(TREAT(T AS Compra)) FROM TRCompraConFecha T WHERE VALUE(T.comprador) = Persona('Juan','555'); El operador de predicados para saber si un objeto instanciado pertenece a una clase es IS OF. IF c1 IS OF (Compra) THEN DBMS_OUTPUT.PUT_LINE('El objeto referenciado por la variabla c1 es de tipo Compra'); END IF;

1.4. Métodos. Existen tres tipos de métodos a definir en objetos de Oracle: métodos propiamente dichos, métodos de comparación y constructores. 1.4.1. Administrando métodos. Podemos añadir nuevos métodos a un tipo de datos modificando el tipo de datos (mediante el comando ALTER TYPE). Por ejemplo, para añadir un método al tipo de dato Persona ya creado: ALTER TYPE Persona ADD MEMBER FUNCTION ObtenNombreTelefono RETURN VARCHAR2 CASCADE;

Cuando se modifica un tipo de datos se listan todos sus métodos, tanto los viejos como los nuevos. Después de modificar el tipo de dato debemos modificar el cuerpo del tipo.

CREATE OR REPLACE TYPE BODY Persona IS MEMBER FUNCTION ObtenNombreTelefono RETURN VARCHAR2 CASCADE IS BEGIN RETURN Nombre || ' (' || telefono || ')'; END, END; No necesitamos tener el permiso EXECUTE sobre las funciones o procedimientos miembros de un tipo de dato abstracto. Si concedemos a otro usuario el permiso EXECUTE sobre el tipo Persona, este usuario adquiere automáticamente el permiso EXECUTE sobre los métodos que son parte del tipo de dato.

Oracle /266

Cuando creamos funciones miembro podemos especificar un método de comparación (o bien de tipo MAP o bien de tipo ORDER) o ninguno. 1.4.2. Definición de métodos. Los métodos (su firma) se indican en la definición del objeto, pero se implementan posteriormente mediante el comando CREATE TYPE BODY. El siguiente código muestra cómo implementar el cuerpo del método getValor() de la clase Compra para obtener el precio de venta de cada compra: CREATE TYPE BODY Compra AS id NUMBER, comprador Persona, detalles Tabla_detalles, MEMBER FUNCTION getValor RETURN NUMBER IS Total NUMBER := 0; BEGIN FOR I IN detalles.FIRST..detalles.LAST LOOP Total := Total + ( detalles(I).cantidad * detalles(I).precioUnitario ); END LOOP; RETURN Total; END; END;

1.4.3. Especificación de los métodos. La especificación de un método se hace junto a la creación de su tipo, y puede llevar asociada una directiva de compilación PRAGMA RESTRICT_REFERENCES(método, lista modificadores) para evitar que el método manipule la base de datos o las variables del paquete PL/SQL. Por ejemplo: CREATE TYPE Compra AS OBJECT ( id NUMBER, comprador Persona, detalles Tabla_detalles, MEMBER FUNCTION getValor RETURN NUMBER, PRAGMA RESTRICT_REFERENCES(getValor, WNDS, RNDS) );

Los modificadores de la directiva pueden ser alguno de los siguientes: • WNDS: no se permite al método modificar las tablas de la base de datos • WNPS: no se permite al método modificar las variables del paquete PL/SQL • RNDS: no se permite al método leer las tablas de la base de datos • RNPS: no se permite al método leer las variables del paquete PL/SQL Los métodos se pueden ejecutar sobre los objetos de su mismo tipo. Si c es una variable PL/SQL que almacena un objeto del tipo Compra, entonces c.getValor() retorna el valor total de la compra. 1.4.4. Constructores. Los métodos constructores, que son implícitamente creados por la base de datos, tienen como objetivo crear instancias de objetos a partir del estado definido por sus atributos. El constructor implícito creado por la base de datos tiene como nombre el mismo que la clase y como parámetros sus atributos en el mismo orden en que se definieron en la clase. CREATE TYPE Cliente_t AS OBJECT ( id NUMBER, nombre VARCHAR2(20), telefono VARCHAR2(30) ); / DECLARE cust1 Cliente_t:= Cliente_t(103, 'Ravi', '1-800-555-1212'); cust2 Cliente_t := NEW Cliente_t(104, 'Ronn', 1-700-444-1212');

Aún así, es posible definir constructores propios en que se soliciten otros atributos y el proceso de construcción no sea simplemente la asignación de valores a sus atributos. CREATE TYPE Cliente_t AS OBJECT ( id NUMBER,

Oracle /267

nombre VARCHAR2(20), telefono VARCHAR2(30), CONSTRUCTOR FUNCTION Cliente_t(id NUMBER, nombre VARCHAR2) RETURN SELF AS RESULT

); / CREATE TYPE BODY Cliente_t AS CONSTRUCTOR FUNCTION Cliente_t(id NUMBER, nombre VARCHAR2) RETURN SELF AS RESULT AS BEGIN SELF.id := id; SELF.nombre := nombre; RETURN; END; END; / DECLARE Cliente_t := NEW Cliente_t(103, 'Ravi');

También se pueden crear métodos estáticos que actúen como constructores:

CREATE TYPE Cliente_t AS OBJECT ( id NUMBER, nombre VARCHAR2(20), telefono VARCHAR2(30), STATIC FUNCTION CrearCliente(id NUMBER, nombre VARCHAR2) RETURN Cliente_t ); / CREATE TYPE BODY Cliente_t AS STATIC FUNCTION CrearCliente(id NUMBER, nombre VARCHAR2) RETURN Cliente_t IS BEGIN RETURN Cliente_t(id, nombre, NULL); END; END; / DECLARE Cliente_t:= Cliente_t.CrearCliente(103, 'Ravi');

1.4.5. Métodos de comparación. Para comparar los objetos de cierto tipo es necesario indicar a Oracle cuál es el criterio de comparación. Para ello hay que escoger entre un método MAP u ORDER, debiéndose definir sólo uno de estos métodos por cada tipo de objeto que necesite ser comparado. La diferencia entre ambos tipos de métodos es la siguiente: • Un método MAP sirve para indicar cuál de los atributos del tipo se utilizará para ordenar los objetos del tipo, y por tanto se puede utilizar para comparar los objetos de ese tipo por medio de los operadores de comparación aritméticos (). Por ejemplo, la siguiente declaración permite establecer que los objetos Persona se van a comparar por su atributo nombre sin tener en cuenta mayúsculas y minúsculas: CREATE TYPE Persona AS OBJECT ( nombre VARCHAR2(30), telefono VARCHAR2(20), MAP MEMBER FUNCTION RetornaNombre RETURN VARCHAR2 ); / CREATE TYPE BODY Persona AS MAP MEMBER FUNCTION RetornaNombre RETURN VARCHAR2 IS BEGIN RETURN UPPER(nombre); END; END;

Oracle /268

• Un método ORDER utiliza los atributos del objeto sobre el que se ejecuta para realizar un cálculo y compararlo con otro objeto del mismo tipo pasado como argumento de entrada. Este método devolverá un valor negativo si el parámetro de entrada es mayor que el atributo, un valor positivo si ocurre lo contrario y un cero si ambos son iguales. El siguiente ejemplo define un orden para el tipo Persona diferente al anterior.

CREATE TYPE Persona AS OBJECT ( nombre VARCHAR2(30), telefono VARCHAR2(20), ORDER MEMBER FUNCTION Compara( P Persona) RETURN INTEGER ); / CREATE TYPE BODY Persona AS ORDER MEMBER FUNCTION Compara( P Persona) RETURN INTEGER IS BEGIN IF (SELF.nombre = P.nombre) THEN RETURN 0; ELSE IF (SELF.nombre < P.nombre) THEN RETURN -1; ELSE RETURN 1; END IF; END IF; END; END; Ahora ya podemos comparar dos objetos Persona: DECLARE p1 Persona := Persona('Juan', '111111'); p2 Persona := Persona('LUIS', '222222'); BEGIN IF p1 < p2 THEN -- p1 es menor que p2 ELSE -- p1 es mayor o igual que p2 END IF; END; Sólo una de estas definiciones (MAP u ORDER) puede ser válida en un tiempo

dado. Si un tipo de objeto no tiene definido ninguno de estos métodos, Oracle es incapaz de deducir cuándo un objeto es mayor o menor que otro. Sin embargo, sí puede determinar cuándo dos objetos del mismo tipo son iguales. Para ello, el sistema compara el valor de los atributos de los objetos uno a uno: • Si todos los atributos son no nulos e iguales, Oracle indica que ambos objetos son iguales. • Si alguno de los atributos no nulos es distinto en los dos objetos, entonces Oracle dice que son diferentes. • En otro caso, Oracle dice que no puede comparar ambos objetos. Estas funciones son muy útiles para funciones de agrupación del estilo DISTINCT, GROUP BY y ORDER BY. 1.5. Tablas relacionales de objetos. Una vez definidos los tipos, éstos pueden utilizarse para definir nuevos tipos, tablas relacionales que almacenen objetos de esos tipos, o para definir el tipo de los atributos de una tabla relacional. 1.5.1. Creación de tablas de objetos. Una tabla relacional de objetos es una clase especial de tabla que almacena un objeto en cada fila y que facilita el acceso a los atributos de esos objetos como si fueran columnas de la tabla. Por ejemplo, se puede definir una tabla relacional para almacenar objetos Persona: CREATE TABLE TRPersona OF Persona (nombre PRIMARY KEY);

Y otra para almacenar la dependencia de trabajo entre dos personas: Oracle /269

CREATE TABLE TRJefeEmpleado ( jefe Persona, empleado Persona);

La diferencia entre la primera tabla ( TRPersona) y la segunda (TRJefeEmpleado) es que la primera almacena objetos con su propia identidad (OID) y la segunda no es una tabla de objetos, sino una tabla con dos columnas con un tipo de datos de objeto. Nota. No podemos borrar y recrear un tipo que es usado por una tabla. Oracle añade un campo identificador a cada objeto de una tabla relacional para poder referenciarlo. Este identificador puede coincidir con el campo clave de los registros si especificamos: CREATE TABLE TRPersona OF Persona ( nombre PRIMARY KEY ) OBJECT IDENTIFIER IS PRIMARY KEY;

O bien puede ser generado automáticamente si especificamos: CREATE TABLE TRPersona OF Persona ( nombre PRIMARY KEY ) OBJECT IDENTIFIER IS SYSTEM GENERATED;

Además de esto, Oracle permite considerar una tabla de objetos desde dos puntos de vista: • Como una tabla con una sola columna cuyo tipo es el de un tipo de objetos. • Como una tabla que tiene tantas columnas como atributos los objetos que almacena. Por ejemplo, se puede ejecutar una de las dos instrucciones siguientes. INSERT INTO TRPersona VALUES ( 'Juan Pérez', '696-779789'); / SELECT VALUE(T) FROM TRPersona T WHERE T.nombre = 'Juan Pérez'; la primera instrucción, la tabla TRPersona

En se considera como una tabla con varias columnas cuyos valores son los especificados. En el segundo caso, se la considera como con una tabla de objetos que en cada fila almacena un objeto. En esta instrucción, la cláusula VALUE permite visualizar el valor de un objeto. Las reglas de integridad, de clave primaria, y el resto de propiedades que se definan sobre una tabla, sólo afectan a los objetos de esa tabla; es decir, no se refieren a todos los objetos del tipo asignado a la tabla. Para crear una tabla relacional que almacene objetos que contengan tablas anidadas debemos especificar una tabla relacional que almacene los elementos de la tabla anidada. Por ejemplo: -- Se crea una tabla relacional 'TRCompra' cuyos registros mapearán los objetos 'Compra' -- También se crea una tabla relacional 'TRDetalles' cuyos registros mapearán los objetos 'LineaDetalle' --contenidos en la tabla anidada 'detalles' CREATE TABLE TRCompra OF Compra (id PRIMARY KEY) OBJECT IDENTIFIER IS PRIMARY KEY NESTED TABLE detalles STORE AS TRDetalles; La tabla anidada TRDetalles no podrá ser accedida directamente. Su contenido debe ser accedido a los objetos de la tabla TRCompra.

través de

1.5.2. Inserción y acceso a los datos en tablas de objetos. Toda clase o tipo de array dispone de un constructor, el cual es creado automáticamente para poder instanciar un objeto de la clase o tipo. Este constructor tiene como parámetros los atributos que definen la clase en el mismo orden en que fueron definidos. Por ejemplo, para insertar un nuevo objeto de tipo Compra en la base de datos: INSERT INTO TRCompra VALUES ( 45, Persona ('Juan', '555'), Tabla_detalles ( LineaDetalle('Peras', 4, 3.5), LineaDetalle('Churros', 12, 0.4) ) );

En una base de datos con tipos y objetos, lo más recomendable es utilizar siempre alias para los nombres de las tablas. El alias de una tabla debe ser único en el contexto de la consulta. Los alias sirven para acceder al contenido de la tabla, pero en el caso de las tablas que almacenan objetos, el alias también sirve como Oracle /270

referencia del objeto, y por tanto se utiliza para acceder a los atributos, métodos y referencias mediante la nomenclatura habitual del punto entre objeto y atributo. Por ejemplo, para hacer una petición de elementos: SELECT T.* FROM TRCompra T WHERE T.comprador.nombre='Juan' AND T.comprador.telefono='555';

O bien:

SELECT VALUE(T) FROM TRCompra T WHERE T.comprador = Persona('Juan', '555');

Ambos devuelven el mismo valor, salvo que la primera en forma de tabla, siendo cada columna un atributo del objeto resultado de la consulta. Y en el segundo devuelve el objeto con la forma de constructor explicada en el ejemplo anterior. 1.5.3. Llamadas a métodos. Para invocar un método hay que utilizar su nombre y unos paréntesis que encierren sus argumentos de entrada. Si el método no tiene argumentos, se especifican los paréntesis aunque estén vacíos. Por ejemplo, la siguiente consulta es correcta: SELECT T.getValor() FROM TRCompra T;

1.5.4. Índices para tablas de objetos. La creación de índices en objetos es igual a la de tablas relacionales, identificando el elemento de indexación mediante la referencia al atributo índice. Por ejemplo, para indexar la tabla TRCompra por el nombre del comprador: CREATE INDEX NombreComprador ON TRCompra (comprador.nombre);

1.6. Tipos referencia (REF). Los identificadores únicos asignados por Oracle a los objetos que se almacenan en una tabla relacional, permiten que éstos puedan ser referenciados desde los atributos de otros objetos o desde las columnas de tablas. El tipo de datos proporcionado por Oracle para soportar esta facilidad se denomina REF. Un atributo de tipo REF almacena una referencia a un objeto del tipo definido, e implementa una relación de asociación entre los dos tipos de objetos. Estas referencias se pueden utilizar para acceder a los objetos referenciados y para modificarlos; sin embargo, no es posible operar sobre ellas directamente. Para asignar o actualizar una referencia se debe utilizar siempre REF o NULL. Cuando se define una columna de un tipo a REF, es posible restringir su dominio a los objetos que se almacenen en cierta tabla. Si la referencia no se asocia a una tabla sino que sólo se restringe a un tipo de objeto, se podrá actualizar a una referencia a un objeto del tipo adecuado con independencia de la tabla donde se almacene. En este caso su almacenamiento requerirá más espacio y su acceso será menos eficiente. El siguiente ejemplo redefine el tipo Compra y restringe el dominio de su campo comprador a los objetos de cierta tabla. -- Tabla relacional cuyos registros se mapean con objetos "Persona" CREATE TABLE TRPersona OF Persona; -- Modificación de la clase "Compra" para que referencie a un objeto "Persona" de la tabla "TRPersona" CREATE TYPE Compra AS OBJECT ( id NUMBER, comprador REF Persona, detalles Tabla_detalles ); -- Tabla relacional cuyos registros se mapean con objetos "Compra" CREATE TABLE TRCompra OF Compra ( PRIMARY KEY (id), SCOPE FOR (comprador) IS TRPersona -- o bien: comprador SCOPE IS TRPersona );

Cuando se borran objetos de la base de datos puede ocurrir que otros objetos que referencien a los borrados queden en estado inconsistente. Estas referencias se denominan dangling references, y Oracle proporciona el predicado llamado IS DANGLING que permite comprobar cuándo sucede esto. Por ejemplo, la siguiente instrucción pone a nulo el comprador que ha perdido su referencia: UPDATE TRCompra SET comprador = NULL WHERE comprador IS DANGLING;

Los tipos referencia permiten navegar a través de la estructura de objetos de la misma manera que si fuesen un atributo del objeto, mediante el operador punto. Oracle /271

SELECT T.comprador.nombre FROM TRCompra T;

Oracle posee dos funciones para los tipos referencia: REF() que devuelve el identificador de objeto dado la instancia de un objeto, y DEREF() que dado el identificador de un objeto devuelve la instancia del objeto (y por tanto es el opuesto de la función REF). Si queremos insertar un objeto en la tabla TRCompra deberemos obtener la referencia de un objeto de la tabla TRPersona de la siguiente manera: Para

INSERT INTO TRCompra (id, comprador, detalles) SELECT 1, REF(tr), NULL FROM TRPersona tr WHERE nombre='José Pérez'; mostrar el contenido de la tabla, ahora debemos aplicar la función DEREF: SELECT id, DEREF(comprador), detalles FROM TRCompra;

1.7. Tablas anidadas y arrays variables. Como ya se ha visto, los arrays variables ( VARRAY) y las tablas anidadas (TABLE) permiten modelar las relaciones de uno a varios que son muy comunes en los modelos orientados a objetos. Estos tipos se denominan colecciones porque representan conjuntos de datos de un mismo tipo. Las tablas anidadas permiten almacenar un conjunto indeterminado de elementos, y por eso no es posible almacenar su contenido dentro del registro que la incluye como campo. Cuando creemos un atributo de tipo tabla anidada debemos indicar siempre qué tabla externa será la que almacene los datos. En el ejemplo del apartado anterior CREATE TABLE TRCompra OF Compra NESTED TABLE detalles STORE AS TRDetalles; TRDetalles es la tabla externa que almacenará los elementos LineaDetalle

de la tabla anidada detalles. Esta tabla relacional quedará oculta en el diccionario de datos y su acceso sólo podrá realizarse a través de la tabla principal TRCompra. Sin embargo, los arrays variables son de un tamaño máximo fijo, por lo que no es necesario guardarlos en una tabla externa y se almacenan en la propia tabla principal (u objeto). Hay dos formas de acceder a los elementos de las colecciones: • La primera es recibiendo la colección como un solo elemento en la forma de su constructor. • La segunda es acceder a su contenido como un conjunto de registros mediante la función TABLE(). Un ejemplo del primer tipo sería el siguiente, en el que se devuelve un valor de tipo colección que engloba a todos los valores de la colección: SELECT T.id, T.detalles FROM TRCompra T;

Un resultado posible será: Id --1 2

detalles -----------–––––––-----------------------------------------------------------Tabla_detalles (LineaDetalle('P1',4,3.5), LineaDetalle('P2',12,0.4)) Tabla_detalles (LineaDetalle('P1',9,7))

Un ejemplo del segundo tipo es el siguiente en el que se devuelven las líneas de detalle para el pedido de id 1: SELECT * FROM TABLE( SELECT T.detalles FROM TRCompra T WHERE T.id=1);

El resultado será:

Producto Cantidad ---------- ---------P1 4 P2 12

PrecioUnitario -–––––––––------3.5 0.4

Igualmente, para la inserción o modificación de colecciones podemos hacer uso del constructor de la colección para introducirle el conjunto de valores por completo, modificándose todo el array variable o tabla anidada de una vez: INSERT INTO TRCompra VALUES ( 1, Persona('Juan', '555'), Tabla_detalles( LineaDetalle('chorizo', 4, 3.5), LineaDetalle('jamón', 3, 7.6) ) );

O bien (sólo para tablas anidadas) podemos insertar tuplas en la columna correspondiente de la tupla seleccionada por una subconsulta usando la palabra clave THE con la siguiente sintaxis: Oracle /272

INSERT INTO THE (SELECT T.detalles FROM TRCompra T WHERE id = 45) VALUES (LineaDetalle('Nuevo producto', 3, 5.2));

Esta técnica es especialmente útil si dentro de una tabla anidada se guardan referencias a otros objetos. También en el caso de una tabla anidada (no es posible en un array variable), podemos acceder a la tabla directamente haciendo uso de la función TABLE(), lo que nos permitiría actualizar de forma selectiva los elementos: UPDATE TABLE(SELECT T.detalles FROM TRCompra T WHERE T.id=1) P SET VALUE(P) = LineaDetalle('chorizo', 6, 5.5) WHERE P.producto = 'choriz';

Para poner condiciones a las tuplas de una tabla anidada, se pueden utilizar cursores dentro de un SELECT o desde un programa PL/SQL. Por ejemplo, la siguiente consulta recupera las compras con su código de compra, el nombre del comprador y las líneas de detalle con cantidades mayores de 5: SELECT T.id, T.comprador.nombre, CURSOR(SELECT * FROM TABLE(T.detalles) D WHERE D.cantidad>5) FROM TRCompra T; cláusula THE también sirve para seleccionar las tuplas de una tabla anidada. Por ejemplo, para seleccionar

La la primera línea de detalle de la compra de código 45:

SELECT LP.* FROM THE (SELECT T.detalles FROM TRCompra T WHERE T.id=45) LP WHERE ROWNUM=1;

1.7.1. Funciones adicionales para tablas anidadas y arrays variables. Desde Oracle Database 10g, podemos usar varias funciones nuevas para tablas anidadas y arrays variables. Para los siguientes ejemplos usaremos los siguientes tipos y tablas:

La

CREATE TYPE ANIMALES_AV AS VARRAY(10) OF VARCHAR(20); CREATE TYPE ANIMALES_TA AS TABLE OF VARCHAR(20); CREATE TABLE GRANJA ( Propietario VARCHAR2(200), Ubicacion VARCHAR2(200), Animales ANIMALES_AV ); función CARDINALITY retorna el número de elementos dentro de una tabla anidada o array variable: SELECT CARDINALITY(Animales) FROM GRANJA; función MULTISET EXCEPT toma dos tablas anidadas como entrada y retorna el conjunto de registros

La están en la primera tabla anidada pero no en la segunda (similar al operador de resta).

La

DECLARE A1 ANIMALES_TA := ANIMALES_TA ('PERRO', 'GATO'); A2 ANIMALES_TA := ANIMALES_TA('GALLINA', 'PERRO'); A3 ANIMALES_TA; BEGIN A3 := A1 MULTISET EXCEPT A2; -- Como resultado: A3 := ANIMALES_TA('GATO') END; función MULTISET INTERSECT toma dos tablas anidadas como entrada DECLARE A1 ANIMALES_TA := ANIMALES_TA ('PERRO', 'GATO'); A2 ANIMALES_TA := ANIMALES_TA('GALLINA', 'PERRO'); A3 ANIMALES_TA; BEGIN A3 := A1 MULTISET INTERSECT A2; -- Como resultado: A3 := ANIMALES_TA('PERRO') END; función MULTISET UNION toma dos tablas anidadas como entrada

La tablas.

DECLARE A1 ANIMALES_TA := ANIMALES_TA ('PERRO', 'GATO'); A2 ANIMALES_TA := ANIMALES_TA('GALLINA', 'PERRO'); A3 ANIMALES_TA; BEGIN

Oracle /273

que

y retorna los elementos en común.

y retorna los elementos de ambas

A3 := A1 MULTISET INTERSECT DISTINCT A2; -- Como resultado: A3 := ANIMALES_TA('PERRO', 'GATO', 'GALLINA') END; Para esta función podemos especificar MULTISET UNION ALL (por defecto) o MULTISET UNION DISTINCT. La función SET convierte una tabla anidada dentro de un conjunto mientras elimina duplicados, retornando

una tabla anidada de valores distintos:

DECLARE A1 ANIMALES_TA := ANIMALES_TA ('PERRO', 'GATO', 'GALLINA', 'PERRO'); A2 ANIMALES_TA; BEGIN A3 := SET(A1); -- Como resultado: A3 := ANIMALES_TA('PERRO', 'GATO', 'GALLINA') END; La función COLLECT toma una columna como entrada y crea una tabla anidada del tipo de entrada. Para obtener los resultados de esta función debemos usarla dentro de una función CAST con un comando SELECT con la cláusula GROUP BY. CREATE OR REPLACE TYPE varchar2_ntt AS TABLE OF VARCHAR2(200); SELECT Propietario, CAST(COLLECT(Ubicacion) AS varchar2_ntt) FROM GRANJA GROUP BY Propietario;

Un posible resultado de esta consulta puede ser: PROPIETARIO -------------------------Juan Pérez María Belo

CAST(COLLECT(UBICACION)ASVARCHAR2_NTT) ----------------------------------------------------------VARCHAR2_NTT('Madrid', 'Castellón') VARCHAR2_NTT('Alicante')

La función POWERMULTISET toma como entrada una tabla anidada y retorna una tabla anidada cuyos elementos son tablas anidadas que contienen todos los subconjuntos no vacíos de la tabla de entrada. SELECT CAST(POWERMULTISET(Animales) AS ANIMALES_TA) FROM GRANJA; función POWERMULTISET_BY_CARDINALITY toma como entrada

La una tabla anidada y una cardinalidad y retorna una tabla anidada cuyos elementos son tablas anidadas que contienen los subconjuntos no vacíos de la tabal de entras en la cardinalidad especificada. SELECT CAST(POWERMULTISET_BY_CARDINALITY(Animales, 2) AS ANIMALES_TA) FROM GRANJA;

1.8. Vistas de objeto. Para poder convertir el modelo relacional en el modelo orientado a objetos, sin necesidad de modificar los datos ni su estructura (metadatos), es posible generar vistas de los datos relacionales de tal manera que éstos puedan ser vistos como objetos. CREATE TABLE Tabla_empleado ( empNum NUMBER (5), eNombre VARCHAR2 (20), salario NUMBER (9, 2), trabajo VARCHAR2 (20) ); / CREATE TYPE Empleado_t AS OBJECT( empNum NUMBER (5), eNombre VARCHAR2 (20), salario NUMBER (9, 2), trabajo VARCHAR2 (20), MEMBER FUNCTION SalarioNeto RETURN NUMBER ); / CREATE VIEW vistaEmpleado OF Empleado_t WITH OBJECT IDENTIFIER (empNum) AS SELECT e.empNum, e.eNombre, e.salario, e.trabajo FROM Tabla_empleado e

Oracle /274

WHERE trabajo = 'Programador';

Al crear la vista de objetos debemos especificar el campo que actuará como identificador de cada objeto y que será normalmente el campo clave de la tabla base. La ventaja de las vistas de objeto está en la capacidad de invocar los métodos del objeto en las consultas: SELECT v.empNo, v.eNombre, v.SalarioNeto() FROM vistaEmpleado v;

1.9. Trabajando con tipos SQL desde aplicaciones JDBC. 1.9.1. Recuperación de objetos Oracle en objetos «oracle.sql.STRUCT». Podemos recuperar un objeto Oracle directamente dentro de una instancia de oracle.sql.STRUCT. En el siguiente ejemplo, se usa el método getObject() para obtener un objeto de una tabla de objetos. Connection conexion = DriverManager.getConnection( cadenaDeConexion, login, password ); // Se crea un tipo de objeto y una tabla de objetos String cmd = "CREATE TYPE Tipo_Lote AS OBJECT (codigo NUMBER, fecha DATE)"; Statement stmt = conexion.createStatement(); stmt.execute(cmd); cmd = "CREATE TABLE Tabla_Lote OF Tipo_Lote"; stmt.execute(cmd); // Se insertan dos registros en la tabla de objetos cmd = "INSERT INTO Tabla_Lote VALUES (Tipo_Lote(10,'01-Abr-11'))"; stmt.execute(cmd); cmd = "INSERT INTO Tabla_Lote VALUES (Tipo_Lote(20,'02-May-11'))"; stmt.execute(cmd); // Se recuperan los registros de la tabla de objetos ResultSet rs= stmt.executeQuery("SELECT * FROM Tabla_Lote"); rs.next(); // Se accede al primer registro oracle.sql.STRUCT oracleSTRUCT = (oracle.sql.STRUCT) rs.getObject(1); Otro modo es retornar los objetos como un objeto STRUCT es moldear el resultado a OracleResultSet y usar el método de Oracle getSTRUCT(): oracle.sql.STRUCT oracleSTRUCT = ((OracleResultSet) rs).getSTRUCT(1); Si queremos acceder ahora a los atributos del STRUCT como tipos oracle.sql, se usa getOracleAttributes(): oracle.sql.Datum[] attrs = oracleSTRUCT.getOracleAttributes();

un objeto el método

1.9.2. Enlazando objetos «STRUCT» dentro de un comando. Para enlazar un objeto oracle.sql.STRUCT a un comando del tipo PreparedStatement o CallableStatement, podemos usar el método estándar setObject(), o moldear el objeto de comando a un OraclePreparedStatement u OracleCallableStatement, y usar el método setOracleObject(). Siguiendo con el ejemplo anterior: PreparedStatement ps = conexion.prepareStatement("UPDATE Tabla_Lote T SET VALUE(T) = ? WHERE VALUE(T).codigo=10"); StructDescriptor sd = new StructDescriptor("Tipo_Lote", conexion); STRUCT miSTRUCT = new STRUCT (sd, conexion, new Object [] {11, new java.sql.Date(2011,12,22)}); ps.setObject(1, miSTRUCT, Types.STRUCT); ps.execute()

O bien:

PreparedStatement ps = conexion.prepareStatement("UPDATE Tabla_Lote T SET VALUE(T) = ? WHERE VALUE(T).codigo=10"); StructDescriptor sd = new StructDescriptor("Tipo_Lote", conexion); STRUCT miSTRUCT = new STRUCT (sd, conexion, new Object [] {11, new java.sql.Date(2011,12,22)}); ((OraclePreparedStatement) ps).setOracleObject(1, miSTRUCT); ps.execute()

1.10. Crear y usar clases de objetos Java personalizadas para objetos Oracle. Si queremos crear clases de objetos personalizadas para los objetos Oracle, entonces debemos definir entradas en el mapa de tipos que especifique las clases de objetos que los drivers deben instanciar para los correspondientes tipos de Oracle. También debemos proporcionar un modo de crear y poblar instancias de la clase personalizada desde los objetos Oracle y sus atributos. Para crear y poblar clases personalizadas y proporcionar capacidades de lectura/escritura podemos elegir entre dos interfaces a implementar: Oracle /275

• La interfaz de JDBC estándar java.sql.SQLData. • Las interfaces oracle.sql.ORAData y oracle.sql.ORADataFactory proporcionadas por Oracle. La clase personalizada debe implementar una de estas interfaces. La interfaz ORAData también puede ser usada para implementar la clase de referencia personalizada correspondiente a una clase de objeto personalizada. Si usamos la interfaz SQLData, sin embargo, sólo podemos usar tipos de referencias débiles en Java (java.sql.Ref u oracle.sql.REF). La interfaz SQLData se usa sólo para mapear objetos SQL. Como un ejemplo, asumamos que existe en la base de datos un tipo de objeto Oracle denominado Empleado.Persona, el cual consiste de dos atributos: Nombre (como un VARCHAR2) y Edad (como un NUMBER). CREATE TYPE Persona AS OBJECT ( Nombre VARCHAR2(100), Edad NUMBER(3) );

Usaremos el mapeado de tipos para especificar que el objeto Persona debería mapear con una clases personalizada que llamaremos JPersona. Podemos implementar tanto SQLData como ORAData en la clase JPersona. 1.10.1. Ventajas de «ORAData» contra «SQLData». A la hora de decidir qué interfaz implementar, consideremos las siguientes ventajas de cada interfaz: Ventajas de «oracle.sql.ORAData» Ventajas de «java.sql.SQLData» • No requiere una entrada en el mapa de tipos para el • Es un estándar JDBC, haciendo nuestro código más objeto Oracle. portable. • Tiene acceso a las extensiones de Oracle. • Podemos crear un ORAData desde un STRUCT. Es más eficiente porque evita conversiones innecesarias. • Proporciona mejor rendimiento: ORAData trabaja directamente con tipos Datum, que es el formato interno usando por el driver que contiene los objetos Oracle.

La interfaz SQLData es sólo para mapear objetos SQL. Mientras que ORAData es más flexible, permitiendo mapear objetos SQL con cualquier otro tipo SQL. Podemos crear un objeto ORAData para cualquier tipo de dato encontrado dentro de la base de datos de Oracle. Esto podría ser útil para serializar datos RAW en Java. 1.10.2. El mapeado de tipos para implementaciones «SQLData». Si usamos la interfaz SQLData en una clase personalizada, entonces podemos crear entradas para el mapeado de tipos que especifiquen las clases de objetos personalizados que usaremos para mapear los tipos de objetos de Oracle. Podemos usar el mapeado de tipos por defecto para el objeto de conexión, o un mapeado de tipos que podemos especificar cuando recuperemos los datos. El método ResultSet.getObject() tiene una firma que permite especificar un mapeado de tipos: public Object getObject(int columnIndex, Map map);

Cuando usamos la implementación SQLData, si no incluimos una entrada en el mapeado de tipos, entonces el objeto será mapeado a la clase oracle.sql.STRUCT por defecto. (Las implementaciones ORAData, en contrasta, tienen su propia funcionalidad de mapeado, así que no requiere entradas de mapeado de tipos. En este caso se usa el método getORAData() en vez del método getObject().) El mapeado de tipos relaciona un tipo de Java a un nombre de tipo SQL para un objeto Oracle. Este mapeado se almacena en un HashTable como pares clave-valor. Cuando de leen datos de un objeto Oracle, el driver JDBC considera el mapeado de tipos para determinar qué clase Java debe usar para materializar los datos desde el tipo de Oracle. Cuando escribimos datos a un objeto Oracle, el driver JDBC obtiene el nombre del tipo SQL desde la clase de Java invocando el método getSQLTypeName() de la interfaz SQLData. La conversión entre SQL y Java es realizada por el driver. Los atributos de una clase Java que correspondan con objetos Oracle pueden usar tipos nativos Java o tipos nativos Oracle (instancias de las clases oracle.sql.*). Creando un mapeado de tipos para implementaciones «SQLData». Cada instancia de conexión (objetos java.sql.Connection) tiene asociado un mapa, el cual puede ser obtenido mediante el método getTypeMap(). Cuando se establece por primera vez la conexión este mapa está vacío. Podemos poblarlo usando cualquier funcionalidad de mapeado SQL-Java. Normalmente deberemos seguir los siguientes pasos para añadir entradas al mapa existente: 1) Usar el método OracleConnection.getTypeMap() para obtener el mapa de tipos: java.util.Map miMapa = conexion.getTypeMap();

Oracle /276

2) Usar el método put() para añadir entradas de mapeado. miMapa.put("EMPLEADO.PERSONA", JPersona.class);

Nota. El nombre del tipo SQL debe ser especificado en mayúsculas, porque es así como es almacenado en el diccionario de datos de la base de datos. Podemos también crear un nuevo mapa y asignarlo al objeto de conexión mediante el método Connection.setTypeMap(). Materializando tipos de objetos no especificados en el fichero de tipos. Si no proporcionamos en el mapa de tipos una entrada apropiada cuando usamos una llamada a getObject(), entonces el driver JDBC materializará un objeto Oracle como una instancia de la clase oracle.sql.STRUCT. Si el objeto Oracle contiene objetos embebidos, y éstos no están presentes en el mapa de tipos, el driver los materializará también como instancias de oracle.sql.STRUCT. Si los objetos embebidos están presentes en el mapa de tipos, una llamada al método getAttributes() retornará los objetos embebidos como instancias de las clases Java especificadas en el mapa. 1.10.3. Cómo se utiliza la interfaz «SQLData». La interfaz SQLData define métodos que trasladan tipos entre SQL y Java para los objetos de Oracle. Una clase que implemente SQLData debe proporcionar los métodos readSQL() y writeSQL(). El driver JDBC llama a nuestro readSQL() para leer un flujo de valores de datos desde la base de datos y puebla una instancia de nuestra clase personalizada. Normalmente el driver usará este método como parte de la llamada al método OracleResultSet.getObject(). Análogamente, el driver llama al método writeSQL() para escribir una secuencia de datos desde una instancia de nuestra clase personalizada a la base de datos. Normalmente el driver usará este métodos como parte de la llamada al método setObject() de objetos OraclePreparedStatement y OracleCallableStatement. La firma de ambos métodos es la siguiente: public void readSQL(SQLInput stream, String typeName) throws SQLException public void writeSQL(SQLOutput stream) throws SQLException

Uso de las interfaces «SQLInput» y «SQLOutput». El método readSQL() recibe como primer argumento un objeto del tipo java.sql.SQLInput, el cual representa un canal de entrada. SQLInput incluye métodos readXXX() para cada tipo de dato Java a los cuales un atributos del objeto Oracle puede ser convertido, tales como readObject(), readInt(), readLong(), readFloat(), readBlob(), y demás. Cada método readXXX() convierte el dato SQL al dato Java correspondiente. Por su parte, el método writeSQL() recibe como argumento un objeto de tipo SQLOutput, el cual representa un canal de salida. SQLOutput incluye métodos writeXXX() para cada uno de los tipos Java. Implementación de los métodos «readSQL()» y «writeSQL()». El método readSQL() recibe un argumento de tipo SQLInput y un string que indica el nombre del tipo SQL de los datos. Cuando nuestra aplicación Java invoca getObject(), el driver JDBC crea un canal de tipo SQLInput y lo puebla con datos desde la base de datos. El driver puede también determinar el nombre del tipo SQL de los datos cuando son leídos desde la base de datos. Cuando el driver llama a readSQL() le pasa estos parámetros. Para cada tipo de dato Java que mapea a un atributo del objeto Oracle, readSQL() debe llamar al apropiado método readXXX() del canal. Por su parte, el método writeSQL() recibe un canal de tipo SQLOutput. Por cada tipo de dato Java que mapea a un atributo de un objeto Oracle, writeSQL() debe llamar a los apropiados métodos writeXXX() del canal para enviar los datos de sus atributos. Por ejemplo, si estamos leyendo objetos Persona (que tienen un atributo VARCHAR2 para el nombre, y un atributo NUMBER para la edad) debemos llamar a readString() y a readInt() para poblar los campos correspondientes del objeto Java. La implementación puede ser como sigue: public class JPersona implements SQLData { private String nombre; private int edad; public JPersona() throws SQLException { } public JPersona(String nombre, int edad) { this.nombre = nombre; this.edad = edad;

Oracle /277

} @Override public String getSQLTypeName() throws SQLException { return "EMPLEADO.PERSONA"; } @Override public void readSQL(SQLInput stream, String typeName) throws SQLException { if (typeName.equals("EMPLEADO.PERSONA")) { nombre = stream.readString(); edad = stream.readInt(); } } @Override public void writeSQL(SQLOutput stream) throws SQLException { stream.writeString(nombre); stream.writeInt(edad); } @Override public String toString() { return "JPersona{" + "nombre=" + nombre + ", edad=" + edad + '}'; } }

Leyendo y escribiendo datos con la implementación «SQLData». Veamos ahora cómo leer registros de una tabla de objetos Persona creada en la base de datos: CREATE TABLE TPERSONA OF PERSONA; INSERT INTO TPERSONA VALUES (PERSONA('JUAN', 30)); INSERT INTO TPERSONA VALUES (PERSONA('ANA', 33));

Previamente debemos crear un objeto de conexión y crear una entrada en el mapa de tipos para nuestro tipo de objeto: Connection conexion = DriverManager.getConnection( cadenaDeConexion ); conexion.getTypeMap().put("EMPLEADO.PERSONA", JPersona.class); Ahora consultamos la tabla para obtener los registros como objetos Persona de Oracle: Statement comando = conexion.createStatement(); ResultSet rs = comando.executeQuery("SELECT VALUE(T) FROM TPERSONA T"); Por último podemos leer el primer registro y mostrar su contenido como un objeto JPersona de Java: rs.next(); JPersona persona = (JPersona) rs.getObject(1); System.out.println(persona); conexion.close(); Si ahora queremos insertar un nuevo objeto Persona en la base de datos podemos utilizar un PreparedStament

y

pasar el dato como un parámetro:

conexion.getTypeMap().put("EMPLEADO.PERSONA", JPersona.class); PreparedStatement ps = conexion.prepareStatement("INSERT INTO TPERSONA VALUES (?)"); ps.setObject(1, new JPersona("Maria", 23)); ps.execute(); conexion.close();

1.10.4. Cómo se utiliza la interfaz «ORAData». La interfaces oracle.sql.ORAData y oracle.sql.ORADataFactory hacen lo siguiente: • El método toDatum() de ORAData transforma los datos en una representación de tipos oracle.sql.*. • El método create() de ORADataFactory equivale a un constructor de nuestra clase personalizada. Crea y retorna una instancia de ORAData. El driver JDBC usa el método create() para retornar una instancia de la clase personalizada para nuestra aplicación Java. Toma como entrada un objeto oracle.sql.Datum y un entero que indica el código de tipo SQL (pueden utilizarse las constantes de la clase oracle.jdbc.OracleTypes). Las interfaces ORAData y ORADataFactory tienen las siguientes definiciones: public interface ORAData { Datum toDatum (OracleConnection conn) throws SQLException; }

Oracle /278

public interface ORADataFactory { ORAData create (Datum d, int sql_Type_Code) throws SQLException; }

Recuperando e insertando datos. El driver JDBC proporciona los siguientes métodos para recuperar e insertar objetos de datos como instancias de ORAData. Para recuperar objetos de datos: • Se usa el método específico de Oracle OracleResultSet.getORAData(), que tiene la siguiente firma: ORAData getORAData (int col_index, ORADataFactory factory);

Este método toma como entrada el índice de la columna de los datos en el ResultSet, y una instancia de ORADataFactory. • O se usa el método estándar ResultSet.getObject(index, map) para recuperar datos como instancias de ORAData. En este caso debemos incluir una entrada en el mapa de tipos para identificar la clase creadora que será usada por el tipo de objeto y que se corresponde con un nombre de tipo SQL. Para insertar objetos de datos: • Se usa el método específico de Oracle OraclePreparedStatement.setORAData(), que tiene la siguiente firma: void setORAData (int bind_index, ORAData custom_obj);

Este método toma como entrada el índice del parámetro de la variable enlazada y el objeto que contiene la variable. • O se usa el método estándar PreparedStatement.setObject(). También se puede usar este método, en sus diferentes formas, para insertar instancias ORAData sin requerir un mapa de tipos. Para continuar con el ejemplo del tipo Persona de Oracle, podemos crear la siguiente clase en nuestra aplicación Java: // Se define la clase personalizada implementando ambas interfaces public class JPersona2 implements ORAData, ORADataFactory { private CHAR nombre; private NUMBER edad; public JPersona2() throws SQLException { } private JPersona2(CHAR nombre, NUMBER edad) { this.nombre = nombre; this.edad = edad; } public JPersona2(String nombre, int edad) { this.nombre = new CHAR(nombre.getBytes(), CharacterSet.make(CharacterSet.UTF8_CHARSET)); this.edad = new NUMBER(edad); } @Override public String toString() { return "JPersona{" + "nombre=" + nombre + ", edad=" + edad.stringValue() + '}'; } @Override public Datum toDatum(Connection cnctn) throws SQLException { StructDescriptor sd = StructDescriptor.createDescriptor("PEDRO.PERSONA", cnctn); Object[] atributos = {nombre, edad}; return new STRUCT(sd, cnctn, atributos); } @Override public ORAData create(Datum datum, int i) throws SQLException { if (datum == null) { return null; } Object[] atributos = ((STRUCT) datum).getOracleAttributes(); return new JPersona2((CHAR) atributos[0], (NUMBER) atributos[1]); } } Ahora podemos escribir un código para recuperar un registro Persona de la tabla TPersona:

Oracle /279

Connection conexion = DriverManager.getConnection( cadenaDeConexion ); Statement comando = conexion.createStatement(); OracleResultSet rs = (OracleResultSet) comando.executeQuery("SELECT VALUE(T) FROM TPERSONA T"); rs.next(); JPersona2 datum = (JPersona2) rs.getORAData(1, new JPersona2()); conexion.close();. En este ejemplo se recupera el primer registro como un objeto ORAData, y para ello se pasa un objeto de tipo JPersona2, puesto que implementa ORADataFactory. El driver JDBC llamará al método create() de este objeto, de forma que retorna a nuestra aplicación Java una instancia de la clase JPersona2 poblada con los datos del ResultSet.

Nota. ORAData y ORADataFactory pueden ser definidas como interfaces independientes, de forma que diferentes clases pueden implementarlas. También podemos usar el método estándar getObject() para recuperar los datos. Este método requiere un mapa de tipos que identifique la clase que implementa ORADataFactory y su correspondiente nombre de tipo SQL.

HashMap mapa = new HashMap(); mapa.put ("EMPLEADO.PERSONA", JPersona2.class); Connection conexion = DriverManager.getConnection( cadenaDeConexion ); Statement comando = conexion.createStatement(); OracleResultSet rs = (OracleResultSet) comando.executeQuery("SELECT VALUE(T) FROM TPERSONA T"); rs.next(); JPersona2 datum = (JPersona2) rs.getObject(1, mapa); conexion.close();. insertar un nuevo registro en la tabla TPersona usando un objeto JPersona2 podemos utilizar el siguiente

Para código:

En

JPersona2 p = new JPersona2("Jóse", 40); OraclePreparedStatement ps = (OraclePreparedStatement) conexion.prepareStatement("INSERT INTO TPERSONA VALUES (?)"); ps.setORAData(1, new JPersona2("Jóse", 40)); ps.execute(); conexion.close(); vez de setORAData() también se puede usar el método estándar setObject(): ps.setObject(1, new JPersona2("Jóse", 40));

1.10.5. Creando objetos SQL a partir de clases de Java. En los apartados previos hemos creado una clase Java para mapear una clase SQL de Oracle creada previamente. También es posible crear una clase Java dentro de la base de datos y usarla para crear a partir de ella un tipo de objeto de Oracle. Definimos la clase JPersona dentro de la base de datos: CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED JPersona AS import java.sql.*; import java.io.*; public class JPersona implements SQLData { private String tipo_sql = "EMPLEADO.PERSONA"; private String nombre; private int edad; public String getNombre() {return nombre;} public void setNombre(String nombre) {this.nombre= nombre;}; public int getEdad() {return edad;} public void setEdad(int edad) {this.edad = edad;} public JPersona () {} public String getSQLTypeName() throws SQLException { return tipo_sql; } public void readSQL(SQLInput stream, String typeName) throws SQLException { tipo_sql = typeName; nombre = stream.readString(); edad = stream.readInt(); }

Oracle /280

}

public void writeSQL(SQLOutput stream) throws SQLException { stream.writeString(nombre); stream.writeInt(edad); } // otros métodos public String toString() { return "(" + nombre + "," + edad + ")"; }

Una vez implementada la clase Java y compilada, se puede cargar en la base de datos, y a partir de este momento la clase y sus métodos están disponibles en Oracle. A continuación se define el tipo abstracto SQL de la manera explicada hasta ahora y se indica que el tipo lo implementa una clase Java y cómo se mapean los métodos de Java a Oracle. La firma del método en Oracle va seguida del comando EXTERNAL NAME con la firma del método en Java de donde se infiere el mapeado de tipos. El siguiente ejemplo modifica el tipo de objeto Persona para que mapee la clase de Java denominada JPersona: CREATE OR REPLACE TYPE Persona AS OBJECT EXTERNAL NAME 'JPersona' LANGUAGE JAVA USING SQLData ( nombre VARCHAR2(30) EXTERNAL NAME 'nombre', edad NUMBER(3) EXTERNAL NAME 'edad', MEMBER FUNCTION TO_STRING RETURN VARCHAR2 EXTERNAL NAME 'toString() return java.lang.String' ); /

2. Documentos XML en Oracle XML es el formato de codificación/estructuración para el intercambio de datos más común hoy en día. Sin embargo el modelado de este tipo de datos en bases de datos relacionales era complicado por el mismo motivo que el modelado de objetos: los documentos XML no tienen una estructura plana, con lo que su representación en tablas se hace complicada. La opción hasta hace poco era el almacenar los documentos en objetos grandes de tipo CLOB o NCLOB y analizar/transformar estos elementos tomando este flujo de caracteres y haciéndolos pasar por un parseador XML habitual. No se aprovechaba ninguna de las características de la base de datos para mejorar la eficiencia de búsquedas y consultas, salvo por alguna característica de indexado avanzada de texto. Con la llegada del modelo objeto-relacional el problema de la planaridad se resuelve y las mismas facilidades que se daban para el modelado de objetos se ofrecen ahora para el modelado de los objetos XML en la estructura de la base de datos. Esta integración permite hacer uso de la eficiencia de las consultas y flexibilidad de recorrido de estructuras del modelo relacional en estos documentos. 2.1. «XMLType». Este mapeado de la estructura XML a la estructura de la base de datos en Oracle se realiza con el tipo XMLType, que es un tipo abstracto. El tipo XMLType se almacena en un tipo CLOB y puede asociarse a un Esquema XML para la definición de su estructura, lo que obliga a que cualquier documento sea validado con este esquema. En este caso, el esquema del documento se modela en la estructura objeto-relacional de la base de datos. La ventaja de no validar contra un esquema es que todo tipo de documentos XML pueden almacenarse en ese elemento XMLType. En caso contrario se obliga a que el elemento sea válido frente al esquema asociado, aunque su mapeado en la estructura objeto-relacional permite tratar el documento de manera más eficiente y flexible. 2.2. Mapeado de «XMLType» dado un esquema XML. Los elementos del esquema XML se mapean como objetos en los que cada elemento anidado de tipo simple es representado por un atributo de un tipo nativo lo más acorde posible con el tipo del esquema: si es un número con NUMBER, si es texto con VARCHAR2,… Aún así, es posible forzar la representación del elemento a un tipo de Oracle mediante el atributo SQLType utilizado en el elemento del esquema. Cuando un elemento contiene un elemento complejo, éste es modelado con un objeto y el elemento padre establece una referencia a él con tipos referencia. Es posible forzar que el mapeado de los tipos complejos se realice en CLOB, NCLOB o VARCHAR2 (sin ser representados en el modelo Oracle /281

objeto-relacional) mediante el atributo SQLType (=CLOB) utilizado en el elemento del esquema. Cuando la ocurrencia de un elemento, bien simple o complejo, es mayor que uno el elemento es representado en el objeto padre con un array variable (si el número de ocurrencias máximas es finito) o con un tabla anidada (si es infinito). El mapeado se entiende mucho mejor con el siguiente ejemplo: DECLARE doc varchar2(3000) := '











';

Se mapearía de la siguiente manera:

CREATE TYPE Direccion_t AS OBJECT ( calle VARCHAR2(4000), ciudad VARCHAR2(4000) ); CREATE TYPE Direccion_t_table AS TABLE OF Direccion_t; CREATE TYPE Empleado AS OBJECT ( Nombre VARCHAR2(4000), Edad NUMBER, Direccion_table Direccion_t_table );

2.3. Crear tablas/columnas «XMLType». Para crear columnas o tablas XMLType sólo tenemos que hacerlo como lo hicimos al definir columnas o tablas de objetos. Si no validamos el tipo XMLType con un esquema, los ejemplos serían los siguientes (el primero es una tabla con una columna tipo XMLType y el segundo es una tabla de XMLType): CREATE TABLE TREmpleado ( id NUMBER(3), xmlEmpleado XMLTYPE ); / CREATE TABLE TXEmpleado OF XMLTYPE;

En este segundo caso, realmente se crea una tabla con un campo de tipo XMLTYPE denominado SYS_NC_ROWINFO$. Para definir una columna o tabla XMLType asociada a un esquema debemos registrar primero el esquema en la base de datos. Esto se realiza mediante la biblioteca DBMS_XMLSCHEMA que posee dos funciones: REGISTERSCHEMA para registrar el esquema y DELETESCHEMA para eliminar el registro. DBMS_XMLSCHEMA.REGISTERSCHEMA ('http://www.oracle.com/empleado.xsd', doc); DBMS_XMLSCHEMA.DELETESCHEMA ('http://www.oracle.com/empleado.xsd', DBMS_XMLSCHEMA.DELETE_CASCADE_FORCE); Una vez registrado el esquema podemos crear columnas y tablas XMLType asociadas comando XMLSCHEMA en la definición:

Oracle /282

haciendo uso del

O

-- Para la siguiente definición de tabla CREATE TABLE TREmpleado ( id NUMBER(3), xmlEmpleado XMLTYPE ) XMLTYPE COLUMN xmlEmpleado XMLSCHEMA "http://www.oracle.com/empleado.xsd"ELEMENT "Empleado"; -- Se asocia el campo "xmlEmpleado" sobre el elemento "Empleado" del esquema bien se crea una tabla de tipo XMLType: CREATE TABLE TXEmpleado OF XMLTYPEXMLSCHEMA "http://www.oracle.com/empleado.xsd" ELEMENT "Empleado"; -- O bien se utiliza la sintaxis: CREATE TABLE TXEmpleado OF XMLTYPEELEMENT "http://www.oracle.com/empleado.xsd#Empleado";

2.4. Operaciones con columnas XMLType. 2.4.1. Insertar y actualizar registros con datos XML. Si tratamos a los tipos XMLType como objetos podemos utilizar el constructor de dichos objetos para instanciar nuevos elementos XMLType, tomando como parámetro la cadena que representa al documento XML: INSERT INTO TREmpleado (id, xmlEmpleado) VALUES( 1, XMLType('Juan34') );

Si queremos actualizar el campo debemos proporcionar un nuevo documento XML entero:

UPDATE TREmpleado SET xmlEmpleado = XMLType('Juan34') WHERE id = 1;

También podemos hacer uso de los comandos SQLX para generar el documento XML en vez del constructor de XMLType, tal como veremos en un apartado posterior. 2.4.2. Consultar registros con datos XML. Es posible recuperar un documento XML de un registro en forma de CLOB, VARCHAR2 o NUMBER mediante los métodos de XMLType: getClobVal(), getStringVal(), y getNumberVal(). Con estas funciones simplemente obtenemos el documento XML convertido en un tipo nativo de Oracle. El siguiente ejemplo obtiene el contenido del campo xmlEmpleado como un valor de tipo CLOB: SELECT e.xmlEmpleado.getClobval() AS "EmpleadoXML" FROM TREmpleado e WHERE e.id = 1;

También es posible recuperar partes del documento y efectuar predicados de selección en partes del documento. Estas partes se basan en la estructura DOM de XML y se señalan haciendo uso de expresiones XPath. Las funciones incluidas con este propósito son extract, extractValue y existsNode: las dos primeras devuelven el nodo del documento XML (de la estructura DOM) solicitado y la tercera devuelve verdadero ( 1) cuando existe el nodo solicitado o falso (0) si no existe. En el siguiente ejemplo se hace una consulta a la tabla relacional TREmpleado para obtener el nodo Empleado de aquellos empleados cuyo nombre sea "Juan": SELECT e.xmlEmpleado.getStringVal() AS EmpleadoXML FROM TREmpleado e WHERE e.xmlEmpleado.existsNode('/Empleado [Nombre = "Juan"]') = 1;

Siendo el resultado (se muestra un registro):

EmpleadoXML -------------------------------Juan34 En el siguiente ejemplo se hace una consulta para obtener el id y el nodo Direccion de aquellos empleados la tabla relacional TREmpleado que tengan asignado el campo xmlEmpleado, y lo convertimos en una cadena no sería un XMLType): SELECT e.id ,EXTRACT(e.xmlEmpleado, '/Empleado/Direccion').getStringVal()AS "Dirección" FROM TREmpleado e WHERE e.xmlEmpleado IS NOT NULL;

Un resultado posible podría ser (se muestran dos registros): Oracle /283

en (si

id ---1 2

Dirección ------------------------------------------------------------------------------------ La Ronda>La Coruña La función extract() siempre devuelve el nodo en formato XMLType; si queremos recuperar el valor de un nodo final podemos utilizar getNumberVal() o getStringVal() sobre el elemento XMLType devuelto. O bien podemos utilizar extractValue(), que tiene una sintaxis idéntica a extract() pero que devuelve el valor del nodo de texto y no el elemento XMLType. Estas funciones sólo son válidas para nodos que tengan un solo y único

nodo de texto. Por ejemplo, si queremos recuperar el nombre del empleado de id 1 podemos usar una de las dos consultas siguientes: SELECT EXTRACT(e.xmlEmpleado, '/Empleado/Nombre/text()').getStringVal() AS "Nombre" FROM TREmpleado e WHERE e.id = 1; / SELECT EXTRACTVALUE(e.xmlEmpleado, '/Empleado/Nombre').getStringVal() AS "Nombre" FROM TREmpleado e WHERE e.id = 1;

2.4.3. Borrar registros con datos XML. El borrado de registros que contienen columnas de tipo XMLType es similar a borrar registros con cualquier otro tipo de datos. Por ejemplo, para eliminar los registros con la ciudad Madrid, ejecutaremos el siguiente comando: DELETE FROM TREmpleado WHERE e.xmlEmpleado.extractValue('/Empleado/Direccion/Ciudad')='Madrid';

En esta consulta se supone que cada empleado posee una única dirección. En caso contrario la función extractValue() produciría un error. 2.4.4. Actualizar datos XML. La función updateXML() permite actualizar el contenido de un objeto de tipo XMLType retornando un nuevo XMLType. Tiene tres parámetros: - El objeto XMLType que queremos modificar. - Una expresión XPath que estable el o los elementos que queremos modificar. - Una expresión que será utilizada para hacer la sustitución. Gracias a esta función podemos evitar modificar todo el documento XML cuando sólo varíe una parte. Por ejemplo, el siguiente comando modifica la edad de todos los empleados de la tabla TREmpleado al valor 33: UPDATE TREmpleado SET xmlEmpleado = UPDATEXML(xmlEmpleado,'/Empleado/Edad/text()', '33'); Es importante notar que, efectivamente, no se modifica un único nodo Edad, sino que el valor de cada nodo Edad de empleado es modificado al valor 33. Si actualizamos un elemento XML a valor null, Oracle elimina los atributos y elementos hijos del mismo, y

entonces el elemento queda vacío. Pero el elemento no desaparece como tal, y retiene sus propiedades de tipo y espacio de nombres. Un valor null para un elemento actualizado es equivalente a asignar un elemento vacío. Si actualizamos un nodo de texto de un elemento a null, Oracle quita el valor quedando el elemento vacío. Asignar un atributo a null es similar a poner el valor del atributo a un string vacío. Si aplicamos la función: updateXML(xmlEmpleado,'/Empleado/Direccion',null)

Obtenemos:

Valor original en el campo xmlEmpleado Nuevo valor tras la actualización

Juan 33

333 Nueva York



Juan 33

Oracle /284

Por tanto no se puede utilizar updateXML() de forma directa para añadir o eliminar elementos o atributos del documento. Para ello se debe actualizar el contenido del documento a un nuevo valor. Supongamos, por ejemplo, que queremos añadir una nueva dirección al empleado de código 1. Podemos obtener la lista de nodos de dirección mediante la función extract() y concatenarle la nueva dirección. A continuación se puede utilizar la función UpdateXml() para cambiar el contenido antiguo por el nuevo. El siguiente comando añade un nuevo nodo de dirección al empleado de id 1: UPDATE TREmpleado SET xmlEmpleado = UPDATEXML(xmlEmpleado, '/Empleado/Direccion', EXTRACT(xmlEmpleado, '/Empleado/Direccion') || '' ) WHERE id = 1;

Y el resultado es:

Valor original en el campo xmlEmpleado Nuevo valor tras la actualización

Juan 33

333 Nueva York



Juan 33

333 Nueva York



Pero aunque esto funciona, ¡CUIDADO! Si volviésemos a ejecutar nuevamente este comando obtendríamos un resultado no esperado donde se duplicarían las direcciones existentes más la nueva. Esto es así porque la primera vez sólo existía un nodo Dirección y por tanto era sustituido por sí mismo más la nueva dirección concatenada. La segunda vez ya existen dos nodos Direccion, y la sustitución se realiza por cada nodo que retorna la expresión Xpath '/Empleado/Direccion'. Este problema se resolvería si encapsulamos los nodos Direccion en un único nodo Direcciones, tal como se muestra a continuación: UPDATE TREmpleado SET xmlEmpleado = UPDATEXML(xmlEmpleado, '/Empleado/Direcciones', '' || EXTRACT(xmlEmpleado, '/Empleado/Direcciones/Direccion') || '' || ' ' ) WHERE id = 1;

Y el resultado es:

Valor original en el campo xmlEmpleado Nuevo valor tras la actualización

Juan 33

333 Nueva York



Juan 33

333 Nueva York



Si queremos eliminar una dirección, podemos obtener la lista de direcciones mediante extract() aplicando una selección sólo sobre las que queremos conservar. Por ejemplo, si queremos eliminar la última dirección podemos realizar la siguiente actualización: UPDATE TREmpleado SET xmlEmpleado = UPDATEXML(xmlEmpleado, '/Empleado/Direccion','' || EXTRACT(xmlEmpleado, '/Empleado/Direcciones/Direccion [not (position() = last())]') ) || '' WHERE id = 1;

Oracle /285

Y el resultado es: Valor original en el campo xmlEmpleado Nuevo valor tras la actualización

Juan 33

333 Nueva York

444 Boston



Juan 33

333 Nueva York



2.4.5. Aplicar hojas de estilo XSTL. Otra forma de modificar un documento XML es utilizando una hoja de estilo XSLT y la función XMLTransform(). El comando XMLTransform() toma como parámetros dos instancias de XMLtype, siendo la primera el documento origen y la segunda un documento XSLT de transformaciones XML, y devuelve el documento resultante de la transformación. Un sinónimo de este comando es el método Transform() de la clase XMLType. Como ejemplo, supongamos la siguiente tabla: CREATE TABLE testxml (xml XMLType);

Insertamos un registro:

INSERT INTO testxml VALUES (XMLType('12'));

Tras la inserción, el contenido de la tabla es el siguiente: XML ----------------12

Ahora aplicamos una transformación, de forma que se eliminan las etiquetas como valor de la etiqueta .

y sus valores se concatenan

SELECT XMLTransform(x.xml, XMLType(



' )) AS xml FROM testxml x;

El resultado es:

XML ----------------12

Otra forma de aplicar la transformación es con la función Transform(): SELECT x.xml.transform(XMLType( . . . )) AS xml FROM testxml x;

Las funciones Transform() retornan un valor XMLType; esto quiere decir que el resultado de la transformación debe ser un documento o parte de un documento XML bien formado. 2.5. Validar los documentos XML sobre un esquema. Es posible validar documentos XML frente a esquemas XML mediante el comando XMLisValid() y el método de XMLType, isSchemaValidated(). Ambos devuelven verdadero (1) si el documento se valida correctamente. El siguiente ejemplo valida cada documento de la columna xmlEmpleado de la tabla TREmpleado con el esquema dado devolviendo 1 (verdadero) cuando la validación es correcta y 0 (falso) cuando no: SELECT e.xmlEmpleado.isSchemaValid('http://www.oracle.com/empleado.xsd','Empleado') FROM TREmpleadoe;

Oracle /286

2.6. Indexar elementos «XMLType». Para acelerar las consultas en los tipos XMLType podemos utilizar índices basados en función, para acelerar las funciones extract() o existsNode() en rutas XPath definidas, como por ejemplo: CREATE INDEX ciudad_index ON TREmpleado(xmlEmpleado.extract('//Ciudad/text()').getStringVal()); CREATE BITMAP INDEX direccion_index ON TREmpleado(xmlEmpleado.existsNode('//Direccion')); los documentos XMLType mapeados, el índice a utilizar para acelerar las consultas con parámetros

Para XPath es CTXSYS.CTXXPATH:

o rutas

CREATE INDEX xml_idx ON TREmpleado(xmlEmpleado) INDEXTYPE IS CTXSYS.CTXXPATH;

Sin embargo, si quisiéramos más flexibilidad y funcionalidad de un índice para los documentos XML sin esquema deberíamos utilizar "Oracle Text", que se utiliza en columnas tipo CLOB o VARCHAR y que en la última versión de Oracle se ha extendido para utilizar XMLType. Para crear un índice de "Oracle Text" en la columna xmlEmpleado de la tabla TREmpleado: CREATE INDEX emp_text_index ON TREmpleado(xmlEmpleado) INDEXTYPE IS CTXSYS.CONTEXT;

El tipo de índice utilizado es CTXSYS.CONTEXT, que es apropiado para predicados y consultas de "Oracle Text" como CONTAINS, SCORE, INPATH, HASPATH. Si queremos acelerarlas consultas con las funciones extract() y existsNode() debemos utilizar el índice CTXSYS.CTXXPATH. Cuando trabajemos con las opciones de secciones de "Oracle Text" debemos definir cómo se crean estas secciones. Las secciones son partes del documento a las que referenciaren las búsquedas, como son por ejemplo los nodos de cada documento XML. Las secciones permiten restringir las búsquedas a ciertas partes del documento. El predicado de búsqueda de "Oracle Text" es CONTAINS(), que toma 2 parámetros: el primero es la columna donde efectuar la búsqueda y el segundo es el predicado a cumplir por el documento. Por cada fila devuelta CONTAINS() devuelve el porcentaje entre 0 y 100 de relevancia del documento. SELECT id FROM mi_tabla WHERE CONTAINS (mi_columna, 'ingresos') > 0

Si el documento esta dividido por secciones (en el modo de secciones de "Oracle Text" AUTO_SECTION_GROUP, las secciones se crean automáticamente con las etiquetas [y se las referencia: etiqueta] y atributos XML [y se las referencia: etiqueta@atributo]) nos es posible restringir la búsqueda a una sección en particular. En el siguiente ejemplo se busca ingresos en el atributo titulo de la etiqueta informe: SELECT id FROM mi_tabla WHERE CONTAINS (mi_columna,'ingresos WITHIN informe@titulo') > 0 El operador INPATH es muy parecido a WITHIN pero ha sido incluido para dar soporte a documentos XML. Su sintaxis es igual pero la ruta de sección ya no es del tipo etiqueta o etiqueta@atributo, si no que se utiliza XPath para referirse a cada nodo del documento. El operador HASPATH busca qué documentos tienen la ruta

especificada en XPath como parámetro.

SELECT id FROM catalogo WHERE CONTAINS(text,'caja INPATH(pedido/item)') > 0; SELECT id FROM catalogo WHERE CONTAINS(text,'HASPATH(pedido/item)') > 0;

2.7. SQLX, generar XML de los datos relacionales. Al igual que en el modelado objeto-relacional, en el que no es necesario convertir los datos del modelo plano relacional al modelo objeto-relacional para trabajar con ellos en este último modelo, es posible mediante comandos y vistas representar datos del modelo relacional u objeto-relacional como documentos XML sin necesidad de modificarlos. Oracle soporta cinco comandos del estándar SQLX (SQL para XML, SQL/XML) para la representación de datos relacionales con XML: XMLElement(), XMLForest(), XMLConcat(), XMLAttributes() y XMLAgg(). También soporta XMLColAttVal() como comando SQLX propio, pero aún no es aceptado en el estándar. Estos comandos permiten representar datos como un documento XML definiendo nosotros la estructura de ese documento. Oracle, además, soporta las funciones SYS_XMLGEN.SYS_XMLAGG(), XMLSEQUENCE() y XMLFormat con el mismo propósito que las anteriores pero sin ser parte del estándar SQLX o de su propuesta. Nos es posible también crear vistas del tipo XMLType para representar tablas y vistas relacionales como documentos XML de forma transparente para la consulta, como si de una consulta a un XMLType se tratase. 2.7.1. Funciones SQLX. XMLElement() es una función que devuelve un tipo XMLType dados como parámetros el nombre del elemento Oracle /287

XML, una serie de atributos y el contenido del nodo. El XMLType devuelto es un nodo con el nombre del primer parámetro, los atributos del segundo y el contenido de los últimos parámetros. El contenido puede ser un valor o un nuevo elemento XMLType para poder formar la estructura anidada de los documentos XML. Los atributos se definen mediante la función XMLAttributes() que toman como método el listado de atributos a asignar al elemento XML. Si no se especifica la cláusula AS en cada atributo se toma como nombre de atributo el inferido de la estructura relacional, y si se utiliza AS se toma el indicado. Supongamos la siguiente tabla relacional: CREATE TABLE TEmpleado ( id INTEGER PRIMARY KEY, fecha DATE, fnombre VARCHAR2(100), lnombre VARCHAR2(100), dept VARCHAR2(20), jubila DATE );

La instrucción siguiente muestra el contenido de esta tabla en formato XML: SELECT XMLELEMENT("Empleado", XMLATTRIBUTES (e.id,e.fecha AS "fechaNacimiento"), XMLELEMENT("nombre", e.fnombre ||''|| e.lnombre), XMLELEMENT("jubilacion", e.jubila)) AS "result" FROM TEmpleado e WHERE id > 200 ;

Donde el resultado puede ser algo como esto (se muestra un único registro): result -------------------------------------------------------------- Juan Martín 24-05-2000

función XMLForest() crea un árbol XML de los parámetros

La que toma. Un árbol XML son nodos situados a la misma altura, es decir, nodos que partirían del mismo nodo raíz, salvo que no definimos este nodo raíz. Cuando el parámetro se acompaña de la cláusula AS se utiliza éste como nombre del elemento XML, cuando no, se infiere de la estructura de los datos. El siguiente es un ejemplo de XMLElement() para crear el nodo raíz y de XMLForest() para crear los elementos de este nodo: SELECT XMLELEMENT("Empleado", XMLATTRIBUTES ( e.fnombre ||''|| e.lnombre AS "nombre" ), XMLForest ( e.jubila, e.dept AS "departamento")) AS "result" FROM TEmpleado e;

Donde el resultado puede ser algo como esto (se muestran dos registros):

result -------------------------------------------------24-05-2000Finanzas 01-02-1996Ventas La función XMLConcat(), dados como parámetros una secuencia de elementos XMLType o datos tipo XMLType, los concatena uno tras otro en el orden en que aparecen como parámetros. Mientras que en XMLForest() los

parámetros son datos relacionales, en XMLConcat() son tipos XMLType:

SELECT XMLCONCAT(XMLELEMENT("primero", e.fnombre),XMLElement("ultimo", e.lnombre))AS "result" FROM TEmpleado e ;

Donde el resultado puede ser algo como esto (se muestran dos registros): result -----------------------------------------MaríaMartin JuanGómez función XMLAgg() es una función de agregado

La que produce un bosque de elementos XML dada una colección de elementos. Se usa normalmente en consultas con cláusulas de agrupación como GROUP BY, como se puede ver en el siguiente ejemplo: SELECT XMLELEMENT("Departamento", XMLATTRIBUTES ( e.dept AS "nombre" ),

Oracle /288

XMLAGG (XMLELEMENT("emp", e.lnombre))) AS "result" FROM TEmpleado e GROUP BY dept;

Donde el resultado puede ser algo como esto (se muestran dos registros):

result ---------------------------------------------LópezGarcía GutierrezMartin este ejemplo, la función XMLAgg() genera elementos por cada registro dentro

En un grupo y los concatena como un único XMLType. La función XMLColAttVal() crea un árbol de XML donde cada elemento es de tipo column y posee un atributo tipo name con el nombre del elemento, especificado por AS en los parámetros o inferido de los datos. El siguiente es un ejemplo de uso de XMLColAttVal(): SELECT XMLELEMENT("Emp",XMLATTRIBUTES(e.fnombre ||''||e.lnombre AS "nombre" ), XMLCOLATTVAL ( e.jubila, e.dept AS "departamento")) AS "result" FROM TEmpleado e;

Donde el resultado puede ser algo como esto (se muestran tres registros): result ------------------------------------------------------------ 25-05-2000 Finanzas

01-02-1996 Ventas

15-11-1992 Personal

2.7.2. «SYS_Xmlgen», «Sys_XmlAgg», «XMLSequence» y «XMLFormat». La función SYS_XMLAGG() permite englobar todos los resultados (con formato XMLType) de una consulta en un único valor de tipo XMLType. La etiqueta raíz del resultado englobado será por defecto ROWSET, pero puede ser cambiada con la función XMLFormat(). Como ejemplo, supongamos la siguiente tabla donde se insertan documentos XML con la información de ordenadores: CREATE TABLE TXOrdenador OF XMLTYPE; / INSERT INTO TXOrdenador VALUES(XMLType('sobremesaFujitsu')); / INSERT INTO TXOrdenador VALUES(XMLType('portátilACER')); / INSERT INTO TXOrdenador VALUES(XMLType('sobremesaIBM/marca>')); La siguiente consulta obtiene de cada registro de la tabla TXOrdenador el nodo y devuelve un único resultado con los nodos concatenados dentro de una etiqueta : SELECT SYS_XMLAGG(EXTRACT(VALUE(T),'/pc/marca'), XMLFORMAT('marcas'))AS "Marcas" FROM TXOrdenador T;

El resultado sería el siguiente (se muestra un único registro):

Marcas ---------------------------------------------------------------------------------------------FujitsuACERIBM/marca>

Por su parte, la función XMLSEQUENCE() devuelve una colección (un array variable) de objetos XMLType a partir de un XMLType. Es decir, toma los nodos hijos directos del XMLType y devuelve un nodo XMLType por cada uno de ellos en un objeto SYS.XMLSequenceType. Supongamos la siguiente tabla: Oracle /289

CREATE TABLE TXPlantilla OF XMLTYPE;

La cual contendrá en cada registro la lista de empleados de un departamento. El contenido de un registro se especifica en el siguiente comando de inserción: INSERT INTO TXPlantilla VALUES ( '

112 Joe 50000

217 Jane 60000

412 Jack 40000

' );

El siguiente código PL muestra como obtener los nodos emp del departamento de Finanzas como un array de valores XMLType: DECLARE vEmp XMLSequenceType; BEGIN -- Realizo una consulta que obtiene la colección de nodos y la asigna en vEmp SELECT XMLSEQUENCE(EXTRACT(VALUE(P), '/empleados/emp')) INTO vEmp FROM TXPlantilla P WHERE EXTRACTVALUE(VALUE(P),'/empleados/@departamento')='Finanzas'; -- Muestro el contenido del varray vEmp FOR I IN vEmp.First()..vEmp.Last() LOOP DBMS_OUTPUT.PUT_LINE( vEmp(I).getStringVal() ); END LOOP; END; El tipo SYS.XMLSequenceType es un tipo de VArray predefinido en Oracle cuyos XMLType.

elementos son de tipo

Si ahora quisiéramos realizar una consulta que muestre los datos de cada empleado del departamento de Finanzas como si se tratase de una tabla relacional, podemos usar la función TABLE() para convertir la colección que devuelve XmlSequence en un origen de datos válido para la cláusula FROM: SELECT EXTRACTVALUE(VALUE(T), '/emp/empno') AS "Número de empleado" , EXTRACTVALUE(VALUE(T), '/emp/nombre') AS "Nombre de empleado" , EXTRACTVALUE(VALUE(T), '/emp/salario') AS "Salario de empleado" , FROM TABLE( (SELECT XMLSEQUENCE(EXTRACT(VALUE(P), '/empleados/emp'))FROM TXPlantilla P WHERE EXTRACTVALUE(VALUE(P),'/empleados/@departamento')='Finanzas' )) T;

El resultado sería:

Número de empleado ----------------------112 217 412

Nombre de empleado ----------------------Joe Jane Jack

Salario de empleado ---------------------50000 60000 40000

Por su parte, la función SYS_XMLGEN() toma un tipo nativo, un tipo abstracto o un tipo XMLType y genera con él un documento XML. Si es un tipo nativo, forma una etiqueta con el valor dentro, si es un tipo abstracto mapea los atributos del tipo abstracto a un documento XML y si es un XMLType engloba a este elemento en otro elemento de nombre por defecto ROW. Es posible indicar el nombre de la etiqueta principal del documento XML generado mediante la función XMLFormat(). Oracle /290

Supongamos el tipo de objeto CLIENTE_T, y una tabla asociada TRCliente donde se insertan varios registros: CREATE TYPE CLIENTE_T AS OBJECT ( cod INT, nombre VARCHAR2(150) ); / CREATE TABLE TRCliente OF CLIENTE_T (cod PRIMARY KEY); / INSERT INTO TRCliente VALUES(1, 'Roberto Pérez'); / INSERT INTO TRCliente VALUES(2, 'Marisa Rubén'); / INSERT INTO TRCliente VALUES(3, 'Adrián Martínez');

La siguiente consulta recupera todos los nombres de cliente como un documento XML: SELECT SYS_XMLGEN( nombre ) AS "Cliente" FROM TRCliente;

El resultado sería el siguiente (se muestran tres registros): Cliente ---------------------------------------------Roberto Pérez Marisa Rubén Adrián Martinez

Si ahora aplicamos la función sobre los registros como objetos:

SELECT SYS_XMLGEN( VALUE(T),XMLFORMAT('CLIENTE')) AS "Cliente" FROM TRCliente T;

Se obtiene (se muestran tres registros):

Cliente ---------------------------------------------1Roberto Pérez 2Marisa Rubén 3Adrián Martinez

La función XMLFormat()puede ser un parámetro de SYS_XMLGEN() y SYS_XMLAGG(). Esta función define las características del documento generado por estas dos funciones mediante sus atributos. Si queremos cambiar el formato del documento XML generado tan solo tendremos que darle el valor adecuado al correspondiente parámetro de XMLFormat(). Los parámetros más importantes son: • enclTag. Es el nombre de la etiqueta que engloba el documento. • vschemaType. Indica si el documento está validado por un esquema o no, sus valores válidos son NO_SCHEMA y USE_GIVEN_SCHEMA. • schemaName. Nombre del esquema. • targetNameSpace. Namespace del documento. • dburl. URL donde encontrar la definición de los esquemas, si no se declara se consideran relativos al documento. 2.8. Vistas «XMLType». Las vistas XMLType nos permiten tomar elementos relacionales u objeto-relacionales de la base de datos y sin modificar ni los datos ni su estructura poder mostrarlos como si documentos XML fuesen. En la forma habitual, se crea una vista indicando que es de tipo XMLType (OF XMLTYPE): -- Tabla relacional: CREATE TABLE TInquilino ( idNUMBER(4), fnombreVARCHAR2(20), lnombreVARCHAR2(20), alquiler DATE, precio NUMBER(6)); / -- Vista de XMLType: CREATE OR REPLACE VIEW VInquilino OF XMLTYPE WITH OBJECT ID (EXTRACT(sys_nc_rowinfo$,'/inquilino/@id').getNumberVal())AS SELECT XMLELEMENT( "inquilino",

Oracle /291

XMLAttributes(id), XMLForest(T.fnombre ||''|| T.lnombre AS "nombre", T.alquiler AS "fechaAlquilier")) AS "result" FROM TInquilino T WHERE precio> 20000; La cláusula OBJECT ID indica que id será el identificador único de cada elemento y que el tipo XMLType se almacenará en la columna sys_nc_rowinfo$. Y creamos la vista mediante las funciones SQLX y de Oracle

vistas en el apartado anterior. Si ahora insertamos datos en la tabla relacional y los consultamos mediante la vista: INSERT INTO TInquilino VALUES (2100, 'John', 'Smith', '24-05-2000', 30000); / INSERT INTO TInquilino VALUES (2200, 'Mary', 'Martin', '01-02-1996', 30000); / SELECT * FROM VInqulino;

Obtendremos:

SYS_NC_ROWINFO$ ----------------------------------------------------------- John Smith 24-05-2000

Mary Martin 01-02-1996

También es posible crear vistas XMLType mapeando los datos relacionales mediante un esquema y no con el comando SELECT de la definición de la vista. El esquema define el mapeado de cada elemento a la columna de datos mediante el atributo xdb:SQLName en el elemento del esquema, de tal manera que el elemento contendrá

el valor de la columna indicada en ese atributo. Usaremos los siguientes tipos de objetos:

CREATE OR REPLACE TYPE Departamento_t AS OBJECT ( deptno NUMBER(2), nombre VARCHAR2(14), lugar VARCHAR2(13)); / CREATE OR REPLACE TYPE Empleado_t AS OBJECT ( empno NUMBER(4), nombre VARCHAR2(10), trabajo VARCHAR2(9), salario NUMBER(7,2), departamento Departamento_t);

Y las siguientes tablas relacionales:

CREATE TABLE TRDepartamento ( deptno NUMBER(2) PRIMARY KEY, nombre VARCHAR2(14), lugar VARCHAR2(13)); / CREATE TABLE TREmpleado ( empno NUMBER(4) PRIMARY KEY, nombre VARCHAR2(10), trabajo VARCHAR2(9), salario NUMBER(7,2), deptno NUMBER(2) REFERENCES TRDepartamento(depno));

El esquema es el siguiente y en él podemos comprobar cómo cada columna de los tipos Empleado_t y Departamento_t es mapeada a un elemento del documento XML. La estructura del documento XML también es definida por el esquema:















A continuación podemos crear la vista de la siguiente manera:

CREATE OR REPLACE VIEW Empleado_xml OF XMLTYPE XMLSCHEMA "http://www.oracle.com/emp.xsd" ELEMENT "Empleado" WITH OBJECT ID (EXTRACTVALUE(sys_nc_rowinfo$, '/Empleado/EmpleadoId')) AS SELECT Empleado_t(e.empno, e.enombre, e.trabajo, e.salario, Departemento_t(d.deptno, d.nombre, d.lugar)) FROM TREmpleado e JOIN TRDepartamento d USING (deptno);

La consulta tendría como respuesta el siguiente documento XML:

2100 John 123003

2000 Sports San Francisco

Oracle /293

VI. PROCEDIMIENTOS DE GESTIÓN DE LA BASE DE DATOS. 1. Diccionario de datos de Oracle El diccionario de datos de Oracle almacena toda la información que es usada para controlar todos los objetos de una base de datos. Aunque el diccionario de datos es normalmente el dominio de los administradores de base de datos (DBA's), también es una fuente de información valiosa para programadores y usuarios finales. En este capítulo veremos el diccionario de datos desde la perspectiva de un usuario final. Con algunas excepciones, los nombres de los objetos en el diccionario de datos de Oracle comienzan con uno de los siguientes prefijos: USER_, ALL_ o DBA_. Los registros en la vistas USER_ muestran normalmente información acerca de los objetos propiedad de la cuenta que realiza la consulta. Los registros en las vistas ALL_ incluyen los registros USER_ más información acerca de los objetos sobre los que se posee el permiso PUBLIC. Las vistas DBA_ incluyen todos los objetos de base de datos, independientemente de su propietario. Existen vistas USER_TipoObjeto, ALL_TipoObjeto y DBA_TipoObjeto para la mayoría de objetos de base de datos. 1.1. Las vistas «DICTIONARY» (DICT) y «DICT_COLUMNS». Las descripciones de todos los objetos que constituyen el diccionario de datos son accesibles a través de una vista llamada DICTIONARY. Esta vista, también accesible con el sinónimo público DICT, consulta la base de datos para determinar qué vistas del diccionario de datos podemos ver. También muestra los sinónimos públicos para estas vistas. El siguiente ejemplo consulta DICT para obtener los nombres de todas las vistas del diccionario de datos cuyos nombres incluyen el término 'VIEWS'. Haciendo la selección desde una cuenta de no administrador se obtiene el siguiente resultado, donde se muestra el nombre y comentario de cada objeto. SELECT Table_Name, Comments FROM DICT WHERE Table_Name LIKE '%VIEWS%' ORDER BY Table_Name; TABLE_NAME ----------------------------------ALL_BASE_TABLE_MVIEWS ALL_MVIEWS ALL_REGISTERED_MVIEWS ALL_VIEWS ALL_XML_VIEWS USER_BASE_TABLE_MVIEWS USER_MVIEWS USER_REGISTERED_MVIEWS USER_VIEWS USER_XML_VIEWS

COMMENTS ------------------------------------------------------------------------------All materialized views with log(s) in the database that the user can see All materialized views in the database Remote materialized views of local tables that the user can see Description of views accessible to the user Description of all XMLType views that the user has privileges on All materialized views with log(s) owned by the user in the database All materialized views in the database Remote materialized views of local tables currently using logs owned by the user Description of the user's own views Description of the user's own XMLType views

Podemos consultar las columnas de las vistas del diccionario de datos mediante la vista DICT_COLUMNS. Como la vista DICTIONARY, DICT_COLUMNS muestra también comentarios. DICT_COLUMNS tiene tres columnas: Table_Name, Column_Name, y Comments. Consultando DICT_COLUMNS podremos determinar cuales vistas del diccionario de datos serán las más útiles para nuestras necesidades. Por ejemplo, si queremos ver el espacio asignado y usado por nuestros objetos de base de datos pero no estamos seguros de qué vista almacena esta información, podemos consultas DICT_COLUMNS, tal como se muestra en el siguiente ejemplo, el cual mira por todas las tablas del diccionario que tienen una columna llamada Blocks: SELECT Table_Name FROM DICT_COLUMNS WHERE Column_Name = 'BLOCKS' AND Table_Name LIKE 'USER%' ORDER BY Table_Name; TABLE_NAME

Oracle /294

-------------------------------------USER_ALL_TABLES USER_EXTENTS USER_FREE_SPACE USER_OBJECT_TABLES USER_SEGMENTS USER_TABLES USER_TAB_PARTITIONS USER_TAB_STATISTICS USER_TAB_SUBPARTITIONS USER_TS_QUOTAS

Para listar todos los nombres de columnas disponibles que podríamos haber usado en este último ejemplo, podemos consultar DICT_COLUMNS: SELECT DISTINCT Column_Name FROM DICT_COLUMNS ORDER BY Column_Name;

Siempre que no estemos seguros de dónde encontrar los datos que necesitamos, podemos verificar las vistas DICTIONARY y DICT_COLUMNS. Si parece que un número grande de vistas podrían ser útiles consultar DICTIONARY para ver los comentarios de cada vista. 1.2. Cosas que podemos seleccionar de: tablas (y columnas), vistas, sinónimos y secuencias. Un catálogo de usuario lista todos aquellos objetos de los cuales el usuario puede seleccionar registros; esto es, cualquier objeto que puede ser listado en la cláusula FROM de una consulta. Aunque las secuencias no pueden ser referenciadas directamente en la cláusula FROM, Oracle las incluye en el catálogo. En esta sección veremos cómo recuperar información sobre tablas, columnas, vistas, sinónimos, secuencias y el catálogo de usuario. 1.2.1. Catálogo: USER_CATALOG (CAT) Consultando la vista USER_CATALOG (o con su sinónimo CAT) podemos ver todas las tablas, vistas, sinónimos y secuencias propiedad del usuario. La columna Table_Name muestra el nombre del objeto (aunque no sea una tabla), y la columna Table_Type muestra el tipo del objeto: SELECT Table_Name, Table_Type FROM USER_CATALOG WHERE Table_Name LIKE 'T%';

Hay dos catálogos adicionales disponibles. La vista ALL_CATALOG muestra lo mismo que USER_CATALOG más cualquier objeto sobre el que tengamos el permiso PUBLIC. La vista DBA_CATALOG muestra todos los objetos del catálogo. Además de las columnas Table_Name y Table_Type mostradas en la consulta de USER_CATALOG, las vistas ALL_CATALOG y DBA_CATALOG incluyen la columna Owner, que indica el propietario del objeto. 1.2.2. Objetos: USER_OBJECTS (OBJ) La vista USER_CATALOG sólo muestra información sobre tablas, vistas, secuencias y sinónimos. Para recuperar información sobre todos los tipos de objetos podemos consultar la vista USER_OBJECTS. Podemos usar esta vista para encontrar cualquier cosa sobre muchos tipos de objetos, incluidos clústeres, enlaces de base de datos, directorios, funciones, índices, librerías, paquetes, cuerpos de paquete, clases Java, tipos de datos abstractos, recursos, secuencias, sinónimos, tablas, triggers, vistas materializadas, LOB's y vistas. Las columnas de la vista USER_OBJECTS se describen en la siguiente tabla. Nombre col. Object_Name SubObject_Name Object_ID Data_Object_ID Object_Type Created Last_DDL_Time Timestamp Status

Descripción El nombre del objeto. El nombre del sub-objeto, como un nombre de partición. Un identificador único de Oracle para el objeto. El número de objeto del segmento que contiene los datos del objeto. El tipo de objeto (TABLE, INDEX, TABLE PARTITION, ...). La fecha y hora de creación del objeto (como una columna DATE). La fecha y hora en que se usó el último comando DDL sobre el objeto, incluidos ALTER, GRANT y REVOKE. La fecha y hora de creación del objeto (como Created, pero almacenado como una columna de caracteres). El estado del objeto (VALID o INVALID). Oracle /295

Temporary Generated Secondary

Un indicador de si el objeto es una tabla temporal. Un indicador de si el nombre del objeto fue generado por el sistema. Un indicador de si el objeto es un índice secundario creado por un índice de dominio.

(o su sinónimo OBJ) contiene varias piezas vitales de información que no se encuentran en otras vistas del diccionario de datos. Registra las fechas de creación de los objetos (la columna Created) y la última vez que un objeto fue alterado (la columna Last_DDL_Time). Estas columnas son útiles cuando intentamos reconciliar diferentes conjuntos de objetos en la misma aplicación. USER_OBJECTS

Nota. Si recreamos objetos de alguna manera (por ejemplo, usando la utilidad la columna Created cambiará a la última vez en que fueron creados.

Import),

su valor para

Análogamente disponemos de las vistas ALL_OBJECTS y DBA_OBJECTS. Estas vistas añaden la columna Owner. 1.2.3. Tablas: USER_TABLES (TABS) Aunque todos los objetos de usuario son mostrados en la vista USER_OBJECTS, pocos atributos de estos objetos se muestran aquí. Para obtener más información sobre un objeto necesitamos mirar la vista que es específica para el tipo de objeto. Para las tablas, se usa la vista USER_TABLES (o por su sinónimo TABS). Nota. Versiones previas de Oracle incluyen una vista llamada TAB. Esta vista, la cual es similar a TABS, se sigue soportando porque es usada por algunos productos de Oracle. Sin embargo, TAB no contiene las mismas columnas que TABS. Hay que usar TABS en nuestras consultas al diccionario de datos. Las columnas de USER_TABLES pueden dividirse en cuatro categorías (identificación, relativas al espacio, relativas a estadísticas, y otras), tal como se muestran en la siguiente tabla. Columnas de identificación Table_Name Backed_Up Partitioned IOT_Name

Nombre de la tabla. Indica si la tabla fue respaldada desde la última modificación. Indica si la tabla fue particionada (YES o NO). Nombre de la tabla de sólo índice, si la hay, a la cual el desbordamiento o las entradas de mapeo pertenecen. Atributo de logging. Logging IOT_Type Si es una tabla de solo índice, entonces es IOT o IOT_OVERFLOW o IOT_MAPPING, sino es NULL. Indica si la sesión actual puede ver solo los datos puesto en este mismo objeto. Temporary Indica si es una tabla anidada. Nested Indica si la tabla fue creada como parte de la creación de índices de dominio. Secondary Columnas relativas al espacio Nombre del tablespace que contiene la tabla. Tablespace_Name Nombre del clúster, si lo hay, en el cual la tabla está contenido. Cluster_Name Mínimo porcentaje de espacio libre en un bloque. Pct_Free Mínimo porcentaje de espacio usado en un bloque. Pct_Used Número inicial de transacciones. Ini_Trans Máximo número de transacciones. Max_Trans Tamaño de la extensión inicial en bytes. Initial_Extent Tamaño de la extensión secundaria en bytes. Next_Extent Mínimo número de extensiones permitidas en el segmento. Min_Extents Máximo número de extensiones permitidas en el segmento. Max_Extents Porcentaje de incremento en el tamaño de las extensiones. Pct_Increase Número de procesos libres asignados en este segmento. Freelists Número de grupos libres asignados en este segmento. Freelist_Groups Columnas relativas a estadísticas El número de filas en la tabla. Num_Rows El número de bloques usados en la tabla. Blocks El número de bloques vacíos (nunca usados) de la tabla. Empty_Blocks El promedio de espacio libre disponible en la tabla. Avg_Space El número de filas encadenadas en la tabla. Chain_Cnt Oracle /296

Avg_Row_Len Sample_Size Last_Analyzed Avg_Space_Freelist_Blocks Num_Freelist_Blocks Global_Stats User_Stats

El promedio de longitud de filas, incluidas las filas de desbordamiento. El tamaño de la muestra usada en los análisis de la tabla. La fecha y la hora más reciente en que la tabla fue analizada. El promedio de espacio libre de todos los bloques de una lista libre. El número de bloques de una lista libre. Indica si las estadísticas se calculan sin combinar particiones subyacentes. Indica si la estadística fue entrada directamente por el usuario.

Otras columnas Degree Instances Cache Table_Lock Buffer_Pool Row_Movement Duration Skip_Corrupt Monitoring Cluster_Owner Dependencies Dropped Compression

El número de hilos por instancia para consultas paralelas sobre la tabla. El número de instancias para consultas paralelas sobre la tabla. Indica si la tabla ha sido almacenada en una caché. Indica si el bloqueo de tabla está habilitado o deshabilitado. El búfer por defecto que será usado por los bloques de la tabla. Indica si el movimiento de fila particionada está permitido o no. Si es una tabla temporal, entonces es SYS$SESSION o SYS$TRANSACTION, sino es NULL. Indica si está habilitado o no saltarse bloques corruptos. Indica si deben registrarse la cantidad de modificaciones. Propietario del clúster, si lo hay, al cual pertenece la tabla. Indica si deberíamos nosotros guardar la pista de dependencias del nivel de fila. Indica si la tabla fue enviada al reciclador (YES) o no (NO). Indica si la tabla está comprimida (YES) o no (NO).

El nombre de la tabla se muestra en la columna Table_Name. La columna Backed_Up muestra si la tabla ha sido o no respaldada desde su última modificación. La columna Partitioned tendrá el valor 'YES' si la tabla ha sido particionada. Las columnas relativas a estadísticas como Num_Rows, Blocks, Empty_Blocks, Avg_Row_Len y Last_Analyzed, son pobladas cuando la tabla es analizada. Nota. Desde Oracle Database 10g, podemos usar la vista USER_TAB_STATISTICS para acceder a las estadísticas de nuestras tablas. La siguiente consulta lista todas las tablas cuyos nombres empiezan con la letra 'L': SELECT Table_Name FROM USER_TABLES WHERE Table_Name LIKE 'L%'; vista ALL_TABLES muestra todas las

La tablas propiedad del usuario así como cualquier otra sobre la que tenga permisos de acceso. La vista DBA_TABLES lista todas las tablas de la base de datos. Estas dos vistas incluyen la columna Owner con el nombre del propietario de la tabla. Nota. Para tablas externas, usar las vistas USER_EXTERNAL_TABLES y USER_EXTERNAL_LOCATIONS. 1.2.4. Columnas: USER_TAB_COLUMNS (COLS) Aunque los usuarios no consulten por columnas, la vista del diccionario de datos que muestra columnas está estrechamente ligada a la vista de diccionario de datos de tablas. Esta vista, llamada USER_TAB_COLUMNS, muestra información específica de las columnas. USER_TAB_COLUMNS también puede ser consultada mediante el sinónimo público COLS. Las columnas que podemos consultar de USER_TAB_COLUMNS pueden ser separadas en tres categorías: • Identificación, como Table_Name, Column_Name, y Column_ID. • Relativas a la definición, como Data_Type, Data_Length, Data_Precision, Data_Scale, Nullable, y Default_Length

• Relativas a estadísticas, como Num_Distinct, Low_Value, High_Value, Density, Num_Nulls, y otras. Las columnas Table_Name y Column_Name contienen los nombres de nuestras tablas y columnas. Las columnas relativas a estadísticas son pobladas cuando la tabla es analizada. Las columnas para estadísticas son también proporcionadas en la vista USER_TAB_COL_STATISTICS. Para ver las definiciones de columnas de una tabla podemos consultar USER_TAB_COLUMNS, especificando Table_Name en la cláusula WHERE: SELECT Column_Name, Data_Type FROM USER_TAB_COLUMNS

Oracle /297

WHERE Table_Name = 'MI_TABLA'; COLUMN_NAME --------------------CAMPO1 CAMPO2 CAMPO3

DATA_TYPE ----------------VARCHAR2 CHAR NUMBER

Esta misma información también se puede obtener mediante SQL*Plus con el comando DESCRIBE; sin embargo, DESCRIBE no nos da la opción de ver los valores por defecto de las columnas y las estadísticas. La vista ALL_TAB_COLUMNS muestra las columnas de todas las tablas y vistas propiedad del usuario así como cualquier otra sobre los que tenga permisos de acceso. DBA_TAB_COLUMNS muestra las columnas de todas las tablas y vistas de la base de datos. Ambas vistas añaden la columna Owner indicando el propietario de las tablas. Columnas de estadísticas. Muchas de las columnas de estadísticas están disponibles tanto en USER_TAB_COLUMNS como en USER_TAB_COL_STATISTICS. Las columnas disponibles en USER_TAB_COL_STATISTICS son las mismas de USER_TAB_COLUMNS más Table_Name y Column_Name. USER_TAB_COL_STATISTICS contiene columnas de estadísticas que también están en USER_TAB_COLUMNS para compatibilidad. Deberíamos acceder a ellos usando USER_TAB_COL_STATISTICS. Histogramas de valores de columna. Podemos usar histogramas para mejorar el análisis usado por el optimizador. La vista USER_TAB_HISTOGRAMS contiene información acerca de cada histograma de columna: Table_Name, Column_Name, Endpoint_Number, Endpoint_Value, y Endpoint_Actual_Value. Los valores en USER_TAB_HISTOGRAMS son usados por el optimizador para determinar la distribución de los valores de columna dentro de la tabla. También están disponibles las vistas ALL_TAB_HISTOGRAMS y DBA_TAB_HISTOGRAMS. Columnas actualizables. Podemos actualizar registros en vistas que contengan un JOIN en su consulta, a condición de que el JOIN cumpla ciertos criterios. La vista USER_UPDATABLE_COLUMNS muestra todas las columnas que podemos actualizar. Para una columna determinada podemos consultar Owner, Table_Name, y Column_Name; la columna Updatable tendrá el valor 'YES' si la columna puede ser actualizada, y el valor 'NO' si la columna no puede ser actualizada. Podemos también consultar las columnas Insertable y Deletable para saber si podemos insertar o borrar registros mediante la vista. 1.2.5. Vistas: USER_VIEWS La consulta subyacente de una vista es accesible mediante la vista del diccionario de datos USER_VIEWS, la cual contiene 10 columnas. Las 3 columnas principales son: View_Name Text_Length Text

El nombre de la vista La longitud de la consulta base, en caracteres. La consulta que usa la vista.

La columna Text es de tipo LONG. Esto puede causar problemas cuando consultemos USER_VIEWS mediante SQL*Plus, porque SQL*Plus trunca los LONG. Sin embargo, el punto en el que ocurre el truncamiento puede ser cambiado mediante el comando SET LONG. USER_VIEWS proporciona un mecanismo para determinar el valor apropiado para el punto de truncamiento, tal como se ve en el siguiente ejemplo. La columna Text_Length muestra la longitud de la consulta subyacente. Por lo tanto, el punto de truncamiento LONG en SQL*Plus debe asignarse a un valor mayor o igual que el valor de esta columna. Por ejemplo, lo siguiente muestra una vista llamada Mi_Vista y cuya longitud de texto es de 355: SELECT View_Name, Text_Length FROM USER_VIEWS WHERE View_Name = 'MI_VISTA';

VIEW_NAME ---------------------MI_VISTA

TEXT_LENGTH --------------------355

Ya que la longitud del texto de la vista es de 355 caracteres, podemos usar el comando incrementar el punto de truncamiento (que por defecto es 80): SET LONG 355

Oracle /298

SET LONG

para

Entonces podemos consultar USER_VIEWS para ver el texto de la vista: SELECT Text FROM USER_VIEWS WHERE View_Name = 'MI_VISTA';

Nota. Podemos consultar la definición de columnas de una vista con misma vista del diccionario de datos para consultar por tablas.

USER_TAB_COLUMNS,

la

Si usamos alias para las columnas de nuestra vista, y los alias de columna son parte de la consulta de la vista, entonces las consultas en el diccionario de datos sobre la vista se simplificarán. Ya que se muestra el texto entero de la consulta de la vista con USER_VIEWS, los alias de columna se mostrarán. Podemos crear vistas usando este formato: CREATE VIEW Cliente_View (Codigo, Nombre) AS SELECT Id, Apellidos || ', ' || Nombre FROM Cliente;

Indicando los nombres de columnas en la cabecera del comando CREATE VIEW se previene que una consulta con USER_VIEWS muestre los alias de las columnas de nuestra vista. El único medo de ver los nombres de las columnas de nuestra vista es consultando USER_TAB_COLUMNS. Para soportar vistas de objetos (véase el capítulo "Soporte de objetos y XML"), USER_VIEWS contiene las siguientes columnas: Type_Text Type_Text_Length OID_Text OID_Text_Length View_Type_Owner View_Type

Tipo de cláusula de la vista tipada. Longitud del tipo de cláusula de la vista tipada. Cláusula WITH OID de la vista tipada. Longitud de la cláusula WITH OID de la vista tipada. Propietario del tipo de la vista para la vista tipada Tipo para la vista

Análogamente también se dispone de las vistas ALL_VIEWS y DBA_VIEWS. 1.2.6. Sinónimos: USER_SYNONYMS (SYN) La vista USER_SYNONYMS (o SYN) muestra todos los sinónimos de los que somos propietarios. Las columnas que incluye son: Synonym_Name Table_Owner Table_Name DB_Link

El nombre del sinónimo. El propietario de la tabla a la que se refiere el sinónimo. El nombre de la tabla a la que se refiere el sinónimo. El nombre del enlace de base de datos usado en el sinónimo.

La vista USER_SYNONYMS es usada depurando programas o resolviendo problemas con accesos de usuario a objetos dentro de aplicaciones. La columna DB_Link será NULL si el sinónimo no usa un enlace de base de datos. Por lo tanto, si queremos ver una lista de enlaces de base de datos actualmente usados por sinónimos de nuestra cuenta, podemos ejecutar la siguiente consulta: SELECT DISTINCT DB_Link FROM USER_SYNONYMS WHERE DB_Link IS NOT NULL;

Análogamente también se dispone de las vistas ALL_SYNONYMS y DBA_SYNONYMS. 1.2.7. Secuencias: USER_SEQUENCES (SEQ) Para mostrar los atributos de secuencias podemos consultar la vista USER_SEQUENCES (o su sinónimo SEQ). Las columnas de USER_SEQUENCES se describen a continuación: Nombre col. Sequence_Name Min_Value Max_Value Increment_By Cycle_Flag Order_Flag Cache_Size Last_Number

Descripción Nombre de la secuencia. Mínimo valor de la secuencia. Máximo valor de la secuencia Incremento entre los valores de la secuencia. Un indicador de si la secuencia es cíclica. Un indicador de si los números de la secuencia son generados en orden. Número de entradas de secuencia en caché. El último número de secuencia escrito a disco o en caché.

Oracle /299

La columna Last_Number no es actualizada durante operaciones normales de la base de datos; se usa durante operaciones de reinicio/recuperación de la base de datos. Análogamente también se dispone de las vistas ALL_SEQUENCES y DBA_SEQUENCES. 1.3. Papelera: USER_RECYCLEBIN y DBA_RECYCLEBIN Desde Oracle Database 10g podemos usar el comando FLASHBACK TABLE para recuperar tablas y objetos dependientes que han sido borrados. Para ver los objetos presentes en nuestra papelera podemos consultar la vista USER_RECYCLEBIN (o RECYCLEBIN). Nos mostrará el nombre del objeto original, si puede ser recuperado (columna Can_Undrop) y su fue borrado porque estaba relacionado con un objeto borrado explícitamente (columna Base_Object). Los objetos permanecen en el portapapeles hasta que son purgados. Los objetos pueden ser removidos de la papelera automáticamente sin no hay espacio libre en el tablespace donde fueron borrados. Los DBA's pueden ver todos los objetos de todas las papeleras mediante la vista DBA_RECYCLEBIN. No hay una versión ALL_ para esta vista. 1.4. Restricciones y comentarios. Las restricciones y comentarios ayudan a comprender cómo las tablas y columnas se relacionan entre sí. Los comentarios son estrictamente información; no imponen ninguna condición sobre los datos almacenados en los objetos que describen. Las restricciones, por otra parte, definen las condiciones bajo las cuales los datos son válidos. Restricciones habituales incluyen NOT NULL, UNIQUE, PRIMARY KEY y FOREIGN KEY. 1.4.1. Restricciones: USER_CONSTRAINTS La información de restricciones es accesible mediante la vista USER_CONSTRAINTS. Esta información es muy útil cuando intentamos modificar las restricciones sobre datos o resolver problemas con los datos de la aplicación. Las columnas de esta vista son las siguientes: Nombre col.

Descripción El propietario de la restricción. Owner El nombre de la restricción. Constraint_Name El tipo de la restricción: Constraint_Type 'C' para restricción CHECK; incluye NOT NULL 'P' para restricción PRIMARY KEY 'R' para restricción FOREIGN KEY 'U' para restricción UNIQUE 'V' para restricción WITH CHECK OPTION (en vistas) 'O' para restricción WITH READ ONLY (en vistas) El nombre de la tabla asociada con la restricción. Table_Name Search_Condition La condición de búsqueda usada (para restricciones CHECK) R_Owner El propietario de la tabla referenciada por la restricción FOREIGN KEY. R_Constraint_Name El nombre de la restricción referenciada por una restricción FOREIGN KEY. Delete_Rule La acción a tomar sobre tablas FOREIGN KEY cuando un registro PRIMARY KEY es borrado (CASCADE o NO ACTION). Status El estado de la restricción (ENABLED o DISABLED). Un indicador de si la restricción puede ser diferida. Deferrable Un indicador de si la restricción fue inicialmente diferida. Deferred Validated Un indicador (VALIDATED o NOT VALIDATED) de si todos los datos cumplen la restricción. Un indicador de si el nombre de restricción ha sido generado por la base de datos. Generated Un indicador de si un dato que fue usado en la creación de la restricción sin especificar Bad un valor de centuria para restricciones CHECK es un año ambiguo de dos dígitos; se aplica sólo a restricciones en bases de datos actualizadas desde versiones previas. Un indicador de si la restricción está forzada o no. Rely La fecha y hora en la que la restricción fue activada o desactivada. Last_Change Propietario de índice relacionado. Index_Owner Nombre del índice relacionado. Index_Name Indicador de valido o no válido. Invalid View_Related Indicador Yes/No para registrar si la restricción es relativa a una vista.

Aunque es una vista

USER_

contiene una columna

Owner.

En esta vista,

Oracle /300

Owner

hace referencia al propietario

de la restricción, y no al propietario de la tabla. Las restricciones FOREIGN KEY tienen siempre un valor para las columnas R_Owner y R_Constraint_Name. Estas dos columnas indican las restricciones referenciadas. Una FOREIGN KEY referencia otra restricción, no otra columna. Las restricciones NOT NULL sobre columnas son almacenadas en restricciones CHECK, así que tiene el tipo 'C'. Consultado USER_CONSTRAINTS obtendremos los nombres de todas las restricciones sobre una tabla. Esto es importante cuando intentamos interpretar mensajes de error que sólo proporcionan el nombre de la restricción que fue violada. Una vez que conocemos el nombre y tipo de la restricción, podemos verificar las columnas asociadas mediante la vista USER_CONS_COLUMNS, descrita en la siguiente sección. Si no hemos asignado nombre a una restricción cuando la hemos creado, Oracle genera un nombre único. Si la restricción tiene un nombre generado por el sistema, esto se indicará en la columna Generated. Si deferimos una restricción (como se indica en la columna Deferred, la restricción no será forzada durante una transacción. Por ejemplo, si hemos realizado actualizaciones masivas sobre tablas relacionadas y no podemos garantizar el orden de las transacciones, podemos decidir diferir la verificación de la restricción sobre las tablas hasta que la actualización se ha completado. Análogamente también se dispone de las vistas ALL_CONSTRAINTS y DBA_CONSTRAINTS. 1.4.2. Restricción de columnas: USER_CONS_COLUMNS Podemos ver las columnas asociadas con restricciones mediante la vista USER_CONS_COLUMNS. Si hemos consultado USER_CONSTRAINTS para obtener los tipos y nombres de las restricciones involucradas, podemos usar USER_CONS_COLUMNS para determinar qué columnas están involucradas con la restricción. Las columnas de esta vista son las siguientes: Nombre col. Owner Constraint_Name Table_Name Column_Name Position

Descripción El propietario de la restricción. El nombre de la restricción. El nombre de la tabla asociada con la restricción. El nombre de la columna asociada con la restricción. El orden de la columna dentro de la definición de la restricción.

Hay sólo dos columnas de USER_CONS_COLUMNS que no están en Position. Una simple consulta de esta tabla se muestra a continuación:

USER_CONSTRAINTS: Column_Name

y

SELECT Column_Name, Position FROM USER_CONS_COLUMNS WHERE Constraint_Name = 'SYS_C0008791';

COLUMN_NAME ---------------------NOMBRE APELLIDOS

POSITION --------------------1 2

Como se ve en el resultado, la combinación del nombre y apellidos forman la restricción (en este caso, una clave primaria). La columna Position es significativa. Cuando creamos una restricción UNIQUE o PRIMARY KEY, Oracle crea automáticamente un índice único sobre el conjunto de columnas que especificamos. El índice es creado según el orden de las columnas. El orden de las columnas afecta al rendimiento del índice. Un índice sobre varias columnas puede ser más eficiente si la primera columna del índice ( Position=1) es usada en la cláusula WHERE de consultas. Análogamente también se dispone de las vistas ALL_CONS_COLUMNS y DBA_CONS_COLUMNS. 1.4.3. Excepciones en restricciones: EXCEPTIONS Cuando activamos restricciones sobre tablas que ya contienen datos, podemos encontrarnos con violaciones con los datos existentes. Por ejemplo, podemos intentar crear una restricción PRIMARY KEY sobre una columna que contiene el mismo valor en varios registros, pero tal tentativa fallaría debido a violaciones de unicidad. Podemos capturar información acerca de las filas que cusan fallos en la creación de la restricción. Primero, crearemos una tabla llamada EXCEPTIONS en nuestro esquema; el script SQL que debemos usar para crear eta tabla se denomina utlexcpt.sql, y normalmente esta localizado en el directorio /rdbms/admin dentro de la carpeta de instalación de Oracle. Oracle /301

Archivo «utlexcpt.sql» rem rem $Header: utlexcpt.sql,v 1.1 1992/10/20 11:57:02 GLUMPKIN Stab $ rem Rem Copyright (c) 1991 by Oracle Corporation Rem NAME Rem except.sql - Rem DESCRIPTION Rem Rem RETURNS Rem Rem NOTES Rem Rem MODIFIED (MM/DD/YY) Rem glumpkin 10/20/92 - Renamed from EXCEPT.SQL Rem epeeler 07/22/91 - add comma Rem epeeler 04/30/91 - Creation create table exceptions(row_id rowid, owner varchar2(30), table_name varchar2(30), constraint varchar2(30));

La tabla EXCEPTIONS contiene cuatro columnas: Row_ID (el ROWID de cada fila que viola la restricción), Owner (el propietario de la restricción violada), Table_Name (la tabla en la cual fue creada la restricción violada), y Constraint (la restricción violada por la fila). Después de crear la tabla EXCEPTIONS, intentaremos habilitar la restricción PRIMARY KEY sobre una tabla REVISTA: ALTER TABLE REVISTAENABLE PRIMARY KEY EXCEPTIONS INTO EXCEPTIONS; ORA-02437: cannot enable (NEWSPAPER.SYS_C00516) - primary key violated

La creación de la restricción falla, y una referencia a todas las filas que violan la restricción es puesta en la tabla EXCEPTIONS. Por ejemplo, si la restricción PRIMARY KEY de este último ejemplo genera una excepción, entonces podemos consultar la tabla EXCEPTIONS tal como se muestra a continuación: SELECT Owner, Table_Name, Constraint FROM EXCEPTIONS; OWNER ------------------EMPLEADO EMPLEADO

TABLE_NAME --------------------REVISTA REVISTA

CONSTRAINT -------------------SYS_C00516 SYS_C00516

Dos filas violan la restricción llamadaSYS_C00516 (el cual, en este ejemplo, es el nombre de la restricción PRIMARY KEY para la tabla REVISTA). Podemos determinar qué filas de la tabla REVISTA se corresponden a estas excepciones combinando la columna Row_ID de la tabla EXCEPTIONS con la pseudo-columna ROWID de la tabla sobre la cual se aplicó la restricción: SELECT * FROM REVISTA WHERE RowID IN (SELECT Row_ID FROM EXCEPTIONS);

Nota. Operaciones de flashback y recuperación pueden cambiar los ROWID de las filas previamente insertadas dentro de las tablas. 1.4.4. Comentarios de tablas: USER_TAB_COMMENTS Podemos añadir un comentario a una tabla, vista o columna después de haber sido creadas. Podemos mostrar estos comentarios mediante las vistas del diccionario de datos DICTIONARY y DICT_COLUMNS. Para mostrar comentarios sobre nuestras propias tablas se usa la vista USER_TAB_COMMENTS. La vista USER_TAB_COMMENTS contiene tres columnas: Table_Name Table_Type Comments

El nombre de la tabla o vista. El tipo de objeto (TABLE, OBJECT TABLE, o VIEW). Comentarios asociados al objeto.

Para añadir comentarios a una tabla se usa el comando COMMENT, tal como se muestra a continuación: COMMENT ON TABLE Festivos IS 'Fechas con los días festivos de la empresa'; Consultando la vista USER_TAB_COMMENTS especificando Table_Name podemos

Oracle /302

ver los comentarios, tal

como se muestra a continuación:

SELECT Comments FROM USER_TAB_COMMENTS WHERE Table_Name = 'Festivos'; COMMENTS --------------------------------------------------------Fechas con los días festivos de la empresa

Para borrar un comentario se debe asignar a un string vacío: COMMENT ON TABLE Festivos IS '';

Podemos ver los comentarios de todas las tablas mediante la vista ALL_TAB_COMMENTS. Esta vista tiene una columna adicional, Owner, que especifica el propietario de la tabla. La vista DBA_TAB_COMMENTS muestra todas tablas de la base de datos. 1.4.5. Comentarios de columnas: USER_COL_COMMENTS La vista USER_COL_COMMENTS muestra los comentarios asociados con las columnas de nuestras tablas. Estos comentarios son añadidos a la base de datos mediante el comando COMMENT. La vista USER_COL_COMMENTS contiene tres columnas: Table_Name Column_Name Comments

El nombre de la tabla o vista. El nombre de la columna. Comentarios asociados ala columna.

Para añadir un comentario a una columna se usa el comando COMMENT, tal como se muestra a continuación: COMMENT ON COLUMN Festivos.Dia IS 'Día correspondiente al festivo'; Table_Name y Column_Name

Consultado USER_COL_COMMENTS y especificando para una columnas.

podemos ver los comentarios

SELECT Comments FROM USER_COL_COMMENTS WHERE Table_Name = 'Festivos' AND Column_Name = 'Dia';

Para borrar un comentario hay que asignar un string vacío:

COMMENT ON COLUMN Festivos.Dia IS ''; También disponemos de las vistas análogas ALL_COL_COMMENTS y DBA_COL_COMMENTS.

1.5. Índices y clústeres. Los índices y clústeres no cambian los datos almacenados en las tablas; sin embargo, cambian el modo en que los datos son accedidos y almacenados. 1.5.1. Índices: USER_INDEXES (IND) En Oracle, los índices están muy relacionados con las restricciones. Las restricciones PRIMARY KEY y UNIQUE siempre tienen asociados índices de unicidad. Hay dos vistas del diccionario de datos para consultar información sobre los índices: USER_INDEXES(o IND) y USER_IND_COLUMNS. Las columnas de USER_INDEXES pueden ser agrupadas en cuatro categorías, tal como se muestra en la siguiente tabla. Columnas de identificación Indexe_Name Table_Owner Table_Name Table_Type Uniqueness Status Partitioned Index_Type Temporary Generated Logging Compression Prefix_Length Secondary

Nombre del índice. Propietario de la tabla asociada con el índice. Nombre de la tabla asociada con el índice.

Oracle /303

Ityp_Owner Ityp_Name

Columnas relativas al espacio Tablespace_Name Ini_Trans Max_Trans Initial_Extent Next_Extent Min_Extents Max_Extents Pct_Increase Pct_Free Freelists Freelist_Groups Pct_Threshold

Columnas relativas a estadísticas Blevel Leaf_Blocks Distinct_Keys Avg_Leaf_Blocks_Per_Key Avg_Data_Blocks_Per_Key Clustering_Factor Num_Rows Sample_Size Last_Analyzed User_Stats Global_Stats

Otras columnas Degree Instances Include_Column Buffer_Pool Duration Pct_Direct_Access Parameters Domidx_Status Domidx_Opstatus Funcidx_Status Join_Index IOT_Redundant_Pkey_Elim Dropped

El nombre del índice se muestra en la columna Index_Name. El propietario y nombre de la tabla están en las columnas Table_Owner y Table_Name. La columna Uniqueness se asignará a UNIQUE para índices de unicidad y a NONUNIQUE para otros índices. La columna Table_Type registra si el índice es sobre una tabla ( 'TABLE') o clúster ('CLUSTER'). La columna Dropped, disponible desde Oracle Database 10g, identifica índices que están en la papelera de reciclaje. Nota. Desde Oracle Database 10g, podemos usar la vista USER_IND_STATISTICS para acceder a las estadísticas de nuestros índices. Para ver todos los índices de una tabla hay que consultar USER_INDEXES usando las columnas Table_Owner y Table_Name en la cláusula WHERE, tal como se muestra a continuación: SELECT Index_Name, Uniqueness FROM USER_INDEXES WHERE Table_Owner = 'Empleado' AND Table_Name = 'Festivos'; INDEX_NAME -------------------

UNIQUENES --------------------

Oracle /304

PK_FESTIVOS

UNIQUE

La columna Clustering_Factor no está directamente relacionada con los clústeres, sino que representa el grado en el cual las filas de la tabla están ordenadas. Cuando más ordenadas estén las filas, más eficientes serán las consultas de rango (las consultas de rango son aquellas en las cuales se da un rango de valores para una columna). Para descubrir qué columnas son parte de los índices, y su orden dentro del índice, necesitamos consultar la vista USER_IND_COLUMNS. También se dispone de las vistas ALL_INDEXES y DBA_INDEXES. Nota. Para índices basados en funciones, ver la vista USER_IND_EXPRESSIONS. 1.5.2. Columnas de índices: USER_IND_COLUMNS Podemos determinar qué columnas están en un índice consultando la vista columnas disponibles de esta vista son las siguientes. Index_Name Table_Name Column_Name Column_Position Column_Length Char_Length Descend

USER_IND_COLUMNS.

Las

El nombre del índice. El nombre de la tabla del índice. El nombre de la columna dentro del índice. La posición de la columna en el índice. La longitud en el índice de la columna. Longitud máxima del código de la columna (Unicode) Un indicador Y/N que indica si la columna está ordenada en orden descendente.

Cinco columnas de esta vista no están en USER_INDEXES: Column_Name, Column_Position, Column_Length, Char_Length, y Descend. La columna Column_Length, como las columnas relativas a estadísticas en USER_INDEXES, se puebla cuando la tabla base del índice es analizada. A continuación se muestra una simple consulta sobre esta tabla, usando el Index_Name de USER_INDEXES (en este ejemplo, la columna Column_Position es referenciada con el alias Pos): SELECT Column_Name, Column_Position Pos FROM USER_IND_COLUMNS WHERE Index_Name = 'PK_FESTIVOS'; COLUMN_NAME --------------------MES DIA

POS ---------1 2

También se dispone de las vistas ALL_IND_COLUMNS y DBA_IND_COLUMNS. 1.5.3. Columnas de índices Bitmap Join: USER_JOIN_IND_COLUMNS Si hemos creado un índice Bitmap Join, podemos consultar la vista USER_JOIN_IND_COLUMNS para obtener los detalles de join. La vista USER_JOIN_IND_COLUMNS registra los nombres de las tablas involucradas en el Join, la columna INNER y OUTER JOIN, y la dimensión y tablas involucradas. 1.5.4. Clústeres: USER_CLUSTERS (CLU) Los parámetros de almacenamiento y estadísticos asociados con clústeres son accesibles mediante la vista USER_CLUSTERS (también conocida por su sinónimo CLU). Las columnas de esta vista se muestran en la siguiente tabla separadas por tipos. Columnas de identificación Cluster_Name Cluster_Type Function

Columnas relativas al espacio Tablespace_Name Pct_Free Pct_Used Key_Size Ini_Trans Max_Trans Initial_Extent Next_Extent

Oracle /305

Min_Extents Max_Extents Pct_Increase Freelists Freelist_Groups

Columnas relativas a estadísticas Avg_Blocks_Per_Key Hashkeys

Otras columnas Degree Instances Cache Buffer_Pool Single_Table Dependencies

La columna Cluster_Name contiene el nombre del clúster. Cluster_Type especifica si el clúster usa un índice estándar B*-tree o una función de hashing para el clúster. 1.5.5. Columnas de clúster: USER_CLU_COLUMNS Para ver el mapeado de columnas de tabla a columnas de clúster, hay que consultar la vista USER_CLU_COLUMNS, cuyas columnas son las siguientes: Cluster_Name Clu_Column_Name Table_Name Tab_Column_Name

El nombre del clúster. El nombre de la columna clave en el clúster. El nombre de la tabla dentro del clúster. El nombre de la columna clave en la tabla.

Desde un único clúster podemos almacenar datos para varias tablas, USER_CLU_COLUMNS es útil para qué columnas de qué tablas mapean las columnas del clúster. No hay versión ALL_ para esta vista, pero sí existe la vista DBA_CLU_COLUMNS. 1.6. Tipos de datos abstractos, estructuras ORDBMS y LOB's. En esta sección veremos las vistas del diccionario de datos asociadas con las estructuras objeto-relacional de Oracle, como los tipos de datos abstractos, métodos y objetos grandes (LOB's). Existen versiones USER_, ALL_ y DBA_ de estas vistas. 1.6.1. Tipos de datos abstractos: USER_TYPES Los tipos de datos abstractos creados dentro de nuestro esquema pueden ser listados usando la vista USER_TYPES, la cual incluye columnas para el nombre del tipo (Type_Name), número de atributos (Attributes) y número de métodos (Methods) definidos por el tipo de dato. Por ejemplo, el tipo de dato ANIMAL_TY tiene tres atributos y un método: SELECT Type_Name, Attributes, Methods FROM USER_TYPES WHERE Type_Name = 'ANIMAL_TY';

TYPE_NAME -------------------ANIMAL_TY

ATTRIBUTES -----------------3

METHODS -------------1

Atributos de tipos de datos: USER_TYPE_ATTRS Para ver los atributos de un tipo de datos podemos consultar la vista USER_TYPE_ATTRS. Las columnas de esta vista son las siguientes: Type_Name Attr_Name Attr_Type_Mod Attr_Type_Owner Attr_Type_Name Length Precision Scale Character_Set_Name

Nombre del tipo. Nombre del atributo. El tipo de modificador del atributo. Propietario del tipo del atributo, si el atributo está basado en otro tipo. Nombre del tipo del atributo. Longitud del atributo. Precisión del atributo. Escala del atributo. Juego de caracteres del atributo. Oracle /306

Attr_No Inherited

Posición ordinal del atributo dentro de la definición del tipo. Indicador Y/N de si el atributo es heredado de un supertipo.

Podemos consultar USER_TYPE_ATTRS para ver la relación entre los tipos de datos abstractos anidados. Por ejemplo, el tipo de dato PERSONA_TY usa el tipo DIRECCION_TY, tal como se muestra en el siguiente ejemplo: SELECT Attr_Name, Length, Attr_Type_Name FROM USER_TYPE_ATTRS WHERE Type_Name = 'PERSONA_TY'; ATTR_NAME -------------------NOMBRE DIRECCION

LENGTH -----------25

ATTR_TYPE_NAME -----------------------VARCHAR2 DIRECCION_TY

Métodos de tipos de datos: USER_TYPE_METHODS y USER_METHOD_PARAMS Si un tipo tiene métodos definidos, entonces podemos consultar la vista USER_TYPE_METHODS para determinar los nombres de los métodos. La vista USER_TYPE_METHODS contiene columnas para mostrar el nombre del tipo (Type_Name), el nombre del método (Method_Name), el número del método (Method_No, usado para métodos sobrecargados), y el tipo de método (Method_Type). La vista USER_TYPE_METHODS también incluye columnas para mostrar el número de parámetros (Parameters) y resultados (Results) retornado por el método. Por ejemplo, el tipo ANIMAL_TY tiene un método, una función miembro llamada EDAD. El método EDAD tiene un parámetro de entrada (FechaNacimiento) y uno de salida (la edad, en días). Consultando USER_TYPE_METHODS se muestra que hay dos parámetros definidos en EDAD y no uno: SELECT Parameters, Results FROM USER_TYPE_METHODS WHERE Type_Name = 'ANIMAL_TY' AND Method_Name = 'EDAD'; PARAMETERS -------------------2

RESULTS -----------1

¿Por qué

EDAD tiene dos parámetros si sólo hay uno definido? Para entenderlos, USER_METHOD_PARAMS, la cual describe los parámetros de nuestros métodos: SELECT Param_Name, Param_No, Param_Type_Name FROM USER_METHOD_PARAMS ORDER BY Param_No; PARAM_NAME -------------------------SELF FECHANACIMIENTO

PARAM_NO ---------------1 2

podemos consultar la vista

PARAM_TYPE_NAME -------------------------ANIMAL_TY DATE

Aquí podemos ver que para cada método Oracle crea un parámetro nuestros métodos son mostrados en USER_METHOD_RESULTS:

SELF

implícito. Los resultados de

SELECT Method_Name, Result_Type_Name FROM USER_METHOD_RESULTS WHERE Type_Name = 'ANIMAL_TY';

METHOD_NAME -------------------EDAD

RESULT_TYPE_NAME --------------------------NUMBER

Otros tipos de datos: USER_REFS, USER_COLL_TYPES y USER_NESTED_TABLES Si usamos referencias a tipos con REF podemos consultar la vista USER_REFS para mostrar los REF's que hemos definido. La vista USER_REFS muestra el nombre de la tabla que contiene la columna REF (Table_Name) y el nombre de columna del objeto columna ( Column_Name). Los atributos del REF —como si están o no almacenados con el ROWID— también son accesibles mediante USER_REFS. Los tipos de colección (tablas anidadas y arrays variables) son descritos mediante la vista USER_COLL_TYPES del diccionario de datos. Las columnas de USER_COLL_TYPES incluyen Type_Name, Upper_Bound (para arrays variables), y la longitud (Length) y precisión (Precision) de los elementos. Podemos usar USER_COLL_TYPES en conjunción con las vistas de los tipos de datos abstractos mostrados previamente para determinar el tipo de Oracle /307

estructura de una colección. Podemos también consultar USER_NESTED_TABLES y USER_VARRAYS para ver los detalles de nuestras colecciones. 1.6.2. LOB's: USER_LOBS La vista USER_LOBS proporciona información de los LOB's definidos en nuestras tablas. A continuación se muestra un ejemplo: SELECT Table_Name, Column_Name FROM USER_LOBS;

TABLE_NAME ------------------OFERTA OFERTA

COLUMN_NAME --------------------TEXTO_OFERTA PRESUPUESTO

también muestra los nombres de los segmentos usados para contener los datos LOB cuando se agranda; sin embargo, no muestra los tipos de datos de columnas LOB. Para ver los tipos de datos LOB podemos usar el comando DESCRIBE sobre la tabla que incluye los LOB's o consultar la vista USER_TAB_COLUMNS. 1.7. Enlaces de base de datos y vistas materializadas. Los enlaces de base de datos y vistas materializadas se usan para acceder a datos remotos. Dependiendo de los tipos de vistas materializadas que usemos, podemos habilitar el uso de registros de vistas materializadas. 1.7.1. Enlaces de base de datos: USER_DB_LINKS Para ver los enlaces de base de datos creados bajo nuestra cuenta podemos consultar la vista USER_DB_LINKS. Las columnas de esta vista, incluyendo el nombre del enlace (DB_Link), nombre de usuario para conectar (Username), la contraseña de la cuenta (Password), y la cadena de conexión (Host), muestran la información sobre la conexión remota que el enlace usará para establecerse. Los valores de Username y Password serán usados para registrarse en la base de datos remota definida por el valor de Host. La columna Host almacena el nombre del servicio Oracle Net. Esta columna almacena la cadena de caracteres exacta especificada durante el comando CREATE DATABASE LINK, y no altera su casuística. Por lo tanto, debemos ser cuidadosos al crear enlaces de base de datos y escribir el nombre del servicio. USER_LOBS

Nota. Si estamos usando inicios de conexión por defecto a la base de datos remota, la columna Password tendrá el valor NULL. También se dispone de las vistas ALL_DB_LINKS y DBA_DB_LINKS. Estas vistas tienen una columna Owner en vez de la columna Password. 1.7.2. Vistas materializadas. Podemos consultar USER_MVIEWS para mostrar información sobre las vistas materializadas propiedad de nuestra cuenta. Esta vista muestra la información de estructura de la vista materializada así como su planificación de refresco. Las columnas de esta vista son: Nombre col. Owner Mview_Name Container_Name Query Query_Len Updatable Update_Log Master_Rollback_Seg Master_Link Rewrite_Enabled Rewrite_Capability Refresh_Mode Refresh_Method Build_Mode

Descripción La cuenta que es propiedad de la vista materializada. En nombre de la vista materializada. La tabla base (en la base de datos local) con los datos de la vista materializada. La consulta que define la vista materializada. La longitud de la consulta base de la vista materializada. Un indicador de si la instantánea puede ser actualizada. El nombre de la tabla que registra los cambios hechos en instantáneas actualizables. El segmento de rollback usado cuando se pueblan instantáneas y se hacen operaciones de refresco. El enlace de base de datos usado para acceder a la base de datos maestra. Un indicado Yes/No de si ha habilitado rescritura de consultas para la vista. Reglas y restricciones para consultas rescritas. DEMAND, COMMIT o NEVER, dependiendo del modo de refresco de la vista. Valores usando para dirigir un refresco rápido de la vista (Complete, Fast, Never, o Force). Instanciación IMMEDIATE, DEFERRED o PREBUILT de los datos de la vista materializada durante su creación. Oracle /308

Fast_Refreshable Last_Refresh_Type Last_Refresh_Date Staleness After_Fast_Refresh Compile_State Use_No_Index

Métodos disponibles para refrescos rápidos de la vista materializada. El tipo de refresco más reciente usado. La fecha y hora de registro de la última vez que los datos fueron refrescados. El estado de los datos de la vista materializada relativos a su tabla maestra. Estado siguiente a un refresco rápido. Validez de la vista materializada. Indicador Yes/No de si la vista materializada fue creada con la cláusula USING NO INDEX. Unknown_Trusted_FD Indica si la vista materializada usa restricción de confianza para el refresco. El tiempo en que la vista materializada se hizo antigua. Stale_Since

El nombre de la vista materializada se encuentra en la columna Mview_Name de USER_MVIEWS. La tabla base local de la vista está en la columna Container_Name. Las nuevas columnas de USER_MVIEWS disponibles desde Oracle Database 10g, incluyen Stale_Since (cuándo la vista materializada se hace vieja). Para determinar qué enlaces de base de datos están siendo usados por vistas materializadas, podemos consultar la columna Master_Link, tal se muestra en el siguiente ejemplo: SELECT Master_Link FROM USER_MVIEWS;

Los nombres de los enlaces de base de datos retornados por esta consulta pueden ser usados como entradas para consultas a la vista USER_DB_LINKS. Esta consulta mostrará toda la información disponible de todos los enlaces de base de datos usados en nuestras vistas materializadas:

SELECT * FROM USER_DB_LINKS WHERE DB_Link IN (SELECT Master_Link FROM USER_MVIEWS); Las vistas ALL_MVIEWS y DBA_MVIEWS tienen las mismas definiciones de columnas que USER_MVIEWS. Dos vistas relacionadas —USER_REFRESH y USER_REFRESH_CHILDREN— muestran información sobre grupos de refrescos. USER_MVIEW_REFRESH_TIMES muestra la última vez que las vistas materializadas fueron

refrescadas. Desde Oracle Database 10g, podemos acceder a los comentarios de las vistas materializadas mediante la vista USER_MVIEW_COMMENTS, y consultar detalles de rescritura mediante USER_REWRITE_EQUIVALENCES. Capacidades adicionales de vistas materializadas. Podemos consultar la vista USER_MVIEW_ANALYSIS para ver las vistas materializadas que soportan rescritura de consulta. Si una vista materializada contiene referencias a tablas remotas, éstas no serán mostradas en esta vista. Podemos consultar el propietario de la vista materializada ( Owner), su nombre (Mview_Name) y el propietario de la tabla base (Mview_Table_Owner). Muchas de las columnas de esta vista son indicadores, como Summary ('Y' si la vista contiene una agregación), Known_Stale ('Y' si los datos de la vista son inconsistente con la tabla base), y Contains_Views ('Y' si la vista materializada referencia una vista). Si la vista materializada contiene agregaciones, podemos consultar USER_MVIEW_AGGREGATES para ver los detalles de la agregación. Las columnas de esta vista son las siguientes: Owner Mview_Name Position_in_Select Container_Column Agg_Function DistinctFlag Measure

Propietario de la vista materializada. Nombre de la vista materializada. Posición dentro de la consulta. Nombre de la columna. Función de agregado. 'Y' si la agregación usa la función DISTINCT. El texto SQL de la medida, excluyendo la función agregada

Podemos consultar los detalles de las relaciones dentro de las vistas materializadas usando las vistas USER_MVIEW_DETAIL_RELATIONS y USER_MVIEW_KEYS. Si la vista materializada está basada en JOIN's, podemos consultar USER_MVIEW_JOINS para los detalles del JOIN. En general, USER_MVIEW_ANALYSIS se usará más habitualmente para consultar datos sobre las vistas materializadas. 1.7.3. Registros de vistas materializadas: USER_MVIEW_LOGS Los registros de vistas materializadas pueden ser usados por muchas vistas materializadas para determinar qué registros en la tabla principal necesitan ser refrescados en la vista materializada de esta tabla. Podemos consultar USER_MVIEW_LOGS para obtener información sobre los registros de un usuario, incluyendo el nombre de la tabla principal ( Master), la tabla que contiene los registros ( Log_Table), y si la vista materializada Oracle /309

está basada sobre la clave primaria o sobre el ROWID (las columnas Primary_Key y ROWID). La vista USER_MVIEW_LOGS es normalmente consultada para propósitos de mantenimiento, como para determinar el nombre del trigger usado para crear los registros de la vista materializada. La vista DBA_MVIEW_LOGS tiene las mismas columnas que USER_MVIEW_LOGS. Podemos consultar la vista USER_BASE_TABLE_MVIEWS para obtener las tablas primarias de la vista materializada que usan registros de vista materializada. 1.8. Triggers, procedimientos, funciones y paquetes. Podemos usar procedimientos, paquetes y triggers para forzar reglas del negocio o para realizar procesos complejos. 1.8.1. Triggers: USER_TRIGGERS La vista USER_TRIGGERS contiene información sobre los trigger propiedad de nuestra cuenta. Esta vista muestra el tipo de trigger y su cuerpo. Las columnas de esta vista son: Nombre col.

Descripción Nombre del trigger. El tipo de trigger (BEFORE STATEMENT, BEFORE EACH ROW, y demás). El comando que ejecuta el trigger (INSERT, UPDATE, o DELETE). El propietario de la tabla para el cual se define el trigger. El tipo de objeto sobre el que está basado el trigger (TABLE, VIEW, SCHEMA, o DATABASE). El nombre de la tabla o vista para el cual está definido el trigger. Table_Name Para triggers de tablas anidadas, el nombre de la columna de la tabla anidada. Column_Name Referencing_Names Nombres usados para referenciar los valores OLD y NEW en el trigger. When_Clause La cláusula WHEN usada por el trigger. Status Si es trigger está activado (ENABLED) o desactivado (DISABLED). La descripción del trigger. Description Tipo de acción del cuerpo del trigger (CALL o PL/SQL). Action_Type El texto del trigger. Trigger_Body Trigger_Name Trigger_Type Triggering_Event Table_Owner Base_Object_Type

Las vistas ALL_TRIGGERS y DBA_TRIGGERS añaden la columna Owner para indicar el propietario del trigger. Otra vista relacionada con los triggers es USER_TRIGGER_COLS, las cual muestra cómo las columnas son usadas por el trigger. Lista el nombre de cada columna afectada por un trigger, así cómo el trigger la usa. También existen las versiones ALL_TRIGGER_COLS y DBA_TRIGGER_COLS. 1.8.2. Procedimientos, funciones y paquetes: USER_SOURCE El código fuente de procedimientos, funciones, paquetes y cuerpos de paquetes puede ser consultado usando la vista USER_SOURCE. La columnas Type de USER_SOURCE identifica el objeto procedimental, y puede tomar los valores: 'PROCEDURE', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY', 'TRIGGER','TYPE', 'TYPE BODY' o 'JAVA SOURCE'. Cada línea de código es almacenada en un registro independiente de USER_SOURCE. Podemos seleccionar información de USER_SOURCE mediante una consulta similar a la mostrada a continuación. En este ejemplo, se selecciona la columna Text y se ordena por el número de la columna Line. El nombre y tipo del objeto se usan para especificar qué código será mostrado: SELECT Text FROM USER_SOURCE WHERE Name = '&procedure_name' AND Type = 'PROCEDURE' ORDER BY Line;

Nota. La secuencia de las líneas es mantenida por la columna Line; por lo tanto, esta columna debe ser usada para ordenar la consulta. Las vistas objeto.

ALL_SOURCE

y

DBA_SOURCE

añaden la columna adicional

Owner

para indicar el propietario del

Nota. Para ver los valores de los parámetros persistentes para unidades PL/SQL hay que consultar la vista USER_STORED_SETTINGS. Códigos de error: USER_ERRORS El comando SHOW ERRORS de SQL*Plus verifica la vista del diccionario de datos USER_ERRORS para mostrar los errores asociados con la última compilación de un objeto procedimental. SHOW ERRORS mostrará la línea Oracle /310

y número de columna de cada error, así como el texto del mensaje de error. Para ver los errores asociados con los objetos procedimentales creados previamente, podemos consultar USER_ERRORS directamente. Podemos necesitar hacer esto cuando miramos los errores asociados con cuerpos de paquetes, donde la compilación del paquete resultó en un error no mostrado por el comando SHOW ERROR. Podemos también necesitar consultar USER_ERRORS cuando encontremos errores de compilación con varios objetos procedimentales. Las siguientes son las columnas disponibles en esta vista: Nombre col.

Descripción El nombre del objeto procedimental. Name Type El tipo de objeto ('PROCEDURE', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY', 'TRIGGER', 'TYPE', 'TYPE BODY', 'VIEW', 'JAVA CLASS', o 'JAVA SOURCE'). Sequence El número de línea de secuencia, para usar en la cláusula ORDER BY. El número de línea dentro del código fuente en el que ocurrió el error. Line La posición dentro de la línea en la que ocurrió el error. Position El texto del mensaje de error. Text Attribute Indicador de si la fila seleccionada es un error ('ERROR') o un aviso ('WARNING'). Message_Number Número de error, sin prefijo.

Consultas sucesivas a esta vista deberían siempre incluir la columna Sequence en la cláusula ORDER BY. Las vistas ALL_ERRORS y DBA_ERRORS añaden la columna adicional Owner. Tamaño del código: USER_OBJECT_SIZE Podemos consultar la cantidad de espacio usado en el tablespace SYSTEM por un objeto procedimental mediante la vista USER_OBJECT_SIZE. Tal como se muestra en el siguiente listado, podemos acumular el tamaño de las cuatro áreas separadas para determinar el espacio total usado en las tablas del diccionario de datos de SYSTEM para almacenar los objetos. Las cuatro columnas de tamaño (_Size), seguidas de las columnas Name y Type, constituyen todas las columnas de esta vista. SELECT Source_Size+Code_Size+Parsed_Size+Error_Size AS Total FROM USER_OBJECT_SIZE WHERE Name = '&procedure_name' AND Type = 'PROCEDURE'; También existe la vista DBA_OBJECT_SIZE.

1.9. Dimensiones. Podemos crear y mantener dimensiones y jerarquías. Podemos consultar la columna Dimension_Name de la vista USER_DIMENSIONS para mostrar los nombres de nuestras dimensiones. USER_DIMENSIONS también contiene columnas para el propietario de la dimensión (Owner), estado (columna Invalid, asignada a 'Y' o 'N'), y nivel de revisión (columna Revision). Los atributos de una dimensión son accedidos mediante vistas adicionales del diccionario de datos. Para ver las jerarquías dentro de una dimensión podemos consultar la vista USER_DIM_HIERARCHIES. Esta vista tiene sólo tres columnas: Owner, Dimension_Name, y Hierarchy_Name. Consultando USER_DIM_HIERARCHIES para la dimensión GEOGRAFIA obtendremos el nombre de sus jerarquías: SELECT Hierarchy_Name FROM USER_DIM_HIERARCHIES WHERE Dimension_Name=''GEOGRAFIA';

HIERARCHY_NAME ------------------------PAISES_ROLLUP

Podemos ver los detalles de la jerarquía para PAISES_ROLLUP consultando la vista USER_DIM_CHILD_OF, tal como se muestra a continuación. El primero comando crea una dimensión llamada GEOGRAFIA que registra las jerarquías de países y continentes. CREATE DIMENSION GEOGRAFIA LEVEL PAIS_ID IS PAIS.Pais LEVEL CONTINENT_ID IS CONTINENTE.Continente HIERARCHY PAISES_ROLLUP ( PAIS_ID CHILD OF CONTINENTE_ID JOIN KEY PAIS.Continente REFERENCES CONTINENTE_ID );

Oracle /311

SELECT Child_Level_Name, Parent_Level_Name, Position, Join_Key_Id FROM USER_DIM_CHILD_OF WHERE Hierarchy_Name = 'PAISES_ROLLUP'; CHILD_LEVEL_NAME -------------------------PAIS_ID

PARENT_LEVEL_NAME ---------------------------CONTINENTE_ID

POSITION -------------1

JOIN -------1

Podemos consultar la vista USER_DIM_JOIN_KEY para ver la clave de join de una jerarquía: SELECT Level_name, Child_Join_Column FROM USER_DIM_JOIN_KEY WHERE Dimension_Name = 'GEOGRAFIA' AND Hierarchy_Name = 'PAISES_ROLLUP'; LEVEL_NAME ----------------------CONTINENTE_ID

CHILD_JOIN_COLUMN ---------------------------CONTINENTE

Podemos ver los niveles de una dimensión consultando la vista USER_DIM_LEVELS, y ver las columnas claves mediante USER_DIM_LEVEL_KEY. La información de atributos para dimensiones es accesible mediante la vista USER_DIM_ATTRIBUTES. Hay versiones ALL_ y DBA_ de las vistas relacionadas con las dimensiones. 1.10. Asignación y uso de espacio, incluyendo particiones y subparticiones. Podemos consultar el diccionario de datos para determinar el espacio que está disponible y asignado para los objetos de base de datos. 1.10.1. Tablespaces: USER_TABLESPACES Podemos consultar la vista USER_TABLESPACES para determinar sobre qué tablespaces tenemos permisos de acceso y los parámetros de almacenamiento por defecto de cada uno. Los parámetros de almacenamiento por defecto del tablespace serán usados para cada objeto almacenado dentro del tablespace a menos que el comando CREATE o ALTER para estos objetos especifique sus propios parámetros de almacenamiento. Las columnas relativas al espacio de USER_TABLESPACES, son muy similares a las columnas relativas al espacio de USER_TABLES. No hay versión ALL_ de esta vista. DBA_TABLESPACES muestra los parámetros de almacenamiento de todos los tablespaces. Nombre col.

Descripción Nombre del tablespace. El tamaño de bloque en uso por este tablespace. El parámetro INITIAL por defecto para objetos del tablespace. El parámetro NEXT por defecto para objetos del tablespace. El parámetro MINEXTENTS por defecto para objetos del tablespace. El parámetro MAXEXTENTS por defecto para objetos del tablespace. El parámetro PCTINCREASE por defecto para objetos del tablespace. El tamaño mínimo de extensión para los objetos del tablespace. El estado del tablespace ('ONLINE', 'OFFLINE', 'INVALID', 'READ ONLY'). Un tablespace inválido es uno que ha sido borrado; sus registros están todavía visibles a través de esta vista. Un indicador de si el tablespace es usado para almacenar objetos permanentes Contents ('PERMANENT') o solo segmentos temporales ('TEMPORARY'). Logging Indicador del valor por defecto del parámetro LOGGING/NOLOGGING para objetos del tablespace. Dónde se realiza la administración de las extensiones en el tablespace Extent_Management ('DICTIONARY' o 'LOCAL'). Tipo de asignación de extensión efectiva. Allocation_ Type Segment_Space_Management Indicador de si el espacio libro es controlado mediante listas libres ('MANUAL') o mapas de bits ('AUTO'). Retention Retención del tablespace de deshacer ('GUARANTEE', 'NO GUARANTEE', o 'NOT APPLY'). Bigfile Indica si el tablespace es un tablespace de ficheros grandes ( 'YES') o de Tablespace_Name Block_Size Initial_Extent Next_Extent Min_Extents Max_Extents Pct_Increase Min_Extlen Status

Oracle /312

ficheros pequeños ('NO'). Indicador Yes/No de si el tablespace está en modo logging.

Force_Logging

1.10.2. Cuotas de espacio: USER_TS_QUOTAS La vista USER_TS_QUOTAS es muy útil para determinar la cantidad de espacio que tenemos actualmente asignada y la máxima cantidad de espacio disponible para nuestros tablespaces. Una consulta sencilla de USER_TS_QUOTAS se muestra a continuación: SELECT * FROM USER_TS_QUOTAS; TABLESPACE_NAME -------------------------USERS

BYTES ---------67584

MAX_BYTES ---------------0

BLOCKS ---------33

MAX_BLOCKS -----------------0

contiene un registro por cada nombre de tabla ( Tablespace_Name). La columna Bytes refleja el número de bytes asignados para objetos propiedad del usuario. La columna Max_Bytes es el máximo número de bytes que el usuario puede poseer en este tablespace; si no hay cuota para este tablespaces, entonces Max_Bytes mostrará el valor 0. Las columnas Bytes y Max_Bytes son trasladadas dentro de bloques de Oracle en las columnas Blocks y Max_Blocks, respectivamente. No hay versión ALL_ para esta vista. La vista DBA_TS_QUOTAS muestra las cuotas para todos los usuarios y tablespaces, y es el modo más efectivo de mostrar el uso de espacio dentro de toda la base de datos. 1.10.3. Segmentos y extensiones: USER_SEGMENTS y USER_EXTENTS El espacio se asigna a los objetos (como tablas, clústeres e índices) en segmentos, las contrapartidas físicas de los objetos lógicas creados en la base de datos. Podemos consultar la vista USER_SEGMENTS para ver los parámetros de almacenamiento actuales y el espacio usado en nuestros segmentos. USER_SEGMENTS es muy útil cuando estamos en peligro de exceder uno de los límites de almacenamiento. Las columnas de esta vista son las siguientes: USER_TS_QUOTAS

Nombre col. Segment_Name Partition_Name Segment_Type Tablespace_Name Bytes Blocks Extents Initial_Extent Next_Extent Min_Extents Max_Extents Pct_Increase Freelists

Freelist_Groups Buffer_Pool

Descripción El nombre del segmento. NULL si el objeto no esta particionado; sino, el nombre de la partición del segmento. El tipo de segmento ('TABLE', 'CLUSTER', 'INDEX', 'ROLLBACK', y demás). El nombre del tablespace en el cual el segmento es almacenado. El número de bytes asignados al segmento. El número de bloques Oracle asignados al segmento. El número de extensiones en el segmento. El tamaño de la extensión inicial del segmento. El valor del parámetro NEXT para el segmento. El número mínimo de extensiones en el segmento. El valor del parámetro MAXEXTENTS para el segmento. El valor del parámetro PCTINCREASE para el segmento. El número de procesos freelist (lista de bloques de datos en el segmento que pueden usarse durante inserciones) asignados al segmento; si un segmento tiene varios freelists, entonces la contención para los bloques libres durante inserciones actuales disminuirá. El número de grupos freelist asignados al segmento. Búfer interno en el cual el segmento será leído ('DEFAULT', 'KEEP', o 'RECYCLE') si hemos definido varios grupos de búferes.

Los segmentos consisten de secciones contiguas llamadas extensiones. Las extensiones que constituyen segmentos son descritas en la vista USER_EXTENTS. En USER_EXTENTS podremos ver el tamaño actual de cada extensión dentro del segmento; esto es útil para registrar el impacto de cambios en las opciones NEXT y PCTINCREASE. Además de las columnas Segment_Name, Segment_Type y Tablespace_Name, la vista USER_EXTENTS tiene tres nuevas columnas: Extent_ID (para identificar la extensión dentro del segmento), Bytes (el tamaño de la extensión, en bytes), and Blocks (el tamaño de la extensión, en bloques Oracle). Tanto USER_SEGMENTS como USER_EXTENTS tienen versiones DBA_, que añaden la columna adicional Owner. Si queremos mostrar todos los propietarios que poseen segmentos en un tablespace, podemos consultar asignando la columna Tablespace_Name en DBA_SEGMENTS. No hay versión ALL_ de estas vistas. 1.10.4. Particiones y subparticiones. Un simple dato de tabla puede ser almacenado a través de varias particiones. Para ver cómo una tabla está particionada debemos consultar la vista USER_PART_TABLES, cuyas columnas se describen en la siguiente Oracle /313

tabla. Columnas de identificación Table_Name Partitioning_Type Subpartitioning_Type Partition_Count Def_Subpartition_Count Partitioning_Key_Count Subpartitioning_Key_Count Def_Logging Def_Buffer_Pool

Columnas relativas a almacenamiento Def_Tablespace_Name Def_Pct_Free Def_Pct_Used Def_Ini_Trans Def_Max_Trans Def_Initial_Extent Def_Next_Extent Def_Min_Extents Def_Max_Extents Def_Pct_Increase Def_Freelists Def_Freelist_Groups Def_Compression

La mayoría de columnas de USER_PART_TABLES definen los parámetros de almacenamiento por defecto para las particiones de la tabla. Cuando una partición es añadida a la tabla, por defecto usará los parámetros de almacenamiento mostrados en USER_PART_TABLES. La vista USER_PART_TABLES también muestra el número de particiones de la tabla (Partition_Count), el número de columnas en la clave de partición (Partitioning_Key_Count), y el tipo de partición (Partitioning_Type). USER_PART_TABLES almacena una fila por cada tabla que ha sido particionada. Para ver información sobre cada partición individual que pertenece a la tabla podemos consultar la vista USER_TAB_PARTITIONS. En USER_TAB_PARTITIONS veremos una fila por cada partición de una tabla. Las columnas de USER_TAB_PARTITIONS se muestran en la siguiente tabla. Columnas de identificación Table_Name Composite Partition_Name Subpartition_Count High_Value High_Value_Length Partition_Position Logging Buffer_Pool

Columnas relativas a almacenamiento Tablespace_Name Pct_Free Pct_Used Ini_Trans Max_Trans Initial_Extent Next_Extent Min_Extent Max_Extent Pct_Increase

Oracle /314

Freelists Freelist_Groups Compression

Columnas relativas a estadísticas Num_Rows Blocks Empty_Blocks Avg_Space Chain_Cnt Avg_Row_Len Sample_Size Last_Analyzed Global_Stats User_Stats

La vista USER_TAB_PARTITIONS contiene columnas que identifican la tabla a la cual pertenecen las particiones y muestra los parámetros de almacenamiento de la partición y las estadísticas para la partición. Las columnas relacionadas con estadísticas son pobladas cuando la tabla es analizada. Las columnas de identificación muestran los valores mayores para el rango usado para definir la partición (High_Value)y la posición de la partición dentro de la tabla (Partition_Position). Las columnas usadas por la clave de partición son accesibles mediante la vista USER_PART_KEY_COLUMNS. Esta vista contiene sólo cuatro columnas. Name Object_Type Column_Name Column_Position

El nombre de la tabla o índice particionado El tipo de objeto (TABLE o INDEX). El nombre de la columna que es parte de la clave de partición. La posición de la columna dentro de la clave de partición.

Las estadísticas para las columnas de partición son accesibles mediante la vista USER_PART_COL_STATISTICS. Las columnas en USER_PART_COL_STATISTICS reflejan las correspondientes en USER_TAB_COL_STATISTICS. La información de histogramas de datos para particiones es accesible mediante la vista USER_PART_HISTOGRAMS. Las columnas de esta vista muestran los valores de punto final para cada uno de los cubos del histograma. Las columnas son Table_Name, Partition_Name, Column_Name, Bucket_Number, Endpoint_Value y Endpoint_Actual_Value. Ya que los índices pueden ser particionados, hay una vista USER_IND_PARTITIONS. Las columnas en USER_IND_PARTITIONS pueden agruparse en tres categorías, tal como se muestra en la siguiente tabla. Columnas de identificación Index_Name Composite Partition_Name Subpartition_Count High_Value High_Value_Length Partition_Position Status Logging Buffer_Pool Compression Domidx_Opstatus Parameters

Columnas relativas a espacio Tablespace_Name Ini_Trans Max_Trans Initial_Extent Next_Extent Min_Extent

Oracle /315

Max_Extent Pct_Increase Pct_Free Freelists Freelist_Groups

Columnas relativas a estadísticas Blevel Leaf_Blocks Distinct_Keys Avg_Leaf_Blocks_Per_Key Avg_Data_Blocks_Per_Key Clustering_Factor Num_Rows Sample_Size Last_Analyzed User_Stats Pct_Direct_Access Global_Stats

Las columnas en USER_IND_PARTITIONS son paralelas a las de USER_INDEXES, con unas pocas modificaciones en las columnas de identificación. Las columnas de identificación para índices particionados incluyen el nombre de la partición, el mayor valor para la partición, y la posición de la partición dentro de la tabla. Las columnas relativas a las estadísticas son pobladas cuando la partición es analizada. Las columnas relativas al espacio describen la asignación de espacio para el índice. Si una partición tiene subparticiones, podemos ver los detalles de las subparticiones mediante varias vistas del diccionario de datos. La vista USER_IND_SUBPARTITIONS contiene las mismas columnas relativas al espacio y a las estadísticas que USER_IND_PARTITIONS, incluyendo columnas para identificar la subpartición (Subpartition_Name y Subpartition_Position). Similarmente, la vista USER_TAB_SUBPARTITIONS contiene las mismas columnas relativas al espacio y las estadísticas que USER_TAB_PARTITIONS, además de las columnas Subpartition_Name y Subpartition_Position. Por tanto, podemos determinar las definiciones de espacio para cada partición y subpartición. Como hemos visto, podemos consultar las vistas USER_PART_COL_STATISTICS y USER_PART_HISTOGRAMS por información de estadísticas independientemente de las particiones. Para subparticiones, podemos consultar USER_SUBPART_COL_STATISTICS y USER_SUBPART_HISTOGRAMS, cuyas estructuras reflejan las vistas de estadísticas de partición. Para ver las columnas claves de subparticiones podemos consultar USER_SUBPART_KEY_COLUMNS, cuya estructura de columnas es idéntica a la de USER_PART_KEY_COLUMNS. 1.10.5. Espacio libre: USER_FREE_SPACE Además de ver el espacio que hemos usado, podemos también consultar el diccionario de datos para ver cuánto espacio esta marcado actualmente como espacio libre. La vista USER_FREE_SPACE muestra las extensiones libres en todos los tablespaces accesibles por el usuario. Esta vista cataloga por el nombre de tablespace (Tablespace_Name) el ID de fichero (File_ID), el ID de bloque (Block_ID), y el número de archivo relativo al punto de partida de la extensión libre. El tamaño de la extensión libre es mostrado tanto en bytes como en bloques. La vista DBA_FREE_SPACE es usado con frecuencia por DBA's para supervisar la cantidad de espacio libre disponible y el grado en el cual está fragmentado. 1.11. Usuarios y permisos. Los usuarios y sus permisos son registrados dentro del diccionario de datos. 1.11.1. Usuarios: USER_USERS Podemos consultar la vista USER_USERS para mostrar información sobre nuestra cuenta. La vista USER_USERS incluye nuestro nombre de usuario ( Username), un ID asignado por la base de datos ( User_ID), el tablespace por defecto (Default_Tablespace), el tablespace temporal (Temporary_Tablespace), y la fecha de creación de la cuenta (Created). La columna Account_Status de USER_USERS muestra el estado de nuestra cuenta, si está bloqueada, desbloqueada ('OPEN'), o ha expirado. Si una cuenta está bloqueada, la columna Lock_Date mostrará la fecha en que fue la cuenta bloqueada y la columna Expiry_Date mostrará la fecha de expiración. Podemos también consultar información sobre los grupos de recursos consumidos Oracle /316

(Initial_Rsrc_Consumer_Group) asignados a nosotros por el DBA, y nuestro nombre externo (External_Name). La vista ALL_USERS contiene sólo el nombre de usuario (Username), identificador (User_ID) y fecha de creación (Created).ALL_USERS es útil cuando necesitamos saber los nombres de usuarios que están disponibles (por ejemplo, durante comandos GRANT). La vista DBA_USERS contiene las mismas columnas que USER_USERS, más dos columnas adicionales: Password (la contraseña cifrada de la cuenta) y Profile (el perfil de recurso del usuario). 1.11.2. Límites de los recursos: USER_RESOURCE_LIMITS En Oracle, se pueden usar perfiles para poner límites a la cantidad de recursos disponibles del sistema y base de datos para un usuario. Si no son creados perfiles en la base de datos, el perfil por defecto especificará recursos ilimitados para todos los usuarios. Los perfiles fuerzan medidas de seguridad adicionales, como fechas de expiración sobre cuentas y la longitud mínima de contraseñas. Para ver los límites que están puestos en nuestra sesión actual podemos consultar la vista USER_RESOURCE_LIMITS. Sus columnas son las siguientes: Resource_Name Limit

El nombre del recurso (por ejemplo, SESSIONS_PER_USER). El límite puesto sobre el recurso.

La vista USER_PASSWORD_LIMITS describe los parámetros de perfil de contraseña para el usuario. Tiene las mismas columnas que USER_RESOURCE_LIMITS. No hay versiones ALL_ o DBA_ de esta vista. Para ver el coste asociado con cada recurso disponible podemos consultar la vista RESOURCE_COST. Los DBA's pueden acceder a la vista DBA_PROFILES para ver los límites de recursos para todos los perfiles. La columna Resource_Type de DBA_PROFILES indica si el perfil de recurso es un perfil 'PASSWORD' o 'KERNEL'. 1.11.3. Permisos de tabla: USER_TAB_PRIVS Podemos usar la vista USER_TAB_PRIVS(Permisos de tabla de usuario) para ver los permisos que nos fueron concedidos, el otorgador del permiso y el propietario del objeto. Además de sus columnas Grantee, Grantor y Owner, esta vista contiene columnas Table_Name, Hierarchy, Privilege, y un indicador (asignado a 'YES' o 'NO') para ver si el permiso fue concedido con WITH ADMIN OPTION (Grantable). La vista USER_TAB_PRIVS_MADE muestra los registros de USER_TAB_PRIVS para los cuales el usuario es el propietario (y por lo tanto carece de una columna Owner). La vista USER_TAB_PRIVS_RECD (Permisos recibidos de tabla de usuario) muestra los registros de USER_TAB_PRIVS con los permisos concedidos al usuario (y por tanto carece de una columna Grantee). Por tanto, tanto USER_TAB_PRIVS_MADE como USER_TAB_PRIVS_RECD son subconjuntos de USER_TAB_PRIVS. Hay versiones ALL_ para las vistas USER_TAB_PRIVS, USER_TAB_PRIVS_MADE y USER_TAB_PRIVS_RECD. Las versiones ALL_ catalogan aquellos objetos para los cuales el usuario o PUBLIC es el cesionario o el otorgador. Hay una versión DBA_ de USER_TAB_PRIVS llamada DBA_TAB_PRIVS. Permisos de columna: USER_COL_PRIVS Además de conceder permisos sobre tabla, podemos también conceder permisos al nivel de columnas. Las vistas del diccionario de datos usada para mostrar los permisos de columnas son la mayoría idénticos en diseña a las vistas de permisos de tablas. Hay una columna adicional Column_Name en cada vista COL_, y la columna Hierarchy se ha suprimido. La vista USER_COL_PRIVS es análoga a USER_TAB_PRIVS, la vista USER_COL_PRIVS_MADE es análoga a USER_TAB_PRIVS_MADE, y la vista USER_COL_PRIVS_RECD es análoga a USER_TAB_PRIVS_RECD. Versiones ALL_ también están disponibles para todas estas vistas. Y DBA_COL_PRIVS muestra todos los permisos de columna concedidos a los usuarios de la base de datos. 1.11.4. Permisos del sistema: USER_SYS_PRIVS La vista USER_SYS_PRIVS muestra los permisos del sistema que han sido concedidos al usuario. Sus columnas son Username, Privilege, y Admin_Option (un indicador 'YES' o 'NO' que dice si el permiso fue concedido con WITH ADMIN OPTION). Todos los permisos de sistema concedidos directamente a un usuario son mostrados mediante esta vista. Los permisos de sistema concedidos a un usuario mediante un rol no son mostrados aquí. La siguiente consulta muestra la salida para la cuenta EMPLEADO: SELECT Username, Privilege * FROM USER_SYS_PRIVS; USERNAME ---------------EMPLEADO EMPLEADO

PRIVILEGE ---------------------------------CREATE DIMENSION UNLIMITED TABLESPACE

Oracle /317

No hay versión ALL_ para esta vista. La vista DBA_SYS_PRIVS permite consultar todos los permisos de sistema concedidos a todos los usuarios de la base de datos. 1.12. Roles. Además de los permiso concedidos directamente a los usuarios, podemos agrupar conjuntos de permisos dentro de roles. Los roles pueden ser concedidos a los usuarios o a otros roles, y pueden consistir de ambos objetos y permisos de sistema. Para ver qué roles nos han sido concedidos debemos consultar la vista USER_ROLE_PRIVS. Cualquier rol que hay sido concedido como PUBLIC será mostrado aquí. Las columnas de esta vista son: Username Granted_Role Admin_Option Default_Role OS_Granted

El nombre de usuario (puede ser 'PUBLIC'). El nombre del rol concedido al usuario. Un indicador de si el rol fue concedido con WITH ADMIN OPTION. Un indicador de si el rol es un rol por defecto de usuario. Un indicador de si el sistema operativo está siendo usado para gestionar roles.

Para mostrar todos los roles disponibles en la base de datos, necesitamos tener autoridad de DBA; entonces podemos usar la vista DBA_ROLES para consultar todos los roles. La vista DBA_ROLE_PRIVS muestra todas las asignaciones de estos roles a todos los usuarios de la base de datos. Los roles pueden recibir tres tipos diferentes de concesiones, cada una de las cuales se corresponde con una vista del diccionario de datos: De tabla/columna Permisos de sistema De rol

ROLE_TAB_PRIVS.

Similar a USER_TAB_PRIVS y USER_COL_PRIVS, excepto que tienen una columna Role en vez de la columna Grantee, y no tiene la columna Grantor. ROLE_SYS_PRIVS. Similar a USER_SYS_PRIVS, excepto que tiene una columna Role en vez de la columna Username. ROLE_ROLE_PRIVS. Muestra todos los roles que han sido concedidos a otros roles.

Nota. Si no somos DBA, estas vistas del diccionario de datos muestran solo los roles que nos han sido concedidos. Además de estas vistas, hay dos vistas, cada una con una única columna, que muestran los permisos y roles habilitados para la sesión actual: SESSION_PRIVS La columna Privilege muestra todos los permisos de sistema habilitados para la sesión, tanto concedidos directamente o a través de roles. SESSION_ROLES La columna Role muestra todos los roles de la sesión actual. Las vistas SESSION_PRIVS y SESSION_ROLES están disponibles para todos los usuarios. 1.13. Auditoría. Si no se es un usuario DBA dentro de una base de datos de Oracle no se pueden habilitar las características de auditoría de la base de datos. Si la auditoría ha sido habilitada hay varias vistas del diccionario de datos que alguien puede usar para ver los resultados de auditoría. Hay muchos resultados de auditoría que podemos ver mediante vistas del diccionario de datos. La mayoría de estas vistas están basadas sobre una única tabla de resultados de auditoría llamada SYS.AUD$. La más genérica de las vistas de resultados de auditoría se denomina USER_AUDIT_TRAIL. Sus columnas se describen a continuación. Nombre col. OS_Username Username UserHost Terminal TimeStamp Owner Obj_Name Action Action_Name New_Owner New_Name

Descripción La cuenta del sistema operativo del usuario. El nombre de usuario Oracle del usuario auditado. Un ID numérico de la instancia usada por el usuario auditado. El identificador de terminal del sistema operativo del usuario. La fecha y hora en que fue creado el registro auditado. El propietario del objeto afectado por una acción (para acciones de auditoría). El nombre del objeto afectado por una acción (para acciones de auditoría). El código numérico para la acción de auditoría. El nombre de la acción de auditoría. El propietario del objeto nombrado en la columna New_Name. El nuevo nombre de un objeto que ha sido renombrado. Oracle /318

Obj_Privilege Sys_Privilege Admin_Option

El permiso de objeto que ha sido concedido o revocado. El permiso de sistema que ha sido concedido o revocado. Un indicador Y/N de si el rol o permiso de sistema fue concedido con WITH ADMIN OPTION. Grantee El nombre de usuario especificado en un comando GRANT o REVOKE. Audit_Option Las opciones de auditoría asignadas mediante el comando AUDIT. Una cadena de caracteres que sirve como resumen de sesión, registrando sucesos y Ses_Actions fallos de diferentes acciones. La fecha y hora de la salida de sesión del usuario. Logoff_Time El número de lecturas lógicas realizadas durante la sesión. Logoff_LRead El número de lecturas físicas realizadas durante la sesión. Logoff_PRead El número de escrituras lógicas realizadas durante la sesión. Logoff_LWrite El número de bloqueos detectados durante la sesión. Logoff_DLock Un comentario de texto sobre la entrada de seguimiento de auditoría. Comment_Text El ID numérico de la sesión. SessionID Un ID numérico de la entrada de seguimiento de auditoría. EntryID El ID numérico para cada comando que fue ejecutado. StatementID El código de retorno de cada comando que fue ejecutado; si el comando sucedió sin ReturnCode fallos, el código de retorno será cero. El permiso de sistema usado para ejecutar la acción. Priv_Used El ID cliente. Client_ID La sesión de CPU usada. Session_CPU Extended_Timestamp Fecha y hora de la creación de la entrada se seguimiento de auditoría en la zona horaria de la sesión. Número de serie del proxy de sesión, si el usuario empresarial ha iniciado sesión Proxy_SessionID mediante un mecanismo de proxy. Identificador global del usuario, si el usuario ha iniciado sesión como un usuario Global_UID empresarial. Número de instancia especificado en el fichero de inicialización de parámetros. Instance_Number Identificador del proceso del sistema operativo del proceso servidor de Oracle. OS_Process Identificador de transacción de la transacción en la cual el objeto es accedido o TransactionID modificado. SCN de la consulta. SCN Variable de enlace de la consulta. SQL_Bind Texto de la consulta. SQL_Text

Aunque esta vista muestra registros de auditoría para muchos tipos diferentes de acciones, muchas de las columnas pueden ser inaplicables para alguna de las filas. La versión DBA_ de esta vista, DBA_AUDIT_TRAIL, muestra todas las entradas de la tabla de seguimiento de auditorías; USER_AUDIT_TRAIL muestra sólo aquellas relevantes para el usuario. Desde Oracle Database 10g, hay nuevas columnas en USER_AUDIT_TRAIL, incluyendo SQL_Text, SQL_Bind, OS_Process, y SCN. Cada tipo de auditoría puede ser accedido mediante su propia vista del diccionario de datos. Las siguientes son las vistas disponibles: USER_AUDIT_OBJECT USER_AUDIT_SESSION USER_AUDIT_STATEMENT

Para comandos concernientes a objetos. Para conexiones y desconexiones. Para comandos GRANT, REVOKE, AUDIT, NOAUDIT, y ALTER SYSTEM realizados por el usuario.

Hay versiones DBA_ de esas tres vistas. Podemos ver las opciones de auditoría que actualmente afectan a nuestros objetos consultando la vista USER_OBJ_AUDIT_OPTS. Para cada objeto mostrado en USER_OBJ_AUDIT_OPTS, las opciones de auditoria de cada comando que puede ser ejecutado sobre estos objetos (identificados por las columnas Object_Name y Object_Type) se muestran en USER_OBJ_AUDIT_OPTS. Los nombres de columna de USER_OBJ_AUDIT_OPTS se corresponden a las primeras letras del comando (por ejemplo, Alt para ALTER, Upd para UPDATE, etc.). Cada columna será registrada si el comando es auditado para el objeto cuando suceda el comando ( 'S'), no suceda ('U') o ambos. Las opciones de auditoría por defecto en efecto para cualquier nuevo objeto de la base de Oracle /319

datos pueden ser mostradas mediante la vista ALL_DEF_AUDIT_OPTS, la cual tiene las mismas columnas que USER_OBJ_AUDIT_OPTS. La vista USER_OBJ_AUDIT_OPTS incluye las columnas Object_Name y Object_Type. Los comandos que pueden ser auditados son almacenados en una tabla de referencia llamada AUDIT_ACTIONS, la cual tiene dos columnas: Action (el código numérico de la acción) y Name (el nombre de la acción o comando). Action y Name se corresponden con las columnas Action y Action_Name de USER_AUDIT_TRAIL. Los DBA's pueden usar varias vistas de auditoría adicionales que no tienen su contrapartida USER_, incluyendo DBA_AUDIT_EXISTS, DBA_PRIV_AUDIT_OPTS, DBA_STMT_AUDIT_OPTS, y STMT_AUDIT_OPTION_MAP. 1.14. Supervisión: las tablas de rendimiento dinámico V$. Las vistas que supervisan el rendimiento del entorno de base de datos son llamadas vistas de rendimiento dinámico. Estas vistas son referenciadas normalmente como tablas V$, porque todas ellas comienzan con este término. La definición y uso de columnas dentro de las vistas de supervisión están sujetos a cambios con cada versión de la base de datos. Las tablas V$ son usadas normalmente sólo por el DBA. 1.14.1. La tabla CHAINED_ROWS. Se puede usar el comando ANALYZE para generar un listado de las filas encadenadas o migradas dentro de una tabla. Este listado de filas encadenadas puede ser almacenado en una tabla llamada CHAINED_ROWS. Para crear la tabla CHAINED_ROWS en nuestro esquema podemos ejecutar el script utlchain.sql (que normalmente se encuentra en el subdirectorio /rdbms/admin del directorio de instalación de Oracle). Fichero «utlchain.sql» rem rem $Header: utlchain.sql 07-may-96.19:40:01 sbasu Exp $ rem Rem Copyright (c) 1990, 1995, 1996, 1998 by Oracle Corporation Rem NAME REM UTLCHAIN.SQL Rem FUNCTION Rem Creates the default table for storing the output of the Rem analyze list chained rows command Rem NOTES Rem MODIFIED Rem syeung 06/17/98 - add subpartition_name Rem mmonajje 05/21/96 - Replace timestamp col name with analyze_timestam Rem sbasu 05/07/96 - Remove echo setting Rem ssamu 08/14/95 - merge PTI with Objects Rem ssamu 07/24/95 - add field for partition name Rem glumpkin 10/19/92 - Renamed from CHAINROW.SQL Rem ggatlin 03/09/92 - add set echo on Rem rlim 04/29/91 - change char to varchar2 Rem Klein 01/10/91 - add owner name for chained rows Rem Klein 12/04/90 - Creation Rem create table CHAINED_ROWS ( owner_name varchar2(30), table_name varchar2(30), cluster_name varchar2(30), partition_name varchar2(30), subpartition_name varchar2(30), head_rowid rowid, analyze_timestamp date );

Para poblar la tabla CHAINED_ROWS hay que usar la cláusula ANALYZE, tal como se muestra a continuación:

LIST CHAINED ROWS INTO

ANALYZE TABLE Festivos LIST CHAINED ROWS INTO CHAINED_ROWS;

Oracle /320

del comando

La tabla CHAINED_ROWS muestra las columnas Owner_Name, Table_Name, Cluster_Name (si la tabla es un clúster), Partition_Name (si la tabla está particionada), Subpartition_Name (si la tabla contiene subparticiones), Head_RowID (el ROWID de la fila) y Analyze_TimeStamp (que muestra la última vez que la tabla o clúster fue analizado). Podemos consultar la tabla según los valores de Head_RowID, como en el siguiente ejemplo: SELECT * FROM Festivos WHERE RowID IN (SELECT Head_RowID FROM CHAINED_ROWS WHERE Table_Name = 'Festivo');

Si la fila encadenada es de poca longitud, entonces podemos eliminar el encadenamiento borrando y reinsertando la fila. 1.14.2. La tabla PLAN_TABLE. Cuando afinamos comandos SQL podemos querer determinar los pasos que el optimizador realizó para ejecutar nuestra consulta. Para ver la ruta de la consulta debemos primero crear una tabla en nuestro esquema llamada PLAN_TABLE. El script usando para crear esta tabla se denomina utlxplan.sql, y normalmente se almacena en el subdirectorio /rdbms/admin. Fichero «utlxplan.sql» rem rem $Header: utlxplan.sql 08-may-2004.12:53:19 bdagevil Exp $ xplainpl.sql rem Rem Copyright (c) 1988, 2004, Oracle. All rights reserved. Rem NAME REM UTLXPLAN.SQL Rem FUNCTION Rem NOTES Rem MODIFIED Rem bdagevil 05/08/04 - add other_xml column Rem bdagevil 06/18/03 - rename hint alias to object_alias Rem ddas 06/03/03 - increase size of hint alias column Rem bdagevil 02/13/03 - add plan_id and depth column Rem ddas 01/17/03 - add query_block and hint_alias columns Rem ddas 11/04/02 - revert timestamp column to DATE (PL/SQL problem) Rem ddas 10/28/02 - change type of timestamp column to TIMESTAMP Rem ddas 10/03/02 - add estimated_time column Rem mzait 04/16/02 - add row vector to the plan table Rem mzait 10/26/01 - add keys and filter predicates to the plan table Rem ddas 05/05/00 - increase length of options column Rem ddas 04/17/00 - add CPU, I/O cost, temp_space columns Rem mzait 02/19/98 - add distribution method column Rem ddas 05/17/96 - change search_columns to number Rem achaudhr 07/23/95 - PTI: Add columns partition_{start, stop, id} Rem glumpkin 08/25/94 - new optimizer fields Rem jcohen 11/05/93 - merge changes from branch 1.1.710.1 - 9/24 Rem jcohen 09/24/93 - #163783 add optimizer column Rem glumpkin 10/25/92 - Renamed from XPLAINPL.SQL Rem jcohen 05/22/92 - #79645 - set node width to 128 (M_XDBI in gendef) Rem rlim 04/29/91 - change char to varchar2 Rem Peeler 10/19/88 - Creation Rem Rem This is the format for the table that is used by the EXPLAIN PLAN Rem statement. The explain statement requires the presence of this Rem table in order to store the descriptions of the row sources. create table PLAN_TABLE ( statement_id varchar2(30), plan_id number, timestamp date, remarks varchar2(4000),

Oracle /321

operation options object_node object_owner object_name object_alias object_instance object_type optimizer search_columns id parent_id depth position cost cardinality bytes other_tag partition_start partition_stop partition_id other distribution cpu_cost io_cost temp_space access_predicates filter_predicates projection time qblock_name other_xml

varchar2(30), varchar2(255), varchar2(128), varchar2(30), varchar2(30), varchar2(65), numeric, varchar2(30), varchar2(255), number, numeric, numeric, numeric, numeric, numeric, numeric, numeric, varchar2(255), varchar2(255), varchar2(255), numeric, long, varchar2(30), numeric, numeric, numeric, varchar2(4000), varchar2(4000), varchar2(4000), numeric, varchar2(30), clob

);

Después de crear esta tabla podemos usar el comando EXPLAIN PLAN, el cual generará registros en nuestra tabla PLAN_TABLE, etiquetando el valor Statement_ID podemos especificar la consulta que queremos desgranar. Las columnas ID y Parent_ID de la tabla PLAN_TABLE establecen la jerarquía de pasos (Operations) que el optimizador sigue cuando ejecuta la consulta. 1.14.3. Interdependencias: USER_DEPENDENCIES y IDEPTREE. Unos objetos dentro de las bases de datos de Oracle pueden depender de otros. Por ejemplo, un procedimiento almacenado puede depender de una tabla, o un paquete pude depender de un cuerpo de paquete. Cuando un objeto dentro de la base de datos cambia, cualquier objeto procedimental que depende de él tendrá que ser recompilado. Esta recompilación puede ser automática (con una penalización consecuente del rendimiento) o manualmente. Dos conjuntos de vistas del diccionario de datos están disponibles para ayudarnos a trazar dependencias. El primero es USER_DEPENDENCIES, el cual muestra todas las dependencias directas de los objetos. Sin embargo, esta vista sólo muestra el primer nivel de dependencias. Para evaluar completamente el árbol de dependencias debemos crear objetos de seguimiento de dependencias recursivas en nuestro esquema. Para crear estos objetos hay que ejecutar el script utldtree.sql (normalmente localizado en el subdirectorio /rdbms/admin). Este script crea dos objetos que podemos consultar: DEPTREE y IDEPTREE. Estos objetos contienen información idéntica, pero IDEPTREE está indentada según la pseudo-columna Level, y por tanto es fácil de leer e interpretar. Parte del contenido del fichero «utldtree.sql» drop sequence deptree_seq / create sequence deptree_seq cache 200 /* cache 200 to make sequence faster */

Oracle /322

/ drop table deptree_temptab / create table deptree_temptab ( object_id number, referenced_object_id number, nest_level number, seq# number ) / create or replace procedure deptree_fill (type char, schema char, name char) is obj_id number; begin delete from deptree_temptab; commit; select object_id into obj_id from all_objects where owner = upper(deptree_fill.schema) and object_name = upper(deptree_fill.name) and object_type = upper(deptree_fill.type); insert into deptree_temptab values(obj_id, 0, 0, 0); insert into deptree_temptab select object_id, referenced_object_id, level, deptree_seq.nextval from public_dependency connect by prior object_id = referenced_object_id start with referenced_object_id = deptree_fill.obj_id; exception when no_data_found then raise_application_error(-20000, 'ORU-10013: ' || type || '' || schema || '.' || name || ' was not found.'); end; / drop view deptree / create view sys.deptree (nested_level, type, schema, name, seq#) as select d.nest_level, o.object_type, o.owner, o.object_name, d.seq# from deptree_temptab d, dba_objects o where d.object_id = o.object_id (+) union all select d.nest_level+1, 'CURSOR', '', '"'||c.kglnaobj||'"', d.seq#+.5 from deptree_temptab d, x$kgldp k, x$kglob g, obj$ o, user$ u, x$kglob c, x$kglxs a where d.object_id = o.obj# and o.name = g.kglnaobj and o.owner# = u.user# and u.name = g.kglnaown and g.kglhdadr = k.kglrfhdl and k.kglhdadr = a.kglhdadr /* make sure it is not a transitive */ and k.kgldepno = a.kglxsdep /* reference, but a direct one */ and k.kglhdadr = c.kglhdadr and c.kglhdnsp = 0 /* a cursor */ / create view deptree (nested_level, type, schema, name, seq#) as select d.nest_level, o.object_type, o.owner, o.object_name, d.seq#

Oracle /323

/

from deptree_temptab d, all_objects o where d.object_id = o.object_id (+)

drop view ideptree / create view ideptree (dependencies) as select lpad('',3*(max(nested_level))) || max(nvl(type, '') || '' || schema || decode(type, NULL, '', '.') || name) from deptree group by seq# /* So user can omit sort-by when selecting from ideptree */ /

1.14.4. Seguridad de etiquetas de Oracle. Los usuarios de la Seguridad de etiquetas de Oracle disponen de vistas adicionales del diccionario de datos, incluyendo ALL_SA_GROUPS, ALL_SA_POLICIES, ALL_SA_USERS, y ALL_SA_USER_PRIVS. 1.14.5. Vistas de carga directa de SQL*Loader. Para controlar la opciones de carga directa de SQL*Loader, Oracle mantiene varias vistas del diccionario de datos. Éstas son generalmente sólo consultadas para propósitos de depuración. Las vistas que soportan la opción de carga directa son LOADER_COL_INFO, LOADER_CONSTRAINT_INFO, LOADER_FILE_TS, LOADER_PARAM_INFO, LOADER_PART_INFO, LOADER_REF_INFO, LOADER_TAB_INFO y LOADER_TRIGGER_INFO. Estas vistas son creadas mediante el script catldr.sql, normalmente localizado en el subdirectorio /rdbms/admin. 1.14.6. Vista para soportar globalización. Tres vistas del diccionario de datos muestran información sobre los parámetros de soporte de globalización que se están aplicando actualmente en la base de datos. Valores no estándar para los parámetros NLS (como NLS_DATE_FORMAT y NLS_SORT) pueden asignarse mediante el fichero de parámetros de la base de datos o mediante el comando ALTER SESSION. Para ver las asignaciones NLS de nuestra sesión, instancia y base de datos podemos consultar las vistas NLS_SESSION_PARAMETERS, NLS_INSTANCE_PARAMETERS, y NLS_DATABASE_PARAMETERS, respectivamente. 1.14.7. Librerías. Nuestras rutinas PL/SQL pueden ser programas C externos. Para ver de qué librerías externas somos propietarios, podemos consultar la vista USER_LIBRARIES, la cual muestra el nombre de la librería (Library_Name), el fichero asociado (File_Spec), si la librería es no dinámica (Dynamic), y el estado de la librería (Status). También están disponibles las vistas ALL_LIBRARIES y DBA_LIBRARIES, que incluyen la columna adicional Owner. 1.14.8. Servicios heterogéneos. Para soportar la gestión de servicios heterogéneos, Oracle proporciona varias vistas del diccionario de datos. Todas las vistas de esta categoría comienzan con HS_ en vez de DBA_. En general, estas vistas son usadas principalmente por los DBA's. 1.14.9. Tipos de índices y operadores. Los operadores y tipos de índices están estrechamente relacionados. Podemos usar el comando CREATE OPERATOR para crear un nuevo operador y definir sus enlaces. Podemos referenciar operadores en tipos de índices en comandos SQL. Los operadores, a su vez, referencian funciones, paquetes, tipos y otros objetos definidos por el usuario. Podemos consultar la vista USER_OPERATORS para ver de cada operador su propietario ( Owner), nombre (Operator_Name), y número de enlaces (Number_of_Binds). La información auxiliar para los operadores es accesible mediante la vista USER_OPANCILLARY, y podemos consultar USER_OPARGUMENTS para ver los argumentos de los operadores. Podemos consultar USER_OPBINDINGS para ver los enlaces de operadores. La vista USER_INDEXTYPE_OPERATORS muestra los operadores soportados por tipos de índices. Los tipos de índices pueden ser mostrados mediante la vista USER_INDEXTYPES. Existen versiones ALL_ y DBA_ de estas vistas. Desde Oracle Database 10g, podemos ver los tipos de índices de tipos de arrays mediante la vista USER_INDEXTYPE_ARRAYTYPES.

Oracle /324

1.14.10. Outlines. Cuando usamos outlines almacenados, podemos recuperar sus nombres y detalles mediante la vista USER_OUTLINES. Para ver las marcas que constituyen los outlines, podemos consultar USER_OUTLINE_HINTS. Hay versiones ALL_ y DBA_ de USER_OUTLINES y USER_OUTLINE_HINTS. 1.14.11. Consejeros. Desde Oracle Database 10g, los usuarios pueden acceder a vistas del diccionario de datos relacionadas con las recomendaciones para afinar. Estas vistas empiezan con USER_ADVISOR_ y tienen sus correspondientes versiones DBA_. Por ejemplo, USER_ADVISOR_ACTIONS contiene datos sobre las acciones asociadas con todas las recomendaciones en la base de datos. Cada acción se especifica en la columna Command, con seis columnas de atributos relacionados. USER_ADVISOR_LOG muestra información sobre el estado actual de todas las tareas, como los datos específicos de ejecución, el progreso de supervisión y el estado completo. Los parámetros para los consejeros son accesibles mediante la vista USER_ADVISOR_PARAMETERS; podemos ver las exposiciones razonadas para recomendaciones mediante la vista USER_ADVISOR_RATIONALE. Para los resultados de un análisis de todas las recomendaciones en la base de datos podemos consultar la vista USER_ADVISOR_RECOMMENDATIONS. Una recomendación puede tener varias acciones asociadas (ver USER_ADVISOR_ACTIONS) basadas en las exposiciones razonadas usadas. 1.14.12. Planificadores. Desde Oracle Database 10g, podemos acceder a un conjunto de vistas relacionadas con las tareas de planificación en la base de datos. Estas vistas incluyen USER_SCHEDULER_JOBS (todas las tareas planificadas propiedad del usuario), USER_SCHEDULER_PROGRAMS (las tareas programadas), y USER_SCHEDULER_PROGRAM_ARGS (los argumentos para los programas programados). Podemos ver los detalles de ejecución mediante las vistas USER_SCHEDULER_JOB_LOG, USER_SCHEDULER_JOB_RUN_DETAILS, y USER_SCHEDULER_RUNNING_JOBS. Podemos ver los planificadores mediante USER_SCHEDULER_SCHEDULES.

2. Administración de la base de datos. En este capítulo veremos los pasos involucrados en la administración de Oracle Database 11g. Hay muchos componentes para las tareas de los administradores de base de datos (DBA). Las tareas básicas consisten de la creación de una base de datos, iniciar y parar la base de datos, dar tamaño y controlar las áreas de memoria de la base de datos, asignar y gestionar espacio para los objetos, crear y gestionar recuperaciones y realizar copias de respaldo. 2.1. Creación de una base de datos. El modo más simple de generar un script de creación de una base de datos es mediante el Instalador Universal de Oracle (OUI). Cuando instalamos Oracle, el OUI nos da la opción de crear una base de datos. Si usamos esta opción, OUI creará una base de datos que es normalmente usada para practicar o que será la base para el desarrollo de nuestras aplicaciones. El comando CREATE DATABASE se utiliza con SQL*Plus, a través de una cuenta con el permiso de sistema SYSDBA: CONNECT system/contraseña AS SYSDBA STARTUP NOMOUNT CREATE DATABASE . . .

Para intentar conectarse como un SYSDBA debemos tener la apropiada autorización en los niveles del sistema operativo y la base de datos. 2.1.1. Usando el «Oracle Enterprise Manager». Oracle Enterprise Manager (OEM), es una herramienta con interfaz gráfica de usuario. Las herramientas incluidas en OEM proporcionan una interfaz robusta para administrar bases de datos remotas. Todos los DBA's pueden usar el mismo repositorio central OEM (un conjunto de tabla creadas en una base de datos) para realizar sus trabajos. Además, OEM incluye un planificador de tareas. Debemos tomar varias decisiones clave antes de instalar y configurar OEM. Necesitamos decidir donde se creará el repositorio OEM y cómo y cuándo se harán copias de respaldo para proteger este repositorio. Aunque podemos usar OEM como una interfaz para el Oracle Recovery Manager (RMAN), la información de recuperación puede ser almacenada en el repositorio OEM. Se puede crear una base de datos mínima Oracle /325

independiente para almacenar el repositorio OEM. Deberemos asegurarnos de que esta base de datos sea respaldada con frecuencia de forma que la recuperación del repositorio mismo esté asegurada. 2.2. Iniciación y parado de la base de datos. Para iniciar una base de datos se usa el comando STARTUP dentro de SQL*Plus, tal como se muestra en el siguiente ejemplo: CONNECT system/contraseña AS SYSDBA; STARTUP OPEN MiBD;

Alternativamente, podemos montar la base de datos: CONNECT system/contraseña AS SYSDBA; STARTUP MOUNT MiBD;

Cuando la base de datos es montada pero no abierta, podemos gestionar sus ficheros. Por ejemplo, si movemos alguno de los ficheros de la base de datos mientras ésta fue parada, necesitamos decirle a Oracle saber dónde encontrarlos antes de reiniciarla. Para obtener la nueva ubicación de los ficheros, podemos montar la base de datos y entonces usar el comando ALTER DATABASE para renombrar los antiguos ficheros a su nueva localización. Una vez que hemos finalizado de decirle a Oracle la nueva localización para los ficheros, podemos abrir la base de datos mediante al comando ALTER DATABASE OPEN. Hay cuatro opciones primarias para parar la base de datos. En una parada normal ( SHUTDOWN NORMAL), Oracle espera a que todos los usuarios salgan de la base de datos antes de pararla. En una parada transaccional (SHUTDOWN TRANSACTIONAL), Oracle espera que las transacciones activas se completen antes de parar. En una parada inmediata ( SHUTDOWN IMMEDIATE), Oracle da marcha atrás a las transacciones no confirmadas y finaliza la sesión de cualquier usuario). En una parada abortada ( SHUTDOWN ABORT), la base de datos se para inmediatamente y cualquier transacción no confirmada se pierde. Nota. Cuando la base de datos está en proceso de parar o iniciarse, no se permiten nuevos inicios de sesión. Por ejemplo, para realizar una parada inmediata de la base de datos: CONNECT system/contraseña AS SYSDBA; SHUTDOWN IMMEDIATE

2.3. Tamaño y gestión de las áreas de memoria. Cuando iniciamos una base de datos, Oracle asignar un área de memoria (el System Global Area, o SGA) compartida por todos los usuarios de la base de datos. Las dos áreas más grandes del SGA son normalmente el búfer caché de la base de datos y el "pool" compartido; sus tamaños impactan directamente en los requerimientos de memoria de la base de datos y el rendimiento de sus operaciones. Sus tamaños son controlados por parámetros del fichero de inicialización de la base de datos. El búfer caché de la base de datos es un área del SGA usado para contener los bloques de datos que son leídos desde los segmentos de datos de la base de datos, como las tablas e índices. El tamaño del búfer caché de la base de datos es determinados por el parámetro DB_CACHE_SIZE (expresado en número de bytes) en el fichero de parámetros de inicialización de la base de datos. Por su parte, el tamaño por defecto para los bloques de la base de datos se asigna mediante el parámetro DB_BLOCK_SIZE. Gestionar el tamaño del búfer caché es una parte importante de controlar y afinar la base de datos. La base de datos tiene un tamaño de bloque por defecto, pero podemos establecer áreas de caché para diferentes tamaños de bloques y entonces crear tablespaces para que usen estas cachés. Por ejemplo, podemos crear un bloque de 4 KB con algunos tablespaces asignados a 8 KB. El tamaño de caché de 8 KB debería ser asignado mediante el parámetro DB_8K_CACHE_SIZE. Para crear tablespaces que usen esta caché debemos especificar BLOCKSIZE 8K como parte del comando CREATE TABLESPACE. Si el tamaño de bloque por defecto de la base de datos es 4 KB no deberíamos asignar un valor para DB_4K_CACHE_SIZE; el tamaño especificado por DB_CACHE_SIZE debería ser usado por la caché de 4 KB. Nota. La caché para el tamaño de bloque debe existir antes de crear un tablespace que use este tamaño de bloque. Las diferentes áreas de caché pueden ser redimensionadas mientras la base de datos se está ejecutando. Las cachés deben ser incrementadas o decrementadas en gránulos. Para una base de datos con un SGA menor de 128M, el tamaño de gránulo es 4M —así DB_8K_CACHE_SIZE puede ser 4M, 8M, 12M, y demás—. Si intentamos usar cualquier otro valor, Oracle lo redondeará al tamaño de gránulo mayor más próximo. El Oracle /326

siguiente comando muestra cómo asignar el parámetro DB_8K_CACHE_SIZE. ALTER SYSTEM SET DB_8K_CACHE_SIZE = 8M;

Si creamos un tablespace que use un tamaño de bloque distinto del de por defecto, debemos asegurarnos de que el parámetro de tamaño de caché relacionado (como DB_8K_CACHE_SIZE) sea actualizado en nuestro fichero de parámetros de base de datos. Si estamos usando un fichero «init.ora», debemos actualizarlo con el nuevo valor. Si estamos usando un fichero de parámetros de sistema (el método preferido), se actualizará automáticamente cuando ejecutemos el comando ALTER SYSTEM con la cláusula SCOPE=BOTH. Nota. No podemos alterar el valor de los parámetros la base de datos esté abierta.

SGA_MAX_SIZE

o JAVA_POOL_SIZE mientras

Oracle gestionará el espacio en el búfer caché usando un algoritmo que guarde los bloques usados más activos en lo que sea posible. Cuando es necesario espacio libre en la caché, los nuevos bloques intentarán usar el espacio ocupado por boques accedidos con poca frecuencia o el espacio ocupado por un bloque modificado una vez que ha sido escrito al disco. Si el SGA no es suficientemente grande para contener los datos más frecuentemente usados, diferentes objetos competirán por el espacio de la caché. La competición se producirá particularmente cuando varias aplicaciones usen la misma base de datos y compartan el mismo SGA. En este caso, las tablas e índices usados más recientemente para cada aplicación constantemente competirán por espacio en el SGA. Como resultado, solicitudes de datos provocan frecuentes lecturas/escrituras físicas de datos, provocando degradación en el rendimiento. El "pool" compartido almacena la caché del diccionario de datos (información sobre la estructura de base de datos) y la caché de librería (información sobre los comandos que se ejecutan sobre la base de datos). Mientras la caché de bloque de datos y la caché del diccionario permiten compartir la estructura e información de datos entre usuarios de la base de datos, la caché de biblioteca permite compartir los comando SQL más usados normalmente. El "pool" compartido contiene el plan de ejecución y árbol de reconocimiento de los comandos SQL que se ejecutan sobre la base de datos. La segunda vez que un comando SQL idéntico se ejecuta (por algún usuario), Oracle es capaz de tomar ventajas de la información del "pool" compartido para servir la ejecución del comando. El tamaño en bytes del "pool" compartido se asigna mediante el parámetro de inicialización SHARED_POOL_SIZE. Como con otras cachés, el "pool" compartido puede ser modificado mientras la base de datos está abierta. Desde Oracle Database 10g, podemos usar el Automatic Shared Memory Management (ASMM). Para activar el ASMM, se asigna un valor no cero al parámetro de inicialización SGA_TARGET. Después de asignar SGA_TARGET al tamaño de SGA deseado entonces debemos asignar los otros parámetros de caché relacionados (DB_CACHE_SIZE, SHARED_POOL_SIZE, JAVA_POOL_SIZE, y LARGE_POOL_SIZE) a cero. Debemos parar y reiniciar la base de datos para que estos cambios sean efectivos. Podemos supervisar el tamaño de las cachés mediante la vista de rendimiento dinámica V$SGASTAT. Nota. Mientras la base de daos se esté ejecutando, podemos modificar el parámetro Oracle modificará automáticamente el tamaño de las cachés relacionadas.

SGA_TARGET

y

Si la carga de trabajo en la base de datos cambia, la base de datos modificará los tamaños de caché para reflejar las necesidades de la aplicación. Por ejemplo, si hay un proceso por lotes intensivo durante la noche y una transacción en línea intensiva durante el día, la base de datos puede modificar los tamaños de caché cada vez que cambie la carga. Estos cambios ocurren automáticamente, si la intervención del DBA. 2.4. Asignar y gestionar espacio para objetos. Cuando una base de datos se crea, se divide en varias secciones lógicas llamadas tablespaces. El tablespace SYSTEM es el primero es ser creado, y el tablespace SYSAUX es creado como parte de cada base de datos de Oracle Database 11g. Podemos crear tablespaces adicionales para contener varios tipos de datos (como tablas, índices y segmentos de deshacer). Desde Oracle Database 10g, los tablespaces pueden ser renombrados mediante la cláusula RENAME del comando ALTER TABLESPACE. Cuando se crea un tablespace, se crean ficheros de datos para contener los datos. Estos ficheros son inmediatamente asignados al espacio especificado durante su creación. Cada fichero de datos puede estar en un único tablespace. Los ficheros de datos se pueden extender automáticamente cuando se ejecutan fuera del espacio asignado; podemos asignar el incremento por el cual se extiende y su tamaño máximo. Cada esquema Oracle /327

de usuario es una colección de objetos lógicos de la base de datos, como tablas e índices, que referencian estructuras de datos físicas almacenadas en los tablespaces. Los objetos de un esquema de usuario pueden almacenar varios tablespaces, y un único tablespace puede contener objetos de varios esquemas. Cuando un objeto de base de datos (como una tabla o índice) se crea, es asignado a un tablespace mediante una asignación por defecto o especificándolo en la instrucción. Se crea un segmento en el tablespace para contener los datos asociados con el objeto. Un segmento está constituido por secciones llamadas extensiones (conjunto de bloques Oracle contiguos). Una vez las extensiones existentes no puede contener más datos, el segmento obtendrá otra extensión. Si el segmento está compuesto de varias extensiones, no hay garantías de que las extensiones sean contiguas. Los tablespaces pueden ser creados para gestionar localmente (por defecto) o por el diccionario. En un tablespace gestionado por diccionario, la información de localización de extensiones se almacena en el diccionario de datos. En un tablespace gestionado localmente, los datos son almacenados en las cabeceras de los ficheros de datos. Oracle recomienda el uso de tablespaces gestionados localmente. 2.4.1. Implicaciones de la cláusula «storage». La cantidad de espacio usado por un segmento está determinado por sus parámetros de almacenamiento. Estos parámetros se determinan en el momento de creación de los segmentos de base de datos; si no especificamos parámetros de almacenamiento en el comando CREATE TABLE, CREATE INDEX, o CREATE CLUSTER, entonces la base de datos usará los parámetros de almacenamiento por defecto del tablespace el cual es segmento se almacena. Nota. Podemos asignar un tablespace por defecto a los usuarios, y asignar cuotas de espacio dentro de estos tablespaces, mediante los comandos CREATE USER, ALTER USER, y GRANT. Cuando creamos una tabla, índice u otro segmento, podemos usar los valores por defecto para tablespaces gestionados localmente (la opción recomendada) o especificar una cláusula STORAGE como parte del comando CREATE. También podemos especificar una cláusula TABLESPACE, habilitando que Oracle directamente almacene datos en un tablespace en particular. Por ejemplo, un comando CREATE TABLE en un tablespace gestionado por diccionario puede incluir las siguientes cláusulas: TABLESPACE USERS STORAGE (INITIAL 1M NEXT 1M PCTINCREASE 0 MINEXTENTS 1 MAXEXTENTS 200) Si no especificamos la cláusula STORAGE, los parámetros de almacenamiento por defecto del tablespace serán usados. Para un tablespace USERS gestionado localmente, el comando CREATE TABLE debería sólo necesitar incluir TABLESPACE USERS.

Los parámetros de almacenamiento especifican el tamaño de extensión inicial, el tamaño de la extensión siguiente, el PCTINCREASE (un factor por el cual la siguiente extensión será geométricamente dimensionada), la máxima extensión (MAXEXTENTS), y el número mínimo de extensiones (MINEXTENTS). Después de creado el segmento, los valores INITIAL y MINEXTENTS no pueden modificarse a menos que realicemos una reorganización del objeto. Los valores por defecto de los parámetros de almacenamiento están disponibles en las vistas DBA_TABLESPACES y USER_TABLESPACES. Cuando creamos un tablespace, podemos especificar los valores por defecto de los parámetros de almacenamiento. El siguiente comando crea un tablespace gestionado localmente y especifica los parámetros por defecto: CREATE TABLESPACE Tablas_Codigos DATAFILE '/u01/oracle/VLDB/codes_tables.dbf' SIZE 10M EXTENT MANAGEMENT LOCAL UNIFORM SIZE 256K;

Cuando se crea un segmento, adquiere al menos una extensión. La extensión inicial será usada para almacenar datos hasta que no hay más espacio disponible (puede usarse la cláusula PCTFREE para reservar un porcentaje de espacio dentro de cada bloque en el segmento para actualizaciones de los registros existentes). Si creamos un tablespace gestionado por diccionario, los tamaños de extensiones pueden ser no uniformes; el segmento se extenderá obteniendo una segunda extensión del tamaño especificado por el parámetro NEXT. Asumiendo que en este ejemplo, el tamaño de bloque de una base de datos es de 4KB, y el tablespace es creado gestionado localmente con un tamaño uniforme de 256KB, cada bit en el mapa de bits describe 64 bloques (256/4). Si la cláusula UNIFORM SIZE se omite, el valor por defecto es AUTOALLOCATE. El SIZE por defecto para UNIFORM es 1MB. Nota. Si especificamos LOCAL en un comando CREATE cláusula DEFAULT STORAGE, MINEXTENTS, o TEMPORARY. Oracle /328

TABLESPACE,

no podemos especificar la

Los tablespaces gestionados localmente puede asumir algunas de las tareas de gestión de espacio realizadas por los DBA's en tablespaces gestionados por diccionario. A causa de su arquitectura, tiene menos probabilidades de fragmentarse, y sus objetos tienen menos probabilidades de tener problemas de espacio. Podemos crear un conjunto de tablespaces gestionados localmente con un pequeño número de parámetros de almacenamiento y resolver la mayoría de solicitudes de espacio en nuestra base de datos. Por ejemplo, podemos crear tres tablespaces DATA como sigue: CREATE TABLESPACE DATA_SMALL DATAFILE '/u01/oracle/VLDB/data_small.dbf' SIZE 10M EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M; CREATE TABLESPACE DATA_MEDIUM DATAFILE '/u01/oracle/VLDB/data_medium.dbf' SIZE 100M EXTENT MANAGEMENT LOCAL UNIFORM SIZE 4M; CREATE TABLESPACE DATA_LARGE DATAFILE'/u01/oracle/VLDB/data_large.dbf' SIZE 1000M EXTENT MANAGEMENT LOCAL UNIFORM SIZE 16M;

Si tenemos una tabla pequeña podemos ubicarla en el tablespace DATA_SMALL. Si aumenta significativamente en tamaño podemos moverla al tablespace DATA_MEDIUM o DATA_LARGE. 2.4.2. Segmentos de tabla. Los segmentos de tabla, también llamadas segmentos de datos, almacenan las filas de datos asociados con las tablas o clústeres. Cada segmento de datos contiene un bloque de cabecera que sirve como un espacio de directorio para el segmento. Una vez que un segmento de datos adquiere una extensión, mantiene esta extensión hasta que el segmento es borrado, trucando o comprimido mediante las opciones de Oracle Database 11g. Borrar filas de una tabla mediante el comando DELETE no afecta a la cantidad de espacio asignado a la tabla. El número de extensiones se incrementará hasta que (1) el valor de MAXEXTENTS se sobrepase (si está asignado), (2) la cuota de usuario en el tablespace se sobrepasa, o (3) el tablespace se ejecuta fuera del espacio (si los ficheros de datos no puede auto-extenderse). Para minimizar la cantidad de espacio gastado en un bloque de datos se puede afinar el parámetro PCTFREE. El parámetro PCTFREE especifica la cantidad de espacio que quedará libre dentro de cada bloque de datos. El espacio libre puede ser entonces usado cuando columnas con valores NULL sean actualizadas para tener valores, o cuando actualizaciones de otros valores en la fila fuercen que la longitud total de la fila se incremente. Podemos usar el comando ALTER TABLE para modificar la mayoría de parámetros de almacenamiento de una tabla existente. Podemos usar la opción MOVE del comando ALTER TABLE para cambiar la asignación del tablespace de una tabla.

3. Auditoría de Seguridad Oracle tienen la capacidad de auditar todas las acciones que tienen lugar en la base de datos. Se pueden auditar tres tipos de acciones: • Intentos de entrada en cuentas de la base de datos. • Accesos a los objetos de la base de datos. • Acciones sobre la base de datos. La base de datos registra todos los intentos de acción, tanto los exitosos como los infructuosos, aunque es un parámetro configurable. Para habilitar la capacidad de auditoría se debe fijar el parámetro AUDIT_TRAIL en el fichero init.ora. Los registros de auditoría se almacenan en la tabla SYS.AUD$ o bien su gestión se deja al sistema operativo. Cuando se decide utilizar la tabla SYS.AUD$ ésta debe revisarse periódicamente, por si hiciera falta truncarla debido a que su aumento de tamaño puede causar problemas de espacio en el tablespace SYSTEM. Los valores del parámetro AUDIT_TRAIL son los que se exponen en la siguiente tabla: Valor

Descripción

NONE

Deshabilita la auditoría. Habilita la auditoría, escribiendo en la tabla SYS.AUD$. Habilita la auditoría, dejando al sistema operativo su gestión.

BD OS

Oracle /329

3.1. Auditando conexiones. Todo intento de conexión con la base de datos será registrado. El comando para iniciar la auditoría es: AUDIT SESSION;

Para determinar si se deben registrar sólo los éxitos, o sólo los fracasos, se pueden utilizar los siguientes comandos: AUDIT SESSION WHENEVER SUCCESSFUL; AUDIT SESSION WHENEVER NOT SUCCESSFUL;

Si los registros de auditoría se almacenan en la tabla DBA_AUDIT_SESSION.

SYS.AUD$,

SELECT OS_USERNAME, USERNAME, TERMINAL, DECODE(RETURNCODE,'0','Conectado', '1005', 'Solo username, sin password', '1017','Password incorrecto', returncode), TO_CHAR(TIMESTAMP,'DD-MON-YY HH24:MI:SS'), TO_CHAR(LOGOFF_TIME,'DD-MON-YY HH24:MI:SS') FROM DBA_AUDIT_SESSION;

entonces pueden verse a través de la vista

/* nombre de usuario SO */ /* nombre de usuario BD */

/* comprobacion de error */ /* hora de entrada */ /* hora de salida */

Para deshabilitar la auditoria de las conexiones basta con ejecutar la siguiente sentencia: NOAUDIT SESSION;

3.2. Auditando Acciones Se puede auditar cualquier acción que afecte a cualquier objeto de la base de datos. Para facilitar la gestión, las acciones a auditar se encuentran agrupadas según los grupos que se muestran en la siguiente tabla: Grupo CLUSTER DATABASE LINK EXISTS INDEX NOT EXISTS PROCEDURE PROFILE PUBLIC DATABASE LINK PUBLIC SINONYM ROLE ROLLBACK SEGMENT SEQUENCE SESSION SYNONYM SYSTEM AUDIT SYSTEM GRANT TABLE TABLESPACE TRIGGER USER VIEW

Comandos auditados Todas las sentencias que afecten a clusters. Todas las sentencias que afecten a enlaces de base de datos. Todas las sentencias que fallen porque ya existe un objeto en la base de datos. Todas las sentencias que afecten a índices. Todas las sentencias que fallen porque un determinado objeto no existe. Todas las sentencias que afecten a procedimientos. Todas las sentencias que afecten a perfiles. Todas las sentencias que afecten a enlaces públicos de base de datos. Todas las sentencias que afecten a sinónimos públicos. Todas las sentencias que afecten a roles. Todas las sentencias que afecten a segmentos de rollback. Todas las sentencias que afecten a secuencias. Todas las sentencias de acceso a la base de datos. Todas las sentencias que afecten a sinónimos. Todas las sentencias AUDIT y NOAUDIT. Todas las sentencias afecten a privilegios. Todas las sentencias que afecten a tablas. Todas las sentencias que afecten a espacios de tablas. Todas las sentencias que afecten a disparadores. Todas las sentencias que afecten a las cuentas de usuarios. Todas las sentencias que afecten a vistas.

Por ejemplo, para auditar todas acciones que tienen que ver con las tablas se usa el siguiente comando: AUDIT TABLE;

Y para deshabilitar la auditoría se utilizará el siguiente comando: NOAUDIT TABLE;

También se puede afinar un poco más en la auditoría fijando un usuario concreto al que seguir la pista: AUDIT TABLE BY Usuario1;

Cada acción auditada recibe un código numérico al que se puede acceder a través de la vista AUDIT_ACTIONS. Una vez que conocemos el código de la acción, podemos utilizarlo para determinar cómo dicha acción ha afectado a un objeto, consultado la vista DBA_AUDIT_OBJECT. Oracle /330

3.3. Auditando objetos. Además de la auditoría de acciones sobre los objetos, se puede seguir el rastro a las operaciones de manipulación de tablas: SELECT, INSERT, UPDATE y DELETE. Estas auditorías se pueden hacer por sesión o por acceso. Un ejemplo de sentencias de auditorías sobre objetos se puede ver en el siguiente grupo de sentencias: AUDIT INSERT ON Usuario1.EMP; AUDIT ALL ON Usuario1.EMP BY SESSION; AUDIT DELETE ON Usuario1.EMP BY ACCESS;

Los registros de auditoría se pueden ver en la misma vista DBA_AUDIT_OBJECT anteriormente mencionada. 3.4. Protegiendo los registros de auditoría. Los registros de la tabla SYS.AUD$ pueden ser objeto de intentos de acceso para ser eliminados, ya que pueden reflejar acciones no autorizadas en la base de datos. Así, resulta interesante reflejar ese tipo de acciones. Esto se consigue con el siguiente comando: AUDIT ALL ON SYS.AUD$ BY ACCESS;

De este modo, cualquier acción contra la tabla SYS.AUD$ quedará registrada. Además, las acciones contra la tabla SYS.AUD$ sólo pueden ser borradas por los usuarios que puedan conectarse como INTERNAL.

Oracle /331