0% encontró este documento útil (0 votos)
4 vistas306 páginas

Curso Completo de Oracle SQL

Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
4 vistas306 páginas

Curso Completo de Oracle SQL

Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

SQL

Curso Oracle SQL

Formación Oracle SQL

Bloque Oracle SQL Básico


SQL

1/323

Índice

1. Diseño de bases de datos relacionales con Oracle 7


1.1. Posicionar las tecnologías de servidor 7
1.1.1. La Arquitectura del Servidor Oracle 7
1.1.2. Oracle WebLogic Server 9
1.1.3. Oracle Enterprise Manager 11
1.1.4. Cloud Computing 11
1.1.5. Herramientas y lenguajes de desarrollo 12
1.2. Entender estructuras relacionales 13
1.2.1. Escenarios reales 13
1.2.2. Modelado de datos 14
1.2.3. Entidades y relaciones 14
1.3. Resumir el lenguaje SQL 28
1.3.1. Estándares SQL 28
1.3.2. Comandos SQL 28
1.3.3. Un lenguaje orientado a un conjunto 29
1.4. Las herramientas cliente 30
1.4.1. SQL*Plus 30
1.4.2. SQL Developer 30
1.5. Crear los esquemas de demostración 31
1.5.1. Usuarios y esquemas (Schemas) 31
1.5.2. Los SCHEMAS RH y OE 32
1.6. Resumen 36

2. Recuperación de datos utilizando la sentencia SQL SELECT 37


2.1. Listar las posibilidades de la sentencia SQL SELECT 37
2.1.1. Introducción a la sentencia SQL SELECT 38
2.1.2. El comando DESCRIBE TABLE 38
2.1.3. Posibilidades de la sentencia SQL SELECT 40
2.2. Ejecutar una sentencia básica SQL SELECT 41
2.2.1. La sentencia primitiva SELECT 41
2.2.2. Reglas de sintaxis 46
2.2.3. Expresiones y operadores SQL 49
2.2.4. El concepto NULL 59
2.3. Resumen 64

3. Restringiendo y ordenando datos 65


3.1. Limitar los registros recuperados de una consulta (Query) 66
3.1.1. La cláusula WHERE 66

2/306
SQL

3.1.2. Operadores de Comparación 74


3.1.3. Operadores Booleanos 86
3.1.4. Reglas de prioridad 94
3.2. Ordenar los registros recuperados de una consulta 98
3.2.1. La clásula ORDER BY 98
3.3. Sustitución ampersand (&) 102
3.3.1. Variables de Sustitución 102
3.4. Definir y verificar 108
3.4.1. Los comandos DEFINE y UNDEFINE 108
3.4.2. El comando VERIFY 112
3.5. Resumen 113

4. Funciones de registro único 114


4.1. Descripción de los tipos de funciones disponibles en SQL 114
4.1.1. Definiendo una función 115
4.1.2. Tipos de funciones 118
4.2. Utilizar las funciones tipo carácter, numérico y fecha en sentencias SELECT 120
4.2.1. Utilizando funciones de conversión de mayúsculas/minúsculas 121
4.2.2. Utilizando funciones de manipulación de caracteres 124
4.2.3. Utilizando Funciones Numéricas 138
4.2.4. Trabajando con fechas 143
4.3. Resumen 155

5. Utilizando funciones de conversión y expresiones condicionales 156


5.1. Descripción de varios tipos de funciones de conversión disponibles en SQL 156
5.1.1. Funciones de conversión 156
5.2. Usando las funciones de conversión TO_CHAR, TO_NUMBER y TO_DATE - 1 159
5.2.1. Utilizando funciones de conversión 159
5.3. Aplicar expresiones condicionales en una sentencia SELECT 171
5.3.1. Funciones anidadas (Opcional) 171
5.3.2. Funciones generales 173
5.3.3. Funciones condicionales 179
5.4. Resumen 184
6. Presentando datos agregados utilizando funciones GROUP 185
6.1. Descripción de las funciones GROUP 185
6.1.1. Definición de las funciones GROUP 186
6.1.2. Tipos y sintaxis de las funciones GROUP 187
6.2. Indentificar las Funciones GROUP Disponibles 190
6.2.1. Utilizando funciones GROUP 190
6.2.2. Funciones GROUP anidadas 195
6.3. Agrupar datos usando la cláusula GROUP BY 196
6.3.1. Creando Grupos de Datos 197
6.3.2. La cláusula GROUP BY 198
6.3.3. Agrupando por múltiples columnas 200

3/306
SQL

6.4. Incluir o excluir registros agrupados usando la cláusula HAVING 202


6.4.1. Restringiendo Resultados Agrupados 202
6.4.2. La cláusula HAVING 204
6.5. Resumen 206

7. Visualización de datos de múltiples tablas 207


7.1. Sentencias SELECT para acceder a datos de más de una tabla utilizando EQUIJOINS y
NONEQUIJOINS 207
7.1.1. Tipos de JOIN 207
7.1.2. Uniendo tablas utilizando la sentencia ANSI SQL (Opcional) 212
7.1.3. Clasificando nombres de columna ambiguos 212
7.1.4. La cláusula NATURAL JOIN 214
7.1.5. La cláusula JOIN USING 216
7.1.6. La cláusula JOIN ON 218
7.1.7. JOIN de N-Tablas y condiciones adicionales del JOIN 220
7.1.8. NONEQUIJOINS 222
7.2. Unir una tabla a sí misma utilizando SELF-JOIN 224
7.2.1. Uniendo una tabla a sí misma utilizando la cláusula JOIN … ON 224
7.3. Visualización de datos que no cumplen una condición JOIN utilizando OUTER JOIN 226
7.3.1. Inner vs Outer joins 226
7.3.2. LEFT OUTER JOIN 227
7.3.3. RIGHT OUTER JOIN 228
7.3.4. FULL OUTER JOIN 230
7.4. Generar un producto cartesiano de dos o más tablas 232
7.4.1. Creando productos cartesianos usando CROSS JOIN 232
7.5. Resumen 235
8. Utilizando subconsultas para resolver problemas 236
8.1. Definir subconsultas (Subqueries) 236
8.2. Descripción de tipos de problemas que las subconsultas pueden resolver 237
8.2.1. Uso del resultado de una subconsulta para realizar comparaciones 237
8.2.2. Transformación en estrella 239
8.2.3. Generar tabla para hacer SELECT 240
8.2.4. Generar valores para proyección (Opcional) 241
8.2.5. Generar registros para enviarlos a una sentencia DML (Opcional) 241
8.3. Listar los tipos de subconsultas 242
8.3.1. Subconsultas de registro único y múltiples registros 242
8.3.2. Subconsultas correlacionadas 243
8.4. Escribir subconsultas de registro único y registros múltiples 245
8.5. Resumen 247
9. Utilizando los operadores de Conjunto 248
9.1. Descripción de los operadores de conjunto 248
9.1.1. Conjuntos y diagramas Venn 248
9.1.2. Principios generales de los operadores de conjunto 249
9.2. Utilizar un operador de conjunto para combinar múltiples consultas en una única consulta 250
9.2.1. El operador UNION ALL 251
9.2.2. El operador UNION 252

4/306
SQL

9.2.3. El operador INTERSECT 253


9.2.4. El operador MINUS 254
9.2.5. Ejemplos más complejos (Opcional) 254
9.3. Controlar el orden de los registros devueltos 257
9.4. Resumen 258
10. Manipulando datos 258
10.1. Describir cada Sentencia del Lenguaje de Manipulación de Datos (DML) 258
10.1.1. INSERT 259
10.1.2. UPDATE 260
10.1.3. DELETE 261
10.1.4. MERGE 261
10.1.5. TRUNCATE 262
10.1.1. Fallos de las Sentencias DML (Opcional) 262
10.2. Insertar registros en una tabla 266
10.3. Actualizar registros en una tabla 272
10.4. Borrar registros de una tabla 275
10.4.1. Borrando registros con DELETE (Opcional) 276
10.4.2. Borrando registros con TRUNCATE 277
10.4.3. MERGE (Opcional) 278
10.5. Control de transacciones 279
10.5.1. Transacciones de base de datos 279
10.5.2. Sentencias de control de transacciones 281
10.6. Resumen 286
11. Utilizando sentencias DDL para crear y administrar tablas 287
11.1. Clasificar los objetos principales de la base de datos 287
11.1.1. Tipos de objeto 288
11.1.2. Usuarios y Esquemas 290
11.1.3. Nombrando Objetos de Esquema 290
11.1.4. Espacio de nombres en los objetos 292
11.2. Revisión de la estructura de la tabla 292
11.3. Listar los tipos de datos disponibles por columna 293
11.4. Crear una tabla básica 295
11.4.1. Creación de tablas con especificaciones de columna 295
11.4.2. Creación de tablas utilizando subconsultas 297
11.4.3. Modificado de definiciones de tabla después de ser creada 298
11.4.4. Borrado y truncado de tablas 299
11.5. Explicación de cómo se crean las restricciones en el momento de creación de la tabla 300
11.5.1. Los tipos de restricciones 300
11.5.2. Definición de restricciones (constraints) 303
11.6. Resumen 305

5/306
SQL

6/306
SQL

1. Diseño de bases de datos relacionales con Oracle

1.1. Posicionar las tecnologías de servidor


Existe una familia de productos que conforman las tecnologías de servidores Oracle:

• La Base de Datos Oracle


• Oracle WebLogic Server
• Oracle Enterprise Manager
• Diferentes herramientas y lenguajes de desarrollo de aplicaciones
Cada uno de estos productos tiene una posición en el conjunto de productos de Oracle. La base de datos
es el repositorio de datos y el motor que gestiona el acceso a los mismos. Oracle WebLogic Server ejecuta
el software que genera las interfaces de usuario web que envían las llamadas para la recuperación y
modificación de los datos, para su ejecución. Oracle Enterprise Manager es una completa herramienta
de administración para la supervisión y gestión, ajusta los procesos de Oracle y también productos de
terceros.
Por último, existen herramientas y lenguajes para el desarrollo de aplicaciones, ya sean aplicaciones que
se ejecutan en los equipos de los usuarios finales en el modelo cliente-servidor o en las aplicaciones que
se ejecutan en servidores de aplicaciones.

La combinación de las tecnologías de servidor y las herramientas de desarrollo conforman una plataforma
para el desarrollo y entrega de aplicaciones que permite la nube. La nube es un enfoque para la entrega
de servicios de IT que maximiza la eficiencia de costes de todo el entorno mediante la entrega de potencia
de computación a partir de un conjunto de recursos disponibles, bajo demanda.

1.1.1. La Arquitectura del Servidor Oracle


Una base de datos Oracle es un conjunto de archivos en disco. Existe hasta que estos archivos son
deliberadamente borrados. No hay límites de tamaño o número de estos archivos y, por lo tanto, no hay
prácticamente límites de tamaño de una base de datos. El acceso a la base de datos se realiza a través
de la instancia Oracle. La instancia es un conjunto de procesos y estructuras de memoria; existe en la(s)
CPU(s) y en la memoria del nodo servidor y esta existencia es temporal, una instancia puede iniciarse y
detenerse. Los usuarios de la base de datos establecen conexión contra la instancia y a continuación,
ésta gestiona todos los accesos a la base de datos. En el entorno Oracle es absolutamente imposible
para cualquier usuario tener contacto directo con la base de datos. Una instancia de Oracle con una base
de datos Oracle conforma un servidor Oracle.
El modelo de procesado implementado por el servidor Oracle es el de cliente-servidor a menudo llamado
modelo dual. En el modelo cliente-servidor, la generación de la interfaz de usuario y gran parte de la
lógica de la aplicación está separada de la gestión de los datos. Para una aplicación desarrollada usando
SQL, esto significa que el cliente genera los comandos SQL y el servidor los ejecuta. Esta es la división
básica cliente-servidor, que por regla general cuenta con una red de área local entre los dos lados. El
protocolo de comunicaciones de la red utilizado entre el proceso de usuario y el proceso del servidor es
el protocolo propietario de Oracle, Oracle Net.

7/306
SQL

El cliente lo forman dos componentes: los usuarios y los procesos de usuario. El servidor tiene tres
componentes: los procesos del servidor que ejecutan el SQL, la instancia, y la propia base de datos.
Cada usuario interactúa con un proceso de usuario. Cada proceso de usuario interactúa con un proceso
del servidor, normalmente a través de una red de área local. Los procesos del servidor interactúan con
la instancia y la instancia con la base de datos. La siguiente figura muestra esta relación en forma de
diagrama.

Una sesión es un proceso de usuario en comunicación con un proceso servidor. Normalmente habrá un
proceso de usuario por usuario y un proceso de servidor por proceso de usuario. Los procesos de usuario
y servidor que conforman las sesiones se inician a petición de los usuarios y se terminan cuando ya no
son necesarias; este es el ciclo de conexión y desconexión. Los procesos de instancia y las estructuras
de memoria son lanzados por el administrador de la base de datos y persisten hasta que el administrador
los termina deliberadamente; éste es el ciclo de inicio y cierre de la base de datos.

El proceso de usuario puede ser cualquier software del lado del cliente que sea capaz de conectar a un
proceso de servidor Oracle. A lo largo de este libro se utilizarán dos procesos de usuario extensivamente:
SQL* Plus y SQL Developer. Estos son procesos sencillos proporcionados por Oracle para establecer
sesiones contra un servidor Oracle y emitir SQL ad hoc. La alternativa más utilizada es TOAD (la
herramienta para desarrolladores de aplicaciones) de Quest Software, aunque se trata de software con
licencia. Las aplicaciones de usuario final tendrán que ser más sofisticadas que estas herramientas, algo
capaz de gestionar las ventanas, menús, diálogos adecuados en pantalla, etc. Dicha aplicación puede ser
escrita con las Herramientas de Desarrollo de Oracle, con Microsoft Access enlazado a los drivers ODBC
de Oracle, con cualquier lenguaje de tercera generación (como C o Java) para el que Oracle haya
proporcionado una librería de funciones que le permita interactuar con el servidor, o con cualquier número
de herramientas de terceros compatibles con Oracle. Lo que realmente es el proceso de usuario no le
importa en absoluto al servidor Oracle. Cuando un usuario final rellena un formulario y pulsa el botón
Enviar, el proceso de usuario generará una sentencia INSERT (detallada en el Capítulo 10) y la enviará a
un proceso del servidor para su ejecución contra la instancia y la base de datos. En lo que concierne al
servidor, la sentencia INSERT podría haber sido escrita en SQL* Plus, como lo que se conoce como SQL
ad hoc.

Toda comunicación con un servidor Oracle sigue este modelo cliente-servidor. La separación del código
de usuario del código del servidor se remonta a las primeras versiones de la base de datos y es inevitable,
incluso si el proceso de usuario se está ejecutando en la misma máquina que el servidor, la división
cliente-servidor se sigue aplicando. Las aplicaciones que se ejecutan en un entorno de servidor de
aplicaciones (descrito en la siguiente sección) también siguen el modelo cliente-servidor para su acceso
a la base de datos.

8/306
SQL

La forma más simple del servidor de base de datos es una instancia conectada a una base de datos, pero
en un entorno más complejo una base de datos puede ser abierta por muchas instancias
simultáneamente. Esto se conoce como RAC (Real Application Cluster). RAC puede traer muchos
beneficios potenciales, que pueden incluir escalabilidad, rendimiento y tiempos de inactividad cero. La
capacidad de añadir dinámicamente más instancias que se ejecutan en nodos suplementarios a una base
de datos es una parte importante de la contribución de la base de datos a la nube.

1.1.2. Oracle WebLogic Server


Con el surgimiento de la Web como la plataforma de comunicaciones estándar para la entrega de
aplicaciones a los usuarios finales, ha surgido la necesidad de servidores de aplicaciones. Un servidor de
aplicaciones sustituye al software del lado del cliente instalado tradicionalmente en los terminales de los
usuarios finales; ejecuta las aplicaciones de forma centralizada, presentándolas a los usuarios en
ventanas que se muestran localmente en los navegadores web. Las aplicaciones utilizan datos
almacenados en uno o más servidores de base de datos.

Oracle WebLogic Server es una plataforma para el desarrollo, la implantación y la gestión de aplicaciones
web. Una aplicación web puede definirse como cualquier aplicación con la que los usuarios se comunican
mediante HTTP. Las aplicaciones Web normalmente se ejecutan en al menos tres niveles: un nivel de
base de datos gestiona el acceso a los datos, el nivel de cliente (a menudo implementado como un
navegador web) maneja la gestión de ventanas locales para las comunicaciones con los usuarios, y un
nivel de aplicación en medio ejecuta la lógica del programa que genera la interfaz de usuario y las
llamadas SQL a la base de datos.

Las aplicaciones web se pueden desarrollar con diversas tecnologías, entre las que predomina Java. Las
aplicaciones escritas en Java deben cumplir con el estándar Java EE (Java Enterprise Edition), que define
cómo deben empaquetarse e implementarse dichas aplicaciones. JEE y los estándares relacionados son
controlados por Oracle y aceptados por prácticamente todos los desarrolladores de software. Oracle
WebLogic Server es un servidor de aplicaciones compatible con JEE. La implementación de los estándares
por parte de Oracle permite el equilibrio de carga automático y la tolerancia a fallos en múltiples
servidores de aplicaciones en múltiples máquinas a través de la agrupación JEE. La agrupación en cluster
virtualiza la prestación del servicio de aplicaciones; los usuarios solicitan una aplicación que puede estar
disponible en varias ubicaciones y el cluster funciona desde donde mejor se puede prestar servicio a
cualquier sesión o solicitud. Si una ubicación falla, otras asumirán la carga y se podrán poner más
recursos a disposición de una aplicación según sea necesario. La capacidad de separar la solicitud de un
servicio de la ubicación de su prestación y de añadir o eliminar servidores JEE de un cluster de forma
dinámica es una parte importante de la contribución de Oracle WebLogic Server a la nube.

Es importante señalar que el compromiso de Oracle con los estándares internacionales es muy fuerte.
Las aplicaciones que se ejecutan en el entorno Oracle WebLogic Server pueden conectarse a cualquier
base de datos para la que existan controladores compatibles con Java, no es necesario utilizar una base
de datos Oracle. Las aplicaciones desarrolladas con las herramientas de Oracle WebLogic Server se
pueden implementar en cualquier servidor de aplicaciones de terceros compatible con JEE.

El modelo de procesamiento más simple de las aplicaciones web es de tres niveles: un nivel de cliente
que gestiona la interfaz de usuario, un nivel medio que genera la interfaz y emite sentencias SQL al nivel
de datos, y un nivel de datos que gestiona los propios datos. En el entorno Oracle, el nivel de cliente
será un navegador (como Mozilla Firefox o Microsoft Internet Explorer) que se encarga de la gestión local
de ventanas, controla el teclado y realiza un seguimiento de los movimientos del ratón. El nivel medio

9/306
SQL

será un Oracle WebLogic Server ejecutando el software (probablemente escrito en Java) que genera las
ventanas enviadas al nivel de cliente para su visualización y las sentencias SQL enviadas al nivel de datos
para su ejecución. El nivel de datos será un servidor Oracle: una instancia y una base de datos. En este
entorno de tres niveles, hay dos tipos de sesiones: sesiones de usuario final desde el nivel del cliente
hasta el nivel medio, y sesiones de base de datos desde el nivel medio hasta el nivel de datos. Las
sesiones de usuario final se establecerán con HTTP. Las sesiones de base de datos son sesiones cliente-
servidor que consisten en un proceso de usuario y un proceso de servidor, como se describe en la sección
anterior.

Es posible que una aplicación utilice un mapeo uno a uno de sesión de usuario final a sesión de base de
datos: cada usuario, desde su navegador, establecerá una sesión contra el servidor de aplicación, y el
servidor de aplicación establecerá entonces una sesión contra el servidor de base de datos en nombre
del usuario. Sin embargo, este modelo ha demostrado ser muy ineficiente en comparación con el modelo
de pool de conexiones. Con el pool de conexiones, el servidor de aplicaciones establece un número
relativamente pequeño de sesiones de base de datos persistentes y las pone a disposición de un número
relativamente grande de sesiones de usuario final en el servidor de aplicaciones. La Figura anterior ilustra
la arquitectura de tres niveles usando el pool de conexiones. Desde el punto de vista de la base de datos,
no importa si una sentencia SQL proviene de un proceso del lado del cliente como SQL* Plus o Microsoft
Access o de una sesión combinada a un servidor de aplicaciones. En el primer caso, todo el proceso de
usuario ocurre en una máquina; en el segundo, el proceso de usuario se ha dividido en dos niveles: un
nivel de aplicación que genera la interfaz de usuario y un nivel de cliente que la muestra.

10/306
SQL

1.1.3. Oracle Enterprise Manager


El tamaño y la complejidad cada vez mayores de las instalaciones de IT hacen que la gestión sea una
tarea difícil. Esto no es de extrañar, nadie ha dicho nunca que la gestión de un entorno complejo deba
ser necesariamente sencilla. Sin embargo, las herramientas de gestión pueden facilitar la tarea y
aumentar la productividad del personal administrativo.

Oracle Enterprise Manager viene en tres formas:

• Database Express
• Fusion Middleware Control
• Control de la nube
Oracle Enterprise Manager Database Express es una herramienta gráfica para administrar una base de
datos, que puede ser una base de datos en clúster RAC. Consiste en un proceso Java que se ejecuta en
el equipo servidor de la base de datos. Los administradores se conectan a Database Express desde un
navegador y Database Express se conecta al servidor de base de datos. Database Express tiene un buen
desempeño en campos como la administración en tiempo real, monitoreo de desempeño y ejecución de
trabajos programados.
Oracle Enterprise Manager Fusion Middleware Control es una herramienta gráfica para gestionar la
implementación de Fusion Middleware. Estas implementaciones suelen incluir Oracle WebLogic Server,
un servidor de aplicaciones líder del sector que proporciona las máquinas virtuales de Java (JVM) que
alojan aplicaciones Oracle o Java personalizadas.

Oracle Enterprise Manager Cloud Control globaliza el entorno de gestión. Un repositorio de gestión
(residente en una base de datos Oracle) y uno o varios servidores de gestión gestionan el entorno
completo: todas las bases de datos y servidores de aplicaciones, estén donde estén. Cloud Control
también puede gestionar los nodos, o equipos, en los que se ejecutan los servidores. Cada nodo
gestionado ejecuta un proceso de agente, que es responsable de supervisar el objetivo gestionado en el
nodo, la ejecución de trabajos contra él y la generación de informes sobre el estado, los niveles de
actividad y las condiciones de alerta al servidor o servidores de gestión.
Cloud Control proporciona una visión holística del entorno y si está bien configurado, hace que el personal
de administración sea mucho más productivo de lo que es sin él. Es posible que un administrador gestione
de forma eficaz cientos de objetivos.

1.1.4. Cloud Computing


La virtualización de servicios es fundamental para el concepto de cloud computing. Esto significa que en
todos los niveles hay una capa de abstracción entre lo que se solicita y lo que se proporciona. Los usuarios
finales solicitan un servicio de aplicaciones y dejan que la nube determine qué servidor de aplicaciones
JEE en clúster puede proporcionarlo mejor. Los servidores de aplicaciones solicitan un servicio de base
de datos y dejan que la nube determine desde qué nodo RAC se pueden servir mejor los datos. Dentro
de la nube hay un mapeo de los posibles servicios a los proveedores de servicios disponibles, y hay
algoritmos para asignar la carga de trabajo y recursos apropiadamente. El resultado es que los usuarios
finales no tienen la necesidad ni la capacidad de saber desde dónde se están proporcionando realmente
sus recursos informáticos. La analogía que a menudo se dibuja es con la entrega de electricidad
doméstica: se suministra bajo demanda y el propietario de la vivienda no tiene forma de saber qué
central eléctrica le está suministrando actualmente.

11/306
SQL

La nube no es exclusiva de Oracle. A nivel físico, algunos proveedores de sistemas operativos y hardware
están proporcionando capacidades similares a las de la nube. Éstos incluyen la capacidad de ‘particionar’
servidores en máquinas virtuales y agregar o quitar dinámicamente CPU(s) y RAM de las máquinas
virtuales según la demanda. Esto es conceptualmente similar al enfoque de Oracle de asignar
dinámicamente los recursos del servidor de aplicaciones y del servidor de bases de datos a los servicios
lógicos. No hay ninguna razón por la que los dos enfoques no puedan combinarse. Ambos están
trabajando hacia la misma meta y pueden trabajar juntos. El resultado debería ser un entorno en el que
siempre se dispusiera de recursos adecuados a petición, sin tener que hacer frente a problemas de exceso
de capacidad en algunos momentos y de rendimiento insuficiente en otros. También debería ser posible
diseñar un entorno de nube sin un único punto de fallo, logrando así el objetivo de un tiempo de actividad
del 100% que está siendo demandado por muchos usuarios.
El desarrollador de aplicaciones SQL no necesita saber cómo se ha implementado la nube, el SQL será
invocado desde un servidor de aplicaciones y ejecutado por una instancia de una base de datos; la nube
se encargará de que en todo momento se disponga de pools de servidores de aplicaciones e instancias
del tamaño adecuado para la carga de trabajo actual.

1.1.5. Herramientas y lenguajes de desarrollo


Las tecnologías de servidor Oracle incluyen varias utilidades para el desarrollo de aplicaciones, algunas
existentes dentro de la base de datos, otras externas a ella.

Dentro de la base de datos, es posible utilizar tres idiomas. El que es inevitable, y el tema de este curso,
es SQL. SQL se utiliza para el acceso a los datos, pero no es suficiente para el desarrollo de aplicaciones
completas. No tiene facilidades reales para desarrollar interfaces de usuario, y también carece de las
estructuras procesales necesarias para manipular filas individualmente. Los otros dos idiomas disponibles
en la base de datos colman estas lagunas. Son PL/SQL y Java, aunque también se puede utilizar Java
fuera de la base de datos. PL/SQL es un lenguaje de tercera generación (3GL) propiedad de Oracle. Tiene
las construcciones procesales usuales (como if-then-else y looping) y facilidades para el diseño de la
interfaz de usuario. En el código PL/SQL, se pueden incrustar llamadas a SQL. Por lo tanto, una aplicación
PL/SQL puede usar SQL para recuperar una o más filas de la base de datos, luego realizar varias acciones
basadas en su contenido, y luego emitir más SQL para volver a escribir filas en la base de datos. Java
ofrece una capacidad similar para incrustar llamadas SQL dentro del código Java.

Se trata de una tecnología estándar del sector: cualquier programador Java debería ser capaz de escribir
código que funcione con una base de datos Oracle (o con cualquier otra base de datos compatible con
Java).
Otros lenguajes están disponibles para el desarrollo de aplicaciones cliente-servidor que se ejecutan
externamente a la base de datos. Los más utilizados son C y Java, pero es posible utilizar la mayoría de
los 3GL convencionales. Para todos estos lenguajes, Oracle Corporation proporciona bibliotecas OCI
(Oracle Call Interface) que permiten que el código escrito establezca sesiones contra una base de datos
Oracle e invoque comandos SQL. Muchas organizaciones no querrán usar un 3GL para desarrollar
aplicaciones de bases de datos. Oracle Corporation proporciona herramientas de desarrollo rápido de
aplicaciones como Oracle Application Express o JDeveloper. Esto puede hacer que los programadores
sean mucho más productivos que si trabajaran con un 3GL. Al igual que los lenguajes, todas estas
herramientas de desarrollo de aplicaciones terminan haciendo lo mismo: construir sentencias SQL que
se envían al servidor de base de datos para su ejecución.

12/306
SQL

1.2. Entender estructuras relacionales


Se introducen varios escenarios de organización de datos del mundo real para discutir el paradigma
relacional e introducir algunas técnicas prácticas de modelado. Un punto crítico en la comprensión de
SQL es una comprensión del paradigma relacional y la capacidad de normalizar datos en estructuras
relacionales. La normalización es el trabajo de los analistas de sistemas, ya que modelan los datos de
negocio en una forma adecuada para su almacenamiento en tablas relacionales. Es una ciencia que puede
ser estudiada durante años, y hay muchas escuelas de pensamiento que han desarrollado sus propios
métodos y notaciones.

1.2.1. Escenarios reales


Este curso utiliza varios escenarios hipotéticos, incluyendo los dos escenarios encapsulados llamados HR
y OE proporcionados por Oracle. Los siguientes escenarios evolucionarán aún más a medida que se
discutan nuevos conceptos.

Concesionario de automóviles
Sid dirige un concesionario de coches y necesita un sistema para hacer un seguimiento de los coches
que compra y vende. Ha notado que la empresa se está hundiendo y quiere entrar en el siglo XXI y crear
un sitio web que anuncie las existencias disponibles. Necesita un sistema para llevar un registro de los
coches que ha comprado y vendido y los detalles de estas transacciones.

Núcleos geológicos
Se han recogido muestras de tierra por la agencia local de estudios geológicos. Para asegurar el rigor
científico, los desarrolladores de GeoCore han determinado que el sistema debe rastrear la ubicación
geográfica exacta, el contenido elemental de las muestras, y las fechas de recogida.

Entrada de pedidos

El escenario de Entrada de pedidos (OE) proporcionado como ejemplo por Oracle contiene información
para un sistema comercial ficticio que realiza un seguimiento de productos, clientes y pedidos de cliente
que han sido realizados.

Recursos humanos
El escenario de Recursos Humanos (HR) proporcionado como ejemplo por los registros de Oracle contiene
información de empleados, departamentos, oficinas e información relacionada con el trabajo para un
típico Departamento de RRHH.
Aunque los escenarios hipotéticos descritos anteriormente varían en complejidad, comparten varias
características, incluyendo un crecimiento potencial de los datos que, eventualmente, puede sobrepasar
a una solución de organización de datos basada en papel o en hojas de cálculo, así como un requisito
para que los datos sean manipulados (insertados, actualizados y borrados) y recuperados de manera
eficiente. El reto de producir un diseño de organización de datos eficiente (también conocido como un
modelo de datos) puede ser superado tanto con una comprensión de cómo es probable que se utilicen
los datos que se están organizando y algunas técnicas básicas de modelado de datos. El objetivo es

13/306
SQL

lograr un equilibrio óptimo entre almacenamiento de datos y acceso a los mismos, lo que supondrá un
ahorro a largo plazo en los costes y posteriores beneficios.

1.2.2. Modelado de datos


Existen varios enfoques de modelado de datos formalizados, como el Zachman y el Proceso Unificado
Racional, que en última instancia buscan proporcionar un enfoque sistemático y basado en estándares
para representar objetos en una empresa. Hay una multitud de anotaciones disponibles para las
entidades del modelo y sus relaciones. La notación popular adoptada por Oracle en sus herramientas
CASE (Computer Aided Software Engineering) y más recientemente en SQL Developer es la notación de
pata de gallo, que se discutirá más adelante. Otras notaciones, como la notación de esquemas
relacionales y UML (Universal Markup Language), también son populares, pero se debe elegir la notación
más cómoda para uno mismo.
El modelo lógico está basado en conceptualizar objetos de interés como entidades y sus interacciones
con cada otro como relaciones. Hay muchos enfoques de los diagramas entidad-relación, cada uno con
sus beneficios y sus limitaciones. A continuación, se presenta un breve análisis de los diagramas de
entidad-relación y su notación.

1.2.3. Entidades y relaciones


Muchos profesionales de Oracle han adoptado un marco de trabajo que consiste en tres etapas para el
modelado de bases de datos relacionales. Un modelo lógico se concibe cuando estructuras de alto nivel
llamadas entidades, que comprenden varios atributos y sus relaciones, son representadas juntas en un
diagrama. Las entidades en los modelos lógicos suelen representarse como rectángulos con esquinas
redondeadas, que comprenden atributos o identificadores a veces marcadas con una "o". Los atributos
que identifican una instancia de una entidad se designan como claves primarias y a veces se indican con
"#*". La tipología de datos puede hacerse en esta etapa, pero generalmente no se refleja en el diseño.
El modelo lógico se convierte entonces en un modelo relacional mediante la traducción de entidades en
relaciones, comúnmente referidas como tablas. La idea aquí es que los conjuntos de las instancias de las
entidades se modelan colectivamente como una tabla. Los atributos se convierten en columnas de la
tabla. Cada instancia de una entidad se refleja como una tupla o fila de datos (registro), cada uno con
valores para sus diferentes atributos o columnas. El número de registros de una tabla es la "cardinalidad
de las tuplas". Normalmente los atributos que son únicos para cada fila se denominan claves únicas, y
normalmente se elige una clave única para ser la clave primaria (que se discutirá más adelante). Las
relaciones entre las entidades son a menudo modeladas como claves externas, que también se
explorarán más adelante.

Las relaciones en los modelos relacionales suelen representarse como rectángulos. En esta etapa se dan
más detalles en términos de tipificación de datos para los atributos y los atributos de la clave primaria y
externa se reflejan respectivamente con una "P" y una "F" en el modelo relacional. Finalmente, el modelo
relacional se convierte en un modelo físico mediante la implementación del diseño en una base de datos
relacional.

La notación “pata de gallo” es un estilo que se utiliza a menudo para representar relaciones en modelos
de datos lógicos y relacionales, ya que muestra tanto la cardinalidad máxima como la mínima en un
formato fácil de leer. A continuación, se verán las relaciones entre entidades y serán exploradas en el
contexto del Concesionario de automóviles.

- 1:N One-to-many
- N:1 Many-to-one

14/306
SQL

- 1:1 One-to-one
- M:N Many-to-many

Se considera el escenario del concesionario de Sid presentado anteriormente. Se podría modelar los
datos probables como una entidad que consiste en los siguientes atributos relacionados con el automóvil:
Fabricación, Modelo, Capacidad del motor y Color. También es necesaria información sobre la
compraventa de los coches, por lo que se podría añadir Fecha de compra, Fecha de venta, Nombre del
vendedor, SSN (Número de Seguro Social) , Compañía Vendedora, los mismos detalles para el comprador,
y finalmente el Precio de Compra y el Precio de Venta, como en la tabla siguiente:

CarDealership

Make
Model
Engine Capacity
Color
Purchase Date
Sold Date
Sellers Name
Seller_SSN

Sellers_Company
Buyers Name
Buyers_SSN
Buyers Company
Selling price
Purchase price

Los datos transaccionales de muestra almacenados en una tabla basada en esta entidad pueden tener el
aspecto que se observa a continuación: tres filas de datos en una tabla de catorce columnas llamado
CAR_DEALERSHIP. Los comandos para crear tablas y rellenarlas con los datos serán discutidos más
adelante en este curso. Por ahora, hay aspectos fundamentales que hay que tener en cuenta. Las tablas
almacenan datos en filas, también llamados registros. Cada dato se encuentra en la intersección de una
fila y una columna, también llamada celda. Es bastante intuitiva y muy parecida a una hoja de cálculo.

Los dos primeros registros de la tabla CAR_DEALERSHIP incluyen la siguiente información:


- Un Mercedes A160 plateado con un motor de 1600cc que pertenecía a Coda, un vendedor privado
con SSN 12345, fue comprado por Sid con SSN 12346 con su compañía Sid´s Cars por 10,000$ el 1
de Junio de 2013.

15/306
SQL

- Un Mercedes A160 plateado con un motor de 1600cc que pertenecía a Sid, un con Customer_SSN
12346, fue comprado por Wags con Customer_SSN 12347 con su compañía Wag´s Auto por 12,000$
el 1 de Agosto de 2013.

Observar la repetición de los datos. Cada registro contiene información duplicada de los coches que se
compran o venden y del cliente que los compra o vende. La duplicación innecesaria de datos por lo
general indica un diseño deficiente, ya que es un desperdicio y una pérdida de tiempo y a menudo
requiere un mantenimiento innecesario. Si el mantenimiento no se hace con cuidado, este diseño
permitirá que los errores (a menudo denominados anomalías de inserción, modificación y borrado) se
introduzcan, sin que nadie se percate de ello, y reduzcan la integridad general de los datos.

La normalización de la base de datos se refiere al modelado de datos usando múltiples entidades con
relaciones entre ellos, lo que puede reducir o eliminar por completo la redundancia de datos. Hay muchos
tipos de formas normalizadas que han sido definidas teóricamente, pero el diseño de bases de datos
relacionales se centra principalmente en las tres siguientes:

16/306
SQL

- La primera forma (1NF) se ocupa de la eliminación de grupos de datos que se repiten


innecesariamente. Un ejemplo de un grupo de repetición en la figura anterior serían las primeras
cuatro columnas de los dos primeros registros, en las que en el texto descriptivo se repite la
información sobre el coche. Podría definirse una nueva entidad Cars que identifica de forma única un
coche específico utilizando el atributo clave primaria Car ID, así como los atributos Make, Model,
Engine Capacity y Color. El ID del Coche se utiliza entonces en la entidad relacionada Transacciones
para evitar la repetición de grupos de datos.
- La segunda forma (2NF) elimina los atributos de la entidad en 1NF que no dependen de la clave
primaria. En la entidad Cars propuesta descrita anteriormente, el atributo Color no depende de un
coche específico. Puede definir una nueva entidad Colors que identifique de forma única un color
específico utilizando el atributo de clave primaria Color ID. El Color ID puede ser referenciado por la
entidad Cars. - La tercera forma (3NF) elimina todos los atributos interdependientes de una entidad
en 2NF. Los compradores y vendedores de automóviles tienen cada uno un número de Seguro Social
(Customer_SSN ) de identificación única. Sus nombres, sin embargo, son interdependientes en el
atributo Customer_SSN . Podría definir una nueva entidad de Cliente que identificara únicamente a

17/306
SQL

un cliente utilizando el atributo de clave primaria de Client ID, donde la información como el nombre
del cliente y la empresa se almacenara.

A menudo hay varios modelos normalizados posibles para una aplicación. Es importante
utilizar el más apropiado. Si el analista de sistemas se equivoca, las implicaciones pueden ser
serias para el rendimiento, las necesidades de almacenamiento y el esfuerzo de desarrollo.
Nota: En el contexto de la optimización del rendimiento, es intencional y aceptable duplicar
datos en entidades. Cuando los datos se normalizan a través de múltiples entidades
instanciadas como múltiples tablas que deben unirse, los procesos del servidor Oracle
necesitan obtener físicamente datos de múltiples tablas y unirlas en un espacio de memoria
para producir el conjunto de resultados requerido. El IO adicional requerido para consultar o
manipular datos normalizados a veces justifica la desnormalización de los datos para reducir
las operaciones de E/S de disco y, por lo tanto, aumentar el rendimiento. Este es común en
Data Warehouse (DWH) y Sistemas de Soporte de Decisiones (DSS) pero es una excepción a
la regla en los sistemas de Procesamiento de Transacciones Online (OLTP).

Considérese el modelo de datos lógico en la siguiente imagen. Los datos relacionados con el coche se
han modelado como la entidad Cars. La información del cliente (compradores y vendedores) es
esencialmente la misma, por lo que han sido modelados como la entidad Customers con el atributo tipo
de Customer para diferenciar entre Compradores y Vendedores. Las ventas y compras se registran en la
entidad Transactions, mientras que una entidad de búsqueda llamada Colors realiza un seguimiento de
los diferentes colores.

Hay varias ventajas en conceptualizar este diseño como cuatro entidades interrelacionadas. En primer
lugar, los datos se han normalizado y no hay duplicidad de datos.

Una ventaja práctica de las entidades múltiples radica en que cada una sigue una sola construcción como
coches, clientes, colores, e incluso transacciones, lo que facilita el mantenimiento de los datos. Se pueden
añadir nuevos colores, cada uno con un código único, y a medida que se compran coches nuevos, estos
colores definidos y mantenidos en un solo lugar se pueden utilizar para describir varios coches con el
mismo color. Se puede mejorar la sofisticación de este modelo definiendo entidades para neumáticos,
sistemas de seguridad, dispositivos de seguimiento o complementos audiovisuales. También se podría
aumentar los detalles recogidos para cada coche, como el número de bastidor y el número de motor; o
para cada cliente, como la dirección y los datos bancarios. Pero este escenario hipotético sirve para
ilustrar varios conceptos y obviamente, no se puede utilizar en un escenario de aplicación de producción
sin más ampliaciones.

18/306
SQL

a. Claves primarias
Cada entidad en la anterior figura tiene un atributo ‘clave primaria’ que identifica de manera única una
tupla o registro denotado por un #* adyacente al nombre del atributo. Cada valor de la clave primaria
Car ID es única en la entidad. Las líneas múltiples no pueden compartir el mismo valor de clave primaria.
Del mismo modo, el Color ID identifica de forma unívoca cada registro de la entidad Colores, al igual que
el Customer ID y el TX ID de las entidades Clientes y Transacciones, respectivamente.

b. Relaciones
Las líneas de la figura anterior que enlazan las diversas entidades se conocen como relaciones. La
notación de “pata de gallo” expresa la cardinalidad de las relaciones entre las entidades uno a uno, uno
a muchos, muchos a uno y muchos a muchos. Esta notación ilustra explícitamente la entidad con el lado
“many” de la relación con múltiples "patas", mientras que la entidad del lado “one” tiene una “pata”. Los
atributos en una relación one-to-one son idénticos, mientras que las relaciones de many-to-many indican
las tuplas múltiples en la entidad A que tienen los mismos valores de atributo que otras tuplas en la

19/306
SQL

entidad B. Tanto las relaciones de one-to-one como las de many-to-many no son muy comunes y a veces
señalan defectos en el modelo relacional. Las relaciones one-to-many y many-to-one ocurren
frecuentemente cuando se modelan entidades relacionales y relacionan atributos en dos entidades en
una relación master-detail. Desde el punto de vista de la relación entre las entidades Cars y Colors (el
orden es significativo), por ejemplo, muchos registros en la entidad Cars tendrán un Color. Muchos coches
podrían tener el mismo atributo de identificación de un solo color que indica que son del mismo color. La
entidad Colors es la entidad maestra o entidad de búsqueda, mientras que la entidad Cars es la entidad
de detalle en este caso. Desde el punto de vista de la relación entre Colors y Cars, un color puede ser
asociado con muchos coches. Así que es sólo una cuestión de perspectiva si una relación es de uno a
muchos o de muchos a uno; todo depende de cuál sea la dirección de la relación que se considere. Las
otras relaciones indicadas por este método muestran que un solo coche puede ser comprado y vendido
varias veces, de ahí la relación uno a muchos entre las entidades Cars y Transactions y, por último, que
un cliente puede realizar muchas transacciones (como comprar y vender muchos coches).

c. Integridad referencial y claves ajenas


Estas relaciones introducen el concepto de integridad referencial, que asegura la consistencia e integridad
de los datos al garantizar que un atributo A perteneciente a la entidad del lado “one” de la relación debe
ser único, mientras que el atributo B de la entidad por el lado “many” debe tener un valor que se
encuentra en el conjunto de valores únicos descritos por el atributo A. El atributo B se denomina clave
externa ya que tiene una dependencia referencial del atributo A.

Se considera la relación Colors-Cars basada en el atributo Color_ID. La integridad referencial asegura


que el atributo Color_ID en cada registro de la entidad Cars debe tener un valor que sea exactamente
idéntico a una instancia del atributo Color_ID en la entidad Colors. Esta garantía es fundamental para el
modelado relacional, ya que la unión de las entidades Cars y Colors en el atributo Color_ID permite que
el atributo [Link] (es decir, la notación por puntos) coincida con un registro relacionado en la
entidad Cars. El atributo Color_ID en la entidad Cars es la clave externa que está relacionada con la clave
única, y, además, es el atributo Color_ID en la entidad Colors, donde también resulta ser la clave
primaria. Es muy común que las claves ajenas en una entidad se basen en claves primarias en una
entidad relacionada, pero ésta no es la regla.

Nota: Las claves externas en una entidad se basan en claves únicas en una entidad relacionada, pero
esas claves únicas no tienen que ser la clave primaria; sólo tienen que ser únicas.

20/306
SQL

El modelo lógico de la figura anterior, normalmente evolucionaría hacia un modelo relacional con más
detalles de tipología de datos y claves primarias y ajenas más claras, como en la figura mostrada a
continuación.

21/306
SQL

Los datos de la muestra transferidos al modelo físico, construido a partir del modelo relacional anterior,
producirían cuatro conjuntos de datos, como en la figura siguiente.

22/306
SQL

Las dos primeras líneas de datos en el conjunto de datos Transactions se pueden interpretar como se
indica a continuación:
- Una transacción con TX_ID 100 describe la compra de un automóvil con Car_ID 1 por parte del
concesionario de Sid a un cliente con Customer_ID 2 por 10.000 dólares el 1 de junio de 2013. Se busca
el cliente con Customer_ID 2 y se observa que fue una venta de Coda, un vendedor privado con
Customer_SSN 12345. Si se busca el automóvil con Car_ID 1, se determina que se trataba de un
Mercedes del 2001- A160 con Color_ID 1.
- Una transacción con TX_ID 101 describe la venta de un automóvil con Car_ID 1 a Customer_ID 4, que
se observa que es un vendedor llamado Wags del concesionario Wags Auto con Customer_SSN 12347,
por 12.000 dólares el 1 de agosto de 2013.

Basándose en las descripciones anteriores ofrecidas en un diseño de entidad única, no se ha perdido


nada al organizar los datos de la muestra a través del diseño de cuatro entidades. Sin embargo, se ha
ganado mucho. No hay duplicidad de datos. Hay una claridad y elegancia que facilitará el mantenimiento
de estos datos a medida que se compren y vendan coches nuevos y a medida que los nuevos clientes
realicen transacciones con el concesionario de Sid.

d. Registros y tablas
El paradigma relacional modela los datos como tablas bidimensionales. Una tabla consta de varios
registros, cada uno de las cuales consta de un conjunto de columnas. Dentro de una tabla, todos los

23/306
SQL

registros tienen la misma estructura de columnas, aunque es posible que algunas columnas se
encuentren vacías. Un ejemplo de una tabla sería una lista de los empleados; cada empleado está
representado por un registro. Las columnas pueden ser: el número de empleado, el nombre y un código
para el departamento en el que trabaja el empleado. Cualquier empleado que no esté actualmente
asignado a un departamento tendrá esa columna en blanco. Otra tabla podría representar los
departamentos: un registro por departamento con columnas para el código del departamento y el nombre
del departamento.

Las tablas relacionales se ajustan a ciertas reglas que limitan y definen los datos. En el nivel de columna,
cada columna debe ser de un determinado tipo de datos, como numérico, fecha/hora o carácter. El tipo
de datos de carácter es el más general, en el sentido de que puede aceptar cualquier tipo de datos. A
nivel de registro, normalmente cada uno debe tener alguna característica identificativa: podría ser el
valor de una columna, como el número de empleado y número de departamento en los ejemplos
anteriores, que no pueden repetirse en otros registros. También puede haber reglas que definan los
vínculos entre las tablas, como la regla de que a cada empleado se le debe asignar un código de
departamento que puede coincidir con un registro de la tabla de departamentos.

Tabla DEPT

Nombre de la Descripción Tipo de datos Longitud


columna

DEPTNO Número de Numérico 2


departamento

DNAME Nombre del Carácter 14


departamento

Tabla EMP

Nombre de la Descripción Tipo de datos Longitud


columna

EMPNO Número de Numérico 4


empleado

ENAME Nombre del Carácter 10


empleado

DEPTNO Número de Numérico 2


departamento

Datos para la tabla DEPT

24/306
SQL

DEPTNO DNAME

10 ACCOUNTING

20 RESEARCH

30 SALES

40 OPERATIONS

Datos para la tabla EMP

EMPNO ENAME DEPTNO

7369 SMITH 20

7499 ALLEN 30

7521 WARD 30

7566 JONES 20

7654 MARTIN 30

7698 BLAKE 30

7782 CLARK 10

7788 SCOTT 20

Si se examinan las tablas DEPT y EMP, la estructura bidimensional es clara. Cada registro es de longitud
fija, cada columna es de longitud fija, y los registros se delimitan con una nueva línea. Los cambios en

25/306
SQL

los datos suelen ser muy eficientes con el modelo relacional. Se pueden añadir nuevos empleados, o
pueden ser movidos de un departamento a otro simplemente modificando el valor DEPTNO en su registro.
Se considera una estructura alternativa, en la que los datos se almacenan según el paradigma jerárquico.
El modelo jerárquico se desarrolló antes que el relacional por razones tecnológicas. Al principio, los
dispositivos de almacenamiento carecían de la capacidad de mantener los archivos separados que se
necesitaban para las tablas relacionales. Téngase en cuenta que este problema se evita en la base de
datos de Oracle mediante abstracción del almacenamiento físico (ficheros) del almacenamiento lógico
(tablas): no hay conexión directa entre tablas y archivos y menos un mapeo one-to-one. En efecto,
muchas tablas se pueden almacenar en muy pocos archivos.

Una estructura jerárquica almacena todos los datos relacionados en una unidad. Por ejemplo, el registro
de un departamento incluiría todos los empleados de ese departamento. El paradigma jerárquico puede
ser muy rápido y muy eficiente en cuanto al espacio. Un acceso a un archivo puede ser todo lo que se
necesita para recuperar todos los datos necesarios para satisfacer una consulta. Los empleados y
departamentos listados anteriormente podrían almacenarse jerárquicamente como se indica a
continuación:

10,ACCOUNTING,7782,CLARK

20,RESEARCH,7369,SMITH,7566,JONES,7788,SCOTT

30,SALES,7499,ALLEN,7521,WARD,7654,MARTIN,7698,BLAKE

40,OPERATIONS

En este ejemplo, los registros y columnas son de longitud variable. Las columnas se delimitan con una
coma, los registros con una nueva línea. La recuperación de datos es muy eficiente si la consulta puede
navegar por la jerarquía: si uno conoce el departamento de un empleado, el empleado puede ser
encontrado rápidamente. Si no lo hace, la recuperación puede ser lenta. Las modificaciones de los datos
pueden ser un problema si la modificación requiere movimiento. Por ejemplo, para mover el empleado
7566, JONES de RESEARCH a SALES supondría un esfuerzo considerable por parte de la base de datos,
ya que el traslado debe llevarse a cabo como una eliminación de un registro y una inserción en otra.
Téngase en cuenta que en este ejemplo, si bien es posible tener un departamento sin empleados (el
departamento OPERATIONS), es absolutamente imposible tener un empleado sin un departamento: no
hay ningún lugar donde ponerlo. Esto es excelente si hay una regla de negocios que establece que todos
los empleados deben estar en un departamento, pero no tan bueno si no es el caso.

El paradigma relacional es altamente eficiente en muchos aspectos para muchos tipos de datos, pero no
es apropiado para todas las aplicaciones. Como regla general, un análisis relacional debería ser el primer
enfoque adoptado al modelar un sistema. Sólo si se demuestra inapropiado, se recurre a estructuras no
relacionales. Aplicaciones en las que el modelo relacional ha demostrado ser altamente efectivo incluye
prácticamente todos los sistemas OLTP y DSS. El paradigma relacional puede ser exigente en sus
requerimientos de hardware y en la habilidad necesaria para desarrollar aplicaciones a su alrededor, pero
a la hora de encajar los datos, ha demostrado ser el modelo más versátil. Puede haber, por ejemplo,

26/306
SQL

problemas causados por la necesidad de mantener los índices que mantienen, a su vez, los vínculos entre
las tablas y los requisitos de espacio (mantener múltiples copias de los datos indexados en los propios
índices y en las tablas en las que residen las columnas). Sin embargo, el diseño relacional es en la
mayoría de los casos el modelo óptimo.

Varios editores de software han creado sistemas de gestión de bases de datos que se ajustan (con
diversos grados de precisión) al paradigma relacional; Oracle es sólo uno. IBM fue quizás la primera
empresa en dedicarle importantes recursos, pero su producto (que más tarde se convirtió en DB2) no
fue importado a plataformas que no fueran IBM durante muchos años. SQL Server de Microsoft es otra
base de datos relacional que ha sido limitada por las plataformas en las que se ejecuta. Las bases de
datos Oracle, por el contrario, siempre se han importado a todas las plataformas importantes desde la
primera versión. Esto puede ser lo que dio a Oracle la ventaja en el mercado de los Sistemas de Gestion
de Bases de Datos (SGDB).

Problema Solución

Una organización está diseñando una Todos. El equipo del proyecto debe
nueva aplicación. ¿Quién debe involucrar a analistas de negocio (que
participar? modelan los procesos de negocio),
analistas de sistemas (que modelan los
datos), diseñadores de sistemas (que
deciden cómo implementar los modelos),
desarrolladores, administradores de
bases de datos, administradores de
sistemas y (lo más importante) usuarios
finales.

Es posible que las estructuras Intentar normalizar los datos en tablas


relacionales no sean adecuadas para una bidimensionales, enlazadas con
aplicación particular. ¿Cómo puede
relaciones de uno a muchos. Si esto
determinarse esto y qué debe hacerse a
continuación? ¿Puede ayudar Oracle? realmente no puede hacerse, hay que
considerar otros paradigmas. Oracle
ayudaría. Por ejemplo, los mapas y otros
datos geográficos realmente no
funcionan en relación. Tampoco lo hacen
los datos de texto (como los documentos
de tratamiento de textos). Pero las
opciones de base de datos Espacial y
Texto pueden ser usadas para estos

propósitos. También existe la posibilidad


de utilizar objetos definidos por el
usuario para almacenar datos no
tabulares.

27/306
SQL

Nota: en la terminología, puede surgir una confusión al discutir sobre bases de datos relacionales con
personas acostumbradas a trabajar con productos de Microsoft. SQL es un lenguaje y SQL Server es una
base de datos, pero en el mundo de Microsoft, el término SQL se utiliza a menudo para referirse a
cualquiera de los dos.

1.3. Resumir el lenguaje SQL


SQL es definido, desarrollado y controlado por organismos internacionales. Oracle Corporation no tiene
que ajustarse al estándar SQL, pero decide hacerlo. El idioma puede considerarse muy simple (sólo hay
16 comandos), pero en la práctica la codificación SQL puede ser tremendamente complicada.

1.3.1. Estándares SQL


Structured Query Language (SQL) fue inventado por un grupo de investigación de IBM en los años 70,
pero, de hecho, Oracle Corporation (que en ese momento operaba con el nombre de Relational Software,
Inc.) afirma haber vencido a IBM en el mercado durante unas semanas con la primera implementación
comercial: Oracle 2, lanzado en 1979. Desde entonces, el lenguaje ha evolucionado enormemente y ya
no está manejado por una sola organización. SQL es ahora un estándar internacional. Está gestionado
por comités de ISO y ANSI. ISO es la Organización Internacional de Normalización, con sede en Ginebra;
ANSI es el American National Standards Institute, con sede en Washington, DC. Los dos órganos
cooperan y sus normas SQL son idénticas.

Las versiones anteriores de la base de datos Oracle utilizaban una implementación de SQL que presentaba
algunas desviaciones significativas del estándar. Esto no se debía a que Oracle estuviera siendo diferente:
normalmente se debía a que Oracle implementaba características que estaban por delante de la norma,
y cuando la norma se actualizó, se utilizó diferente sintaxis. Un ejemplo es OUTER JOIN (detallada en
este curso), que Oracle implementó mucho antes que el estándar de SQL. Cuando el estándar de SQL
introdujo un sistema externo, Oracle agregó soporte para la nueva sintaxis de JOIN, al tiempo que
mantenía el soporte para su propia sintaxis. Oracle Corporation garantiza el cumplimiento futuro
mediante la inserción de personal en los diversos comités de ISO y ANSI, impulsando el estándar de
SQL.

1.3.2. Comandos SQL


Estos son los 16 comandos SQL primarios, separados en grupos de uso común:
Los comandos Data Manipulation Language (DML):
- SELECT
- INSERT
- UPDATE
- DELETE
- MERGE
Los comandos Data Definition Language (DDL):
- CREATE
- ALTER
- DROP

28/306
SQL

- RENAME
- TRUNCATE
- COMMENT
Los comandos Data Control Language (DCL):
- GRANT
- REVOKE
Los comandos Transaction Control Language (TCL):
- COMMIT
- ROLLBACK
- SAVEPOINT
El primer comando, SELECT, es el tema principal de los capítulos 2 a 9. Los comandos DML restantes se
tratan en el Capítulo 10, junto con los comandos TCL. El DDL se detalla en el Capítulo 11. DCL, que tiene
que ver con la seguridad, sólo se menciona brevemente: entra más en el dominio del administrador de
la base de datos que en el del desarrollador.

Según toda la documentación, SELECT es una declaración DML. En la práctica, no se incluye


cuando se refiere a DML; se trata como si fuera un lenguaje por derecho propio (casi lo es) y
usan DML para referirse sólo a los comandos que cambian datos.

1.3.3. Un lenguaje orientado a un conjunto


La mayoría de los 3GL son lenguajes procedurales. Los programadores que trabajan en lenguajes
procedurales especifican qué hacer con los datos, un registro cada vez. Los programadores que trabajan
en un lenguaje orientado a un conjunto especifican qué hacer con un grupo (un "conjunto") de registros
y dejan que la base de datos resuelva cómo hacerlo en todos los registros del conjunto.
Los lenguajes procedurales suelen ser menos eficaces que los lenguajes de programación en la gestión
de datos, tanto en lo que se refiere al desarrollo como a la ejecución. Una rutina de procedimiento para
hacer un bucle a través de un grupo de registros y actualizarlos uno a uno implicaría muchas líneas de
código, donde SQL puede hacer toda la operación con un comando. El resultado: aumenta la
productividad de los programadores. Durante la ejecución del programa, el código procedural no da
opciones a la base de datos; debe ejecutarse el código tal y como ha sido escrito. Con SQL, el
programador indica lo que quiere hacer, pero no cómo hacerlo; la base de datos tiene la libertad de
determinar la mejor manera de llevar a cabo la operación. Esto generalmente dará mejores resultados.

Cuando SQL falla al proporcionar una solución completa es porque es puramente un lenguaje de acceso
a datos. La mayoría de las aplicaciones necesitarán declaraciones procesales, como el control de flujo:
ramificación e iteración condicional. Por lo general, también necesitarán control de pantalla, las
instalaciones de la interfaz de usuario y las variables; SQL no tiene ninguno de estos. SQL es un lenguaje
orientado a la configuración capaz solamente de acceder a datos. Para el desarrollo de aplicaciones, por
lo tanto, se necesitará un lenguaje procedural que pueda invocar llamadas SQL. Es por lo tanto necesario
para SQL trabajar con un lenguaje procedural.

Se considera una aplicación que solicita un nombre a un usuario, recupera a todas las personas con ese
nombre de una tabla, le pide al usuario que elija uno de ellos, y luego borra la persona elegida. El
lenguaje procedural dibujará una pantalla y generará una solicitud de un nombre. El usuario introducirá

29/306
SQL

el nombre. El lenguaje de procedimiento construirá una sentencia SQL SELECT usando el nombre y
enviará la sentencia a través de una sesión de base de datos al servidor de base de datos para su
ejecución. El servidor devolverá un conjunto de registros (todas las personas con ese nombre) al lenguaje
procedural, que formateará el conjunto para que se muestre al usuario y le pedirá que elija uno (o más)
de ellos. El identificador de la persona (o personas) elegida(s) se utilizará para construir una sentencia
SQL DELETE para que el servidor la ejecute. Si el identificador es un identificador único (la clave
primaria), entonces el conjunto de registros a borrar será un conjunto de un solo registro; si el
identificador no es único, entonces el conjunto seleccionado para borrado será más grande. El código
procedural no sabrá nada sobre el tamaño probable de los conjuntos recuperados o borrados.

1.4. Las herramientas cliente


Existen numerosas herramientas que se pueden usar para conectarse a una base de datos Oracle. Dos
de las más utilizadas son SQL*Plus y SQL Developer. Puesto que Oracle Corporation es su proveedor, son
perfectamente adecuadas para la gran mayoría del trabajo que un desarrollador o un administrador de
bases de datos pueda necesitar. La elección entre ambas depende en gran medida de las preferencias
personales de cada usuario, relacionadas tanto con la configuración del entorno de desarrollo como con
sus funcionalidades. SQL Developer ofrece muchas más funcionalidades que SQL*Plus, pero es más
exigente dado que necesita una interfaz gráfica, mientras que SQL*Plus se puede utilizar desde consola.

1.4.1. SQL*Plus
SQL*Plus es una herramienta cliente-servidor para conectarse a una base de datos y para lanzar
comandos SQL ad hoc. También se pueden usar para crear código PL/SQL y tiene herramientas para dar
formatos determinados a los resultados. Está disponible en todas las plataformas donde la base de datos
se traslade. En las siguientes secciones se darán detalles acerca del uso de SQL*Plus tanto en Windows
como en Linux. No hay grandes diferencias en el uso de SQL*Plus en otras plataformas.

En términos de arquitectura, SQL*Plus se trata de un proceso de usuario escrito en C que establece una
sesión a través de una instancia y una base de datos a través del protocolo de Oracle Net. Las plataformas
para el cliente y el servidor pueden ser diferentes. Por ejemplo, no hay ninguna razón para no usar
SQL*Plus en un ordenador con Windows y conectarlo a una base de datos siendo ejecutada en un servidor
Unix o viceversa, dado que Oracle Net está configurado para este tipo de conexiones.

1.4.2. SQL Developer


SQL Developer es una herramienta para conectarse a una base de datos Oracle (incluso a algunas bases
de datos que no sean Oracle, también) al tiempo que también sirve para lanzar comandos SQL ad hoc.
También puede gestionar objetos PL/SQL. A diferencia que SQL*Plus, es una herramienta gráfica con
asistentes para acciones comunes. Está escrito en Java y requiere un Java Runtime Environment (JRE)
para poder ejecutarse.

Al estar escrito en Java, SQL Developer está disponible en todas las plataformas que puedan lanzar la
versión que corresponda del JRE. No hay diferencias significativas entre plataformas.
SQL Developer puede ser una herramienta muy útil, altamente configurable. Se puede experimentar con
el entorno, leer la Ayuda y construir su interfaz para que resulte lo más cómodo posible trabajar en ella.

30/306
SQL

1.5. Crear los esquemas de demostración


A lo largo de este curso, hay cientos de ejemplos de código SQL ejecutándose. En su mayor parte, los
ejemplos utilizan tablas de dos esquemas de muestra proporcionados por Oracle: el esquema HR, que
es un ejemplo de datos que simula una simple aplicación de recursos humanos, y el esquema OE, que
simula una aplicación de entrada de pedidos más complicada.

Estos esquemas se pueden habilitar cuando se crea la base de datos; es una opción que muestra el
Asistente de Configuración de Base de Datos (DBCA). Si no existen, se pueden crear más tarde
ejecutando algunos scripts que existirán en la base de datos Oracle Home.

1.5.1. Usuarios y esquemas (Schemas)


En el lenguaje de Oracle, un usuario de base de datos es una persona que puede conectarse a la base
de datos. Un esquema de base de datos son todos los objetos de la base de datos que pertenecen a un
usuario. Los dos términos se pueden utilizar a menudo indistintamente, ya que existe una relación uno
a uno entre los usuarios y los esquemas. Hay que tener en cuenta que, aunque en realidad existe un
comando CREATE SCHEMA, éste no crea un esquema, sino que es sólo una forma rápida de crear objetos
en un esquema. Un esquema se crea vacío inicialmente, cuando se crea un usuario con el comando
CREATE USER.

Los esquemas se utilizan para almacenar objetos. Estos pueden ser objetos de datos tales como tablas
o procedimientos almacenados en PL/SQL. Para conectarse a una base de datos y acceder a sus objetos
el usuario tiene primero que ejecutarse y arrancar la base de datos. Por defecto, los usuarios tienen
acceso a los objetos de su propio esquema y a ningún otro, pero muchas aplicaciones modifican este
comportamiento. Normalmente, un esquema puede utilizarse para almacenar datos a los que acceden
otros usuarios a los que se les ha dado permiso para usar los objetos, aunque no les pertenezcan. En la
práctica, muy pocos usuarios tendrán objetos en su propio esquema, o permiso para crearlos: tendrán
permisos de acceso sólo a objetos en otro esquema. Estos objetos serán utilizados por todos los usuarios
que ejecutan la aplicación cuyos datos almacena ese esquema. Por el contrario, los usuarios propietarios
de los esquemas de almacenamiento de datos nunca accederán: el único propósito de sus esquemas es
contener datos utilizados por otros.
Es imposible que un objeto de datos exista independientemente de un esquema. O, en otras palabras,
todas las tablas deben tener un propietario. El propietario es el usuario en cuyo esquema reside la tabla.
El identificador único para una tabla (o cualquier otro objeto de esquema) es el nombre de usuario,
seguido del nombre de objeto. De ello se deduce que no es posible que existan dos tablas con el mismo
nombre en el mismo esquema, pero sí es posible que dos tablas con el mismo nombre (aunque
posiblemente con estructuras o contenidos diferentes) existan en esquemas diferentes. Si un objeto no
existe en el propio esquema, para acceder a él se debe calificar su nombre con el nombre del esquema
en el que reside. Por ejemplo, [Link] es la tabla denominada EMPLOYEES en el esquema del
usuario HR. A menos que haya sinónimos disponibles, sólo un usuario conectado como HR puede acceder
a la tabla haciendo referencia a EMPLOYEES sin un calificador de nombre de esquema. Un sinónimo es
una construcción que hace que un objeto accesible a otros usuarios sin requerir su nombre de esquema
como prefijo.

31/306
SQL

1.5.2. Los SCHEMAS RH y OE


El esquema de HR consta de siete tablas, enlazadas por clave primaria (primary key) a relaciones de
clave foránea (foreign key). La siguiente figura ilustra las relaciones entre las tablas, como un diagrama
de relación de entidad.

Dos de las relaciones mostradas en la figura anterior pueden no ser inmediatamente comprensibles. En
primer lugar, existe una relación de muchos a uno entre EMPLOYEES y EMPLOYEES. Esto es lo que se
conoce como clave foránea autorreferenciada. Esto significa que muchos empleados pueden estar
conectados a un empleado, y se basa en el hecho de que muchos empleados pueden tener un gerente,
pero el gerente también es un empleado. La relación es implementada por la columna manager_id siendo
una clave foránea para employee_id, que es la clave principal de la tabla.

La segunda relación que puede requerir explicación es entre DEPARTMENTS y EMPLOYEES, que es
bidireccional. La relación de un departamento a muchos empleados simplemente establece que puede
haber muchos miembros del personal en cada departamento, basado en la columna dept_id de la tabla
EMPLOYEES, que es una clave foránea de la columna dept_id (primary key) de la tabla DEPARTMENTS.
La relación de un empleado con muchos departamentos muestra que un empleado podría ser el gerente
de varios departamentos y es implementado por la columna manager_id en la tabla DEPARTMENTS
siendo una clave foránea a la columna employee_id (primary key) en la tabla EMPLOYEES.

La siguiente tabla muestra las columnas de cada tabla en el esquema HR, utilizando la notación descrita
en la sección anterior “Normalización de datos” para indicar las claves primarias (#), las claves foráneas
(\), y si las columnas son opcionales (o) u obligatorias (*).

Las tablas son:

- REGIONS contiene registros para las principales áreas geográficas.


- COUNTRIES contiene registros para cada país, que se asignan opcionalmente a una región.

32/306
SQL

- LOCATIONS contiene direcciones individuales, que se asignan opcionalmente a un país.


- DEPARTMENTS contiene un registro para cada departamento, asignada opcionalmente a una
ubicación y opcionalmente con un gerente (que debe existir como empleado).
- EMPLOYEES contiene un registro para cada empleado, a cada uno de los cuales se le debe
asignar a un trabajo y opcionalmente a un departamento y a un gerente. Los gerentes deben
ser empleados.
- JOBS enumera todos los posibles puestos de trabajo en la organización. Es posible que muchos
empleados tengan el mismo trabajo.
- JOB_HISTORY enumera los puestos de trabajo anteriores ocupados por empleados,
identificados de forma unívoca por employee_id y start_date; un empleado no puede retener
dos trabajos al mismo tiempo. Cada registro del historial de trabajo hará referencia a un
empleado que haya tenido un trabajo en ese momento y que pueda haber sido miembro de
uno de los departamentos.

Tabla Columnas

REGIONS #* region_id

o region_name

COUNTRIES #* country_id

o country_name

\o region_id

LOCATIONS #* location_id

o street_address

o postal_code

* city

33/306
SQL

34/306
o state_province
SQL

\o country_id

DEPARTMENTS #* department_id

* department_name

\o manager_id

\o location_id

EMPLOYEES #* employee_id

o first_name

* last_name

* e-mail

o phone_number

* hire_date

\* job_id

o salary

o comission_pct

\o manager_id

\o department_id

35/306
SQL

JOBS #* job_id

* job_title

o min_salary

o max_salary

JOB_HISTORY #* employee_id

#* start_date

* end_date

\* job_id

\o department_id

Hay registros en EMPLOYEES que no tienen un registro correspondiente en DEPARTMENTS.


Esto podría ser porque el diseño es así, pero también podría ser un error de diseño ya que la
columna DEPARTMENT_ID en EMPLOYEES no es obligatoria (puede contener valores NULL).
Existen posibles errores similares en la jerarquía REGIONS-COUNTRIES-LOCATIONS, lo que
realmente no tiene sentido en la vida real.

El esquema OE es considerablemente más complejo que el esquema HR. Las estructuras de las tablas
son mucho más complicadas: incluyen columnas definidas como tablas anidadas, tipos de datos definidos
por el usuario y tipos de datos XML. Hay una serie de ejercicios opcionales al final de cada capítulo que
normalmente se basan en el esquema OE. Los objetos a los que se hace referencia se describen a medida
que se utilizan.

Los esquemas de demostración no deberían existir en las bases de datos de producción. No es


bueno, por razones de seguridad, tener esquemas innecesarios en una base de datos que
tengan nombres de usuario, capacidades y (posiblemente) contraseñas conocidas.

1.6. Resumen
Posicionar las tecnologías de servidor

• Oracle Database almacena y gestiona el acceso a los datos de los usuarios.

36/306
SQL

• Oracle WebLogic Server ejecuta aplicaciones que conectan a los usuarios con la base de datos.
• Oracle Enterprise Manager es una herramienta para gestionar bases de datos, servidores de
aplicaciones y, si se desea, todo el entorno informático.
• Los lenguajes incorporados en la base de datos para el desarrollo de aplicaciones son SQL,
PL/SQL y Java.

Entender estructuras relacionales

• Los datos deben normalizarse en tablas bidimensionales.


• Las tablas se enlazan mediante claves primarias y ajenas. Los diagramas de
entidad/relación representan las tablas gráficamente.

Resumir el lenguaje SQL

• Las sentencias DML son SELECT, INSERT, UPDATE, DELETE, y MERGE.


• Las sentencias DDL son CREATE, ALTER, DROP, RENAME, TRUNCATE y COMMENT.
• Las sentencias DCL son GRANT y REVOKE. Las sentencias TCL son COMMIT, ROLLBACK y
SAVEPOINT.

Usar las herramientas del cliente

• SQL*Plus es una herramienta de línea de comando instalada en Oracle Home.


• SQL Developer es una herramienta gráfica instalada en el propio directorio.
• Ambas herramientas requieren una conexión a la base de datos, que consiste en un nombre de
usuario, una contraseña y un identificador de conexión.

Crear los esquemas de demostración

• Los esquemas de demostración son proporcionados por Oracle para facilitar el aprendizaje, pero
deben ser creados antes de que se puedan utilizar.

2. Recuperación de datos utilizando la sentencia SQL SELECT

2.1. Listar las posibilidades de la sentencia SQL SELECT


Este capítulo estudia los conceptos de extracción o recuperación de datos almacenados en tablas
relacionales usando la sentencia SELECT. La sentencia se introduce en su forma básica y se construye
progresivamente para ampliar su funcionalidad principal. A medida que se aprendan las reglas de esta
declaración, un punto importante a recordar es que la sentencia SELECT nunca altera la información
almacenada en la base de datos. En su lugar, proporciona un método de sólo lectura para extraer
información.

Saber cómo recuperar datos en un formato establecido utilizando un idioma de consulta es el primer
paso para la comprensión de las posibilidades de las sentencias SELECT.
Las tres áreas principales que se estudian son las siguientes:

37/306
SQL

• Introducción a la sentencia SQL SELECT


• El comando DESCRIBE TABLE
• Posibilidades de la sentencia SQL SELECT

2.1.1. Introducción a la sentencia SQL SELECT


La sentencia SELECT del Structured Query Language (SQL) es un elegante, flexible y altamente
extensible mecanismo creado para recuperar información de una tabla de base de datos. Una base de
datos serviría de poco si no pudiera ser consultada para responder a todo tipo de preguntas. Por ejemplo,
se puede tener una base de datos que contiene registros financieros personales como el estado de la
cuenta bancaria, las facturas de servicios públicos, y las nóminas. Se puede pedir fácilmente a la base
de datos una lista ordenada por fecha de las facturas de servicios eléctricos de los últimos seis meses o
consultar el estado de la cuenta bancaria por una lista de los pagos efectuados a una cuenta determinada
durante el mismo período. La instrucción SELECT presenta un sencillo formato similar al inglés, que
permite formular preguntas a la base de datos de forma intuitiva.
Las tablas, también conocidas como relaciones, consisten en filas de información (registros) divididas
por columnas. Para futuros ejemplos, se considerarán dos tablas de ejemplo: la tabla EMPLOYEES y la
tabla DEPARTMENTS. Este conjunto de datos de ejemplo se basa en la información de Recursos Humanos
(HR) para alguna organización ficticia. En la terminología Oracle, cada tabla pertenece a un esquema
(propietario), en este caso al esquema HR. La tabla EMPLOYEES almacena filas o registros de
información. Éstos contienen varios atributos (campos) que describen a cada empleado de esta
organización. La tabla DEPARTMENTS contiene información descriptiva de cada departamento dentro de
esta organización.
Asumiendo que una conexión a una base de datos que contiene el esquema modelo de HR está disponible,
entonces usando SQL*Plus o SQL Developer se puede establecer una sesión de usuario. Una vez se
conecte a la base de datos, es hora de comenzar el recorrido por SQL.

SQL*Plus tiene un amplio entorno de comandos en tiempo de ejecución que se puede explorar
utilizando la documentación en línea o el comando HELP INDEX. Estas listas incluyen los
comandos SQL*Plus disponibles, tales como el comando SHOW, que muestra el valor de una
variable SQL*Plus. Por ejemplo, SHOW USER muestra el nombre del usuario actualmente
conectado.

2.1.2. El comando DESCRIBE TABLE


Para obtener las respuestas que uno busca, se deben hacer las preguntas correctas. Una buena
comprensión de los términos de referencia, que en este caso son tablas relacionales, es esencial para la
formulación de las preguntas correctas. Una descripción estructural de una tabla es útil para establecer
qué preguntas se pueden hacer. El servidor Oracle almacena información sobre todas las tablas de un
conjunto especial de tablas relacionales llamado diccionario de datos, con el fin de manejarlos. El
diccionario de datos es bastante similar a un diccionario de lenguaje normal. Almacena definiciones de
objetos de base de datos en un formato centralizado, ordenado y estructurado.
Hay que distinguir entre el almacenamiento de la definición y el contenido de una tabla. La definición de
una tabla incluye información como el nombre de la tabla, propietario de la tabla, detalles sobre las
columnas que lo componen, y su tamaño de almacenamiento físico en disco. Esta información también

38/306
SQL

se denomina metadato (metadata). El contenido de una tabla se almacena en registros y se hace


referencia a él como datos (data).
Los metadatos de una tabla pueden obtenerse consultando la base de datos, usando el comando
DESCRIBE. La forma general de la sintaxis de este comando es intuitiva:

DESC[RIBE] <SCHEMA>.tablename

La palabra clave DESCRIBE puede ser acortada a DESC. Todas las tablas pertenecen a un esquema o a
un propietario. Si se está describiendo una tabla que hace referencia al esquema al que está conectado,
la parte "<SCHEMA>" del comando puede ser omitida. La imagen que aparece más abajo muestra el uso
del comando “SHOW user” para verificar que el usuario conectado actualmente es HR. Mientras se está
conectado a la base de datos como HR, la tabla EMPLOYEES se puede mostrar con el comando DESCRIBE
employee, y la tabla DEPARTMENTS se puede mostrar utilizando la notación abreviada DESC
[Link]. El prefijo “hr” puede omitirse ya que la tabla DEPARTMENTS pertenece al esquema HR.
El esquema HR (y cualquier otro esquema) tiene acceso a una tabla especial llamada DUAL, que
pertenece al esquema SYS. Esta tabla puede ser descrita con el comando DESCRIBE [Link].

La descripción de las tablas origina resultados interesantes y útiles. Se sabe qué columnas de una tabla
pueden seleccionarse ya que DESCRIBE muestra los nombres de todas las columnas de la tabla. También
es posible saber qué tipo de datos contienen estas columnas, puesto que DESCRIBE también muestra el
tipo de dato de la columna. Véanse los tipos de datos que se muestran en la imagen de arriba:

Para las columnas numéricas se utiliza a menudo el tipo de dato NUMBER(p,s), donde el primer parámetro
es la precisión (el número máximo de dígitos que va a tener el dato) y el segundo es la escala (el número
máximo de dígitos decimales que se puede almacenar a la derecha del separador decimal). En la figura
anterior, la columna SALARY de la tabla EMPLOYEES tiene un tipo de datos NUMBER(8,2). Esto significa

39/306
SQL

que los valores almacenados en esta columna pueden tener un máximo de 8 dígitos. De estos 8 dígitos,
2 pueden estar a la derecha del punto decimal y hasta 6 pueden estar a la izquierda. Si hay más de dos
dígitos a la derecha del punto decimal, el número se redondeará a 2 decimales siempre que haya un
máximo de 8 dígitos. Un valor SALARY de 999999.99 es aceptable, pero un valor SALARY de 9999999.9
no lo es, aunque ambos números contengan 8 dígitos.

El tipo de datos VARCHAR2(length) almacena longitudes variables de datos de caracteres alfanuméricos,


donde la longitud determina el número máximo de caracteres que puede contener un campo. La columna
FIRST_NAME de la tabla EMPLOYEES tiene el tipo de datos VARCHAR2(20), lo que significa que puede
almacenar nombres de empleados de hasta 20 caracteres.

Hay que tener en cuenta que si esta columna no contiene datos o su contenido es inferior a 20 caracteres,
no utilizará necesariamente el mismo espacio que utilizaría para almacenar un nombre de 20 caracteres.
El tipo de datos CHAR(size) especifica columnas de longitud fija, en las que el espacio del campo se
preasigna para contener un número fijo de caracteres independientemente de su contenido. CHAR es
mucho menos utilizado que VARCHAR2. A menos que la longitud de los datos sea predecible y constante,
el tipo de datos CHAR utiliza el almacenamiento de manera ineficiente, rellenando con espacios los
componentes no utilizados.

Los tipos de datos de columna DATE y TIMESTAMP almacenan información de la fecha y la hora. DATE
almacena día, mes, año, horas, minutos y segundos. TIMESTAMP(f) almacena la misma información que
DATE pero es también capaz de almacenar segundos fraccionarios.

Hay una gran variedad de tipos de datos, muchos tienen un propósito concreto como BLOBs
(Binary Large Objects), usado para almacenar datos binarios como música o datos de vídeo.
Pero la gran mayoría de las tablas, sin embargo, utilizan los tipos de datos de columna
primitiva NUMBER, VARCHAR2 y DATE. El tipo de datos TIMESTAMP se ha utilizado
ampliamente desde su introducción en Oracle 9i.

Cualquier columna de datos que esté restringida por la directiva NOT NULL cuando se crea la tabla debe
contener algunos datos. Es importante señalar que NULL tiene un significado especial para el servidor
Oracle. NULL se refiere a la ausencia de datos. Los espacios en blanco no cuentan como NULL ya que
están presentes en el registro y tienen cierta longitud, aunque no sean visibles.

2.1.3. Posibilidades de la sentencia SQL SELECT


Las tablas de bases de datos relacionales están construidas sobre una sólida base matemática llamada
teoría relacional. En esta teoría, las relaciones, o tablas, son operadas por un lenguaje formal llamado
álgebra relacional. Tres conceptos de la teoría relacional abarcan la capacidad de la sentencia SELECT:
proyección, selección y unión.
La proyección hace referencia a la restricción de atributos (campos) seleccionados de una relación o
tabla. Al solicitar información de una tabla, se puede pedir ver todas las columnas. Por ejemplo, en la
tabla [Link], se pueden recuperar todos los registros y todas las columnas con una simple
instrucción SELECT. Esta consulta devolverá información de DEPARTMENT_ID, DEPARTMENT_NAME,
MANAGER_ID y LOCATION_ID para cada registro de departamento almacenado en la tabla. ¿Y si se

40/306
SQL

quisiera una lista que contiene sólo las columnas DEPARTMENT_NAME y MANAGER_ID? Entonces, se
pediría sólo esas dos columnas de la tabla. Esta restricción de columnas se llama proyección.
La selección hace referencia a la restricción de las tuplas o registros seleccionados de una relación (tabla).
A menudo no se desea recuperar cada registro de una tabla. Las tablas pueden contener muchos registros
y, en lugar de preguntar por todos ellos, la selección proporciona un medio para restringir las líneas
devueltas. Quizás se ha pedido que se identifique sólo los empleados que pertenecen al departamento
30. Con la selección es posible limitar los resultados establecidos en aquellos registros que tienen un
valor DEPARTMENT_ID de 30.

La unión, como concepto relacional, se refiere a la interacción de las tablas entre sí en una consulta. La
tercera forma normal, como se vio en el Capítulo 1, presentaba la noción de separar los diferentes tipos
de datos en tablas autónomas para evitar la duplicidad y anomalías de mantenimiento y asociar datos
relacionados utilizando claves primarias y foráneas. Estas relaciones proporcionan el mecanismo para
unir las tablas entre sí. La unión se discute más extensamente en el Capítulo 7.

Suponiendo que es necesario recuperar las direcciones de correo electrónico de todos los empleados que
hay en el departamento de Ventas. La columna EMAIL pertenece a los EMPLOYEES mientras que la
columna DEPARTMENT_NAME pertenece a la tabla DEPARTMENTS. La proyección y selección de la tabla
DEPARTMENTS puede ser utilizada para obtener el valor DEPARTMENT_ID que corresponde al
departamento de Ventas. Los registros correspondientes de la tabla EMPLOYEES se pueden unir a la tabla
DEPARTMENTS basándose en el valor común DEPARTMENT_ID. La columna EMAIL se mostrará a partir
de este conjunto de resultados.

La sentencia SQL SELECT se rige matemáticamente por estos tres principios. Una combinación ilimitada
de proyecciones, selecciones y uniones proporciona al lenguaje extraer los datos relacionales que se
necesiten.

2.2. Ejecutar una sentencia básica SQL SELECT


La clave para ejecutar cualquier sentencia en un lenguaje de consulta es una comprensión profunda de
su sintaxis y las reglas que rigen su uso. Esta sección tratará la sentencia SELECT.

Estos son los temas que se tratarán en los siguientes cuatro apartados:

• La instrucción SELECT primitiva


• Reglas de sintaxis
• Expresiones SQL y operadores
• El concepto NULL

2.2.1. La sentencia primitiva SELECT


En su forma más primitiva, la instrucción SELECT permite la proyección de columnas y la creación de
expresiones aritméticas, de caracteres y de fechas. También permite la eliminación de valores duplicados
del conjunto de resultados.

La sintaxis básica de la instrucción SELECT es la siguiente:

SELECT *|{[DISTINCT] column|expression [alias],…}

FROM table

41/306
SQL

Las palabras clave o palabras reservadas de la sintaxis de la propia sentencia SELECT son las que
aparecen en mayúsculas. Estas palabras reservadas son también denominadas como cláusulas.
Las palabras reservadas no se pueden usar como nombres de columnas o nombre de otros objetos de la
base de datos. SELECT, DISTINCT y FROM son tres palabras clave; es decir, tres cláusulas SQL. La
sentencia SELECT siempre está compuesta por dos o más cláusulas. Las dos cláusulas obligatorias son
la cláusula SELECT y la cláusula FROM. El símbolo de la tubería (|) se usa para denotar la expresión
lógica OR.
A continuación, se muestra el uso más sencillo de la instrucción SELECT:

SELECT *
FROM table;

El símbolo de asterisco (*) se utiliza para hacer referencia a todas las columnas.
SELECT * es una forma concisa de pedirle al servidor de Oracle que devuelva todas las columnas
disponibles. Se usa como un atajo, un símbolo para ahorrar tiempo en vez de teclear SELECT column1,
column2,…,columnX, para seleccionar todas las columnas. La cláusula FROM especifica qué tabla
consultar para obtener (proyectar) las columnas solicitadas en la cláusula SELECT.
Se puede utilizar el siguiente comando SQL para recuperar todas las columnas y todas las filas de la tabla
REGIONS en el esquema HR:

SELECT *

FROM regions;

Cuando este comando se ejecuta en SQL * Plus, devuelve todos los registros de datos y todas las
columnas que pertenecen a esta tabla. El uso del asterisco en una instrucción SELECT a veces se
denomina consulta "ciega" porque las columnas que se deben buscar no están especificadas.

La segunda forma de la sentencia básica SELECT tiene la misma cláusula FROM que la primera forma,
pero la cláusula SELECT es diferente:

SELECT {[DISTINCT] column|expression [alias],…}

FROM table;

Esta cláusula SELECT se puede simplificar en dos formatos:

SELECT column1 (possibly other columns or expressions) [alias opcional]

SELECT DISTINCT column1 (possibly other columns or expressions) [alias opcional]

Un alias es un nombre alternativo para hacer referencia a una columna o expresión. Los alias se usan
generalmente para mostrar resultados de una manera fácil de usar. También sirven como atajo para
hacer referencia a columnas o expresiones para reducir teclear. Los alias se tratarán en detalle más
adelante en este capítulo.

Al enumerar explícitamente solo las columnas que se desean obtener en la cláusula SELECT, en efecto,
se proyecta el subconjunto exacto de los resultados referentes a dichas columnas. La siguiente consulta
devolverá solo el subconjunto de columnas REGION_NAME de la tabla REGIONS, tal y como se muestra
en la segunda consulta de la imagen de abajo.

42/306
SQL

SELECT region_name
FROM regions;

También se puede ver en la imagen cómo la primera consulta muestra tanto las columnas REGION_ID
como REGION_NAME.

Supóngase que se quieren obtener todos los roles laborales que ha ejercido un empleado en la
organización a lo largo de la historia. Para esto puede emitir la consulta SELECT * FROM JOB_HISTORY.
Sin embargo, SELECT * además devuelve las columnas EMPLOYEE_ID, START_DATE y END_DATE.

En la imagen que aparece a continuación se muestra el conjunto de resultados obtenidos únicamente


recuperando las columnas JOB_ID y DEPARTMENT_ID.

43/306
SQL

El uso de la palabra clave DISTINCT permite eliminar registros duplicados del conjunto de resultados. En
numerosas situaciones se requiere un conjunto de registros únicos. Es importante tener en cuenta que
el criterio empleado por el servidor Oracle para determinar si un registro es único o distinto a otro
depende completamente de lo que se especifique después de la palabra clave DISTINCT en la cláusula
SELECT. Seleccionar distintos valores JOB_ID de la tabla JOB_HISTORY devolverá los ocho tipos de
trabajos distintos, como se puede ver a continuación.

Comparando esta salida con la anterior, donde se devuelven diez registros. Se ve que hay dos apariciones
de los valores AC_ACCOUNT y ST_CLERK JOB_ID. Estos son los dos registros duplicadas que se han
eliminado buscando valores distintos JOB_ID con DISTINCT.

Seleccionando la columna DEPARTMENT_ID con DISTINCT de la tabla JOB_HISTORY devolverían solo


seis registros. Los valores de DEPARTMENT_ID 50, 80, 90 y 110 aparecen dos veces en la tabla
JOB_HISTORY y, por lo tanto, se eliminarían cuatro registros buscando valores distintos de
DEPARTMENT_ID.

DISTINCT permite por lo tanto eliminar valores duplicados en combinaciones de columnas. Véase ahora
un ejemplo.

Hay diez registros en la tabla JOB_HISTORY. Ocho registros contienen distintos valores JOB_ID. Seis
registros contienen distintos valores DEPARTMENT_ID. ¿Cuántos registros contienen distintas
combinaciones de valores JOB_ID y DEPARTMENT_ID? En el conjunto de resultados se devuelven nueve
registros que contienen distintas combinaciones JOB_ID y DEPARTMENT_ID. Este es, por supuesto, el
registro que contiene un valor JOB_ID de ST_CLERK y un valor DEPARTMENT_ID de 50. Se puede ver el
resultado de la consulta y la propia consulta en la siguiente imagen:

44/306
SQL

A continuación, se puede apreciar una imagen seleccionando los DEPARTAMENT_IDs diferentes de la


tabla JOB_HISTORY.

45/306
SQL

La capacidad de proyectar columnas específicas desde una tabla es muy útil. Junto con la
capacidad de eliminar valores duplicados o combinaciones de valores, esto permite ayudar
con los requisitos básicos de informes de los usuarios. En muchas bases de datos de
aplicaciones, las tablas a veces pueden almacenar datos duplicados. Los informes del usuario
final frecuentemente requieren que estos datos se presenten como un conjunto manejable de
registros únicos. Hay que tener cuidado, sin embargo, al usar consultas ciegas para
seleccionar datos de tablas grandes. Ejecutando un SELECT * FROM huge_table; La sentencia
puede causar problemas de rendimiento si la tabla contiene millones de registros de datos.

2.2.2. Reglas de sintaxis


SQL es un lenguaje bastante estricto en términos de reglas de sintaxis, pero sigue siendo simple y lo
suficientemente flexible como para admitir una variedad de estilos a la hora de hacer uso de él. Esta
sección discute algunas de las reglas básicas que rigen las declaraciones de SQL.

a. Mayúsculas o minúsculas
Los ejemplos utilizados hasta ahora se han escrito en mayúsculas y minúsculas, utilizando las mayúsculas
para las palabras reservadas y las minúsculas para el resto de la declaración con fin de hacer más claros
los ejemplos. Muchos desarrolladores prefieren escribir sus declaraciones SQL en minúsculas. También
existe una idea errónea de que las palabras reservadas de SQL deben especificarse en mayúsculas. Se
aconseja adherirse a un formato consistente y estandarizado. Las siguientes tres declaraciones son
sintácticamente equivalentes:

46/306
SQL

SELECT * FROM LOCATIONS; SELECT


* FROM locations; select * from
locations;

Hay un caso a tener en cuenta sobre la sensibilidad de mayúsculas y minúsculas. Al interactuar con
valores literales, el uso de mayúsculas o minúsculas sí importa. Se considera la columna JOB_ID de la
tabla JOB_HISTORY. Esta columna contiene registros de datos que se almacenan en la base de datos en
mayúsculas, por ejemplo, SA_REP y ST_CLERK. Al solicitar que el conjunto de resultados esté restringido
por el valor literal de los campos de una columna, el uso de mayúsculas o minúsculas es crítico para
obtener un resultado u otro. El servidor de Oracle trata la solicitud de todos los registros en la tabla
JOB_HISTORY que contienen un valor de St_Clerk en la columna JOB_ID diferente de la solicitud para
todos los registros que tienen un valor de ST_CLERK en la columna JOB_ID.

Las consultas SQL pueden enviarse a la base de datos en minúsculas, mayúsculas o


mayúsculas y minúsculas. Se debe prestar especial atención cuando se interactúa con valores
literales o alias. Pedir una columna llamada JOB_ID o job_id devuelve la misma columna, pero
pedir registros donde el valor de JOB_ID es PRESIDENT es diferente de pedir registros donde
el valor de JOB_ID es President. Los valores literales siempre deben tratarse de manera que
distingan entre mayúsculas y minúsculas.

Los metadatos sobre diferentes objetos de la base de datos se almacenan de forma predeterminada en
mayúsculas en el diccionario de datos. Si se consulta una tabla diccionario de una base de datos para
devolver una lista de tablas del esquema HR, es probable que los nombres de tabla devueltos se
almacenen en mayúsculas. Esto no significa que no se pueda crear una tabla con un nombre en
minúscula; puede ser. Es más común y es el comportamiento predeterminado del servidor de Oracle
crear y almacenar tablas, columnas y otros metadatos de objetos de base de datos en mayúsculas en el
diccionario de la base de datos.

b. Finalizar sentencias
Los puntos y coma se usan generalmente para finalizar consultas SQL. SQL * Plus siempre requiere
terminar la sentencia, y generalmente se usa un punto y coma.

Una sola instrucción SQL o incluso grupos de consultas asociadas a menudo se guardan como script para
uso futuro.

Las sentencias individuales en las secuencias de comandos SQL suelen terminarse mediante un salto de
línea (o retorno de carro) y una barra diagonal (/) en la siguiente línea, en lugar de un punto y coma.
Se puede crear una instrucción SELECT, terminarla con un salto de línea, incluir una barra inclinada para
ejecutar la instrucción y guardarla en un script. El script se puede llamar desde SQL * Plus. Hay que
tener en cuenta que SQL Developer no requiere de finalizar explícitamente una consulta si esta es la
única instrucción que debe ejecutarse, pero no es incorrecto utilizar un finalizador de sentencia. Es una
buena práctica terminar siempre las consultas SQL con un punto y coma. A continuación, se incluyen
varios ejemplos de sentencias SQL * Plus:

SELECT country_name, country_id, region_id FROM countries;

47/306
SQL

En el primer ejemplo se expone una consulta SELECT terminada con un punto y coma y escrita en una
sola línea. Es totalmente aceptable que una instrucción SQL se escriba en una línea o abarque varias
líneas, siempre que no haya palabras cortadas.

SELECT city, location_id, state_province, country_id


FROM locations
/

Este segundo ejemplo muestra una instrucción que abarca tres líneas y que finaliza con una nueva línea,
la sentencia es ejecutada cuando aparece el carácter (/).

c. Sangría, legibilidad y buenas prácticas


Véase la siguiente consulta:

SELECT city, location_id, state_province, country_id


FROM locations
/

Este ejemplo destaca los beneficios de sangrar una consulta SQL para mejorar la legibilidad del código
(el servidor de Oracle también acepta si la declaración completa está escrita en una línea sin sangría).
Es una buena práctica separar diferentes cláusulas de la instrucción SELECT en diferentes líneas.
El intérprete de SQL es mucho más manejable durante el proceso de desarrollo si las expresiones
complejas se aíslan en líneas separadas, ya que los errores suelen mostrarse en el formato de: "ERROR
en la línea X:". Esto hace que el proceso de depuración sea mucho más simple.

Un solo signo de puntuación faltante, como un punto y coma, puede marcar la diferencia entre
una consulta correcta y una incorrecta.

Problema Solución

Se quiere construir y ejecutar consultas No. Oracle proporciona SQL * Plus y SQL
en tablas almacenadas en una base de Developer como herramientas gratuitas
para crear y ejecutar consultas. Existen
datos Oracle. ¿Es obligatorio el uso de
numerosas herramientas disponibles de
SQL*Plus o SQL Developer? Oracle (por ejemplo, Discoverer, APEX y
JDeveloper) y otros proveedores
externos que proporcionan una interfaz
para trabajar con las tablas de una base
de datos Oracle.

48/306
SQL

Para explorar aún más el entorno de El diccionario de datos es un conjunto de


base de datos del que se está haciendo tablas y vistas de otras tablas que
uso, supóngase que se quiere obtener la pueden consultarse a través de SQL. La
lista de tablas del esquema actual para instrucción SELECT table_name FROM
poder utilizarlo en consultas posteriores. user_tables; consulta el diccionario de
¿Cómo se obtendrían estos metadatos la base de datos para obtener una lista
del diccionario de la base de datos? de nombres de tablas que pertenecen al
usuario actual.

Al consultar la tabla JOBS para cada Se realiza una proyección ya que las
registro que contiene solo las columnas columnas en la tabla JOBS se han
restringido a las columnas JOB_ID y
JOB_ID y MAX_SALARY, ¿se está
MAX_SALARY.
realizando una proyección, selección o
unión?

2.2.3. Expresiones y operadores SQL


Una expresión consiste en una combinación de símbolos y operadores que el motor de la base de datos
evalúa para obtener un único valor de datos.
Existen una serie de operadores básicos: los operadores aritméticos cardinales de suma, resta,
multiplicación y división para columnas numéricas; el operador de concatenación para caracteres o
cadenas de texto; y los operadores de suma y resta para las columnas de fecha y hora.

Como en la aritmética regular, hay un orden predefinido de evaluación (prioridad del operador) cuando
existe más de un operador en una expresión. Los paréntesis tienen la mayor prioridad. Las operaciones
de división y multiplicación son las siguientes en la jerarquía y se evalúan antes de la suma y la resta,
que tienen la prioridad más baja. Los operadores con el mismo nivel de prioridad se evalúan de izquierda
a derecha.

Por lo tanto, los paréntesis pueden utilizarse para evaluar y otorgar prioridad a lo que haya dentro de
ellos, sea el operador que sea. Se recomienda utilizar paréntesis generosamente cuando se construyen
consultas complejas ya que conduce a un código legible que es menos propenso a errores.

Los niveles de prioridad se muestran en la tabla siguiente:

Nivel de prioridad Símbolo del operador Operación


Alta () Corchete o paréntesis
Media / División
Media * Multiplicación
Baja - Resta
Baja + Suma

49/306
SQL

a. Operadores aritméticos
Considérese el ejemplo de la tabla JOB_HISTORY, que almacena la fecha de inicio y la fecha de finalización
referentes al tiempo que ha permanecido un empleado en un cargo de trabajo anterior. Puede ser útil
para fines tributarios o de pensión, por ejemplo, calcular cuánto tiempo trabajó ese empleado en ese
cargo. Esta información se puede obtener usando una expresión aritmética. A continuación se ve un
ejemplo de esta consulta, en la cual hay algunos elementos interesantes tanto en la propia consulta SQL
como de los resultados obtenidos (el resultado es devuelto en horas).

Se han especificado siete operaciones en la cláusula SELECT. Las primeras cuatro son columnas regulares
de la tabla JOB_HISTORY, concretamente: EMPLOYEE_ID, JOB_ID, START_DATE y END_DATE. Los dos
últimos términos proporcionan la información de origen necesaria para calcular la cantidad de días y
horas que un empleado ocupó un puesto determinado.

Véase el número de empleado 176 en el noveno registro de salida. Este empleado comenzó como gerente
de ventas el 1 de enero de 2007 y finalizó el empleo el 31 de diciembre de 2007. Por lo tanto, este
empleado trabajó durante exactamente un año, que en 2007 consistió en 365 días o 2.920 horas.

El número de días para los cuales un empleado estuvo contratado se puede calcular utilizando el quinto
término en la cláusula SELECT, que es una expresión. Esta expresión demuestra que la aritmética
realizada en columnas que contienen información de fecha arroja valores numéricos que representan un
cierto número de días. El sexto término se asemeja mucho al quinto, pero calcula el número de horas
trabajadas al multiplicar por 8 (suponiendo un día laboral de 8 horas).

Para imponer la prioridad del operador de las operaciones de resta y suma en el sexto término, la
subexpresión end_date - start_date + 1 está encerrada entre paréntesis y luego multiplicada por 8 para
obtener la cantidad correcta de horas trabajadas. El séptimo término se evalúa primero multiplicando 1
por 8, que se agrega a end_date - start_date, que devuelve los resultados incorrectos.

50/306
SQL

Véase ahora otro ejemplo. Se ha diseñado una fórmula hipotética para predecir la probabilidad de una
lluvia de meteoritos en una región geográfica particular. Las dos consultas que aparecen en la imagen
siguiente son idénticas, excepto por la expresión Meteor Shower Probability.
Sin embargo, como lo demuestran los resultados en la siguiente tabla, cada expresión está haciendo un
cálculo diferente. Hay que tener cuenta que las dos expresiones difieren muy levemente. La segunda
consulta tiene un par de paréntesis al final, que engloban (10 - 5).

Véase como son evaluada para Asia (con REGION_ID 3) las expresiones de ambas consultas:

Paso Consulta 1 Consulta 2

1. region_id * 100/5 + 20 / 10 − 5 region_id * 100/5 + 20 / (10 − 5)

2. Sustituir region_id con el valor : Sustituir region_id con el valor:

3 * 100 /5 + 20 / 10 −5 3 * 100 / 5 + 20 / (10 − 5)

51/306
SQL

3. Los operadores con la prioridad más alta son El operador con la prioridad más alta es el par
los operadores de división y multiplicación. de paréntesis y estos deben evaluarse primero.
Por lo tanto, la primera subexpresión que se
Estos deben ser evaluados primero. Si más de evaluará es: (10 - 5): 3*100/5 + 20/5
un operador con el mismo nivel de prioridad
está presente en una expresión, luego estos se
evaluarán de izquierda a derecha. Por lo tanto,
la primera subexpresión que se evaluará es: 3 *
100: 300/5 + 20/10 - 5

4. La siguiente subexpresión a evaluar es: Los siguientes operadores en la expresión con


300/5: 60 + 20/10 -5 la prioridad más alta son los operadores de
división y de multiplicación. Si hay más de un
operador con el mismo nivel de prioridad en una
expresión, estos se evaluarán de izquierda a
derecha.

Por lo tanto, la siguiente subexpresión que se


evaluará es:
3 * 100: 300/5 + 20/5

5. La siguiente subexpresión a evaluar es: 20/10: La siguiente subexpresión que se evaluará es:
60 + 2 - 5 300/5: 60 + 20/5

6. Los operadores restantes son operadores de La siguiente subexpresión que se evaluará es:
suma y resta que comparten el mismo nivel de 20/5: 60 + 4 = 64
prioridad. Por lo tanto, estos se evaluarán de
izquierda a derecha. La siguiente subexpresión
a evaluar es:
60 + 2: 62 - 5 = 57

b. Expresión y alias de columnas


Un alias es un nombre alternativo para una columna o una expresión. Como se vio en el ejemplo que
calculaba las horas trabajadas por un empleado, las tres columnas constituidas por expresiones utilizaban
alias: “Days worked”, “Correct hours worked” y “Incorrect hours worked”. De no haber sido así, los
encabezados de las columnas serían del tipo (END_DATE - START_DATE) +1, lo cual no es muy
descriptivo. Los alias son especialmente útiles con expresiones o cálculos y pueden implementarse de
varias maneras. Existen algunas reglas que rigen la forma de usar alias para hacer referencia a columnas
en las instrucciones SELECT.
Como muestra la imagen siguiente, se devuelve un error "ORA-00923: FROM keyword not found where
expected " cuando un alias formado por más de una palabra no se pone entre comillas.

52/306
SQL

El error ORA-00923 no es generado aleatoriamente por el servidor. El intérprete de Oracle intenta


procesar la consulta y encuentra un problema. A medida que procesa esta consulta, encuentra un
problema con la línea 2: la palabra Worked. La línea 2 se procesó y a la expresión se le asignó el alias
Days. El espacio después de Days indica al intérprete de Oracle que, dado que no hay una coma adicional
para indicar otro término que pertenece a la cláusula SELECT, esta ya ha acabado. A continuación se
esperaría encontrar la cláusula FROM; no obstante, se encuentra la palabra Worked, y se produce el
error. Los mensajes de error del servidor de Oracle son informativos y se deben leer detenidamente para
resolver problemas. Este error se evita entrecomillando el alias que contiene espacios o caracteres
especiales como # o $; esto es, poniendo “Days Worked” en vez de Days Worked.

En el segundo ejemplo de la imagen anterior, se ilustra otra característica interesante de los alias de
columnas. Se ha prescindido de las comillas dobles y, en lugar del carácter espacio, se ha puesto un
guion bajo entre Days y Worked, de forma que Days_Worked es una sola palabra y ya no hay error. El
intérprete de Oracle procesa la sentencia, no encuentra ningún problema y la ejecuta. Aunque el alias se

53/306
SQL

especificó como Days_Worked, el encabezado de consulta se devolvió como DAYS_WORKED: todas las
letras se convirtieron automáticamente a mayúsculas. Por lo tanto, para que se preserven las mayúsculas
y minúsculas del alias, este deberá estar entre comillas dobles.
Los alias encontrados hasta el momento se han especificado dejando un espacio después de una columna
o expresión e insertando el nombre del alias. SQL ofrece una manera más formal de utilizar alias, y es
haciendo uso de la palabra clave AS, la cual se debe poner tras el nombre de la columna o expresión y
delante del nombre que se le quiera dar al propio alias.
La siguiente imagen ilustra las diferentes formas de poner alias a las columnas. Tanto las columnas
EMPLOYEE_ID como JOB_ID reciben un alias utilizando la palabra clave AS, mientras que la consulta
"Days Worked" recibe un alias utilizando un espacio. La palabra clave AS es por lo tanto opcional; sin
embargo, el uso de AS mejora la legibilidad de las consultas.

c. Operador de concatenación de expresiones y caracteres


Los símbolos || representan el operador de concatenación de caracteres. Este operador se utiliza para
unir expresiones de caracteres o columnas para crear una expresión de caracteres más compleja.

54/306
SQL

La figura anterior muestra que el operador de concatenación puede ser usado múltiples veces y casi en
cualquier lugar en una expresión de caracteres.
Aquí se concatena el carácter de cadenas "The" con el contenido de datos de la columna REGION_NAME.
Esta nueva cadena de caracteres se concatena con la cadena de caracteres "region is on Planet Earth",
y toda la expresión tiene el alias "Planetary Location". Se puede observar cómo se construye cada registro
del conjunto de resultados mediante la aplicación sistemática de la expresión a cada valor del registro
de la tabla.

Véase el primer registro de datos de la columna "Planetary Location", el cual devuelve "The Europe region
is on Planet Earth". Se ha creado una frase legible para los registros de datos mediante la concatenación
de cadenas de caracteres y espacios a ambos lados del valor de la columna REGION_NAME de cada
registro. A la columna REGION_ID se le ha puesto un alias para mostrar que tanto las columnas regulares
como las expresiones pueden recibir un alias. Además, los encabezados de columna se muestran por
defecto en mayúsculas, pero se pueden sustituir con un alias como "Region Id".

55/306
SQL

d. Cadenas de caracteres y tabla DUAL


Las cadenas de caracteres en las expresiones son una ocurrencia común. Estos valores hacen referencia
a valores numéricos, caracteres o de fecha y hora que se encuentran en las cláusulas SELECT y no se
originan a partir de ningún objeto de la base de datos.

¿Qué hay del procesamiento de cadenas de caracteres que no tienen nada que ver con los datos de
columnas existentes? Para asegurar la consistencia relacional, Oracle ofrece una solución inteligente al
problema de usar la base de datos para evaluar expresiones que no tienen nada que ver con ninguna
tabla o columna.

Para que la base de datos evalúe una expresión, se debe enviar una sentencia SELECT sintácticamente
correcta. ¿Y si se quisiera saber la suma de dos números o dos cadenas de caracteres de tipo numérico?
Esta información sólo se puede obtener interactuando con la base de datos de manera relacional.
Oracle soluciona el problema de las interacciones relacionales con una base de datos que opera con
expresiones de cadena de caracteres a través de una tabla “especial” denominada DUAL.
La tabla DUAL está constituida por una única columna de tipo de dato carácter. Permite evaluar
expresiones constituidas por cadenas de caracteres y devolver el nuevo valor de la expresión para su
posterior procesamiento.
Se va a presentar un ejemplo sobre cómo calcular los segundos que hay en un año.

La figura anterior muestra una consulta aritmética ejecutada en la tabla DUAL. Probar expresiones
complejas durante el desarrollo, a través de la tabla DUAL, es un método eficaz para evaluar si estas
expresiones están funcionando correctamente. Las expresiones de cadenas de caracteres pueden ser
consultadas desde cualquier tabla, pero cabe recordar que será procesada para cada registro de la tabla.

SELECT 'literal '||'processing using the REGIONS table'


FROM regions;

56/306
SQL

La consulta anterior devolverá cuatro registros en el conjunto de resultados, ya que hay cuatro registros
de datos en la tabla REGIONS.

e. Comillas simples o el operador alternativo quote


Las cadenas de caracteres concatenadas han sido hasta ahora cadenas de texto añadidas al inicio o al
final de las expresiones que conforman la columna.
Estas cadenas de caracteres se especifican mediante comillas simples. Por ejemplo:

SELECT 'I am a character literal string' FROM dual;

¿Qué hay de las cadenas de caracteres que contienen comillas simples? Por ejemplo, si se añade un
apostrofo, eso causaría un error ya que se considerará como final de la cadena de caracteres.
Véase, la siguiente consulta:

SELECT 'Plural's have one quote too many' FROM dual;

Al ejecutar esta consulta se genera un error de Oracle ORA-01756. Puede parecer un error extraño, pero
al examinarlo más de cerca, el intérprete de Oracle procesa con éxito la instrucción SELECT hasta la
posición 16, momento en el que espera una cláusula FROM. La posición 1 a la posición 16 es:

select 'Plural's

El servidor de Oracle procesa este segmento como si “s” fuese el alias de ‘Plural’.
En este punto, el intérprete espera una cláusula FROM, pero en su lugar encuentra la palabra "have". A
continuación, se genera el error.

Entonces, ¿cómo se tratan las palabras que contienen comillas simples? Hay esencialmente dos
mecanismos disponibles. El más popular de ellos es añadir una comilla simple adicional junto a cada
comilla simple natural en la cadena de caracteres.

La imagen siguiente muestra que usando comillas simples, para arreglar el problema anterior, puede
volverse un tanto confuso en el caso que se dé muchas veces en una misma consulta.

57/306
SQL

Para solucionarlo, Oracle propone otra alternativa: el operador alternativo q, de quote en inglés.
El operador q permite elegir entre un conjunto de caracteres para sustituir y hacer el papel de las comillas
simples.
Las opciones son cualquier carácter de un byte o multibyte o en su defecto (paréntesis), {llaves},
[corchetes], o < signos de menor que y mayor que >. Usando el operador q, el delimitador de caracteres
puede ser cambiado de una sola comilla a cualquier otro carácter, como se muestra en la figura siguiente.

58/306
SQL

La sintaxis del operador alternativo es la siguiente:

q'delimiter carácter literal que puede incluir las comillas simples delimiter'

Donde delimiter puede ser cualquier carácter o paréntesis. Los ejemplos primero y segundo de la imagen
anterior muestran el uso de corchetes y signos de menor que y mayor que como delimitadores de
caracteres, mientras que el tercer ejemplo demuestra cómo se ha utilizado una "X" mayúscula como
símbolo delimitador de caracteres especiales.

2.2.4. El concepto NULL


El concepto de valor nulo (NULL) se introdujo anteriormente. Tanto el número cero como un espacio en
blanco son diferentes al valor nulo ya que ocupan espacio. NULL hace referencia a la ausencia de datos.
Un registro que contiene un valor nulo carece de datos para esa columna. NULL es pues definido
formalmente como un valor que no está disponible, no está asignado, es desconocido o inaplicable. Si

59/306
SQL

no se tiene en cuenta el tratamiento especial que requieren los valores nulos, cabe la posibilidad que
produzca un error o, peor aún, una respuesta inexacta.
Los valores nulos pueden ser un concepto difícil de entender. No es un valor real y tangible que pueda
relacionarse con el mundo físico. NULL es un marcador de posición en memoria en una columna no
obligatoria (NOT NULL column) hasta que algunos datos reales se almacenan en su lugar.
Esta sección pretende interactuar con campos o valores nulos (NULL) en consultas SELECT para poder
observar los resultados de valores nulos en diferentes expresiones.

a. Columnas que no pueden contener valores nulos (NOT NULL columns) y


columnas que pueden contener valores nulos (NULLABLE columns)
Las tablas almacenan registros de datos que se dividen en una o más columnas. Estas columnas tienen
nombres y tipos de datos asociados. Algunas de ellas están limitadas por las reglas de la base de datos
a ser columnas que deben contener datos. Es obligatorio que haya datos almacenados en las columnas
NOT NULL de cada registro. Sin embargo, cuando las restricciones de la base de datos no obligan a las
columnas de una tabla a mantener los datos de un registro, estas columnas corren el riesgo de estar
vacías.
En la primera figura que podemos observar a continuación, se describe la tabla EMPLOYEES y se
seleccionan algunas columnas de la misma. Hay cinco columnas NOT NULL y seis columnas que sí pueden
contener valores nulos (NULLABLE columns).

NULLABLE es un término que, por lo tanto, se utiliza para describir una columna que puede contener
valores nulos. Una de las columnas NULLABLE es la columna COMMISSION_PCT.

Esta figura muestra los primeros cinco registros de datos de la tabla EMPLOYEES. Esto es suficiente para
ver que todos estos registros de empleado tienen valores nulos en sus columnas COMMISSION_PCT.

60/306
SQL

SQL Developer facilita la visualización de valores nulos en columnas, como se muestra en la segunda
figura. Aquí, la palabra (null) se muestra en el resultado de la consulta cuando se encuentra un valor
nulo, como en la columna COMMISSION_PCT. SQL Developer soporta la personalización de esta
descripción predeterminada de datos nulos.

61/306
SQL

La columna con el alias "Null Arithmetic" es una expresión compuesta por COMISSION_PCT + SALARY +
10. En lugar de devolver un valor numérico, esto devuelve un valor nulo. Hay una razón importante por
la que sucede esto:

Cualquier cálculo aritmético con un valor NULL siempre devuelve NULL.

Oracle ofrece un mecanismo para interactuar aritméticamente con valores NULL usando las funciones
generales discutidas en el Capítulo 5. Como la expresión de la columna con el alias "Division by Null"
ilustra, incluso la división por un valor nulo resulta en nulo, a diferencia de la división por cero, que da
como resultado un error. Por último, obsérvese el impacto del valor nulo cuando se utiliza con el operador
de concatenación de caracteres. NULL está concatenado entre las columnas FIRST_NAME y LAST_NAME,
pero no tiene ningún impacto.

Los operadores de concatenación de caracteres ignoran los valores nulos, mientras que las operaciones
aritméticas que involucran valores nulos siempre resultan nulas.

62/306
SQL

b. Claves foráneas y columnas que pueden contener valores nulos (NULLABLE


columns)
El diseño del modelo de datos a veces lleva a situaciones problemáticas cuando las tablas están
relacionadas entre sí a través de una relación de clave primaria y clave foránea, pero la columna en la
que se basa la clave foránea puede tener valores nulos (es NULLABLE).
La tabla DEPARTMENTS tiene como clave principal DEPARTMENTS_ID. La tabla EMPLOYEES tiene una
columna DEPARTMENT_ID que está restringida por su relación de clave foránea a la columna
DEPARTMENT_ID en la tabla DEPARTMENTS. Esto significa que no se permite que ningún registro de la
tabla EMPLOYEES tenga en su columna DEPARTMENT_ID un valor que no esté en la tabla DEPARTMENTS.
Esta integridad referencial forma la base de la tercera forma normal y es crítica para la integridad general
de los datos.

¿Pero qué hay de los valores NULL? ¿Puede la columna DEPARTMENT_ID en la tabla DEPARTMENTS
contener valores nulos? La respuesta es no. Oracle insiste en que cualquier columna que es una clave
primaria está implícitamente restringida a ser obligatoria (NOT NULL). ¿Pero qué hay de las restricciones
implícitas en las claves foráneas? Este es un problema para Oracle, ya que, para permanecer flexible, no
puede pedir que las columnas relacionadas a través de restricciones de integridad referenciales deban
ser obligatorias. Además, no todas las situaciones exigen esta funcionalidad.

La columna DEPARTMENT_ID de la tabla EMPLOYEES puede contener valores nulos. Por lo tanto, existe
el riesgo de que existan registros con valores DEPARTMENT_ID nulos presentes en esta tabla. De hecho,
existen tales registros en la tabla EMPLOYEES. El modelo de datos de HR permite que los empleados,
correctamente o no, no pertenezcan a ningún departamento. Cuando se realizan uniones relacionales
entre tablas, es totalmente posible que se pierda o excluyan ciertos registros que contienen nulos en la
columna de unión. En el capítulo 7 se verán las formas de hacer frente a este desafío mediante el uso
de uniones externas.

Problema Solución

Se está construyendo una Sí, pero no con la información


expresión aritmética que calcula que se tiene hasta ahora. Los
los ingresos imponibles a partir de valores nulos requieren un
las columnas SALARY y tratamiento especial. En el
COMMISSION_PCT de un Capítulo 5, se verá la función
empleado, que son nulas. ¿Es NVL, que proporciona un
posible convertir a cero los valores mecanismo para convertir
nulos en cualquiera de las valores nulos en valores de datos
columnas para siempre devolver más fáciles de calcular.
un ingreso numérico imponible?

63/306
SQL

Un alias proporciona un Si un alias contiene más de una


mecanismo para renombrar una palabra o si deben conservarse
columna o una expresión. ¿Bajo las mayúsculas o minúsculas,
qué condiciones se debe incluir un deberá escribirse entre comillas
alias entre comillas dobles? dobles. Si no se pone entre
comillas dobles un alias de varias
palabras, se producirá un error
de Oracle. Si no se pone entre
comillas dobles un alias de una
sola palabra, el alias se devuelve
en mayúsculas.

Al trabajar con cadenas de Hay dos mecanismos. El enfoque


caracteres que incluyen comillas más común es reemplazar cada
comilla simple por comillas
simples, ¿cómo debería especificar
dobles. El otro enfoque es hacer
estos valores literales en la uso del operador de comillas
cláusula SELECT sin generar un alternativo para especificar un
error? par de caracteres alternativos
con los que encerrar cadenas de
caracteres.

2.3. Resumen
Listar las Posibilidades de la Sentencia SQL SELECT

• Las tres operaciones fundamentales de las sentencias SELECT son proyección, selección y unión.
• Proyección hace referencia a la restricción de columnas seleccionadas de una tabla. Usando
proyección, se recuperan sólo las columnas de interés.
• Selección hace referencia a la extracción de registros de una tabla. La selección incluye la
restricción adicional de los registros extraídos basados en varios criterios o condiciones. Esto
permite recuperar sólo los registros que son de interés y no todos los registros de la tabla.
• Unión implica enlazar dos o más tablas basadas en campos comunes. La unión permite que los
datos se almacenen en tercera forma normal en tablas discretas, en lugar de en una gran tabla.
• Una combinación ilimitada de proyecciones, selecciones y uniones proporciona el lenguaje para
extraer los datos relacionales requeridos.
• La definición estructural de una tabla puede obtenerse usando el comando DESCRIBE.
• Las columnas de las tablas almacenan diferentes tipos de datos, los más comunes son NUMBER,
VARCHAR2, DATE, y TIMESTAMP.
• El tipo de datos NUMBER(p, s) almacena datos numéricos, tanto enteros como decimales, con o
sin signo. Precisión (p), indica el número máximo de dígitos que va a tener el dato. Escala (s),
indica el número de dígitos que puede haber a la derecha del punto decimal.
• El comando DESCRIBE lista los nombres, tipos de datos y si una columna puede ser o no NULL.
• Las columnas obligatorias también se denominan columnas NOT NULL.

Ejecutar una Sentencia Básica SQL SELECT

• La sintaxis de la cláusula primitiva SELECT es la siguiente:

64/306
SQL

SELECT *|{columna[DISTINCT]|expresión[alias], ...}}

• La sentencia SELECT también se denomina consulta SELECT y está comprendida por al menos
dos cláusulas, a saber, la cláusula SELECT y la cláusula FROM.
• La cláusula SELECT determina la proyección de las columnas. En otras palabras, la cláusula
SELECT especifica qué columnas se incluyen en los resultados devueltos.
• El operador asterisco (*) se utiliza como símbolo comodín para indicar todas las columnas. Así,
la sentencia SELECT * FROM accounts devuelve todas las columnas disponibles en la tabla
ACCOUNTS.
• La cláusula FROM especifica la tabla o tablas fuente a partir de las cuales los datos son
seleccionados.
• La palabra clave DISTINCT se añade a las sentencias SELECT, justo después de la palabra clave
SELECT. Devuelve valores únicos, ya que, en una tabla, una columna puede contener valores
duplicados y algunas veces sólo se necesita un listado de los valores diferentes.
• Las sentencias SQL deben terminar con punto y coma. Como alternativa, se puede añadir una
nueva línea después de una sentencia y se puede utilizar una barra oblicua hacia delante (/) para
que se ejecute la expresión.
• Las sentencias SQL pueden escribirse y ejecutarse en minúsculas, mayúsculas o mixto. Se debe
tener cuidado al interactuar con las cadenas de caracteres ya que distinguen entre mayúsculas
y minúsculas.
• Los operadores aritméticos y el operador de concatenación de cadenas que actúan sobre datos
de columna y las cadenas de caracteres forman la base de las expresiones SQL.
• Se puede utilizar un alias para las expresiones y columnas regulares utilizando la palabra clave
AS o dejando un espacio entre la columna o expresión y el alias.
• Si un alias contiene varias palabras o en caso de que la combinación entre mayúsculas y
minúsculas sea relevante en el alias, debe ir entre comillas dobles.
• Para poder incluir dentro de una cadena de caracteres comillas dobles se tiene que encapsular
dentro de comillas simples.
• La tabla DUAL es una tabla de una sola columna y un solo registro que se utiliza a menudo para
evaluar expresiones que no tienen que ver con columnas o tablas específicas.
• Las columnas que no tienen una restricción NOT NULL tienen la opción para almacenar valores
nulos y a veces se denominan columnas nulas (nullable columns).
• Los valores NULL no son lo mismo que un espacio en blanco o cero. Los valores NULL hacen
referencia una ausencia de datos. El valor nulo (NULL) se define como un valor que no está
disponible, no asignado, desconocido o inaplicable.
• Hay que tener cuidado cuando se trabaja con valores nulos ya que las operaciones aritméticas
con un valor nulo siempre producen un resultado nulo.

3. Restringiendo y ordenando datos

65/306
SQL

3.1. Limitar los registros recuperados de una consulta (Query)


Limitar las columnas recuperadas por una instrucción SELECT se conoce como proyección y se introdujo
en el capítulo 2. La restricción de los registros devueltos se conoce como selección. En este capítulo se
discute la cláusula WHERE, que es una mejora de la funcionalidad de selección de la secuencia SELECT.

La cláusula WHERE especifica una o más condiciones que el servidor Oracle evalúa para restringir los
registros devueltos por la petición. Otra mejora del lenguaje se introduce con la cláusula ORDER BY que
proporciona capacidades de clasificación de datos.
La sustitución por ampersand (&) proporciona una forma de reutilizar la misma sentencia para ejecutar
diferentes consultas mediante la sustitución de elementos de consulta en tiempo de ejecución. Este
capítulo concluye con una exploración de esta técnica de encuadernación en tiempo de ejecución en
sentencias SQL.
Uno de los principios fundamentales de la teoría relacional es la selección. La selección se actualiza
utilizando la cláusula WHERE de la secuencia SELECT. Las condiciones que restringen el conjunto de datos
devuelto adoptan muchas formas y funcionan tanto en columnas como en expresiones. Sólo los registros
en la tabla que se adecúen a estas condiciones serán devueltos. Las condiciones restringen los registros
que utilizan operadores de comparación junto con columnas y valores literales. Los operadores booleanos
proporcionan un mecanismo para especificar múltiples condiciones para restringir los registros devueltos.
Se discuten los operadores booleanos, condicionales, de concatenación y aritméticos para establecer su
orden de precedencia cuando se encuentran en una sentencia SELECT.

Se investigan las siguientes cuatro áreas:

• La cláusula WHERE
• Los operadores de comparación
• Operadores booleanos Reglas de precedencia

3.1.1. La cláusula WHERE


La cláusula WHERE amplía la declaración SELECT proporcionando el lenguaje para restringir los registros
devueltos en función de una o más condiciones. La consulta de una tabla sólo con las cláusulas SELECT
y FROM da como resultado el retorno de todos los registros almacenados en la tabla. Utilizando la palabra
clave DISTINCT, se excluyen los valores duplicados y los registros devueltos se limitan hasta cierto grado.
¿Y si la información requerida de una tabla es muy específica, como, por ejemplo, los datos en los que
una columna contiene un valor específico? ¿Cómo se recuperarían los países que pertenecen a la región
de Europa de la tabla de COUNTRIES? ¿Y si sólo se quiere saber que empleados trabajan en ventas?
Estas preguntas se responden utilizando la cláusula WHERE para especificar exactamente qué registros
deben devolverse. El formato de la sentencia SQL SELECT que incluye la cláusula WHERE es:

SELECT*|{[DISTINCT] column|expression [alias],…}


FROM table
[WHERE condition(s)];

La cláusula WHERE siempre sigue a la cláusula FROM. Los corchetes indican que la cláusula WHERE es
opcional. Se pueden aplicar simultáneamente una o más condiciones para restringir el conjunto de
resultados. Una condición se especifica comparando dos términos utilizando un operador condicional.
Estos términos pueden ser valores de columna, literales o expresiones. El operador de igualdad es el más
utilizado para restringir los conjuntos de resultados. A continuación se muestran dos ejemplos de las
cláusulas WHERE:

66/306
SQL

SELECT country_name
FROM countries
WHERE region_id=3;

SELECT last_name, first_name


FROM employees
WHERE job_id='SA_REP';

El primer ejemplo proyecta la columna COUNTRY_NAME desde la tabla COUNTRIES. En lugar de


seleccionar cada registro, la cláusula WHERE restringe los registros devueltos a solo los que contengan
un 3 en la columna REGION_ID.

El segundo ejemplo devuelve dos columnas, LAST_NAME y FIRST_NAME de la tabla EMPLOYEES. Los
registros devueltos se limitan a las que contienen el valor SA_REP en sus columnas JOB_ID.

a. Condiciones basadas en números


Las condiciones deben formularse adecuadamente para los diferentes tipos de datos de columna. Las
condiciones que limitan los registros basados en columnas numéricas se pueden especificar de varias
maneras diferentes. Considere la columna SALARY en la tabla EMPLOYEES. Esta columna tiene un tipo
de datos NUMBER(8,2). En la figura que se muestra a continuación se muestran dos formas diferentes
en que se ha restringido la columna SALARY. El primer y segundo ejemplo recupera los valores
LAST_NAME y SALARY de los empleados que ganan $10,000. Observe la diferencia en las cláusulas
WHERE de las siguientes consultas. La primera consulta especifica el número 10000, mientras que la
segunda encierra el número entre comillas simples como un carácter literal. Ambos formatos son
aceptables para Oracle ya que se realiza una conversión implícita de tipos de datos cuando es necesario.

SELECT last_name, salary


FROM employees
WHERE salary = 10000;

SELECT last_name, salary


FROM employees
WHERE salary = '10000';

67/306
SQL

Una columna numérica puede ser comparada con otra columna numérica en el mismo registro para
construir una condición en la cláusula WHERE, como muestra la siguiente consulta:

SELECT last_name, salary, department_id


FROM employees
WHERE salary = department_id;

En el ejemplo de la figura que se muestra más abajo se puede apreciar como la cláusula WHERE es
demasiado restrictiva y no se selecciona ningún registro. Esto se debe a que el rango de SALARY es de
2100 a 24000, y el rango de valores de DEPARTMENT_ID es de 10 a 270. Dado que no hay solapamiento
en el rango de DEPARTMENT_ID y SALARY, no hay registros que satisfagan esta condición y, por lo tanto,
la consulta no devuelve ningún resultado. El ejemplo también ilustra cómo una condición de cláusula
WHERE compara una columna numérica con otra. El segundo ejemplo de la figura muestra la extensión
de la cláusula WHERE para comparar una columna numérica SALARY con la expresión numérica:
DEPARTMENT_ID*100. Para cada registro, el valor de la columna SALARY es comparado con el producto
del valor DEPARTMENT_ID y 100. La cláusula WHERE también permite expresiones a ambos lados del
operador de comparación. Se puede emitir la siguiente expresión para obtener resultados idénticos:

SELECT last_name, salary, department_id


FROM employees
WHERE salary/10 = department_id*10;

Como en álgebra regular, la expresión (SALARY = DEPARTMENT_ID * 100) es equivalente a (SALARY /10
= DEPARTMENT_ID * 10). La característica notable de este ejemplo es que los términos a ambos lados
del operador de comparación son expresiones.

68/306
SQL

b. Condiciones basadas en caracteres


Las condiciones que determinan qué registros se seleccionan en base a los datos en formato texto se
especifican adjuntando una cadena de caracteres que sea literalmente el registro que se quiere recuperar
de la base de datos. La columna JOB_ID de la tabla EMPLOYEES tiene un tipo de datos VARCHAR2(10).
Supóngase que desea un informe que conste de los valores LAST_NAME de los empleados que
desempeñan actualmente el trabajo de representantes de ventas. El valor JOB_ID para un representante
de ventas es SA_REP. La siguiente declaración produce dicho informe:

SELECT last_name
FROM employees
WHERE job_id='SA_REP';

Si se intenta especificar la cadena de caracteres sin las comillas, se producirá un error de Oracle.
Recuerde que los datos de cadena de caracteres se distinguen entre mayúsculas y minúsculas, por lo
que las siguientes cláusulas WHERE no son equivalentes.

Cláusula 1: WHERE job_id=SA_REP


Cláusula 2: WHERE job_id='Sa_Rep'
Cláusula 3: WHERE job_id='sa_rep'

Cláusula 1 genera un error “ORA-00904: “SA_REP”: invalid identifier” ya que la cadena de caracteres
SA_REP no está envuelto en comillas simples. Cláusula 2 y 3 son sintácticamente correctas pero no
equivalentes. Además, ninguna de estas cláusulas produce datos ya que no hay registros en la tabla
EMPLOYEES que tengan valores de columna JOB_ID que sean Sa_Rep o sa_rep,sino SA_REP como se
muestra en la siguiente figura:

69/306
SQL

Las condiciones basadas en caracteres no se limitan a comparar valores de columna con cadenas de
texto exactas. También se pueden especificar utilizando otras columnas tipo texto y expresiones. Las
columnas LAST_NAME y FIRST_NAME se especifican como columnas escritas con datos VARCHAR2(25).
Se considera la consulta:

SELECT employee_id, job_id, last_name, first_name


FROM employees
WHERE last_name=first_name;

Tanto la columna LAST_NAME como la columna FIRST_NAME aparecen a ambos lados del operador de
igualdad en la cláusula WHERE. No hay cadenas de caracteres exactas presentes en la consulta; por lo
tanto, no se necesitan comillas para delimitarlos. Esta condición estipula que sólo se devolverán los
registros que contengan el mismo valor de datos (una coincidencia exacta entre mayúsculas y
minúsculas) en las columnas LAST_NAME y FIRST_NAME. Esta condición es demasiado restrictiva y,
como muestra la figura siguiente, no se devuelven registros.

70/306
SQL

Las expresiones basadas en caracteres forman una o ambas partes de una condición separadas por un
operador condicional. Estas expresiones pueden formarse concatenando cadenas de caracteres o
caracteres simples con una o más columnas de caracteres. Las siguientes cuatro cláusulas muestran
algunas de las opciones para las condiciones basadas en caracteres:

Cláusula 1: WHERE 'A ' || last_name || first_name = 'A King'


Cláusula 2: WHERE first_name || ' ' || last_name = last_name || ' ' ||first_name
Cláusula 3: WHERE 'SA_REP' || 'King' = job_id || last_name
Cláusula 4: WHERE job_id || last_name ='SA_REP' || 'King'

La cláusula 1 concatena el carácter "A" con las columnas LAST_NAME y FIRST_NAME. Esta expresión se
compara con la cadena de caracteres "A King" y se devuelve cualquier registro que cumpla esta condición.
La cláusula 2 demuestra que las expresiones de caracteres pueden colocarse a ambos lados del operador
condicional. La cláusula 3 demuestra que las expresiones de cadenas de caracteres también pueden
colocarse a la izquierda del operador condicional. Es lógicamente equivalente a la cláusula 4, que ha
cambiado los operando de la cláusula 3. Tanto la cláusula 3 como la cláusula 4 tienen como resultado
que se devuelva el mismo registro de datos, como se muestra en la figura siguiente.

71/306
SQL

c. Condiciones basadas en fecha


Las columnas de tipo DATE son útiles cuando se almacena información de fecha y hora. Las fechas con
formato de texto deben de estar entrecomilladas al igual que los caracteres; de lo contrario se produce
un error. Cuando se usan en cláusulas WHERE condicionales, las columnas DATE se comparan con otras
columnas DATE o con fechas con formato de texto con TO_DATE. Las fechas escritas en formato texto
se convierten automáticamente en valores DATE basados en el formato de fecha por defecto, que es
DDMON-RR. Si una fecha en formato texto se encuentra en una expresión que involucra una columna de
tipo DATE, se convierte automáticamente en un valor de fecha usando la máscara de formato por defecto.
DD representa días, MON representa las tres primeras letras de un mes, y RR representa un año
compatible con el año 2000 (es decir, si RR está entre 50 y 99, entonces el servidor Oracle devuelve el
siglo anterior, si no, devuelve el siglo actual). También se puede especificar el año completo de cuatro
dígitos, YYYY. Considérese las siguientes cuatro sentencias SQL:

Consulta 1: SELECT employee_id


FROM job_history
WHERE start_date = end_date;

Consulta 2: SELECT employee_id


FROM job_history
WHERE start_date = '01-JAN-2001';
Consulta 3: SELECT employee_id
FROM job_history
WHERE start_date = '01-JAN-01';

Consulta 4: SELECT employee_id


FROM job_history

72/306
SQL

WHERE start_date = '01-JAN-99';

El primer enunciado pone a prueba la igualdad entre dos columnas de tipo DATE. Los registros que
contengan los mismos valores en sus columnas START_DATE y END_DATE serán devueltos. Hay que
tener en cuenta, sin embargo, que los valores tipo DATE sólo son iguales entre sí si hay una coincidencia
exacta entre todos sus componentes, incluyendo día, mes, año, horas, minutos y segundos. En la cláusula
WHERE de la segunda sentencia, la columna START_DATE se compara con el carácter literal '01-JAN-
2001'. Se ha especificado todo el componente de cuatro dígitos del año (YYYY). Esto es aceptable para
el servidor Oracle, y se devolverán todos los registros de la tabla JOB_HISTORY con valores de columna
START_DATE iguales al 1 de enero de 2001.

La tercera expresión es equivalente a la segunda, ya que la cadena de caracteres "01-JAN-01" se


convierte en el valor de fecha 01-JAN-2001. Esto se debe a que el componente de RR es inferior a 50,
por lo que el siglo XXI, 20, se antepone al componente de RR del año para proporcionar un valor de siglo.
Se devolverán todas las líneas de la tabla JOB_HISTORY con los valores de columna START_DATE = 01-
JAN-2001. El componente de siglo para el literal '01- JAN -99' pasa a ser el siglo anterior (vigésimo), 19,
y arroja un valor de fecha de 01- JAN -1999 para la cuarta expresión, ya que el componente RR, 99, es
mayor que 50. Los registros de la tabla JOB_HISTORY con los valores de columna START_DATE = 01-
JAN-1999 se devolverán. La aritmética usando los operadores de suma y resta es soportada en
expresiones con valores tipo DATE. Una expresión como END_DATE - START_DATE devuelve un valor
numérico que representa el número de días entre START_DATE y END_DATE. Una expresión como
START_DATE + 1 devuelve un valor tipo DATE que es 1 día más tarde que START_DATE.
Así que, la siguiente expresión es comprobada en la imagen siguiente:

SELECT start_date, employee_id


FROM job_history
WHERE start_date + 1 = '25-MAR-06';

73/306
SQL

Esta consulta devuelve los registros de la tabla JOB_HISTORY que contiene un valor de START_DATE
igual a 1 día antes del 25-MAR-2006. Por lo tanto, sólo se recuperarán los registros con un valor de
24MAR-2006 en la columna START_DATE.

Las cláusulas condicionales comparan dos términos utilizando operadores de comparación. Es


importante entender los tipos de datos de los términos involucrados para que puedan ser
incluidos entre comillas simples, si es necesario. Un error común es asumir que una cláusula
WHERE es sintácticamente correcta cuando, de hecho, faltan comillas que delimitan cadenas
de caracteres o fecha. Otro descuido común es no ser consciente de que los términos a la
izquierda y a la derecha del operador de comparación en una cláusula condicional pueden ser
expresiones, columnas, cadenas de caracteres o texto.

3.1.2. Operadores de Comparación


El comparador de igualdad es utilizado normalmente para explicar el concepto de restricción de registros
al usar la cláusula WHERE. Existen varios operadores alternativos que también pueden ser utilizados. Los
operadores de desigualdad “menor o igual que” (≤) o “mayor o igual que” (≥) pueden utilizarse para
devolver registros según una condición de desigualdad. El operador BETWEEN facilita la comparación
mediante rangos para probar si el valor de una columna está entre dos valores dados. El operador IN
comprueba un conjunto de valores, devolviendo uno o varios registros si el valor de la columna evaluada
en la condición forma parte del conjunto de cadenas de caracteres. El operador de comparación por
excelencia es LIKE, que permite, según un patrón de coincidencia específico dado, comparar los
componentes de los datos de las columnas con las cadenas de caracteres dadas en el patrón. El último
operador de comparación en esta sección es el operador IS NULL, que devuelve registros donde el valor

74/306
SQL

que contiene la columna es nulo. Estos operadores pueden ser combinados en la cláusula WHERE y serán
analizados a continuación.

a. Igualdad y desigualdad
Limitar los registros devueltos por una consulta conlleva especificar una cláusula WHERE adecuada. Si la
cláusula es demasiado restrictiva, entonces la consulta devolverá uno (o incluso ningún) registro. Por el
contrario, si la condición es demasiado amplia, entonces serán devueltos más registros que los
solicitados.

Analizando los diferentes operadores válidos, éstos deben estar provistos del lenguaje requerido para
devolver exactamente los registros que interesan. Comprobar por igualdad en una condición debe ser
tanto natural como intuitivo. Tal condición se forma utilizando el operador “es igual que” (=). Se
devolverá un registro si la condición de igualdad es verdadera para este registro. Se considera la siguiente
consulta:

SELECT last_name, salary


FROM employees
WHERE job_id='SA_REP';

Se comprueba si la columna JOB_ID en cada registro de la tabla EMPLOYEES coincide literalmente con
los caracteres SA_REP. Deben coincidir todos los caracteres, siendo sensible a mayúsculas y minúsculas.
Cuando se encuentra una coincidencia, los valores para las columnas seleccionadas LAST_NAME y
SALARY se devuelven para este registro como se muestra en la siguiente Figura.

75/306
SQL

Nótese que, aunque la cláusula condicional se basa en la columna JOB_ID, no es necesario que esta
columna sea seleccionada para la consulta.
Las condiciones basadas en desigualdad mejoran la especificación de la cláusula WHERE. En las
comparaciones por rango y por patrón de coincidencia es posible utilizar operadores de igualdad y
desigualdad, pero es preferible utilizar los operadores BETWEEN y LIKE para este tipo de comparaciones.
Los operadores de desigualdad son descritos en la siguiente tabla.

OPERADORES DESCRIPCIÓN

< Menor que

> Mayor que

<= Menor o igual que

>= Mayor o igual que

<> Distinto que

!= Distinto que

Los operadores de desigualdad permiten que se satisfagan las consultas basadas en rangos. Se debe
proporcionar un conjunto de resultados cuando el valor de una columna es mayor que otro. Por ejemplo,
se puede realizar la siguiente consulta para obtener una lista de valores de LAST_NAME y SALARY para
empleados que ganan más de 5000$:

SELECT last_name, salary


FROM employees
WHERE salary > 5000;

Análogamente, para obtener una lista de empleados que ganan menos de 3000$, se puede realizar la
siguiente consulta:

SELECT last_name, salary


FROM employees
WHERE salary < 3000;

Los operadores de desigualdad compuestos (es decir, que tienen más de un símbolo) son utilizados es
las siguientes cuatro cláusulas:

Cláusula 1: WHERE salary <= 3000;


Cláusula 2: WHERE salary >= 5000;

76/306
SQL

Cláusula 3: WHERE salary <> department_id;


Cláusula 4: WHERE salary != 4000+department_id;

La cláusula 1 devuelve los registros que contienen un valor de SALARY menor o igual que 3000. La
cláusula 2 obtiene datos donde el valor de SALARY es mayor o igual que 5000, mientras que las cláusulas
3 y 4 manifiestan las dos formas de los operadores “distinto que”. La cláusula 3 devuelve los registros
con los valores de la columna SALARY que no son iguales a los valores de DEPARTAMENT_ID. El operador
alternativo de “distinto que” en la cláusula 4 muestra las columnas, cadenas de caracteres y expresiones
que pueden ser comparadas usando operadores de desigualdad. Esta cláusula devuelve los registros que
contienen un valor de SALARY que es distinto a la suma de 4000 y del valor de DEPARTAMENT_ID para
este registro.
La desigualdad numérica es intuitiva. La comparación de caracteres y fechas, sin embargo, son más
complejas. Comprobar la desigualdad de caracteres es interesante ya que se comparan las cadenas
situadas a ambos lados del operador de desigualdad en orden lexicográfico. Basado en el conjunto de
caracteres de la base de datos y en NLS (National Language Support –Soporte en idioma nacional-),
cada cadena de caracteres se evalúa utilizando un método de comparación binario o lingüístico. En los
conjuntos de caracteres en inglés, las comparaciones semánticas actúan como se describen en este
capítulo. Con el método predeterminado de comparación binaria, los caracteres tienen asignados un valor
numérico con espacios en blanco, teniendo un valor menor que otros caracteres. Estos valores numéricos
forman la base para la evaluación de la comparación por desigualdad. Se considera la siguiente sentencia:

SELECT last_name
FROM employees
WHERE last_name < 'King';

El carácter cadena de caracteres ‘King’ es convertido a su representación numérica. Utilizando el conjunto


de caracteres de la base de datos de US7ASCII con el conjunto AMERICAN NLS, la cadena de caracteres
‘King’ es convertido a un carácter ordinal con valores: K (75), i (105), n (110), y g (103). Para cada
registro en la tabla EMPLOYEES, la columna de datos LAST_NAME es convertida similarmente a valores
numéricos para cada carácter, los cuales son comparados al transformarlos con los valores numéricos de
los caracteres de la cadena de caracteres ‘King’. Por ejemplo, el registro con valor LAST_NAME=’Kaufling’
es comparado como sigue: El primer carácter de ambas cadenas es ‘K’, con el mismo valor ordinal de
75. El segundo carácter (i=105) es comparado con (a=97). Como (97<105), o (a<i), ‘Kaufling’ < ‘King’
y el registro es seleccionado. El mismo proceso se utiliza para comparar datos numéricos utilizando
operadores de desigualdad aplicados a caracteres numéricos. La diferencia solo está en que el carácter
numérico es convertido, implícitamente, por el servidor de Oracle a valor numérico utilizando ciertos
conjuntos de bases de datos.
Las comparaciones de desigualdad que funcionan con valores de fecha siguen un proceso similar al de
los caracteres numéricos. El servidor de Oracle almacena las fechas en un formato numérico interno y
estos valores se comparan dentro de las condiciones. El 2 de junio siempre ocurre antes del 3 de junio
del mismo año. Por lo tanto, el valor numérico de la fecha 02-JUN-2008 es menor que el valor numérico
de la fecha 03-JUN-2008. Se considera, ahora, la siguiente consulta:

SELECT last_name, hire_date


FROM employees
WHERE hire_date < '01-JAN-2003';

77/306
SQL

Esta consulta recupera el apellido y la fecha de contratación de cada registro de empleado que contiene
un valor de HIRE_DATE anterior a ’01-JAN-2003’. Se devuelven los registros con HIRE_DATE de
empleados a fecha de 31-DEC-2002 o anterior, mientras que los registros con valores de HIRE_DATE de
empleados posteriores a 1 de enero de 2003 no serán devueltos, como se muestran en la siguiente
Figura:

La cláusula WHERE es una extensión fundamental de la sentencia SELECT y forma parte de la


mayoría de las consultas. Aunque existen muchos operadores de comparación, la mayoría de
condiciones están basadas en la comparación de dos términos utilizando tanto los operadores
de igualdad como los de desigualdad.

b. Comparador de rangos con el operador BETWEEN


El operador BETWEEN comprueba si un valor de columna o expresión está dentro de un rango con dos
valores límites. Para que la condición sea verdadera, la posición debe ser al menos igual que el valor del
límite inferior y a lo sumo igual que el valor del límite superior.

Si se quiere buscar los apellidos y los salarios de los empleados que ganan un salario en el rango de
3400$ y 4000$, una posible solución utilizando el operador BETWEEN será la siguiente:

SELECT last_name, salary


FROM employees
WHERE salary BETWEEN 3400 AND 4000;

Este operador permite que la condición WHERE sea leída de manera natural. Se devolverán los apellidos
y salarios de todos los empleados que ganan entre 3400$ y 4000$. Los operadores booleanos como AND,

78/306
SQL

OR y NOT serán analizados más adelante en este capítulo, aunque serán introducidos aquí para mejorar
la descripción del operador BETWEEN. El operador AND es utilizado para especificar múltiples condiciones
WHERE, las cuales deben cumplirse para que un registro sea devuelto. Al utilizar el operador AND, el
operador BETWEEN es equivalente a utilizar dos condiciones con los operadores “mayor o igual que” y
“menor o igual que”, respectivamente. La sentencia SQL anterior es equivalente a utilizar la siguiente
sentencia:

SELECT last_name
FROM employees
WHERE salary >= 3400 AND salary <= 4000;

Tal y como se muestra también en la siguiente Figura:

Para el valor de SALARY de un registro, se comprueba primero si éste es mayor o igual que 3400 y,
seguidamente, si es menor o igual que 4000. Si ambas condiciones se cumplen el valor de LAST_NAME
del registro forma parte del conjunto de resultados. Por el contrario, si solo una o ninguna de las
condiciones se satisfacen, el registro no será seleccionado.

79/306
SQL

Por lo tanto, las condiciones especificadas con el operador BETWEEN se pueden denotar de forma
equivalente utilizando dos condiciones de desigualdad, pero es más corto y sencillo especificar el rango
utilizando el operador BETWEEN. La implicación de esta equivalencia es que el mecanismo utilizado para
evaluar operandos numéricos, caracteres y fechas por operadores de desigualdad es lo mismo que por
el operador BETWEEN. La siguiente consulta comprueba si el valor de la columna HIRE_DATE es igual o
posterior a 24-JUL-2004 pero igual o anterior a 07-JUN-2007:

SELECT first_name, hire_date


FROM employees
WHERE hire_date

BETWEEN '24-JUL-2004' AND '07-JUN-2007';

No está restringido a utilizar valores cadenas de caracteres en los operandos del operador BETWEEN, ya
que pueden ser valores de columnas y expresiones tales como la siguiente:

SELECT first_name, hire_date


FROM employees
WHERE '24-JUL-2004'

BETWEEN hire_date+30 AND '07-JUN-2007';

Para que un registro sea devuelto por esta consulta, la cadena de caracteres tipo fecha 24-JUL-1994 debe
estar entre los registros de los valores de la columna HIRE_DATE más 30 días y la cadena de caracteres
tipo fecha 07-JUN-2007.

c. Conjunto de comparación con el operador IN


El operador IN comprueba si un elemento pertenece a un conjunto de valores cadenas de caracteres. El
conjunto se especifica por comas separando las cadenas de caracteres y encerrados entre paréntesis. Si
las cadenas de caracteres son valores de caracteres o de fecha, entonces éstos deben delimitarse
utilizando comillas simples. Se pueden incluir tantas cadenas de caracteres como se deseen. Se considera
el siguiente ejemplo.

SELECT last_name, salary


FROM employees
WHERE salary IN (3000,4000,6000);

El valor de SALARY en cada registro es comparado por igualdad a las cadenas de caracteres especificados
en el conjunto. Si los valores de SALARY son iguales a 3000, 4000 o 6000, los valores de LAST_NAME y
SALARY para esos registros serán devueltos. El operador booleano OR, que se analizará después en este
capítulo, es usado para especificar múltiples condiciones en la cláusula WHERE, donde al menos una de
ellas debe cumplirse para que el registro sea devuelto. El operador IN es, por lo tanto, equivalente a una
serie de condiciones OR. La sentencia SQL anterior puede ser reescrita utilizando múltiples condiciones,
por ejemplo:

SELECT last_name, salary


FROM employees
WHERE salary = 3000
OR salary = 4000

80/306
SQL

OR salary = 6000;

Esta sentencia devolverá los valores de LAST_NAME y SALARY de empleados si al menos una de las
condiciones de la cláusula WHERE es verdadera; esto tiene el mismo significado que la sentencia anterior
en la que se utilizaba el operador IN, como se muestra en la Figura siguiente:

Los componentes del conjunto de pruebas utilizando el operador IN se realizan de manera más rápida
que con múltiples condiciones OR, especialmente a medida que aumenta el número de elementos en el
conjunto. Las dos sentencias siguientes muestran el uso del operador IN con datos tipo DATE y
CHARACTER.

SELECT last_name
FROM employees
WHERE last_name IN ('King','Garbharran','Ramklass');

SELECT last_name
FROM employees
WHERE hire_date IN ('01-JAN-1998','01-DEC-1999');

81/306
SQL

d. Patrones de comparación con el operador LIKE


A modo de resumen, el operador BETWEEN proporciona una manera concisa de especificar condiciones
basadas en rango, y el operador IN proporciona un método óptimo para comprobar el contenido del
conjunto. Ahora se introduce el operador LIKE, el cual se designa exclusivamente a datos de tipo carácter
y proporciona un gran mecanismo para buscar letras o palabras. Al comparar valores numéricos utilizando
el operador LIKE, éstos son automáticamente tratados como datos de tipo carácter. LIKE va acompañado
de dos caracteres comodín: el símbolo del porcentaje (%) y el guion bajo (_). El símbolo del porcentaje
es utilizado para especificar cero o más caracteres, mientras que el guion bajo especifica un carácter
comodín.

La siguiente consulta puede utilizarse para proporcionar una lista de empleados cuyo primer nombre
empieza por la letra “A”:

SELECT first_name
FROM employees
WHERE first_name LIKE 'A%';

El carácter con el que se compara la columna FIRST_NAME está encerrado entre comillas simples como
un carácter. Además, tiene un símbolo de porcentaje, el cual tiene un significado especial en el contexto
del operador LIKE. El símbolo del porcentaje sustituye a cero o más caracteres añadidos a la letra “A”.
Los registros de empleados con valores de FIRST_NAME que empiezan por la letra “A” son devueltos.
Los caracteres ‘comodín’ pueden aparecer al principio, en el medio o al final de la cadena de caracteres.
Incluso pueden aparecer solos, como en:

WHERE first_name LIKE '%';

En este caso, cada registro que contenga un valor de FIRST_NAME que no sea nulo será devuelto. Los
símbolos ‘comodín’ no son obligatorios cuando se utiliza el operador LIKE.
En tal caso, LIKE se comporta como un operador de igualdad comprobando que hay coincidencias exactas
de caracteres; así las dos siguientes cláusulas WHERE son equivalentes:

WHERE last_name LIKE 'King';


WHERE last_name = 'King';
El símbolo del guion bajo sustituye exactamente a otro carácter en una cadena de caracteres. Se
considera la posibilidad de buscar empleados cuyos apellidos sean de cuatro letras, empezando por “K”,
con una segunda letra desconocida y terminando con “ng”, mediante la siguiente sentencia:

WHERE last_name LIKE 'K_ng';

Dependiendo del conjunto de datos, éste podría recuperar, por ejemplo, empleados llamados King, Kong
o Kung. Una forma alternativa para realizar la concordancia de patrones es utilizar una serie interminable
de condiciones OR, pero lograr los resultados anteriores sin usar el operador LIKE es extremadamente
complejo. Por ejemplo, se podría lograr con la siguiente serie de condiciones OR:

WHERE last_name = 'Kang'


OR last_name ='Kbng'
OR last_name ='Kcng'

OR last_name ='Kzng'

82/306
SQL

Este ejemplo está incompleto, ya que no es factible listar todos los caracteres posibles por los que podría
ser sustituido. Este ejemplo muestra el gran esfuerzo que se requiere para sustituir un solo carácter sin
usar el operador LIKE y el símbolo comodín de guion bajo. Para un número desconocido (cero o más) de
sustituciones de caracteres, las posibilidades son exponencialmente mayores que para la sustitución de
un solo carácter. Prácticamente no es posible realizar la concordancia de patrones de caracteres sin el
uso del operador LIKE y los símbolos ‘comodín’.

Como muestra la siguiente Figura, los dos símbolos ‘comodín’ pueden ser usados independientemente,
juntos o incluso varias veces en una sola condición WHERE. La primera consulta recupera aquellos
registros en los que COUNTRY_NAME comienza con la letra "I" seguida de cero o más caracteres, seguida
de una "a" minúscula que a su vez es seguida de cero o más caracteres.

La segunda consulta recupera aquellos países cuyos nombres contienen la letra "i" como su quinto
carácter. La longitud de los valores COUNTRY_NAME y la letra con la que comienzan no son importantes.
Los cuatro símbolos ‘comodín’ de guion bajo que preceden a la "i" minúscula en la cláusula WHERE
representan exactamente cuatro caracteres (que pueden ser cualquier carácter). La quinta letra debe
ser una "i" y el símbolo de porcentaje especifica que el COUNTRY_NAME puede tener cero o más
caracteres del sexto carácter en adelante.

83/306
SQL

¿Qué pasa cuando se está buscando una cadena de caracteres que contiene un carácter porcentaje o
guion bajo? Oracle proporciona una forma de desactivar temporalmente su significado especial y
considerarlos como caracteres regulares utilizando el identificador ESCAPE. La tabla JOBS contiene
valores de JOBS_ID que están especificados con el carácter guion bajo, como SA_MAN, AD_VP, MK_REP
y SA_REP. Supóngase, además, que existe un registro en la tabla JOBS con un valor de JOBS_ID de
SA%MAN (nótese que no hay carácter guion bajo en este JOB_ID). ¿Cómo se pueden recuperar entonces,
de la tabla JOBS si se están buscando valores de JOB_ID que empiezan por los caracteres SA_?
Considérese la siguiente sentencia SQL:

SELECT *
FROM jobs
WHERE job_id LIKE 'SA_%';

Esta consulta devolverá los registros SA_REP, SA_MAN, y SA%MAN. El requisito en este ejemplo no se
cumple, ya que un registro adicional, SA%MAN, que no cumple el criterio de que comienza por los
caracteres SA_, también es devuelto, como se muestra en la siguiente Figura.

Un carácter guion bajo puede ser “escapado” (o tratarse como un símbolo normal no especial) utilizando
el identificador ESCAPE junto con el carácter ESCAPE (\). El segundo ejemplo de la figura anterior
muestra una sentencia de SQL que devuelve registros de la tabla JOBS con valores de JOB_ID iguales a
SA_MAN y SA_REP y que cumplen con el requisito original:

SELECT *
FROM jobs
WHERE job_id LIKE 'SA\_%' ESCAPE '\';

El identificador ESCAPE indica al servidor de Oracle que trate cualquier carácter encontrado después de
la
84/306
SQL

barra invertida como un carácter normal no especial sin significado de comodín. En la cláusula WHERE
anterior, cualquier valor de JOB_ID que empiece con los tres caracteres “SA_” será devuelto.
Tradicionalmente, el carácter ESCAPE es el símbolo de la barra invertida (\), pero no tiene por qué serlo.
La siguiente sentencia es equivalente a la anterior, pero en su lugar utiliza el símbolo de dólar como
carácter ESCAPE.

SELECT job_id
FROM jobs
WHERE job_id LIKE 'SA$_%' ESCAPE '$';

El símbolo del porcentaje puede ser “escapado” de forma similar cuando debe tratarse como un dato de
carácter. Supóngase que hay un requisito para recuperar el registro con el hipotético valor de JOB_ID:
SA%MAN introducido anteriormente. Si se consulta la tabla JOBS para valores de JOB_ID tales como
SA%MAN, utilizando el siguiente código se devolverían los registros SA_MAN y SA%MAN.

SELECT job_id
FROM jobs
WHERE job_id LIKE 'SA%MAN';

El servidor de Oracle interpreta el símbolo de porcentaje en la cláusula WHERE como un símbolo comodín
cuando se utiliza con el operador LIKE. Para obtener el registro con el valor de JOB_ID de SA%MAN
utilizando el operador LIKE, el símbolo de porcentaje debe ser “escapado” utilizando la siguiente
sentencia:

SELECT job_id
FROM jobs
WHERE job_id LIKE 'SA\%MAN' ESCAPE '\';

La barra invertida se define como el carácter ESCAPE que instruye al servidor de Oracle a ignorar las
propiedades comodín del símbolo que aparece inmediatamente después de la barra invertida. De esta
manera, ambos símbolos ‘comodín’ pueden utilizarse como caracteres especializados o normales en
diferentes segmentos de la misma cadena de caracteres.

e. Comparación Nula con el Operador IS NULL


Los valores NULL inevitablemente se encuentran en las tablas de bases de datos. A menudo se requiere
que sólo se busquen aquellos registros que contengan un valor NULL en una columna específica. El
operador IS NULL selecciona solamente los registros donde una columna específica tenga valores NULL.
Para probar por igualdad que los valores de las columnas son NULL, se realiza utilizando el operador IS
NULL en lugar del operador “igual que” (=).

Se considera la siguiente consulta que obtiene las columnas de LAST_NAME y COMMISSION_PCT de la


tabla EMPLOYESS para aquellos registros que tienen valores NULL almacenados en la columna de
COMMISSION_PCT.

SELECT last_name, commission_pct


FROM employees

85/306
SQL

WHERE commission_pct IS NULL;


Esta cláusula WHERE se lee naturalmente y se recuperan solamente los registros que contiene valores
NULL en COMMISION_PCT. Tal y como muestra la Figura siguiente, la consulta utilizando el operador
“igual que” no devuelve ningún registro, mientras que la consulta utilizando el operador IS NULL sí.

3.1.3. Operadores Booleanos


Los datos se restringen utilizando una cláusula WHERE con una condición única. Los operadores,
conocidos cómo booleanos o lógicos permiten especificar las condiciones implícitas en la cláusula WHERE
de la sentencia SELECT, permitiendo así filtrar, específicamente qué datos se quieren extraer de la base
de datos.

Se considera un ejemplo donde existe una tabla con los datos guardados de los empleados. Esta tabla
tiene un campo llamado FIRST_NAME (primer nombre) y COMMISSION_PCT. Si se quisiera extraer
aquellos empleados cuyo FIRST_NAME empiece por la letra “J” y que ganan una COMMISSION_PCT
superior al 10%, se debería hacer una consulta donde se restrinjan estos datos. De esta forma “J%” se
correspondería a la primera condición. En cuanto a la segunda, se deberán testear los datos para
comprobar cuáles son superiores a 10 por ciento. Estas dos condiciones que van a trabajar
conjuntamente se asocian mediante el operador Booleano AND y se aplican consecutivamente en la
cláusula WHERE.

En general, se usarán los operadores Booleanos siempre que se quiera obtener un resultado basado en
la
86/306
SQL

combinación de varios condicionantes. De esta manera, se conseguirá una respuesta en el momento


que todas las condiciones se cumplan, o bien en cuanto no lo haga ninguna de ellas, o bien se obtendrá
el resultado cuando se cumpla la/s condición/es contraria/s a las especificadas, etc.

a. El operador AND
El operador AND une condiciones en una condición más larga, que una línea de una tabla deberá cumplir
para ser incluida en el resultado. Los operadores Booleanos se definen utilizando tablas verdadero-falso.
En la tabla siguiente se define la tabla verdadero-falso para el operador AND, resumiendo así su
funcionalidad.

Si dos condiciones especificadas en una cláusula WHERE se unen mediante un operador AND, el registro
al que pertenecen será testeada para comprobar que ambas se cumplan antes de recuperar los datos e
incluirlos en el conjunto de resultados. Si finalmente ninguna de las condiciones o sólo una de ellas se
cumplen, el registro será excluido del resultado, dado que este es FALSE(falso). Si una de las condiciones
es NULL (campo vacío) causando que una de las condiciones se evalúe a NULL, el registro también se
excluirá.

Condition X Condition Y Result

FALSE FALSE FALSE

TRUE FALSE FALSE

FALSE TRUE FALSE

TRUE TRUE TRUE

TRUE NULL NULL

NULL TRUE NULL

FALSE NULL FALSE

NULL FALSE FALSE

NULL NULL NULL

87/306
SQL

En resumen, el registro sólo se incluirá en el conjunto de resultados si todas las condiciones son evaluadas
como TRUE (verdadero). Si hubiera más de dos condiciones, sólo los casos en los que todas las
condiciones se cumplieran sería incluidos como resultados válidos.
En el ejemplo que se usó anteriormente, todos aquellos empleados con FIRST_NAME empezando por “J”
y además tienen un valor para COMMISSION_PCT superior a 10 por ciento, serán recuperados de la base
de datos mediante la siguiente consulta:

SELECT first_name, last_name,

commission_pct, hire_date
FROM employees
WHERE first_name LIKE 'J%'
AND commission_pct > 0.1;

Nótese que la cláusula WHERE tiene ahora dos condiciones, pero sólo hay una palabra clave WHERE. El
operador AND separa ambas condiciones. Para especificar más condiciones que imperativamente se
deben cumplir, simplemente se añadirán detrás de la segunda condición separándolas cada vez con un
operador AND; De esta manera se pueden especificar tantas condiciones como se desee. Se debe tener
en cuenta que cuantas más condiciones AND se incluyan en una misma consulta, más restrictiva será.
En la Figura siguiente se muestra la consulta anterior con dos nuevas restricciones adicionales. El valor
HIRE_DATE debe ser mayor que 01-JUN-1996 y LAST_NAME debe contener la letra “o”.

88/306
SQL

La primera consulta devolverá 4 registros. Nótese que las condiciones AND adicionales de la segunda
consulta solo son satisfechas por dos registros.

b. El Operador OR
El operador OR separa condiciones múltiples donde al menos una de ellas debe ser satisfecha por el
registro seleccionado para que este se incluya en el conjunto de respuestas. La siguiente es la tabla de
verdadero-falso para el operador OR, que resume su funcionalidad.

Condition X Condition Y Result

FALSE FALSE FALSE

TRUE FALSE TRUE

89/306
SQL

FALSE TRUE TRUE

TRUE TRUE TRUE

TRUE NULL TRUE

NULL TRUE TRUE

FALSE NULL NULL

NULL FALSE NULL

NULL NULL NULL

Si dos condiciones especificadas en una cláusula WHERE se unen con un operador OR, los registros se
evalúan para que una o todas las condiciones se cumplan. Si uno de los requisitos se cumple, es condición
suficiente para que el registro se incluya en el conjunto de resultados. En cambio, si ninguno de estos
requisitos se cumple el resultado será FALSE (falso) y por tanto quedará excluido. En resumen, un
registro será incluido en el conjunto de resultados siempre que al menos uno de los requisitos asociados
al operador OR sea evaluado como TRUE (verdadero).

En el siguiente ejemplo se ve como se escribe la consulta para recuperar a todos aquellos empleados
cuyo FIRST_NAME empiece por “B” o que su COMMISION_PCT sea mayor que 35 por ciento:

SELECT first_name, last_name,

commission_pct, hire_date
FROM employees
WHERE first_name LIKE 'B%'
OR commission_pct > 0.35;

Nótese que las dos condiciones están separadas por la palabra clave OR. Todos aquellos empleados que
tengan valores de FIRST_NAME empezando por la mayúscula “B” se recuperaran sin importar el valor de
COMMISSION_PCT, aún si estos son NULL. Todos aquellos registros cuyo valor para COMMISSION_PCT
sea mayor que 35% serán también recuperados sin importar el valor que tengan para FIRST_NAME.

90/306
SQL

Se pueden especificar tantas condiciones OR como se deseen, siempre que se separen por dicho operador.
Contrariamente a cómo funciona el operador AND, cuantas más condiciones OR se incluyan, menos
restrictiva se vuelve la consulta. En la Figura anterior se observa la consulta que se ha explicado
anteriormente con dos condiciones OR adicionales. El valor HIRE_DATE debe ser mayor que 01-MAR2008
o el LAST_NAME debe empezar por la letra “B”. Efectivamente, la primera consulta devuelve menos
registros que la segunda. Esto de se debe a que, dado que sólo una de las condiciones debe ser
verdadera, cuantas más se añaden, más registros de la tabla serán capaces de cumplir alguna de dichas
condiciones.

c. El Operador NOT
El operador NOT niega a los operadores condicionales. Un registro seleccionado deberá ajustarse al
contrario lógico de las condiciones que se establezcan para poder ser incluido en el conjunto de
resultados. La tabla siguiente es la tabla verdadero-falso para el operador NOT donde se puede ver su
funcionalidad.

91/306
SQL

Condition X Condition Y

FALSE TRUE

TRUE FALSE

NULL NULL

En la siguiente tablase ve cómo se pueden negar los operadores condicionale por el operador NOT.

Positive Negative

WHERE last_name = ‘King’ WHERE NOT (last_name = ‘King’)

WHERE first_name LIKE ‘R%’ WHERE first_name NOT LIKE ’R%’

WHERE department_id IN (10,20,30) WHERE department_id NOT in (10, 20, 30)

WHERE salary BETWEEN 1 and 3000 WHERE salary NOT BETWEEN 1 AND 3000

WHERE commission_pet IS NULL WHERE commission_pet IS NOT NULL

Como se muestra en los ejemplos de la tabla anterior, el operador NOT puede ser muy útil. Es importante
entender que el operador NOT niega al operador lógico en una condición, sea cual sea su naturaleza.

Problema Solución

Se tiene una consulta compleja No. Se pueden especificar tantas


con condiciones múltiples. ¿Hay condiciones en la cláusula WHERE
alguna como

restricción al número de se deseen, siempre separadas por


condiciones que se pueden un operador Booleano. No hay
especificar en una cláusula ningún límite a la hora de usar
WHERE? ¿Hay algún límite para operadores de comparación, y se
el número de operadores lógicos pueden usar tantas veces como
que se pueden usar en una única sea necesario en una misma
consulta? consulta.

92/306
SQL

Tiene como tarea localizar Si. Oracle convierte


aquellos registros en la tabla automáticamente los datos en el
EMPLOYEES donde el valor de
tipo requerido, siempre que sea
SALARY contiene los números 8
y 0 de manera adyacente. La posible. En este caso, el valor
columna SALARY es de tipo numérico SALARY se cambia
NUMBER. ¿Es posible usar el momentáneamente a tipo carácter,
comparador LIKE con datos
numéricos? permitiendo usar el operador LKE
para localizar patrones
equivalentes.
La siguiente consulta localiza los
registros deseados:
SELECT * FROM employees
WHERE salary LIKE '%80%';"
Si se restringen los registros Se trata de una operación de
recuperados de la tabla JOBS a selección, puesto que los registros
aquellos que contienen el valor están restringidos.
SA_REP en la columna JOB_ID,
se trata de una operación de
selección, proyección o de
unión?

Para recuperar a todos aquellos empleados cuyo valor para FIRST_NAME no empiece por la letra “B” o
aquellos que no se ajustan a la condición para COMMISSION_PCT siendo esta mayor que 35%, se
escribiría una consulta tal que:

SELECT first_name, last_name, commission_pct, hire_date


FROM employees
WHERE first_name NOT LIKE 'B%'
OR

NOT (commission_pct > 0.35);

Nótese que las dos condiciones siguen estando separadas por el operador OR. El operador NOT ser añade
justo detrás.

AND y OR son operadores Booleanos que permiten añadir condicionantes múltiples a la


cláusula WHERE de una única consulta. Todas las condiciones separadas por un operador AND
deben ser evaluadas como verdaderas para que el registro evaluado se añada al conjunto de
resultados. Por el contrario, sólo una de las condiciones separadas por el operador OR es
necesaria para que el registro evaluado se añada al conjunto de resultados. Si cinco
condiciones A,B,C,D y E se incluyen en una consulta: WHERE A and B or C or D and E, el registro
se recuperará siempre que las condiciones A y B sean verdaderas o sólo la condición C sea
verdadera o ambas condiciones D y E sean verdaderas.

93/306
SQL

3.1.4. Reglas de prioridad


Operadores aritméticos, caracteres de comparación y operadores Booleanos son examinados en el
contexto de una cláusula WHERE. ¿Cómo interactúan unos con otros?
Los operadores aritméticos están suscritos a una jerarquía de prioridad. Las expresiones entre llaves se
evalúan antes que los operadores de multiplicación y división, que se evalúan a su vez, antes que los
operadores de suma o resta. De la misma manera, hay una jerarquía para el resto de operadores
mencionados, que se muestra en la siguiente tabla:

Nivel Símbolo del Operación


precedente operador

1 () Paréntesis o llaves

2 /,* División y multiplicación

3 +,- Suma y resta

4 || Concatenación

5 =,<,>,<=,>= Comparadores de igualdad y


desigualdad

6 [NOT] LIKE, IS [NOT] Comparador de patrón, nulo y conjunto


NULL, [NOT] IN

7 [NOT] BETWEEN Comparador de rango

8 !=,<> No igual a

9 NOT Condición lógica NOT

10 AND Condición lógica AND

11 OR Condición lógica OR

Los operadores al mismo nivel de prioridad se evalúan de izquierda a derecha, si se encuentran en la


misma expresión. Cuando el operador NOT modifica los operadores de comparación LIKE, IS NULL y IN,

94/306
SQL

su nivel de prioridad se mantiene igual que si fueran positivos.


Considérese la siguiente sentencia SELECT que demuestra la interacción entre los diferentes operadores:

SELECT last_name,salary,department_id,job_id,commission_pct FROM


employees
WHERE last_name LIKE '%a%' AND salary > department_id * 200 OR
job_id IN ('MK_REP','MK_MAN') AND commission_pct IS NOT NULL;

Las columnas LAST_NAME, SALARY, DEPARTMENT_ID, JOB_ID y COMMISSION_PCT se proyectan desde


la tabla EMPLOYEES basándose en dos condiciones discretas. La primera condición recupera aquellos
registros que contienen el carácter “a” en LAST_NAME y(AND) con un valor para SALARY mayor que 200
veces el valor de DEPARTMENT_ID. El producto se procesa antes que la inecuación dado que la prioridad
para la multiplicación es mayor.
La segunda condición busca aquellos registros con valores para JOB_ID iguales a MK_MAN o MKREP
donde los valores para COMMISSION_PCT no sean NULL. Para que un registro sea devuelto tras ser
evaluado con esta consulta, cualquiera de ambas condiciones a sendos lados del operador OR debe
cumplirse. La siguiente figura muestra tres consultas diferentes; en la primera se recuperan 4 registros;
la segunda consulta se basa en la primera condición y devuelve 4 registros; la consulta número tres se
basa en la segunda condición y devuelve 0 registros.

95/306
SQL

Cambiar el orden de las condiciones en la cláusula WHERE significa cambiar su significado, dado que se
modificará su orden de prioridad. Considérese el siguiente código:

SELECT last_name,salary,department_id,job_id,commission_pct
FROM employees
WHERE last_name LIKE '%a%'
AND salary > department_id * 100
AND commission_pct IS NOT NULL
OR job_id = 'MK_MAN';
Hay dos condiciones compuestas en esta consulta, la primera condición recupera los registros con un
carácter “a” en el campo LAST_NAME y con un valor para SALARY 100 veces mayor que el valor para
DEPARTMENT_ID y donde el valor de COMMISSION_PCT no puede ser NULL.

La segunda condición busca aquellos registros cuyo valor para JOB_ID es MK_MAN. Un registro se
recuperará cuando cualquiera de las condiciones se cumpla, puesto que están separadas por el operador
OR.

96/306
SQL

Tal y como se ve en la figura a continuación, esta consulta recupera 6 registros. También se observa la
división de la consulta en otras dos. La primera condición separada devuelve 5 registros, mientras que
la segunda sólo recupera 1.

Los operadores Booleanos OR y AND permiten definir múltiples condiciones en la cláusula


WHERE. El Booleano NOT, niega a los operadores condicionales y se puede usar más de una
vez dentro de la misma condición. Los comparadores igualdad, inecuación, BETWEEN, IN y
LIKE examinan dos términos dentro de una condición. Solo se puede usar un operador para
cada cláusula condicional. La distinción entre operador Booleano y comparador es importante.

97/306
SQL

3.2. Ordenar los registros recuperados de una consulta


Los datos se pueden ordenar de muchas formas, incluso en más de una columna, tanto si están listadas
en la sentencia SELECT como si no lo están. Una vez se han buscado los datos mediante dicha sentencia,
se produce la ordenación, que no influye en los registros que se recuperan, sino en la presentación de
dichos resultados. Esta ordenación se realiza gracias a la cláusula ORDER BY.

3.2.1. La clásula ORDER BY


Cuando las tablas se crean, están vacías y no contienen ningún registro. Al tiempo que los registros se
rellenan, se actualizan o se eliminan, por uno o varios usuarios de la aplicación, la ordenación inicial se
ve modificada. El servidor de Oracle no puede garantizar que los registros se vayan a guardar de manera
secuencial. Esto no suele ser un problema dado que se dispone de un mecanismo de ordenación para los
registros recuperados tras una consulta, la cláusula ORDER BY.
Esta cláusula es responsable de transformar la salida de una consulta para que sea más práctica y fácil
de utilizar por el usuario; la cláusula ORDER BY siempre es la última que se añade a la sentencia. En los
siguientes capítulos se verán nuevas cláusulas que se van a ir añadiendo a la sintaxis de la sentencia
SELECT. Sin embargo, ninguna de ellas se va a posicionar a la derecha de ORDER BY.

El formato de la cláusula ORDER BY en el contexto de una sentencia SQL SELECT es la siguiente:

SELECT *|{[DISTINCT] column|expression[alias],…}


FROM table
[WHERE condition(s)]
[ORDER BY {col(s)|expr|numeric_pos} [ASC|DESC] [NULLS FIRST|LAST]];

a. Ordenación ascendente y descendente


La ordenación ascendente es natural para muchos tipos de datos y es por tanto la ordenación por defecto
que se usa si la cláusula ORDER BY se especifica. Para números, la ordenación ascendente significa
ordenar de menor a mayor, mientras que para fechas es de más pronto a más tarde y alfabéticamente
sería la correspondiente para los caracteres.
La primera forma de la cláusula ORDER BY muestra que los resultados de una consulta se pueden ordenar
en una o más columnas:

ORDER BY col(s)|expr;
En el supuesto que se requiera un informe que deba contener el LAST_NAME, SALARY y
COMMISSION_PCT de los empleados, ordenados alfabéticamente para la primera columna, para todos
los representantes de ventas y gestores de marketing. Este informe se podría extraer mediante la
siguiente sentencia SELECT:

SELECT last_name, salary, commission_pct


FROM employees
WHERE job_id IN ('SA_MAN','MK_MAN')
ORDER BY last_name;

Los datos seleccionados pueden ser ordenados en cualquiera de las columnas de las tablas de la cláusula
FROM, incluyendo aquellas que no aparecen en la lista del SELECT. Los resultados de la sentencia anterior
se pueden ordenar por COMMISSION_PCT como se muestra en la siguiente figura:

98/306
SQL

En el segundo ejemplo de la figura anterior, se muestra que al añadir la palabra clave DESC a la cláusula
ORDER BY, los registros se devuelven ordenados de manera descendente basándose nuevamente en la
columna COMMISSION_PCT. En el tercer ejemplo se muestra que al añadir las palabras clave NULLS
LAST, todos aquellos registros que en la columna que se usa para la ordenación tengan un valor nulo
serán enviados al final después de los valores no nulos. De similar forma, si se añaden las palabras clave
NULLS FIRST, estos registros nulos se mostrarán los primeros, antes que los registros no nulos para la
columna utilizada como base de ordenación.

En el siguiente ejemplo se ordena un conjunto de datos basados en una expresión que calcula el valor
de un empleado para una compañía basándose en sus valores para las columnas HIRE_DATE y SALARY.
Dicha fórmula toma el valor HIRE_DATE y le resta un número específico de días para devolver una fecha
anterior. Este número de días restado se calcula a partir del valor del campo SALARY dividido por 10. La
expresión se denomina EMP_VALUE como se muestra en el siguiente código:

SELECT last_name, salary, hire_date, hire_date-(salary/10) emp_value

99/306
SQL

FROM employees
WHERE job_id IN ('SA_REP','MK_MAN')
ORDER BY emp_value;

La expresión EMP_VALUE se inicializa con el valor HIRE_DATE y se desfasa hacia el pasado basándose
en el campo SALARY. La fecha más temprana de EMP_VALUE aparecerá primero en el registro de
resultados dado que la cláusula ORDER BY especifica que el resultado debe ser organizado por la
expresión. Nótese que los resultados podrían ser ordenados por la expresión explícitamente y el alias se
podría omitir: ORDER BY HIRE_DATE-(SALARY/10), pero utilizar un alias para dicha expresión hace que
la consulta sea mucho más sencilla de leer.

Hay varias opciones seleccionadas por defecto implícitas cuando se usa ORDER BY. La más importante
es que si no se especifica mediante DESC, la ordenación se asume como ascendente. Si hay valores
nulos en la columna base, se ordenará como NULL LAST por defecto para ordenaciones ascendentes y
NULL FIRST para descendentes. Si no hay una cláusula ORDER BY, la misma consulta ejecutada diversas
veces devuelve el mismo conjunto de resultados en diferente orden, por lo que no se pueden asumir
patrones para la ordenación por defecto en este caso.

b. Ordenación por posicionamiento


Oracle ofrece una forma más corta para especificar la columna que se usará como base para la
ordenación: en vez de especificar su nombre, se puede especificar mediante su posición. Considérese el
siguiente ejemplo:

SELECT last_name, hire_date, salary FROM employees


WHERE job_id IN ('SA_REP','MK_MAN')
ORDER BY 2;
La cláusula ORDER BY especifica el número literal 2. Es equivalente a especificar ORDER BY HIRE_DATE,
dado que HIRE_DATE ocupa la segunda posición en la lista de columnas seleccionadas por la consulta
SELECT.
Esta opción solo es aplicable a las columnas que se especifican en la consulta. En la consulta anterior, no
sería posible ordenar los registros por JOB_ID mediante esta técnica puesto que no es una de las
columnas especificadas.

c. Ordenación compuesta
Los resultados de una consulta se pueden ordenar mediante más de una columna. Dos o más columnas
se pueden especificar (tanto literalmente como por posición) como base para la ordenación si se separan
por comas en la cláusula ORDER BY. Considérese como requisito la ordenación del conjunto de resultados
mediante JOB_ID, LAST_NAME, SALAY y HIRE_DATE de la tabla EMPLOYEES. Otras especificaciones para
la ordenación son que para JOB_ID se debe ordenar de manera alfabéticamente descendente,
alfabéticamente ascendente para LAST_NAME y finalmente numéricamente descendente para SALARY.
La siguiente consulta SELECT cumple dichos requisitos:

SELECT job_id, last_name, salary, hire_date


FROM employees
WHERE job_id IN ('SA_REP','MK_MAN')
ORDER BY job_id DESC, last_name, 3 DESC;

100/306
SQL

Cada columna implicada en la ordenación se lista de izquierda a derecha en orden de importancia,


separadas por comas en la cláusula ORDER BY, incluyendo el modificador DESC, que aparece dos veces
en dicha cláusula. Este ejemplo también demuestra que se pueden mezclar especificaciones literales y
posicionales para las columnas. En la figura que se presenta a continuación, se muestra que hay varios
registros con el mismo JOB_ID, por ejemplo, SA_REP. Para estos registros, los datos se ordenan
alfabéticamente según la segunda clave de ordenación, LAST_NAME. De la misma forma, para aquellos
registros con el mismo valor para JOB_ID y para LAST_NAME como por ejemplo SA_REP y Smith, se
establece un orden numérico descendente según la tercera clave de ordenación, SALARY.

El concepto de ordenación de datos se suele estudiar minuciosamente. La sintaxis de la


cláusula ORDER BY es sencilla, pero utilizar distintos variables de ordenación como
expresiones, columnas y especificadores posicionales, mezclados con ordenaciones
descendentes para algunos casos y ascendentes para otros, proporciona una herramienta muy
potente de ordenación que ve incrementada su complejidad.

101/306
SQL

3.3. Sustitución ampersand (&)


A medida que las consultas se desarrollan y perfeccionan, pueden guardarse para su uso futuro. A veces
las consultas difieren muy ligeramente, y es deseable tener una forma más genérica de la consulta que
tenga definida una variable o marcador de posición que se pueda sustituir en tiempo de ejecución. Cada
elemento de la secuencia SELECT puede ser sustituido, y la reducción de las consultas a sus elementos
centrales facilita su reutilización. En esta sección se examinan las siguientes áreas:

Variables de sustitución
Comandos VERIFY y
DEFINE

3.3.1. Variables de Sustitución


La clave para entender las variables de sustitución es considerarlas como marcadores de posición. Una
consulta SQL se compone de dos o más cláusulas. Cada cláusula se puede dividir en sub-cláusulas, que
a su vez se hacen de texto. Cualquier texto, sub-cláusula o elemento de cláusula, o incluso toda la
consulta SQL, es candidato a la sustitución. Considere la sentencia SELECT en su forma general:

SELECT *|{[DISTINCT] column|expression [alias],…}


FROM table
[WHERE condition(s)]
[ORDER BY {col(s)|expr|numeric_pos} [ASC|DESC] [NULLS FIRST|LAST]];

Utilizando la sustitución, se insertan valores en los elementos en cursiva, seleccionando las palabras
clave opcionales que se utilizarán en las consultas. Cuando se necesita la columna LAST_NAME de la
tabla EMPLOYEES, la consulta se construye utilizando la forma general de la sentencia SELECT y
sustituyendo el nombre de la columna: APELLIDOS_NOMBRE en lugar de la columna palabra en la
cláusula SELECT y el nombre de la tabla; EMPLEADOS en lugar de la tabla palabra en la cláusula FROM.

a. Sustitución Ampersand simple


La sustitución más básica y popular de elementos en las sentencias SQL es la sustitución con Ampersand
simple. El carácter Ampersand (&) es el símbolo elegido para designar una variable de sustitución en una
expresión y precede al nombre de la variable sin espacios entre ellos. Cuando se ejecuta la sentencia, el
servidor Oracle procesa la expresión, notifica una variable de sustitución e intenta resolverla en una de
las dos formas. Primero, verifica si la variable está definida en la sesión de usuario, sino está definida, el
proceso de usuario solicita un valor que será sustituido en lugar de la variable. Una vez que se presenta
un valor, la declaración es completa y es ejecutado por el servidor Oracle. La variable de sustitución
Ampersand se resuelve en el momento de la ejecución y a veces se conoce como enlace en tiempo de
ejecución o sustitución en tiempo de ejecución. Un requisito común en el departamento de personal de
muestra puede ser recuperar la misma información para diferentes empleados en diferentes momentos.
Tal vez se le pida que busque información de contacto como los datos de PHONE_NUMBER con los valores
de LAST_NAME o EMPLOYEE_ID.

Esta solicitud genérica se puede escribir de la siguiente manera:

SELECT employee_id, last_name, phone_number


FROM employees
WHERE last_name = &LASTNAME OR employee_id = &EMPNO;

102/306
SQL

En la figura siguiente cuando se ejecuta la consulta anterior, el servidor Oracle le pide que ingrese un
valor para la variable llamada LASTNAME; introduzca el apellido de un empleado si lo conoce, por ejemplo
"King"; si no sabe el apellido, pero sabe el número de identificación del empleado, puede escribir
cualquier valor y pulsar la tecla ‘Intro’ para enviar el valor; Oracle le pedirá que introduzca un valor para
la variable EMPNO.

Después de escribir un valor, por ejemplo 0 y pulsar ‘Intro’, no quedan variables de sustitución para que
Oracle las resuelva, y se ejecuta la siguiente sentencia:

SELECT employee_id, last_name, phone_number


FROM employees
WHERE last_name = 'King' OR employee_id = 0;

A las variables se les puede asignar cualquier nombre alfanumérico que sea un nombre de identificador
válido. La variable que se pide debe ser de un tipo de datos apropiado para ese contexto; de lo contrario,
se devuelve un error de identificador no válido ORA-00904: invalid identifier error. Si la variable está
destinada a sustituir un carácter o un valor de fecha, la variable debe incluirse entre comillas simples.
Una técnica útil es encerrar la variable Ampersand de sustitución entre comillas simples cuando se trata
de valores de carácter y fecha. De esta manera, se requiere que el usuario envíe un valor literal sin
preocuparse de incluirlo entre comillas. La siguiente sentencia reescribe la anterior, pero incluye la
variable LASTNAME entre comillas:

SELECT employee_id, last_name, phone_number, email


FROM employees
WHERE last_name = '&LASTNAME' OR employee_id = &EMPNO;
Cuando se le pida un valor para sustituir la variable LASTNAME, puede enviar el valor King sin comillas
simples, ya que éstas ya están presentes; y cuando se realiza la sustitución en tiempo de ejecución, la
primera condición de la cláusula WHERE se resolverá en WHERE LAST_NAME = 'King'.

103/306
SQL

b. Sustitución ampersand doble


Hay ocasiones en las que se hace referencia a una variable de sustitución varias veces en la misma
consulta. En tales situaciones, el servidor Oracle le pedirá que introduzca un valor para cada ocurrencia
de la variable Ampersand de sustitución. Para scripts complejos esto puede ser muy ineficiente y tedioso.
La siguiente sentencia aparece para recuperar las columnas FIRST_NAME y LAST_NAME de la tabla
EMPLOYEES para aquellas filas que contienen el mismo conjunto de caracteres en ambos campos:

SELECT first_name, last_name


FROM employees
WHERE last_name LIKE '%&SEARCH%'
AND first_name LIKE '%&SEARCH%';

Las dos condiciones son idénticas, pero se aplican a columnas diferentes. Cuando este se ejecuta, primero
se le pedirá que introduzca un valor de sustitución para la variable SEARCH utilizada en la comparación
con la columna LAST_NAME. A continuación, se le pedirá que introduzca un valor de sustitución para la
variable SEARCH utilizada en la comparación con la columna FIRST_NAME. Esto plantea dos problemas.
Primero, es ineficiente introducir el mismo valor dos veces, pero segundo y más importante, los errores
tipográficos pueden confundir la consulta ya que Oracle no verifica que se introduzca el mismo valor
literal cada vez que se utilizan variables de sustitución con el mismo nombre. En este ejemplo, la
suposición lógica es que el contenido de las variables sustituidas debe ser el mismo, pero el hecho de
que las variables tengan el mismo nombre no tiene ningún significado para el servidor Oracle y este no
hace tal suposición.
El primer ejemplo en la figura que viene a continuación se muestra los resultados de ejecutar la consulta
anterior y enviar dos valores distintos para la variable de sustitución SEARCH. En este ejemplo en
particular, los resultados son incorrectos ya que el requisito era recuperar los pares FIRST_NAME y
LAST_NAME que contenían la misma cadena de caracteres. En situaciones en las que una variable de
sustitución es referenciada varias veces en la misma consulta y su intención es que la variable tenga el
mismo valor en cada ocurrencia en la sentencia, es preferible hacer uso la sustitución con doble
Ampersand. Esto implica prefijar la primera ocurrencia de la variable de sustitución que ocurre varias
veces en una consulta, con dos símbolos de Ampersand en lugar de uno. Cuando el servidor Oracle
encuentra una variable de sustitución de doble Ampersand, se define un valor de sesión para esa variable
y no se le pide que introduzca un valor para sustituir esta variable en referencias posteriores.

El segundo ejemplo de la figura demuestra cómo la variable SEARCH es precedida por dos Ampersand
en la condición con la columna FIRST_NAME y a partir de entonces es precedido por un Ampersand en
la condición con la columna LAST_NAME. Cuando se ejecuta, se le pide que introduzca un valor para
sustituir la variable SEARCH sólo una vez para la condición con la columna FIRST_NAME. Este valor se
resuelve automáticamente a partir del valor de la sesión de la variable en referencias posteriores a la
misma, como en la condición con la columna LAST_NAME. Para vaciar la variable SEARCH, se debe utilizar
el comando UNDEFINE que se describe más adelante en este capítulo.

104/306
SQL

Tanto si trabaja como desarrollador, administrador de bases de datos o usuario final de


negocio, todas las consultas SQL que encuentre pueden clasificarse como consultas ad hoc o
repetidas. Las consultas ad hoc son, por lo general, declaraciones puntuales escritas durante
algunos ejercicios de investigación de datos que es poco probable que se reutilicen. Las
consultas repetidas son aquellas que se ejecutan frecuente o periódicamente, que
normalmente se guardan como archivos de script y se ejecutan con poca o ninguna
modificación cuando es necesario. La reutilización evita el coste de volver a desarrollar y
permite que estas consultas coherentes se beneficien potencialmente de las funciones de
ajuste automático de Oracle orientadas a mejorar el rendimiento de las consultas.

c. Sustitución de nombres de columnas


Las variables de la cláusula WHERE han sido el centro de la discusión sobre la sustitución hasta ahora,
pero prácticamente cualquier elemento de una declaración SQL es candidato a la sustitución. En la
siguiente sentencia, las columnas FIRST_NAME y JOB_ID son estáticas y siempre se recuperarán, pero
la tercera columna seleccionada es variable y especificada como una variable de sustitución llamada COL.
El conjunto de resultados se clasifica además por esta columna variable en la cláusula ORDER BY:

105/306
SQL

SELECT first_name, job_id, &&col


FROM employees
WHERE job_id IN ('MK_MAN','SA_MAN')
ORDER BY &col;

En la siguiente figura se pide que proporcione un valor para la variable Ampersand y Ampersand doble
llamada COL. Por ejemplo, podría introducir una columna llamada SALARY y envíe sus comentarios. La
sentencia que ejecuta la sustitución realiza la sustitución y recupera las columnas FIRST_NAME, JOB_ID
y SALARY de la tabla EMPLOYEES ordenados por SALARY. A diferencia de las cadenas de caracteres y las
fechas, las referencias de nombre de columna no requieren comillas simples tanto cuando se especifican
explícitamente como cuando se sustituyen mediante la sustitución por Ampersand.

d. Sustituyendo expresiones y texto


Casi cualquier elemento de una sentencia SQL puede ser sustituido en tiempo de ejecución. La limitación
es que Oracle requiere que al menos la primera palabra sea estática. En el caso de la sentencia SELECT,
como mínimo se requiere la palabra clave SELECT y el resto de la sentencia se puede sustituir de la
siguiente manera:

SELECT &rest_of_statement;

Cuando se ejecuta, se le pide que envíe un valor para la variable REST_OF_STATEMENT que podría ser
cualquier consulta legítima, como DEPARTMENT_NAME FROM DEPARMENTS. Si envía este texto como
entrada para la variable, la consulta que se ejecuta se resolverá en la siguiente sentencia:

SELECT department_name

FROM departments;

Considere la forma general de la sentencia SQL reescrita usando la sustitución Ampersand como se
muestra a continuación:

106/306
SQL

SELECT &SELECT_CLAUSE
FROM &FROM_CLAUSE
WHERE &WHERE_CLAUSE
ORDER BY &ORDER_BY_CLAUSE;

La utilidad de esta declaración es discutible, pero ilustra el concepto de sustitución de manera efectiva.
En la figura que sigue, la declaración anterior permite que cualquier consulta vista hasta ahora sea
enviada en tiempo de ejecución. La primera ejecución consulta la tabla REGIONS, mientras que la
segunda consulta la tabla CONTRIES. Todos los candidatos para la sustitución por Ampersand son
sentencias que se ejecutan varias veces y difieren ligeramente entre sí.

107/306
SQL

3.4. Definir y verificar


La doble sustitución Ampersand se usa para evitar entradas de datos repetitivas cuando la misma variable
aparece varias veces en una misma sentencia. Cuando una doble sustitución Ampersand ocurre, la
variable se conserva como una variable de sesión. Al tiempo que la sentencia es ejecutada, cualquier
otra concurrencia de las variables se resuelve automáticamente utilizando la variable de sesión guardada.
De hecho, cualquier otra ejecución de dicha sentencia en la misma sesión, resuelve también
automáticamente la sustitución de las variables desde los valores almacenados por dicha sesión.

Este efecto no siempre se desea y, de hecho, puede llegar a suponer un límite para la capacidad de
sustituir variables. Sin embargo, ORACLE permite resolver el conflicto borrando estas variables de sesión
a petición del desarrollador.
El comando VERIFY es específico de SQL*Plus y controla si los elementos sustituidos se repiten o no en
la pantalla del usuario antes de ejecutar una sentencia SQL que utiliza la sustitución de variables. Este
tipo de comandos se presentan en las siguientes secciones.

3.4.1. Los comandos DEFINE y UNDEFINE


Las variables de sesión se crean implícitamente cuando se referencian inicialmente mediante una
sentencia SQL utilizando la doble sustitución Ampersand. Dichas variables permanecen accesibles a lo
largo de toda la sesión o hasta que se borran explícitamente. Una sesión termina cuando el usuario sale
de su herramienta cliente como en SQL*Plus o cuando el proceso usuario es abortado anormalmente.
El problema de las variables de sesión persistentes es que tienden a limitar la naturaleza genérica de las
sentencias que usan variables de sustitución Ampersand. Afortunadamente, estas variables se pueden
eliminar con el comando UNDEFINE. Dentro de un script o en la línea de comando de SQL*Plus o SQL
Developer, la sintaxis para eliminar las variables de sesión es como sigue:

UNDEFINE variable;

Considere un ejemplo genérico que selecciona una columna estática y una columna variable de la tabla
EMPLOYEES y ordena el resultado basándose en la columna variable. La columna estática podría ser la
llamada como LAST_NAME.

SELECT last_name, &&COLNAME


FROM employees
WHERE department_id=30
ORDER BY &COLNAME;

La primera vez que esta sentencia se ejecuta, se pide que se introduzca el valor para la variable llamada
COLNAME y SALARY como valores de entrada. Este valor se sustituye y la sentencia se ejecuta. Si esta
sentencia se vuelve a ejecutar dentro de la misma sesión, no se requerirá que la introducción de ningún
valor para COLNAME, dado que ya está definido como SALARY en el contexto de la sesión y solo puede
ser eliminado mediante el comando UNDEFINE COLNAME, como se muestra en la siguiente figura:

108/306
SQL

Una vez que la variable se ha eliminado, la siguiente ejecución de la sentencia volverá a requerir al
usuario que introduzca un nuevo valor para COLNAME y FIRST_NAME será sustituido, cambiando la
consulta.

El comando DEFINE sirve para, por un lado, recuperar la lista de todas las variables definidas en ese
momento en la sesión SQL y por otro lado puede ser usado para definir explícitamente valores para las
variables referenciadas como variables de sustitución en una o más sentencias mientras la sesión siga
activa. La sintaxis de ambas variantes del comando DEFINE son como sigue:

DEFINE;

109/306
SQL

DEFINE variable=value

Tal y como se ve en la Figura siguiente, una variable llamada EMPNAME se define explícitamente para
tener el valor ‘King’. El comando independiente DEFINE de SQL*Plus, recupera las variables de sesión,
algunas con una barra baja delante, y otras que ya son conocidas, como EMPNAME y las variables de
sustitución doble Ampersand, que se definieron anteriormente de manera implícita.

Dos consultas diferentes, simples, se ejecutan y en ambas, la variable sustituida explícitamente definida
EMPNAME es referenciada en ambas.

La capacidad de la herramienta cliente en SQL para mantener las variables que persisten durante la
sesión se puede desactivar y activar mediante el comando SET. Si se escribe SET DEFINE OFF, la
herramienta cliente (SQL*Plus, por ejemplo) no guardará ninguna variable de sesión.

Gracias a esta acción, el símbolo que define el Ampersand (&) se puede usar como un carácter más si es
necesario. El comando SET DEFINE ON|OFF determina si la sustitución Ampersand está activa o no para
la sesión.
El siguiente ejemplo se usa el símbolo Ampersand como un valor literal. Cuando se ejecuta la sentencia,
se pide que se introduzca un valor para la variable de unión SID.

110/306
SQL

SELECT 'Coda & Sid' FROM dual;

Al desactivar esta funcionalidad, la consulta se ejecutará sin que lance peticiones para recibir datos de
entrada:

SET DEFINE OFF


SELECT 'Coda & Sid' FROM dual;
SET DEFINE ON

Una vez la declaración se ejecuta, el comando SET DEFINE ON se podrá usar para recuperar la
funcionalidad. Hay que tener en cuenta que, si dicha característica de la sesión está desactivada y por el
contesto, se necesitase utilizar la sustitución Ampersand, la consulta no se podría realizar con éxito y
Oracle devolvería un error.

Problema Solución

La lista de una consulta SELECT Si. Siempre que no se quiera


contiene solo una columna. ¿Es utilizar la ordenación por
posible ordenar los resultados en posición, la cláusula ORDER BY es
base a otra columna? independiente de la lista de
SELECT en una declaración.

Las variables de sustitución Si. Los dos métodos que se


Ampersand permiten la podrían usar a tal efecto son la
reutilización de consultas SQL. Si doble sustitución Ampersand y el
un valor sustituido se va a usar en comando DEFINE. Ambos
múltiples ocasiones en una misma métodos tienen como resultado
declaración, ¿Es posible que se que el usuario deberá proveer el
pida un valor de sustitución la valor de la variable una única vez
primera vez y en las siguientes por sesión. Este valor de variable
utilizaciones de la variable se se mantendrá constante mientras
sustituya automáticamente? la sesión dure y no se elimine
explícitamente mediante el
comando UNDEFINED.

Se le ha encomendado la tarea de Si. La cláusula ORDER BY ofrece


recuperar los valores de la posibilidad de ordenar las
LAST_NAME y DEPARTMENT_ID columnas que potencialmente
para todos los tengan campos nulos

111/306
SQL

registros en la tabla EMPLOYEES. mediante modificadores como


Los datos de salida deben ser NULLS FIRST o NULLS LAST.
ordenados por la columna
Obtendríamos los datos
DEPARTMENT_ID, cuyos posibles
valores nulos deben ser listados requeridos mediante la siguiente
los últimos. Es posible obtener los declaración:
datos siguiendo estos
SELECT last_name,
requerimientos?
department_id
FROM employees
ORDER BY department_id
NULLS
LAST;

3.4.2. El comando VERIFY


Como se ha comentado anteriormente, hay dos categorías de comandos disponibles para trabajar con el
servidor de Oracle: comandos en lenguaje SQL y comandos de control del cliente SQL. Las declaraciones
SELECT son un ejemplo de comandos de lenguaje SQL mientras que el comando SET es un ejemplo de
los comandos de control en el entorno cliente SQL. Hay muchos lenguajes diferentes y comandos de
control disponibles. De todos ellos, los comandos que dedicados a la sustitución Ampersand son DEFINE
y VERIFY.
El comando de control VERIFY comprueba si la variable de sustitución introducida está siendo mostrada
en la pantalla del cliente, de manera que se pueda verificar que la sustitución se ha realizado
correctamente. Este comando se puede configurar como ON u OFF, mediante el comando SET VERIFY
ON|OFF. Tal y como se ve en la siguiente Figura, VERIFY primero se configura como OFF (inactivo), se
lanza una consulta que usa la sustitución Ampersand y automáticamente el usuario debe introducir un
dato. Este dato queda sustituido, la declaración se ejecuta y el resultado se imprime en pantalla.
Posteriormente el comando VERIFY se activa (ON), la misma sentencia se ejecuta y se pide que se
introduzca un valor. Una vez dicho valor es introducido y antes de que la petición se ejecute, Oracle
muestra en pantalla la referencia al anterior valor de la variable con su número de línea y justo debajo,
se muestra la consulta con el valor ya sustituido.

112/306
SQL

3.5. Resumen

Limitar los registros recuperados por una consulta

• La cláusula WHERE amplía la sentencia SELECT con un lenguaje que permite la selección.
• La cláusula WHERE está formada por una o más condiciones. Estas condiciones especifican unas
reglas de selección de los datos.
• Para cada registro revisado en una condición, existen términos a izquierda y derecha de un
operador de comparación. Estos términos pueden ser valores de columna, cadenas de caracteres,
o expresiones.
• Los operadores de comparación pueden comparar dos términos de muchas maneras, los más
comunes son los comparadores de igualdad; pero los comparadores de rango, de conjunto o de
patrón también están disponibles
• Los comparadores de rango se formulan utilizando el operador BETWEEN, que comprueba si un
término está entre dos límites dados.
• Con el operador IN se puede comprobar si un término pertenece a un conjunto. La condición
basada en una comparación de conjunto evalúa a TRUE si el término de la izquierda se encuentra
en el conjunto entre paréntesis del lado derecho del operador IN.

• El Operador LIKE permite comparar cadenas de caracteres con otras cadenas, valores de
columna, o expresiones. El símbolo porcentaje (%) se comporta como un símbolo ‘comodín’ que

113/306
SQL

puede ser igual a cero o muchos caracteres. El símbolo guion bajo (_) se comporta como un
símbolo ‘comodín’ que solo coincide única y exactamente con otro carácter .
• Los operadores booleanos son AND, OR y NOT. Los operadores AND y OR permiten múltiples
condiciones, estas son denominadas como Cláusulas WHERE múltiples. El operador NOT niega
cualquier operador de comparación que se encuentre en una condición.

Ordenar los registros recuperados de una consulta

• Los Resultados se ordenan mediante la cláusula ORDER BY. Los registros recuperados pueden
ser ordenados según uno o más atributos de columna; y estos se pueden especificar mediante
el nombre de la misma o con su posición numérica en la cláusula SELECT.
• El resultado ordenado se puede obtener en orden ascendente o descendente utilizando los
modificadores DESC o ASC al final de la cláusula ORDER BY.

Sustitución ampersand

• La sustitución Ampersand mejora la reusabilidad de las sentencias SQL, funcionan de manera


que es posible sustituir variables en tiempo de ejecución. Por tanto, la misma sentencia SQL
puede ser utilizada con diferentes parámetros.
• La sustitución de Ampersand simple necesita que el usuario rellene la variable cada vez que es
solicitada dentro de la sentencia. La sustitución Ampersand doble sólo necesita que el usuario
introduzca una vez la variable, ya que se define una variable persistente en la sesión.
• Las variables persistentes en la sesión se deben expecificar mediante el comando DEFINE. El
comando UNDEFINE se utiliza para eliminar esas variables persistentes que provienen del doble
Ampersand, para así definir explícitamente variables propias de sesión.
• La configuración de entorno VERIFY controla si SQL*Plus muestra o no por pantalla la versión
antigua y la nueva de las líneas que contienen variables de sustitución.

4. Funciones de registro único

4.1. Descripción de los tipos de funciones disponibles en SQL


Las funciones son una gran extensión a SQL y proporcionan una primera visión de las capacidades que
soporta Oracle. Los lenguajes de procesamiento procedimental permiten un alto grado de programación,
lo que ofrece una gama casi ilimitada de posibilidades de manipulación de datos. El servidor de Oracle
implementa un lenguaje de procesamiento procedimental propio llamado PL/SQL. Un amplio rango de
los objetos de programación se puede constituir utilizando PL/SQL. A pesar de que escribir PL/SQL es
sencillo, es requisito necesario un minucioso estudio de SQL. Las funciones mostradas en este capítulo
se implementan en programas PL/SQL empaquetados y suministrados por Oracle como características
incorporadas.

En términos generales, las funciones de SQL están divididas en dos grandes grupos. Aquellas que calculan
y devuelven un valor para cada registro de un conjunto de datos y aquellas que devuelven un único valor
agregado para todas las filas.

Las áreas a tratar serán:

114/306
SQL

- Definiendo una función -


Tipos de función

4.1.1. Definiendo una función


Una función es un programa escrito para, opcionalmente, aceptar parámetros de entrada, ejecutar una
operación y devolver un valor único. Una función devuelve solo un valor por ejecución.

Hay tres componentes básicos en la definición de una función. El primero es la lista de parámetros de
entrada. Estos especifican cero o más argumentos que serán introducidos a la función para ser
procesados. Estos parámetros pueden ser de distinto tipo, obligatorios u opcionales. El segundo
componente es el tipo de dato del valor resultante. En el momento de la ejecución, solo un valor de un
tipo predefinido es devuelto por la función. Por último, el tercer componente encapsula los detalles del
proceso ejecutado por la función y contiene el código de programa que de forma opcional manipula los
parámetros introducidos, ejecuta cálculos y operaciones y genera un valor de salida.
A menudo se describe una función como una “caja negra” que recibe unos datos de entrada, los ejecuta
y transforma, y devuelve un resultado como se detalla a continuación:

F(x,y,z,…) = resultado

Las funciones se pueden anidar con otras funciones como por ejemplo F1( x, y, F2( a, b), z), donde F2,
que tiene “a” y “b” como parámetros de entrada, forma parte de los parámetros de entrada de la función
F1. Las funciones pueden operar con todos los tipos de datos disponibles, los más utilizados son los de
tipo carácter, fecha y numérico. Estos operandos serán columnas o expresiones.

Como ejemplo, se considera una función que calcula la edad de una persona. La función EDAD tendrá
como parámetro de entrada la fecha de nacimiento de la persona. El resultado devuelto es un número
que representa la edad de esta. El cálculo caja negra implica obtener la diferencia de edad en años entre
la fecha actual y la fecha introducida como parámetro de entrada.

a. Operando con datos tipo carácter


Los datos de tipo carácter o cadenas de texto son versátiles ya que facilitan el almacenamiento en casi
cualquier tipo de datos. Las funciones que operan con caracteres se clasifican en general en funciones
de conversión de mayúsculas/minúsculas y funciones de manipulación de caracteres. A continuación, se
muestran ejemplos de funciones que operan con caracteres, las cuales se explicarán más adelante en
este capítulo.

LOWER ('SQL') = sql


UPPER ('sql') = SQL
INITCAP ('sql') = Sql

Las funciones de manipulación de caracteres incluyen las funciones LENGTH, CONCAT, SUBSTR, INSTR,
LPAD, RPAD, TRIM, y REPLACE.

La función LENGTH (string) utiliza una cadena de texto como parámetro de entrada y devuelve un valor
numérico del número de caracteres que la forman:

115/306
SQL

LENGTH ('A short string') = 14

La función CONCAT (string1, string2) toma dos cadenas de texto y las concatena del mismo modo que
el operador ||:

CONCAT ('SQL is' , ' easy to learn.') = SQL is easy to learn.

La función SUBSTR (string, start position, number of characters) toma tres parámetros de entrada: Una
cadena de texto, la posición de inicio y el número de caracteres a extraer, y devuelve una cadena de
texto formada por los caracteres extraídos, a partir de la posición fijada, de la cadena de texto
introducida.

SUBSTR ('[Link] , 12, 6) = domain

La función INSTR (source string, search item, [start position], [nth occurrence of search item]) devuelve
un número que indica la posición en la cadena de caracteres (source string), empezando desde una
posición inicial dada (start position), en la que aparece por enésima vez (nth ocurrence of search ítem)
el elemento buscado (search ítem).

INSTR ('[Link] 1, 2) = 18

Las funciones LPAD (string, length after padding, padding string) y RPAD (string, length after padding,
padding string) añaden un carácter a la derecha o a la izquierda de la cadena de texto, en la posición
especificada.

RPAD ('#PASSWORD#',11,'#') = #PASSWORD##


LPAD ('#PASSWORD#',11,'#') = ##PASSWORD#

La función TRIM (trailing|leading|both trimstring from s) recorta literalmente los caracteres iniciales o
finales (o ambos) de una cadena determinada:

TRIM ('#' from '#PASSWORD#') = PASSWORD

La función REPLACE (string, search item, replacement term) localiza un elemento y lo sustituye por otro
elemento de reemplazo, devolviendo una cadena de texto con los caracteres sustituidos:
REPLACE ('#PASSWORD#','WORD','PORT') = #PASSPORT#

116/306
SQL

b. Operando con datos numéricos


Se dispone de numerosas funciones que operan con este tipo de datos. Algunas calculan raíces
cuadradas, realizan exponenciales y convierten números en formato hexadecimal. La mayoría de cálculos
matemáticos, estadísticos y financieros están incorporados en funciones por Oracle.

Tres funciones numéricas comunes, detalladas en este capítulo, son ROUND, TRUNC y MOD.

ROUND (number, decimal precision) facilita el redondeo de un número al número de decimales


especificado:

ROUND (42.39,1) = 42.4

La función TRUNC (number, decimal precision) trunca un numero decimal al número de decimales
especificado:

TRUNC (42.39,1) = 42.3

La función MOD (dividend, divisor) devuelve el resto de una operación de división:

MOD (42,10) = 2

c. Operando con datos tipo fecha


Trabajar con fechas puede ser complicado. Realizar operaciones aritméticas teniendo en cuenta años
bisiestos y la duración variable de los meses puede ser frustrante y propensa al error en operaciones
como MONTHS_BETWEEN, ADD_MONTHS, LAST_DAY, NEXT_DAY, SYSDATE, ROUND y TRUNC.
La función MONTHS_BETWEEN (date1, date2) devuelve el número de meses entre dos fechas mientras
que ADD_MONTHS (date, number of months) devuelve la fecha resultante de añadir unos meses
determinados a una fecha inicial.

MONTHS_BETWEEN ('01-FEB-2008','01-JAN-2008') = 1
ADD_MONTHS ('01-JAN-2008',1) = 01-FEB-2008

La función LAST_DAY (date) devuelve el último día del mes de la fecha especificada. Mientras que
NEXT_DAY (date, day of the week) devuelve la siguiente fecha con el día de la semana especificado.

LAST_DAY ('01-FEB-2008') = 29-FEB-2008


NEXT_DAY ('01-FEB-2008','Friday') = 08-FEB-2008

117/306
SQL

La función SYSDATE no requiere parámetros y devuelve una fecha que representa la fecha y hora actuales
del servidor. Las funciones ROUND (date, date precision format) y TRUNC (date, date precision format)
redondea y trunca una fecha al formato de la fecha dada respectivamente.

SYSDATE = 17-DEC-2007
ROUND (sysdate,'month') = 01-JAN-2008
TRUNC (sysdate,'month') = 01-DEC-2007

Las funciones de registro único se utilizan en casi todas las consultas emitidas por los
analistas, desarrolladores y administradores. Al buscar datos de caracteres, la función TRIM
se utiliza con frecuencia para eliminar espacios adicionales que se producen en campos de
tipo carácter. Las funciones de conversión se utilizan para estandarizar los datos de una
columna. Esto facilita una búsqueda más precisa y eficiente de los registros que se capturan
ya que, a menudo, son inconsistentes.

4.1.2. Tipos de funciones


A continuación, se analizan dos amplios tipos de funciones que operan en registros simples y múltiples,
respectivamente. Esta distinción es vital para comprender el contexto más amplio en el que se utilizan
dichas funciones. Oracle garantiza que su interpretación comercial de SQL se ajuste a las normas
internacionales. Esto facilita la migración de sistemas y habilidades entre vendedores y proveedores de
software RDBMS. La implementación de Oracle de SQL cumple con el Core SQL:2011, un estándar
avalado por ISO (International Organization for Standardization), las que son total o parcialmente
compatibles con el estándar SQL:2011.

a. Funciones de registro único


Hay varias categorías de funciones de registro único, incluyendo las de tipo carácter, numérico, fecha,
conversión y general. Este capítulo se centra en las funciones de registro único de tipo carácter, numérico
y de fecha. Éstas son funciones que operan en un registro de un conjunto de datos a la vez. Si una
consulta selecciona 10 registros, la función se ejecuta 10 veces, una vez por registro, normalmente con
los valores de ese registro como entrada a la función.

Como muestra la figura más adelante, se han seleccionado dos columnas de la tabla REGIONS junto con
una expresión utilizando la función LENGTH con la columna REGION_NAME.
La longitud de la columna REGION_NAME se calcula para cada registro, lo que demuestra que la función
se ha ejecutado cuatro veces separadas, devolviendo un resultado por registro.
Las funciones de registro único manipulan los datos de un registro para extraerlos y formatearlos con
fines de visualización. Los valores de entrada a una función de registro único pueden ser constantes o
literales especificados por el usuario, una columna de datos, variables o expresiones proporcionadas, de
forma opcional, por otras funciones de registro único anidadas. El anidamiento de las funciones de
registro único es una técnica comúnmente utilizada. Las funciones pueden devolver un valor con un tipo
de datos diferente de sus parámetros de entrada. Como muestra la siguiente figura, la función LENGTH
acepta un parámetro de entrada de tipo carácter y devuelve una salida de tipo numérico.

118/306
SQL

Las funciones de conversión como TO_CHAR, TO_NUMBER y TO_DATE se tratarán en el Capítulo 5.


Cambian el tipo de datos de una columna de datos o expresiones permitiendo que otras funciones operen
sobre ellos. Las funciones generales también se discutirán en el Capítulo 5. Estas simplifican el trabajo
con valores NULL y facilitan la lógica condicional dentro de una instrucción SELECT. Incluyen las funciones
NVL, NVL2, NULLIF, COALESCE, CASE y DECODE.
Aparte de su inclusión en la lista SELECT de una consulta SQL, se pueden utilizar funciones de registro
único en las cláusulas WHERE y ORDER BY. Supóngase que es necesario listar los registros de la tabla
REGIONS donde la longitud de la propiedad REGION_NAME es de al menos cinco caracteres. También es
necesario que esta lista se clasifique en orden alfabético según el valor del último carácter de la columna
REGION_NAME. La cláusula WHERE se muestra aquí:

WHERE length(region_name) > 4

Para obtener el último carácter de una cadena, la función SUBSTR se utiliza con la columna
REGION_NAME como cadena de origen. La longitud de REGION_NAME se utiliza como posición inicial
(para obtener la posición del último carácter) produciendo la siguiente cláusula ORDER BY:

119/306
SQL

ORDER BY substr(region_name, length(region_name),1)

Como muestra la figura anterior, sólo se devuelven tres de las cuatro regiones, y la lista se ordena en
orden alfabético según el último carácter de la columna REGION_NAME para cada registro.

b. Funciones de registros múltiples


Como su nombre indica, esta categoría de funciones opera en más de un registro a la vez. Los usos
típicos de las funciones de múltiples registros incluyen el cálculo de la suma o promedio de los valores
de las columnas numéricas o el recuento del número total de registros en un conjunto. A veces se
conocen como funciones de agregación o de grupo y se analizarán en el Capítulo 6.

Se ejecutan funciones de registro único para cada registro del conjunto de datos seleccionado.
Las funciones siempre devuelven sólo un valor de un tipo de datos predeterminado. Pueden
aceptar cero o más parámetros de diferentes tipos de datos. Las funciones de registro único
como LENGTH, SUBSTR, e INSTR se usan frecuentemente anidadas. Cabe recordar que los
parámetros de entrada que pueden convertirse implícitamente a los tipos de datos requeridos
por las funciones.

4.2. Utilizar las funciones tipo carácter, numérico y fecha en sentencias SELECT
Esta sección lleva a cabo una investigación detallada de las funciones de un solo registro que se han
presentado anteriormente. Se adoptará un enfoque estructurado que incluye descripciones de funciones,
reglas sintácticas, descripciones de parámetros y ejemplos de uso. Se examinarán las funciones de
conversión de mayúsculas/minúsculas, seguidas de las funciones de manipulación de caracteres.
Seguidamente, se examinarán las funciones numéricas. La sección concluye con una evaluación de las
funciones de fecha.

120/306
SQL

4.2.1. Utilizando funciones de conversión de mayúsculas/minúsculas


Los datos de tipo carácter se pueden guardar en tablas desde numerosas fuentes, incluyendo interfaces
de la aplicación, programas, etc. Es por esto que no es seguro asumir que los datos de tipo carácter han
sido asignados de manera consistente. Las funciones de conversión de mayúsculas y minúsculas sirven
para dos propósitos importantes. Pueden utilizarse en primer lugar, para modificar la apariencia de un
carácter para fines de visualización, y en segundo lugar, para que sean coherentes a efectos de
operaciones de comparación. Es más sencillo buscar una cadena de texto usando un formato
mayúsculas/minúsculas consecuente, en lugar de probar cada permutación de caracteres en mayúsculas
y minúsculas que pudiesen coincidir con la cadena. Es importante recordar que estas funciones no alteran
los datos almacenados en tablas. Siguen formando parte de las consultas SQL de sólo lectura.
Las funciones de tipo carácter discutidas a continuación esperan como parámetros cadenas de texto.
Estos pueden ser una cadena de texto literal, un carácter o una expresión que dé como resultado un
carácter.

a. La función LOWER
La función LOWER convierte una cadena de caracteres en su equivalente en minúsculas. No añade
caracteres extra o acorta la longitud inicial de la cadena de texto. Los caracteres en mayúscula se
convierten a su equivalente en minúscula. Los números, puntuaciones o caracteres especiales no se
tienen en cuenta en la transformación. La función LOWER solo puede tomar un parámetro de entrada:
LOWER(s)
Las siguientes consultas ilustran el uso de esta función:

Consulta 1: SELECT LOWER (100) FROM dual;

Consulta 2: SELECT LOWER (100+100) FROM dual;


Consulta 3: SELECT LOWER ('The SUM'||'100 + 100'||' = 200') FROM dual;

Las Consultas 1 y 2 devuelven cadenas de texto “100” y “200” respectivamente. El parámetro introducido
a la Consulta 3 es una expresión formada por caracteres y la cadena devuelta por la función es “the sum
100 + 100 = 200”.

Consulta 4: SELECT LOWER (SYSDATE) FROM dual;

Consulta 5: SELECT LOWER (SYSDATE+2)

FROM dual;

Asumiendo que la fecha actual del servidor es: 17-DEC-2007. Las Consultas 4 y 5 devolverán “17-
dec2007” y “19-dec-2007” respectivamente. Las expresiones de fechas se evalúan y se convierten
implícitamente en tipo carácter antes de que se ejecute la función LOWER.
La siguiente imagen muestra la función LOWER utilizada en la cláusula WHERE para localizar los registros
con las minúsculas “u” y “r” consecutivas en el campo LAST_NAME.

121/306
SQL

Una consulta alternativa para obtener el mismo resultado sin usar la función LOWER en la cláusula WHERE
sería:

SELECT first_name, last_name, LOWER (last_name)


FROM employees
WHERE last_name LIKE '%ur%'
OR last_name LIKE '%UR%'
OR last_name LIKE '%uR%'
OR last_name LIKE '%Ur%';

La consulta es correcta pero incómoda para trabajar con ella, y el número de cláusulas OR requeridas se
incrementa exponencialmente con la longitud de la cadena de texto.

b. La función UPPER
La función UPPER es el opuesto lógico de la función LOWER y convierte una cadena de caracteres en su
equivalente en mayúsculas. No añade caracteres adicionales o acorta la longitud de la cadena de texto
inicial. Todos los caracteres en minúsculas se convierten a sus equivalentes en mayúsculas. Los números,
puntuaciones o caracteres especiales no se tienen en cuenta en la transformación. La función UPPER solo
puede tomar un parámetro de entrada:

UPPER(s),
Las siguientes consultas ilustran el uso de esta función:

Consulta 1: SELECT UPPER (1+2.14) FROM dual;

Consulta 2: SELECT UPPER (SYSDATE) FROM dual;

La Consulta 1 devuelve la cadena “3.14”. El parámetro introducido a la función UPPER en la Consulta 2


es SYSDATE, el cual devuelve la fecha actual del servidor. Al devolver la fecha en mayúsculas por defecto
no se produce ninguna transformación.

122/306
SQL

La función UPPER se utiliza en la siguiente imagen para extraer los registros de la tabla COUNTRIES
donde los valores de COUNTRY_NAME contienen las letras ‘U’, ‘S’ y ‘A’ en este orden. Las letras indicadas
no tienen por qué ser adyacentes.

Una consulta alternativa para obtener el mismo resultado sin utilizar las funciones UPPER o LOWER podría
ser la siguiente, utilizando ocho condiciones:

SELECT * FROM COUNTRIES


WHERE country_name LIKE '%u%s%a%' OR country_name LIKE '%u%s%A%'
OR country_name LIKE '%u%S%a%' OR country_name LIKE '%u%S%A%'
OR country_name LIKE '%U%s%a%' OR country_name LIKE '%U%s%A%'
OR country_name LIKE '%U%S%a%' OR country_name LIKE '%U%S%A%';

La consulta es correcta pero difícil de trabajar y el número de cláusulas OR requeridas se incrementa


exponencialmente con la longitud de la cadena de texto.

c. La función INITCAP
La función INITCAP convierte la primera letra de cada palabra de una cadena de texto en mayúscula. Se
utiliza a menudo para la presentación de datos. Normalmente, una palabra es una cadena de caracteres
adyacentes separada de otras por un espacio o un guion bajo, pero otros caracteres como el porcentaje,
exclamaciones o símbolos de dólar también son aceptados como caracteres de separación. Los símbolos
de puntuación o caracteres especiales son válidos para la separación de palabras. La función INITCAP
solo puede tomar un parámetro de entrada:

INITCAP(s),
Las siguientes consultas ilustran el uso de esta función:

Consulta 1: SELECT INITCAP (21/7) FROM dual;

123/306
SQL

Consulta 2: SELECT INITCAP (SYSDATE) FROM dual;

Consulta 3: SELECT INITCAP ('init cap or init_cap or init%cap') FROM dual;

La Consulta 1 devuelve el cociente 3 como cadena de texto. La Consulta 2 devuelve la fecha actual del
servidor con el mes transformado de mayúsculas a únicamente el primer carácter en mayúscula y el
resto en minúscula. Asumiendo que la fecha actual del servidor es 17-DEC-2007, la Consulta 2 devuelve
“17-Dec-2007”. La Consulta 3 devuelve “Init Cap Or Init_Cap Or Init%Cap”.

Las consultas de la siguiente imagen seleccionan los valores LAST_NAME y JOB_ID de la tabla EMPLOYEES
para aquellos empleados con LAST_NAME que empiece con la letra “H”. La primera consulta aplica la
función INITCAP para toda la cláusula SELECT. La segunda consulta muestra como la función INITCAP se
aplica por separado a cada cadena de texto. Ambas consultas devuelven resultados idénticos.

4.2.2. Utilizando funciones de manipulación de caracteres


Algunas de las características más útiles que ofrece Oracle son las funciones de manipulación de
caracteres. Su utilidad en la manipulación de datos es casi inigualable. Anidar este tipo de funciones es
común. El operador de concatenación (||) se utiliza generalmente en lugar de la función CONCAT. Las
funciones LENGTH, INSTR, SUBSTR y REPLACE a menudo se encuentran en compañía de otras funciones
como RPAD, LPAD y TRIM.

124/306
SQL

a. La función CONCAT
La función CONCAT une dos caracteres, columnas o expresiones de caracteres a una expresión de
caracteres más grande. Los literales numéricos y de fecha son implícitamente convertidos como
caracteres cuando aparecen como parámetros de la función CONCAT. Expresiones numéricas o de fecha
son evaluadas antes de ser convertidas en cadenas de texto preparadas para ser concatenadas.

La función CONCAT toma dos parámetros. Su sintaxis es:


CONCAT(s1, s2)
donde s1 y s2 representan cadenas de caracteres, valores de columna de tipo carácter o expresiones
resultantes en valores de tipo carácter. Las siguientes consultas ilustran el uso de esta función:

Consulta 1: SELECT CONCAT (1+2.14,' approximates pi') FROM dual;

Consulta 2: SELECT CONCAT ('Today is: ',SYSDATE) FROM dual;

La Consulta 1 devuelve la cadena "3.14 aproximates pi". La expresión numérica devuelve el número
3.14. Este número cambia automáticamente a la cadena de caracteres "3.14", que se concatena con el
literal de tipo carácter del segundo parámetro. El segundo parámetro de la función CONCAT en la Consulta
2 es SYSDATE, el cual devuelve la fecha actual del sistema. Este valor se convierte implícitamente a una
cadena a la cual se le concatena el literal del primer parámetro. Si la fecha del sistema fuese 17-DIC-
2007, la Consulta 2 devolverá la cadena "Hoy es: 17-DIC-2007".

Considérese la posibilidad de usar la función CONCAT para unir tres términos para devolver una única
cadena de caracteres. Dado que CONCAT sólo acepta dos parámetros, sólo es posible unir dos términos
con una instancia con esta función. La solución es anidar la función CONCAT dentro de otra función
CONCAT, como se muestra aquí:

SELECT CONCAT ('Outer1', CONCAT ('Inner1',' Inner2'))


FROM dual;

La primera función CONCAT tiene dos parámetros: el primero es la cadena "Outer1", mientras que la
segunda es una función CONCAT anidada. La segunda función CONCAT toma dos parámetros: el primero
es la cadena "Inner1" mientras que el segundo es la cadena "Inner2". Esta consulta resulta en la siguiente
cadena: "Outer1 Inner1 Inner2". El anidado de funciones se describe más detalladamente en el capítulo
5.
La función CONCAT se utiliza en la imagen mostrada a continuación para extraer los registros de la tabla
EMPLOYEES donde DEPARTMENT_ID=100. El objetivo es producir una salida literal de una sola cadena a
partir de la función CONCAT del formato ‘FIRST_NAME LAST_NAME earns SALARY’.

125/306
SQL

Esta simple tarea resulta en un complejo conjunto de cuatro niveles anidados de llamadas a la función.
Como demuestra el segundo ejemplo de la figura anterior, el operador de concatenación realiza la tarea
equivalente de una manera más sencilla.

b. La función LENGTH
La función LENGTH devuelve el número de caracteres que constituyen una cadena de caracteres. Esto
incluye caracteres, columnas o expresiones de caracteres. Valores de tipo fecha y numéricos se
convierten automáticamente al tipo carácter cuando aparecen como parámetros de la función LENGTH.
Las expresiones numéricas o de fecha se evalúan antes de ser convertidas a cadenas listas para medir
su longitud. Los espacios en blanco, tabulaciones y caracteres especiales son contados por la función
LENGTH. La función LENGTH sólo acepta un parámetro. Su sintaxis es:

LENGTH (s)
Donde s representa cualquier cadena de valores, valor de columna de tipo carácter o expresión resultante
en un valor de tipo carácter.
Las consultas siguientes ilustran el uso de esta función:
Consulta 1: SELECT LENGTH (1+2.14||'approximates pi') FROM dual;

Consulta 2: SELECT LENGTH (SYSDATE)FROM dual;

La Consulta 1 devuelve el número 19. La expresión numérica se evalúa para devolver el número 3.14.
Este número se trata automáticamente como una cadena de caracteres "3.14" la cual es luego

126/306
SQL

concatenada al carácter literal " approximates pi". La cadena de caracteres resultante contiene 20
caracteres. La Consulta 2 evalúa primero la función SYSDATE, que devuelve la fecha actual del sistema.
Este valor se convierte automáticamente en una cadena de caracteres cuya longitud se evalúa a
continuación. Suponiendo que la fecha del sistema devuelta sea "17-DEC-07", la consulta 2 devuelve el
valor 9.

La función LENGTH se usa en la siguiente imagen para extraer los registros cuyo COUNTRY_NAME tenga
longitud superior a 10 caracteres de la tabla COUNTRIES.

c. Las funciones LPAD y RPAD

Las funciones LPAD y RPAD, también conocidas como funciones ‘left pad’ y ‘right pad’, devuelven una
cadena con un número específico de caracteres a la izquierda o a la derecha de la cadena
respectivamente. Las cadenas de caracteres utilizadas incluyen literales, valores de columna, o
expresiones de caracteres. Los literales numéricos y de fecha están implícitamente convertidos a
caracteres cuando aparecen como parámetros de las funciones LPAD y RPAD. Expresiones de tipo fecha
o numérico son evaluadas antes de ser convertidas a cadenas. También se puede utilizar espacios en
blanco, tabulaciones y caracteres especiales como parámetros para dichas funciones.
Las funciones LPAD y RPAD toman tres parámetros. Sus sintaxis son:

LPAD (string, length,[padstring])


RPAD (string, length,[padstring])

127/306
SQL

donde string representa la cadena de origen, length es un valor entero que representa la longitud final
de la cadena devuelta, y padstring especifica la cadena de caracteres a utilizar como relleno. Si se utiliza
LPAD (o RPAD), el padstring se añade a la izquierda (o derecha) de la cadena de origen hasta que alcance
la longitud deseada. Hay que tener en cuenta que, si la longitud es menor o igual a la longitud de la
cadena de la fuente, entonces no hay relleno y se devuelve una sub-cadena de la cadena de origen con
un número de caracteres igual a la longitud. Si se omite el padstring, la cadena de caracteres utilizada
para el relleno es, por defecto, un espacio en blanco. Las consultas siguientes ilustran la utilización de
esta función:

Consulta 1: SELECT LPAD (1000+200.55,14,'*')

FROM dual;

Consulta 2: SELECT RPAD (1000+200.55,14,'*')

FROM dual;

Consulta 3: SELECT LPAD (SYSDATE,14,'$#')

FROM dual;

Consulta 4: SELECT RPAD (SYSDATE,4,'$#')

FROM dual;

La Consulta 1 devuelve una cadena de 14 caracteres: “*******1200.55”. La expresión numérica se


evalúa para devolver el número 1200.55. Este número se trata como la cadena "1200.55" de longitud
siete (incluyendo el punto decimal). Para lograr la longitud final de 14 caracteres, se utilizan 7 asteriscos
para rellenar la cadena. La Consulta 2 devuelve la cadena “1200.55*******”.

La función LPAD en la Consulta 3 tiene una longitud de cadena deseada de 14 caracteres. Suponiendo
que SYSDATE devuelve un valor de fecha de nueve caracteres: “17-DEC-07”. Esta fecha se convierte en
una cadena de caracteres, y se "rellena" sistemáticamente para alcanzar la longitud objetivo,
devolviendo: "$#$#$17-DIC-07". Téngase en cuenta que, aunque el "relleno" consta de dos caracteres
($#), la cadena no se aplicó uniformemente ya que hay tres símbolos de dólar y dos símbolos de
almohadilla. Esto se debe a que LPAD y RPAD rellenarán la cadena de origen tanto como sea posible con
la cadena de "relleno" hasta la longitud que se desea alcanzar. La función RPAD en la Consulta 4 tiene
una longitud objetivo de 4 caracteres, pero la función la función SYSDATE devuelve un valor de nueve
caracteres. Por lo tanto, no se produce "relleno" y, suponiendo que la fecha actual del sistema es "17DEC-
07", solo se devuelven los cuatro primeros caracteres: “17-D”.
Los resultados de las siguientes consultas son idénticos en las dos imágenes siguientes, pero se han
vuelto más legibles utilizando (en la segunda imagen) las funciones LPAD y RPAD, que formatean los
resultados de una manera más ordenada y presentable.

128/306
SQL

129/306
SQL

d. La función TRIM

La función TRIM elimina caracteres del principio o del final de las cadenas, columnas o expresiones de
caracteres para obtener un elemento de tipo carácter potencialmente más corto.

Los valores de tipo fecha o numérico se convierten automáticamente en caracteres cuando aparecen
como parámetros en la función TRIM. Las expresiones numéricas o de fecha se evalúan primero, antes
de ser convertidas a cadenas de caracteres, para poder ser recortadas.
La función TRIM toma un parámetro compuesto por un parámetro opcional y un parámetro obligatorio.
Su sintaxis es:

TRIM ([trailing|leading|both] trimstring from str)

La cadena a recortar (str) es obligatoria. Los siguientes puntos enumeran las reglas que rigen el uso de
esta función:

• TRIM(str) elimina espacios a ambos lados de la cadena de entrada. Cuando no se especifica la


dirección de corte, se recorta por ambos lados de la cadena.

• TRIM(trailing trimstring from str) elimina todas las coincidencias (si existen) con trimstring del
final de la cadena str.
• TRIM(leading trimstring from str) remueve todas las coincidencias (si existen) con trimstring del
inicio de la cadena str.

130/306
SQL

• TRIM(both trimstring de str) elimina todas las coincidencias (si existen) con trimstring del inicio
y final de la cadena str.
Las consultas siguientes ilustran la utilización de esta función:

Consulta 1: SELECT TRIM (TRAILING 'e' FROM 1+2.14||' is pie') FROM dual;

Consulta 2: SELECT TRIM (BOTH '*' FROM '*********Hidden*********') FROM dual;

Consulta 3: SELECT TRIM (1 from SYSDATE) FROM dual;

La Consulta 1 evalúa la expresión numérica para devolver el número 3.14. Este es entonces convertido
como cadena de caracteres "3.14" la cual es a su vez concatenada con el literal de tipo carácter para
construir la cadena "3.14 is pie". A continuación, la función TRIM elimina cualquier coincidencia con el
carácter "e" del final de la cadena para devolver "3.14 es pi".

La Consulta 2 elimina todas las coincidencias con el carácter "*" del principio y el final de la cadena de
texto y devuelve la cadena "Hidden". Aunque se especifique un solo carácter de recorte, se recortarán
múltiples caracteres siempre y cuando estos sean consecutivos.
La Consulta 3 tiene dos aspectos interesantes. La cadena origen no está entre comillas y se convierte
implícitamente en una de tipo carácter.

La función SYSDATE devuelve la fecha actual del sistema (supóngase 17-DEC-07). Dado que no se
especifica ninguna palabra clave (trailing, leading o both) para el recorte, se aplica el valor por defecto
both. Por lo tanto, todas las coincidencias con el carácter ‘1’ al principio o al final de la cadena de fecha
se cortan, devolviéndose "7-DEC-07".

La función TRIM utilizada en la imagen siguiente no parece hacer nada, pero un análisis más profundo
revela un uso práctico y común para ello.

131/306
SQL

Como se discutió anteriormente, los datos se introducen con frecuencia en las tablas de la base de datos
de la aplicación a través de una variedad de fuentes.

Puede ocurrir que los espacios se introduzcan accidentalmente y se guarden sin querer en campos de
caracteres.

La cadena de recorte de la cláusula WHERE simula el campo LAST_NAME rellenado con espacios. Esto
podría dificultar la búsqueda de empleados con los valores LAST_NAME = Smith. Recortar el espacio
añadido a LAST_NAME permite una búsqueda precisa y elimina el riesgo de espacios no intencionados
que pueden estar presentes en los datos de tipo carácter. Recordar que cuando no se especifican
parámetros distintos de la cadena str en la función TRIM, entonces su comportamiento por defecto es el
siguiente:

TRIM (both ' ' from str);

e. La función INSTR (In-String)


La función INSTR localiza la posición de una cadena de caracteres de búsqueda dentro de otra cadena
de caracteres determinada. Se devuelve la posición numérica en la que comienza la enésima aparición
de la cadena de búsqueda, relativa a una posición de inicio especificada. Si no aparece la cadena de
búsqueda, el INSTR devuelve cero.

Los valores numéricos y de fecha están implícitos como caracteres cuando aparecen como parámetros
de la función INSTR. Las expresiones de fecha o numéricas se evalúan primero antes de convertirse en
cadenas de caracteres para poder ser buscadas.
La función INSTR toma cuatro parámetros, compuestos por dos obligatorios y dos opcionales. La sintaxis
es:

132/306
SQL

INSTR (source string, search string,[search start position],[nth ocurrence]),

El valor por defecto para el parámetro search start position es 1 o el inicio del parámetro source string.
El valor por defecto para el parámetro nth ocurrence es 1 o la primera aparición.
Las siguientes consultas ilustran la función INSTR con expresiones numéricas y de fecha:

Consulta 1: SELECT INSTR (3+0.14,'.') FROM dual;

Consulta 2: SELECT INSTR (SYSDATE,'DEC') FROM dual;

La Consulta 1 evalúa la expresión numérica para devolver el número 3.14. Este se convierte
implícitamente como la cadena de caracteres "3.14". Se busca el carácter "." y aparece por primera vez
en la posición 2.

La Consulta 2 evalúa el SYSDATE y convierte la fecha devuelta en una cadena de caracteres (supuesto
que la fecha del sistema es 17-DEC-07). La primera aparición de los caracteres DEC se da a partir de la
posición 4.

Considérense, ahora, las siguientes consultas con datos de tipo carácter que ilustran el valor por defecto
y los parámetros tercero y cuarto de la función INSTR:

Consulta 3: SELECT INSTR ('1#3#5#7#9#','#') FROM dual;

Consulta 4: SELECT INSTR ('1#3#5#7#9#','#',5) FROM dual;

Consulta 5: SELECT INSTR ('1#3#5#7#9#','#',3,4) FROM dual;

La Consulta 3 busca la primera aparición del carácter "#" en la cadena de origen comenzando en la
posición 1 y devolviendo la posición 2 como resultado.

La Consulta 4 tiene el número 5 como tercer parámetro, que indica que la búsqueda del carácter debe
comenzar en la posición 5 en la cadena de origen. La aparición posterior del símbolo de control se
encuentra en la posición 6, devuelta por la consulta.

La Consulta 5 tiene los números 3 y 4 como su tercer y cuarto parámetro, respectivamente. Esto indica
que la búsqueda del carácter debe comenzar en la posición 3 de la cadena de origen. La consulta 5
devuelve el número 10, que es la posición de la cuarta aparición del símbolo de almohadilla cuando la
búsqueda comienza en posición 3.

La función INSTR utilizada en la siguiente figura devuelve los registros de la tabla DEPARTMENTS donde
se encuentra la cadena "on" comenzando en la segunda posición de la columna DEPARTMENT_NAME.

133/306
SQL

La función INSTR se utiliza a menudo en combinación con la función SUBSTR en programas de


utilidades, diseñados para extraer datos codificados de flujos de datos electrónicos.

f. La función SUBSTR (Substring)


La función SUBSTR extrae y devuelve un segmento de una cadena de caracteres dada. Se extrae una
subcadena de una longitud especifica de la cadena de origen que comienza en una posición dada. Si la
posición inicial es mayor que la longitud de la cadena de origen, se devuelve null. Si el número de
caracteres a extraer de una posición inicial dada es mayor que la longitud de la cadena de origen, el
segmento devuelto es la sub-cadena desde la posición inicial hasta el final de la cadena.
Los valores numéricos y de fecha son convertidos automáticamente como cadena de caracteres cuando
aparecen como parámetros de la función SUBSTR. Las expresiones numéricas y de fecha se evalúan
antes de convertirse en cadenas para poder ser buscadas.
La función SUBSTR toma tres parámetros, siendo los dos primeros obligatorios. Su sintaxis es:

SUBSTR (source string, start position,[number of characters to extract]),

El número por defecto de caracteres a extraer es igual al número de caracteres desde la posición inicial
hasta el final de la cadena de origen. Las siguientes consultas ilustran la función SUBSTR con expresiones
numéricas y de fecha:

Consulta 1: SELECT SUBSTR (10000-3,3,2) FROM dual;

Consulta 2: SELECT SUBSTR (SYSDATE,4,3) FROM dual;

134/306
SQL

La Consulta 1 evalúa la expresión numérica para devolver el número 9997. Este se convierte
automáticamente en la cadena de caracteres "9997". La búsqueda comienza en la posición 3 y los dos
caracteres desde esa posición en adelante son extraídos, dando como resultado la sub-cadena "97".
La Consulta 2 evalúa la función SYSDATE y convierte la fecha devuelta en una cadena de caracteres
(supuesto que la fecha del sistema es 17-DEC-07). La búsqueda de la sub-cadena comienza en la posición
4 y, a partir de esta posición, se extraen los 3 caracteres, dando lugar a la sub-secuencia "DEC".
Considérense las siguientes consultas con datos de tipo carácter que ilustran el comportamiento por
defecto del parámetro opcional de la función SUBSTR:

Consulta 3: SELECT SUBSTR ('1#3#5#7#9#',5) FROM dual;

Consulta 4: SELECT SUBSTR ('1#3#5#7#9#',5,6) FROM dual;

Consulta 5: SELECT SUBSTR ('1#3#5#7#9#',-3,2) FROM dual;

La Consulta 3 extrae la sub-cadena comenzando en la posición 5. Como el tercer parámetro no se


especifica, la longitud de extracción por defecto es igual al número de caracteres desde la posición de
inicio (posición 5 inclusive en este caso) hasta el final de la cadena que tiene seis caracteres de longitud.
Por lo tanto, la consulta 3 es equivalente a la Consulta 4 y la cadena devuelta por ambas consultas es
"5#7#9#". La Consulta 5 tiene el número -3 como posición de inicio. El parámetro negativo de posición
de inicio instruye a Oracle a comenzar buscando tres caracteres del final de la cadena. Por lo tanto, la
posición inicial es tres caracteres del final de la cadena, que es la posición 8. El tercer parámetro es 2,
lo que resulta en la devolución de la sub-cadena "#9".
La función SUBSTR utilizada en la figura siguiente devuelve los registros de la tabla EMPLOYEES donde
los dos primeros caracteres de las columnas JOB_ID son AD. Esta función se ha utilizado también en la
sentencia SELECT para extraer el carácter inicial del campo FIRST_NAME de cada empleado del conjunto
de resultados.

135/306
SQL

g. La función REPLACE

La función REPLACE reemplaza todas las coincidencias de una cadena dada, que se encuentren en otra
cadena origen, con un elemento de reemplazo, devolviendo la cadena de origen modificada. Si la longitud
del elemento de reemplazo es diferente de la del elemento de búsqueda, la longitud de la cadena origen
y la cadena resultado serán diferentes. Si no se encuentra la cadena de búsqueda, se devuelve la cadena
sin cambios. Los valores y expresiones numéricas y de fecha son evaluados antes de ser convertidos
implícitamente como caracteres cuando aparecen como parámetros de la función REPLACE.

La función REPLACE toma tres parámetros, siendo los dos primeros obligatorios. Su sintaxis es:

REPLACE (source string, search item,[replacement term]),

Si se omite el parámetro replacement term, cada aparición del parámetro search item es borrado de
source string. En otras palabras, se sustituye por una cadena vacía. Las siguientes consultas ilustran la
función REPLACE con números y expresiones de fecha:

Consulta 1: SELECT REPLACE (10000-3,'9','85') FROM dual;

Consulta 2: SELECT REPLACE (SYSDATE, 'DEC','NOV') FROM dual;

La Consulta 1 evalúa la expresión numérica para devolver el número 9997, el cual es convertido como la
cadena de caracteres "9997". La cadena o elemento de búsqueda es el carácter "9" que aparece tres
veces en la cadena origen. Cada carácter de búsqueda se sustituye por la cadena de sustitución "85",
dando como resultado la cadena "8585857".
La Consulta 2 evalúa la función SYSDATE y convierte la fecha devuelta en una cadena de caracteres
(supuesto que la fecha del sistema es 17-DEC-07). La cadena de búsqueda "DEC" aparece una vez en la

136/306
SQL

cadena de origen y se sustituye por los caracteres "NOV", obteniéndose el resultado "17-NOV-07".
Téngase en cuenta que esto es una cadena de caracteres y no un valor de fecha.
Considérese las siguientes consultas que ilustran el comportamiento por defecto del parámetro opcional
de la función REPLACE:

Consulta 3: SELECT REPLACE ('1#3#5#7#9#','#','->') FROM dual;

Consulta 4: SELECT REPLACE ('1#3#5#5#7#9#','#') FROM dual;

El símbolo "#" en la Consulta 3 se especifica como el carácter de búsqueda y el de sustitución se especifica


como "->". El símbolo "#" aparece cinco veces en la cadena origen, dando como cadena resultante: "1-
>3->5->7->7->9->".

La Consulta 4 no especifica un elemento de sustitución. Por lo tanto, el comportamiento por defecto es


reemplazar la cadena de búsqueda por una cadena vacía que, en efecto, elimina completamente el
carácter de búsqueda de la cadena origen, dando como resultado la cadena "13579".

La función REPLACE utilizada en la imagen siguiente devuelve los registros de la tabla EMPLOYEES donde
JOB_ID = SA_MAN, pero modifica la columna SALARY sustituyendo cada 0 por 000 y solapando la nueva
expresión como "Dream Salary".

Problema Solución

137/306
SQL

Se desea buscar una cadena de caracteres Sí, la solución más sencilla es, en primer lugar,
almacenada en la base de datos. El formato en utilizar TRIM, eliminando los espacios en
la que se encuentra almacenada es blanco de la columna (leading y trailing) y
desconocido y posee espacios en blanco luego convertir los datos de columna utilizando
delante y detrás de la cadena. ¿Puede dicha una función de conversión como LOWER,
consulta realizarse? UPPER, o INITCAP para simplificar el número
de comparaciones requeridas en la cláusula
WHERE.

Se pide extraer los últimos tres caracteres de Sí. La función SUBSTR(source string, start
la columna LAST_NAME en la tabla position, number of characters) tiene tres
EMPLOYEES. ¿Se puede realizar una consulta parámetros. Si la posición de inicio se
de este tipo sin el uso de la función LENGTH? establece a -3, y el número de caracteres se
establece en 3 o se omite, los últimos tres
caracteres de los datos de la columna
LAST_NAME serán devueltos. Se puede utilizar
la siguiente consulta:

SELECT SUBSTR (LAST_NAME,-3)


FROM EMPLOYEES;

Se quiere extraer una cadena de diez Sí, la función LPAD se puede utilizar de la
caracteres basada en la columna SALARY en la siguiente manera:
tabla EMPLOYEES. Si el valor del salario es
menor de diez caracteres, se deben añadir SELECT LPAD (SALARY,10,0)
ceros a la izquierda del valor para obtener una FROM EMPLOYEES;
cadena de diez caracteres. ¿Es esto posible?

4.2.3. Utilizando Funciones Numéricas


Hay una gama de funciones numéricas proporcionadas por Oracle que rivalizan con las funciones
matemáticas de hojas de cálculo de paquetes de software populares. Una diferencia significativa entre
las funciones de tipo numérico y el resto es que estas aceptan y devuelven solo datos numéricos. Oracle
proporciona funciones numéricas para resolver trigonometría, exponenciación y problemas logarítmicos
entre otros. Esta sección se centra en tres funciones numéricas de registro único: ROUND, TRUNC y
MOD.

a. La función numérica ROUND


La función ROUND realiza una operación de redondeo sobre un valor numérico basado en la precisión
decimal especificada. El valor devuelto se redondea por exceso o por defecto, basándose en el valor
especificado para la cifra significativa. Si el decimal especificado es n, la cifra significativa para redondear
se encuentra (n+1) posiciones a la DERECHA de la coma. Si es negativo, la cifra significativa para
redondear se encuentra n posiciones a la IZQUIERDA de la coma. Si el valor numérico de la cifra
significativa es mayor o igual a 5, se produce un redondeo por exceso; en caso contrario, se producirá
un redondeo por defecto. La función ROUND acepta dos parámetros:

ROUND (source number, [decimal precision]),

138/306
SQL

El parámetro source number representa cualquier literal, columna o expresión numérica. El parámetro
decimal precision (opcional) especifica el grado de redondeo. Si no se introduce el parámetro decimal
precision, por defecto, el grado de redondeo es cero, lo que significa que se redondea al número entero
más cercano.

Se consideran los grados decimales listados en la siguiente tabla para el número 1234.5678. Los valores
negativos para el parámetro de precisión se encuentran a la izquierda de la coma mientras que los
positivos a la derecha.

Precisión Cifra Posición


decimal significativa decimal

-3 1 Millares (nx1000)

-2 2 Centenas
(nx100)

-1 3 Decenas (nx10)

0 4 Unidades (nx1)

1 5 Décimas (n/10)

2 6 Centésimas
(n/100)

3 7 Milésimas(n/1000)

Si el parámetro de precisión decimal es cero, el número se redondea al número entero más cercano. Si
es 2, entonces el parámetro source number se redondea a la centésima más cercana y así sucesivamente.
Las siguientes consultas ilustran la utilización de esta función:

Consulta 1: SELECT ROUND (1601.916718,1) FROM dual;


Consulta 2: SELECT ROUND (1601.916718,2) FROM dual;
Consulta 3: SELECT ROUND (1601.916718,-3) FROM dual;
Consulta 4: SELECT ROUND (1601.916718) FROM dual;

La Consulta 1 tiene una precisión decimal (n) de 1, lo que implica que el número introducido se redondea
a la décima más cercana. Al ser el dígito (n+1) menor que 5 no se produce redondeo y el número
devuelto es 1601.9. El parámetro decimal precision en la Consulta 2 es 2, así que el valor introducido se

139/306
SQL

redondeará a la centésima. El valor de las milésimas es 6 por lo que se realizará el redondeo y el valor
devuelto será 1601.92. En la Consulta 3, el parámetro es -3. Al ser negativo, la cifra significativa a
redondear es 6, se encuentra tres posiciones a la izquierda de la coma y se redondea al millar,
devolviendo 2000. En la Consulta 4 no se ha introducido parámetro decimal precision por lo que el
número se redondeará al número entero más cercano, devolviendo el valor 1602.

El ejemplo mostrado a continuación selecciona los empleados que trabajan como jefes de ventas y calcula
un "bono de antigüedad" basándose en el número de días trabajados redondeado al número entero más
cercano. La función ROUND se utiliza para redondear la parte fraccional de la diferencia entre la fecha
del servidor y el valor de HIRE_DATE para cada empleado.

b. La función numérica TRUNC


La función TRUNC realiza una operación de truncamiento en base a un valor numérico en la precisión
decimal especificada. Un truncamiento numérico se diferencia del redondeo en que el valor resultante
aproxima los números a la precisión decimal especificada y no intenta redondear por exceso ni por defecto
si la precisión decimal es positiva. Sin embargo, si la precisión decimal especificada (n) es negativa, el valor
introducido es puesto a cero desde la posición decimal n.

TRUNC (source number, [decimal precision]),

El parámetro source number representar cualquier literal, valor de columna o expresión numé[Link]
parámetro decimal precision (opcional) especifica el grado de truncamiento. Si no se introduce el
parámetro decimal precision, el grado de truncamiento por defecto es cero, lo que significa que se trunca
al número entero más cercano. Si el parámetro decimal precision es 1, entonces el número se trunca en
las décimas. Si es 2, se trunca en la centésima, y así sucesivamente. Las siguientes consultas ilustran el
uso de esta función:

Consulta 1: SELECT TRUNC (1601.916718,1) FROM dual;

140/306
SQL

Consulta 2: SELECT TRUNC (1601.916718,2) FROM dual;


Consulta 3: SELECT TRUNC (1601.916718,-3) FROM dual;
Consulta 4: SELECT TRUNC (1601.916718) FROM dual;

La Consulta 1 tiene un valor del parámetro decimal precision de 1, lo que implica que el número
introducido se truncará a la décimas de unidad y el valor devuelto es 1601.9. En la Consulta 2, el
parámetro (n) es 2 por lo que el valor introducido será truncado a las centésimas, devolviendo 1601.91.
Es importante saber diferenciar el resultado entre ROUND y TRUNC para el mismo valor con el mismo
del parámetro decimal precision, ya que en la función ROUND, al ser el digito (n+1=3) 6 y es mayor que
5, el valor devuelto habría sido 1601.92.
En la Consulta 3 se introduce un valor de -3 al parámetro (n), lo que indica que el truncamiento se
realizará tres posiciones a la izquierda de la coma, al millar más cercano. Por lo tanto, el número se
trunca al millar y devuelve 1000. Por último, la Consulta 4 no contiene el parámetro decimal precision,
por lo que el truncamiento se realizará al número entero más próximo. El valor devuelto es 1601.
En la siguiente figura se muestra el ajuste salarial con el premio otorgado al departamento de finanzas.
El departamento de finanzas ha decidido otorgar un premio departamental con el cual la empresa
recompensará a su personal de finanzas ajustando sus salarios. La función TRUNC se utiliza para truncar
el aumento de sueldo propuesto a un número entero.

c. La función MOD
La función MOD devuelve el resto de una operación de división. Se proporcionan dos números, el
dividendo (número que se divide) y el divisor (número por el que se divide) y se realiza una operación
de división. Si el divisor es factor del dividendo, MOD devuelve cero ya que no hay resto. Si el divisor es
cero, no se devuelve error, en su lugar la función MOD devuelve el dividendo. Si el divisor es mayor que
el dividendo, entonces la función MOD devuelve el dividendo como resultado. Esto se debe a que divide
cero veces el dividendo, dejando el resto igual al dividendo.

141/306
SQL

MOD (dividend, divisor),

Los parámetros dividend y divisor representan un literal, un valor de columna o una expresión numérica,
que pueden ser negativos o positivos. Las siguientes consultas ilustran el uso de esta función:

Consulta 1: SELECT MOD (6,2) FROM dual;


Consulta 2: SELECT MOD (5,3) FROM dual;
Consulta 3: SELECT MOD (7,35) FROM dual;
Consulta 4: SELECT MOD (5.2,3) FROM dual;

La Consulta 1 divide 6 entre 2 por lo que el resto devuelto es 0. La Consulta 2 divide 5 entre 3 devolviendo
2 como resto. La Consulta 3 intenta dividir 7 entre 35. Al tratarse de un divisor mayor que el dividendo
la función MOD devuelve 7. La Consulta 4 divide 5.2 entre 3 devolviendo 2.2 como resto.

Cualquier número par dividido por 2 no tendrá resto, pero los impares divididos por 2 siempre
devolverán 1. Por lo tanto, la función MOD se usa a menudo para distinguir entre números
pares e impares.

La columna EMPLOYEE_ID de la tabla EMPLOYEES almacena una secuencia única de números para cada
registro empezando por el 100. Los primeros 12 empleados deben ser asignados a uno de los cuatro
equipos de manera cíclica para una tarea en particular. En la imagen anterior se muestra la sentencia
para realizar la asignación.

Los registros de los 12 empleados se aíslan con un operador BETWEEN en la cláusula WHERE. La función
MOD se aplica a la división de la columna EMPLOYEE_ID entre 4. la función MOD asigna los números del
0 al 3 a cada fila de una manera cíclica.

Los valores propuestos asumidos por los parámetros opcionales de las funciones no son
siempre intuitivos, pero a menudo se prueban. Por ejemplo, llamando a la función SUBSTR
con sólo los resultados de los dos primeros parámetros en la función que extrae una

142/306
SQL

subcadena de una posición de inicio hasta el final de la cadena de texto. El parámetro opcional
tanto para el numérico como para la fecha TRUNC y ROUND es el grado de precisión. Por
ejemplo, llamando a la función numérica TRUNC sin especificar el grado de truncamiento
resulta que el número sea truncado al número entero más cercano. Es útil estar familiarizado
con el valor por defecto asignado a los parámetros opcionales para estas funciones.

4.2.4. Trabajando con fechas


Las funciones incorporadas de fecha proporcionan una manera conveniente de resolver problemas
relacionados con fechas sin necesidad de hacer un seguimiento de los años bisiestos o del número de
días en meses en particular. Se discutirá el almacenamiento de fechas por Oracle y las máscaras de
formato de fecha predeterminadas antes de examinar la función SYSDATE. A continuación, se indagará
en la aritmética de fechas y las funciones de manipulación de fecha: ADD_MONTHS, MONTHS_BETWEEN,
LAST_DAY, NEXT_DAY, ROUND y TRUNC.

a. Almacenamiento de datos en base de datos


Las fechas se almacenan internamente en un formato numérico que soporta el almacenamiento de siglo,
año, mes y día, así como información de horas, minutos, y segundos. Estos atributos de fecha están
disponibles para cada cadena, valor de columna o expresión del tipo fecha.
Cuando se accede a la información de fecha desde una tabla, el formato predeterminado de los resultados
consta de dos dígitos que representan el día, una abreviatura de tres letras para el mes, y dos dígitos
que representan el componente de año. Por defecto, estos componentes están separados con guiones
en SQL*Plus y SQL Developer. Las versiones anteriores de SQL Developer utilizaban barras oblicuas hacia
adelante como separador de fecha predeterminado al mostrarlas. La siguiente figura muestra el
contenido de la columna START_DATE después de consultar la tabla JOB_HISTORY, utilizando SQL*Plus.

143/306
SQL

Aunque el siglo no se muestra por defecto, se almacena en la base de datos cuando se inserta o actualiza
el valor de fecha y está disponible para su recuperación.
El formato en el que se muestra una fecha se denomina máscara de formato. Hay varios códigos o
máscaras de formato de fecha disponibles, como se muestra en la siguiente tabla.
El lenguaje para formatear elementos de fecha utilizando máscaras de formato de fecha se discutirá en
el Capítulo 5. La máscara de formato DD-MON-RR es la máscara de visualización y entrada de datos por
defecto. La máscara de formato de fecha RR difiere de la máscara de formato YY porque se utiliza para
especificar diferentes siglos basándose en el año actual y en el especificado. El componente siglo
asignado a un valor de fecha al insertarse o actualizarse cuando los dos dígitos del año se envían,
depende de la configuración NLS_DATE_FORMAT en esa sesión. Si el NLS_DATE_FORMAT se fija en el
formato por defecto DD-MON-RR, el componente de siglo se asigna automáticamente en base a las
siguientes reglas:

• Si los dos últimos dígitos del año en curso y del año especificado se encuentran entre 0 y 49, se
devuelve el siglo actual (supuesto que el año en curso es 2014). La fecha asignada para el 24JUL-
04 es el 24-JUL-2004.
• Si los dos últimos dígitos del año en curso se encuentran entre 0 y 49 y los dígitos del año
especificado entre 50 y 99, se devuelve el siglo anterior (supuesto que el año en curso es 2014).
La fecha asignada para el 24-JUL-94 es el 24-JUL-1994.
• Si los dos últimos dígitos del año en curso y del año especificado se encuentran entre 50 y 99,
se devuelve el siglo actual por defecto. Si el año en curso es 2055, la fecha asignada para el
24JUL-94 es el 24-JUL-2094.
• Si los dos últimos dígitos del año en curso se encuentran entre 50 y 99 y el año especificado está
entre 0 y 49, se devuelve el siguiente siglo. Si el año actual es 1975, la fecha asignada para el
24-JUL-17 es el 24-JUL-2017.

144/306
SQL

Formato de la máscara Descripción de formato

DD Día del mes

MON Mes del año

YY Últimos dos dígitos del año

YYYY Cuatro dígitos del año

RR Últimos dos dígitos del año (con


asignación de siglo)

CC Siglo

HH Hora (Formato AM - PM)

HH24 Hora (Formato 24 h)

MI Minutos

SS Segundos

b. La función SYSDATE
La función SYSDATE no toma ningún parámetro y devuelve la fecha y hora actual del sistema según el
servidor de la base de datos. Por defecto, la función SYSDATE devuelve los componentes DD-MON-RR de
la fecha actual del sistema. Es importante recordar que SYSDATE no devuelve la fecha y hora
especificadas por su reloj del sistema local. Si el servidor de base de datos está ubicado en una zona
horaria diferente a la de un cliente que consulta la base de datos, la fecha y la hora que se devuelve
difieren de las del sistema operativo en la máquina del cliente. La consulta para recuperar la fecha del
servidor es la siguiente:

145/306
SQL

SELECT SYSDATE

FROM dual;

Aritmética de fechas
Las siguientes ecuaciones ilustran un principio importante con respecto a la aritmética de fechas:

Fecha1 - Fecha2 = Num1


Fecha1 - Num1 = Fecha2
Fecha1 = Fecha2 + Num1
Fecha1 = Fecha2 - Num1

Una fecha se puede restar de otra fecha. La diferencia entre dos fechas se representa como el número
de días entre ellos. Cualquier número (incluidas las fracciones) puede sumarse o restarse de una fecha.
En este contexto, el número representa un número de días. La suma o diferencia entre un número y una
fecha siempre devuelve una fecha.
Para ilustrar la función SYSDATE en lo que se refiere a aritmética de fechas, el entorno SQL Developer
se ha modificado temporalmente para mostrar la información de la hora y la fecha.

Para modificar el entorno SQL Developer para mostrar información horaria y de fecha, por
defecto, navegar a Herramientas | Preferencias | Base de datos | NLS | Formato de fecha.
Cambiar la máscara de visualización por defecto (DD-MON-RR) a (DD-MON-RR HH24:MI:SS).

La siguiente figura muestra como la función TO_DATE es utilizada para convertir el valor fecha 02-
JUN2008 con hora 12.10pm en un elemento de tipo fecha.

La primera consulta de la figura se desglosa como: Dos días antes del 2-JUN-2008 es 31-MAY-2008, que
es la fecha devuelta por la expresión 1. Sumando 0.5 días (12 horas) al 2-JUN-08 12:10, como muestra
la expresión 2, resulta en la fecha 3-JUN-08 00:10. La expresión 3 suma seis horas a la fecha dada,
devolviendo 2-JUN-08 18.10.

La columna HIRE_DATE de la tabla EMPLOYEES con DEPARTMENT_ID = 30 se resta de la fecha 2-


JUN2006 12:10. Para cada registo, se devuelve el número de días entre estas dos fechas. Nótese que
para los empleados contratados en una fecha posterior al 2-JUN-2006 se devuelve un número negativo.

146/306
SQL

c. Utilizando funciones tipo fecha


Las funciones de manipulación de datos proporcionan un medio fiable y preciso para trabajar con
elementos de tipo fecha. Estas funciones proporcionan tal facilidad y flexibilidad para la manipulación de
fechas que muchos especialistas en integración, administradores de bases de datos y otros
desarrolladores hacen uso frecuente de ellas.

La función MONTHS_BETWEEN
La función MONTHS_BETWEEN devuelve un valor numérico que representa el número de meses entre
dos valores fecha. Las fechas en formato DD-MON-RR o DD-MON-YYYYY se lanzan automáticamente
como elementos fecha cuando se introducen como parámetros de la función MONTHS_BETWEEN. La
función MONTHS_BETWEEN toma dos parámetros obligatorios:

MONTHS_BETWEEN (date1, date2)

La función calcula la diferencia, en meses, entre date1 y date2. Si date1 ocurre antes de date2, se
devuelve un número negativo. La diferencia entre los dos parámetros de fecha puede consistir en un
número entero y un componente fraccionario. El número entero representa el número de meses entre
las dos fechas. El componente fraccionario representa los días y el tiempo restante después del número
entero al calcular la diferencia entre años y meses, basándose en un mes de 31 días. Si los componentes
que se comparan comparten el mismo (o el último) día de su respectivo mes, se devuelve un número
entero.

Las siguientes consultas muestran la función MONTHS_BETWEEN:

147/306
SQL

Consulta 1: SELECT MONTHS_BETWEEN (SYSDATE+31, SYSDATE), Months_between(SYSDATE+61, SYSDAT


E),

MONTHS_BETWEEN (SYSDATE+92, SYSDATE)


FROM dual;

Consulta 2: SELECT MONTHS_BETWEEN ('29-mar-2008','28-feb-2008')


FROM dual;

Consulta 3: SELECT MONTHS_BETWEEN ('29-mar-2008','28-feb-2008') * 31 FROM dual;

Se asume que la fecha actual es 29-DEC-2007.


La primera expresión de la Consulta 1 devuelve 1 como resultado, siendo 1 el mes de diferencia entre
29-DEC-2007 y 29-JAN-2008 (31 días después). La segunda expresión es parecida, devolviendo 2 como
resultado al haber 62 días de diferencia. Dado que febrero tiene 29 días, deben añadirse 91 días al
29DIC-2007 para obtener el 29-MAR-2008, y la función MONTHS_BETWEEN (29-MAR-2008, 29-DIC-
2007) devuelve exactamente tres meses.
La Consulta 2 convierte implícitamente los literales de fecha en elementos de fecha con el formato
DDMON-YYYY. Dado que no se proporciona información de tiempo, Oracle asume que el tiempo para
ambos días es medianoche, (las 00:00:00). La función MONTHS_BETWEEN devuelve aproximadamente
1.03225806. La parte entera del componente indica que hay un mes entre estas dos fechas.
Por último, la Consulta 3 es idéntica a la 2, exceptuando que esta multiplica el resultado de la Consulta
2 por 31. Concretamente, devuelve 1.03225806 × 31 = (1 x 31) + (0.03225806 × 31) = 31 + 1 = 32.
La función MONTHS_BETWEEN utilizada en la siguiente imagen devuelve registros de la tabla
JOB_HISTORY. Se calculan los meses entre las fechas en las que un empleado inicia y termina un trabajo
en particular, y los resultados se clasifican en orden descendente.

Un error común es asumir que el tipo de datos de retorno de las funciones de un solo registro
es el mismo que al que pertenece la función. Este sólo se aplica a las funciones numéricas.
Las

148/306
SQL

funciones de carácter y fecha pueden devolver otro tipo de datos. Por ejemplo, la función de
carácter INSTR y la función MONTHS_BETWEEN, ambas devuelven un valor numérico. Es
importante familiarizarse con la aritmética de fechas, ya que es común asumir erróneamente
que la diferencia entre dos fechas es una fecha, cuando en realidad es un número.

La función ADD_MONTHS
La función ADD_MONTHS devuelve un objeto fecha calculado añadiendo un número de meses
especificado a un valor fecha dado. Las fechas en formato DD-MON-RR o DD-MON-YYYY se lanzan
automáticamente como elementos fecha cuando se introducen como parámetros de la función
ADD_MONTHS. La función ADD_MONTHS toma dos parámetros obligatorios. Su sintaxis es:

ADD_MONTHS (start date, number of months),

La función calcula la fecha prevista después de añadir el number of months especificado a start date. El
número de meses puede ser negativo, lo cual devolvería una fecha anterior a la fecha inicial dada. El
número de meses puede ser fraccional, pero se ignorará el componente fraccional y se utiliza el
componente entero.
Las tres consultas de la siguiente imagen ilustran el comportamiento de la función ADD_MONTHS.

149/306
SQL

La primera consulta devuelve 07-MAY-2009, ya que incrementa en un mes la fecha introducida. La


segunda consulta tiene dos cuestiones interesantes. El parámetro que especifica el número de meses a
añadir contiene un componente fraccional, el cual es ignorado, devolviendo el mismo resultado que
ADD_MONTHS ('31-dec-2008',2). Añadiendo dos meses al 31-DEC-2008, debería devolver 31-FEB-2009,
pero esa fecha no existe por lo que se devuelve el último día de ese mes: 28-FEB-2009. Al añadir -12
meses en la tercera consulta, se devuelve la fecha 07-APR-2008; es decir, 12 meses anterior a la fecha
inicial.

La función NEXT_DAY
La función NEXT_DAY devuelve la próxima fecha que coincide con el día de la semana especificado. Son
aceptados los valores que implícitamente se pueden tomar como elementos de fecha cuando aparecen
como parámetros de la función NEXT_DAY. La función NEXT_DAY toma dos parámetros obligatorios. Su
sintaxis es:

NEXT_DAY (start date, day of the week)


La función calcula la siguiente fecha a la del parámetro start date que coincide con el día de la semana
especificado en el parámetro day of the week . El parámetro day of the week puede ser un valor de
carácter o un valor de tipo entero. Los valores aceptados se determinan mediante el método
NLS_DATE_LANGUAGE, pero los valores por defecto son, como mínimo, los tres primeros caracteres del
nombre del día, o valores enteros, donde 1 representa el domingo, 2 representa el lunes, y así
sucesivamente. En cualquier caso, los valores de los caracteres que representan los días de la semana
se tienen que especificar. El parámetro day of the week puede tener más de tres caracteres, pero
cualquier carácter que sigue a una abreviatura válida es ignorado; por ejemplo, Sunday puede ser
referido como "sun", "sun dance", "sun hat", o "Sunday".

Las tres consultas de la siguiente imagen ilustran el comportamiento de la función NEXT_DAY.


01-JAN-2009 es un jueves. Por lo tanto, el próximo martes será cinco días después, el 06-JAN-2009, que
es lo que recupera la primera consulta de la imagen.

La segunda consulta especifica el carácter literal WEDNE, que se interpreta como WEDNESDAY. El próximo
miércoles después del 01-JAN-2009 es 07-JAN-2009.

La tercera consulta utiliza el entero para especificar el quinto día de la semana. Asumiendo el valor por
defecto donde el domingo está representado por el número 1, el quinto día es el jueves. El próximo
jueves después del 01-JAN-2009 es el 08-JAN-2009.

150/306
SQL

Problema Solución

Se desea recuperar la duración de Sí, la función SYSDATE se puede utilizar para obtener la fecha
empleo en días para cada empleado. actual del sistema.
¿Es posible realizar un cálculo de este
tipo? La siguiente consulta calcula la duración restando la columna
HIRE_DATE a la fecha devuelta por la función SYSDATE:
SELECT SYSDATE-HIRE_DATE FROM
EMPLOYEES;

Se quiere identificar la fecha en la cual Sí, se llama a la función NEXT_DAY con el parámetro start
se pagará la prima de fin de año. date fijado para el último día de diciembre y el parámetro
Las bonificaciones se pagan day of the week fijado en viernes, por lo tanto, se devolverá
normalmente el último viernes de el primer viernes de enero. Restándo siete días a esta fecha
diciembre. se obtiene el último viernes de diciembre. Conidérese la
siguiente consulta para el año 2009:
¿Se puede calcular la fecha de la
prima utilizando la función SELECT NEXT_DAY ('31-DEC-2009', 'Friday') -7
NEXT_DAY? FROM DUAL;

151/306
SQL

Los empleados que trabajan en el Sí. Utilizando la función REPLACE. Si se reemplaza cada 4
departamento de IT se han mudado a por un 6 se cambiarán dígitos que no deben ser cambiados,
nuevas oficinas y, aunque los últimos por lo que la cadena a reemplazar debe ser especificada de
cuatro dígitos de sus números de manera única.
teléfono son iguales, el conjunto de La siguiente consulta proporciona la lista:
los tres dígitos 423 se ha cambiado a
SELECT FIRST_NAME,
623. Un número de teléfono típico de
LAST_NAME,PHONE_NUMBER, REPLACE
un miembro del personal de IT es
(PHONE_NUMBER, '.423.', '.623.')
590.423.4567. Se quiere
FROM EMPLOYEES WHERE
proporcionar una lista de los nombres
DEPARTMENT_ID=60
de los empleados con sus viejos y
nuevos números de teléfono.
¿Puede hacerse esa lista?

La función LAST_DAY
La función LAST_DAY devuelve la fecha del último día del mes que se ha especificado. Los valores que
pueden ser convertidos implícitamente como fechas son aceptables cuando aparecen como parámetros
de la función LAST_DAY. La función LAST_DAY tiene un parámetro obligatorio. Su sintaxis es:

LAST_DAY (start date),


La función extrae el mes al que pertenece el parámetro start date y calcula la fecha del último día de ese
mes. Las dos consultas de la siguiente imagen ilustran el comportamiento de la función LAST_DAY. El
último día del mes de enero de 2009 es el 31-JAN-2009, que se devuelve por la llamada de la función
LAST_DAY('01-JAN-2009') en la primera consulta de la imagen.
La segunda consulta extrae los empleados con JOB_ID de IT_PROG. El número de días trabajados por
estos empleados en su primer mes de empleo se calcula restando los valores de HIRE_DATE a los de
LAST_DAY de ese mes.

152/306
SQL

La función ROUND
La función ROUND realiza una operación de redondeo de un valor basado en un formato de precisión de
fecha especificado. El valor devuelto se redondea por exceso o por defecto al formato de precisión de
fecha más cercano. La función de fecha ROUND tiene un parámetro obligatorio y otro opcional. Su sintaxis
es:

ROUND (source date,[date precision format]),


El parámetro source date representa cualquier valor que se pueda convertir implícitamente en una fecha.
El parámetro date precision format (opcional) específica el grado de redondeo. Si está ausente, el grado
de redondeo por defecto es día. Esto significa que el parámetro source date se redondea al día más
cercano. Los formatos de precisión de fecha incluyen siglo (CC), año (YYYY), trimestre (Q), mes (MM),
semana (W), día (DD), hora (HH) y minuto (MI). Muchos de estos formatos se discuten en el Capítulo 5.

Redondear por siglo equivale a sumar 1 al siglo actual. Redondear por mes, redondeará la fecha hasta el
mes siguiente, si el componente del día es mayor que 16, o, de lo contrario, redondeará hasta el comienzo
del mes en curso. Si el mes está entre 1 y 6, el redondeo por año devuelve el año en curso; en caso
contrario, devuelve la fecha al principio del año siguiente. La siguiente figura muestra cuatro elementos
en la lista SELECT, cada uno redondeando una fecha con un grado de precisión diferente.

153/306
SQL

El primer elemento redondea la fecha al día más cercano. Mientras la hora sea las 13:00, que es después
de las 12:00, la fecha se redondea a medianoche del día siguiente, 03-JUN-2009 00:00.
El segundo redondea la fecha al mismo día de la semana que el primer día del mes y devuelve 01-
JUN2009.
El tercero redondea la fecha al principio del mes siguiente, ya que el componente de día es 16 y devuelve
01-JUL-2009.

Para el cuarto elemento, se redondea a la fecha al principio del año siguiente, ya que el componente de
mes es 7 y se devuelve el 01-JAN-2010.

La función TRUNC
La función TRUNC realiza una operación de truncamiento en un valor de fecha basado en un formato de
precisión de fecha definido. La función de fecha TRUNC tiene un parámetro obligatorio y un parámetro
opcional. Su sintaxis es:

TRUNC (source date,[date precision format]),

El parámetro source date representa cualquier valor que se pueda convertir implícitamente en una fecha.
El parámetro date precision format (opcional) define el grado de truncamiento. Si está ausente, el grado
de truncamiento por defecto es día. Esto significa que cualquier componente horario de la fecha de origen
se ajusta a medianoche o 00:00:00 (00 horas 00 minutos y 00 segundos).

Truncar a nivel de mes fija la fecha hasta el primer día del mes. Truncar a nivel de año devuelve la fecha
a principios del año en curso. La siguiente figura muestra cuatro elementos en la lista SELECT, cada uno
de los cuales trunca una fecha con un grado diferente de precisión.
El primer elemento fija el componente de tiempo de 13:00 a 00:00 y devuelve el día actual.

154/306
SQL

El segundo trunca la fecha al mismo día de la semana que el primer día del mes y devolviendo el 01JUN-
2009.
El tercero trunca la fecha al inicio del mes en curso y devuelve 01-JUN-2009.
El cuarto trunca la fecha a principios del año en curso y devuelve el 01-JAN-2009.

4.3. Resumen
Varios tipos de funciones disponibles en SQL

• Las funciones aceptan cero o más parámetros de entrada, pero siempre devuelven un resultado
de un tipo de datos predeterminado.
• Las funciones de registro único se ejecutan una vez por cada registro seleccionado, mientras que
las funciones de registros múltiples se ejecutan una vez para todo el conjunto de registros
consultados.
• Las funciones de caracteres son la conversión de mayúsculas-minúsculas o la manipulación de
caracteres. Pueden utilizarse en las cláusulas SELECT, WHERE y ORDER BY en una sentencia
SELECT.

Las funciones de tipo carácter, numérico y fecha en sentencias SELECT

• La función INITCAP acepta una cadena de caracteres y devuelve cada palabra en formato título.
• La función que calcula el número de caracteres de una cadena, incluyendo espacios y caracteres
especiales, es la función LENGTH.

155/306
SQL

• La función INSTR devuelve la posición de la enésima coincidencia de una cadena de caracteres


especificada en otra cadena origen.
• La función SUBSTR extrae y devuelve una sub-cadena de una cadena origen.
• La función REPLACE sustituye cada coincidencia de un elemento de búsqueda en la cadena origen
con un término de reemplazo y devuelve la cadena de origen modificada.
• Una operación módulo devuelve el resto de una operación de división a través de la función MOD.
• La función numérica ROUND redondea los números por exceso o por defecto según el grado de
precisión especificado.
• La función SYSDATE se ejecuta tradicionalmente contra la tabla DUAL y devuelve la fecha y hora
actuales del servidor de base de datos.
• Los tipos fecha almacenan siglo, año, mes, día, hora, minutos y segundos.
• La diferencia entre dos fechas es siempre un número que representa el número de días entre
estas.
• Cualquier número, incluyendo fracciones, puede ser sumado o restado a una fecha y, en este
contexto, el número representa un número determinado de días.
• La función MONTHS_BETWEEN calcula el número de meses entre dos fechas dadas. Cualquier
componente fraccionario devuelto está basado en un mes de 31 días.
• La función LAST_DAY se utiliza para obtener el último día de un mes dado cualquier fecha válida.

5. Utilizando funciones de conversión y expresiones


condicionales

5.1. Descripción de varios tipos de funciones de conversión disponibles en SQL


En ocasiones los datos no se encuentran disponibles en el formato exacto que una función requiere, lo
cual desemboca en un error por desajuste del tipo de datos. Para evitar este tipo de errores, Oracle
implícitamente convierte tipos de datos compatibles. Esta conversión implícita se analizará antes de
introducir las funciones de conversión explicita, que se usan para llevar a cabo conversiones consistentes
de tipos de datos.

Se definirá en este capítulo el concepto de funciones de anidamiento, así como una serie de funciones
generales cuyo objetivo es simplificar la interacción con valores nulos, incluyendo las funciones NVL,
NVL2, NULLIF y COALESCE.
Se verá también la lógica condicional o la capacidad para mostrar distintos resultados según el valor de
los datos gracias a las funciones condicionales CASE y DECODE. Estas funciones proporcionan la lógica
ifthen-else(si… -> entonces …; en cualquier otro caso…) en el contexto de una consulta SQL.
Las funciones de conversión SQL son funciones de una sola línea que se han designado para alterar la
naturaleza del tipo de datos de los valores de una columna, expresión o variable. TO_CHAR, TO_NUMBER
y TO_DATE son las tres funciones de conversión más utilizadas y se presentaran posteriormente con más
detalle. La función TO_CHAR convierte información numérica y de fecha a caracteres, mientras que
TO_NUMBER y TO_DATE convierte datos de tipo carácter en números y fechas respectivamente. El
concepto de conversión implícita y explícita de datos se presenta en la siguiente sección.

5.1.1. Funciones de conversión


Oracle permite que las columnas se puedan definir con tipos de datos ANSI, DB2 y SQL/DS. Internamente
se
156/306
SQL

convierten a tipos de datos Oracle. Esta estrategia permite que aquellas aplicaciones que fueron escritas
para otros sistemas de bases de datos puedan ser migradas a Oracle fácilmente.
La definición de una tabla se obtiene utilizando el comando DESCRIBE. Cada columna está asociada a un
tipo de dato que limita la naturaleza de los datos que puede almacenar. Una columna NUMBER no va a
poder almacenar datos de tipo carácter.
Una columna de tipo DATE no puede almacenar números o caracteres aleatorios. Sin embargo, los
caracteres equivalentes a números y fechas se pueden almacenar en campos de tipo VARCHAR2.
Si una función que acepta una entrada de datos tipo carácter recibe un número, Oracle procede
automáticamente a convertirlo en su carácter equivalente. Si la función acepta números o fechas y
encuentra un carácter, hay condiciones específicas que permiten que la conversión automática tenga
lugar. Los tipos de dato DATE y NUMBER son mucho más estrictos que los tipos VARCHAR2 y CHAR.
Aunque las conversiones de datos implícitas siempre están disponibles, es importante trabajar con
conversiones explicitas utilizando funciones que se aplican a un único registro (single-row conversion
functions). De hecho, la conversión de datos tipo carácter a datos de tipo NUMBER y DATE dependen de
máscaras de formato, que se presentarán más adelante en esta sección.

Cuando datos numéricos son proporcionados como entrada para funciones que esperan datos
de tipo carácter, la conversión de tipo de datos implícita asegura que estos datos numéricos
sean tratados como caracteres. De la misma manera, cadenas de caracteres que están
compuestas por dígitos numéricos se convierten implícitamente a valores numéricos cuando
ocurre una discordancia de tipos de datos. Sin embargo, hay ocasiones en las que esta
transformación implícita no se ejecuta como se esperaría, tal y como ocurre en la siguiente
cláusula WHERE:
Considere datos de una tabla T basados en una columna de tipo carácter C, que contiene la
cadena de caracteres ‘100’. La condición WHERE C=’100’ funciona como se esperaría, pero la
condición WHERE C=100 provocará el error “ORA-1722: invalid number”.

a. Conversión implícita de tipos de datos


Aquellos valores que no comparten tipos de datos idénticos dentro de los parámetros de una función se
convierten implícitamente al formato requerido. Se conocen a los tipos de dato VARCHAR2 y CHAR como
tipo de datos “carácter”. Los campos carácter son campos flexibles que permiten almacenar
prácticamente todo tipo de datos. De esta forma los valores DATE y NUMBER pueden ser convertidos
fácilmente a sus caracteres equivalentes. Estas conversiones se conocen como conversiones número a
carácter y fecha a carácter. Considérense las siguientes consultas:

Consulta 1: SELECT length(1234567890) FROM dual;


Consulta 2: SELECT length(SYSDATE) FROM dual;

Ambas utilizan la función LENGTH, que toma una cadena de caracteres como parámetro. El número
1234567890 en la Consulta 1 se convierte implícitamente a cadena de caracteres, “1234567890”, antes
de ser evaluado por LENGTH, devolviendo el número 10. La Consulta 2 evalúa la función SYSDATE
inicialmente devuelve 07-APR-38. Esta fecha se convierte implícitamente a la cadena de caracteres:
“07APR-38” y posteriormente la función LENGTH devuelve el número 9.

157/306
SQL

No es común para los datos de tipo carácter que sean convertidos directamente a tipos numéricos dado
que la única condición bajo la que esto ocurre es que los datos de tipo carácter representen un formato
válido de número. La cadena de caracteres “11” se transformará implícitamente a número, pero
“11.123.456” no, tal y como demuestran las siguientes consultas:

Consulta 3: SELECT mod('11',2) FROM dual;


Consulta 4: SELECT mod('11.123',2) FROM dual;
Consulta 5: SELECT mod('11.123.456',2) FROM dual;
Consulta 6: SELECT mod('$11',2) FROM dual;

Las consultas 3 y 4 convierten las cadenas “11” y “11.123” a los números 11 y 11.123 respectivamente,
antes de que la función MOD los evalúe y recupere los resultados 1 y 1.123. La consulta 5 devuelve el
error “ORA-1722: invalid number” cuando Oracle intenta realizar la transformación implícita carácter a
número falla, puesto que la cadena de caracteres “11.123.456” no es un número válido. La consulta 6
falla también con el error “ORA-1722: invalid number” dado que el símbolo del dólar no se puede
transformar implícitamente a número. La conversión implícita carácter a fecha es posible cuando la
cadena de caracteres presenta el siguiente patrón:

[D|DD] separador1 [MON|MONTH] separador2 [R|RR|YYYY]

D y DD representan días del mes de uno o dos dígitos. MON es la abreviación de los meses en tres
dígitos, mientras que MONTH representa el nombre completo. R y RR representan años de uno o dos
dígitos. YYYY representa el año escrito con cuatro dígitos. Los elementos separador1 y separador2
equivalen a casi cualquier signo de puntuación, espacios y guiones. En la siguiente tabla se muestran
varios ejemplos de conversiones implícitas carácter a fecha, formando una lista de varias llamadas de
funciones y de resultados que SQL Developer devuelve.

Función Formato Resultado

add_months(’24-JAN-09’,1) DD-MON-RR 24/FEB/09

add_months(‘1\january/8’,1) D\MONTH/R 01/FEB/08

months_between(‘13*jan*8’,’13/feb/2008’) DD*MON*R, -1
DD/MON/YYYY

add_months(‘01$jan/08’,1) DD$MON/RR 01/FEB/08

add_months(’13!jana08’,1) JANA no es un mes ORA-1841: (full) year


válido must be between -4713
and +9999 and not be 0

add_months(’24-JAN-09 18:45’,1) DD-MON-RR ORA-1830: date format


HH24:MI picture ends before
converting entire input
string

158/306
SQL

b. Conversión explicita de tipos de datos


Oracle ofrece muchas funciones para convertir ítems de un tipo de dato a otro, conocidas como funciones
de conversión de datos explícitas, que devuelven valores que seguro serán del tipo requerido y ofrece
un método fiable y seguro de conversión.
Los objetos NUMBER y DATE se pueden convertir explícitamente a caracteres mediante el método
TO_CHAR. Una cadena de caracteres se puede transformar a NUMBER usando TO_NUMBER y a DATE
mediante TO_DATE. Las máscaras de formato de Oracle permiten un gran control sobre las conversiones
carácter a número y carácter a fecha.

Las funciones de conversión explicita son esenciales para manipular información de fechas,
números y caracteres.

5.2. Usando las funciones de conversión TO_CHAR, TO_NUMBER y TO_DATE - 1


Esta sección contiene una descripción sistemática de las funciones TO_NUMBER, TO_DATE y TO_CHAR
incluyendo también algunos ejemplos. El análisis de la función TO_CHAR se divide en la conversión de
dos tipos de dato (DATE y NUMBER) a un elemento tipo carácter. Esta división está justificada por la
disponibilidad de diferentes máscaras de formato para controlar dicha conversión a tipo carácter. Estas
funciones de conversión existen junto con muchas otras, pero tienden a ser las más utilizadas. Esta
sección se centra en los aspectos prácticos de la utilización de las funciones de conversión.

5.2.1. Utilizando funciones de conversión


Muchas situaciones exigen del uso de funciones de conversión. Estas situaciones pueden variar desde
formatear campos DATE en un informe, hasta asegurarse de que los dígitos numéricos extraídos de los
campos de tipo carácter se conviertan correctamente en números antes de aplicarlos en una expresión
aritmética.

La tabla siguiente muestra la sintaxis de las funciones de conversión explícita que se aplican a un único
registro (single-row explicit data type conversion functions).

TO_NUMBER(char1, [format mask], TO_CHAR(num1, [format mask],


[nls_parameters]) = char1
[nls_parameters]) = num1

TO_DATE(char1, [format mask], TO_CHAR(date1, [format mask],


[nls_parameters]) = date1 [nls_parameters]) = char1

Los parámetros opcionales de soporte de idioma nacional (nls_parameters) son útiles para especificar el
idioma y el formato en el que se devuelven los nombres de los elementos de fecha y numéricos. Estos
parámetros suelen estar ausentes, y se utilizan los valores por defecto para elementos como los nombres

159/306
SQL

de día o mes y las abreviaturas. En la figura siguiente se pueden ver algunos parámetros
NLS_SESSION_PARAMETERS que contienen parámetros NLS para la sesión.

El valor por defecto NLS_CURRENCY es el símbolo del dólar, pero se puede modificar a nivel de sesión de
usuario. Por ejemplo, para cambiar la moneda a la cadena USD de tres caracteres, se puede emitir el
siguiente comando:
ALTER SESSION SET nls_currency='USD';

a. Convirtiendo números a variables tipo carácter usando la función TO_CHAR


La función TO_CHAR devuelve un elemento del tipo VARCHAR2. Cuando se aplica a elementos del tipo
NUMBER, hay varias opciones de formato disponibles. La sintaxis es la siguiente:

TO_CHAR(number1, [format], [nls_parameter]);

El parámetro number1 es obligatorio y debe ser un valor que sea o pueda ser convertido implícitamente
en un número. El parámetro opcional format se puede utilizar para especificar información respecto al
formato numérico, como la anchura, el símbolo de moneda, la posición de un punto decimal y los
separadores de grupo (o miles). Se deben incluir entre comillas simples.

Considérense las siguientes dos consultas:

160/306
SQL

Consulta 1: SELECT to_char(00001)||' is a special number' FROM dual;


Consulta 2: SELECT to_char(00001,'0999999')||' is a special number' FROM dual;

La Consulta 1 evalúa el número 00001, elimina los ceros a la izquierda, convierte el número 1 en el
carácter "1" y devuelve la cadena "1 is a special number". La Consulta 2 aplica la máscara de formato
numérico "0999999" al número 00001, convirtiéndolo en la cadena de caracteres "0000001". Después
de la concatenación con los literales de los caracteres, la cadena devuelta es "0000001 is a special
number". El cero y los seis nueves en la máscara de formato indican a la función TO_CHAR que deben
visualizarse ceros a la izquierda y que el ancho de visualización debe ajustarse a siete caracteres. Por lo
tanto, la cadena devuelta por la función TO_CHAR contiene siete caracteres.
Hay otras opciones de formato para los números que son convertidos en caracteres, algunos de los cuales
se enumeran en la tabla siguiente:

Formato de Descripción Formato Número Resultado en


los de elemento formato
elementos carácter

9 Ancho numérico 9999 12 12

0 Muestra de ceros a 09999 0012 00012


la izquierda

. Posición del punto 09999.999 030.40 00030.400


decimal

D Posición del 09999D999 030.40 00030.40


separador decimal
(el período es por
defecto)

, Posición de 09999,999 03040 00003,040


la coma

G Posición de 09999G999 03040 00003,040


separación de
grupo

161/306
SQL

En la imagen siguiente se puede observar cómo la consulta recupera las columnas JOB_TITLE y
MAX_SALARY de la tabla JOBS para los registros con la palabra “PRESIDENT” en el JOB_TITLE
independientemente de las mayúsculas y minúsculas de los caracteres utilizados para almacenar el título
puesto. MAX_ SALARY también ha sido formateado para tener un símbolo de moneda de dólar, una coma
como separador de miles y un punto como separador de decimales. Cuando una máscara de formato es
más pequeña que el número que se está convirtiendo, como se ilustra en el cuarto elemento de la lista
SELECT, se devuelve una cadena de símbolos (#). Cuando una máscara de formato contiene menos
componentes fraccionarios que el número, primero se redondea al número de decimales en la máscara
de formato antes de ser convertida.

162/306
SQL

La conversión de números a caracteres es una forma fiable de garantizar que las funciones y
la sintaxis SQL general, que espera la introducción de caracteres, no devuelvan errores cuando
se encuentran números. La conversión de números en cadenas de caracteres es común cuando
los datos numéricos deben formatearse con fines informativos. Las máscaras de formato que
soportan moneda, separadores de miles y separadores de punto decimal se utilizan con
frecuencia al presentar datos financieros.

b. Convirtiendo fechas a variables tipo carácter usando la función TO_CHAR


Se pueden aprovechar una variedad de modelos de formato para convertir elementos DATE en casi
cualquier representación de caracteres de una fecha utilizando TO_CHAR. Su sintaxis es la siguiente:

163/306
SQL

TO_CHAR(date1, [format], [nls_parameter]);

Solo el parámetro date1 es obligatorio y debe adoptar la forma de un valor que pueda convertirse
implícitamente en una fecha. El parámetro format es opcional, distingue entre mayúsculas y minúsculas
y debe incluirse entre comillas simples. La máscara de formato especifica qué elementos de fecha se
extraen y si el elemento debe describirse con un nombre largo o abreviado. Los nombres de los días y
meses se rellenan automáticamente con espacios. Estos pueden ser eliminados usando un modificador
de la máscara de formato llamado fill mode operator (fm). Al prefijar el modelo de formato con las letras
fm, se instruye a Oracle para que recorte todos los espacios de los nombres de días y meses. Hay muchas
opciones de formato para las fechas que son convertidas a caracteres, algunas de las cuales se enumeran
en la siguiente tabla.

Formato del Descripción Resultado


elemento

Y Último digito del año 5

YY Últimos dos dígitos 75


del año

YYY Últimos tres dígitos 975


del año

YYYY Año con cuatro dígitos 1975

RR Año con 2 dígitos 75

YEAR Año en inglés NINETEEN SEVENTY-


distinguiendo FIVE
mayúsculas de
minúsculas

MM Mes en dos dígitos 06

MON Abreviatura del mes JUN


en tres letras

MONTH Mes en inglés JUNE


distinguiendo
mayúsculas y
minúsculas

164/306
SQL

D Día de la semana 2

DD Día del mes en dos 02


dígitos

DDD Día respecto al año 153

DY Abreviatura de tres MON


letras del día

DAY Día en inglés MONDAY


distinguiendo
mayúsculas y
minúsculas

Considérense las siguientes consultas:

Consulta 1: SELECT to_char(sysdate)||' is today''s date' FROM dual;


Consulta 2: SELECT to_char(sysdate,'Month')||'is a special time' FROM dual;
Consulta 3: SELECT to_char(sysdate,'fmMonth')||'is a special time' FROM dual;

Si la fecha actual del sistema es 03/JAN/09 y el formato de visualización por defecto es DD/MON/RR, la
Consulta 1 devuelve la cadena "03/JAN/09 is today’s date".

Hay dos componentes a resaltar de la Consulta 2. En primer lugar, sólo se convierte a tipo carácter el
componente ‘Month’ de la fecha actual del sistema. En segundo lugar, como la máscara de formato
distingue entre mayúsculas y minúsculas y el componente 'Month' aparece como título, la cadena
devuelta es " January is a special time". No hay necesidad de añadir un espacio delante de la cadena "is
a special time", ya que la función TO_CHAR rellena automáticamente el nombre del mes con espacios (si
es necesario) para obtener una cadena de nueve caracteres de longitud. Dado que january tiene ocho
caracteres, se añade un espacio, pero si el mes fuera septiembre, no se añadirían espacios
automáticamente. Si la máscara de formato en la Consulta 2 fuera 'MONTH', la cadena devuelta sería
"January is a special time".
El modificador fm se aplica a la Consulta 3, y la cadena resultante es " Januaryis a special time". Nótese
que no hay espacio entre January y la cadena de texto “is a special time” como resultado del modificador
‘fm’. En la tabla anterior, se asume que los elementos están operando en la fecha 02-JUN-1975, siendo
el 2009 el año en curso.
Los elementos con formato de fecha correspondientes a semanas, trimestres, siglos y otras máscaras de
formato menos comunes se enumeran en la tabla que se mostrará a continuación. La columna de
resultados se obtiene de la evaluación de la función TO_CHAR utilizando la fecha 24-SEP-1000 BC, con
la misma máscara de formato que la de la primera columna de la tabla. El componente de tiempo de los
datos de tipo fecha y hora se extraen utilizando los modelos de formato de la tabla siguiente. Son el
resultado de evaluar la función TO_CHAR usando la fecha incluyendo su componente de tiempo 27-
JUN2010 21:35:13, con la máscara de formato de la primera columna de la tabla.

165/306
SQL

Formato del elemento Descripción Resultado

W Semana del mes 4

WW Semana del año 39

Q Trimestre del año 3

CC Centenario 10

S precedido CC, YYYY, o Si la fecha es BC, el signo -10,-1000 o –ONE


YEAR menos es prefijado al THOUSAND
resultado

IYYY,IYY,IY,I Fechas ISO para cuatro, 1000,000,00,0


tres dos y un dígito,
respectivamente

BC,AD,B.C. y A.D. BC o AD y periodos BC


espaciados B.C. o A.D.

J Calendario Juliano – días 1356075


desde 31 de Diciembre
4713 BC

IW Estándar semanal ISO 39


(1-53)

RM Mes en números romanos IX

AP,PM,A.M. y P.M. Indicadores meridianos PM

HH,HH12 y HH24 Hora del día, 1-12 horas y 09,09,21


0-23 horas

166/306
SQL

MI Minuto (0-59) 35

SS Segundo (0-59) 13

SSSSS Segundos pasada la 77713


medianoche(0-86399)

En la tabla que se encuentra a continuación se resumen otros elementos que también pueden ser
utilizados en los modelos de fecha y hora. Los signos de puntuación se utilizan para separar los elementos
de formato. Existen tres tipos de sufijos para formatear los componentes de los elementos de fecha y
hora. Además, las cadenas de caracteres pueden incluirse en un modelo de formato de fecha si están
declarados entre comillas. Los resultados de la siguiente tabla se obtienen al aplicar la función TO_CHAR
utilizando la fecha 12/SEP/08 14:31 con las máscaras de formato de la columna “Descripción y máscara
de formato”.

Formato del Descripción y máscara de formato Resultado


elemento

-/ . , ¿ # ! Marcas de puntuación: ‘[Link]’ 09.08

“any caracter Caracteres de texto fijos: ‘ Week 2 of September


literal” “Week” W ”of ” Month’

TH Texto ordinal o posicional: ‘DDth “of” Month’ 12TH of September

SP Número deletreado: ‘MmSP Month Yyyysp’ Nine September Two


Thousand Eight

THSP o STPH Número ordinal o posicional escrito con Fourteenth


palabras: ‘hh24SpTh’

La tabla JOB_HISTORY realiza un seguimiento de las funciones llevadas a cabo por los empleados en la
empresa. La consulta realizada en la siguiente imagen recupera una sentencia descriptiva sobre la fecha
de renuncia de cada empleado basada en sus campos END_DATE, EMPLOYEE_ID y JOB_ID. Una
expresión de carácter se concatena a una llamada a la función TO_CHAR con un modelo de formato de
'fmDay "the "ddth "of" Month YYYYY'. El modificador fm se utiliza para recortar los espacios en blanco
que siguen los nombres de los días y meses más cortos. Los dos caracteres literales entre comillas dobles
son las palabras "the" y "of". El modelo en formato th se aplica al elemento de fecha ‘dd’ para crear un

167/306
SQL

día ordinal como el día 17º o 31º. El formato del modelo ‘Month’ muestra el nombre completo del mes
de la columna END_DATE como título. Por último, la máscara de formato YYYY recupera el componente
de cuatro dígitos del año.

c. Convirtiendo caracteres a fechas utilizando la función TO_DATE


La función TO_DATE devuelve un ítem de tipo DATE. Las cadenas de caracteres convertidas en fechas
pueden contener todos los elementos de tiempo para fechas o subconjuntos de ellos. Cuando dichas
cadenas con un solo subconjunto se convierten, Oracle proporciona valores por defecto para construir el
valor completo de la fecha. Los subconjuntos se asocian a los elementos de fecha mediante diferentes
máscaras de formato. La sintaxis es como sigue:

TO_DATE(string1, [format], [nls_parameter]);

Solamente el parámetro string1 es obligatorio y si no se proporciona ninguna máscara de formato, deberá


tener un formato que se pueda convertir implícitamente. El parámetro format, que es opcional, se utiliza
casi siempre y se especifica entre comillas. Las máscaras de formato se encuentran especificadas en las
tablas anteriores.

168/306
SQL

La función TO_DATE tiene un modificador fx, que es similar al modificador fm, usado en la función
TO_CHAR, y especifica una correspondencia exacta entre string1 y la máscara de formato. Cuando fx se
especifica, los caracteres que no concuerdan exactamente devuelven un error. Considérense las
siguientes 5 consultas:

Consulta 1: SELECT to_date('25-DEC-2010') FROM dual;


Consulta 2: SELECT to_date('25-DEC') FROM dual;
Consulta 3: SELECT to_date('25-DEC','DD-MON') FROM dual;
Consulta 4: SELECT to_date('25-DEC-2010 18:03:45', 'DD-MON-YYYY HH24:MI:SS') FROM dual;
Consulta 5: SELECT to_date('25-DEC-10', 'fxDD-MON-YYYY') FROM dual;

La Consulta 1 evalúa la cadena 25-DEC-2010 y tiene suficiente información para convertirla en un ítem
DATE, utilizando la máscara DD-MON-YY, implícitamente. El guion utilizado para separar podría ser
sustituido por cualquier otro símbolo de puntuación. Dado que no se proporcionan elementos de tiempo,
se configurará para la media noche o 00:00:00.
La Consulta 2 no puede convertir de manera implícita la cadena dado que no hay suficiente información
y se devuelve un error de tipo: “ORA-01840: input value is not long enough for date format”.
En la Consulta 3 sí se proporciona una máscara de tipo DD-MON, por lo que se consigue que concuerden
los ítems para la transformación. El año será introducido implícitamente por la función SYSDATE y el
horario se configura a media noche.

La Consulta 4 procede a hacer una conversión completa con todos los elementos de fecha y hora y Oracle
no necesita dar ningún valor por defecto.

La Consulta 5 utiliza en modificador fx en su máscara de formato. Dado que el componente año es 10 y


su máscara correspondiente es YYYY, el modificador lanza un error del tipo: “ORA-01862: the numeric
value does not match the length of the format item”.

169/306
SQL

La función TO_DATE se usa en la cláusula WHERE de la anterior imagen para limitar los registros
devueltos para aquellos empleados que se contrataron después del 12 de enero de 2008. La máscara de
formato concuerda 01 con MM, 12 con DD y 2008 con YYYY.

d. Convirtiendo caracteres a números utilizando la función TO_NUMBER


La función TO_NUMBER devuelve un ítem de tipo NUMBER. Las cadenas de caracteres convertidas a
números deben estar formateadas correctamente para que cualquier elemento no numérico se pueda
traducir o eliminar con la máscara apropiada. La sintaxis es:

TO_NUMBER(string1, [format], [nls_parameter]),

Solamente el string1 es obligatorio. Si no hay máscara de formato (format) se debe introducir un valor
que se pueda convertir implícitamente en números. El parámetro opcional format se especifica entre
comillas. Las máscaras de formato son idénticas a las que se listaron en las tablas para TO_CHAR.
Considérense las siguientes consultas:

Consulta 1: SELECT to_number('$1,000.55') FROM dual;


Consulta 2: SELECT to_number('$1,000.55','$999,999.99') FROM dual;

La Consulta 1 no puede proceder a hacer una conversión implícita debido a los símbolos del dólar, la
coma y el punto. Se recupera un error de tipo: “ORA-1722: invalid number”.
La Consulta 2 combina el símbolo del dólar, la coma y el punto de la cadena de caracteres con la máscara
de formato, y aunque la amplitud numérica es mayor que la de la cadena, se devuelve el número
1000.55.
Como se muestra en la siguiente imagen, se utiliza la función SUBSTR para extraer los últimos 8
caracteres de la columna PHONE_NUMBER. Posteriormente, se utiliza la función TO_NUMBER para
convertir dichos caracteres en un número (incluyendo el punto decimal), y, finalmente, se multiplica este
número por 10000 para aquellos empleados con valor de DEPARTMENT_ID = 30.

170/306
SQL

Problema Solución

Se necesita extraer el día y el mes de Sí. Con la función TO_CHAR se puede colocar un ítem de
una columna fecha y compararla con los tipo DATE mediante una máscara: “DD-MON” de manera
componentes correspondientes de la que se aíslen los componentes día y mes. Este valor se
fecha de sistema actual. ¿Esta compara con la actual fecha del sistema mediante la
comparación se puede hacer? siguiente expresión: TO_CHAR(SYSDATE, 'DD-MON')

Se pide un informe sobre las ganancias Sí. La cantidad numérica se debe convertir a una cadena
y pérdidas que muestre los resultados de caracteres utilizando la función TO_CHAR con una
de la siguiente manera: máscara de formato que la englobe entre paréntesis si es
Si la cantidad es negativa, debe estar negativa y la preceda por un signo del dólar. La siguiente
colocada entre paréntesis. La cantidad función ofrece dicho formato: TO_CHAR(AMOUNT,
debe tener el símbolo del dólar delante. '$999999PR')
¿Se pueden obtener estos resultados en
dicho formato?

Se requiere introducir los datos de los Sí. Considérese la función de conversión


empleados en una tabla JOB_HISTORY TO_DATE('2000','YYYY') para un empleado que empieza en
desde una base de datos de papel, pero el año 2000. Si la fecha se extrae como sigue, la cadena
la información de la fecha de inicio solo

se encuentra disponible como el año en de caracteres 01/01/2000 se devuelve como resultado:


el que el empleado empezó. ¿Se puede TO_CHAR(TO_DATE('2000','YYYY'),'MM/DD/YYYY')
convertir a el 1 de enero de ese año?

5.3. Aplicar expresiones condicionales en una sentencia SELECT


Las funciones anidadas se introdujeron en capítulos anteriores, pero en esta sección se van a presentar
y desarrollar formalmente. Se introducirán dos nuevas categorías de funciones: las funciones generales,
que proporcionan la posibilidad de trabajar con valores NULL y las funciones condicionales que permiten
la lógica condicional.

5.3.1. Funciones anidadas (Opcional)


Las funciones anidadas utilizan la salida de una función como entrada de otra, por lo que estas funciones
devuelven siempre un único resultado. De esta manera, se puede considerar, de forma fiable, una
llamada a una función del mismo modo que si se tratase de un valor (STRING, CHAR, NUMBER, DATE,…)
al proporcionar parámetros de entrada a dicha función. Las funciones que se aplican a un único registro
(single-row conversion functions) pueden ser anidadas a cualquier nivel de profundidad.

171/306
SQL

Función1(parameter 1, parameter2,…) = result1

Al sustituir parámetros de la función anterior por funciones, se puede obtener una expresión como la
siguiente:

F1(param1.1, F2(param2.1, param2.2, F3(param3.1)), param1.3)

Las funciones anidadas son las primeras en ser evaluadas, ya que sus salidas son utilizadas como
parámetros de entradas en otras funciones. Además, se evalúan desde el nivel más profundo al más
externo. La expresión previa es analizada de la siguiente forma:

1. Se evalúa F3(param3.1). Su salida será el tercer parámetro de la función F2 (param2.3).


2. Se evalúa F2(param2.1, param2.2, param2.3). Su salida será el segundo parámetro de la función
F1 (param1.2).
3. Se evalúa F1(param1.1, param1.2, param1.3). Su resultado se devuelve al programa de
llamada.

Considérese el siguiente ejemplo:

SELECT length(to_char(to_date('28/10/09','DD/MM/RR'),'fmMonth'))FROM dual;

Hay tres funciones dentro de la cláusula SELECT, que de nivel más bajo a nivel más alto son: TO_DATE,
TO_CHAR y LENGTH. El proceso que se sigue para evaluar esta función es:

1. Dado que la función más interna es TO_DATE('28/10/09','DD/MM/RR'), se transforma la cadena


de caracteres 28/10/09 en un valor DATE: 28-OCT-2009. La máscara de formato RR se usa para
la parte concerniente al año. Por tanto, el siglo es el actual (el siglo XXI) dado que el componente
año está entre 0 y 49.
2. La segunda función de dentro a fuera es TO_CHAR('28-OCT-2009','fmMonth'). Convertirá la fecha
en función de la máscara de formato Month y devolverá la cadena de caracteres “October”.
El modificador fm corta los posibles espacios del nombre del mes. 3. Finalmente, la función
LENGTH se evalúa para la cadena “October”, recuperando el valor 7.

Problema Solución

En las funciones anidadas, ¿se evalúan primero las No. Las funciones anidadas se resuelven
funciones más externas? evaluando de la función más interna hasta
la más externa.

¿Deben todas las funciones dentro de una expresión No. Los tipos de datos pueden diferir de
anidada devolver el mismo tipo de datos? una a la otra. La única característica
imprescindible es que el tipo de datos de
salida de un nivel inferior sea igual al de
los datos de entrada del nivel justo por
encima.

172/306
SQL

¿Existe alguna manera de mostrar la información sobre Si, una solución elegante y simple podría
SALARY de la tabla EMPLOYEES en la forma $13,000 sin ser utilizar la función TO_CHAR con la
usar la siguiente expresión? máscara de formato “$99G999”:
SELECT '$'|| SELECT TO_CHAR(SALARY,
SUBSTR(SALARY,1,MOD(LENGTH(SALARY),3))||', '$99G999') FROM EMPLOYEES;
'|| SUBSTR(SALARY,
MOD(LENGTH(SALARY),3)+1)

5.3.2. Funciones generales


Las funciones generales simplifican el trabajo con columnas que contienen valores potencialmente nulos.
Estas funciones aceptan parámetros de entrada de todo tipo de datos. Los servicios que ofrecen son
primordialmente relevantes para los valores nulos.
Las funciones examinadas en estas secciones incluyen la función NVL, que proporciona un valor
alternativo a utilizar si se encuentra un valor nulo. La función NVL2 realiza una evaluación condicional de
su primer parámetro y devuelve un valor si se encuentra un valor nulo y una alternativa si el parámetro
no es nulo. La función NULLIF compara dos términos y devuelve un resultado nulo si son iguales, o el
primer término en caso contrario. La función COALESCE acepta un número ilimitado de parámetros y
devuelve el primer parámetro no nulo, o devuelve NULL en caso contrario.

a. La función NVL
La función NVL evalúa si una columna o expresión de cualquier tipo de dato es NULL o no. Si es NULL,
se devuelve un valor alternativo; en caso contrario, se devuelve el término inicial.
La función NVL tiene dos parámetros obligatorios. Su sintaxis es:

NVL (original, ifnull)

Donde original representa el término que se está probando e ifnull representa el resultado devuelto si el
término original evaluado es NULL. Los tipos de datos de los parámetros original e ifnull deben ser
siempre compatibles. Deberán ser del mismo tipo, o bien ser posible convertir ifnull implícitamente al
tipo del parámetro original. La función NVL devuelve un valor con el mismo tipo de dato que el parámetro
original. Considérese la siguiente consulta:

Consulta 1: SELECT nvl(1234) FROM dual;


Consulta 2: SELECT nvl(null,1234) FROM dual;
Consulta 3: SELECT nvl(substr('abc',4),'No substring exists') FROM dual;

Dado que la función NVL tiene dos parámetros obligatorios, la Consulta 1 devolverá el error “ORA-00909:
invalid number of arguments”.
La Consulta 2 devolverá 1234 después de que la palabra clave NULL sea probada y se encuentre que es
el valor nulo.
La Consulta 3 devolverá una función anidada SUBSTR que intenta extraer el cuarto carácter de una
cadena de 3 caracteres. La función interna devuelve NULL, dejando NVL(null,'No substring exists') que,
al ser ejecutada, que devuelve la cadena ‘No substring exists’.

173/306
SQL

La imagen anterior muestra dos consultas casi idénticas. Ambas consultas seleccionan registros en los
que LAST_NAME empieza por la letra ‘E’. Las columnas LAST_NAME, SALARY y COMMISSION_PCT son
también seleccionadas. La diferencia está en la expresión calculada llamada MONTHLY_COMMISSION.
Debido a la función NVL en la primera consulta se devuelven resultados numéricos. La segunda consulta
devuelve algunos valores nulos y un elemento numérico, aunque se añadan 1000 a cada registro.

Es tentador construir una expresión compleja compuesta por muchas llamadas a funciones
anidadas, pero este enfoque evoluciona con la práctica y la experiencia. Es recomendable
analizar una solución para una consulta y separar los componentes en llamadas a funciones.
La tabla DUAL es útil para pruebas lógicas ad hoc y depuración de llamadas a funciones
separadas. No se debe tener miedo de ejecutar consultas tantas veces como se desee para
perfeccionar los componentes antes de enlazarlos en componentes cada vez más grandes. Se
debe probar y depurar éstos hasta que la expresión final quede establecida.

174/306
SQL

b. La función NVL2
La función NVL2 proporciona una mejora a la función NVL, ambas tienen un propósito muy similar. Se
evalúa si la columna o expresión de cualquier tipo de datos es NULL. Si el primer término es no nulo,
entonces se devuelve el segundo parámetro; en caso contrario, se devuelve el tercer parámetro.
Recordar que la función NVL es diferente ya que devuelve el término original si no es nulo.
La función NVL2 tiene tres parámetros obligatorios. Su sintaxis es:

NVL2(original, ifnotnull, ifnull)

Donde original representa el término que se empieza a probar. Ifnotnull es el parámetro devuelto si
original es no nulo, e ifnull es el parámetro que se devuelve si original es nulo. El tipo de datos de los
parámetros ifnotnull y de ifnull deben ser compatibles y no pueden ser de tipo LONG. Ambos deben ser
del mismo tipo o a ser posible, convertir el parámetro ifnull al mismo tipo que el parámetro ifnotnull. El
tipo de dato devuelto por la función NVL2 es el mismo que el del parámetro ifnotnull. Considérese las
siguientes consultas:

Consulta 1: SELECT nvl2(1234,1,'a string') FROM dual;


Consulta 2: SELECT nvl2(null,1234,5678) FROM dual;
Consulta 3: SELECT nvl2(substr('abc',2),'Not bc','No substring') FROM dual;

El término ifnotnull en la Consulta 1 es un número mientras que el parámetro ifnull es ‘a string’. Ya que
los tipos de datos son incompatibles entre ellos, se devolverá el error “ORA-01722: invalid number”.

La Consulta 2 devolverá el parámetro ifnull, que es 5678.


La Consulta 3 extrae los caracteres “bc” utilizando la función SUBSTR y se evalúa la función NVL2(‘bc’,
‘Not bc’, ‘No substring’). Se devolverá el parámetro ifnotnull, es decir, la cadena ‘Not bc’.

175/306
SQL

La imagen anterior muestra cómo es utilizada la función NVL2 para proporcionar texto descriptivo que
clasifica a los empleados con valores de LAST_NAME que empiezan por “F” en “Commision Earner” o no,
basados en la columna COMMISSION_PCT (que puede ser nula).

c. La función NULLIF
La función NULLIF comprueba dos términos por igualdad. Si estos son iguales, la función devolverá un
NULL; en caso contrario, devolverá el primero de los dos términos probados.

La función NULLIF tiene dos parámetros obligatorios de cualquier tipo de dato. La sintaxis es:

NULLIF(ifunequal, comparison_term)
Donde los parámetros ifunequal y comparison_term son comparados. Si estos son idénticos, entonces
se devuelve NULL. En caso de ser diferentes, el parámetro ifunequal será devuelto. Considérense las
siguientes consultas:

Consulta 1: SELECT nullif(1234,1234) FROM dual;


Consulta 2: SELECT nullif(1234,1233+1) FROM dual;
Consulta 3: SELECT nullif('24-JUL-2009','24-JUL-09') FROM dual;

La Consulta 1 devolverá un valor nulo, ya que los parámetros son idénticos.

176/306
SQL

La ecuación aritmética de la Consulta 2 se evalúa implícitamente y la función NULLIF encuentra que 1234
es equivalente a 1233+1 (1234), por tanto, también devolverá NULL.
Los caracteres de las cadenas de caracteres de la Consulta 3 no son convertidos implícitamente a tipo
DATE y son comparados por la función NULLIF como dos cadenas de caracteres. Ya que ambas cadenas
tienen diferente longitud, se devolverá el parámetro ifunequal 24-JUL-2009.

La anterior imagen muestra cómo NULLIF es anidada como un parámetro a la función NVL2. La función
NULLIF en sí misma contiene las funciones de caracteres SUBSTR y UPPER incorporadas como una
expresión, utilizándose como el parámetro ifunequal. La columna EMAIL se compara con una expresión
formada por la concatenación del primer carácter de FIRST_NAME con el equivalente en mayúsculas de
la columna LAST_NAME para empleados con nombres con cuatro caracteres. Cuando estos términos son
iguales, NULLIF devuelve un valor NULL; en caso contrario, devuelve el parámetro evaluado ifunequal.
Este se utiliza como un parámetro para la función NVL2. La función NVL2 proporciona texto descriptivo
clasificando registros como coincidentes con el patrón o no.

177/306
SQL

d. La función COALESCE
La función COALESCE devuelve el primer valor no nulo de una lista de parámetros. Si todos los
parámetros son nulos, entonces devuelve NULL. La función COALESCE tiene dos parámetros obligatorios
y un número de parámetros opcionales. La sintaxis es:

COALESCE(expr1, expr2,…,exprn)

Donde se devuelve la expr1 si es no nula; en caso contrario, se devuelve el parámetro expr2, y así
sucesivamente. COALESCE es una forma general de la función NVL, como se muestra en las siguientes
ecuaciones:

COALESCE(expr1, expr2)=NVL(expr1,expr2)
COALESCE(expr1,expr2,expr3)=NVL(expr1,NVL(expr2,expr3))

El tipo de dato que devuelve la función COALESCE es un valor no nulo del mismo tipo que el primer
parámetro no nulo que se encuentre. Para evitar el error “ORA-00932: inconsistent data types”, todos
los parámetros no nulos deben tener tipos de datos compatibles con el primer parámetro no nulo.
Considérense las siguientes consultas:

Consulta 1: SELECT coalesce(null, null, null, 'a string') FROM dual;


Consulta 2: SELECT coalesce(null, null, null) FROM dual;
Consulta 3: SELECT coalesce(substr('abc',4),'Not bc','No substring') FROM dual;

La Consulta 1 devuelve el cuarto parámetro: una cadena de texto, ya que es el primer parámetro no
nulo encontrado.
La Consulta 2 devuelve un valor nulo porque todos los parámetros pasados son NULL.
La Consulta 3 evalúa el primer parámetro, que es una función anidada SUBSTR, y lo encuentra nulo. El
segundo parámetro es no nulo, así que la cadena ‘Not bc’ es devuelta.

178/306
SQL

La información de STATE_PROVINCE, POSTAL_CODE y CITY fue recuperada de la tabla LOCATIONS por


los registros con valores de COUNTRY_ID de UK, IT o JP. Como muestra la imagen anterior, la función
COALESCE devuelve el valor de STATE_PROVINCE para un registro si éste es no nulo. Si éste es nulo, se
devuelve el valor de POSTAL_CODE. Si éste también es nulo, se devuelve el campo CITY; en caso
contrario, se devuelve NULL.

Los parámetros de la función general NVL2 pueden llevar a confusión si se está ya


familiarizado con NVL. NVL(original, ifnull) devuelve original si éste es no nulo, o ifnull, en
caso contrario. La función NVL2(original, ifnotnull, ifnull) devuelve ifnotnull si original es no
nulo, o ifnull, en caso contrario.
La confusión puede aparecer debido a que el segundo parámetro de la función NVL es ifnull,
mientras que el segundo parámetro de la función NVL2 es ifnotnull. Hay que estar atento al
significado de las posiciones de los parámetros en las funciones.

5.3.3. Funciones condicionales


La lógica condicional, también conocida como lógica ‘if-then-else’ (si…-> entonces…; en cualquier otro
caso…) hace referencia a elegir un camino de ejecución basándose en datos que cumplen ciertas
condiciones. Las funciones condicionales, tales como DECODE y CASE, recuperan diferentes valores
dependiendo de evaluaciones comparativas. Estas condiciones se especifican en sus parámetros.
Mientras que la expresión DECODE es específica de Oracle, la expresión CASE es compatible con ANSI
SQL. A continuación, un ejemplo de esta lógica:
Si el valor del país es Brasil o Australia, entonces recupera Hemisferio Sur. En cualquier otro caso,
recupera Hemisferio Norte.

a. La función DECODE
La función DECODE implementa la lógica ‘if-then-else’ al comprobar sus dos primeros términos para ver
si son iguales y recupera un tercero si efectivamente lo son, siendo opcional recuperar un cuarto valor
en caso de que esta condición no se cumpla. Es decir, esta función necesita obligatoriamente al menos
tres parámetros de entrada. La sintaxis es como sigue:

DECODE(expr1,comp1,iftrue1,[comp2,iftrue2...[compN,iftrueN]],[iffalse])

Sus parámetros se evalúan tal y como se muestra en el siguiente ejemplo de pseudocódigo:

If expr1 = comp1 then return iftrue1 else


if expr1 = comp2 then return iftrue2 ...
... else if expr1 = compN then return
iftrueN else return null | iffalse;

Expr1 se compara a comp1. Si fueran iguales, se recupera iftrue1. Si no lo fueran, el valor recuperado
dependerá de si comp2 y iftrue2 existen. Si están presentes, expr1 se comparará con comp2. Si ambos
son iguales se recupera iftrue2. Si no lo son, lo que pase dependerá de si compN e iftrueN existen como
pareja y el ciclo sigue hasta que no queda ninguna comparación por hacer. Finalmente, si ninguna de las

179/306
SQL

comparaciones son verdaderas, se recupera iffalse. Si este valor no estuviera definido, se recuperaría un
valor NULL.
Todos los parámetros de DECODE pueden ser expresiones, con lo cual, esta función se convierte en una
función anidada. Dado que los datos deben coincidir por parejas, si hubiera alguna discordancia en tipo
de datos, implícitamente se procede a realizar las adaptaciones necesarias.
Considérese como ejemplo las siguientes consultas:

Consulta 1: SELECT decode(1234,123,'123 is a match') FROM dual;

Consulta 2: SELECT decode(1234,123,'123 is a match','No match') FROM dual;

Consulta 3: SELECT decode('search','comp1','comp2','true1','true2','true3',substr('2search


',2,6),'true4','false')FROM dual;

La Consulta 1 compara el número 1234 con el primer término 123 de la comparación. Dado que no son
iguales, el primer resultado no se puede devolver. Además, como no hay una cláusula definida para
iffalse, se recupera un valor NULL.
La Consulta 2 es idéntica, pero sí tiene un valor definido para iffalse: “No match”.
La Consulta 3 busca a través de los valores de la comparación para encontrar una concordancia. En el
componente 3, parámetro 6, sí se encuentra una coincidencia puesto que contiene la cadena “search”.

Por tanto, se recuperará el parámetro 7 “true3”. Dado que se ha encontrado una comparación verdadera,
la búsqueda finaliza. Por tanto, aunque el parámetro 4 también daría una búsqueda positiva, esta
expresión nunca será evaluada.

En la siguiente imagen podemos ver cómo se puede construir la expresión que permita clasificar
COUNTRY_ID de la tabla LOCATIONS según “Northern Hemisphere” o “Southern Hemisphere”, utilizando
la función DECODE.

180/306
SQL

b. La expresión CASE
Prácticamente todos los lenguajes de programación de 3ª y 4ª generación incluyen un comando CASE,
que facilita la lógica ‘if-then-else’. Existen dos variantes de la expresión CASE. La expresión simple CASE
contiene una lista con los elementos de búsqueda y procede a comprobar si son iguales. La expresión de
búsqueda CASE construye una lista separada de condiciones para cada expresión de comparación.
Para poder entender estos conceptos con claridad, obsérvese la sintaxis de la expresión simple CASE:

CASE search_expr WHEN comparison_expr1 THEN iftrue1 [WHEN comparison_expr2 THEN iftrue2

WHEN comparison_exprN THEN iftrueN ELSE iffalse]
END

Como se puede observar, esta expresión está encapsulada en un bloque CASE … END y consta de, al
menos una sentencia WHEN … THEN, donde comparison_expr1 se compara con search_expr. Si son
iguales, se devuelve iftrue1. Si no, a no ser que se defina un parámetro iffalse, se recupera un valor
NULL. Si existe más de una cláusula WHEN … THEN, la búsqueda sigue hasta encontrar un caso
verdadero.

Los parámetros de búsqueda, comparación y resultado pueden ser columnas, expresiones o variables,
pero todos deben ser del mismo tipo de datos. Obsérvese la siguiente consulta:

181/306
SQL

SELECT
CASE substr(1234,1,3)
WHEN '134' THEN '1234 is a match'
WHEN '1235' THEN '1235 is a match'
WHEN concat('1','23') THEN concat('1','23')||' is a match'
ELSE 'no match'
END
FROM dual;

La expresión de búsqueda que se obtiene de la función SUBSTR(1234,1,3) es la cadena de caracteres


123. El caso WHEN … THEN realiza la comparación que resulta ser falsa, pasa al siguiente caso, que
tampoco resulta ser verdadero y pasa al tercer WHEN … THEN. Dado que en este caso la comparación
es correcta, se devuelve: “123 is a match”.

Véase un ejemplo. Las columnas LAST_NAME y HIRE_DATE para empleados con DEPARTMENT_ID no
iguales a 50, 80, 90, 100 o 110 se recuperan junto con dos expresiones numéricas y una expresión
CASE, tal y como se ve en la imagen que se muestra más abajo. La expresión numérica llamada MONTHS
devuelve un valor truncado obtenido de calcular los meses de servicio entre el 1 de enero 2013 y la fecha
de contratación del empleado utilizando la función MONTHS_BETWEEN.
Se han definido cinco categorías de lealtad a la empresa, basadas en cada dos años de servicio, al truncar
el cociente obtenido al dividir el total de meses de servicio entre 24. Con esto, se forma la expresión
para la búsqueda CASE. Ninguna de las expresiones coincide con la expresión de la primera sentencia
WHEN … THEN, pero como vemos en la imagen, el resto de WHEN … THEN recupera 13 registros y 3 más
se recuperan gracias a la sentencia ELSE. El conjunto de datos se clasifica por meses.

182/306
SQL

La sintaxis para la expresión de búsqueda CASE es:

CASE
WHEN condition1 THEN iftrue1 [WHEN condition2 THEN iftrue2

WHEN conditionN THEN iftrueN ELSE iffalse]
END

La expresión buscada en CASE está encapsulada en un bloque CASE … END y consiste de al menos una
sentencia WHEN … THEN. En la forma más simple con una sola sentencia, la condition1 se evalúa, si es
verdadera, se recupera iftrue1, si no, se recupera un valor NULL a menos que haya componente definido
en ELSE. Cuando hay más de una sentencia WHEN … THEN, se continúa con la búsqueda hasta encontrar
un caso que sea VERDADERO. De no encontrar ninguno, se recuperará o bien NULL o bien el valor definido
en la cláusula ELSE. Para recuperar los mismos datos que en la imagen anterior mediante esta nueva
sintaxis, se podría utilizar la siguiente consulta:

183/306
SQL

SELECT last_name,hire_date, trunc(months_between('01-JAN-


2013',hire_date)) months,
trunc(months_between('01-JAN-2013',hire_date)/24) "Months divided by 24", CASE
WHEN trunc(months_between('01-JAN-2013',hire_date)/24) < 2 then 'Intern'
WHEN trunc(months_between('01-JAN-2013',hire_date)/24) < 3 then 'Junior'
WHEN trunc(months_between('01-JAN-2013',hire_date)/24) < 4 then 'Intermediate'
WHEN trunc(months_between('01-JAN-2013',hire_date)/24) < 5 then 'Senior'
ELSE 'Furniture'
END loyalty FROM
employees
where department_id NOT IN (50,80,90,100,110)

ORDER BY months;
5.4. Resumen
Describir varios tipos de funciones de conversión válidas en SQL

• Cuando los valores no coinciden con los parámetros definidos de las funciones, Oracle intenta
convertirlos en los tipos de datos requeridos. Esto se conoce como conversión implícita.
• La conversión explícita se produce cuando se llama una función como TO_CHAR para modificar
el tipo de datos de un valor.
• La función TO_CHAR realiza una conversión de fecha a carácter y de número a carácter.
• Los parámetros de tipo carácter son transformados explícitamente a valores de fecha utilizando
la función de conversión TO_DATE.
• Los parámetros de tipo carácter son transformados a valores de números utilizando la función
de conversión TO_NUMBER.

Utilizar las funciones de conversión TO_CHAR, TO_NUMBER y TO_DATE

• La función TO_CHAR devuelve un elemento del tipo VARCHAR2.


• Los modelos o máscaras de formato indican patrones que se deben cumplir en las cadenas de
caracteres para facilitar una conversión precisa y consistente en parámetros de tipo fecha o
número.
• Cuando la función TO_CHAR realiza conversiones de tipo número a carácter, las máscaras de
formato pueden especificar precisión, número de dígitos, posición del operador decimal,
separador de miles, y muchos otros códigos de formato.
• Las máscaras de formato válidas cuando la función TO_CHAR es utilizada para convertir
caracteres a fecha incluyen día, semana, mes, trimestre, año y siglo.
• Los formatos de máscaras deben siempre estar especificados entre comillas simples.
• Cuando se realiza la conversión de fecha a carácter, la máscara de formato especifica qué
elemento de fecha es extraído y si el elemento debe estar descrito por un nombre largo o
abreviado.
• Los parámetros tales como los nombres de mes y día son extraídos de un tipo de dato fecha con
la función TO_CHAR. Se rellenan automáticamente con espacios que pueden recortarse
prefijando la máscara de formato con el modificador fm.
• La función TO_DATE tiene un modificador fx que especifica una coincidencia exacta para la
cadena de caracteres a convertir y la máscara de formato de fecha.
• Las funciones de anidamiento utilizan la salida de una función como entrada de otra.
• La función NVL devuelve el parámetro original sin cambios o un parámetro alternativo si el
parámetro inicial es NULL.

184/306
SQL

• La función NVL2 devuelve un nuevo parámetro if-null si el parámetro original es NULL o un


parámetro alternativo if-not-null si el parámetro original no es NULL.
• La función NULLIF comprueba dos parámetros por igualdad. Si éstos son iguales, la función
devuelve NULL; o el primero de los parámetros, en caso contrario.
• La función COALESCE devuelve el primer parámetro no nulo de una lista de parámetros. Si todos
los parámetros son NULL, entonces devuelve un valor nulo.
• La función DECODE implementa el condicional lógico if-then-else para comprobar dos parámetros
por igualdad y devuelve el tercer parámetro si éstos son iguales, u opcionalmente algún otro
parámetro si éstos son distintos.
• Hay dos variantes de la expresión CASE utilizada para facilitar el condicional lógico if-then-else:
la expresión CASE simple y la expresión CASE de búsqueda.

6. Presentando datos agregados utilizando funciones GROUP

6.1. Descripción de las funciones GROUP


Las funciones de registro único, exploradas en los capítulos 4 y 5, devuelven un valor único para cada
registro en un conjunto de resultados. Las funciones GROUP o de agregación, trabajan con varios
registros. Se utilizan para contar el número de registros o para encontrar el promedio de valores de
columna específicos en un conjunto de datos. Muchas operaciones estadísticas, como el cálculo de la
desviación estándar, las medianas y los promedios, dependen de la ejecución de funciones sobre datos
agrupados y no sobre un solo registro.

Las funciones GROUP se examinan en dos etapas. Primero, se discute su propósito y su sintaxis. En
segundo lugar, se realiza un análisis detallado de las funciones AVG, SUM, MIN, MAX y COUNT. El concepto
de agrupar o segregar datos basados en uno o más valores de columna se evalúa antes de introducir la
cláusula GROUP BY. La cláusula WHERE restringe los registros de un conjunto de datos antes de la
agrupación, mientras que la cláusula HAVING los restringe después de la agrupación. Este capítulo
concluye con un debate sobre la cláusula HAVING.

En esta sección, se definen las funciones group SQL y se discuten las diferentes variantes. Se explica la
sintaxis de las funciones GROUP seleccionadas, se discuten sus tipos de datos y se explora el efecto de
la instrucción DISTINCT sobre ellas. Esta discusión se divide en dos áreas principales:

Definición de las funciones GROUP .


Tipos y sintaxis de las funciones GROUP
.

185/306
SQL

6.1.1. Definición de las funciones GROUP


Las funciones group trabajan con agrupaciones de datos y devuelven un único resultado por grupo. Estos
grupos suelen tener cero o más registros de datos. Las funciones de un solo registro se definen con la
fórmula: F(x,y,z,…)= resultado, donde x, y , z…son parámetros de entrada. La función F se ejecuta en
un registro del conjunto de datos a la vez y devuelve un resultado para cada registro.
Las funciones GROUP se pueden definir mediante la siguiente fórmula:

F(g1, g2,g2,…, gn)=resultado1,resultado2,resultado3,…,resultadon

La función group se ejecuta una vez para cada registro y devuelve un único resultado por grupo. Estos
grupos pueden ser tablas enteras o partes de tablas asociadas usando un valor o atributo común. Si
todos los registros de las tablas se presentan como un grupo en la función GROUP , se devuelve un
resultado. Una o más funciones de agregación pueden aparecer en la lista SELECT de la siguiente manera:

SELECT group_function(column or expression),…


FROM table [WHERE …] [ORDER BY…]

Considérese la tabla EMPLOYEES. Hay 107 registros en esta tabla. Los grupos se pueden crear basándose
en los valores comunes que comparten los registros. Por ejemplo, los registros que comparten el mismo
valor de DEPARTMENT_ID pueden agruparse. A continuación, las funciones GROUP se ejecutan por
separado para cada grupo único. Como muestra la siguiente imagen, hay 12 valores distintos de
DEPARTMENT_ID en la tabla EMPLOYEES, incluyendo un valor nulo. Los registros se distribuyen en 12
grupos basados en valores comunes de DEPARTMENT_ID. La función COUNT se ejecuta 12 veces, una
por cada grupo. Se observa que los distintos grupos no contienen el mismo número de registros.

186/306
SQL

Las funciones GROUP agregan varios valores de varios registros en un único resultado. Se
utilizan ampliamente con fines de presentación de informes y también se conocen como
funciones resumidas o agregadas. Los datos agregados útiles como suma, promedios y
recuentos a menudo forman la base de cálculos estadísticos más sofisticados. Es útil tener un
buen entendimiento de los datos almacenados en las tablas de aplicación para maximizar la
calidad de los informes.

6.1.2. Tipos y sintaxis de las funciones GROUP


A continuación se ofrece una breve descripción de las funciones GROUP más utilizadas. Muchas de ellas
se examinan en detalle más adelante en este capítulo.

La función COUNT cuenta el número de registros de un grupo. Su sintaxis es la siguiente:

COUNT({*|[DISTINCT|ALL] expr}) ;
El tipo de datos expr puede ser NUMBER, DATE, CHAR, o VARCHAR2. Esta sintaxis puede descomponerse
de la siguiente forma:

187/306
SQL

1. COUNT(*)
2. COUNT(DISTINCT expr)
3. COUNT(ALL expr)
4. COUNT(expr)

Cuando se invoca COUNT(*), se cuentan todos los registros del grupo, incluidos los que tienen valores
nulos o duplicados. Cuando se ejecuta COUNT(DISTINCT expr), sólo se cuentan las veces que aparece
expr en cada grupo. La palabra clave ALL es parte de la sintaxis por defecto, por lo que COUNT(ALL expr)
y COUNT(expr) son equivalentes: cuentan el número de veces que expr aparece en cada grupo. Si expr
es nulo, se ignora a menos que se maneje usando una función general como NVL, NVL2, o COALESCE.

La función AVG calcula el valor promedio de una columna numérica o expresión en un grupo. Su sintaxis
es la siguiente:

AVG([DISTINCT|ALL] expr) ;

El tipo de datos del parámetro expr es NUMBER. Esta sintaxis puede descomponerse de la siguiente
forma:

1. AVG(DISTINCT expr)
2. AVG(ALL expr)
3. AVG(expr)

Cuando se invoca AVG(DISTINCT expr), los valores de expr se suman y dividen por el número de
ocurrencias únicas de expr. AVG(ALL expr) y AVG(expr) suman los valores no nulos de expr para cada
registro y dividen la suma por el número de registros no nulos en el grupo.

La función SUM devuelve la agregación de los valores numéricos no nulos en un grupo. Tiene la siguiente
sintaxis:

SUM([DISTINCT|ALL] expr) ;

El tipo de datos expr es NUMBER. Esta sintaxis puede descomponerse de la siguiente forma:

1. SUM(DISTINCT expr)
2. SUM(ALL expr)
3. SUM(expr)
4.
SUM(DISTINCT expr) proporciona un resultado sumando todos los valores únicos devueltos después de
que expr es evaluado para cada registro en el grupo. SUM(expr) y SUM(ALL expr) proporcionan un
resultado añadiendo expr para cada registro del grupo. Los valores nulos son ignorados.

Las funciones MAX y MIN devuelven el valor expr máximo (mayor) y mínimo (menor) en un grupo. Su
sintaxis es la siguiente:

MAX([DISTINCT|ALL] expr); MIN([DISTINCT|ALL] expr)

188/306
SQL

El tipo de datos del parámetro expr puede ser NUMBER, DATE, CHAR, o VARCHAR2. Esta sintaxis puede
descomponerse de la siguiente forma:

1. MAX(DISTINCT expr); MIN(DISTINCT expr)


2. MAX(ALL expr); MIN(ALL expr)
3. MAX(expr); MIN(expr);

MAX(expr), MAX(ALL expr) y MAX(DISTINCT expr) examinan los valores de expr en un grupo de registros
y devuelven el valor mayor. Los valores nulos son ignorados. MIN(expr), MIN(ALL expr) y MIN(DISTINCT
expr) examinan los valores de expr en un grupo de registros y devuelven el valor más pequeño.
Las funciones STDDEV y VARIANCE son dos de las muchas funciones GROUP estadísticas que ofrece
Oracle.

VARIANCE tiene la siguiente sintaxis:

VARIANCE([DISTINCT|ALL] expr);

El tipo de datos del parámetro expr es NUMBER. Esta sintaxis puede descomponerse de la siguiente
forma:

1. VARIANCE(DISTINCT expr)
2. VARIANCE(ALL expr)
3. VARIANCE(expr)

La varianza estadística se refiere a la variabilidad de los datos de una muestra o conjunto de datos.
VARIANCE(DISTINCT expr) devuelve la variabilidad de datos únicos no nulos en un grupo.
VARIANCE(expr) y VARIANCE(ALL expr) devuelven la variabilidad de los datos no nulos en el grupo.

STDDEV tiene la siguiente sintaxis:


STDDEV([DISTINCT|ALL] expr);

El tipo de datos del parámetro expr es NUMBER. Esta sintaxis puede descomponerse de la siguiente
forma:

1. STDDEV(DISTINCT expr)
2. STDDEV(ALL expr)
3. STDDEV(expr)

STDDEV calcula la desviación estándar estadística, que es el grado de desviación del valor medio en un
grupo. Se obtiene encontrando la raíz cuadrada de la varianza. STDDEV(DISTINCT expr) devuelve la
desviación estándar de datos únicos no nulos en un grupo. STDDEV(expr) y STDDEV(ALL expr) devuelven
la desviación estándar de los datos no nulos en el grupo.

Hay dos reglas fundamentales a recordar cuando se estudian las funciones GROUP . Primero,
siempre operan sobre un solo grupo de registros a la vez. El grupo puede ser uno de los
muchos grupos en los que se ha segmentado un conjunto de datos, o puede ser una tabla

189/306
SQL

completa. La función group se ejecuta una vez por grupo. Segundo, los registros con algún
campo nulo en sus columnas o expresiones de grupo son ignorados, a menos que se provea
una función general como NVL, NVL2, o COALESCE para manejarlas.
Considerar el siguiente ejemplo. Si el valor medio de COMMISSION_PCT se recupera de la
tabla EMPLOYEES, sólo se consideran los valores no nulos. La expresión
AVG(COMMISSION_PCT) suma los 35 valores no nulos de COMMISSION_PCT y divide el total
entre 35. El promedio basado en las 107 filas puede calcularse utilizando la expresión
AVG(NVL(COMMISSION_PCT,0)).

6.2. Indentificar las Funciones GROUP Disponibles


Las diferentes variaciones de las funciones GROUP y su sintaxis ya han sido analizadas. En esta sección
se ven ejemplos aplicando estas funciones. Las interacciones de las funciones GROUP con valores NULL
y el uso de la palabra clave DISTINCT son analizadas junto con el concepto de anidación de las funciones.
Las funciones GROUP disponibles se identifican y exploran en los siguientes apartados:

Utilizando funciones GROUP


Anidando funciones GROUP

6.2.1. Utilizando funciones GROUP


La aplicación práctica de funciones GROUP se estudiará mediante el uso de las funciones AVG, SUM, MIN,
MAX y COUNT. Este grupo de funciones devuelve resultados numéricos. Adicionalmente, las funciones
MIN y MAX pueden devolver resultados de tipo carácter y fecha. Estas cinco funciones operan con valores
no nulos, pero, al contrario que el resto, la función COUNT (*) también cuenta registros con valores
nulos. La palabra clave DISTINCT es usada para restringir los registros que se envía a estas funciones.
Los analistas a menudo quieren saber la suma total o la media de una columna o expresión. Esto es fácil
de conseguir usando un programa de hojas de cálculo. Usando las funciones GROUP de SQL ofrece dos
ventajas sobre una hoja de cálculo para el análisis. La primera, ofrecen una plataforma para realizar
cálculos usando datos en tiempo real. La segunda, permiten el análisis de todos los valores en un
conjunto de datos o el análisis de un grupo específico de datos de una manera sencilla.

a. La función COUNT
La ejecución de COUNT en una columna o en una expresión devuelve un valor entero que representa el
número de registros en el grupo. La función COUNT tiene la siguiente sintaxis:

COUNT({*|[DISTINCT|ALL] expr});

Hay un parámetro que puede ser, o bien *, que representa todas las columnas incluyendo valores nulos,
o bien una columna específica o una expresión. Puede ser precedida por la palabra clave DISTINCT o
ALL. Se consideran las siguientes consultas:

Consulta 1: SELECT count(*) FROM employees;


Consulta 2: SELECT count(commission_pct) FROM employees;
Consulta 3: SELECT count(DISTINCT commission_pct) FROM employees;
Consulta 4: SELECT count(hire_date), count(manager_id) FROM employees;

190/306
SQL

La consulta 1 cuenta los registros en la tabla EMPLOYEES y devuelve el resultado 107. La consulta 2
cuenta los registros de COMMISSION_PCT con valores no nulos y devuelve 35. La consulta 3 tiene en
cuenta los 35 registros no nulos, determina el número de valores únicos y devuelve 7. La consulta 4
muestra dos funcionalidades. La primera, es posible usar varias funciones GROUP en la misma consulta
SELECT y segunda, la función COUNT se usa en columnas de tipo DATE y tipo NUMBER. Los resultados
107 y 106 son los devueltos ya que hay 107 valores no nulos de HIRE_DATE y 106 valores no nulos de
MANAGER_ID en el grupo.

En la imagen siguiente, tres funciones COUNT son usadas a la vez. Esta consulta muestra que hay 107
registros de empleados en la tabla EMPLOYEES. Además, estos 107 empleados están en 12
departamentos, incluyendo departamentos nulos y trabajan en 19 trabajos únicos.

b. La función SUM
La suma total de una columna o una expresión se calcula usando la función SUM. Su sintaxis es la
siguiente:

SUM([DISTINCT|ALL ] expr);

Un valor numérico, puede ser precedido por la palabra clave DISTINCT o ALL, se le pasa a la función
SUM, que devuelve un número. Se consideran las siguientes consultas:

Consulta 1: SELECT sum(2) FROM employees;


Consulta 2: SELECT sum(salary) FROM employees;
Consulta 3: SELECT sum(DISTINCT salary) FROM employees; Consulta 4: SELECT
sum(commission_pct) FROM employees;

Hay 107 registros en la tabla EMPLOYEES. La consulta 1 añade 2 a todos los registros y devuelve 214.
La consulta 2 toma el valor de la columna SALARY para cada registro en el grupo, en este caso es la
tabla entera y devuelve el salario total de 691416. La consulta 3 devuelve un total de 409908 ya que
muchos de los empleados tienen el mismo salario y la palabra clave DISTINCT solo suma los valores
únicos de la columna al total. La consulta 4 devuelve 7.8 después de sumar los valores no nulos.

191/306
SQL

La siguiente imagen muestra dos consultas. La primera calcula el número de días entre el final de 2015
y el valor de la columna HIRE_DATE. La aritmética de fechas se realiza para cada registro. El número
devuelto es sumado usando una llamada a la función SUM.

El resultado está dividido por 365.25 para dar el número total de años trabajados por todos los empleados
actuales. La segunda consulta muestra que la función SUM devuelve el error “ORA-00932: inconsistent
datatypes” si la función recibe un parámetro no numérico.

c. La Función AVG
El valor medio de una columna o expresión divide la suma por el número de registros no nulos en el
grupo. La función AVG tiene la siguiente sintaxis:

AVG([DISTINCT|ALL] expr);

Un valor numérico, puede ser precedido por la palabra clave DISTINCT o ALL, se le pasa a la función
AVG, que devuelve un número. Se consideran las siguientes consultas:

Consulta 1: SELECT avg(2) FROM employees;


Consulta 2: SELECT avg(salary) FROM employees;
Consulta 3: SELECT avg(DISTINCT salary) FROM employees;
Consulta 4: SELECT avg(commission_pct) FROM employees;

Existen 107 registros en la tabla EMPLOYEES. La consulta 1 añade el número 2 a los 107 registros y
divide el total por el número de registros para devolver el número 2. Las variables numéricas enviadas
a la función AVG son devueltos sin cambios. La consulta 2 suma el valor SALARY en cada registro para
obtener el salario total de 691400. Dicho valor se divide por los 107 registros con valores no nulos de
SALARY y devuelve la media de 6461.83178. Cabría esperar que la consulta 3 devolviese un resultado
menor que la consulta 2, pero no. Existen 58 valores únicos de salario, que sumados dan un total de
409908. Dividiendo 409908 entre 58 devuelve 7067.37931 como media de los salarios distintos. La
consulta 4 puede producir resultados imprevistos si no se entiende de forma adecuada. Tras sumar los
valores no nulos, incluyendo duplicados, suma un total de 7.8 que, dividido entre 35 devuelve una media
de 0.222857143 de COMMISION_PCT.

La siguiente imagen muestra dos consultas. La primera muestra las columnas LAST_NAME y JOB_ID con
una
192/306
SQL

expresión que calcula el número total de años trabajados por los programadores en la organización desde
el final de 2015. El segundo usa la función AVG para calcular el número medio de años que los
programadores actuales han estado contratados desde el año 2015.

d. Las Funciones MAX y MIN


Las funciones MIN y MAX operan con los tipos de datos de NUMBER, DATE, CHAR y VARCHAR2. Devuelven
un valor del mismo tipo de dato que el argumento introducido, que son el más grande o más pequeño
del grupo. Cuando se aplican a valores de tipo DATE, MAX devuelve la fecha más reciente y MIN la fecha
más antigua. Las cadenas de caracteres son convertidas a las representaciones numéricas de sus valores
basándose en las opciones NLS de la base de datos. Cuando la función MIN es aplicada a un grupo de
cadena de caracteres, la palabra que aparezca como primera de manera alfabética será devuelta, MAX
devolverá la última. La función MAX y MIN tienen la siguiente sintaxis:

MAX([DISTINCT|ALL] expr); MIN([DISTINCT|ALL] expr)

Toman un parámetro precedido por la palabra clave DISTINCT o ALL. Se consideran las siguientes
consultas:

Consulta 1: SELECT min(commission_pct), max(commission_pct) FROM employees;


Consulta 2: SELECT min(start_date),max(end_date) FROM job_history;
Consulta 3: SELECT min(job_id),max(job_id) FROM employees;

La consulta 1 devuelve los valores numérico 0.1 y 0.4 para el mínimo y máximo de los valores de
COMMISION_PCT en la tabla EMPLOYEES. Nótese que los valores nulos de la tabla COMMISION_PCT son
ignorados. La consulta 2 evalúa una columna del tipo DATE e indica que la fecha de START_DATE en la
tabla JOB_HISTORY más antigua es 17-SEP-1995 y la última END_DATE es 31-DEC-2007. La consulta 3
devuelve los valores AC_ACCOUNT y ST_MAN de la columna JOB_ID como primer y último registro
ordenado alfabéticamente en la tabla EMPLOYEES.

La primera consulta mostrada en la siguiente imagen usa las funciones MAX y MIN para obtener
información sobre los empleados con el valor de JOB_ID igual a “SA_REP”. Los resultados indican que los
representantes de ventas trabajando el menor y máximo tiempo fueron empleados el 21-APR-2008 y el
30-JAN-2004, respectivamente. Por otra parte, los representantes de ventas con mayor y menor salario

193/306
SQL

ganan 11500 y 6100, respectivamente. La segunda consulta muestra los valores de la columna
LAST_NAME de los representantes de ventas a los que se aplican los valores mínimo y máximo
HIRE_DATE y SALARY.

Problema Solución

Se desea recuperar la primera Sí, la función MIN funciona con


fecha de una columna que datos numéricos, de fecha y de
almacena información de
carácter. Cuando se ejecuta la
FECHA. ¿Se puede utilizar una
función grouppara recuperar función MIN sobre una columna
este valor? DATE, se devuelve el valor de fecha
más antiguo.

El personal directivo necesita Sí, no hay restricción en el número


estadísticas resumidas. Esto de funciones GROUP enumeradas
incluye detalles como el número
en la cláusula SELECT. El informe
de empleados, el importe total
de los salarios del personal, el solicitado puede elaborarse
salario más bajo y los valores utilizando la siguiente consulta:
salariales más altos. ¿Puede
SELECT COUNT(*)
redactarse un informe de este
tipo utilizando una sola Num_Employees, SUM(SALARY)
consulta? Tot_Salary_Cost,

194/306
SQL

MIN(SALARY)
Lowest_Salary,
MAX(SALARY)
Maximum_Salary
FROM EMPLOYEES;

Se pide listar solamente las Sí, la palabra clave DISTINCT se


tareas realizadas por los puede utilizar con las funciones
empleados de una empresa de
agregadas. Para contar valores
entre todas las tareas obtenidas
del registro JOB_ID ¿Es posible JOB_ID únicos en la tabla
contar solamente esas tareas? EMPLOYEES, se puede realizar la
siguiente consulta:
SELECT COUNT(DISTINCT
JOB_ID)
FROM EMPLOYEES;

6.2.2. Funciones GROUP anidadas


Recordar que las funciones de un solo registro pueden anidarse o ser embebidas a cualquier nivel de
profundidad. Las funciones de GROUP sólo pueden ser anidas a dos niveles de profundidad. Aquí se
muestran tres formatos que utilizan funciones de GROUP :

G1(group_item) = result
G1(G2(group_item ) = result
G1(G2(G3(group_item))) no está permitido.

Las funciones GROUP se representan mediante la letra “G” seguida de un número. El primer formulario
simple no contiene funciones anidadas. Los ejemplos incluyen las funciones SUM(group_item) y
AVG(group_item) que devuelven un único resultado por grupo. El segundo formulario contiene dos
funciones de GROUP anidadas: SUM (AVG(group_item)). En este caso, una cláusula GROUP BY es
obligatoria ya que el valor medio del grupo group_item por cada agrupación se calcula antes de ser
agregado por la función SUM.

La tercera forma es rechazada por Oracle. Se considera una expresión que anida tres funciones GROUP
. Si la función MAX se aplica al ejemplo anterior, se forma la expresión MAX(SUM(AVG(group_item))).
Las dos funciones GROUP internas devuelven un único valor que representa la suma de un conjunto de
valores medios. Esta expresión se convierte en MAX (valor individual). Una función GROUP no se puede
aplicar a un valor individual.

195/306
SQL

Esta imagen muestra dos consultas. Ambas restringen los registros devueltos a aquellos con valores de
DEPARTMENT_ID null, 40 y 80. Estos se divididen por sus valores DEPARTMENT_ID en tres grupos. La
primera consulta calcula la suma de los valores de COMMISSION_PCT para cada grupo y devuelve los
valores 0.15, null, y 7.65. La consulta 2 contiene las funciones GROUP anidadas, que se pueden evaluar
como se indica a continuación:

AVG(SUM(COMMISSION_PCT)) = (0.15 + 7.65) /2 = 3.9.

Las funciones de un solo registro pueden anidarse a cualquier nivel, pero las funciones GROUP
pueden anidarse, como máximo, dos niveles de profundidad. La llamada de función anidada
COUNT(SUM(AVG( X))) devuelve el error, “ORA-00935: group function is nested too deeply.”
Es aceptable anidar funciones de un solo registro dentro de funciones GROUP . Se puede
considerar la siguiente pregunta: SELECT SUM(AVG(LENGTH(LAST_NAME))) FROM
EMPLOYEES GROUP BY DEPARTMENT_ID. Calcula la suma de la longitud media de los valores
de LAST_NAME por departamento.

6.3. Agrupar datos usando la cláusula GROUP BY


Las funciones GROUP explicadas anteriormente usan grupos de registros usando la tabla entera. Esta
sección explora la partición de conjuntos de datos en grupos usando la cláusula GROUP BY. Las funciones
GROUP pueden ser aplicadas a estos subconjuntos o agrupación de registros. La sintaxis de las funciones
GROUP y la cláusula GROUP BY se identifican y exploran siguiendo los siguientes enunciados:

196/306
SQL

• Creando grupos de datos


• La cláusula GROUP BY Agrupando por varias columnas

6.3.1. Creando Grupos de Datos


Una tabla tiene al menos una columna y cero o más registros. En muchas tablas estos datos requieren
ser analizados para transformarlos en información útil. Es, de hecho, un requisito común para calcular
estadísticas desde un conjunto de datos divididos en grupos usando diferentes atributos. En ejemplos
anteriores, se ha hecho uso de las funciones GROUP , que operaban sobre todos los registros en una
tabla. La tabla entera era tratada como un gran grupo.

Los grupos de datos dentro de un conjunto se crean asociando registros con propiedades o atributos
comunes. Desde ese momento, las funciones GROUP pueden ejecutarse contra cualquiera de esos
grupos.
Se considera la tabla EMPLOYEES. Está compuesta por 11 columnas y 107 registros. Se podrían crear
grupos de registros que compartan el valor de DEPARTMENT_ID. La función SUM podría ser usada para
crear el cómputo de salarios totales por departamento. Otra posible agrupación podría ser para registros
que compartan el mismo valor de la columna JOB_ID. La función AVG podría ser usada para saber el
salario medio pagado a los empleados en diferentes trabajos.
Un grupo es definido como un subconjunto del conjunto de datos entero, cuyo subconjunto comparte
uno o más atributos. Estos atributos son típicamente valores de una columna, pero también pueden ser
expresiones. El número de grupos creado depende de los diferentes valores únicos de un atributo común.

Como se muestra en la siguiente imagen, hay 12 valores únicos de DEPARTMENT_ID en la tabla


EMPLOYEES. Si los registros son agrupados usando los valores comunes de DEPARTMENT_ID, habrá 12
grupos. Si una función GROUP es ejecutada sobre estos grupos, habrá 12 valores devueltos, ya que se
ejecutará una vez por cada grupo.

197/306
SQL

El agrupamiento de datos y las funciones de resumen son ampliamente utilizadas como fines
de reporte. Es importante practicar la segmentación de un conjunto de datos en diferentes
agrupaciones. Oracle proporciona el lenguaje analítico para deconstruir conjuntos de datos
en grupos, dividirlos en subgrupos, etc. Se pueden ejecutar funciones de agregación sobre
estos grupos y subgrupos.

6.3.2. La cláusula GROUP BY


La función SELECT puede complementarse con la adición de la cláusula GROUP BY. Esta cláusula facilita
la creación de grupos. Aparece después de la cláusula WHERE pero antes de la cláusula ORDER BY, como
sigue:

SELECT

column|expression|group_function(column|expression [alias]),…}
FROM table

198/306
SQL

[WHERE condition(s)]
[GROUP BY {col(s)|expr}]
[ORDER BY {col(s)|expr|numeric_pos} [ASC|DESC] [NULLS FIRST|LAST]];

La columna o expresión especificada en la cláusula GROUP BY también se conoce como atributo de


agrupación y es el componente por el que se agrupan los registros. El conjunto de datos se segmenta
en función del atributo de agrupación. Se considera la siguiente consulta:

SELECT max(salary), count(*)


FROM employees
GROUP BY department_id
ORDER BY department_id;

El atributo de la agrupación en este ejemplo es la columna DEPARTMENT_ID. El conjunto de datos, en el


que las funciones GROUP de la lista SELECT deben operar, se dividide en 12 grupos, uno para cada
departamento. Para cada uno de ellos, se devuelve el valor del salario máximo y el número de registros.
Puesto que los resultados se clasifican por DEPARTMENT_ID, el tercer registro del conjunto de resultados
contiene los valores 11000 y 6. Esto indica que 6 empleados tienen un valor para DEPARTMENT_ID de
30 (En este schema hay 10 DEPARTMENTS, con ID de 10 a 100). De estos 6, el empleado con SALARY
màximo es de 11000. Esta consulta demuestra que el atributo de la agrupación no tiene porqué estar en
la lista SELECT.

Es común ver el atributo de la agrupación en la lista SELECT junto con las funciones GROUP . Si un
elemento que no es una función GROUP aparece en la lista SELECT con otras funciones GROUP y no hay
ninguna cláusula GROUP BY, se produce un error “ORA-00937: not a single-group group function”. Si una
cláusula GROUP BY está presente pero este ítem no es un atributo de agrupación, entonces se devuelve
el error “ORA-00979: not a GROUP BY expression”.
Cualquier elemento de la lista SELECT que no sea una función GROUP debe ser un atributo de agrupación
de la cláusula GROUP BY.

Si se introduce una función GROUP en una cláusula WHERE, se devuelve un error “ORA-00934: group
function is not allowed here”.

Se pueden imponer condiciones a nivel de grupo usando la cláusula HAVING discutida en la siguiente
sección. Sin embargo, las funciones GROUP pueden utilizarse como parte de la cláusula ORDER BY.

La primera consulta de la imagen plantea un error ya que la columna END_DATE está en la lista SELECT
con una función GROUP y no hay ninguna cláusula GROUP BY. Se devuelve un error “ORA-00979” de la
segunda consulta, ya que el elemento START_DATE aparece en la cláusula SELECT, pero no es un atributo
de la agrupación.

La tercera consulta divide los registros de JOB_HISTORY en grupos por año de la columna END_DATE.
Se crean cuatro grupos utilizando este atributo de agrupación. Estos representan diferentes años en los
que los empleados terminaron su trabajo. El COUNT muestra el número de empleados que renunciaron
a sus trabajos durante cada uno de estos años. Los resultados se listan en orden descendente según la
expresión "Number of Employees". Se debe tener en cuenta que la función GROUP COUNT está presente
en la cláusula ORDER BY.

199/306
SQL

Un conjunto de datos se divide en grupos utilizando la cláusula GROUP BY. El atributo de la


agrupación es la clave común compartida por los miembros de cada grupo. El atributo de la
agrupación suele ser una sola columna, pero pueden ser varias columnas o una expresión que
no puede basarse en funciones GROUP . Tener en cuenta que la cláusula SELECT sólo permite
agrupar atributos y funciones GROUP cuando se utiliza GROUP BY.

6.3.3. Agrupando por múltiples columnas


Una gran extensión de la cláusula GROUP BY es que utiliza múltiples atributos de agrupación. Oracle
permite a los conjuntos de datos repartirse en grupos y permite a esos grupos dividirse más en subgrupos
utilizando diferentes atributos de agrupación. Se consideran las siguientes dos consultas:

Consulta 1: SELECT department_id, sum(commission_pct)


FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY department_id;

Consulta 2: SELECT department_id, job_id, sum(commission_pct)


FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY department_id, job_id;

200/306
SQL

La consulta 1 restringe los registros devueltos de la tabla EMPLOYEES a 35 registros con valores de
COMMISSION_PCT no nulos. Estos registros están divididos en 2 grupos: 80 y NULL basados en el
atributo de agrupación DEPARTMENT_ID. El conjunto de resultados contiene dos registros, los cuales
devuelven la suma de los valores de COMMISSION_PCT de cada grupo.

La consulta 2 es similar a la primera excepto porque tiene un elemento adicional: JOB_ID en sendas
cláusulas SELECT y GROUP BY. Este segundo atributo de agrupación se descompone en dos grupos
basados en DEPARTMENT_ID en los componentes JOB_ID constituyentes que pertenecen a los registros
de cada grupo. Los distintos valores de JOB_ID para los registros con DEPARTMENT_ID=80 son SA_REP
y SA_MAN. El valor de JOB_ID para los registros con valor de DEPARTMENT_ID nulo es SA_REP. Por lo
tanto, la consulta 2 devuelve dos agrupaciones, una que consiste en dos subgrupos y la otro con
solamente uno, tal y como se muestra en la siguiente imagen:

Problema Solución

Se desea imprimir tarjetas de Sí. Las funciones MAX y MIN aplicadas a


identificación para el personal que la columna LAST_NAME determinará la
trabaja como representante de ventas.
máxima y mínima longitud de los
¿Se puede determinar la longitud de los
valores de LAST_NAME más cortos y más nombres, tal y como se muestra en la
largos para estos empleados? siguiente consulta:
SELECT
MIN(LENGTH(LAST_NAME)),
MAX(LENGTH(LAST_NAME))
FROM EMPLOYEES
WHERE JOB_ID='SA_REP';

201/306
SQL

¿Es posible contar los registros en cada Sí. Agrupar múltiples columnas es una
grupo, primero dividiendo los registros gran opción permitiendo un análisis
de empleados por año de contratación,
bastante fino, tal y como se muestra en
después por trabajo, y finalmente por
salario? la siguiente consulta:
SELECT COUNT(*),

TO_CHAR( HIRE_DATE, 'YYYY'),


JOB_ID,
SALARY
FROM EMPLOYEES

GROUP BY
TO_CHAR(HIRE_DATE,'YYYY') ,
JOB_ID, SALARY;

¿Existe un límite para el número de No. No existe un límite para el número


grupos que se pueden formar? de grupos y subgrupos que se pueden
formar.

6.4. Incluir o excluir registros agrupados usando la cláusula HAVING


Crear grupos de datos y aplicar funciones GROUP es muy útil. Un valor añadido a estas características
es la capacidad de incluir o excluir resultados basados en condiciones a nivel de grupo. Esta sección
introduce la cláusula HAVING. Además, se hace una distinción clara entre la cláusula WHERE y la cláusula
HAVING. La cláusula HAVING se explica en las siguientes áreas:

• Restringir resultados de grupo


• La cláusula HAVING

6.4.1. Restringiendo Resultados Agrupados


Las condiciones de la cláusula WHERE restringen los registros devueltos por una consulta. Los registros
se incluyen en función de si cumplen las condiciones listadas o no y a veces se conocen como resultados
a nivel de registro (row-level results). La agrupación de registros mediante la cláusula GROUP BY y la
aplicación de una función GROUP a estos grupos producen resultados a los que a menudo se hace
referencia como resultados a nivel de grupo. La cláusula HAVING proporciona el lenguaje para
restringirlos.

La siguiente consulta limita las filas recuperadas de la tabla JOB_HISTORY especificando una condición
WHERE basada en la columna DEPARTMENT_ID.

SELECT department_id
FROM job_history
WHERE department_id IN (50,60,80,110);

Esta consulta devuelve siete registros. Si la cláusula WHERE estuviera ausente, se recuperarían los 10
registros. Suponga que desea saber cuántos empleados trabajaban anteriormente en cada uno de estos
departamentos. Hay siete registros que se pueden agrupar y contar manualmente. Sin embargo, si hay
un gran número de registros, se puede utilizar una función GROUP como COUNT, como se muestra en la
siguiente consulta:

202/306
SQL

SELECT department_id, count(*)


FROM job_history
WHERE department_id IN (50,60,80,110)
GROUP BY department_id;

Esta consulta es muy similar a la anterior. La función GROUP: COUNT se añadió a la lista SELECT, y
también se incorporó una cláusula GROUP BY DEPARTMENT_ID. Se devuelven cuatro registros con su
número total de registros y los siete registros originales restringidos por la cláusula WHERE que se han
agrupado en cuatro grupos basados en valores comunes de DEPARTMENT_ID, como se muestra en la
siguiente tabla:

DEPARTMENT_ID COUNT(*)

50 2

60 1

80 2

110 2

Suponga que desea refinar esta lista para incluir sólo los departamentos con más de un empleado. La
cláusula HAVING limita o restringe el nivel de grupo filas si es necesario.
Esta consulta se construirá siguiendo los siguientes pasos:

1. Considerar todo el set de datos de nivel de registro.


2. Limitar el conjunto de datos en función de las condiciones de la cláusula WHERE.
3. Segmentar los datos en uno o más grupos utilizando los atributos de agrupación especificado en
la cláusula GROUP BY.
4. Aplicar cualquier función GROUP para crear un nuevo set de datos a nivel de grupo. Cada registro
puede considerarse como una agregación de sus datos a nivel de registro de origen basados en
grupos creados.
5. Limitar o restringir los datos a nivel de grupo con una condición de cláusula HAVING. Sólo se
devuelven los resultados a nivel de grupo que cumplan estas condiciones.
6.
Elegir el contexto apropiado para utilizar una cláusula WHERE o HAVING depende de si se
deben restringir los registros a nivel físico o de grupo. Los registros que contienen datos
almacenados en columnas se denominan a veces registros reales o físicos. Cuando se limitan
los registros (físicos) reales, se imponen una o más condiciones utilizando una cláusula
WHERE. Cuando se agrupan estos registros, se pueden aplicar una o más funciones GROUP ,
dando como resultado una o más registros a nivel de grupo. No se trata de registros físicos,
sino de agregaciones temporales de datos. Los registros a nivel de grupo están restringidas
mediante condiciones impuestas por una cláusula HAVING.

203/306
SQL

6.4.2. La cláusula HAVING


La forma general de la sentencia SELECT se ve reforzada por la adición de la cláusula HAVING y se
convierte en:

SELECT column|expression|group_function(column|expression [alias]),…}


FROM table
[WHERE condition(s)]
[GROUP BY {col(s)|expr}]
[HAVING group_condition(s)]
[ORDER BY {col(s)|expr|numeric_pos} [ASC|DESC] [NULLS FIRST|LAST]];

Una diferencia importante entre la cláusula HAVING y las demás cláusulas de la declaración SELECT es
que sólo puede especificarse si existe una cláusula GROUP BY. Esta dependencia es razonable, ya que
los registros a nivel de grupo deben existir antes de que se puedan restringir. La cláusula HAVING puede
aparecer antes de la cláusula GROUP BY en la sentencia SELECT. Sin embargo, es más común colocar la
cláusula HAVING después de la cláusula GROUP BY. Se realiza toda la agrupación y las funciones GROUP
se ejecutan antes de evaluar la cláusula HAVING.

La siguiente consulta muestra cómo se utiliza la cláusula HAVING para restringuir un conjunto de datos
agregado. Los registros de la tabla JOB_HISTORY se dividen en cuatro grupos. Se devuelven los registros
que cumplen la condición de la cláusula HAVING (contribución de más de un registro al recuento de
registros del grupo):

SELECT department_id, count(*)


FROM job_history
WHERE department_id IN (50,60,80,110)
GROUP BY department_id
HAVING count(*) > 1 AND department_id > 50;

Dos registros con valores DEPARTMENT_ID de 80 y 110, cada una con un COUNT(*) de 2 son devueltos.
Obsérvese que el atributo de agrupación DEPARTMENT_ID puede estar presente en la cláusula HAVING.
La imagen que se muestra a continuación presenta tres consultas. La primera consulta divide los 107
registros de la tabla EMPLOYEES en 19 grupos basados en valores JOB_ID comunes. Se calcula el salario
medio de cada grupo JOB_ID y el número total de registros. La segunda consulta introduce un grado
más de filtraje, al excluir condicionalmente los registros agregados en las que el salario medio es inferior
o igual a 10000, utilizando una cláusula HAVING. La tercera consulta demuestra que se pueden utilizar
los operadores booleanos para especificar múltiples condiciones de la cláusula HAVING.

204/306
SQL

La cláusula HAVING sólo puede especificarse cuando existe una cláusula GROUP BY. Se puede
especificar una cláusula GROUP BY sin una cláusula HAVING. Se pueden imponer múltiples
condiciones mediante una cláusula HAVING utilizando los operadores booleanos AND, OR, y

205/306
SQL

NOT. Las condiciones de la cláusula HAVING restringen los datos a nivel de grupo y deben
contener una función GROUP o una expresión basada en los atributos de agregación.

6.5. Resumen
Describir las funciones grupales

• Las funciones GROUP se conocen también como funciones de registro múltiple, agregadas o de
resumen. Se ejecutan una vez para cada grupo de datos y agregan la información desde múltiples
registros a un único resultado por cada grupo.
• Los grupos pueden ser tablas enteras o porciones de una tabla agrupada por un atributo de
agrupación común.
Identificar las Funciones GROUP Disponibles.

• La función COUNT devuelve un valor entero que representa el número de registros en un grupo.
• La función SUM calcula la agregación total de todas las expresiones numéricas del grupo que no
sean nulas.
• La función AVG divide la suma de una columna o expresión por el número de registros no nulos
del grupo.
• Las funciones MAX y MIN operan sobre tipos de datos NUMBER, CHAR, DATE y VARCHAR2.
Devuelven los valores mayor y menor del grupo. Las funciones de
grupo solo pueden anidarse con dos niveles de profundidad.

Agrupar datos utilizando la cláusula GROUP BY

• La cláusula GROUP BY especifica el atributo de agrupación que los registros deben tener en
común para poder ser aglomerados conjuntamente.
• La cláusula GROUP BY permite la creación de grupos dentro de un conjunto de datos
seleccionados y se coloca después de la cláusula WHERE pero antes que la cláusula ORDER BY.
• Cualquier elemento de la lista SELECT que no es una función GROUP , debe ser un atributo de
agrupación.
• Las funciones GROUP no pueden estar colocadas dentro de la cláusula WHERE.
• Los conjuntos de datos se pueden partir en grupos y dividirse en subgrupos basándose en
atributos de agrupación múltiple.

Incluir o excluir registros agrupados utilizando la cláusula HAVING

• Las agrupaciones de registros que utilizan un atributo de agregación común con la cláusula
GROUP BY y, sobre las que se aplica una función GROUP para cada uno de los conjuntos,
devuelven resultados a nivel de grupo.
• La cláusula HAVING proporciona el lenguaje para limitar los resultados a nivel de grupo
devueltos.
• La cláusula HAVING solo puede especificarse si hay una cláusula GROUP BY presente.
• Todas las agrupaciones que se llevan a cabo y las funciones GROUP son ejecutadas antes de
evaluar la cláusula HAVING.

206/306
SQL

7. Visualización de datos de múltiples tablas

7.1. Sentencias SELECT para acceder a datos de más de una tabla utilizando
EQUIJOINS y NONEQUIJOINS
Los tres pilares para la teoría relacional son selección, proyección y unión. Este capítulo se
centra en la aplicación práctica de la unión. Registros de tablas diferentes se asocian unas a
otras mediante uniones (joins). Muchos modelos de datos se diseñan para poder usar esta
herramienta, tales como los esquemas en estrella o la tercera forma normal.

Las tablas se pueden unir de varias formas. La más común se llama equijoin. Un registro se asocia con
uno o más registros de otra tabla basándose en la igualdad de los valores o expresiones comprendidas
en dicha columna. Las tablas se pueden también unir con nonequijoin. En este caso, se asocian registros
de tablas diferentes dentro de rangos definidos por operadores de desigualdad. En ambos casos, se
excluyen los registros nulos o que tienen distintas entradas para uniones comunes.
Algo menos común es asociar registros de la misma tabla. Se usa con columnas que tienen relaciones
lógicas y normalmente jerárquicas entre ellas. Este tipo de unión se llama self-join.

Además, existe un OUTER JOIN para buscar registros sin uniones si es necesario.
Un CROSS JOIN o producto cartesiano se forma cuando cada registro de una tabla se une a todos los
registros de otra. Esta unión normalmente es el resultado de uniones inadecuadas, aunque puede ser
intencionado en alguna ocasión.

7.1.1. Tipos de JOIN


Hay dos tipos básicos de JOIN: equijoin y nonequijoin. Son los que se usan de forma más habitual.
Aunque normalmente las uniones se pueden hacer entre muchas tablas, en las próximas páginas las
explicaciones se centraran en uniones entre sólo dos tablas, para simplificar los ejemplos e ilustrar los
conceptos.
La primera tabla se llama fuente (source) y la segunda objetivo (target). Los registros de ambas tablas
pueden tener una o más columnas. Por ejemplo, se asume que la tabla fuente es COUNTRIES y la tabla
objetivo REGIONS, ambas del esquema HR.
La tabla COUNTRIES contiene tres columnas: COUNTRY_ID, COUNTRY_NAME y REGION_ID. La tabla
REGIONS contiene dos columnas: REGION_ID y REGION_NAME. Los datos de estas tablas se relacionan
mediante la columna común REGION_ID. Considérese las siguientes consultas:

Consulta 1: SELECT *
FROM countries
WHERE country_id='CA';
Consulta 2: SELECT region_name FROM regions
WHERE region_id=2;

El nombre de la región a la que un país pertenece se puede determinar al obtener su REGION_ID. Este
valor se usa para unirlo con el registro en la tabla REGIONS con el mismo REGION_ID. La Consulta 1

207/306
SQL

recupera los valores de la columna asociada a la tabla COUNTRIES donde COUNTRY_ID = “CA” y su
REGION_ID es 2. La Consulta 2 busca el REGION_NAME “Americas” de la tabla REGIONS para el registro
con REGION_ID =2. Hacer un equijoin hace fácil la recuperación de las columnas de varias tablas
utilizando una única consulta.

Las tablas fuente y objetivo pueden intercambiar su naturaleza, siendo REGIONS la fuente y COUNTRIES
el objetivo.
Considere las dos consultas siguientes:

Consulta 1: SELECT *
FROM regions
WHERE region_name='Americas';
Consulta 2: SELECT country_name
FROM countries
WHERE region_id=2;

La Consulta 1 busca un registro con un REGION_ID = 2. Al unir de esta forma, la pregunta equivalente
sería: ¿Qué países pertenecen a la región Americas? La respuesta de la Consulta 2 son 5
COUNTRY_NAME: Argentina, Brasil, Canadá, México y Estados Unidos de América. Estos resultados se
podrían obtener de una consulta única que uniera ambas tablas. A continuación, se introduce la sintaxis
para hacer equijoins, nonequijoins, outer joins, ycross joins.

a. INNER JOIN
La cláusula INNER JOIN se implementa usando tres posibles cláusulas JOIN: NATURAL JOIN, USING y
ON.
Cuando las tablas fuente y objetivo comparten nombres idénticos de columnas, es posible hacer una
unión natural entre ellas sin especificar una columna de unión. En este escenario, las columnas de mismo
nombre se asocian automáticamente. Los registros con valores de columnas coincidentes en ambas
tablas son recuperados como resultados. Ambas tablas del ejemplo comparten la columna REGION_ID y
se pueden unir de forma natural tal y como se ve en la siguiente figura.

208/306
SQL

Las palabras clave NATURAL JOIN hacen que Oracle identifique las columnas con igual nombre entre las
tablas. Seguidamente, se realiza implícitamente la operación JOIN. En la primera consulta, REGION_ID
se identifica como la única columna común; REGIONS es la tabla fuente y aparece en la cláusula FROM;
la tabla objetivo es COUNTRIES. Para cada registro de las tablas REGIONS se busca una coincidencia
para REGION_ID en la tabla COUNTRIES. Se construye una unión provisional que contiene los registros
que coinciden con la condición de unión. Entonces, este conjunto se restringe con la cláusula WHERE.
Dado que “COUNTRY_NAME” tiene el valor “Canada”, la consulta devuelve “Americas”, que es el valor de
la “REGIONS” a la que pertenece.
La segunda consulta muestra una unión natural donde COUNTRIES es la tabla fuente. El valor de
REGION_ID para cada registro en la tabla COUNTRIES se identifica y se busca una coincidencia en la

209/306
SQL

tabla REGIONS. Si se encuentran, los resultados provisionales quedaran limitados por las condiciones en
la cláusula WHERE. Los COUNTRY_NAME de los registros con “Americas” como REGION_NAME son
recuperados como conjunto de resultados.
A veces se necesita ejercer más control sobre qué columnas se usan para las uniones. Cuando existen
columnas con nombres idénticos, pero se quieren excluir como columnas de unión, se debe usar le
formato JOIN … USING. Recuerde que Oracle no impone ninguna regla que diga que las columnas con el
mismo nombre deben tener necesariamente una relación. La tercera consulta especifica explícitamente
que la tabla REGIONS debe unirse con COUNTRIES a través de la columna unión REGION_ID. Esta
sintaxis permite INNER JOINs sobre columnas especificas en vez de sobre cualquier columna que tenga
el mismo nombre.

La cuarta consulta hace una demostración de cómo usar el formato JOIN_ON de los INNER JOIN, que
permite declarar explícitamente las columnas unión. Este formato no depende de que las columnas
compartan nombre puesto que es mucho más general y de hecho es el método INNER JOIN más usado.

Cuidado cuando se usan uniones naturales puesto que los diseñadores de bases de datos
pueden asignar nombres iguales a columnas únicas. Estas columnas puede que lleven
nombres como ID o SEQ_NO. Si una unión natural se lleva a cabo entre dichas tablas, pueden
aparecer resultados inesperados o ambiguos.

b. OUTER JOIN
No todas las tablas comparten una relación perfecta, donde cada registro de la tabla fuente se puede
unir con al menos un registro de la tabla objetivo. Ocasionalmente se requiere que se recuperen registros
de una columna de registros dispares.

Suponga que las tablas EMPLOYEES y DEPARTMENTS se unen a través de valores comunes
DEPARTMENT_ID. Los registros de EMPLOYEES con DEPARTMENT_ID = Null serán excluidos junto con
los valores ausentes de la tabla DEPARTMENTS. Un OUTER JOIN consigue recuperar esos registros
“perdidos”.

c. CROSS JOIN
Un CROSS JOIN o producto Cartesiano recibe su nombre de matemáticas, donde también se refiere a un
producto cruzado entre dos conjuntos de matrices. Esta unión crea un registro de salida para cada
combinación de tabla fuente y objetivo.

Si la tabla fuente tiene 3 registros y la tabla objetivo tiene 4, la unión cruzada resultará en (3 x 4 =12)
registros de salida. Considérese la siguiente figura, donde los primeros dos recuentos se producen entre
las tablas COUNTRIES y REGIONS con 25 y 4 registros respectivamente. En la consulta 3 el número de
registros recuperado por el producto cruzado de estas tablas es 100. En la consulta 4 se recuperarían
100 registros si la cláusula WHERE no estuviera definida. Cada uno de los cuatro registros de la tabla
REGIONS se une con una de la tabla COUNTRIES. Cada registro obtenido contiene todas las columnas
de ambas tablas.

210/306
SQL

d. Sintaxis de Oracle para JOIN


La sintaxis tradicional de Oracle para INNER JOINS, OUTER JOINS y JOINS Cartesianos se puede ver en
las siguientes consultas:

Consulta 1: SELECT regions.region_name, countries.country_name


FROM regions, countries
WHERE regions.region_id=countries.region_id;
Consulta 2: SELECT last_name, department_name
FROM employees, departments
WHERE employees.department_id (+) = departments.department_id;
Consulta 3: SELECT *

211/306
SQL

FROM regions,countries;

La Consulta 1 procede a realizar un INNER JOIN al especificar la condición de unión mediante la cláusula
WHERE. Esta es la diferencia más importante con respecto a la sintaxis tradicional y la sintaxis de uniones
ANSI SQL. Nótese que renombrar una columna como TABLE.COLUMN_NAME hace que desaparezca la
ambigüedad entre columnas con el mismo nombre. Este tipo de notación se va a desarrollar en
profundidad durante este capítulo.

La Consulta 2 especifica la unión entre ambas tablas mediante la condición WHERE. Hay un signo (+) a
la izquierda del símbolo igual que indica a Oracle que se debe proceder a realizar un RIGHT OUTER JOIN.
Esta consulta devuelve el LAST_NAME de los empleados y los asocia a los valores de DEPARTMENT_NAME.
Además, el OUTER JOIN devuelve DEPARTMENT_NAME de los registros con valores de DEPARTMENT_ID
no asignados a ningún registro de empleado. La Consulta 3 realiza un producto Cartesiano o CROSS JOIN
al excluir la condición de unión.

7.1.2. Uniendo tablas utilizando la sentencia ANSI SQL (Opcional)


Antes de Oracle 9i, la sintaxis de unión tradicional era el único lenguaje válido para unir tablas. Desde
entonces, Oracle ha introducido un nuevo lenguaje que cumple con la norma de los últimos estándares
ANSI. No ofrece beneficios de rendimiento sobre la sintaxis tradicional. Las uniones interiores, exteriores
y transversales pueden ser escritas utilizando tanto ASI SQL como el tradicional Oracle SQL.

La forma original de la sentencia SELECT utilizada en ANSI SQL es la siguiente:

SELECT [Link], [Link]


FROM table1
[NATURAL JOIN table2] |
[JOIN table2 USING (column_name)] |
[JOIN table2 ON (table1.column_name = table2.column_name)] |
[LEFT | RIGHT | FULL OUTER JOIN table2
ON (table1.column_name = table2.column_name)]|
[CROSS JOIN table2];

Esto se analizará y se explicarán los ejemplos en las siguientes secciones. La forma general de la sintaxis
tradicional de Oracle, relevante para las uniones es la siguiente:

SELECT [Link], [Link] FROM table1, table2 [WHERE (table1.column_name =


table2.column_name)] |
[WHERE (table1.column_name(+)= table2.column_name)] |
[WHERE (table1.column_name)= table2.column_name) (+)] ;

Si en las condiciones de la cláusula WHERE no se especifican uniones o hay menos de N-1, donde N se
refiere al número de tablas de la consulta, se realiza una unión cartesiana o cruzada. Si se especifica un
número adecuado de condiciones de unión, entonces la primera cláusula condicional opcional
especifica una unión interna, mientras que las segundas dos cláusulas opcionales especifican la sintaxis
para las uniones externas derecha e izquierda.

7.1.3. Clasificando nombres de columna ambiguos


Las columnas con los mismos nombres pueden aparecer en tablas implicadas en una unión. Las columnas
llamadas DEPARTMENT_ID y MANAGER_ID se encuentran en las tablas EMPLOYEE y DEPARTMENTS. La

212/306
SQL

columna REGION_ID está presente tanto en la tabla de REGIONS y COUNTRIES. Hacer una consulta de
alguna de estas columnas resulta problemática cuando Oracle no puede resolver su origen. Las columnas
con nombres únicos a través de las tablas involucradas en un JOIN no causan ambigüedad ya que Oracle
puede resolver fácilmente su tabla fuente.

El problema de los nombres de columna ambiguos, se aborda con notación por puntos. Una columna
puede ir precedida por el nombre de su tabla y un punto o símbolo para designar su origen, esto lo
diferencia de una columna con el mismo nombre en otra tabla. La notación de puntos se puede utilizar
en consultas que involucren cualquier número de tablas. La referencia a algunas columnas mediante
notación por puntos no implica que todas las columnas deban ser referenciadas de esta manera.

La notación de puntos se puede mejorar añadiendo un alias a la tabla. Un alias en la tabla proporciona
un nombre alternativo, normalmente más corto, para una tabla.
Una columna puede ser referenciada como TABLE_NAME.COLUMN_NAME o
TABLE_ALIAS.COLUMN_NAME.

Como se puede observar en la imagen anterior a la tabla EMPLOYEES se le referencia con el nombre
corto EMP mientras que a la tabla DEPARTMENTS no se le añade ningún alias. La cláusula SELECT hace
referencia a la EMPLOYEE_ID y MANAGER_ID como EMP.EMPLOYEE_ID y EMP.MANAGER_ID. Calificar la
columna EMPLOYEE_ID usando notación por puntos es innecesario porque sólo hay una columna con
este nombre entre las dos tablas. La columna MANAGER_ID debe estar calificada para evitar
ambigüedades ya que aparece en ambas tablas. Dado que se aplica el formato JOIN...USING, sólo se
utiliza DEPARTMENT_ID como columna de unión. Si se empleara un NATURAL JOIN, se utilizarían las
columnas DEPARTMENT_ID y MANAGER_ID. Si la columna MANAGER_ID no estaba cualificada, un error

213/306
SQL

"ORA-00918: column ambiguously defined " será devuelto. Si se creara un alias en DEPARTMENT_ID, se
produciría un error “ORA-25154: column part USING clause cannot have qualifier”.
SQL Developer proporciona el título MANAGER_ID a la primera referencia hecha en la cláusula SELECT.
La cadena "_1" se añade automáticamente a la segunda referencia, creando el título MANAGER_ID_1.
Las referencias de columna que califican con notación de puntos para indicar la tabla de origen
de una columna tienen un beneficio de rendimiento. Se ahorra tiempo porque Oracle es
dirigido instantáneamente a la tabla apropiada y no tiene que resolver el nombre de la tabla.

7.1.4. La cláusula NATURAL JOIN


La sintaxis general para la cláusula NATURAL JOIN es la siguiente:

SELECT [Link], [Link] FROM table1


NATURAL JOIN table2;

La unión natural identifica las columnas con nombres comunes en table1 y table2 e implícitamente une
las tablas utilizando todas estas columnas. Las columnas en la cláusula SELECT puede ser calificada
utilizando la notación punto; a menos que sean una de las columnas de unión. Considérense las
siguientes consultas:

Consulta 1: SELECT *
FROM locations
NATURAL JOIN countries;
Consulta 2: SELECT *
FROM locations, countries

WHERE locations.country_id = countries.country_id;


Consulta 3: SELECT *
FROM jobs
NATURAL JOIN countries;
Consulta 4: SELECT *
FROM jobs, countries;

La unión natural identifica columnas con nombre comunes entre las dos tablas. En la Consulta 1,
COUNTRY_ID aparece en ambas tablas y se convierte en la columna de unión. La Consulta 2 es escrita
siguiendo la sintaxis tradicional de Oracle y recupera los mismos registros que la Consulta 1. A menos
que se esté familiarizado con las columnas de la tabla origen y destino las uniones naturales deben ser
utilizadas con precaución, ya que las condiciones de unión se forman automáticamente entre todas las
columnas con nombres compartidos.
La Consulta 3 realiza una unión natural entre las tablas JOBS y COUNTRIES. No hay columnas con
nombres idénticos, resultando entonces un producto cartesiano. La Consulta 4 es equivalente a la
Consulta 3, y la unión cartesiana es realizada utilizando la sintaxis tradicional de Oracle.
La unión natural es simple, pero sufre el riesgo de que dos columnas con el mismo nombre no tengan
ninguna relación y ni siquiera tengan tipos de datos compatibles. En la imagen siguiente, las tablas
COUNTRIES, REGIONS y SALE_REGIONS están detalladas. La tabla SALES_REGIONS estaba construida
para ilustrar el siguiente punto importante: Aunque ésta tiene REGION_ID en común con la tabla
COUNTRIES, éstos no pueden ser unidos naturalmente porque sus tipos de datos son incompatibles. Los
tipos de datos de las columnas COUNTRIES.REGION_ID y SALES_REGIONS.REGION_ID son NUMBER y

214/306
SQL

VARCHAR2, respectivamente. El dato tipo carácter no puede ser convertido implícitamente a dato tipo
numérico y aparece el error “ORA-01722: invalid number”. La columna REGIONS.REGION_ID es de tipo
NUMBER y su dato está relacionado con el dato de la tabla COUNTRIES. Por lo tanto, la unión natural
entre las tablas REGIONS y COUNTRIES se realiza perfectamente.

215/306
SQL

7.1.5. La cláusula JOIN USING


El formato de la sintaxis de la cláusula JOIN USING es el siguiente:

SELECT [Link], [Link]

216/306
SQL

FROM table1
JOIN table2 USING (join_column1, join_column2…);

Mientras que la unión natural contiene la palabra clave NATURAL en su sintaxis, la sintaxis JOIN...USING
no la contiene. Se produce un error si las palabras clave NATURAL y USING aparecen en la misma cláusula
de unión. La cláusula JOIN...USING permite una o más columnas de EQUIJOIN que se especificarán
explícitamente entre paréntesis después de la palabra clave USING. Esto evita los defectos asociados
con la unión natural. Muchas situaciones exigen que las tablas se unan sólo en ciertas columnas y este
formato satisface este requisito. Considerese las siguientes consultas:

Consulta 1: SELECT *
FROM locations
JOIN countries
USING (country_id);
Consulta 2: SELECT *
FROM locations, countries
WHERE locations.country_id = countries.country_id;

La Consulta 1 especifica que las tablas LOCATIONS y COUNTRIES deben unirse en valores de columna
COUNTRY_ID comunes. Todas las columnas de estas tablas se recuperan para los registros con valores
de columna de unión coincidentes. La Consulta 2 muestra una consulta especificada tradicionalmente
que recupera los mismos registros que la Consulta 1. Las columnas de unión especificadas con la sintaxis
JOIN...USING no se pueden clasificar utilizando nombres de tabla o alias cuando se hace referencia a
ellos en las cláusulas SELECT y JOIN.
Dado que esta sintaxis de unión excluye potencialmente algunas columnas con nombres idénticos de la
cláusula de unión, estas deben ser clasificadas si se hace referencia a ellas para evitar ambigüedades.
Como se muestra en la siguiente figura, las tablas JOB_HISTORY y EMPLOYEES se unieron basándose en
la presencia de valores iguales en sus columnas JOB_ID y EMPLOYEE_ID. Se recuperan los registros que
se ajustan a esta condición de unión.
Estas tablas comparten tres columnas con nombres idénticos. En este ejemplo, sólo dos de ellas se
especifican como columnas de unión. Observe que, aunque la tercera columna con el mismo nombre es
DEPARTMENT_ID, está clasificada con un alias de tabla para evitar ambigüedades, mientras que las
columnas de unión especificadas en la cláusula SELECT no pueden clasificarse con alias de tabla.

217/306
SQL

7.1.6. La cláusula JOIN ON


El formato de la sintaxis para la cláusula JOIN ON es el siguiente:

SELECT [Link], [Link] FROM table1 JOIN table2 ON (table1.column_name =


table2.column_name);

Las cláusulas NATURAL JOIN y JOIN…USING dependen de las columnas unidas mediante nombres
idénticos de columna. La cláusula JOIN…ON permite la especificación explícita de las columnas unidas,
independientemente de sus nombres de columna. Ésta es la forma más flexible y abierta de utilizar las
cláusulas de unión. Las palabras claves ON y NATURAL no pueden aparecer juntas en una misma cláusula
JOIN. Las columnas EQUIJOIN quedan perfectamente calificadas como table1.column1 = table2.column2
y están especificadas, opcionalmente, entre paréntesis después de la palabra clave ON.
Las siguientes consultas ilustran la cláusula JOIN…ON:

Consulta 1: SELECT *
FROM departments d
JOIN employees e ON (e.employee_id=d.department_id);
Consulta 2: SELECT *
FROM employees e, departments d
WHERE e.employee_id=d.department_id;
La Consulta 1 recupera todos los valores de las columnas de las tablas DEPARTMENTS y EMPLOYEES para
los registros que reúnen una condición EQUIJOIN. Esta condición está satisfecha por los valores de
EMPLOYEE_ID coincidiendo con los valores DEPARTMENT_ID en la tabla DEPARTMENTS. La sintaxis
tradicional de Oracle en la Consulta 2 devuelve el mismo resultado que la Consulta 1. Nótese las
similitudes entre la condición de unión tradicional especificada en la cláusula WHERE y la condición de
unión especificada después de la palabra clave ON.
En la imagen siguiente, la columna STAR_DATE en la tabla JOB_HISTORY está unida a la columna
HIRE_DATE de la tabla EMPLOYEES. Este EQUIJOIN recupera los detalles de los empleados que trabajaron

218/306
SQL

para la organización y cambiaron de trabajo.

Problema Solución

Se requiere recuperar Sí. La unión de múltiples tablas


información de múltiples tablas, produce, en última instancia, un
grupos de resultados y utilizar conjunto de datos que comprende
una función de agregado en ellos. uno o más registros y columnas.
Una vez creado el conjunto de

¿Se puede utilizar un grupo de datos, las funciones de agregado


funciones contra datos de varias lo tratan como si los datos
tablas origen? procedieran de una fuente.

219/306
SQL

Al unir dos tablas, existe el riesgo No. Oracle no sabe a partir de qué
de que entre ellas haya nombres tablas se originan dichas
de columnas comunes. ¿Sabe columnas, y se produce un error.
Oracle de qué tablas obtener los Las referencias ambiguas a
datos si tales columnas están columnas pueden evitarse
presentes en la lista SELECT? utilizados clasificadores. Los
clasificadores emplean la notación
punto para aclarar la tabla de
origen de una columna.

La cláusula NATURAL JOIN es Sí. La cláusula recomendada para


utilizada para unir registros de unir dos tablas basadas en una o
dos tablas basadas en columnas más columnas con idénticos
con nombres comunes que nombres es JOIN…USING. Unos
comparten valores idénticos. ¿Es paréntesis siguen a la cláusula
posible unir dos tablas basadas USING en la que se especifican las
en algunas de las columnas columnas unidas no clasificadas.
compartidas y no en todas?

7.1.7. JOIN de N-Tablas y condiciones adicionales del JOIN


Las uniones analizadas se han demostrado solamente mediante la interacción de dos tablas. Sin embargo,
no hay restricción en cuanto al número de tablas que se pueden unir. La tercera forma normal consiste
en un conjunto de tablas conectadas a través de una serie relaciones mediante claves primarias y
foráneas.

Navegar sobre este tipo de relaciones mediante uniones permite una recuperación de datos coherente y
fiable. Sin embargo, hay momentos en los que estas relaciones no están definidas entre tablas y, aun
así, pueden ser unidas, aunque el resultado no se beneficie de la integridad referencial impuesta por la
base de datos. Cuando existen uniones múltiples en una consulta, se evalúan de izquierda a derecha.
Considérese la siguiente consulta que mezcla uniones naturales y uniones de Oracle:

SELECT r.region_name, c.country_name, [Link], d.department_name


FROM departments d
NATURAL JOIN locations l, countries c, regions r;

La unión natural entre DEPARTMENTS y LOCATIONS crea un resultado provisional que consiste en un
conjunto de 27 registros dado que se unen de manera implícita sobre la columna LOCATION_ID.
Seguidamente, este conjunto se une mediante un producto Cartesiano a la tabla COUNTRIES dado que
la condición de unión no se ha especificado. El resultado provisional se une a los 25 registros de la tabla
COUNTRIES y devuelve un nuevo resultado provisional de 675 (27 X 25) registros y tres columnas:

DEPARTMENT_NAME, CITY y COUNTRY_NAME. Este conjunto se une a su vez a la tabla REGIONS. Una
vez más, el producto cartesiano se aplica dado que no hay ninguna condición para la columna REGION_ID
que lo impida. El resultado final contiene 2700(675 x 4) registros y 4 columnas. La utilización de uniones
naturales y de Oracle de forma conjunta lleva a cometer errores y no se recomienda. En la siguiente
consulta se unen 4 tablas mediante la sintaxis de una unión natural:

SELECT region_id, country_id, c.country_name, [Link], d.department_name


FROM departments d
NATURAL JOIN locations l

220/306
SQL

NATURAL JOIN countries c


NATURAL JOIN regions r;

Tras ejecutar esta petición, se devuelven 27 registros correctamente en el conjunto final de resultados
dado que las columnas de unión se establecen en la sentencia SELECT. La siguiente consulta demuestra
cómo se usaría la cláusula JOIN … ON para obtener las mismas 27 respuestas. En el siguiente ejemplo,
la unión de DEPARTMENTS sobre LOCATIONS no referencia ninguna columna de COUNTRIES o REGIONS,
pero la unión entre ambas puede referirse a cualquier columna de las 4 tablas incluidas en la consulta:

SELECT r.region_name, c.country_name, [Link], d.department_name


FROM departments d
JOIN locations l ON (l.location_id=d.location_id)
JOIN countries c ON (c.country_id=l.country_id)
JOIN regions r ON (r.region_id=c.region_id);

La cláusula JOIN … USING también puede usarse para unir las 4 tablas como se expone seguidamente:

SELECT r.region_name, c.country_name, [Link], d.department_name


FROM departments d
JOIN locations l USING (location_id)
JOIN countries c USING (country_id)
JOIN regions r USING (region_id);

La cláusula WHERE se usa para especificar las condiciones que restringen el conjunto de resultados de
una consulta tanto si contiene uniones como si no. La cláusula JOIN … ON se usa también para especificar
condiciones que limitan los resultados creados por la unión. Considérese las siguientes dos consultas:

Consulta 1: SELECT d.department_name


FROM departments d
JOIN locations l ON (l.LOCATION_ID=d.LOCATION_ID)
WHERE d.department_name LIKE 'P%';
Consulta 2: SELECT d.department_name
FROM departments d
JOIN locations l ON (l.LOCATION_ID=d.LOCATION_ID
AND d.department_name like 'P%');
La Consulta 1 utiliza una cláusula WHERE que restringe los 27 registros creados al equiparar las tablas
DEPARTMENTS y LOCATIONS basándose en sus valores de LOCATION_ID a los tres que contiene
DEPARTMENT_ID, que comienzan por “P”.
La Consulta 2 implementa la condición entre paréntesis de la sub-cláusula ON y recupera los mismos tres
registros.
En la siguiente imagen se ve la unión de 5 tablas, cuyo resultado es una lista que presenta los empleados
que más dinero ganan e información geográfica acerca de sus departamentos.

221/306
SQL

Hay tres formatos para equijoin o inner join. La unión natural utiliza la cláusula NATURAL JOIN
y une dos tablas basadas en todas las columnas con nombres compartidos. Los otros dos
formatos usan las cláusulas JOIN … USING y JOIN … ON. Preste atención a la sintaxis dado
que una cláusula de unión tal que: SELECT * FROM TABLE 1 NATURAL JOIN TABLE 2 USING
(COLUMN) podría parecer correcta, pero es sintácticamente incorrecta. Se debe recordar que
el uso de las palabras clave USING, ON y NATURAL son mutuamente restrictivas en el contexto
de la misma cláusula de unión.

7.1.8. NONEQUIJOINS
Los NONEQUIJOINS coinciden con los valores de las columnas de diferentes tablas basadas en una
expresión de desigualdad. El valor de la columna de unión en cada registro de la tabla de origen se
compara con los valores correspondientes de la tabla de destino. Una coincidencia se encuentra si la
expresión usada en la unión, basada en un operador de desigualdad, se evalúa como verdadera.

Un NONEQUIJOIN se especifica usando la sintaxis JOIN...ON, pero la condición JOIN contiene un operador
de desigualdad en lugar de un signo igual.
El formato de la sintaxis para una cláusula NONEQUIJOIN es el siguiente:

SELECT [Link], [Link]

222/306
SQL

FROM table1
[JOIN table2 ON (table1.column_name < table2.column_name)]|
[JOIN table2 ON (table1.column_name > table2.column_name)]|
[JOIN table2 ON (table1.column_name <= table2.column_name)]|
[JOIN table2 ON (table1.column_name >= table2.column_name)]|
[JOIN table2 ON ([Link] BETWEEN table2.col1 AND table2.col2)]

Considere los 16 registros devueltos por la consulta en la siguiente imagen. La tabla EMPLOYEES no está
ajustada a la tabla JOBS basada en la condición de igualdad de oportunidades
(2*[Link]<J.MAX_SALARY). La tabla JOBS almacena los rangos salariales para empleados con
diferentes funciones en la empresa. El valor SALARY para cada registro de empleado se duplica y se
compara con todos los valores MAX_SALARY de la tabla JOBS. Si la condición de unión se evalúa a TRUE,
se devuelve el registro.

Las NONEQUIJOINS no son tan usadas como las EQUIJOIN. El operador BETWEEN aparece a
menudo con condiciones NONEQUIJOIN. Es más sencillo usar un operador BETWEEN entre dos
condiciones NONEQUIJOIN basado en operadores: <= (menor o igual) y >= (mayor o igual)

223/306
SQL

7.2. Unir una tabla a sí misma utilizando SELF-JOIN


Guardar datos jerárquicos en una única tabla relacional puede conseguirse asignando al menos dos
columnas por registro. Una columna guarda el identificador del registro padre y la segunda el identificador
del registro en cuestión. Al hacer esto, Oracle deberá unir una tabla a sí misma.

7.2.1. Uniendo una tabla a sí misma utilizando la cláusula JOIN … ON


Supóngase que se necesita guardar un árbol familiar en una tabla relacional. Una opción para solucionar
esta necesidad sería utilizar una tabla llamada FAMILY, con columnas llamadas ID, NAME, MOTHER_ID y
FATHER_ID, donde cada registro guarda una clave única, el nombre de la persona, y los ID de sus padres.

Cuando dos tablas se unen, cada registro de la tabla fuente está supeditada a la condición de unión con
los registros de la tabla objetivo. Si la condición es VERDADERA, se recupera el registro unido,
consistiendo en columnas de ambas tablas.
Cuando las columnas unión se originan de la misma tabla, un self-join es necesario. Conceptualmente,
la tabla fuente se duplica para crear la tabla objetivo. Posteriormente, funciona como una unión normal
entre ambas tablas. Internamente, Oracle no duplica la tabla y esta descripción es meramente
proporcionada para explicar el concepto. Considere las siguientes tres consultas:

Consulta 1: SELECT id, name, father_id


FROM family;
Consulta 2: SELECT name
FROM family
WHERE id=&father_id;
Consulta 3: SELECT [Link] Dad, [Link] Child
FROM family f1
JOIN family f2 ON([Link]=f2.father_id);

Para identificar el padre de una persona en la tabla FAMILY se puede usar la Consulta 1 para obtener los
valores de ID, NAME y FATHER_ID de la persona.
En la Consulta 2, el valor de FATHER_ID que se obtiene en la primera consulta, se puede sustituir para
obtener el NAME del padre. Nótese que ambas consultas obtienen la información de la tabla FAMILY.
En la Consulta 3 se procede a realizar un self-join utilizando la cláusula JOIN … ON al renombrar la tabla
FAMILY como f1 y f2. Oracle las trata como tablas diferentes, aunque ambas apuntan a la misma tabla
física. F1 se designa como tabla fuente, mientras que f2 es la tabla objetivo.
La condición en la cláusula ON tiene el formato source.child_id=target.parent_id. En la siguiente tabla
se ven las tres formas de self-join para la misma tabla:

224/306
SQL

225/306
SQL

7.3. Visualización de datos que no cumplen una condición JOIN utilizando OUTER JOIN
Los equijoins emparejan los registros entre dos tablas basadas en la igualdad de los datos de las
columnas almacenados en cada tabla. Los nonequijoins se basan en registros coincidentes entre tablas
basadas en una condición de unión que contiene una expresión de desigualdad. Por lo general, no se
requieren registros de la tabla destino sin una columna de unión coincidente en la tabla origen. Cuando
éstos son requeridos, sin embargo, se utiliza un OUTER JOIN para recogerlos. Se pueden utilizar varias
variaciones de OUTER JOIN dependiendo de si faltan datos en la columna unión de la tabla origen o de
la tabla destino o de ambos. Estas técnicas OUTER JOIN se describen a continuación:

• INNER JOIN vs. OUTER JOIN


• LEFT OUTER JOIN
• RIGHT OUTER JOIN FULL OUTER JOIN

7.3.1. Inner vs Outer joins


Cuando se realizan equijoins y nonequijoins, los registros de las tablas origen y destino se emparejan
utilizando una condición JOIN formulada con operadores de igualdad y de desigualdad, respectivamente.
Éstas se denominan inner joins o uniones internas. Un outer join se realiza cuando se recuperan, además,
los registros que no han sido devueltos por una unión interna (es decir, se recuperan TAMBIÉN los
registros que no cumplen las condiciones JOIN formuladas con operadores de igualdad y de desigualdad
respectivamente).
Dos tablas a veces comparten un detalle maestro o relación padre-hijo. En el ejemplo del esquema HR
hay varios pares de tablas con tal relación. Una pareja son las tablas DEPARTMENTS y EMPLOYEES. La
tabla DEPARTMENTS almacena una lista maestra de valores de DEPARTMENT_NAME y DEPARTMENT_ID.
Cada registro de la tabla EMPLOYEES tiene una columna restringida DEPARTMENT_ID en la que cada
valor existe en la tabla DEPARTMENTS o es nulo. Esto lleva a una de las tres opciones siguientes. La
cuarta opción podría ocurrir si la restricción entre las tablas es eliminada.

1. Un registro de empleado tiene un valor de DEPARTMENT_ID que coincide con un registro de la


tabla DEPARTMENT.
2. Un registro de empleado tiene un valor nulo en la columna DEPARTMENT_ID.
3. Hay registros en la tabla DEPARTMENTS con valores de DEPARTMENT_ID que no están
almacenados en ningún registro de empleados.
4. Un registro de empleado tiene un valor de DEPARTMENT_ID que no aparece en la tabla
DEPARTMENTS.
La primera opción describe un INNER JOIN entre dos tablas. La segunda y tercera opción causa muchos
problemas. Unir las tablas EMPLOYEES y DEPARTMENTS mediante la columna DEPARTMENT_ID debe dar
como resultado que los registros con valores de DEPARTMENT_ID nulos queden excluidos. Un OUTER
JOIN puede ser utilizado para incluir estos registros “huérfanos” en el conjunto de resultados. La cuarta
opción raramente ocurre en una base de datos bien diseñada, porque las restricciones de claves ajenas
deberían evitar la inserción de registros ‘hijos’ sin valores ‘padres’. Ya que este registro será excluido por
un INNER JOIN, éste debe recuperarse utilizando un OUTER JOIN.
Un LEFT OUTER JOIN entre las tablas origen y destino devuelve los resultados de un INNER JOIN, así
como los registros de la tabla origen excluidos por ese INNER JOIN. Un RIGHT OUTER JOIN entre las
tablas origen y destino devuelve los resultados de un INNER JOIN, así como los registros de la tabla
destinos excluidos por ese INNER JOIN. Si una unión devuelve los resultados de un INNER JOIN, así
como los registros de ambas tablas origen y destino excluidos por ese INNER JOIN, se dirá que se ha
realizado un FULL OUTER JOIN.

226/306
SQL

7.3.2. LEFT OUTER JOIN


El formato de la sintaxis para la cláusula LEFT OUTER JOIN es la siguiente:

SELECT [Link], [Link] FROM table1


LEFT OUTER JOIN table2
ON ([Link] = [Link]);

Un LEFT OUTER JOIN realiza un INNER JOIN de table1 y table2 basado en la condición de unión especifica
después de la palabra clave ON. También se devuelven los registros de la tabla de la izquierda de la
palabra clave JOINexcluidas por no cumplir la condición del JOIN. Considérense las dos siguientes
consultas:

Consulta 1: SELECT e.employee_id, e.department_id EMP_DEPT_ID,


d.department_id DEPT_DEPT_ID, d.department_name
FROM departments d
LEFT OUTER JOIN employees e ON (d.DEPARTMENT_ID=e.DEPARTMENT_ID)
WHERE d.department_name like 'P%';
Consulta 2: SELECT e.employee_id, e.department_id EMP_DEPT_ID,
d.department_id DEPT_DEPT_ID, d.department_name
FROM departments d
JOIN employees e ON (d.DEPARTMENT_ID=e.DEPARTMENT_ID)
WHERE d.department_name like 'P%';

Las Consultas 1 y 2 son idénticas excepto por las cláusulas JOIN, las cuales están formadas por las
palabras claves LEFT OUTER JOIN y JOIN, respectivamente. La consulta 2 realiza un INNER JOIN,
devolviendo 7 registros. Estos registros comparten valores idénticos de DEPARTMENT_ID en ambas
tablas. La Consulta 1 devuelve los mismos 7 registros más un registro adicional. Este registro extra se
obtiene de la tabla que se encuentra a la izquierda de la palabra clave JOIN, que es la tabla
DEPARTMENTS. Este registro contiene detalles del departamento Payroll. El INNER JOIN no incluye este
registro ya que ningún empleado está asignado actualmente a dicho departamento.

En la siguiente imagen se muestra un ejemplo de un LEFT OUTER JOIN. El INNER JOIN produce 27
registros con valores de LOCATION_ID coincidentes en ambas tablas. Se muestran 43 registros en total,
lo que implica que se recuperaron 16 registros de la tabla LOCATIONS, que se encuentra a la izquierda
de la palabra clave JOIN. Ninguna de las filas de la tabla DEPARTMENTS contiene ninguno de estos 16
valores de LOCATION_ID.

227/306
SQL

7.3.3. RIGHT OUTER JOIN


La forma de la sintaxis de la cláusula RIGHT OUTER JOIN es la siguiente:

SELECT [Link], [Link] FROM table1


RIGHT OUTER JOIN table2
ON ([Link] = [Link]);

Un RIGHT OUTER JOIN realiza un INNER JOIN de table1 y table2 basado en la condición de unión
específica después de la palabra clave ON. También se devuelven los registros de la tabla de la derecha

228/306
SQL

de la palabra clave JOINexcluidas por no cumplir la condición del JOIN. Considérese la siguiente consulta:

SELECT e.last_name, d.department_name


FROM departments d
RIGHT OUTER JOIN employees e
ON (e.department_id=d.department_id)
WHERE e.last_name LIKE 'G%';

El INNER JOIN produce 7 registros que contienen detalles sobre los empleados con valores de LAST_NAME
que empiezan por la letra “G”. La tabla EMPLOYEES está a la derecha de la palabra clave JOIN. Cualquier
registro de empleados que no cumpla la condición JOIN está incluido, siempre y cuando cumplan con la
condición de la cláusula WHERE. Además, el RIGHT OUTER JOIN obtiene un registro de EMPLOYEE con
un LAST_NAME que comienza por la letra ‘G’. Este registro actualmente tiene un valor de
DEPARTMENT_ID nulo. El INNER JOIN excluye el registro ya que no se asigna ningún valor de
DEPARTMENT_ID a este empleado.

En la siguiente imagen se muestra un ejemplo de un RIGHT OUTER JOIN entre las tablas JOB_HISTORY
y EMPLOYEES. La tabla EMPLOYEES está a la derecha de la palabra clave JOIN. La palabra clave DISTINCT
elimina combinaciones duplicadas de valores de JOB_ID de las tablas. Los resultados muestran los
trabajos que los empleos que los empleados han dejado históricamente. También se devuelven los
trabajos que ningún empleado ha dejado. Estos tienen valor nulo en la columna “Jobs in JOB_HISTORY”.

229/306
SQL

Hay tres tipos de formatos OUTER JOIN. Cada uno de ellos realiza un INNER JOIN antes de
incluir registros que la condición JOIN excluye. Si se realiza un LEFT OUTER JOIN, entonces
los registros excluidos por el INNER JOIN, a la izquierda de la palabra clave JOIN, también se
devuelven. Si se realiza un RIGHT OUTER JOIN, los registros excluidos por el INNER JOIN, a
la derecha de la palabra clave JOIN, también se devuelven. El FULL OUTER JOIN realiza un
INNER JOIN asi como un LEFT OUTER JOIN y un RIGHT OUTER JOIN.

7.3.4. FULL OUTER JOIN


La forma de la sintaxis de la cláusula FULL OUTER JOIN es la siguiente:

SELECT [Link], [Link] FROM table1


FULL OUTER JOIN table2
ON ([Link] = [Link]);

Un FULL OUTER JOIN devuelve los resultados combinados de un LEFT OUTER JOIN y un RIGHT OUTER
JOIN. Se realiza un INNER JOIN de table1 y table2 antes de que los registros excluidos por la condición
JOIN de ambas tablas sean incluidos en el conjunto de resultados.

230/306
SQL

La sintaxis tradicional de JOIN Oracle no soporta un FULL OUTER JOIN, que se realiza habitualmente por
combinación de resultados de un LEFT OUTER JOIN y un RIGHT OUTER JOIN utilizando el conjunto de
operadores UNION descrito en el capítulo 9. Considérese el FULL OUTER JOIN que se muestra en la
siguiente imagen. La cláusula WHERE, que restringe los resultados a los registros con valores de
DEPARTMENT_ID NULL, muestra los registros huérfanos en ambas tablas. Hay un registro en la tabla
EMPLOYEES que no tiene un valor de DEPARTMENT_ID, y hay 16 departamentos que no tienen ningún
empleado asignado.

Problema y solución
Problema Solución

231/306
SQL

Los datos de las dos tablas que se quieren unir Sí. La cláusula JOIN…ON está prevista para este
están relacionados, pero no comparten ninguna fin. Proporciona una solución flexible y genérica
columna con el mismo nombre. ¿Es posible unir para unir tablas basadas en nombre de columnas
las tablas utilizando columnas que no no idénticos.
comparten el mismo nombre?

Se desea dividir al personal en cuatro grupos Sí. El rango de valores de REGION_ID es de 1 a 4.


con el nombre de cuatro regiones en la tabla Sumar 1 al resto de EMPLOYEE_ID dividido entre
REGIONS. ¿Es posible obtener una lista de 4 crea un valor en el rango de 1 a 4. La asignación
valores EMPLOYEE_ID, LAST_NAME y en cadena de empleados puede hacerse de la
REGION_NAME para cada empleado uniendo las siguiente forma:
columnas EMPLOYEE_ID y REGION_ID en
SELECT LAST_NAME, EMPLOYEE_ID,
cadena?
REGION_NAME

FROM EMPLOYEES JOIN REGIONS


ON (MOD(EMPLOYEE_ID,4)+1= REGION_ID)

Se precisa recuperar una lista de valores de Sí. Dependiendo de a qué lado de la palabra clave
DEPARTMENT_NAME y LAST_NAME para todos JOIN se escriba la tabla DEPARTMENTS, un LEFT
los departamentos, incluyendo aquellos que
OUTER JOIN o un RIGHT OUTER JOIN debe ser
actualmente no tienen empleados asignados.
En tales casos la cadena ‘No Employees’ debe utilizado, ya que ésta es la tabla donde se originan
mostrarse como valor de la columna los registros huérfanos. La siguiente consulta
LAST_NAME. ¿Se puede hacer esto utilizando satisface esta respuesta:
JOIN?
SELECT DEPARTMENT_NAME,
NVL(LAST_NAME, 'No
Employees')

FROM EMPLOYEES RIGHT OUTER JOIN


DEPARTMENTS USING (DEPARTMENT_ID)

7.4. Generar un producto cartesiano de dos o más tablas


Un producto cartesiano de dos tablas se puede explicar conceptualmente como unir cada una de los
registros de una tabla fuente con cada uno de los registros de la tabla objetivo. El número de registros
en el conjunto de resultados es igual al número de registros de la tabla fuente multiplicado por el número
de registros de la tabla objetivo. Los productos cartesianos se pueden ejecutar intencionadamente
utilizando la sintaxis del producto cruzado de ANSI SQL. Esta técnica se describe en la siguiente sección.

7.4.1. Creando productos cartesianos usando CROSS JOIN


El producto cartesiano es un término matemático. Se refiere a un conjunto de datos creados al combinar
los registros de dos o más tablas. Para ejecutar este producto, se utiliza la sintaxis para la unión cruzada.
Ambos términos usualmente se utilizan como sinónimos. De este modo, la sintaxis para la cláusula
CROSS JOIN es tal y como se muestra a continuación:

SELECT [Link], [Link]


FROM table1
CROSS JOIN table2;

Es importante tener en cuenta que no hay ninguna condición especificada utilizando las palabras clave
ON o USING. Un producto Cartesiano asocia de manera libre los registros de la tabla 1 con cada registro

232/306
SQL

de la tabla 2. Si se quisieran introducir limitaciones, debería utilizarse la cláusula WHERE. Si ambas tablas
contienen x e y número de registros, respectivamente, el producto Cartesiano tendría x por y registros.
El resultado de dicho producto se puede usar para identificar registros huérfanos o para generar un
conjunto de datos grande para la comprobación de aplicaciones. Considérese las siguientes consultas:

Consulta 1: SELECT *
FROM jobs
CROSS JOIN job_history;
Consulta 2: SELECT *
FROM jobs j
CROSS JOIN job_history jh
WHERE j.job_id='AD_PRES';

La Consulta 1 toma 19 registros y 4 columnas de la tabla JOBS y 11 registros con 5 columnas de la tabla
JOBS_HISTORY, generando un conjunto de 190 registros con 9 columnas. SQL*Plus presenta las
columnas con los mismos nombres. SQL Developer, sin embargo, añade al final del nombre un guion
bajo y un número para cada nombre de columna compartida y lo utiliza como título.
La columna JOB_ID es común para ambas tablas. Los nombres para SQL*Plus y SQL Developer son
JOB_ID y JOB_ID_1 respectivamente. En la Consulta 2, se genera el mismo producto, pero los 190
registros están filtrados por una cláusula WHERE y solo se recuperan 11 registros.

La siguiente imagen muestra un producto cruzado entre las tablas REGIONS y COUNTRIES. Hay 4
registros en REGIONS y 25 en COUNTRIES. Dado que hay una cláusula WHERE que limita la primera
tabla de 4 a 2 registros, Se obtendrán 50 resultados que se ordenarán alfabéticamente, primero por
REGION_NAME y después por COUNTRY_NAME. Nótese que COUNTRY_NAME aparece repetido para cada
REGION_NAME.

233/306
SQL

Cuando se usa la sintaxis del producto cruzado, se genera intencionadamente un producto


Cartesiano. Sin embargo, en cuanto existen condiciones insuficientes para generar una unión,
automáticamente y de manera implícita, se obtiene este producto también. Las uniones que

234/306
SQL

especifican menos condiciones que N-1, siendo N el número de tablas, o que especifican
condiciones invalidas, reúnen las características necesarias para generar dicho producto de
manera inintencionada. Una unión natural entre tablas que no comparten columnas con el
mismo nombre también genera un producto cartesiano, dado que hay dos tablas, pero hay
menos de una condición de unión disponible.

7.5. Resumen
Escribir sentencias SELECT para acceder a datos desde más de una tabla usando Equijoins y
Nonequijoins

• Los equijoins se producen cuando una consulta obtiene valores de columna de varias tablas en
las que los registros cumplen una condición de acoplamiento basada en la igualdad.
• La unión natural se realiza usando la sintaxis NATURAL JOIN cuando las tablas fuente y destino
están equijoined usando las columnas con idénticos nombres.
• La sintaxis JOIN...USING permite formar una unión interna en columnas específicas con nombres
compartidos.
• La notación de puntos se refiere a clasificar una columna prefijándola con el nombre de su tabla
y un punto o símbolo de punto. Esto designa la tabla que origina una columna y lo diferencia de
columnas con idénticos nombres de otras tablas.
• La cláusula JOIN...ON permite la especificación explícita para unir columnas independientemente
de sus nombres. Esto proporciona un formato de unión flexible.
• Las palabras clave ON, USING y NATURAL son mutuamente excluyentes y por lo tanto no pueden
aparecer juntas en una cláusula de unión.
• Un nonequijoin se realiza cuando los valores de las columnas de unión cumplen la condición de
unión basada en una expresión de desigualdad.

Unir una tabla a ella misma usando SELF_JOIN

• Se requiere una SELF JOIN cuando las columnas de unión se originan en la misma tabla.
Conceptualmente, la tabla fuente se duplica y se crea una tabla destino. El self-join funciona
entonces como una unión regular entre dos tablas discretas.
• El almacenamiento de datos jerárquicos en una tabla relacional requiere un mínimo de dos
columnas por registro. Una columna almacena un identificador del registro padre de la línea y la
segunda almacena el identificador de la línea.

Ver datos que no coinciden con una condición JOIN mediante uniones externas

• Cuando se realizan equijoin y nonequijoin, se emparejan los registros de las tablas fuente y
destino. Estos se denominan INNER JOIN.
• Un OUTER JOIN se realiza cuando los registros, que no son recuperadas por un INNER JOIN, se
incluyen para la recuperación además de los registros recuperados por el INNER JOIN.
• Un LEFT OUTER JOIN entre las tablas origen y destino devuelve los resultados de un INNER JOIN
y los registros que faltan de la tabla origen.
• Un RIGHT OUTER JOIN entre las tablas origen y destino devuelve los resultados de una INNER
JOIN y los registros faltantes excluidas de la tabla destino.

235/306
SQL

• Un FULL OUTER JOIN devuelve los resultados combinados de un LEFT OUTER JOIN y un RIGHT
OUTER JOIN.

Generar un producto cartesiano de dos o más tablas

• Un producto cartesiano es a veces llamado un CROSS JOIN. Es una cuestión matemática que se
refiere al conjunto de datos creados por la fusión de los registros de dos o más tablas.
• El número de registros devueltos de un producto cartesiano es igual al número de registros en
la tabla de origen multiplicado por el número de registros en la tabla de destino.
• Las uniones que especifican menos de N-1 condiciones de unión al unir N tablas, o que especifican
condiciones de unión inválidas, crean productos cartesianos.

8. Utilizando subconsultas para resolver problemas

8.1. Definir subconsultas (Subqueries)


Los seis capítulos anteriores han tratado la instrucción SELECT con bastante detalle, pero en todos los
casos la instrucción SELECT ha sido un comando único e independiente. Este capítulo es el primero que
muestra cómo se pueden combinar dos o más comandos SELECT en una sola sentencia. La primera
técnica (tratada en este capítulo) es el uso de subconsultas. Una subconsulta es una instrucción SELECT
cuya salida se utiliza como entrada a otra instrucción SELECT (o incluso una instrucción DML, como se
muestra en el capítulo 10). La segunda técnica es el uso de operadores de conjuntos, donde los resultados
de varios comandos SELECT se combinan en un conjunto único de resultados.

Se denomina subconsulta a una consulta que está anidada dentro de una instrucción SELECT, INSERT,
UPDATE, o DELETE o dentro de otra subconsulta. Una subconsulta puede devolver un conjunto de
columnas o solo una de ellas a la consulta padre. Se llama subconsultas escalares a aquellas consultas
que devuelven exactamente un valor: sólo un registro o sólo una columna. Una subconsulta escalar se
puede usar en declaraciones SQL donde suele usarse un valor literal.

Los lugares donde una subconsulta puede ser usada dentro de una consulta son los siguientes:

• En la lista SELECT donde se usa para la proyección de una columna.


• En la cláusula FROM
• En la cláusula WHERE
• En la cláusula HAVING
A menudo se hace referencia a una subconsulta como una consulta interna, y la expresión dentro de la
cual ocurre se denomina entonces consulta externa. No hay nada malo en esta terminología, excepto
que se da a entender que solamente se pueden tener dos niveles, interior y exterior. De hecho, la
implementación Oracle de subconsultas no impone ningún límite práctico al nivel de anidamiento. La
profundidad de anidamiento de subconsultas permitida es ilimitada en la cláusula FROM y hasta 255
niveles en la cláusula WHERE.

Una subconsulta puede tener cualquiera de las cláusulas habituales de selección y proyección. Las
siguientes, son cláusulas obligatorias:

• Una lista SELECT

236/306
SQL

• Una cláusula FROM


Las siguientes, son cláusulas opcionales:

• WHERE
• GROUP BY
• HAVING
La subconsulta (o subconsultas) dentro de una sentencia debe ejecutarse antes que la consulta de nivel
superior que la llama, para que los resultados de la subconsulta puedan pasarse al nivel superior.

8.2. Descripción de tipos de problemas que las subconsultas pueden resolver


Hay muchas situaciones en las que se necesitará el resultado de una consulta para usarla como entrada
de otra consulta.

8.2.1. Uso del resultado de una subconsulta para realizar comparaciones


¿Qué empleados tienen un salario por debajo de la media? Esto podría ser resuelto con dos consultas o
usando una única consulta con una subconsulta. El siguiente ejemplo usa dos sentencias:

SELECT avg(salary)
FROM employees;

SELECT last_name
FROM employees
WHERE salary < result_of_previous_query;

Otra forma de hacerlo es usando una subconsulta dentro de una consulta.

SELECT last_name
FROM employees
WHERE salary <
(SELECT avg(salary)
FROM employees);
En este ejemplo, la subconsulta se usa para sustituir un valor en la cláusula WHERE de la consulta
principal: devuelve un solo resultado, este valor se usa para filtrar los registros obtenidos en la consulta
principal.

La subconsulta podría devolver varios registros. Por ejemplo, se podría usar la siguiente consulta para
buscar todos los departamentos que tienen uno o más empleados asignados:

SELECT department_name FROM


departments
WHERE department_id IN
(SELECT DISTINCT(department_id)
FROM employees);

237/306
SQL

En el ejemplo anterior, la subconsulta se utiliza como alternativa a un JOIN. Se podría obtener el mismo
resultado con la siguiente consulta tal y como se muestra en la imagen.

SELECT department_name
FROM departments
JOIN employees
ON employees.department_id = departments.department_id
GROUP BY department_name;

Si una subconsulta puede devolver más de un resultado, el operador de comparación tiene que poder
aceptar varios valores. Estos operadores son IN, NOT IN, ANY y ALL.

Si el operador de comparación es EQUAL, GREATER THAN o LESS THAN (solo pueden aceptar un valor),
la consulta principal fallará.

238/306
SQL

El uso de NOT IN está plagado de problemas debido a la forma que SQL maneja valores nulos
(NULL). Como regla general, no se utiliza NOT IN a menos que sea muy claro que el conjunto
de resultados no devolverá ningún valor NULL.

8.2.2. Transformación en estrella


Una extensión del uso de subconsultas como alternativa a una consulta con JOIN es habilitar la
transformación en estrella, a menudo necesaria en aplicaciones de almacenaje. Considérese la tabla
SALES, que es una tabla grande, del esquema SH de demostración (no es accesible de manera gratuita)
usado para registrar las transacciones de ventas. Cada registro guarda un producto en concreto vendido
a un cliente en particular a través de un canal específico. Estos atributos se identifican con códigos de
consultas usados como claves ajenas en otras tablas donde se describe cada producto, cliente y canal.
Para identificar todas las ventas de un producto llamado “Comic Book Heroes” para clientes en la ciudad
de Oxford a través de Internet, se podría usar la siguiente consulta:

SELECT count(quantity_sold)
FROM sales s, products p, customers c, channels ch
WHERE s.prod_id=p.prod_id
AND s.cust_id=c.cust_id
AND s.channel_id=ch.channel_id
AND p.prod_name='Comic Book Heroes'
AND c.cust_city='Oxford'
AND ch.channel_desc='Internet';

Esta consulta usa la cláusula WHERE para unir las tablas y filtrar los resultados. La siguiente alternativa
que devolverá el mismo resultado como se muestra en la imagen más abajo.

SELECT count(quantity_sold)
FROM sales
WHERE prod_id IN
(SELECT prod_id
FROM products
WHERE prod_name='Comic Book Heroes')
AND cust_id IN
(SELECT cust_id
FROM customers
WHERE cust_city='Oxford')
AND channel_id IN
(SELECT channel_id
FROM channels
WHERE channel_desc='Internet');

239/306
SQL

El pasar de la primera consulta a la segunda es la transformación en estrella. A parte de ser una


estructura más elegante, hay razones técnicas de por qué una base de datos puede ejecutarse de una
manera más eficaz que la primera consulta. Así mismo, las sentencias en estrella son más sencillas de
mantener; es muy simple añadir más dimensiones a la consulta o reemplazar los valores (‘Comic Book
Heroes’, ‘Oxford’ e ‘Internet’) con listas de valores.

Existe un parámetro de configuración (STAR_TRANSFORMATION_ENABLED), que en el caso


de estar activado (true) permitirá al optimizador de consultas de Oracle reescribir código en
consultas estrella.

8.2.3. Generar tabla para hacer SELECT


Las subconsultas también se pueden utilizar en la cláusula FROM, donde a veces se denominan vistas en
línea. Se considera el siguiente problema, basado en el esquema HR: Los empleados se asignan a un
departamento y los departamentos tienen una ubicación. Cada ubicación está en un país. ¿Cómo se
puede encontrar el salario medio del personal de un país, aunque trabaje para diferentes departamentos?
Así:

240/306
SQL

SELECT avg(salary),country_id
FROM (SELECT salary, country_id
FROM employees
NATURAL JOIN departments
NATURAL JOIN locations)
GROUP BY country_id;

La subconsulta construye conceptualmente una tabla con el salario de cada empleado y el país en el que
se encuentra su departamento. La consulta padre se dirige a esta tabla, promediando SALARY y
agrupando por COUNTRY_ID.

8.2.4. Generar valores para proyección (Opcional)


El tercer lugar en el que se puede utilizar una subconsulta es en la lista SELECT de una consulta. ¿Cómo
se puede identificar el salario más alto y la comisión más alta? Además, ¿cuál sería la comisión máxima
pagada si el empleado asalariado más alto también tuviera la tasa de comisión más alta? Así, con dos
subconsultas:

SELECT (SELECT max(salary) FROM employees)*


(SELECT max(commission_pct) FROM employees) / 100
FROM dual;

En esta sentencia, la lista SELECT se rellena con los resultados de las subconsultas. Una subconsulta
utilizada de esta manera debe ser escalar sino la consulta padre fallará con un error.

8.2.5. Generar registros para enviarlos a una sentencia DML (Opcional)


Las sentencias DML se tratan en detalle en el Capítulo 10. Por ahora, considere estos ejemplos:

INSERT INTO sales_hist


SELECT *

FROM sales

WHERE date > sysdate-1;

UPDATE employees
SET salary = (SELECT avg(salary)
FROM employees);

DELETE FROM departments


WHERE department_id NOT IN
(SELECT department_id FROM employees

241/306
SQL

WHERE department_id is not null);

El primer ejemplo, referido al esquema SH, utiliza una subconsulta para identificar un conjunto de
registros en una tabla que se insertarán en otra. El segundo ejemplo utiliza una subconsulta para calcular
el salario medio de todos los empleados y transfiere este valor (una cantidad escalar) a una sentencia
de actualización. El tercer ejemplo utiliza una subconsulta para recuperar todos los DEPARTMENT_ID que
están en uso y pasa la lista a un comando DELETE, que eliminará todos los departamentos que no están
en uso. Se usa la cláusula adicional WHERE en la subconsulta para garantizar que la subconsulta no
devuelva valores NULL. Si no existiera esta cláusula, no se eliminaría ningún registro. Se tiene en cuenta
que no se puede utilizar una subconsulta en la cláusula VALUES de una expresión insertada; esto está
bien:

INSERT INTO dates


SELECT sysdate

FROM dual;

Y esto no está bien:

INSERT INTO dates (date_col)


VALUES
(SELECT sysdate
FROM dual);

8.3. Listar los tipos de subconsultas


Las subconsultas se pueden dividir en tres grupos

• Subconsultas de registro único


• Subconsultas de múltiples registros Subconsultas
correlacionadas

8.3.1. Subconsultas de registro único y múltiples registros


Las subconsultas de registro único devuelven un registro. Un caso especial es la subconsulta escalar, que
devuelve un registro con una columna. Las subconsultas escalares son aceptables (y a menudo muy
útiles) en prácticamente cualquier situación dónde se podría usar una variable, una constante o una
expresión. Las subconsultas de registros múltiples devuelven una colección de registros. Estas consultas
se usan normalmente para generar conjuntos de resultados que enviarán a una sentencia DML o SELECT
para un procesamiento posterior. Ambos tipos de subconsultas, registro único y múltiples registros, serán
evaluadas una vez, antes de que la consulta principal se ejecute.

Las subconsultas de registros únicos y múltiples se pueden utilizar en las cláusulas WHERE y HAVING de
la consulta principal, pero hay restricciones a la hora de usarlos con operadores de comparación. Si el
operador de comparación es cualquiera de la siguiente tabla, la subconsulta tiene que ser una subconsulta
de registro único:

242/306
SQL

Símbolo Significado

= Igual

> Mayor que

>= Mayor o igual

< Menor que

<= Menor o igual

<> Diferente

!= Diferente

Si cualquiera de los operadores de la tabla anterior se encuentra en una subconsulta que devuelve más
de un registro, la consulta fallará. Los operadores de la siguiente tabla se usan en subconsultas de
registros múltiples :

Símbolo Significado

IN Igual a cualquier valor de la lista

NOT IN Diferente a cualquier valor de la lista

ANY Devuelve registros que coinciden con cualquier valor en la


lista y tiene que usarse con un operador de comparación

ALL Devuelve registros que coinciden con todos los valores en la


lista y tiene que usarse junto con un operador de
comparación

8.3.2. Subconsultas correlacionadas


Una subconsulta correlacionada tiene una forma más compleja de ejecución que una de registro único o
de múltiples registros y tiene un potencial mucho mayor. Si una subconsulta hace referencia a columnas
en la consulta principal, su resultado dependerá de la consulta principal. Esto hace imposible evaluar la

243/306
SQL

subconsulta antes de evaluar la consulta padre. En esta consulta, se muestra todos los empleados cuyo
salario es inferior al salario medio:

SELECT last_name
FROM employees
WHERE salary <
(SELECT avg(salary)
FROM employees);

La subconsulta de registro único necesita ser ejecutada una única vez y su resultado se sustituirá en la
consulta principal. Sin embargo, ahora se considera una consulta que muestre a todos los empleados
cuyo salario es inferior a la media de su departamento. En ese caso, la subconsulta tendrá que ejecutarse
para cada empleado para determinar la media del salario para su departamento; la subconsulta necesita
el código del departamento del empleado. Esto se puede hacer de la siguiente forma:

SELECT p.last_name, p.department_id


FROM employees p
WHERE [Link] <
(SELECT avg([Link])
FROM employees s
WHERE s.department_id=p.department_id);

En este ejemplo, la subconsulta referencia a una columna, p.department_id, de la lista de la consulta


principal. De esta forma, indicamos que en lugar de evaluar una única vez la subconsulta, tiene que ser
evaluada una vez por cada registro en la consulta principal. Para ejecutar la consulta, Oracle buscará
cada registro en EMPLOYEES y mientras lo hace, ejecutará la subconsulta usando el DEPARTMENT_ID
del empleado actual.

El flujo de ejecución será el siguiente:

1. Empezará en el primer registro de la tabla EMPLOYEES.


2. Se lee DEPARTMENT_ID y SALARY del registro actual.
3. Se ejecuta la subconsulta usando DEPARTMENT_ID del paso 2.
4. Se compara el resultado del paso 3 con SALARY del paso 2 y se devuelve el registro si SALARY
es menor que el del resultado.
5. Se lee el siguiente registro de la tabla EMPLOYEES.
6. Se repite desde el paso 2.
7.
Las subconsultas de resultado único o las de resultado múltiple se evaluan una vez, antes de evaluar la
consulta de fuera (la principal, normalmente); una subconsulta correlacionada tiene que ser evaluada
una vez para cada registro en la consulta de fuera (la principal, normalmente). Una subconsulta puede
ser de resultado único o múltiple si el operador de comparación es el apropiado.
Las subconsultas correlacionadas pueden ser muy ineficientes, debido a la necesidad de repetir la
subconsulta. Se intentará siempre encontrar un enfoque diferente.

Problema Solución

244/306
SQL

¿Cómo se puede diseñar mejor las Hay dos técnicas comunes: usar una
subconsultas para que no fallen con los agregación por si se obtienen varios
errores “ORA-01427: single-row registros que se reduzca el número a
subquery returns more than one row”? uno, o usar uno de los operadores IN,
ANY, o ALL para que no importe si se
devuelven varios registros. Pero la
solución ideal es utilizar siempre la clave
primaria al identificar el registro a
devolver, no una clave no única.

A veces se puede elegir entre usar una Depende de las circunstancias. No es


subconsulta o usar alguna otra técnica: raro que las diferentes técnicas causen
la transformación en estrella, por un método de ejecución diferente dentro
ejemplo. ¿Cuál es mejor? de la base de datos. Dependiendo de
cómo se configuren la instancia, la base
de datos y las estructuras de datos
dentro de ella, una puede ser mucho más
eficiente que otra. Siempre que surja tal
opción, las afirmaciones deben ser
sometidas a un análisis de eficiencia o
tuning.

8.4. Escribir subconsultas de registro único y registros múltiples


A continuación, se muestran ejemplos de subconsultas de uno y múltiples registros. Se basan en el
esquema de demostración HR. ¿Cómo se averiguaría qué empleados tienen un gerente que trabaja para
un departamento con sede en el Reino Unido? Esta es una posible solución, utilizando subconsultas de
varios registros:

SELECT last_name
FROM employees
WHERE manager_id IN
(SELECT employee_id
FROM employees
WHERE department_id IN
(SELECT department_id
FROM departments
WHERE location_id IN
(SELECT location_id
FROM locations
WHERE country_id='UK')));

245/306
SQL

En el ejemplo anterior, las subconsultas están agrupadas en tres niveles de profundidad. Observe que
las subconsultas utilizan el operador IN porque es posible que las consultas puedan devolver varios
registros.

Se pide que se busque el trabajo con el salario promedio más alto. Esto se puede hacer con una
subconsulta de un solo registro:

SELECT job_title
FROM jobs
NATURAL JOIN employees
GROUP BY job_title
HAVING avg(salary) =
(SELECT max(avg(salary))
FROM employees
GROUP BY job_id);

La subconsulta devuelve un único valor: el salario medio del departamento con el salario medio más alto.
Es seguro utilizar el operador de igualdad para esta subconsulta porque la función MAX garantiza que
sólo se devuelva un registro. Los operadores ANY y ALL son compatibles con la sintaxis, pero su función
puede duplicarse con la de otros operadores más utilizados combinados con agregaciones.

Por ejemplo, en las siguientes dos sentencias, que recuperan todos los empleados cuyo sueldo es superior
al de cualquier empleado del departamento 80, se obtienen conjuntos de resultados idénticos:

SELECT last_name
FROM employees
WHERE salary > ALL
(SELECT salary
FROM employees
WHERE department_id=80);

SELECT last_name
FROM employees
WHERE salary > (SELECT max(salary)
FROM employees
WHERE department_id=80);

La siguiente tabla resume los posibles operadores para ANY y ALL:

Operador Función

< ANY Menor que el más alto

> ANY Mayor que el más bajo

246/306
SQL

= ANY Equivalente a IN

> ALL Mayor que el más alto

< ALL Menor que el más bajo

8.5. Resumen
Definición de subconsulta

• Una subconsulta es una declaración SELECT junto a otra declaración de SQL.


• Las subconsultas pueden ser anidadas dentro de otras.
• Con la excepción de las subconsultas correlacionadas, la subconsultas son ejecutadas antes que
la consulta en la cual están anidadas.

Descripción de los tipos de problemas que las subconsultas pueden solucionar

• La selección de registros de una tabla con una condición que depende de los datos de otra tabla
puede implementarse mediante una subconsulta.
• Los JOINs complejos pueden simplificarse al utilizar subconsultas.
• Las subconsultas pueden añadir valores a la salida de la consulta que las anida, que no están
disponibles desde sus tablas.

Lista de los tipos de subconsultas

• Las subconsultas de registro múltiple pueden devolver varios registros, posiblemente con varias
columnas.
• Las subconsultas de resultado único devuelven un único registro, posiblemente con varias
columnas.
• Una consulta escalar devuelve un único registro; es una subconsulta de registro y columna
únicos.
• Una subconsulta correlacionada es ejecutada una vez por cada registro de la consulta que la
anida.

Escritura de subconsultas de registro único y múltiples registros

• Las subconsultas se pueden usar para generar valores en la lista de selección de una consulta y
generar una vista en línea que se puede introducir en las cláusulas FROM, WHERE y HAVING.
• Las subconsultas de registro único, cuando se usan en las cláusulas WHERE o HAVING, deben
ser usadas con operadores de comparación: =, >, >=, <, <=, <>.
• Las subconsultas de registros múltiples pueden usarse con estos operadores de comparación:
IN, NOT IN, ANY, ALL.
• Además, los operadores ALL y ANY pueden ser alternativas al uso de agregaciones.

247/306
SQL

9. Utilizando los operadores de Conjunto

9.1. Descripción de los operadores de conjunto


Todas las sentencias SELECT devuelven un conjunto de registros. Los operadores de conjunto toman
como entrada los resultados de dos o más sentencias SELECT y a partir de éstas, se genera un único
conjunto de resultados. Esto se conoce como una consulta compuesta. Oracle proporciona tres
operadores de conjunto: UNION, INTERSECT, y MINUS. UNION puede ser cualificado/complementado
con “ALL”. Hay una desviación significante aquí respecto al estándar ISO para SQL, ya que el ISO SQL
utiliza EXCEPT donde Oracle utiliza MINUS, pero la funcionalidad es idéntica.

Los operadores de conjunto utilizados en las consultas compuestas son los siguientes:

• UNION: Devuelve los registros combinados de dos consultas, ordenándolos y eliminando


duplicados.
• UNION ALL: Devuelve los registros combinados de dos consultas sin ordenar y sin eliminar
duplicados.
• INTERSECT: Devuelve sólo los registros que aparecen en los conjuntos de resultados de ambas
consultas, ordenándolos y eliminando duplicados.
• MINUS: Devuelve sólo los registros del primer conjunto de resultados que no aparecen en el
segundo conjunto de resultados, ordenándolos y eliminando los duplicados.

9.1.1. Conjuntos y diagramas Venn


Considere agrupaciones de seres vivos clasificados de la siguiente manera:

• Seres vivos con dos patas: humanos, loros y murciélagos.


• Seres vivos que pueden volar: loros, murciélagos y abejas.
• Seres vivos con pelo: osos, murciélagos.
Cada clasificación se conoce como un conjunto, y cada miembro del conjunto es un elemento:

• La unión de los tres grupos son humanos, loros, murciélagos, abejas y osos. Son todos los
elementos de todos los grupos, sin repeticiones.
• La intersección de los conjuntos consta de todos los elementos que son comunes a los tres
conjuntos, una vez más eliminando las repeticiones. En este sencillo ejemplo, la intersección sólo
tiene un elemento: los murciélagos. La intersección del conjunto de dos patas y el conjunto
volador tiene dos elementos: loros y murciélagos.
• El menor de los conjuntos se define como los elementos de un conjunto sin los elementos de
otro, así que el conjunto de seres vivos de dos patas, menos el conjunto de seres vivos voladores,
menos el conjunto de seres vivos peludos, consta de un solo elemento: los humanos.

Estos conjuntos pueden representarse gráficamente como el diagrama de Venn mostrado en la figura de
abajo. (Los diagramas de Venn llevan el nombre de John Venn, quien formalizó la teoría en la Universidad
de Cambridge en el siglo XIX).

248/306
SQL

El círculo de la parte superior izquierda de la figura representa el conjunto de seres vivos de dos patas;
el círculo superior derecho corresponde a seres vivos que pueden volar; el círculo inferior a animales
peludos. Las uniones, intersecciones, y menor de los conjuntos aparecen inmediatamente al dibujar los
círculos y se corresponden con las regiones superpuestas o no de los círculos. El diagrama de la figura
también incluye el conjunto universal, representado por el rectángulo. El conjunto universal son todos
los elementos que existen, incluyendo aquellos que no son miembros de los conjuntos definidos. En este
caso, el conjunto universal se definiría como todos los seres vivos, incluyendo aquellos que no
desarrollaron piel, dos patas, o la capacidad de volar (como los peces). Después de ver este ejemplo del
concepto matemático de conjuntos se procederá a la implementación en SQL.

9.1.2. Principios generales de los operadores de conjunto


Todos los operadores de conjuntos realizan consultas compuestas combinando los conjuntos resultado
de dos o más consultas. Si una instrucción SELECT incluye más de un operador de conjunto (y por lo
tanto más de dos consultas), se aplicarán en el orden que especifique el programador: de arriba a abajo
y de izquierda a derecha. Para cumplir con los estándares SQL, algunas versiones le dan a INTERSECT
una prioridad más alta que otras. Sin embargo, en la mayor parte de las versiones actuales no hay
prioridad de un operador sobre otro. Para anular esta diferencia de versiones, basado en el orden en que
aparecen los operadores, se puede utilizar paréntesis: los operadores entre paréntesis se evaluarán antes
de pasar los resultados a los operadores fuera de los paréntesis.

Debido al cambio en la prioridad del operador, puede ser una buena práctica utilizar siempre
paréntesis. Esto asegurará que la función del código no cambiará cuando se ejecute con una
versión posterior de la base de datos.

Cada consulta en una consulta compuesta proporcionará su propia lista de columnas seleccionadas. Estas
listas deben tener el mismo número de elementos, estar nominadas en la misma secuencia y tener los
mismos tipos de datos. No deben tener los mismos nombres (o alias de columna), ni deben provenir de

249/306
SQL

las mismas tablas (o subconsultas). Si los nombres de columna (o alias) son diferentes, el conjunto de
resultados de la consulta compuesta tendrá columnas con el mismo nombre que en la primera consulta.
Aunque las listas de columnas seleccionadas no tienen que ser exactamente del mismo tipo de datos,
deben pertenecer al mismo grupo de tipos de datos. Por ejemplo, las columnas seleccionadas por una
consulta podrían ser de tipo NUMBER y DATE y las de la segunda consulta podrían ser INTEGER y
TIMESTAMP. El conjunto de resultados de la consulta compuesta tendrá columnas con el mayor nivel de
precisión: en este caso, serían TIMESTAMP y NUMBER. Además de aceptar tipos de datos del mismo
grupo, los operadores de conjunto no realizarán ningún tipo de conversión implícita. Si la segunda
consulta recuperara columnas del tipo VARCHAR2, la consulta compuesta lanzaría un error incluso si las
variables string pudieran resolverse con valores numéricos y de fecha legítimos.

Las columnas de las consultas que componen una consulta compuesta pueden tener diferentes
nombres, pero el conjunto de resultados de salida utilizará los nombres de las columnas de la
primera consulta.

Las columnas del resultado de una consulta compuesta deben ser del mismo grupo de tipos
de datos que las columnas de la primera consulta.

UNION, MINUS e INTERSECT siempre combinarán los conjuntos de resultados de las consultas de entrada
y luego ordenarán los resultados para eliminar los registros duplicados. La clasificación se basa en todas
las columnas, de izquierda a derecha. Si todas las columnas de dos registros tienen el mismo valor, sólo
se devuelve el primer registro en el conjunto de resultados compuesto. Un efecto secundario de esto es
que la salida de una consulta compuesta será ordenada. Si el sentido de ordenación (que es ascendente,
basado en el orden en el que las columnas aparecen en las listas de selección) no es el orden deseado,
es posible poner una única cláusula ORDER BY al final de la consulta compuesta. No es posible utilizar
ORDER BY en ninguna de las consultas que componen toda la consulta compuesta, ya que esto
interrumpiría la clasificación necesaria para eliminar duplicados.

Una consulta compuesta devolverá por defecto los registros ordenados a través de todas las
columnas, de izquierda a derecha. La única excepción es UNION ALL, donde los registros no
serán ordenados. El único lugar donde se permite una cláusula de ORDER BY es al final de la
consulta compuesta.

UNION ALL es la excepción a la regla de ordenación sin duplicados ya que los conjuntos de resultados de
las dos consultas de entrada se concatenan para formar el resultado de la consulta compuesta. En este
caso tampoco se puede utilizar ORDER BY en las consultas individuales; sólo puede aparecer al final de
la consulta compuesta donde se aplicará al conjunto de resultados completo.

9.2. Utilizar un operador de conjunto para combinar múltiples consultas en una única
consulta
Las consultas compuestas son de dos o más consultas, enlazadas por uno o más operadores conjunto.
El resultado final será un único conjunto de registros.
Los siguientes ejemplos estarán basados en dos tablas, OLD_DEPT y NEW_DEPT. La tabla OLD_DEPT
tiene por objetivo representar una tabla creada con una versión anterior de Oracle, cuando el único tipo
de dato para representar un dato de fecha y hora era DATE, la única opción para un dato de tipo numérico
era NUMBER y para el dato carácter era CHAR (de tamaño fijo). La tabla NEW_DEPT usa el tipo de dato

250/306
SQL

numérico, mejor definido, INTEGER (que Oracle implementa como un NUMBER de hasta 38 dígitos
significantes, pero sin decimales), el tipo de dato de caracteres más eficiente (en cuanto a espacio)
VARCHAR2 y el tipo de dato TIMESTAMP, que puede guardar fechas y horas con seis decimales de
precisión en los segundos por defecto. Hay dos registros en cada tabla.

9.2.1. El operador UNION ALL


Un UNION ALL toma dos conjuntos de resultados y los concatena juntos en un único conjunto de
resultados. Los conjuntos de resultados se obtienen de dos sentencias que tienen el mismo número de
columnas y las columnas correspondientes de las dos sentencias (en el orden que hayan sido
especificadas) tienen que ser del mismo grupo de tipo de datos. Las columnas no tienen que tener los
mismos nombres.
La imagen siguiente muestra la operación UNION ALL de dos tablas. El UNION ALL de las dos tablas
convierte todos los valores al tipo de datos más preciso: las fechas son devueltas como TIMESTAMP (el
tipo de datos menos preciso DATE, es rellenado con ceros), el tipo de datos carácter es convertido al
más eficiente, VARCHAR2 con el tamaño de la columna más grande y los números (aunque no es obvio
debido a la naturaleza del dato) aceptará decimales. El orden de los registros es el orden de los registros
de la primera tabla, en el orden que estén guardados, seguidos por los registros de la segunda tabla en
el orden que estén ordenados.

251/306
SQL

9.2.2. El operador UNION


Un operador UNION realiza un UNION ALL, ordena el resultado y elimina duplicados. La primera
consulta en la siguiente imagen devuelve los cuatro registros porque no hay duplicados. No obstante,
los registros están ordenados. Puede parecer que los dos primeros registros no están ordenados debido
a los valores en DATED, pero lo están: el DNAME en la tabla OLD_DEPTS tiene un tamaño de 20 bytes
(rellenado con espacios), mientras que el DNAME en NEW_DEPT, donde es un VARCHAR2, tiene el
tamaño de su propio valor. Los espacios dan a los registros de OLD_DEPT un valor más alto, aunque el
valor sea menor.
La segunda consulta en la siguiente imagen elimina cualquier espacio en blanco, sea del principio o del
final, de la columna DNAME y elimina los tiempos del de DATED y STARTD. Los dos registros se convierten
en idénticos, así que solo aparece uno en el ejemplo.

Debido a la ordenación, el orden de las consultas en una consulta compuesta UNION, no habrá diferencia
en el orden en el que los registros son devueltos.

252/306
SQL

Si no pueden haber duplicados entre dos tablas, entonces se usará siempre UNION ALL. La
base de datos ahorrará mucho tiempo de filtrado.

9.2.3. El operador INTERSECT


La intersección de dos colecciones son los registros que son comunes entre sí, como se muestra en la
siguiente imagen.

La primera consulta que se muestra, no obtiene ningún resultado ya que todos los registros en las dos
tablas son diferentes. En la siguiente consulta, aplicamos funciones para eliminar algunas de las
diferencias y obtenemos un registro común. En este caso, solo un registro es devuelto; si hubiese varios
registros, estarían en orden. El orden en que las consultas aparecen en la combinación no afecta al orden
de los resultados.

253/306
SQL

9.2.4. El operador MINUS


El operador MINUS ejecuta las dos consultas, ordena los resultados y devuelve solo el conjunto de
registros del primer conjunto de resultados que no aparece en el segundo conjunto de resultados
La tercera consulta de la imagen anterior obtiene todos los registros de OLD_DEPT ya que no hay registros
que coincidan en NEW_DEPT. La última consulta fuerza a que haya datos en común, lo que causa que
uno de los registros del resultado sea eliminado. Debido a la ordenación los registros estarán en un orden
independiente al orden en que las consultas aparecen en la combinación.

9.2.5. Ejemplos más complejos (Opcional)


Si dos consultas no devuelven el mismo número de columnas, es posible ejecutarlas combinándolas en
una consulta generando columnas con valores NULL.
Por ejemplo, se considera que hay un sistema de clasificación de animales: todos los animales tienen un
nombre y un peso, pero los pájaros tienen envergadura y los gatos tienen un tamaño de cola. Una
consulta para obtener todos los pájaros y gatos podría ser:

254/306
SQL

SELECT name, tail_length, NULL


FROM cats
UNION ALL
SELECT name, NULL, wingspan FROM birds;

Se utiliza NULL para generar los valores faltantes.


Una consulta compuesta puede consistir en más de dos consultas, en cuyo caso el operador de
precedencia puede ser controlado mediante paréntesis. Sin los paréntesis, los operadores de conjunto
serán aplicados en la secuencia en la que están especificados. Considérese la situación en la que existe
una tabla PERMSTAFF con un listado de todos los miembros del personal permanente y una tabla
CONSULTANTS con un listado del personal de asesoría. También existe una tabla BLACKLIST para
personas que han sido expulsadas por una u otra razón.

La siguiente consulta listará el personal permanente y de asesoría en una determinada ubicación


geográfica, eliminando aquellos que están expulsados:

SELECT name
FROM permstaff
WHERE location = 'Germany'
UNION ALL
SELECT name
FROM consultants
WHERE work_area = 'Western Europe'
MINUS
SELECT name
FROM blacklist;

Se usa UNION ALL porque se asume que nadie estará en la tabla CONSULTANTS y PERMSTAFF; un UNION
forzaría a una ordenación innecesaria. El orden de precedencia para los operadores de colecciones es
especificado por el programador, así que el operador MINUS comparará los registros de BLACKLIST con
el resultado de UNION ALL. El resultado será todo el personal (permanente y asesores) que no aparecen
en la BLACKLIST. Si el listado de expulsados pudiera ser aplicado solo al personal de asesores y no al
personal permanente habría dos posibilidades. La primera, las sentencias podrían ser listadas en orden
diferente:

SELECT name
FROM consultants
WHERE work_area = 'Western Europe'
MINUS
SELECT name
FROM blacklist
UNION ALL
SELECT name
FROM permstaff
WHERE location = 'Germany';

255/306
SQL

Esto devolvería los asesores que no están expulsados y juntaría al personal permanente. Otra opción,
los paréntesis podrían controlar la precedencia explícitamente:

SELECT name
FROM permstaff
WHERE location = 'Germany'
UNION ALL
(SELECT name
FROM consultants
WHERE work_area = 'Western Europe'
MINUS
SELECT name
FROM blacklist);

Esta consulta mostrará a todo el personal permanente y juntaría a todos los asesores que no están
expulsados.

Estas dos consultas devolverán los mismos registros, pero el orden será diferente debido a que las
operaciones UNION ALL muestran las tablas PERMSTAFF y CONSULTANTS en un orden diferente. Para
asegurar que ambos resultados de las consultas sean iguales, necesitaríamos una cláusula ORDER BY en
la última línea.

Las dos consultas anteriores devolverán los mismos registros, pero la segunda versión podría
considerarse mejor ya que es más clara por el uso de paréntesis. Por otra parte, confiar en la
precedencia implícita basándose en el orden de las consultas funciona por ahora, pero en
futuras versiones de SQL podrían incluir otras precedencias.

Problema Solución

¿Cómo se muestran varias tablas con Esto es un problema común, a menudo


datos similares como una única tabla? causado por un mal análisis del sistema
o quizás por probar integrar varios
sistemas. Las consultas de combinación
suelen ser la solución. Usando funciones
que fuercen a las columnas a ser del
mismo tipo y NULL para generar las
columnas que falten, se podrían mostrar
los datos como si de una única tabla se
tratara.

256/306
SQL

¿Hay algún problema de rendimiento con Puede haberlo. Con la excepción de las
las consultas combinadas? consultas combinadas con UNION ALL,
las consultas combinadas tienen que
ordenar los resultados por todas sus
columnas. Esto puede ser costoso a nivel
de memoria y CPU. Así mismo, si dos
consultas apuntan a la misma tabla, se
recorrerá dos veces cada registro ya que
cada consulta se ejecuta de manera
independiente; si se pudiese obtener el
mismo resultado con una consulta
(posiblemente aumentaría la
complejidad), sería normalmente una
solución más rápida. Las consultas
combinadas deben ser una herramienta.

9.3. Controlar el orden de los registros devueltos


Por defecto, la salida de una consulta compuesta UNION ALL no está ordenada en absoluto: los registros
se devolverán en grupos en el orden en que la consulta fue listada primero y dentro de los grupos en el
orden en que se almacenaron. La salida de cualquier otro operador conjunto se ordenará en orden
ascendente de todas las columnas, comenzando con la primera columna nombrada.

No es posible utilizar sintácticamente una cláusula ORDER BY en las consultas individuales que componen
una consulta compuesta. Esto se debe a que la ejecución de la mayoría de las consultas compuestas
tiene que ordenar los registros, lo que entraría en conflicto con el comando ORDER BY. Teóricamente,
podría ser posible que un UNION ALL (que no ordena los registros) pudiera tomar un ORDER BY para
cada consulta, pero la implementación Oracle de UNION ALL no lo permite.

Sin embargo, no hay ningún problema en colocar una cláusula ORDER BY al final de la consulta
compuesta. Esto ordenará toda la salida de la consulta compuesta. La clasificación por defecto de los
registros se basa en todas las columnas de la secuencia en la que aparecen. Una cláusula ORDER BY
especificada no tiene restricciones: puede basarse en cualquier columna (y funciones aplicadas a las
columnas) en cualquier orden. Por ejemplo:

SELECT deptno, trim(dname) name


FROM old_dept
UNION
SELECT dept_id, dname
FROM new_dept
ORDER BY name;

DEPTNO NAME
10 Accounts
30 Admin
20 Support

257/306
SQL

Los nombres (o alias) de columna en la cláusula ORDER BY deben ser el nombre o el alias de las columnas
en la primera consulta de la consulta compuesta.

9.4. Resumen
Definición de los operadores Conjunto

• UNION ALL concatena los resultados de dos consultas.


• UNION ordena los resultados de las dos consultas y elimina duplicados.
• INTERSECT devuelve solo los registros comunes del resultado de las dos consultas. MINUS
devuelve los registros de la primera consulta que no existen en la segunda.

Uso de un operador Conjunto para combinar varias consultas en una única consulta

• Las consultas en la consulta combinada tienen que devolver el mismo número de columnas.
• Las columnas correspondientes tienen que ser de tipos de datos compatibles.
• Los operadores conjunto tienen la misma precedencia y serán aplicados en el orden que están
especificados.

Controlar el orden de los registros devueltos

• No es posible usar ORDER BY en una de las consultas que están en una consulta compuesta.
• Una cláusula ORDER BY puede ser usada al final de una consulta compuesta.
• Los registros devueltos por UNION ALL estarán en el orden en el que aparecen en las dos
consultas.
• Los registros devueltos por UNION estarán ordenados por las columnas de izquierda a derecha.

10. Manipulando datos

10.1. Describir cada Sentencia del Lenguaje de Manipulación de Datos (DML)


Este capítulo trata sobre los comandos que modifican datos de una base de datos. Sintácticamente, los
comandos DML (Data Manipulation Language) son mucho más simples que el comando SELECT que se
ha estudiado hasta ahora. Sin embargo, hay una gran cantidad de teoría de bases de datos relacionales
que viene con DML. Además de entender los comandos que realizan los cambios, es esencial entender
la teoría de la gestión de transacciones, que forma parte del paradigma de la base de datos relacional.
Los mecanismos de gestión de transacciones proporcionados por la base de datos Oracle garantizan la
conformidad con los estándares relacionales para las transacciones: implementan lo que comúnmente
se conoce como ACID test (atomicidad, consistencia, aislamiento y durabilidad).
Después de los detalles de los comandos DML, este capítulo incluye un tratamiento completo de la teoría
necesaria: integridad transaccional, bloqueo de registros y consistencia en la lectura.

Estrictamente hablando, hay cinco comandos DML:

• SELECT INSERT
• UPDATE
• DELETE
• MERGE

258/306
SQL

En la práctica, la mayoría de los profesionales de bases de datos nunca incluyen SELECT como parte del
DML. Se considera un lenguaje separado por derecho propio, lo que no carece de sentido si se tiene en
cuenta que se han necesitado los ocho capítulos anteriores para describirla.
El comando MERGE también suele ser omitido, no porque no sea claramente un comando de manipulación
de datos, sino porque no hace nada que no se pueda hacer con otros comandos. MERGE puede ser visto
como un atajo para ejecutar un INSERT, un UPDATE o un DELETE dependiendo de alguna condición.
Un comando que a menudo se considera parte de DML es TRUNCATE. Esto es en realidad un DDL (Data
Definition Language o lenguaje de definición de datos), pero como el resultado para los usuarios finales
es el mismo que el del comando DELETE (aunque su implementación es totalmente diferente), encaja
con el DML.
Así pues, los siguientes comandos se describen en las siguientes secciones, con su sintaxis y ejemplos:

• INSERT
• UPDATE
• DELETE
• TRUNCATE
Además, veremos, para completar:

• MERGE
Estos son los comandos que manipulan los datos.

10.1.1. INSERT
Oracle almacena los datos en forma de registros en tablas. Las tablas se rellenan con registros de varias
maneras, pero el método más común es con la sentencia INSERT. SQL es un lenguaje orientado a
conjuntos, por lo que un comando puede afectar a un registro o a un conjunto de registros. De ello se
deduce que una sentencia INSERT puede insertar uno o más registros en una o más tablas. Las versiones
básicas de la sentencia insertan sólo un registro, pero variaciones más complejas pueden, con un
comando, insertar múltiples registros en múltiples tablas.

Existen técnicas mucho más rápidas que INSERT para rellenar una tabla con un gran número
de registros. Se trata de la utilidad SQL*Loader, que puede cargar datos de archivos
producidos por un sistema externo, y Data Pump, que puede transferir datos en masa de una
base de datos Oracle a otra, ya sea a través de archivos de disco o a través de un enlace de
red.

Las tablas tienen reglas definidas que controlan los registros que se pueden insertar. Estas reglas son
restricciones. Una restricción es la implementación de una regla de negocio. Los analistas que modelan
los procesos de negocio de una organización diseñarán un conjunto de reglas para los datos de la
organización. Ejemplos de tales reglas podrían ser que cada empleado debe tener un número de
empleado único o que cada empleado debe estar asociado a un departamento válido. La creación de
restricciones se describe en el Capítulo 11; por ahora, hay que recordar que no hay forma de que un
comando INSERT pueda insertar un registro que infrinja una restricción. Así que si se intenta insertar
una línea en EMPLOYEES con un EMPLOYEE_ID que ya existe en otro registro, o con un DEPARTMENT_ID
que no coincide con un registro de la tabla DEPARTMENTS, la inserción fallará. Las restricciones

259/306
SQL

garantizan que los datos de la base de datos se ajustan a las normas que definen los procedimientos
empresariales.
Existen diferentes formas de insertar datos en la base de datos usando la sentencia INSERT. Se puede
insertar un solo registro proporcionando los valores para las columnas del registro individualmente. Dicha
declaración puede construirse escribiéndola en SQL*Plus o SQL Developer, o mediante un proceso de
usuario más sofisticado que presente un formulario que solicite los valores a insertar. Esta es la técnica
utilizada para que el usuario introduzca datos manualmente en una base de datos, por ejemplo.
Para inserciones de varios registros, la fuente de los datos puede ser una instrucción SELECT. La salida
de todas y cada una de las sentencias SELECT vistas en los ocho capítulos anteriores puede utilizarse
como entrada a una sentencia INSERT.
El resultado final de cualquier sentencia SELECT puede considerarse como una tabla: un conjunto
bidimensional de registros. Esta "tabla" puede mostrarse a un usuario (quizás en una herramienta simple
como SQL*Plus), o puede pasarse a un comando INSERT para rellenar otra tabla, definida dentro de la
base de datos. Usar una instrucción SELECT para construir registros para una instrucción INSERT es una
técnica muy común. El SELECT puede realizar muchas tareas. Estas, típicamente, incluyen la unión de
tablas y agregaciones, de modo que los registros resultantes insertados en la tabla de destino contienen
información que es mucho más comprensible para los usuarios finales que los datos sin procesar en las
tablas de origen.

10.1.2. UPDATE
El comando UPDATE se utiliza para modificar registros que ya existen - registros que han sido creados
por un comando INSERT, o posiblemente por una herramienta como Data Pump. Al igual que con
cualquier otro comando SQL, un UPDATE puede afectar a un registro o a un conjunto de registros. El
tamaño del conjunto afectado por un UPDATE se determina por una cláusula WHERE, de la misma manera
que el conjunto de registros recuperados por una instrucción SELECT se define por una cláusula WHERE.
La sintaxis es idéntica.

Todos los registros actualizados estarán en una tabla; no es posible que un único comando UPDATE afecte
a los registros de varias tablas. Cuando se actualiza un registro o un conjunto de registros, el comando
UPDATE especifica qué columnas del registro(s) se deben actualizar. No es necesario actualizar cada
columna de un registro. Si la columna que se está actualizando ya tiene un valor, este valor se sustituye
por el nuevo valor especificado por el comando UPDATE. Si la columna no estaba previamente rellenada
-es decir, su valor era NULL- entonces se rellenará después del UPDATE con el nuevo valor.

Un uso típico de UPDATE es recuperar un registro y actualizar una o más columnas del registro. La
recuperación se hará usando una cláusula WHERE que selecciona un registro por su clave primaria, el
identificador único que permitirá asegúrese de que sólo se recupera un registro. Luego las columnas que
se actualicen serán cualquier columna que no sea la columna principal. Es muy inusual cambiar el valor
de la clave primaria.

La vida útil de un registro comienza cuando se inserta, luego puede continuar a través de varias
actualizaciones, hasta que se borra. A lo largo de esta vida, no se podrá normalmente cambiar su clave
primaria.

Para actualizar un conjunto de registros, se utiliza una cláusula WHERE menos restrictiva que la clave
primaria. Para actualizar todos los registros de una tabla, no se utiliza ninguna cláusula WHERE.

Si se seleccionan los registros que se actualizarán por cualquier columna que no sea la clave primaria,
se pueden actualizar varios registros, no sólo uno. Si se omite la cláusula WHERE por completo, se
actualizará toda la tabla -quizás miles de millones de registros actualizados con una sola sentencia-
cuando se quería cambiar sólo uno.

260/306
SQL

El comando UPDATE debe respetar cualquier restricción definida para la tabla, tal y como lo haría el
INSERT original. Por ejemplo, no será posible actualizar una columna que ha sido marcada como
obligatoria a un valor NULL o actualizar una columna clave primaria para que ya no sea única.

10.1.3. DELETE
Los registros previamente insertados se pueden eliminar de una tabla con el comando DELETE. El
comando eliminará un registro o un conjunto de registros de la tabla, dependiendo de la cláusula WHERE.
Si no hay cláusula WHERE, se eliminarán todos los registros de la tabla (lo que puede ser un problema
si se omite la cláusula WHERE por error).

No hay avisos de "advertencia" para ningún comando SQL. Si le indica a la base de datos que
borre un millón de registros, lo hará. Inmediatamente. No hay nada similar a ventanas
emergentes con mensajes del tipo "¿Estás seguro?" que algunos entornos ofrecen.

No es posible especificar en el comando DELETE qué columnas serán borradas, siempre se borrarán
registros completos. Cuando se insertan registros, se puede elegir qué columnas rellenar. Cuando se
actualizan los registros, se puede seleccionar las columnas que se desea actualizar. Un borrado no
obstante se aplica a todo el registro: la única opción es seleccionar qué registros de qué tabla. Esto hace
que el comando DELETE sea sintácticamente más simple que los otros comandos DML.

10.1.4. MERGE
En versiones anteriores de SQL no existía un comando MERGE. Se introdujo MERGE con el estándar
SQL1999, implementado por Oracle en la versión 9i de la base de datos. La versión 10g (conforme al
estándar SQL2003) proporciona algunas mejoras. Algunas implementaciones de SQL tenían un comando
llamado UPSERT. Esta palabra describe el comando MERGE bastante bien: ejecuta un UPDATE o un
INSERT, dependiendo de alguna condición. El término UPSERT no obstante está actualmente obsoleto,
porque la versión actual de MERGE puede, según las circunstancias, hacer también un DELETE.
Hay muchas ocasiones en las que se desea tomar un conjunto de datos e integrarlo en una tabla
existente. Si ya existe un registro de la nueva tabla origen en la tabla destino, es posible que se desee
actualizar el registro de destino o que desee reemplazarlo completamente, o tal vez se desee dejar el
registro de destino sin cambios. Si un registro en la tabla origen no existe en el destino, es posible que
se desee insertarlo. El comando MERGE permite hacer esto. Un MERGE pasa por los datos de origen, por
cada registro intenta localizar un registro idéntico en la tabla destino. Si no se encuentra ninguna
coincidencia, se puede insertar el registro; si se encuentra una coincidencia, se puede actualizar el
registro correspondiente. El resultado final es una tabla destino en la que se han fusionado los datos de
la tabla de origen.

Una operación MERGE no hace nada que no se pueda hacer con sentencias INSERT, UPDATE y DELETE,
pero con un solo paso a través de los datos de origen, se pueden hacer las tres cosas. Un código
alternativo sin MERGE requeriría tres pasadas a través de los datos, uno por cada comando.

261/306
SQL

MERGE puede ser de vital importancia para codificar aplicaciones que funcionan bien y utilizan
la base de datos eficientemente.

Los datos de origen para una sentencia MERGE pueden ser una tabla o cualquier subconsulta. La condición
utilizada para encontrar registros coincidentes en la tabla de destino es similar a una cláusula WHERE.

Las cláusulas que actualizan o insertan registros son tan complejas como un comando UPDATE o INSERT.
De ello se deduce que MERGE es el más complicado de los comandos DML, lo que no es irrazonable, ya
que es (podría decirse) el más potente.

10.1.5. TRUNCATE
El comando TRUNCATE no es un comando DML, sino DDL. Existe una gran diferencia entre cambos.
Cuando los comandos DML interactúan con los datos, estos insertan, actualizan y eliminan registros de
la base de datos como parte de transacciones. Las transacciones se definen más adelante en este capítulo
en el apartado "Controlar transacciones". Por ahora, se podría decir que una transacción puede ser
controlada por el usuario, en el sentido de que este tiene la opción de hacer que los cambios realizados
en una transacción sean permanentes o se puedan revertir. Esto es muy útil, pero obliga a la base de
datos a realizar un trabajo adicional entre bastidores que el usuario no conoce. Los comandos DDL no
son transacciones que el usuario pueda controlar, por lo que aunque dentro de la base de datos sí que
se implementan como transacciones, el desarrollador no puede hacer los cambios permanentes o
revertirlos. Los comandos DDL son por lo tanto mucho más rápidos que los comandos DML.
Desde el punto de vista del usuario, un truncamiento de una tabla equivale a ejecutar un DELETE para
cada uno de sus registros; es decir, como si fuese un comando DELETE sin cláusula WHERE. Mientras
que un borrado DELETE puede llevar algún tiempo (posiblemente horas, si hay muchos registros en la
tabla) un truncamiento pasará instantáneamente. No importa si la tabla contiene un registro o miles de
millones; un TRUNCATE será virtualmente instantáneo. La tabla seguirá existiendo, pero estará vacía.

Los comandos DDL, como TRUNCATE, fallarán si hay algún comando DML activo en la tabla.
Una transacción bloqueará el comando DDL hasta que el DML termine con un COMMIT o un
ROLLBACK.

10.1.1. Fallos de las Sentencias DML (Opcional)


Los comandos pueden fallar por muchas razones, incluyendo las siguientes:

• Errores de sintaxis
• Referencias a objetos o columnas inexistentes
• Permisos de acceso
• Violaciones de restricciones de la base de datos
• Cuestiones de espacio
Un comando SQL puede afectar a un conjunto de registros, así pues, existe la complicación de que un
comando pueda tener éxito parcialmente, actuando solo sobre parte de los registros que debiera.

Uno de los propósitos de esta sección es prevenir errores de sintaxis. Cuando ocurren (y es algo bastante
habitual), deben ser detectados por la herramienta que está construyendo el SQL que será enviado a la
base de datos. Hay un número ilimitado de posibles errores de sintaxis, empezando por simples errores
ortográficos o de transposición de caracteres.
Los errores de naturaleza sintáctica no afectarán a la base de datos, porque esta nunca llegará a ejecutar
una sentencia que contenga errores de sintaxis. Será la herramienta que se utilice para trabajar con SQL
la que detecte los errores de sintaxis de las sentencias. Existen multitud de herramientas y programas,

262/306
SQL

más o menos precisos a la hora de detectar este tipo de errores.


Una sentencia SQL puede ser sintácticamente correcta, pero hacer referencia a objetos que no existen.
Los problemas típicos son las faltas de ortografía, pero hay cuestiones más complejas: una expresión
podría hacer referencia a una columna que existía en un momento dado, pero que ha sido eliminada de
la tabla o renombrada. Una sentencia de este tipo será enviada a la base de datos y fallará antes de que
la base de datos intente ejecutarla. Esto es peor para la base de datos que un simple error de sintaxis,
pero la sentencia se detiene antes de que consuma recursos significativos de la base de datos.
Un error similar al anterior tiene que ver con los casting (conversión tipos de dato). SQL es un lenguaje
fuertemente tipado: las columnas se definen con un cierto tipo de dato, y un intento de introducir un
valor de un tipo de datos diferente normalmente fallará. Sin embargo, la implementación de Oracle de
SQL, en algunas circunstancias, hará el casting (es decir, la conversión de un tipo de dato a otro)
automáticamente.
La siguiente figura muestra varios intentos de ejecución de una sentencia con SQL*Plus. Un usuario se
conecta con nombre SUE y contraseña sue (lo cual no es un buen ejemplo en lo que a seguridad se
refiere), y consulta la tabla EMPLOYEES. La sentencia falla debido a un simple error de sintaxis,
correctamente identificado por SQL*Plus. Hay que tener en cuenta que SQL*Plus nunca intenta corregir
estos errores, incluso cuando sabe exactamente lo que se pretendía escribir. Existen otras herramientas
que pueden ser más útiles, ya que ofrecen corrección automática de errores.

263/306
SQL

El segundo intento de ejecutar la sentencia falla con un error que indica que el objeto no existe. Esto se
debe a que no existe en el esquema del usuario actual; existe en el esquema HR.

Una vez corregido esto, el tercer intento de ejecutar la sentencia encuentra otro problema. El valor
pasado en la cláusula WHERE es una cadena, '21-ABR-08', pero la columna HIRE_DATE no está definida
en la tabla como una cadena de texto, sino como una fecha. Para ejecutar la sentencia, la base de datos
tendría que averiguar lo que el usuario realmente quería decir e interpretar la cadena como una fecha.
En el último ejemplo, el casting falla. Esto se debe a que la cadena pasada está formateada como una
fecha de estilo europeo, pero la base de datos ha sido configurada para esperar un NLS_DATE_FORMAT
de DD-MON-RR (Dia-Mes-Año).

Los desarrolladores nunca deben confiar en el casting automático. Es una programación ineficiente.
Siempre se debe hacer cualquier tipo de casting explícito que sea necesario, usando las funciones
apropiadas como se vieron en los capítulos anteriores. Si el casting automático funciona, en el mejor de

264/306
SQL

los casos hay un incremento en el rendimiento de la BBDD ya que esta tiene que hacer trabajo extra.
En este ejemplo, si hay un índice en la columna HIRE_DATE, será un índice de fechas, por lo que no hay
manera de que la base de datos pueda usarla cuando se pasa una cadena de texto. En el peor de los
casos, el resultado será erróneo. Si la cadena de fechas pasada fuera '04/05/2007' esto funcionaría,
¿pero sería el 4 de mayo o el 5 de abril?

Oracle intentará corregir los desajustes de tipos de datos en las sentencias SQL (DML y
SELECT) mediante la creación automática de tipos de casting, pero los resultados pueden ser
impredecibles y no se considera una buena práctica que el programador delegue el hacer las
conversiones de un tipo de dato a otro.

Si una sentencia es sintácticamente correcta y no tiene errores respecto a los objetos a los que hace
referencia, todavía puede fallar debido a los permisos de acceso. Si el usuario que intenta ejecutar la
sentencia no tiene los permisos correspondientes en las tablas a las que hace referencia en la propia
sentencia, la base de datos devolverá un error idéntico al que se produciría si el objeto no existiera. en
lo que respecta a ese usuario concreto, de hecho, no existe.
Respecto a los errores que pueden provocar los permisos de acceso, estos pueden ser diferentes
dependiendo del usuario que esté trabajando con la base de datos. Es posible que un usuario tenga
permisos para realizar consultas a una tabla, pero no para insertar, actualizar o borrar registros. También
es posible que los permisos sean configurados de tal manera que sea posible insertar registros pero no
leerlos, o quizás sea posible borrar registros pero no leerlos y actualizarlos; sin embargo, no es lo común.
Por otra parte, en lo referente a las posibles violaciones de las restricciones, cabe destacar que una
restricción es una regla de negocio, implementada dentro de la base de datos. Una limitación típica es
que una tabla debe tener una clave primaria: un valor de una columna (o combinación de columnas) que
pueden identificar de forma unívoca cada registro. Un comando INSERT puede insertar varios registros
en una tabla, y para cada registro la base de datos comprobará si ya existe un registro en ella con la
misma clave primaria. Esto ocurre a medida que se inserta cada registro. Podría ser que los primeros
registros (o los primeros millones de registros) se inserten sin ningún problema, y luego se encuentre
un registro con un valor de clave primaria duplicado. En este punto se devolverá un error, y la sentencia
fallará. Este fallo provocará que todos los cambios hechos hasta ahora se deshagan, incluso los que ya
habían sido realizados. Esto es parte del estándar SQL: todos los cambios que implica una sentencia
deben llevarse a cabo en su totalidad, o no habrá cambio alguno. La inversión de los cambios se denomina
rollback (implica deshacer todos los cambios provocados por la sentencia que se han realizado hasta el
momento). Los mecanismos de un rollback se describen en la sección de este capítulo titulada "Control
de transacciones".

Si una sentencia falla debido a problemas de espacio en memoria, el efecto es similar. Una parte de la
sentencia puede haber tenido éxito antes de que la base de datos se quedara sin espacio. Los cambios
provocados hasta el momento serán automáticamente deshechos. El hecho de deshacer los cambios
(rollback) de una sentencia es un concepto clave en SQL. Obliga a la base de datos a hacer trabajo extra
y por lo general tomará al menos tanto tiempo como la sentencia en sí ya haya tomado (o a veces incluso
bastante más tiempo).

265/306
SQL

10.2. Insertar registros en una tabla


La forma más simple de insertar un registro en una tabla, con la instrucción INSERT, es utilizando los
valores que se proporcionen en la cláusula VALUES. La sintaxis es la siguiente:

INSERT INTO table [(column [,column…])] VALUES (value [,value…]);

Por ejemplo:

INSERT INTO [Link]


VALUES (10,'Great Britain');

INSERT INTO [Link] (region_name, region_id)


VALUES ('Australasia',11);

INSERT INTO [Link] (region_id)


VALUES(12);

INSERT INTO [Link] VALUES (13,null);

La primera de las sentencias anteriores proporciona valores para ambas columnas de la tabla REGIONS.
Si la tabla tuviera una tercera columna, la sentencia fallaría porque el comando INSERT inserta datos
basándose en notación posicional; es decir, la sentencia no indica qué valor debe insertarse en cada
columna, sino que se basa en la posición de los valores, en el orden de los valores en el comando. Cuando
la base de datos recibe una sentencia utilizando notación posicional, hará coincidir el orden de los valores
con el orden en el que se definen las columnas de la tabla. La sentencia también fallaría si el orden de
las columnas fuera incorrecto: la base de datos intentaría la inserción, pero fallaría debido a desajustes
en el tipo de datos.
En la segunda sentencia se definen explícitamente las columnas que se van a rellenar y los valores con
los cuales se rellenarán. Ahora el orden en el que se mencionan las columnas pasa a ser irrelevante,
siempre y cuando dicho orden se corresponda al orden de los valores a insertar.
En el tercer ejemplo se desean insertar datos en una sola columna y, por lo tanto, se insertará un solo
valor. El resto de columnas adquirirán por defecto valores nulos (NULL). Esta sentencia fallaría si la
columna REGION_NAME no aceptara valores nulos.

El cuarto ejemplo producirá el mismo resultado, pero debido a que no hay una lista de columnas explícitas
a insertar, se debe proporcionar un valor de algún tipo para cada columna; como mínimo, un valor NULL.

A menudo se considera una buena práctica no confiar en la notación posicional y en su lugar


listar siempre las columnas. Esto supone más trabajo, pero hace que el código se
autodocumente y también hace que el código sea más robusto ante los cambios en la
estructura de la tabla. Por ejemplo, si se añade una columna a una tabla, todas las sentencias
INSERT que dependen de la notación posicional fallarán hasta que sean reescritas para incluir
un NULL en la nueva columna. El código INSERT que nombra las columnas explícitamente
continuará ejecutándose sin errores.

Muy a menudo, una sentencia INSERT incluirá funciones de casting (convertir una expresión de un tipo
de datos a otro). Véase la sentencia:

INSERT INTO emp_copy (employee_id, last_name, hire_date, email, job_id) VALUES


(1000,'WATSON','03-Nov-13', 'jwatson@[Link]', 'SA_REP');

266/306
SQL

En comparación con la siguiente:

INSERT INTO emp_copy (employee_id, last_name, hire_date, email, job_id) VALUES


(1000,

upper('Watson'), to_date('03-Nov-
13','dd-mon-yy'),
lower('JWatson@[Link]'),
upper('sa_rep'));
Los registros insertados con cada sentencia serían idénticos. La primera inserta exactamente las cadenas
de caracteres facilitadas.
Es muy posible que la aplicación haga uso de los apellidos de los empleados en mayúsculas, por ejemplo.
Esto podría suponer un grave problema a la hora de realizar búsquedas, ya que si también hay apellidos
que alternen mayúsculas y minúsculas, se pueden obtener resultados no deseados o incorrectos.
Además, la inserción del valor de fecha se basa en el casting automático de una cadena de caracteres a
una fecha, lo que implica un cierto coste computacional, además de que se podrían obtener valores
incorrectos.

La segunda sentencia obliga a que el apellido esté en mayúsculas tanto si se ha introducido en


mayúsculas como si no, así como también se especifica exactamente el formato de la cadena de texto
que es la fecha antes de convertirla explícitamente en una fecha.
Sin duda la segunda sentencia es una mejor práctica que la primera. El ejemplo siguiente también hace
uso de funciones:

INSERT INTO emp_copy (employee_id, last_name, hire_date, email, job_id)


VALUES (1000 + 1, user, sysdate - 7, 'jwatson@[Link]', 'SA_REP');

En esta sentencia, la columna EMPLOYEE_ID se rellena con el resultado de una operación aritmética, la
columna LAST_NAME se rellena con el resultado de la función USER (que devuelve el nombre del usuario
que está accediendo a la base de datos), y la columna HIRE_DATE se rellena con el resultado de una
función y una operación aritmética: la fecha correspondiente a siete días antes de la fecha actual del
sistema.
La imagen siguiente muestra la ejecución de las tres consultas anteriores, seguidas por los resultados.

267/306
SQL

Usando funciones para pre-procesar los valores antes de insertarlos en los registros puede ser
particularmente importante cuando se ejecutan scripts con variables de sustitución, ya que se podrán
corregir valores no deseados en la entrada de datos, lo cual ocurre frecuentemente cuando el usuario los
introduce interactivamente.
Para insertar muchos registros con una sola sentencia INSERT, los valores a insertar deberán ser los
valores obtenidos como resultado de una subconsulta (subquery) SQL. La sintaxis sería la siguiente:

268/306
SQL

INSERT INTO table [(column [, column…])] subquery;

Hay que tener en cuenta que esta sintaxis no usa la palabra clave VALUES. Al igual que en los ejemplos
anteriores, si se omite la lista de columnas, la subconsulta debe proporcionar valores para cada columna
que exista en la tabla.

Supóngase que se quisiesen copiar todos los registros de una tabla a otra tabla, ambas con la misma
estructura de columnas. Podría utilizarse el siguiente comando:

INSERT INTO regions_copy


SELECT *
FROM [Link];

Se asume que la tabla REGIONS_COPY ya existe (con o sin registros). La subconsulta (subquery) SELECT
lee cada registro de la tabla fuente, la cual es REGIONS, e inserta a través del comando INSERT este
conjunto de resultados en la tabla destino REGIONS_COPY.

Cualquier consulta devuelve una matriz bidimensional de registros; si la tabla destino (que también es
una matriz bidimensional) tiene columnas para recibirlas, la inserción funcionará.
Normalmente se pretende presentar los datos a los usuarios finales en una forma que les facilite la
extracción de información y que les impida malinterpretarla. Esto generalmente significa desnormalizar
las tablas relacionales, hacer agregaciones, renombrar columnas y ajustar los datos que pueden
distorsionar los resultados si no se procesan correctamente.

Seguidamente se verá un ejemplo en el esquema HR: supóngase la necesidad de informar sobre los
salarios en cada departamento. La consulta debe realizar un OUTER JOIN para asegurar que no se pierda
ningún empleado sin un departamento, y que todos los departamentos estén listados
independientemente de si tienen empleados o no. También se debe asegurar que no hay ningún valor
nulo que distorsione ninguna operación aritmética sustituyendo ceros o cadenas de texto por NULL.

Esta consulta es normalmente sencilla para cualquier programador SQL, pero cuando los usuarios finales
intentan ejecutar este tipo de consulta, es muy probable que produzcan resultados inexactos al omitir
las comprobaciones. Un proceso (job) de mantenimiento diario en un almacén de datos, que ensamblaría
los datos en una forma adecuada, podría ser un script como este:

CREATE TABLE department_salaries (department varchar(25), staff number, salaries number);


TRUNCATE TABLE department_salaries;

INSERT INTO department_salaries (department,staff,salaries) SELECT


coalesce(department_name,'Unassigned'), count(employee_id),
sum(coalesce(salary,0))
FROM [Link] e
FULL OUTER JOIN [Link] d
ON e.department_id = d.department_id
GROUP BY department_name
ORDER BY department_name;

La imagen siguiente muestra la ejecución de la sentencia INSERT anterior, seguida del resultado.

269/306
SQL

El comando TRUNCATE vaciará la tabla, la cual se rellenará de nuevo con los datos de la subconsulta.
Haciendo todo el trabajo complejo en la instrucción INSERT, los usuarios pueden entonces ejecutar
consultas mucho más simples en las tablas con datos desnormalizados y agregados.

Sus consultas también serán rápidas: ya se ha hecho todo el trabajo más pesado anteriormente.

270/306
SQL

Para concluir la descripción del comando INSERT, cabe mencionar que es posible insertar registros en
varias tablas con una sola sentencia, como se puede ver en el ejemplo siguiente:

INSERT ALL
WHEN 1=1 THEN
INTO emp_no_name(department_id,job_id,salary,commission_pct,hire_date)
VALUES (department_id,job_id,salary,commission_pct,hire_date)
WHEN department_id <> 80 THEN
INTO emp_non_sales(employee_id,department_id,salary,hire_date)
VALUES (employee_id,department_id,salary,hire_date)
WHEN department_id = 80 THEN
INTO emp_sales(employee_id,salary,commission_pct,hire_date)
VALUES (employee_id,salary,commission_pct,hire_date)
SELECT employee_id,department_id,job_id,salary,commission_pct,hire_date
FROM [Link]
WHERE hire_date > sysdate - 30;

Para leer esta consulta, hay que comenzar por abajo. La subconsulta recupera todos los empleados
contratados en los últimos 30 días. Ahora, se irá leyendo el resto de la sentencia desde arriba.
La palabra clave ALL implica que cada registro obtenido en la subconsulta será considerado para ser
insertado en todas las tablas siguientes, no sólo en la primera tabla para que se cumpla una condición.
La primera condición es 1=1, lo cual es siempre verdadero, entonces cada registro fuente creará un
registro en EMP_NO_NAME. Eso es una copia de la tabla EMPLOYEES con el identificador del personal
borrado, un requisito común en un almacén (warehouse) de datos. La segunda condición es
DEPARTMENT_ID<>80, la cual crea un registro en EMP_NON_SALES para cada empleado que no está
en el departamento de ventas. La tercera condición genera una línea en EMP_SALES para todos los
vendedores; no hay necesidad de la columna DEPARTMENT_ID, porque todos estarán en el departamento
80.

Este es un ejemplo sencillo de una inserción en varias tablas, por lo que se puede ver que, con una sola
sentencia, y por lo tanto un solo paso a través de los datos de origen, es posible poblar muchas tablas
destino, lo cual es eficiente.

271/306
SQL

10.3. Actualizar registros en una tabla


El comando UPDATE modifica los valores de las columnas en uno o más registros existentes en una única
tabla. La sintaxis básica es la siguiente:

272/306
SQL

UPDATE table SET column=value [,column=value…] [WHERE condition];

La forma más compleja del comando es utilizar subconsultas para uno o más de los valores de la columna
y para la condición WHERE. La siguiente imagen permite visualizar diversos usos del comando UPDATE,
desde ejemplos simples a más complicados, ejecutados desde SQL*Plus.

El primer ejemplo es el más simple. Se fija el valor de la columna SALARY a 10000 para los empleados
con id=206.

Debido a que el registro a modificar se selecciona con una cláusula WHERE que utiliza la condición de
igualdad con la clave primaria de la tabla, hay una garantía absoluta de que a lo sumo sólo un registro
será afectado. No se cambiará ningún registro si la cláusula WHERE no encuentra ninguna coincidencia.

El segundo ejemplo muestra el uso de operaciones aritméticas aplicadas sobre una columna ya existente
para fijar el nuevo valor de esta. Esta vez los registros a modificar no se seleccionan por su clave primaria,

273/306
SQL

lo cual implica que posiblemente los registros a actualizar sean más de uno. Esto también ocurrirá si se
usan comandos como BETWEEN.
En caso de que la cláusula WHERE se omita por completo, la actualización se aplicará a cada registro de
la tabla.
El tercer ejemplo en la figura anterior introduce el uso de una subconsulta para definir el conjunto de
registros a actualizar. Una complicación adicional es el uso de una variable de sustitución para solicitar
al usuario el valor a utilizar en la cláusula WHERE de la subconsulta.
En este ejemplo, la subconsulta (líneas 4, 5 y 6) seleccionará a cada empleado que esté en un
departamento cuyo nombre incluya la cadena de caracteres 'IT' e incrementará su salario actual en un
diez por ciento.

También es posible utilizar subconsultas para determinar el valor de una columna, como en el cuarto
ejemplo. En este caso, un empleado (identificado por la clave primaria, en la línea 7) se transfiere al
departamento 80 (el departamento de ventas) y a continuación la subconsulta en las líneas 4, 5 y 6
establece su tasa de comisión a la más baja del departamento.

La sintaxis de un comando UPDATE que utiliza subconsultas es la siguiente:

UPDATE table

SET column=[subquery][,column=subquery...]

WHERE column = (subquery)

[AND column=subquery...];

Existe una restricción en las subconsultas que actualizan columnas en la cláusula SET: la subconsulta
debe devolver un valor escalar. Un valor escalar es un valor simple de cualquier tipo de datos; es decir,
la consulta debe devolver un registro asociado a una columna. Si la subconsulta devuelve varios valores,
el UPDATE fallará.
Considérense estos dos ejemplos:

UPDATE employees
SET salary=
(SELECT salary
FROM employees
WHERE employee_id=206);

UPDATE employees
SET salary=
(SELECT salary
FROM employees
WHERE last_name='Abel');

El primer ejemplo, usando la condición de igualdad con la clave primaria, siempre tendrá éxito. Incluso
si la subconsulta no recupera ningún registro (como sería el caso si no hubiera ningún empleado con
EMPLOYEE_ID igual a 206), la consulta todavía devolverá un valor escalar: un NULL. En ese caso, todos
los registros en EMPLOYEES tendrían su SALARY a NULL. Esto será probablemente un resultado no
deseado, pero no es un error en lo que respecta a SQL.
El segundo ejemplo utiliza una condición de igualdad en LAST_NAME, el cual no se garantiza que devuelva
un registro único. La declaración tendrá éxito si sólo hay un empleado con ese nombre, pero si hubiera
más
274/306
SQL

de uno se produciría el error “ORA-01427: single-row subquery returns more than one row.” Para que el
código funcione de forma fiable, sea cual sea el estado de los datos, es vital garantizar que las
subconsultas utilizadas para fijar valores de columna sean escalares.

Una solución comúnmente utilizada para asegurarse que las consultas son escalares es utilizar
MAX y MIN.

Esta sentencia siempre se ejecutará de forma correcta:

UPDATE employees
SET salary=

(SELECT max(salary)
FROM employees
WHERE last_name='Abel');

No obstante que se ejecute de forma correcta no asegura que realice aquello que se esperaba.

Las subconsultas de la cláusula WHERE serán escalares si se está utilizando en ella una condición de
igualdad (como en los ejemplos anteriores) o las condiciones de mayor/menor que. Si se está usando el
predicado IN, entonces la consulta puede devolver múltiples registros, como muestra el siguiente
ejemplo:
UPDATE employees
SET salary=10000
WHERE department_id IN

(SELECT department_id

FROM departments
WHERE department_name LIKE '%IT%');
Esto aplicará la actualización a todos los empleados de un departamento cuyo nombre incluya la cadena
"IT". Pero a pesar de que la consulta puede devolver varios registros, debe devolver únicamente una
columna.

Las subconsultas utilizadas para SET deben ser subconsultas escalares. Las subconsultas
utilizadas para seleccionar los registros también deben ser escalares, a menos que usen el
predicado IN.

10.4. Borrar registros de una tabla


Para eliminar registros de una tabla existen dos opciones: utilizar el comando DELETE o utilizar el
comando TRUNCATE.

DELETE es menos drástico, en el sentido de que un borrado puede deshacerse, mientras que si se ejecuta
un comando TRUCANTE no hay vuelta atrás. DELETE es más flexible, ya que es posible seleccionar qué

275/306
SQL

registros se desean borrar, mientras que el comando TRUNCATE siempre afecta a una tabla en su
totalidad. DELETE es sin embargo mucho más lento y puede suponer una gran carga computacional para
la base de datos. TRUNCATE por otro lado es virtualmente instantáneo y su carga computacional es
mucho menor.

10.4.1. Borrando registros con DELETE (Opcional)


El comando DELETE se usa para eliminar registros de una tabla escogida. Su sintaxis es la siguiente:

DELETE FROM

[WHERE condition];

Este es el comando más simple del DML, si en el comando anterior se omitiera la condición, todos los
registros de la tabla serían eliminados sin ningún aviso. Para un correcto uso de este comando, es
necesario especificar bien la condición deseada para eliminar los registros correctos. Estas condiciones
pueden contener tanto enteros o cadenas de texto como expresiones SQL.

DELETE FROM employees


WHERE employee_id=206;

DELETE FROM employees


WHERE last_name LIKE 'S%';

DELETE FROM employees


WHERE department_id=&Which_department;

DELETE FROM employees


WHERE department_id IS NULL;
La primera sentencia identifica un registro por su clave primaria. Sólo se eliminará el registro con una
clave que coincida con la especificada, o ninguno, en el caso de que ningún registro coincida con clave
especificada (206 en este caso).
La segunda sentencia tiene una condición que podría dar lugar al borrado de muchos registros: cada
empleado cuyo apellido empiece por la letra especificada en la condición (en este caso la “S”), será
eliminado.
La tercera sentencia utiliza una condición de igualdad por variable de sustitución, por lo que todos los
empleados del departamento que se indique a través de la variable de sustitución serán eliminados de
la tabla.

La condición final elimina a todos los empleados que actualmente no están asignados a un departamento.
La condición de la cláusula WHERE también puede ser una subconsulta:

DELETE FROM employees


WHERE department_id IN
(SELECT department_id

FROM departments
WHERE location_id IN
(SELECT location_id
FROM locations

276/306
SQL

WHERE country_id IN
(SELECT country_id
FROM countries
WHERE region_id IN
(SELECT region_id
FROM regions
WHERE region_name='Europe'))));

Este ejemplo utiliza una serie de subconsultas para borrar a cada empleado que trabaja en cualquier
departamento con sede en Europa. Se aplica la misma regla para el número de valores devueltos por la
subconsulta que para un comando UPDATE: si la selección del registro se basa en una condición de
igualdad (como en el ejemplo anterior) la subconsulta debe ser escalar, pero si utiliza el comando IN, la
subconsulta puede devolver varios registros.
Si el comando DELETE no encuentra registros que eliminar, no se provocará un error. El comando
devolverá el mensaje "0 rows deleted" en lugar de un mensaje de error porque la sentencia se completó
con éxito, simplemente no encontró ningún registro que tuviese las características especificadas en la
declaración.

10.4.2. Borrando registros con TRUNCATE


TRUNCATE es un comando DDL (Data Definition Language). Opera sobre el diccionario de datos y afecta
a la estructura de la tabla, no a su contenido. Sin embargo, la modificación que realiza en la estructura
tiene el efecto de destruir todos los registros de la tabla.
Una parte de la definición de una tabla almacenada en el diccionario de datos es la ubicación física de la
tabla. Cuando se crea por primera vez, a una tabla se le asigna una sola área de espacio, de tamaño fijo,
en los archivos de datos de la base de datos. Esto se conoce como una extensión (extent) y estará vacía.
Luego, a medida que se insertan los registros, la extensión se va llenando. Una vez que esté llena, se
asignarán más extensiones a la tabla automáticamente. Además de hacer un seguimiento de la
asignación de extensiones, el diccionario de datos también hace un seguimiento de la cantidad de espacio
asignado a la tabla que se ha utilizado. Esto se hace guardando la última posición de la última extensión
que ha sido usada, también conocida como high water mark. Todo el espacio “por debajo” de la high
water mark ha sido usado para almacenar registros en algún momento, y ninguno de los espacios “por
encima” de la high water mark han sido utilizados.
Se debe tener en cuenta que es posible que haya mucho espacio por debajo de la high water mark que
no se esté utilizando en este preciso momento; esto se debe a que los registros se han eliminado con un
comando DELETE. Insertar registros en una tabla desplaza la high water mark hacia arriba, pero eliminar
registros con DELETE deja la high water mark donde está; el espacio que ocupaban permanece asignado
a la tabla pero se libera para insertar más registros.
Al truncar una tabla se reinicia la high water mark. Dentro del diccionario de datos, la posición registrada
de la high water mark se desplaza al principio de la tabla. Como Oracle asume que no puede haber
registros por encima de la high water mark, esto tiene como efecto eliminar cada registro de la tabla. La
tabla se vacía y permanece vacía hasta que las inserciones posteriores comiencen a desplazar el high
water mark de nuevo. De esta manera, un comando DDL que solamente actualiza el diccionario de datos,
puede eliminar miles de millones de registros de una tabla.

277/306
SQL

Un truncamiento es rápido: virtualmente es instantáneo, independientemente del número de


registros que tenga la tabla. En cambio un borrado puede tomar un cierto tiempo e implica
mucha más carga computacional en la base de datos que un truncamiento.

TRUNCATE vacía completamente la tabla y no existe ninguna condición para la elección de


registros a eliminar como existe en el DELETE.

10.4.3. MERGE (Opcional)


El comando MERGE se puede considerar como un comando secundario, ya que no tiene ninguna
funcionalidad que no se pueda hacer con los comandos INSERT, UPDATE y DELETE. Sin embargo es un
comando muy potente, en el sentido de que con sólo un paso a través de los datos puede llevar a cabo
las tres operaciones. Esto puede mejorar drásticamente el rendimiento de las consultas. A continuación
se puede observar un ejemplo simple del uso de MERGE:

MERGE INTO employees e


USING new_employees n
ON (e.employee_id = n.employee_id)
WHEN MATCHED THEN
UPDATE SET [Link]=[Link]
WHEN NOT MATCHED THEN
INSERT (employee_id, last_name, salary, email, job_id)
VALUES (n.employee_id, n.last_name, [Link], [Link], n.job_id);

La sentencia anterior utiliza el contenido de una tabla NEW_EMPLOYEES para actualizar o insertar
registros en la tabla EMPLOYEES. La situación podría ser que la tabla EMPLOYEES contenga todo el
personal, y NEW_EMPLOYEES sólo contenga registros para el personal nuevo y para los cambios salariales
del personal existente. El comando pasará a través de NEW_EMPLOYEES, y para cada registro, intentará
encontrar un registro en EMPLOYEES con el mismo EMPLOYEE_ID. Si se encuentra un registro con el

278/306
SQL

mismo EMPLOYEE_ID, su columna SALARY se actualizará con el valor del registro de la tabla
NEW_EMPLOYEES. Si no existe tal registro, se insertará uno nuevo. Las variaciones de la sintaxis
permiten el uso de subconsultas para seleccionar registros de origen, e incluso es posible eliminar
registros coincidentes.

10.5. Control de transacciones


El concepto de transacción es una parte del paradigma de base de datos relacional. Una transacción
consiste en una o más sentencias DML, seguidas por uno de los dos comandos ROLLBACK o COMMIT. Es
posible utilizar el comando SAVEPOINT para conseguir un grado de control en las transacciones. Antes
de entrar en la sintaxis, es necesario revisar el concepto de transacción. También se tratarán temas
relacionados como la consistencia de lectura (read consistency); esto es implementado automáticamente
por el servidor de Oracle, pero hasta cierto punto los programadores pueden manejarlo por la forma en
que utilizan la sentencia SELECT.

10.5.1. Transacciones de base de datos


A continuación, se presenta una breve descripción de algunos de los principios a los que deben ajustarse
todas las bases de datos relacionales. En resumen, cualquier base de datos debe ser capaz de pasar el
ACID test. ACID es un acrónimo de Atomicity, Consistency, Isolation and Durability: Atomicidad,
Consistencia, Aislamiento y Durabilidad en español.

a. A de Atomicidad
El principio de atomicidad establece que todas las partes de una transacción o ninguna de ellas deben
completarse (la razón detrás de este término es que un átomo no puede ser dividido). Por ejemplo, si
los analistas de negocio dicen que cada vez que cambie el sueldo de un empleado, también debe cambiar
el grado del empleado, entonces la transacción atómica consistirá en dos actualizaciones. La base de
datos debe garantizar que ambas (o ninguna) se realicen. Si solamente una de ellas se actualizara, se
tendría un empleado con un salario incompatible a su grado: una corrupción de datos, en términos
comerciales. Si algo sale mal antes de que se complete la transacción, la propia base de datos debe
garantizar que cualquiera de las partes que hubiera realizado la transacción pueda revertirse; esto debe
ocurrir automáticamente. Pero, aunque el término “atómico” pueda hacer pensar que una transacción es
algo pequeño (como un átomo), en realidad una transacción puede ser enorme. Poniendo otro ejemplo:
supóngase que es imposible que en un determinado libro de contabilidad la mitad de sus cuentas
pertenezcan al mes de agosto y la otra mitad al mes de septiembre. El contenido debe ir de mes a mes,
por lo tanto (en términos comerciales), esto es una transacción atómica, que puede afectar a millones
de registros en miles de tablas y tardar horas en completarse (o en retroceder, si algo saliese mal). El
ROLLBACK de una transacción incompleta puede ser manual (como cuando se emite mediante el
comando ROLLBACK), pero debe ser automática e imparable en caso de error.

b. C de Consistencia
El principio de consistencia establece que los resultados de una consulta deben ser consistentes con el
estado de la base de datos en el momento en el que se ejecuta la consulta. Supóngase una simple
consulta que promedia el valor de una columna de una tabla. Si la tabla es grande, llevará mucho tiempo

279/306
SQL

recorrer toda la tabla. Si otros usuarios están actualizando la columna mientras la consulta está en
progreso, ¿debe la consulta incluir los nuevos o los antiguos valores? ¿Debe incluir los registros que
fueron insertados o borrados después que se empezó la consulta? El principio de consistencia precisa
que la base de datos garantice que los valores modificados no sean tenidos en cuenta al ejecutar la
consulta; dará un promedio de la columna como estaba cuando la consulta se empezó a ejecutar, sin
importar cuánto tarde la consulta o qué otras actividades estén llevándose a cabo en las tablas
correspondientes. Oracle garantiza que, si una consulta tiene éxito, el resultado será consistente. Sin
embargo, si el administrador de la base de datos no ha configurado la base de datos de forma apropiada,
la consulta podrá no tener éxito y se lanzará un error: “ORA-1555 snapshot too old”. Esto solía ser un
problema extremadamente difícil de resolver con versiones anteriores de la base de datos, pero con
versiones recientes, el administrador de la base de datos debería ser capaz siempre de prevenir esto.

c. I de aislamiento (Isolation, en inglés)


El principio de aislamiento establece que una transacción incompleta (es decir, una transacción de la que
no se ha hecho COMMIT) debe ser “invisible” para el resto del mundo. Mientras la transacción está en
progreso, solo se le permite ver los cambios a la sesión que está ejecutando la transacción; todas las
demás sesiones deben ver los datos inalterados, no los nuevos valores. La lógica detrás de esto es,
primero, que la transacción completa podría no realizarse (por el principio de atomicidad y rollback
automático o manual) y que, por lo tanto, a ningún otro usuario se le debería permitir ver los cambios
que podrían revertirse. Y segundo, durante el progreso de la transacción los datos son incoherentes: hay
un corto espacio de tiempo en el que el empleado tiene su salario cambiado, pero no su grado. El
aislamiento de transacciones requiere que la base de datos oculte transacciones en progreso para otros
usuarios: estos verán la versión pre-actualizada de los datos hasta que se complete la transacción,
cuando verán los cambios como un conjunto consistente. Oracle garantiza el aislamiento de las
transacciones: no hay forma de que ninguna sesión (aparte de la que realiza los cambios) pueda ver los
datos de los que no se ha hecho COMMIT. Una lectura de datos que aún no han sido confirmados es
conocida como dirty read, la cual Oracle no permite (aunque otras bases de datos sí).

d. D de Durabilidad
El principio de durabilidad establece que una vez completada la transacción, debe ser imposible que la
base de datos la pierda. Durante el tiempo que la transacción está en progreso, el principio de aislamiento
requiere que nadie (aparte de la sesión afectada) puede ver los cambios que ha hecho hasta ahora. En
el instante en que la transacción se completa, debe ser transmitida al resto de usuarios, y la base de
datos debe garantizar que el cambio nunca se pierda: una base de datos relacional no puede perder
datos. Oracle cumple este requisito escribiendo todos los vectores de cambio que se aplican a los datos
a medida que se realizan los cambios. Al aplicar este registro de cambios a las copias de seguridad
realizadas anteriormente, es posible repetir cualquier proceso realizado en el caso de que la base de
datos sea dañada. Por supuesto, los datos pueden perderse debido a errores de usuario tales como DML
inapropiado, o tablas caídas o truncadas, pero en lo que concierne a Oracle tales eventos son
transacciones como cualquier otra: de acuerdo con el principio de durabilidad, son absolutamente
irreversibles.

e. Comienzo y final de una transacción


Una sesión comienza una transacción en el momento en el que emite cualquier sentencia INSERT, UPDATE
o DELETE (pero no una sentencia TRUNCATE –que es un comando DDL, no DML). La transacción continúa
mediante cualquier número de comandos DML adicionales hasta que la sesión emita una sentencia
COMMIT o ROLLBACK. Solo entonces se harán los cambios de forma permanente y estarán visibles para
otras sesiones (si se ha hecho COMMIT, en lugar de ROLLBACK). Es imposible anidar transacciones. El

280/306
SQL

estándar de SQL no permite a un usuario empezar una transacción y después otra antes de terminar la
primera. Esto puede hacerse con PL/SQL, pero no con un estándar industrial de SQL.
Las sentencias explícitas de control de transacciones son COMMIT, ROLLBACK y SAVEPOINT. Hay también
otras circunstancias en las que un usuario emite un COMMIT o ROLLBACK que implícitamente terminarán
una transacción:
• Emisión de una sentencia DDL o DCL.
• Salir de la herramienta de usuario (SQL*Plus, SQL Developer o cualquier otra).
• Si la sesión de cliente acaba.
• Si el sistema falla.
Si un usuario emite un comando DDL (CREATE, ALTER o DROP) o DCL (GRANT o REVOKE), la transacción
en progreso (si hay alguna) estará confirmada (se habrá hecho COMMIT), por lo que se hará permanente
y será visible para todos los usuarios. Esto es debido a que los comandos DDL y DCL son transacciones
en sí mismas. Si fuera posible ver el código fuente de esos comandos, se podría ver que estos modifican
las estructuras de datos realizando comandos DML en las tablas que componen el diccionario de datos,
y estos comandos terminan con un COMMIT. Si esto no fuera así, podría no garantizarse la permanencia
de dichos cambios. Como no es posible en SQL anidar transacciones, si el usuario ya ha lanzado una
transacción, las sentencias que el usuario ha lanzado deberán confirmarse junto con las sentencias que
componen el comando DDL o DCL.
Si un usuario empieza una transacción emitiendo un comando DML y luego sale de la herramienta que
está utilizando sin emitir explícitamente un COMMIT o un ROLLBACK, la transacción terminará. Que
termine con un COMMIT o un ROLLBACK depende totalmente de la implementación de la herramienta.
Por ejemplo, en el entorno de Microsoft Windows, es común poder terminar un programa seleccionando
la opción de Archivo|Salir de un menú en la parte superior izquierda de la ventana, o haciendo clic sobre
una “X” en la esquina superior derecha. Los programadores que escribieron la herramienta pueden haber
codificado diferentes lógicas en estas funciones. En cualquier caso, será una salida controlada, por lo que
los programadores deben emitir un COMMIT o un ROLLBACK, la elección depende de ellos.

Si la sesión de un cliente falla por alguna razón, la base de datos siempre hará un ROLLBACK de la
transacción. Tal fallo puede deberse a varias razones: el proceso de usuario puede acabar o ser terminado
a nivel de sistema operativo, la conexión de red del servidor de la base de datos puede caerse o la
máquina en la que se está lanzando la herramienta cliente puede bloquearse. En cualquiera de estos
casos, no hay una emisión ordenada de un COMMIT o un ROLLBACK, y depende de la base de datos
detectar qué ha ocurrido. En estos casos, la sesión termina y se hace un ROLLBACK. El comportamiento
es el mismo si el fallo es del servidor. Si el servidor de la base de datos se bloquea por cualquier razón,
cuando se inicien la próxima vez todas las transacciones de cualquier sesión que estuviera en progreso
harán un ROLLBACK.

10.5.2. Sentencias de control de transacciones


Una transacción comienza implícitamente con la primera sentencia DML. No existe un comando para
empezar explícitamente una transacción. La transacción continúa a través de todas las sentencias
subsecuentes de DML emitidas por la sesión. Estas sentencias pueden ser lanzadas para cualquier
número de tablas: una transacción no está restringida a una tabla. Se termina (a excepción de cualquiera
de los eventos listados en la sección anterior) cuando la sesión emita un COMMIT o un ROLLBACK. El
comando SAVEPOINT puede ser utilizado para establecer marcadores que llevarán a cabo la acción del

281/306
SQL

ROLLBACK, pero la misma transacción se mantiene en progreso independientemente del uso de


SAVEPOINT.

a. COMMIT
Sintácticamente, COMMIT es el comando más sencillo de SQL. La sintaxis es la siguiente:

COMMIT;

Esto pondrá fin a la transacción actual, además de hacer que los cambios sean permanentes y visibles
para otras sesiones. Hasta que no se confirma una transacción haciendo COMMIT, los cambios provocados
por dicha transacción no pueden verse en ninguna otra sesión, incluso si estas sesiones están conectadas
a la base de datos con el mismo usuario que el de la sesión que ejecuta la transacción.
Hasta que una transacción no se confirma (commited), es invisible para otras sesiones y se puede anular.
Una vez se confirma (commited), es absolutamente irreversible. Se aplica por lo tanto el principio de
durabilidad.

El estado de los datos antes de un COMMIT es que los cambios han sido realizados, pero todas las demás
sesiones, distintas a la que realizó los cambios, son redirigidas a las copias de los datos antes de ser
modificados. Así pues, si una sesión ha insertado registros, el resto de sesiones que hagan un SELECT
de la tabla no verán dichos registros. Si la transacción ha eliminado registros, las otras sesiones que
realicen un SELECT de la tabla seguirán viendo estos registros. Si la transacción ha hecho una
actualización, será la versión anterior a dicha actualización la que se les presente al resto de sesiones.
Esto está en concordancia con el principio de aislamiento: ninguna sesión puede presentarse de forma
que dependa del estado de una transacción no confirmada (commited).

Después de un COMMIT, todas las sesiones verán inmediatamente los nuevos datos en cualquiera de las
consultas que realicen: podrán ver los nuevos registros, no verán los registros eliminados y verán las
nuevas versiones de los registros actualizados, lo cual está en concordancia con el principio de
durabilidad.

b. ROLLBACK
Mientras una transacción está en progreso, Oracle mantiene una imagen de los datos tal y como se
encontraban antes de la transacción. Esta imagen es presentada a las otras sesiones que consultan los
datos mientras la transacción está en progreso. También se utiliza para deshacer automáticamente la
transacción si algo sale mal, o, deliberadamente, la sesión lo solicita. La sentencia para solicitar un
ROLLBACK es la siguiente:

COMMIT;

El uso opcional de SAVEPOINT se detalla en la siguiente sección.


El estado de los datos antes de un ROLLBACK es que los datos han sido modificados, pero la información
necesaria para revertir los cambios sigue disponible. Esta información se presenta a todas las demás
sesiones, con el fin de aplicar el principio de aislamiento. El ROLLBACK descartará todos los cambios
restaurando la imagen de los datos anterior a los cambios; cualquier registro que la transacción inserte
será eliminado, los registros eliminados serán insertados de nuevo en la tabla, y los registros que fueron
actualizados volverán a su estado original. El resto de sesiones no sabrán en absoluto que algo ha
sucedido, ya que nunca verán los cambios. La sesión que realizó la transacción verá de nuevo los datos
tal y como estaba antes de que comenzase la transacción.

282/306
SQL

Un COMMIT es instantáneo, porque realmente no tiene nada que hacer. Los cambios ya han
sido realmente realizados. Un ROLLBACK puede ser, no obstante, muy lento: normalmente se
tardará lo mismo (si no más) en anular una transacción que lo que se tardó en hacer los
cambios en primer lugar. Hacer ROLLBACK no es bueno para el rendimiento de la base de
datos.

c. SAVEPOINT
El uso de SAVEPOINT permite al programador establecer un marcador en una transacción que puede ser
utilizado para controlar el efecto del comando ROLLBACK. En vez de revertir la transacción completa y
terminarla, hace posible anular todos los cambios realizados después de un punto en concreto, pero
mantiene intactos los cambios realizados hasta ese punto. La transacción en sí sigue en curso: aún no
se ha confirmado, aún es anulable, y aún es invisible para el resto de sesiones.

La sintaxis es la siguiente:

SAVEPOINT savepoint;

Esto crea un marcador en la transacción que puede ser utilizado en un comando ROLLBACK posterior.
La siguiente tabla ilustra, en varias etapas, el número de registros de una tabla en una transacción.

COMANDO REGISTROS VISIBLES REGISTROS VISIBLES


PARA EL USUARIO PARA EL RESTO

TRUNCATE TABLE 0 0
tab1;

INSERT INTO TAB1 1 0


VALUES ('one');

SAVEPOINT first; 1 0

INSERT INTO tab1 2 0


VALUES ('two');

SAVEPOINT second; 2 0

INSERT INTO tab1 3 0

283/306
SQL

VALUES ('three');

ROLLBACK TO 2 0
SAVEPOINT second;

ROLLBACK TO 1 0
SAVEPOINT first;

COMMIT; 1 1

DELETE FROM tab1; 0 1

ROLLBACK; 1 1

El ejemplo muestra dos transacciones: la primera terminó con un COMMIT, la segunda con un ROLLBACK.
Puede verse que el uso de SAVEPOINT es visible solamente dentro de la transacción: las otras sesiones
no ven nada de lo que no está committed.
El comando SAVEPOINT no es (todavía) parte oficial del estándar de SQL, así que puede
considerarse buena práctica evitarlo en sistemas en producción. Sin embargo, puede ser muy
útil en el desarrollo cuando se está probando el efecto de las sentencias DML y yendo a través
de una transacción compleja paso a paso.

d. AUTOCOMMIT en SQL*PLUS y en SQL Developer


El comportamiento por defecto de SQL*Plus y SQL Developer es seguir el estándar de SQL: una
transacción comienza implícitamente con una sentencia DML y termina explícitamente con un COMMIT o
un ROLLBACK. Se puede cambiar este comportamiento en ambas herramientas haciendo que cada
sentencia DML se confirme en su propia transacción de manera implícita e inmediata. En este caso, no
habría necesidad de una sentencia COMMIT, y la sentencia ROLLBACK podría no tener ningún efecto:
todas las sentencias DML se hacen permanentes y visibles para otros tan pronto como sean ejecutadas.
En SQL*Plus, se habilita el modo de AUTOCOMMIT con el siguiente comando:

SET AUTOCOMMIT ON

Para volver a la normalidad:

SET AUTOCOMMIT OFF

En SQL Developer, desde el menú Herramientas, seleccionar Preferencias. A continuación, expandir Base
de Datos y Avanzado y marcar la casilla de confirmación automática.

Puede ser difícil justificar la activación del modo de confirmación automática de la


herramienta SQL*Plus o de SQL Developer. Quizás la única razón sea la compatibilidad con

284/306
SQL

otros programas que no siguen el estándar SQL. Los scripts de SQL escritos para dichos
programas pueden no tener ninguna sentencia COMMIT.

e. SELECT FOR UPDATE


Una última sentencia de control de transacciones es SELECT FOR UPDATE. Oracle, por defecto,
proporciona el mayor nivel posible de concurrencia: los “lectores” no bloquean a los “escritores”. O más
concretamente, no existe ningún problema con los datos de consulta de una sesión que otra sesión está
actualizando. Sin embargo, hay ocasiones en las que se desea cambiar este comportamiento y evitar
que los datos que se están consultando sean cambiados.

No es inusual que una aplicación recupere un conjunto de registros con un comando SELECT, las presente
al usuario para que las examine y pida cualquier cambio. Debido a que Oracle es una base de datos
multiusuario, no es imposible que otra sesión también haya recuperado los mismos registros. Si ambas
sesiones intentan hacer cambios, puede haber algunos efectos no deseados. La siguiente tabla muestra
dicha situación:

PRIMER USUARIO SEGUNDO USUARIO

SELECT * FORM regions; SELECT * FORM regions;

DELETE FROM regions


WHERE region_id=5;

COMMIT;

UPDATE regions
SET region_name='GB'
WHERE region_id=5;

Lo que verá el primer usuario desde una entrada SQL*Plus, será:

Esto puede ser un poco desconcertante. Una forma de evitar este problema es bloquear los registros en
los que se está interesado.

SELECT *

285/306
SQL

FROM regions FOR UPDATE;

La cláusula FOR UPDATE bloqueará todos los registros recuperados. No se podrán hacer cambios en ellos
por ninguna otra sesión que no sea la que emitió el comando, y, por lo tanto, las actualizaciones
posteriores tendrán éxito: no será posible para los registros que hayan sido cambiados. Esto significa
que una sesión tendrá una vista consistente de los datos (no cambiará), pero, por el contrario, las otras
sesiones se colgarán si intentan actualizar cualquier registro bloqueado (pueden, por supuesto,
consultarlos).
Los bloqueos situados por una cláusula FOR UPDATE se mantendrán hasta que la sesión que realizó el
comando emita un COMMIT o un ROLLBACK. Esto debe hacerse para liberar los bloqueos, incluso si no
se ha ejecutado ningún comando DML.

Problema Solución

Las transacciones son reglas de negocio: No necesariamente. Como una


una técnica a través de la cual la base de transacción puede tardar horas, ésta
datos puede imponer reglas utiliza una gran parte de los recursos de
desarrolladas por los analistas de la base de datos. En estos casos, se debe
negocio. Si la “unidad lógica de trabajo” discutir con los analistas de negocio y el
es enorme, como un periodo acumulado DBA si es posible dividir una transacción
del paquete de contabilidad, ¿se debería en varias transacciones. Por supuesto, si
implementar realmente como una algo va mal a la hora de dividir las
transacción? transacciones, se tendrá una parte del
paquete de contabilidad en una
transacción y otra parte en otra. La
aplicación tendrá que ser capaz de
resolverlo.

Poder realizar operaciones DML, No, en realidad no. La aplicación no


observar el resultado, luego hacer debería estar diseñada para que los
rollback e intentar las operaciones de usuarios finales puedan hacer esto. Es
nuevo puede resultar muy útil, pero mucho mejor para la aplicación hacer
¿realmente es una buena idea? todo este trabajo en la parte cliente y
solamente enviar el trabajo a la base de
datos cuando esté listo y pueda ser
confirmado (commited)
inmediatamente.

10.6. Resumen
Descripción de cada sentencia DML (Lenguaje de Manipulación de Datos)

• INSERT inserta registros en una tabla


• UPDATE modifica los valores de registros existentes.
• DELETE elimina registros.
• MERGE combina las funciones INSTERT, UPDATE y DELETE.
• Aunque TRUNCATE no es DML (es un comando DDL), elimina todos los registros de una tabla.
• Todos los comandos DML pueden hacer ROLLBACK (automática o manualmente).

286/306
SQL

Inserción de registros en una tabla

• INSERT añade un registro o un conjunto de registros.


• Es posible para una sentencia INSERT introducir registros en múltiples tablas.
• Se pueden usar subconsultas para generar los registros a introducir.
• Se pueden usar subconsultas y funciones para generar los valores de las columnas.
• Un INSERT no es permanente hasta que no se hace un COMMIT.

Actualización de registros en una tabla

• UPDATE puede afectar a un registro o un conjunto de registros.


• Se pueden usar subconsultas para seleccionar los registros a actualizar.
• Se pueden usar subconsultas y funciones para generar los valores de las columnas.
• Un UPDATE no es permanente hasta que no se hace un COMMIT.

Borrado de registros en una tabla

• DELETE puede eliminar un registro o un conjunto de registros.


• Se pueden usar subconsultas para seleccionar los registros a eliminar.
• Un DELETE no es permanente hasta que no se hace un COMMIT.
• TRUNCATE elimina todos los registros de una tabla.
• TRUNCATE es inmediatamente permanente: no se puede hacer un ROLLBACK.

Control de transacciones

• Una transacción constituye una o varias sentencias DML que se ejecutan de forma atómica en la
base de datos.
• Las transacciones son invisibles para otras sesiones hasta que se les efectúa un COMMIT.
• Las transacciones se pueden deshacer mediante un ROLLBACK hasta que se les efectúa un
COMMIT.
• Una vez se ejecuta el comando COMMIT, no se puede revertir.
• Un SAVEPOINT permite que la sesión se pueda retroceder a un punto anterior de la transacción.

11. Utilizando sentencias DDL para crear y administrar tablas

11.1. Clasificar los objetos principales de la base de datos


Hay varios tipos de objetos de datos en una base de datos que pueden ser accesibles usando SQL. El
tipo de objeto más común que se usa es la tabla. Las tablas pueden presentarse de varias formas, pero
SQL es independiente. Una tabla también puede ser asociada con otros objetos, tales como índices o
LOBs (objetos grandes con una estructura diseñada para guardar elementos grandes de información,
como grabaciones de vídeo). La sentencia SQL se referirá solo a la tabla con la que los otros objetos
están asociados. Este capítulo detalla la creación de tablas.
Cuando se crea una tabla, hay ciertas reglas que se deben seguir en cuanto a la estructura de la tabla:
sus columnas pueden ser solo de ciertos tipos de datos. Hay también reglas que pueden definirse para

287/306
SQL

los registros; estas son conocidas como “constraints”. Las reglas estructurales y las reglas constraint
restringen la información que puede ser insertada dentro de la tabla.

Hay varios tipos de objetos que pueden existir dentro de una base de datos, muchos más con la versión
actual que con las versiones anteriores. Todos los objetos tienen nombres, y todos los objetos son
propiedad de un usuario de base de datos, como HR. Los objetos que el usuario posee son su esquema.
El nombre de un objeto debe cumplir ciertas reglas.

11.1.1. Tipos de objeto


Esta consulta lista los tipos de objeto que existen en esta base de datos en particular, con el recuento de
cuántos hay:

SELECT object_type, count(object_type)


FROM dba_objects
GROUP BY object_type
ORDER BY object_type;

OBJECT_TYPE COUNT(OBJECT_TYPE)
----------------------------------------------
CLUSTER 10
CONSUMER GROUP 12
CONTEXT 6
DIMENSION 5
DIRECTORY 9
EDITION 1
EVALUATION CONTEXT 13
FUNCTION 286
INDEX 3023
INDEX PARTITION 342
INDEXTYPE 12
JAVA CLASS 22018
JAVA DATA 322
JAVA RESOURCE 820
JOB 11
JOB CLASS 11
LIBRARY 177
LOB 769
LOB PARTITION 7
MATERIALIZED VIEW 3
OPERATOR 60
PACKAGE 1240
PACKAGE BODY 1178
PROCEDURE 118
PROGRAM 17
QUEUE 37
RESOURCE PLAN 7
RULE 1

288/306
SQL

RULE SET 21
SCHEDULE 2
SEQUENCE 204
SYNONYM 26493
TABLE 2464
TABLE PARTITION 199
TRIGGER 413
TYPE 2630
TYPE BODY 231
UNDEFINED 6
VIEW 4669
WINDOW 9
WINDOW GROUP 4 XML SCHEMA 93 42 rows selected.

Esta consulta hace referencia a DBA_OBJECTS, que tiene una línea para cada objeto en la base de datos.
Los números son bajos, porque la base de datos es muy pequeña y se utiliza sólo para enseñar. Una
base de datos utilizada para una aplicación de negocio puede tener cientos de miles de objetos. Es posible
que no se pueda ver la vista DBA_OBJECTS, según los permisos de la cuenta.
La vista alternativa es USER_OBJECTS, que mostrará todos los objetos que tiene en propiedad y
ALL_OBJECTS, que mostrará todos los objetos a los que se ha concedido acceso. Todos los usuarios
tienen acceso a estos.

Los objetos de mayor interés para un programador SQL son aquellos que contienen o dan acceso a los
datos, estos son:

• Tablas
• Vistas
• Sinónimos
• Índices Secuencias

Brevemente, una instrucción SELECT almacenada puede ser tratada como si fuera una tabla. No es nada
más que una sentencia SELECT, pero en lugar de ejecutar el comando el usuario emite una instrucción
SELECT contra la vista. Un sinónimo es un alias para una tabla (o una vista). Los usuarios pueden ejecutar
sentencias SQL contra el sinónimo, y la base de datos los mapeará en sentencias contra el objeto al que
se refiere el sinónimo. Los índices son un medio para mejorar los tiempos de acceso a los registros de
las tablas. Si una consulta requiere sólo un registro, en lugar de escanear toda la tabla para encontrar
un índice puede dar un puntero a la ubicación exacta del registro. Por supuesto, el índice debe ser
buscado, pero esto es a menudo más rápido que el escaneo de la tabla. Una secuencia es una
construcción que genera números únicos. Hay muchos casos en los que números únicos son necesarios.
Las secuencias emiten los números en orden, a petición: es absolutamente imposible que el mismo
número se emita dos veces.

Los tipos de objeto restantes son comúnmente menos relevantes para un programador SQL. Su uso
recae más en el ámbito de los programadores PL/SQL y los administradores de base de datos.

289/306
SQL

11.1.2. Usuarios y Esquemas


Muchas personas utilizan los términos "usuario" y "esquema" indistintamente. Un usuario es una persona
que puede conectarse a la base de datos. El usuario tendrá un nombre de usuario y una contraseña. Un
esquema es un contenedor para los objetos en propiedad de un usuario. Cuando se crea un usuario,
también se crea su esquema. Un esquema son los objetos en propiedad de un usuario; inicialmente,
estará vacío.

Algunos esquemas siempre estarán vacíos: el usuario nunca creará ningún objeto, porque no es necesario
y (si el usuario está configurado correctamente) no dispondrá de privilegios de todos modos. A algunos
usuarios se les habrán concedido permisos, ya sea a través de privilegios directos o a través de roles,
para utilizar el código y acceder a los datos en otros esquemas, propiedad de otros usuarios. Otros
usuarios pueden ser lo contrario de esto, poseerán muchos objetos, pero nunca se conectarán a la base
de datos. Ni siquiera se les tiene que haber concedido el privilegio de CREATE SESSION, por lo que la
cuenta estará efectivamente deshabilitada (también puede ser bloqueada). Estos esquemas se utilizan
como repositorios de código y datos a los que acceden otros.
Los objetos de esquema son objetos con un propietario. El identificador único para un objeto de un tipo
particular no es su nombre; es su nombre prefijado con el nombre del esquema al que pertenece. Por lo
tanto, la tabla [Link] es una tabla denominada REGIONS, que es propiedad del usuario HR. Podría
haber otra tabla [Link] que sería una tabla completamente diferente (quizás diferente tanto
en estructura como en contenido) propiedad del usuario SYSTEM y que residen en su esquema.
Se crean automáticamente varios usuarios (y sus esquemas asociados) cuando se produce la creación
de la base de datos. Los principales son SYS y SYSTEM. El usuario SYS posee el diccionario de datos: un
conjunto de tablas (en el esquema SYS) que definen la base de datos y su contenido. SYS también posee
varios cientos de paquetes PL/SQL: código que se proporciona para el uso de administradores y
desarrolladores de bases de datos. Los objetos en el esquema SYS nunca deben modificarse con
comandos DML. Si se ejecutara DML contra las tablas del diccionario de datos, se correría el riesgo de
corromper el diccionario de datos, con resultados desastrosos. El diccionario de datos se actualiza
ejecutando comandos DDL (como CREAR TABLA), que proporcionan una capa de abstracción entre el
usuario y el propio diccionario de datos. El esquema SYSTEM almacena v arios objetos adicionales
utilizados para administración y monitoreo.

Dependiendo de las opciones seleccionadas durante la creación de la base de datos, puede haber más
usuarios creados. Estos usuarios almacenan el código y los datos requeridos por varias opciones de base
de datos.

Por ejemplo, el usuario MDSYS almacena los objetos usados por Spatial, una opción que amplía las
capacidades de la base de datos Oracle para gestionar la información geográfica.

11.1.3. Nombrando Objetos de Esquema


Un objeto de esquema es un objeto que pertenece a un usuario. Todos los nombres de objetos de
esquema deben cumplir ciertas reglas:

• El nombre puede tener de 1 a 30 caracteres (excepto los nombres de enlaces de la base de datos
que pueden tener hasta 128 caracteres).
• Las palabras reservadas (como SELECT) no pueden utilizarse como nombres de objetos.
• Todos los nombres deben comenzar con una letra de la A a la Z.
• Los caracteres de un nombre sólo pueden ser letras, números, un guion bajo (_), el signo del
dólar ($), o el símbolo de almohadilla (#).
• Las letras minúsculas se convertirán en mayúsculas.

290/306
SQL

Al incluir el nombre entre comillas dobles, todas estas reglas (con la excepción de la longitud) pueden
romperse. El acceso posterior al objeto deberá hacerse siempre especificando su nombre entre comillas
dobles. Téngase en cuenta que las mismas restricciones también se aplican a los nombres de columna.
Aunque herramientas como SQL*Plus o SQL Developer convertirán automáticamente letras minúsculas
a mayúsculas, a menos que el nombre esté entre comillas dobles, recuerde que los nombres de los
objetos siempre distinguen entre mayúsculas y minúsculas. En este ejemplo, los dos son completamente
diferentes:

CREATE TABLE lower (c1 date);


Table created.

CREATE TABLE "lower" (col1 varchar2(2));


Table created.

SELECT table_name
FROM user_tables
WHERE lower(table_name) = 'lower';

TABLE_NAME
------------------------------ lower LOWER

291/306
SQL

Los nombres de objetos no deben tener más de 30 caracteres. Los caracteres usados pueden
ser letras, dígitos, guion bajo, dólar, o almohadilla.

Si bien es posible utilizar nombres en minúsculas y caracteres no estándar (incluso espacios),


se considera una mala práctica debido a la confusión que puede causar.

11.1.4. Espacio de nombres en los objetos


A menudo se dice que el identificador único de un objeto es el nombre del objeto, prefijado con el nombre
del esquema. Si bien esto es generalmente cierto, para una comprensión completa es necesario introducir
el concepto de espacio de nombres. Un espacio de nombres define un grupo de tipos de objeto dentro
del cual se deben identificar exclusivamente todas las denominaciones de esquema y nombre.
Los objetos en diferentes espacios de nombres pueden compartir el mismo nombre.
Todos estos tipos de objeto comparten el mismo espacio de nombres:

• Tablas
• Vistas
• Secuencias
• Sinónimos privados
Por lo tanto, es imposible crear una vista con el mismo nombre que una tabla, al menos si están en el
mismo esquema. Y una vez creadas, las sentencias SQL pueden referirse a una vista o un sinónimo como
si fuera una tabla. El hecho de que las tablas, las vistas y sinónimos privados comparten el mismo espacio
de nombres significa que se pueden configurar varias capas de abstracción entre lo que ven los usuarios
y las tablas reales, que pueden ser invaluable tanto para la seguridad como para simplificar el desarrollo
de aplicaciones. Los índices y las restricciones, tiene cada uno su propio espacio de nombres. Por lo
tanto, es posible que un índice tenga el mismo nombre que una tabla, incluso dentro del mismo esquema.

11.2. Revisión de la estructura de la tabla


Según el paradigma de la base de datos relacional, una tabla es una estructura bidimensional que
almacena registros. Un registro tiene una o más columnas. Todos los registros comparten las mismas
columnas siguiendo la estructura de la tabla. La base de datos Oracle permite variaciones de este modelo
bidimensional. Algunas columnas pueden definirse como tablas anidadas que a su vez tienen varias
columnas. Otras columnas pueden ser de un tipo de datos ilimitado como un objeto binario grande,
teóricamente del orden de terabytes. También se pueden definir columnas como objetos. El objeto tendrá
una estructura interna (posiblemente basado en columnas) que no es visible como parte de la tabla.
En la fase de análisis de sistemas del ciclo de vida de desarrollo del sistema se habrán modelado las
estructuras de datos necesarias para almacenar la información del sistema en tercera forma normal. El
resultado es un conjunto de tablas bidimensionales, cada una con clave primaria, y enlazadas entre sí
mediante claves externas. La fase de diseño del sistema puede haber comprometido esta estructura, tal
vez por revertir la normalización de las tablas o por aprovecharse de las capacidades específicas de
Oracle, como las tablas anidadas. El resultado final, en lo que respecta al desarrollador de SQL, es un
conjunto de tablas.

Cada tabla aparece como una definición en el diccionario de datos. En la creación, a la tabla se le habrá
asignado una cantidad limitada de espacio (conocido como extensión) dentro de la base de datos. Este
puede ser pequeño, quizás sólo unos pocos kilobytes o megabytes. Cuando se inserten los registros, esta
extensión se llenará. Cuando esté llena, la base de datos asignará otra extensión a la tabla. A medida
que se borran los registros, el espacio dentro de la extensión asignado estará disponible para su
reutilización, incluso si se borran todos los registros de la tabla. Sólo será liberado y devuelto a la base

292/306
SQL

de datos para usarlo en otro lugar si la tabla se borra o se trunca.

11.3. Listar los tipos de datos disponibles por columna


Al crear tablas, a cada columna se le debe asignar un tipo de datos que determine la naturaleza de los
valores que se pueden insertar en la columna. Estos tipos de datos también se usan para especificar la
naturaleza de los argumentos para los procedimientos y funciones PL/SQL.
Al seleccionar un tipo de datos, se debe tener en cuenta los datos que se necesita almacenar y las
operaciones que se querrá realizar sobre ellos. El espacio también es una consideración, algunos de los
tipos de datos son de longitud fija, ocupando el mismo número de bytes sin importar los datos
almacenados realmente en él; otros son variables. Si una columna no está poblada, entonces Oracle no
le da espacio. Si más tarde se actualiza el registro para rellenar la columna, entonces el registro se hará
más grande sin importar si el tipo de datos es de longitud fija o variable. En 12c un nuevo parámetro de
sistema, MAX_STRING_SIZE, permite a los tipos de datos de cadena de caracteres ser mucho más grande
que en versiones anteriores cuando se cambia de su valor predeterminado de STANDARD a EXTENDED.
Los siguientes son los tipos de datos para datos alfanuméricos:

• NVARCHAR2: Igual que VARCHAR2, pero los datos se almacenan en el juego de caracteres de
idioma nacional, uno de los juegos de caracteres Unicode permitidos.
• CHAR: Datos de caracteres de longitud fija, de 1 byte a 2000 bytes. Si los datos no son de la
longitud de la columna, entonces serán rellenados con espacios.

El siguiente es el tipo para datos binarios:

• RAW: Datos binarios de longitud variable, desde 1 byte hasta 4000 bytes si
MAX_STRING_SIZE=STANDARD o 32767 bytes si MAX_STRING_SIZE=EXTENDED. A diferencia
de los tipos de datos CHAR y VARCHAR2, RAW no convierte los datos de tipo carácter de la base
de datos a caracteres del proceso de usuario en SELECT o INSERT.

Los siguientes son los tipos para datos numéricos, todos de longitud variable:

• NUMBER: Datos numéricos, para los que se puede especificar la precisión y la escala. La precisión
puede variar de 1 a 38, la escala puede variar de -84 a 127.
• FLOAT: Éste es un tipo de datos ANSI, un número de coma flotante con una precisión de 126
binarios (o 38 decimales). Oracle también proporciona BINARY_FLOAT y BINARY_DOUBLE como
alternativas.
• INTEGER: Equivalente a NUMBER, con escala cero.

Los siguientes son los tipos para datos de fecha y hora, todos de longitud fija:

• DATE: La longitud es 0 si está vacía, o 7 bytes en caso contrario. Todos los datos de FECHA
incluyen siglo, año, mes, día, hora, minuto y segundo. El rango válido es del 1 de enero de 4712
a.C. al 31 de diciembre de 9999 d.C.
• TIMESTAMP: Esta es de longitud cero si la columna está vacía, o hasta 11 bytes, dependiendo
de la precisión especificada. Similar a DATE, pero con una precisión de hasta 9 decimales para
los segundos, 6 posiciones por defecto.

• TIMESTAMP WITH TIMEZONE: Como TIMESTAMP, pero los datos se almacenan con un registro
de la zona horaria a la que se refiere. La longitud puede ser de hasta 13 bytes, dependiendo de

293/306
SQL

la precisión. Este tipo de datos permite a Oracle determinar la diferencia entre dos horas
normalizándolas a UTC, incluso si las horas son de zonas horarias diferentes.
• TIMESTAMP WITH LOCAL TIMEZONE: Como TIMESTAMP, pero los datos se normalizan a la zona
horaria de la base de datos al guardarlos. Cuando se recupera, se normaliza a la zona horaria
del proceso de usuario que lo selecciona.
• INTERVAL YEAR TO MONTH: Se utiliza para registrar un período en años y meses entre dos DATEs
o TIMESTAMPs.
• INTERVAL DAY TO SECOND: Se utiliza para registrar un período en días y segundos entre dos
DATEs o TIMESTAMPs.
Los siguientes son los tipos de datos de objeto grandes:

• CLOB: Datos de caracteres almacenados en la base de datos, tamaño ilimitado: (4GB -1)
multiplicado por el tamaño del bloque de la base de datos.
• NCLOB: Como CLOB, pero los datos se almacenan en el juego de caracteres de idioma nacional
alternativo, uno de los juegos de caracteres Unicode permitidos.
• BLOB: Como CLOB, pero con datos binarios que no serán convertidos por Oracle Net.
• BFILE: Un puntero que apunta a un archivo almacenado en el sistema operativo del servidor de
base de datos. El tamaño de los archivos está limitado a 4 GB.
• LONG: Datos de caracteres en la base de datos, hasta 2 GB. Toda la funcionalidad de LONG (y
más) es proporcionada por CLOB; LONG no se debería usar en una base de datos moderna, y si
su base de datos tiene alguna columna de este tipo deberían ser convertidas a CLOB. Sólo puede
haber una columna LONG en una tabla.
• LONG RAW: Como LONG, pero con datos binarios que no serán convertidos por Oracle Net.
Cualquier columna LONG RAW debe ser convertida a BLOBs.

El siguiente es el tipo de datos ROWID:

• ROWID: Un valor codificado en base 64 que es el puntero a la ubicación de un registro en una


tabla. Encriptado dentro está la dirección física exacta. ROWID es un tipo de datos propietario
de Oracle, no visible a menos que se seleccione específicamente.

El tipo de datos VARCHAR2 debe calificarse con un número que indique la longitud máxima de la columna.
Si se inserta un valor menor en la columna, el valor sólo ocupará el espacio que necesite. Si el valor es
mayor que este máximo, el INSERT fallará con un error. Si el valor se actualiza en un valor mayor o
menor, la longitud de la columna (y por lo tanto el registro) se modificará en consecuencia. Si no se
ingresa en absoluto o se actualiza a NULL, entonces no ocupará ningún espacio.

El tipo de datos NUMBER se puede calificar opcionalmente con una precisión y una escala. La precisión
establece el número máximo de dígitos decimales significativos, donde el dígito más significativo es el
dígito más a la izquierda que no es cero, y el menos significativo es el dígito conocido más a la derecha
del número. La escala es el número de dígitos desde el punto decimal hasta el dígito menos significativo.
Una escala positiva es el número de dígitos significativos a la derecha del punto decimal hasta el dígito
menos significativo (incluido). Una escala negativa es el número de dígitos significativos a la izquierda
del punto decimal hasta el dígito menos significativo (no incluido).
El tipo de datos DATE siempre incluye siglo, año, mes, día, hora, minuto y segundo, incluso si no se
especifican todos estos elementos a la hora de insertar. Se debe especificar el año, el mes y el día; si se
omiten las horas, los minutos y los segundos, el valor predeterminado será medianoche. El uso de la
función TRUNC en una fecha también tiene el efecto de ajustar las horas, minutos y segundos hasta la
medianoche.

294/306
SQL

Oracle proporciona una gama de funciones de conversión de tipos para convertir entre tipos de datos y
en algunas circunstancias hará la conversión de tipos automáticamente. La siguiente figura ilustra el uso
de las técnicas de conversión manual y automática.
En el siguiente ejemplo, el primer INSERT utiliza funciones de tipo casting para convertir los datos de
carácter introducidos en los tipos de datos especificados para las columnas de la tabla. El segundo INSERT
intenta insertar cadenas de caracteres en las tres columnas, pero la inserción sigue teniendo éxito porque
Oracle puede convertir tipos de datos automáticamente si es necesario, pero sólo si el formato de los
datos es adecuado. Si el valor de la fecha se hubiera introducido en cualquier otro formato que no sea
DD-MM-YY, como '23-Nov-13', habría fallado.

11.4. Crear una tabla básica

11.4.1. Creación de tablas con especificaciones de columna


Las tablas pueden almacenarse en la base de datos de varias maneras. La más simple es la tabla HEAP.
Un HEAP contiene registros de longitud variable en orden aleatorio. Puede haber correlación entre el
orden en el que se introducen los registros y el orden en el que se almacenan, pero es cuestión de suerte.
Otras estructuras de tabla más avanzadas pueden imponer orden y agrupamiento de registros o forzar
una distribución aleatoria. Algunos ejemplos son:

• Tablas organizadas indexadas: Almacenan registros en orden según una clave índice.
• Bloques indexados: Pueden desnormalizar las relaciones padre-hijo de las tablas de manera
que se pueden agrupar registros de diferentes tablas.
• Bloques referenciados: Fuerzan una distribución aleatoria de registros, que descompondrá
cualquier orden basado en la secuencia de entrada.

295/306
SQL

• Tablas particionadas: Almacenan registros en estructuras físicas separadas, las particiones,


asignando registros según el valor de una columna.
El uso de tablas con estructuras más avanzadas no tiene ningún efecto sobre SQL. Cada sentencia SQL
ejecutada contra tablas definidas con estas opciones devolverá exactamente los mismos resultados que
si las tablas fueran tablas HEAP estándar, por lo que el uso de estas características no afectará al código;
pero si bien su uso es transparente para los programadores, ofrecen enormes beneficios en el
rendimiento.
Para crear tablas HEAP estándar, la sintaxis es:

CREATE TABLE [schema.]table [ORGANIZATION HEAP]

(column datatype [DEFAULT expression]

[,column datatype [DEFAULT expression]…);

Como mínimo, se especifica el nombre de la tabla (se creará en su propio esquema, si no especifica el
de otra persona) y al menos una columna con un tipo de datos. Hay muy pocos desarrolladores que
especifiquen ORGANIZATION HEAP, ya que éste es el valor por defecto y es el estándar SQL de la
industria. La palabra clave DEFAULT en una definición de columna permite proporcionar una expresión
que generará un valor para la columna cuando se inserte una fila si la sentencia INSERT no proporciona
un valor.
Considérese esta sentencia:

CREATE TABLE [Link]


(EMPNO NUMBER(4),
ENAME VARCHAR2(10),
HIREDATE DATE DEFAULT TRUNC(SYSDATE),
SAL NUMBER(7,2),
COMM NUMBER(7,2) DEFAULT 0.03);
Esta creará una tabla llamada EMP en el esquema SCOTT. Bien el propio usuario SCOTT debe ejecutar la
expresión (en cuyo caso la nominación del esquema no sería entonces necesaria), o cualquier otro
usuario podría ejecutarla si previamente se le ha concedido permiso para crear tablas en el esquema de
otro usuario. Describiendo las columnas una por una:

• EMPNO: Puede ser de hasta 4 dígitos de longitud sin parte decimal. Si se introducen partes
decimales en un INSERT, el valor se redondeará por exceso o por defecto al entero más próximo.
ENAME: Puede almacenar hasta 10 caracteres de cualquier tipo.
• HIREDATE: Acepta cualquier fecha, opcionalmente con la hora, pero si el valor no se introduce,
se almacenará el valor de la fecha actual a medianoche.
• SAL: Destinada a salarios de empleados, acepta valores numéricos de hasta 7 dígitos. Si incluye
parte decimal, esta se redondeará.
• COMM (para porcentaje de comisión): Tiene un valor por defecto de 0.03 que se introducirá si la
sentencia INSERT no incluye un valor para esta columna.

Después de la creación de la tabla, estas expresiones insertan un registro y seleccionan el resultado:

296/306
SQL

Téngase en cuenta que los valores de las columnas no mencionados en la sentencia INSERT han sido
generadas por las cláusulas DEFAULT. Si esas cláusulas no se hubieran definido en las columnas, habrían
sido NULL. Obsérvese también el redondeo del valor previsto para SAL.

La cláusula DEFAULT puede ser útil, pero tiene una funcionalidad limitada. No se puede utilizar
una subconsulta para generar el valor propuesto. Sólo se pueden especificar valores literales
o funciones.

11.4.2. Creación de tablas utilizando subconsultas


En lugar de crear una tabla de la nada e insertar registros en ella (como en la sección anterior), las tablas
se pueden crear a partir de otras tablas utilizando una subconsulta. Esta técnica permite crear la
definición de tabla y rellenar la tabla con registros con sólo una sentencia. Cualquier consulta puede ser
usada como fuente de la tabla y de los registros. La sintaxis es la siguiente:

CREATE TABLE [schema.]table AS subquery;

Todas las consultas devuelven un conjunto bidimensional de registros; este resultado se almacena como
la nueva tabla. Un ejemplo simple de cómo crear una tabla con una subconsulta es:

CREATE TABLE employees_copy AS


SELECT *
FROM employees;

Esta sentencia creará la tabla EMPLOYEES_COPY, la cual es una copia exacta de la tabla EMPLOYEES,
idéntica tanto en la definición como en los registros que contiene.

Cualquier constraint de tipo “not null” y “check” en las columnas también se aplicará a la nueva tabla,
pero no se aplicará ninguna restricción de clave primaria, única o de clave externa. Las restricciones se
discuten en una sección posterior. Esto se debe a que estos tres tipos de restricciones requieren índices
que podrían no estar disponibles o no ser deseados.

El siguiente ejemplo es más complejo:

CREATE TABLE emp_dept AS


SELECT last_name ename, department_name dname, round(sysdate – hire_date) service FROM
employees
NATURAL JOIN departments
ORDER BY dname, ename;

297/306
SQL

Los registros en la nueva tabla serán el resultado de unir las dos tablas, con dos de las columnas
seleccionadas con sus nombres cambiados. La nueva columna SERVICIO se rellenará con el resultado de
la aritmética que calcula el número de días desde que se contrató al empleado. Los registros se insertarán
en el orden especificado. Este orden no será mantenido por el DML subsiguiente, pero suponiendo los
datos del esquema HR estándar, la nueva tabla tendrá el siguiente aspecto:

La subconsulta puede incluir una cláusula WHERE para restringir los registros insertados en la nueva
tabla. Para crear una tabla sin registros, utilice una cláusula WHERE que excluya todos los registros:

CREATE TABLE no_emps AS


SELECT *
FROM employees
WHERE 1=2;

La cláusula WHERE 1=2 nunca puede devolver TRUE, así que la estructura de la tabla será creada lista
para usar, pero no se insertarán registros en el momento de la creación.

11.4.3. Modificado de definiciones de tabla después de ser creada


Son muchas las modificaciones que se le pueden hacer a una tabla después de su creación. Aquellas que
afectan al almacenamiento físico recaen en las funciones del administrador de la base de datos, pero
muchos cambios son puramente lógicos y serán llevados a cabo por los desarrolladores SQL. Los
siguientes son ejemplos:

• Añadir columnas:

ALTER TABLE emp

ADD (job_id number);

• Modificar columnas:

ALTER TABLE emp

MODIFY (comm number(4,2) DEFAULT 0.05);

298/306
SQL

• Borrar columnas:

ALTER TABLE emp

DROP COLUMN comm;

• Marcar columnas como no utilizadas:

ALTER TABLE emp

SET UNUSED COLUMN job_id;

• Renombrar columnas:

ALTER TABLE emp

RENAME COLUMN hiredate TO recruited;

• Marcar una tabla como sólo lectura :

ALTER TABLE emp

READ ONLY;
Todos estos cambios son comandos DDL con el COMMIT incorporado. Estos son irreversibles y fallarán si
hay una transacción activa contra la tabla. También son virtualmente instantáneos, con la excepción de
los borrados de columna. Borrar una columna puede ser un ejercicio que consume mucho tiempo porque
a medida que cada columna se borra, cada registro debe ser reestructurado para eliminar los datos de
la columna. El comando SET UNUSED, que hace que las columnas no existan en lo que se refiere a SQL,
es a menudo una mejor alternativa:

ALTER TABLE tablename

DROP UNUSED COLUMNS;

Este borra todas las columnas no utilizadas en un solo paso a través de la tabla.
Marcar una tabla como ‘Sólo lectura’ causará errores en cualquier intento de comando DML. Pero la tabla
todavía se puede borrar. Esto puede ser desconcertante, pero es perfectamente lógico. Un comando
DROP no afecta a la tabla, afecta a las tablas del diccionario de datos que definen la tabla, y éstas no
son de sólo lectura.

11.4.4. Borrado y truncado de tablas


El comando TRUNCATE TABLE fue descrito anteriormente: tiene el efecto de eliminar cada registro de
una tabla, dejando intacta la definición de la tabla. DROP TABLE es más drástico en el sentido de que
también se elimina la definición de la tabla. La sintaxis es como sigue:

DROP TABLE [schema].tablename;

Si no se especifica el esquema, se eliminará la tabla llamada tablename en el esquema conectado


actualmente.

299/306
SQL

Al igual que con un TRUNCATE, SQL no producirá una advertencia antes de que la tabla sea borrada,
además como cualquier comando DDL, incluye un COMMIT.
Pero hay algunas restricciones: si alguna sesión tiene una transacción en progreso que incluye un registro
en la tabla, entonces el DROP fallará y también será imposible borrar una tabla a la que se hace referencia
en una restricción de clave foránea definida en otra tabla. Esta tabla (o la restricción) debe ser eliminada
primero.

Oracle 12c incluye una papelera de reciclaje que está habilitada por defecto. Esta permite
restaurar cualquier tabla que se haya borrado a menos que se haya eliminado con la opción
PURGE o se haya desactivado la opción de la papelera de reciclaje.

11.5. Explicación de cómo se crean las restricciones en el momento de creación de la


tabla
Las restricciones de tabla son una herramienta mediante la cual la base de datos puede hacer cumplir
las reglas de negocio y garantizar que los datos se ajusten al modelo de entidad-relación determinado
por el análisis de sistemas que define la estructura de datos de la aplicación. Por ejemplo, los analistas
empresariales de una empresa pueden haber decidido que cada cliente y cada factura deben ser
identificables unívocamente por número; que no se pueden emitir facturas a un cliente antes de que éste
haya sido creado y que cada factura debe tener una fecha válida y un valor mayor que cero. Éstas se
implementarían creando restricciones de clave primaria en la columna CUSTOMER_NUMBER de la tabla
CUSTOMERS y en la columna INVOICE_NUMBER de la tabla INVOICES, una restricción de clave externa
en la tabla INVOICES que hace referencia a la tabla CUSTOMERS, una restricción no nula en la columna
DATE de la tabla INVOICES y una restricción de verificación en la columna AMOUNT de la tabla INVOICES.
Cuando se ejecuta cualquier DML contra una tabla con restricciones definidas, si el DML viola una
restricción, entonces toda la sentencia retrocederá automáticamente. Recordar que una declaración de
DML que afecte a muchos registros podría tener éxito parcialmente antes de llegar a un problema de
restricción con un registro en particular. Si el extracto forma parte de una transacción de varios extractos,
aquellos que ya han tenido éxito se mantendrán intactos pero "sin confirmar" (uncommited).

11.5.1. Los tipos de restricciones


Los tipos de restricción soportados por la base de datos Oracle son los siguientes:

• UNIQUE
• NOT NULL
• PRIMARY KEY
• FOREIGN KEY
• CHECK
Todas las restricciones tienen nombre. Es una buena práctica especificar los nombres con una convención
de nomenclatura estándar, pero si no se nombran explícitamente, Oracle generará nombres para cada
una de ellas.

a. Limitaciones UNIQUE (únicas)


Una restricción única designa una columna (o combinación de columnas) para la cual el valor debe ser
diferente para cada registro de la tabla. Si se basa en una sola columna, se denomina columna clave. Si

300/306
SQL

la restricción está compuesta de más de una columna (conocida como restricción de clave compuesta),
las columnas no tienen que ser del mismo tipo de datos ni estar adyacentes en la definición de tabla.
Una rareza de las restricciones únicas es que es posible introducir un valor NULL en la(s) columna(s)
clave; de hecho, es posible tener cualquier número de registros con valores NULL en su(s) columna(s)
clave. Por lo tanto, seleccionar registros en una columna clave garantizará que sólo se devuelva un
registro, a menos que busque NULL, en cuyo caso se devolverán todos los registros en las que las
columnas clave sean NULL.
Las restricciones únicas son impuestas por un índice. Cuando se define una restricción única, Oracle
buscará un índice en la(s) columna(s) clave, y si no existe se creará. Luego, cada vez que se inserta un
registro, Oracle buscará en el índice para ver si los valores de las columnas clave ya están presentes: si
lo están, rechazará la inserción. La estructura de estos índices (conocidos como índices B-Árbol) no
incluye valores NULL, por lo que se permiten muchos registros con NULL: simplemente no existen en el
índice. Aunque el primer objetivo del índice es hacer cumplir la restricción, tiene un efecto secundario:
mejorar el rendimiento si se utilizan las columnas clave en las cláusulas WHERE de las sentencias SQL.
Sin embargo, al seleccionar WHERE key_column IS NULL no puede usar el índice porque no incluye los
valores NULL y por lo tanto el resultado siempre será el escaneado de toda la tabla.

b. Restricciones NOT NULL (no nulas)


La restricción no nula obliga a introducir valores en la columna clave. Se definen restricciones no nulas
por columna; si el requisito empresarial es que un grupo de columnas tenga valores, no se puede definir
una restricción no nula para todo el grupo, sino que se debe definir una restricción no nula para cada
columna.

Cualquier intento de insertar un registro sin especificar valores para las columnas restringidas no nulas
produce un error. Es posible evitar la necesidad de especificar un valor incluyendo una cláusula DEFAULT
en la columna cuando se crea la tabla.

c. Restricciones PRIMARY KEY (clave primaria)


La clave primaria es el medio de localizar un solo registro en una tabla. El paradigma de la base de datos
relacional incluye el requisito de que cada tabla debe tener una clave primaria, una columna que pueda
utilizarse para distinguir cada registro. La base de datos Oracle se desvía del paradigma al permitir tablas
sin claves primarias.
La implementación de una restricción clave primaria es, en efecto, la unión de una restricción única y
una restricción no nula. Las columnas clave deben tener valores únicos y no pueden ser nulas. Al igual
que con las restricciones únicas, debe existir un índice en la columna o columnas restringidas. Si uno no
existe ya, se creará un índice cuando se defina la restricción. Una tabla sólo puede tener una clave
primaria e intentar crear una segunda devolverá un error. Sin embargo, una tabla puede tener cualquier
número de restricciones únicas y columnas no nulas, por lo que si hay varias columnas que los analistas
de negocio han decidido que deben ser únicas y pobladas, una de ellas puede ser designada como la
clave primaria y las otras como únicas y no nulas. Un ejemplo podría ser una tabla de empleados, donde
la dirección de correo electrónico, el número de Seguridad Social y el número de empleado deben ser
todos obligatorios y únicos.

301/306
SQL

d. Restricciones CHECK (de verificación)


Se puede utilizar una restricción de verificación para hacer cumplir reglas simples, como que el valor
introducido en una columna debe estar dentro de un rango de valores. La regla debe ser una expresión
que evalúe a TRUE o FALSE. Las reglas pueden referirse a valores absolutos introducidos literalmente o
a otras columnas del mismo registro y pueden hacer uso de algunas funciones. Se pueden aplicar tantas
restricciones de verificación como se desee a una columna, pero no es posible utilizar una subconsulta
para evaluar si un valor es permisible o utilizar funciones como SYSDATE.

e. Restricciones FOREIGN KEY (clave ajena)


En una relación padre-hijo se define una restricción de clave ajena en la tabla hijo. La restricción propone
una columna (o columnas) en la tabla secundaria que corresponde a la(s) columna(s) clave primaria en
la tabla principal (padre). Las columnas no tienen que tener los mismos nombres, pero deben ser del
mismo tipo de datos. Las restricciones de clave externa definen la estructura relacional de la base de
datos: las relaciones de muchos a uno que conectan la tabla, en su tercera forma normal.
Si la tabla padre tiene restricciones únicas, así como una restricción de clave primaria, estas columnas
se pueden utilizar como base de restricciones de clave ajena, incluso, si es posible, poner valores nulos.

Así como una restricción única permite valores nulos en la columna restringida, también lo hace una
restricción de clave ajena. Se puede insertar registros en la tabla secundaria con columnas de clave
ajenas nulas, incluso si no hay ningún registro en la tabla padre con un valor nulo. Esto crea registros
huérfanos y puede causar una terrible confusión. Como regla general, todas las columnas de una
restricción única y todas las columnas de una restricción de clave ajena se definen mejor con restricciones
no nulas también; esto será a menudo un requisito empresarial.
Si se intenta insertar un registro en la tabla secundaria para la que no hay ningun registro coincidente
en la tabla padre, se producirá un error. Del mismo modo, borrar un registro en la tabla padre dará un
error si ya hay registros que se refieran a ella en la tabla secundaria. Hay dos técnicas para cambiar este
comportamiento. En primer lugar, la restricción se puede crear como ON DELETE CASCADE. Por tanto, si
se elimina un registro de la tabla padre, Oracle buscará en la tabla secundaria todas los registros
coincidentes y las eliminará también.
Esto ocurrirá automáticamente. Una técnica menos drástica es crear la restricción como ON DELETE SET
NULL. En este caso, si se elimina un registro de la tabla principal, Oracle buscará en la tabla secundaria
todos los registros que coincidan y establecerá las columnas de clave ajena como nulas. Esto significa
que los registros inferiores quedarán huérfanos, pero seguirán existiendo. Si las columnas en la tabla
secundaria también tienen una restricción no nula, entonces la eliminación de la tabla padre fallará.
No es posible borrar la tabla padre en una relación de clave ajena, incluso si no hay registros en la tabla
secundaria. Esto sigue siendo válido si se utilizaron las cláusulas ON DELETE SET NULL u ON DELETE
CASCADE.

Una variación de la restricción de la clave ajena es la restricción de la clave ajena autorreferenciada. Esto
define una condición en la que los registros padre e hijo existen en la misma tabla. Un ejemplo sería una
tabla de empleados que incluye una columna para el gerente del empleado. El propio gerente es un
empleado y debe existir en la tabla. Por lo tanto, si la clave primaria es la columna EMPLOYEE_NUMBER
y el administrador está identificado por una columna MANAGER_NUMBER, la restricción de clave ajena
indicará que el valor de la columna MANAGER_NUMBER debe referirse a un EMPLOYEE_NUMBER válido.
Si un empleado es su propio manager, la línea se referirá a sí misma.

302/306
SQL

11.5.2. Definición de restricciones (constraints)


Las restricciones pueden definirse al crear una tabla o añadirse a la tabla más tarde. Al definir
restricciones en el momento de la creación de la tabla, la restricción se puede definir en línea con la
columna a la que se refiere o al final de la definición de la tabla. El uso de esta última técnica es más
flexible. Por ejemplo, es imposible definir una restricción de clave ajena que se refiera a dos columnas o
una restricción de verificación que se refiera a cualquier columna distinta de la que está siendo restringida
si la restricción está definida en la línea, pero ambas son posibles si la restricción está definida al final
de la tabla.

Para las restricciones que requieren un índice (las restricciones clave únicas y primarias). El índice se
creará con la tabla si la restricción se define en el momento de la creación de la tabla.

Problema Solución

Se están diseñando estructuras de tabla Probablemente no. Las restricciones


para una aplicación de recursos tienen la intención de hacer cumplir
reglas de negocio simples y esto puede
humanos. Los analistas de negocios han
ser demasiado complicado. Es posible
dicho que cuando un empleado deja la que sea necesario utilizar un
compañía, su registro de empleado debe desencadenante DML en la tabla activa,
ser movido a una tabla de archivo. que insertará automáticamente un
registro en la tabla de archivo cada vez
¿Pueden ayudar las restricciones? que se borre a un empleado de la tabla
activa. Los triggers(disparadores)
pueden realizar un tratamiento mucho
más complicado que una restricción.

Las transacciones activas bloquean Quizás no se debería estar haciendo esto


algunas sentencias DDL contra tablas. Si cuando la base de datos está en uso,
pero se debería esperar hasta el
se desea añadir una restricción o
siguiente período de inactividad
renombrar una columna en una tabla programado. Sin embargo, si realmente
ocupada y se encuentra que la sentencia se necesita hacer el cambio
siempre falla con rápidamente, se le pide al administrador
de la base de datos que paralice la base
"ORA-00054: resource busy and acquire de datos: este es un proceso que
with NOWAIT specified or timeout congelará todas las sesiones de usuario.
expired", ¿qué se puede hacer? Si se es muy rápido, se puede hacer el
cambio y luego restaurar la base de
datos antes de que los usuarios finales
se quejen.

Considerar estas dos expresiones de creación de tablas (a las que se han añadido números de registro):

303/306
SQL

1 CREATE TABLE dept(


2 deptno NUMBER(2,0) CONSTRAINT dept_deptno_pk PRIMARYKEY
3 CONSTRAINT dept_deptno_ck CHECK (deptno BETWEEN 10 AND 90),
4 dname VARCHAR2(20) CONSTRAINT dept_dname_nn NOT NULL);
5 CREATE TABLE emp(
6 empno NUMBER(4,0) CONSTRAINT emp_empno_pk PRIMARY KEY,
7 ename VARCHAR2(20) CONSTRAINT emp_ename_nn NOT NULL,
8 mgr NUMBER(4,0) CONSTRAINT emp_mgr_fk REFERENCES emp (empno),
9 dob DATE,
10 hiredate DATE,
11 deptno NUMBER(2,0) CONSTRAINT emp_deptno_fk REFERENCES dept(deptno)
12 ON DELETE SET NULL,
13 email VARCHAR2(30) CONSTRAINT emp_email_uk UNIQUE,
14 CONSTRAINT emp_hiredate_ck CHECK (hiredate>= dob + 365*16),
15 CONSTRAINT emp_email_ck
16 CHECK ((INSTR (email,'@') > 0) AND (INSTR (email,'.') > 0)));
Analizando estas declaraciones, línea por línea:

1. La primera tabla creada es DEPT, con la intención de tener un registro para cada departamento.
2. DEPTNO es numérico, 2 dígitos sin decimales. Esta es la clave primaria de la tabla. La restricción
se denomina DEPT_DEPTNO_PK.
3. Una segunda restricción que se aplica al DEPTNO es un control que lo limita a los números
comprendidos entre 10 y 90. La restricción se denomina DEPT_DEPTNO_CK.
4. La columna DNAME está formada por caracteres de longitud variable, con una restricción
DEPT_DNAME_NN que la hace no nula.
5. La segunda tabla creada es EMP, con la intención de tener un registro por cada empleado.
6. EMPNO es numérico, hasta 4 dígitos sin decimales. La restricción EMP_ EMPNO_PK lo marca
como la clave primaria de la tabla.
7. ENAME son caracteres de longitud variable, con una restricción EMP_ENAME_NN que lo hace no
nulo.
8. MGR es el gerente del empleado, que debe ser un empleado. La columna se define del mismo
modo que la columna clave primaria de EMPNO de la tabla. La restricción EMP_MGR_FK define
esta columna como autorreferencial por lo que cualquier valor introducido debe referirse a un
registro ya existente en el EMP (aunque no está restringido a no ser nulo, por lo que puede
dejarse en blanco).
9. DOB, la fecha de nacimiento del empleado, es una fecha y no está restringida.
10. HIREDATE es la fecha en que el empleado fue contratado y no está restringido. Al menos, todavía
no.
11. DEPTNO es el departamento al que está asociado el empleado. La columna se define del mismo
modo que la columna clave primaria de DEPTNO de la tabla DEPT, y la restricción
EMP_DEPTNO_FK refuerza una relación de clave ajena: no es posible asignar un empleado a un
departamento que no existe, aunque este sea nulo.
12. La restricción EMP_DEPTO_FK se define más adelante como ON DELETE SET NULL, de modo que,
si se borra el registro padre en DEPT, todos los registros secundarias coincidentes en EMPNO
tendrán DEPTNO a NULL.
13. EMAIL es un dato de caracteres de longitud variable, y debe ser único si se introduce (aunque
puede dejarse vacío).

304/306
SQL

14. Esto define una restricción adicional a nivel de tabla EMP_HIREDATE_CK. La restricción se verifica
para el trabajo infantil, rechazando los registros en los que la fecha de contratación no sea por
lo menos 16 años posterior a la fecha de nacimiento. Esta restricción no se puede definir en línea
con HIREDATE, ya que la sintaxis no permite referencias a otras columnas en ese punto.
15. Se añade una restricción adicional EMP_EMAIL_CK a la columna EMAIL, que realiza dos
verificaciones en la dirección de correo electrónico.
16. Las funciones INSTR buscan los caracteres arroba (@) y punto (.), que siempre estarán presentes
en una dirección de correo electrónico válida. Si no puede encontrar ambos, la condición CHECK
devolverá FALSE y el registro será rechazado.

Los ejemplos anteriores muestran varias posibilidades para definir restricciones en el momento de la
creación de la tabla. Las siguientes son otras posibilidades no cubiertas:

• Controlar la creación de índices para las restricciones clave única y primaria


• Definir si la restricción debe verificarse en el momento de insertar o más tarde cuando se
confirma la transacción.
• Indicar si la restricción se está aplicando o si está desactivada.
Es posible crear tablas sin restricciones y luego añadirlas con un comando ALTER TABLE. El resultado
final será el mismo, pero esta técnica hace que el código sea menos auto-documentado, ya que la
definición completa de la tabla se distribuirá en varias sentencias en lugar de estar en una sola.

11.6. Resumen
Categorizar los objetos principales de la base de datos

• Algunos objetos contienen datos, principalmente tablas e índices.


• Los objetos programáticos como los procedimientos y funciones almacenadas son código ejecutable.
• Las vistas y sinónimos son objetos que dan acceso a otros objetos.

Revisar la estructura de la tabla

• Las tablas son estructuras bidimensionales que almacenan registros definidos en columnas.
• Las tablas existen dentro de un esquema. El nombre del esquema con el nombre de la tabla
hacen un identificador único.

Tipos de datos que están disponibles para las columnas

• Los tipos de datos más comunes son VARCHAR2, NUMBER y DATE. Existen muchos más tipos.

Crear una tabla básica

• Las tablas pueden crearse desde cero o con una subconsulta.


• Tras su creación, las definiciones se pueden añadir, modificar o eliminar. La definición de la tabla
puede incluir valores por defecto para las columnas.

Explicar cómo se crean las restricciones en el momento de la creación de la tabla

• Las restricciones pueden definirse en el momento de la creación de la tabla o añadirse posteriormente.

305/306
SQL

• Una restricción puede ser definida con su columna o a nivel de tabla.


• Las restricciones a nivel de tabla pueden ser más complejas.
• Una tabla puede tener sólo una clave primaria, pero puede tener muchas claves únicas.
• Una clave primaria es funcionalmente equivalente a UNIQUE junto con NOT NULL.
• Una restricción única no detiene la inserción de varios valores nulos. Las restricciones de
clave ajena definen las relaciones entre las tablas.

306/306

También podría gustarte