Oracle Web 2003

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 234

1 - Instalación de Oracle.

Para este curso utilizaremos la versión Oracle 10g XE (Express Edition para Windows) Para descargar el mismo
debemos ingresar al sitio de Oracle:

1. Oracle 10g XE
2. Para permitir descargarlo del sitio seleccionamos con el mouse el control Radio "Accept License
Agreement".
3. Luego seleccionamos la versión "Oracle Database 10g Express Edition (Universal)" (OracleXEUniv.exe
(216,933,372 bytes))
4. El sitio de Oracle requiere que nos registremos. Debemos seleccionar "sign up now" y luego "Create your
Oracle account now", es decir crear una cuenta Oracle.
Luego de habernos registrado podemos descargar el motor de base de datos Oracle.
5. El paso siguiente es instalar el gestor de base de datos propiamente dicho. Ejecutamos el archivo que
acabamos de descargar: OracleXEUniv.exe
Debemos ir presionando el botón "siguiente" en el asistente de instalación, salvo cuando nos pide ingresar
la contraseña de la base de datos, es importante no olvidar dicha clave.
Luego de algunos minutos ya tenemos instalado el gestor de bases de datos Oracle en nuestro equipo.

La segunda aplicación que instalaremos será el "Oracle SQL Developer". Es un entorno visual que nos permite
comunicar con nuestro gestor de base de datos Oracle. Desde este entorno aprenderemos a administrar una base de
datos Oracle.

1. Debemos ingresar a la siguiente página para descargar el Oracle SQL Developer


2. Aceptamos la licencia y seleccionamos "Oracle SQL Developer for Windows (JDK1.5.0_06 is bundled in this
zip)
3. Luego de descargar el archivo procedemos a descomprimir el archivo zip en una carpeta (este programa no
requiere instalación)
4. En la carpeta donde descomprimimos debemos ejecutar el archivo sqldeveloper.exe

2 - Crear tablas (create table - describe - all_tables - drop table)

Existen varios objetos de base de datos: tablas, constraints (restricciones), vistas, secuencias, índices, agrupamientos
(clusters), disparadores (triggers), instantaneas (snapshots), procedimientos, funciones, paquetes, sinónimos,
usuarios, perfiles, privilegios, roles, etc.

Los primeros objetos que veremos son tablas.

Una base de datos almacena su información en tablas, que es la unidad básica de almacenamiento.
Una tabla es una estructura de datos que organiza los datos en columnas y filas; cada columna es un campo (o
atributo) y cada fila, un registro. La intersección de una columna con una fila, contiene un dato específico, un solo
valor.
Cada registro contiene un dato por cada columna de la tabla. Cada campo (columna) debe tener un nombre. El
nombre del campo hace referencia a la información que almacenará.
Cada campo (columna) también debe definir el tipo de dato que almacenará.

Las tablas forman parte de una base de datos.

Nosotros trabajaremos con la base de datos ya creada.

Para ver las tablas existentes tipeamos:

select * from all_tables;

Aparece una tabla que nos muestra en cada fila, los datos de una tabla específica; en la columna "TABLE_NAME"
aparece el nombre de cada tabla existente.

Al crear una tabla debemos resolver qué campos (columnas) tendrá y que tipo de datos almacenarán cada uno de
ellos, es decir, su estructura.

La sintaxis básica y general para crear una tabla es la siguiente:

create table NOMBRETABLA(


NOMBRECAMPO1 TIPODEDATO,
...
NOMBRECAMPON TIPODEDATO
);

La tabla debe ser definida con un nombre que la identifique y con el cual accederemos a ella.
Creamos una tabla llamada "usuarios" y entre paréntesis definimos los campos y sus tipos:

create table usuarios(


nombre varchar2(30),
clave varchar2(10)
);

Cada campo con su tipo debe separarse con comas de los siguientes, excepto el último.

Cuando se crea una tabla debemos indicar su nombre y definir al menos un campo con su tipo de dato. En esta tabla
"usuarios" definimos 2 campos:

- nombre: que contendrá una cadena de caracteres de 30 caracteres de longitud, que almacenará el nombre de
usuario y
- clave: otra cadena de caracteres de 10 de longitud, que guardará la clave de cada usuario.

Cada usuario ocupará un registro de esta tabla, con su respectivo nombre y clave.

Para nombres de tablas, se puede utilizar cualquier caracter permitido para nombres de directorios, el primero debe
ser un caracter alfabético y no puede contener espacios. La longitud máxima es de 30 caracteres.

Si intentamos crear una tabla con un nombre ya existente (existe otra tabla con ese nombre), mostrará un mensaje
indicando que a tal nombre ya lo está utilizando otro objeto y la sentencia no se ejecutará.

Para ver la estructura de una tabla usamos el comando "describe" junto al nombre de la tabla:

describe usuarios;

Aparece la siguiente información:

Name Null Type


-------------------------------
NOMBRE VARCHAR2(30)
CLAVE VARCHAR2(10)

Esta es la estructura de la tabla "usuarios"; nos muestra cada campo, su tipo y longitud y otros valores que no
analizaremos por el momento.

Para eliminar una tabla usamos "drop table" junto al nombre de la tabla a eliminar:

drop table NOMBRETABLA;

En el siguiente ejemplo eliminamos la tabla "usuarios":

drop table usuarios;

Si intentamos eliminar una tabla que no existe, aparece un mensaje de error indicando tal situación y la sentencia no
se ejecuta.

Problema:

Para probar todos los ejercicios resueltos y propuestos debemos ingresar al sqldeveloper.exe y luego en el entorno
crear una nueva conexión:

1. Elegimos File -> New y seleccionamos "New Connection" y presionamos el botón "Aceptar".
2. Ingresamos los campos Connection Name (por ejemplo ingresamos "prueba"), en el campo Username
ingresamos el usuario SYSTEM y por último en el campo Password ingresamos la clave que creamos al
instalar Oracle.
3. Luego en la ventana que aparece el mansaje "Enter SQL Statement" debemos tipear los comandos SQL y
mediante el primer botón "triangulo verde" ejecutaremos el comando SQL donde se encuentra el cursos
(también podemos ejecutar todos los comando SQL mediante el segundo botón.

Veamos las tablas existentes:

select * from all_tables;

Aparece una tabla que nos muestra todas las tablas; la columna "TABLE_NAME" contiene el nombre de cada tabla.

Vamos a crear una tabla denominada "usuarios". En primer lugar vamos a eliminar la tabla "usuarios" porque si ya
existe no podremos crear otra con el mismo nombre.

drop table usuarios;

Si la tabla no existe aparecerá un mensaje indicando tal situación.

Ahora si creamos una tabla llamada "usuarios" con dos campos:

- nombre: cadena de caracteres que no supere los 30 caracteres y


- clave: cadena que no supere los 10 caracteres:

create table USUARIOS(


nombre varchar2(30),
clave varchar2(10)
);

Aparece un mensaje que indica que la sentencia "create table" ha sido procesada.
Cada usuario ocupará un registro de esta tabla, con su respectivo nombre y clave.

Podemos verificar que se ha creado:

select *from all_tables;

La tabla "usuarios" Debe aparecer en la lista.

Veamos la estructura de la tabla "usuarios":

describe usuarios;

Aparece la siguiente información:

Name Null Type


-------------------------------
NOMBRE VARCHAR2(30)
CLAVE VARCHAR2(10)

Nos informa que la tabla "usuarios" tiene 2 campos, el campo "nombre" de tipo "varchar2" de 30 caracteres de
longitud y el campo "clave", de tipo "varchar2" de 10 caracteres de longitud. La columna "Null" aparece vacía y la
explicaremos más adelante.

Intentemos crear una tabla con el mismo nombre, mostrará un mensaje indicando que ya hay un objeto llamado
"usuarios" y la sentencia no se ejecutará:

create table usuarios (


nombre varchar(30),
clave varchar(10)
);

Eliminemos la tabla:

drop table usuarios;

Verifiquemos si se ha eliminado:
select *from all_tables;

no debe aparecer la tabla "usuarios".

3 - Ingresar registros (insert into- select)

Un registro es una fila de la tabla que contiene los datos propiamente dichos. Cada registro tiene un dato por cada
columna (campo). Nuestra tabla "usuarios" consta de 2 campos, "nombre" y "clave".

Al ingresar los datos de cada registro debe tenerse en cuenta la cantidad y el orden de los campos.

La sintaxis básica y general es la siguiente:

insert into NOMBRETABLA (NOMBRECAMPO1, ..., NOMBRECAMPOn)


values (VALORCAMPO1, ..., VALORCAMPOn);

Usamos "insert into", luego el nombre de la tabla, detallamos los nombres de los campos entre paréntesis y separados
por comas y luego de la cláusula "values" colocamos los valores para cada campo, también entre paréntesis y
separados por comas.

En el siguiente ejemplo se agrega un registro a la tabla "usuarios", en el campo "nombre" se almacenará "Mariano" y
en el campo "clave" se guardará "payaso":

insert into usuarios (nombre, clave)


values ('Mariano','payaso');

Luego de cada inserción aparece un mensaje indicando la cantidad de registros ingresados.

Note que los datos ingresados, como corresponden a cadenas de caracteres se colocan entre comillas simples.

Para ver los registros de una tabla usamos "select":

select *from usuarios;

El comando "select" recupera los registros de una tabla. Con el asterisco indicamos que muestre todos los campos de
la tabla "usuarios".

Aparece la tabla, sus campos y registros ingresados; si no tiene registros, aparecerían solamente los campos y la tabla
vacía).

Es importante ingresar los valores en el mismo orden en que se nombran los campos: En el siguiente ejemplo se lista
primero el campo "clave" y luego el campo "nombre" por eso, los valores también se colocan en ese orden:

insert into usuarios (clave, nombre)


values ('River','Juan');

Si ingresamos los datos en un orden distinto al orden en que se nombraron los campos, no aparece un mensaje de
error y los datos se guardan de modo incorrecto.

En el siguiente ejemplo se colocan los valores en distinto orden en que se nombran los campos, el valor de la clave
(la cadena "Boca") se guardará en el campo "nombre" y el valor del nombre (la cadena "Luis") en el campo "clave":

insert into usuarios (nombre,clave)


values ('Boca','Luis');

Problema:

Vemos si la tabla "usuarios" existe:

select *from all_tables;


Si existe la eliminamos:

drop table usuarios;

Creamos una nueva tabla denominada "usuarios" con los siguientes campos:

create table usuarios(


nombre varchar2(30),
clave varchar2(10)
);

Veamos si tiene registros:

select *from usuarios;

No tiene, la tabla aparece vacía, solamente vemos las columnas que muestran los nombres de sus campos.

Agregamos un registro a la tabla:

insert into usuarios (nombre, clave)


values ('Mariano','payaso');

Un mensaje indica que se ingreso una fila.

Veamos nuevamente los registros de la tabla "usuarios":

select *from usuarios;

Aparece la siguiente tabla:

NOMBRE CLAVE
-------------
Mariano payaso

La tabla contiene un solo registro, el ingresado recientemente.

Ingresamos otro registro, esta vez cambiamos el orden de los campos:

insert into usuarios (clave, nombre)


values ('River','Juan');

Ingresamos los datos en un orden distinto al orden en que se nombran los campos, no aparece un mensaje de error y
los datos se guardan de modo incorrecto:

insert into usuarios (nombre,clave)


values ('Boca','Luis');

Veamos cómo se almacenaron los datos:

select * from usuarios;

Aparece la siguiente tabla:

NOMBRE CLAVE
-------------
Mariano payaso
Juan River
Boca Luis

La tabla tiene 3 registros. Note que la clave "Boca" se guardó en el campo "nombre" y el nombre de usuario "Luis" en
el campo "clave".
4 - Tipos de datos

Ya explicamos que al crear una tabla debemos resolver qué campos (columnas) tendrá y que tipo de datos
almacenará cada uno de ellos, es decir, su estructura.

El tipo de dato especifica el tipo de información que puede guardar un campo: caracteres, números, etc.

Estos son algunos tipos de datos básicos de Oracle (posteriormente veremos otros y con más detalle):

 - varchar2: se emplea para almacenar cadenas de caracteres. Una cadena es una secuencia de caracteres.
Se coloca entre comillas simples; ejemplo: 'Hola', 'Juan Perez', 'Colon 123'. Este tipo de dato definen una
cadena de longitud variable en la cual determinamos el máximo de caracteres entre paréntesis. Puede
guardar hasta xxx caracteres. Por ejemplo, para almacenar cadenas de hasta 30 caracteres, definimos un
campo de tipo varchar2 (30), es decir, entre paréntesis, junto al nombre del campo colocamos la longitud.
Si intentamos almacenar una cadena de caracteres de mayor longitud que la definida, la cadena no se
carga, aparece un mensaje indicando tal situación y la sentencia no se ejecuta.
Por ejemplo, si definimos un campo de tipo varchar(10) e intentamos almacenar en él la cadena 'Buenas
tardes', aparece un mensaje indicando que el valor es demasiado grande para la columna.
 - number(p,s): se usa para guardar valores numéricos con decimales, de 1.0 x10-120 a 9.9...(38 posiciones).
Definimos campos de este tipo cuando queremos almacenar valores numéricos con los cuales luego
realizaremos operaciones matemáticas, por ejemplo, cantidades, precios, etc.
Puede contener números enteros o decimales, positivos o negativos. El parámetro "p" indica la precisión, es
decir, el número de dígitos en total (contando los decimales) que contendrá el número como máximo. El
parámetro "s" especifica la escala, es decir, el máximo de dígitos decimales. Por ejemplo, un campo
definido "number(5,2)" puede contener cualquier número entre 0.00 y 999.99 (positivo o negativo).
Para especificar número enteros, podemos omitir el parámetro "s" o colocar el valor 0 como parámetro "s".
Se utiliza como separador el punto (.).
Si intentamos almacenar un valor mayor fuera del rango permitido al definirlo, tal valor no se carga,
aparece un mensaje indicando tal situación y la sentencia no se ejecuta.
Por ejemplo, si definimos un campo de tipo number(4,2) e intentamos guardar el valor 123.45, aparece un
mensaje indicando que el valor es demasiado grande para la columna. Si ingresamos un valor con más
decimales que los definidos, el valor se carga pero con la cantidad de decimales permitidos, los dígitos
sobrantes se omiten.

Antes de crear una tabla debemos pensar en sus campos y optar por el tipo de dato adecuado para cada uno de
ellos.
Por ejemplo, si en un campo almacenaremos números telefónicos o un números de documento, usamos "varchar2", no
"number" porque si bien son dígitos, con ellos no realizamos operaciones matemáticas. Si en un campo guardaremos
apellidos, y suponemos que ningún apellido superará los 20 caracteres, definimos el campo "varchar2(20)". Si en un
campo almacenaremos precios con dos decimales que no superarán los 999.99 pesos definimos un campo de tipo
"number(5,2)", es decir, 5 dígitos en total, con 2 decimales. Si en un campo almacenaremos valores enteros de no
más de 3 dígitos, definimos un campo de tipo "number(3,0)".

Problema:

Eliminamos la tabla "libros":

drop table libros;

Vamos a crear una tabla llamada "libros" para almacenar información de los libros de una librería. Necesitamos los
siguientes campos:

-titulo: cadena de caracteres de 20 de longitud,


-autor: cadena de caracteres de 15 de longitud,
-editorial: caracteres de 10 de longitud,
-precio: valor numérico con 2 decimales y que no superará el valor 9999.99 y
-cantidad: valor numérico entero que no superará el valor 999.

Al crear la tabla, entonces, elegimos el tipo de dato más adecuado para cada campo:

create table libros(


titulo varchar2(20),
autor varchar2(15),
editorial varchar2(10),
precio number(6,2),
cantidad number(3,0)
);

Vemos la estructura de la tabla:

describe libros;

Aparece la siguiente información:

Name Null Type


--------------------------------------
titulo varchar2(20)
autor varchar2(15)
editorial varchar2(10)
precio number(6,2)
cantidad number(3)

Ingresamos algunos registros:

insert into libros (titulo,autor,editorial,precio,cantidad)


values ('El aleph','Borges','Emece',25.50,100);
insert into libros (titulo,autor,editorial,precio,cantidad)
values ('Matematica estas ahi','Paenza','Siglo XXI',18.8,200);

Note que al ingresar valores numéricos no se utilizan comillas y para el separador de decimales se usa el caracter
punto (.).

Veamos los registros cargados:

select * from libros;

Aparece la siguiente tabla:

TITULO AUTOR EDITORIAL PRECIO CANTIDAD


----------------------------------------------------------------
El Aleph Borges Emece 25,5 100
Matematica estas ahi Paenza Siglo XXI 18,8 200

Veamos lo que sucede si intentamos ingresar para el campo "titulo" una cadena de más de 20 caracteres:

insert into libros (titulo,autor,editorial,precio,cantidad)


values ('Alicia en el pais de las maravillas','Lewis Carroll','Atlantida',10,200);

aparece un mensaje de error y la sentencia no se ejecuta.

vamos a cortar la cadena para que SQL Server acepte el ingreso del registro:

insert into libros (titulo,autor,editorial,precio,cantidad)


values ('Alicia en el pais','Lewis Carroll','Atlantida',10,200);

Veamos los registros cargados:

select * from libros;

La tabla tiene ahora 3 registros.

Veamos qué sucede si intentamos ingresar para el campo "cantidad" un valor fuera de rango:

insert into libros (titulo,autor,editorial,precio,cantidad)


values ('El gato con botas','Anonimo','Atlantida',10,2000);

Oracle muestra un mensaje de error y la sentencia no se ejecuta, es decir, el registro no fue ingresado.
Veamos qué sucede si intentamos ingresar en el campo "precio" un valor con más decimales que los permitidos:

insert into libros (titulo,autor,editorial,precio,cantidad)


values ('El gato con botas','Anonimo','Atlantida',10.123,200);

La sentencia se ejecutó, el registro ha sido cargado. Veamos cómo se almacenó:

select *from libros;

Oracle omitió el último dígito decimal porque el campo sólo admitía 2 decimales.

5 - Recuperar algunos campos (select)

Hemos aprendido cómo ver todos los registros de una tabla, empleando la instrucción "select".
La sintaxis básica y general es la siguiente:

select * from NOMBRETABLA;

El asterisco (*) indica que se seleccionan todos los campos de la tabla.

Podemos especificar el nombre de los campos que queremos ver, separándolos por comas:

select titulo,autor from libros;

La lista de campos luego del "select" selecciona los datos correspondientes a los campos nombrados. En el ejemplo
anterior seleccionamos los campos "titulo" y "autor" de la tabla "libros", mostrando todos los registros.

Problema:

Trabajamos con la tabla "libros" que almacena los datos de los libros de una librería.
Eliminamos la tabla:

drop table libros;

Creamos la tabla:

create table libros(


titulo varchar2(40),
autor varchar2(30),
editorial varchar2(15),
precio number(6,2),
cantidad number(3,0)
);

Veamos la estructura de la tabla (5 campos):

describe libros;

Ingresamos algunos registros:

insert into libros (titulo,autor,editorial,precio,cantidad)


values ('El aleph','Borges','Emece',25.50,100);
insert into libros (titulo,autor,editorial,precio,cantidad)
values ('Alicia en el pais de las maravillas','Lewis Carroll','Atlantida',10,200);
insert into libros (titulo,autor,editorial,precio,cantidad)
values ('Matematica estas ahi','Paenza','Siglo XXI',18.8,200);

Veamos todos los campos la tabla:

select * from libros;


Aparece la siguiente tabla:

TITULO AUTOR EDITORIAL PRECIO


CANTIDAD
----------------------------------------------------------------------------------------
--
El aleph Borges Emece 25.50
100
Alicia en el pais de las maravillas Lewis Carroll Atlantida 10 200
Matematica estas ahi Paenza Siglo XXI 18.8
200

Recuperamos solamente el título, autor y editorial de todos los libros especificando los nombres de los campos
separados por comas:

select titulo,autor,editorial from libros;

Aparece la siguiente tabla:

TITULO AUTOR EDITORIAL


-----------------------------------------------------------------
El aleph Borges Emece
Alicia en el pais de las maravillas Lewis Carroll Atlantida
Matematica estas ahi Paenza Siglo XXI

Con la siguiente sentencia seleccionamos los títulos y precios de todos los libros:

select titulo,precio from libros;

Aparece la siguiente tabla:

TITULO PRECIO
-----------------------------------------------
El aleph 25.50
Alicia en el pais de las maravillas 10
Matematica estas ahi 18.8

Para ver solamente la editorial y la cantidad de libros, tipeamos:

select editorial,cantidad from libros;

Aparece la siguiente tabla:

EDITORIAL CANTIDAD
-------------------------
Emece 100
Atlantida 200
Siglo XXI 200

Note que en todos los casos recuperamos TODOS los registros, pero solamente ALGUNOS campos que especificamos.

6 - Recuperar algunos registros (where)

Hemos aprendido a seleccionar algunos campos de una tabla.

También es posible recuperar algunos registros.

Existe una cláusula, "where" con la cual podemos especificar condiciones para una consulta "select". Es decir,
podemos recuperar algunos registros, sólo los que cumplan con ciertas condiciones indicadas con la cláusula "where".
Por ejemplo, queremos ver el usuario cuyo nombre es "Marcelo", para ello utilizamos "where" y luego de ella, la
condición:

select nombre, clave


from usuarios
where nombre='Marcelo';

La sintaxis básica y general es la siguiente:

select NOMBRECAMPO1, ..., NOMBRECAMPOn


from NOMBRETABLA
where CONDICION;

Para las condiciones se utilizan operadores relacionales (tema que trataremos más adelante en detalle). El signo
igual(=) es un operador relacional. Para la siguiente selección de registros especificamos una condición que solicita
los usuarios cuya clave es igual a "River":

select nombre,clave
from usuarios
where clave='River';

Si ningún registro cumple la condición establecida con el "where", no aparecerá ningún registro.

Entonces, con "where" establecemos condiciones para recuperar algunos registros.

Para recuperar algunos campos de algunos registros combinamos en la consulta la lista de campos y la cláusula
"where":

select nombre
from usuarios
where clave='River';

En la consulta anterior solicitamos el nombre de todos los usuarios cuya clave sea igual a "River".

7 - Operadores relacionales

Los operadores son símbolos que permiten realizar operaciones matemáticas, concatenar cadenas, hacer
comparaciones.

Oracle reconoce de 4 tipos de operadores:

1) relacionales (o de comparación)
2) aritméticos
3) de concatenación
4) lógicos

Por ahora veremos solamente los primeros.

Los operadores relacionales (o de comparación) nos permiten comparar dos expresiones, que pueden ser variables,
valores de campos, etc.

Hemos aprendido a especificar condiciones de igualdad para seleccionar registros de una tabla; por ejemplo:

select *from libros


where autor='Borges';

Utilizamos el operador relacional de igualdad.

Los operadores relacionales vinculan un campo con un valor para que Oracle compare cada registro (el campo
especificado) con el valor dado.

Los operadores relacionales son los siguientes:

= igual
<> distinto
> mayor
< menor
>= mayor o igual
<= menor o igual

Podemos seleccionar los registros cuyo autor sea diferente de "Borges", para ello usamos la condición:

select * from libros


where autor<>'Borges';

Podemos comparar valores numéricos. Por ejemplo, queremos mostrar los títulos y precios de los libros cuyo precio
sea mayor a 20 pesos:

select titulo, precio


from libros
where precio>20;

Queremos seleccionar los libros cuyo precio sea menor o igual a 30:

select *from libros


where precio<=30;

Los operadores relacionales comparan valores del mismo tipo. Se emplean para comprobar si un campo cumple con
una condición.

No son los únicos, existen otros que veremos mas adelante.

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla "libros":

drop table libros;

La creamos con la siguiente estructura:

create table libros(


titulo varchar2(30),
autor varchar2(30),
editorial varchar2(15),
precio number(5,2)
);

Agregamos registros a la tabla:

insert into libros (titulo,autor,editorial,precio)


values ('El aleph','Borges','Emece',24.50);
insert into libros (titulo,autor,editorial,precio)
values ('Martin Fierro','Jose Hernandez','Emece',16.00);
insert into libros (titulo,autor,editorial,precio)
values ('Aprenda PHP','Mario Molina','Emece',35.40);
insert into libros (titulo,autor,editorial,precio)
values ('Cervantes y el quijote','Borges','Paidos',50.90);

Seleccionamos los registros cuyo autor sea diferente de 'Borges':

select *from libros


where autor<>'Borges';

Seleccionamos los registros cuyo precio supere los 20 pesos, sólo el título y precio:

select titulo,precio
from libros
where precio>20;
Note que el valor con el cual comparamos el campo "precio", como es numérico, no se coloca entre comillas. Los
libros cuyo precio es menor a 20 pesos no aparecen en la selección.

Recuperamos aquellos libros cuyo precio es menor o igual a 30:

select *from libros


where precio<=30;

8 - Borrar registros (delete)

Para eliminar los registros de una tabla usamos el comando "delete".

Sintaxis básica:

delete from NOMBRETABLA;

Se coloca el comando delete seguido de la palabra clave "from" y el nombre de la tabla de la cual queremos eliminar
los registros. En el siguiente ejemplo se eliminan los registros de la tabla "usuarios":

delete from usuarios;

Luego, un mensaje indica la cantidad de registros que se han eliminado.

Si no queremos eliminar todos los registros, sino solamente algunos, debemos indicar cuál o cuáles; para ello
utilizamos el comando "delete" junto con la clausula "where" con la cual establecemos la condición que deben
cumplir los registros a borrar.

Por ejemplo, queremos eliminar aquel registro cuyo nombre de usuario es "Marcelo":

delete from usuarios


where nombre='Marcelo';

Si solicitamos el borrado de un registro que no existe, es decir, ningún registro cumple con la condición especificada,
aparecerá un mensaje indicando que ningún registro fue eliminado, pues no encontró registros con ese dato.

Tenga en cuenta que si no colocamos una condición, se eliminan todos los registros de la tabla especificada.

Problema:

Trabajamos con la tabla "usuarios".


Eliminamos la tabla "usuarios":

drop table usuarios;

La creamos con la siguiente estructura:

create table usuarios(


nombre varchar2(30),
clave varchar2(10)
);

Agregamos registros a la tabla:

insert into usuarios (nombre,clave)


values ('Marcelo','River');
insert into usuarios (nombre,clave)
values ('Susana','chapita');
insert into usuarios (nombre,clave)
values ('CarlosFuentes','Boca');
insert into usuarios (nombre,clave)
values ('FedericoLopez','Boca');
Seleccionamos todos los registros:

select *from usuarios;

Vamos a eliminar el registro cuyo nombre de usuario es "Marcelo":

delete from usuarios


where nombre='Marcelo';

aparece un mensaje indicando que se ha borrado 1 fila.

Intentamos eliminarlo nuevamente:

delete from usuarios


where nombre='Marcelo';

Como ningún registro cumple con la condición especificada (nombre igual a Marcelo), aparecerá un mensaje
indicando que ningún registro fue borrado.

Eliminamos todos los registros cuya clave es 'Boca':

delete from usuarios


where clave='Boca';

Aparece un mensaje indicando que 2 registros fueron eliminados, es decir, se eliminaron los 2 registros cuyas claves
eran igual a "River".

Eliminemos todos los registros:

delete from usuarios;

Veamos el contenido de la tabla:

select * from usuarios;

No hay registros.

9 - Actualizar registros (update)

Decimos que actualizamos un registro cuando modificamos alguno de sus valores.

Para modificar uno o varios datos de uno o varios registros utilizamos "update" (actualizar).

Sintaxis básica:

update NOMBRETABLA set CAMPO=NUEVOVALOR;

Utilizamos "update" junto al nombre de la tabla y "set" junto con el campo a modificar y su nuevo valor.

El cambio afectará a todos los registros.

Por ejemplo, en nuestra tabla "usuarios", queremos cambiar los valores de todas las claves, por "RealMadrid":

update usuarios set clave='RealMadrid';

Podemos modificar algunos registros, para ello debemos establecer condiciones de selección con "where".

Por ejemplo, queremos cambiar el valor correspondiente a la clave de nuestro usuario llamado "Federicolopez",
queremos como nueva clave "Boca", necesitamos una condición "where" que afecte solamente a este registro:
update usuarios set clave='Boca'
where nombre='Federicolopez';

Si Oracle no encuentra registros que cumplan con la condición del "where", un mensaje indica que ningún registro fue
modificado.

Las condiciones no son obligatorias, pero si omitimos la cláusula "where", la actualización afectará a todos los
registros.

También podemos actualizar varios campos en una sola instrucción:

update usuarios set nombre='Marceloduarte', clave='Marce'


where nombre='Marcelo';

Para ello colocamos "update", el nombre de la tabla, "set" junto al nombre del campo y el nuevo valor y separado por
coma, el otro nombre del campo con su nuevo valor.

Problema:

Trabajamos con la tabla "usuarios".


Eliminamos la tabla:

drop table usuarios;

Creamos la tabla:

create table usuarios(


nombre varchar2(20),
clave varchar2(10)
);

Ingresamos algunos registros:

insert into usuarios (nombre,clave)


values ('Marcelo','River');
insert into usuarios (nombre,clave)
values ('Susana','chapita');
insert into usuarios (nombre,clave)
values ('Carlosfuentes','Boca');
insert into usuarios (nombre,clave)
values ('Federicolopez','Boca');

Cambiaremos los valores de todas las claves, por la cadena "RealMadrid":

update usuarios set clave='RealMadrid';

Un mensaje indica que se actualizaron 4 registros.

El cambio afectó a todos los registros, veámoslo:

select * from usuarios;

Necesitamos cambiar el valor de la clave del usuario llamado "Federicolopez" por "Boca":

update usuarios set clave='Boca'


where nombre='Federicolopez';

Verifiquemos que la actualización se realizó:

select *from usuarios;

Vimos que si Oracle no encuentra registros que cumplan con la condición del "where", un mensaje indica que ningún
registro se modifica:
update usuarios set clave='payaso'
where nombre='JuanaJuarez';

Para actualizar varios campos en una sola instrucción empleamos:

update usuarios set nombre='Marceloduarte', clave='Marce'


where nombre='Marcelo';

Verifiquemos que la actualización se realizó:

select * from usuarios;

10 - Comentarios

Para aclarar algunas instrucciones, en ocasiones, necesitamos agregar comentarios.

Es posible ingresar comentarios en la línea de comandos, es decir, un texto que no se ejecuta; para ello se emplean
dos guiones (--):

select *from libros;--mostramos los registros de libros

en la línea anterior, todo lo que está luego de los guiones (hacia la derecha) no se ejecuta.

Para agregar varias líneas de comentarios, se coloca una barra seguida de un asterisco (/*) al comienzo del bloque de
comentario y al finalizarlo, un asterisco seguido de una barra (*/)

select titulo, autor


/*mostramos títulos y
nombres de los autores*/
from libros;

todo lo que está entre los símbolos "/*" y "*/" no se ejecuta.

11 - Valores nulos (null)

"null' significa "dato desconocido" o "valor inexistente".

A veces, puede desconocerse o no existir el dato correspondiente a algún campo de un registro. En estos casos
decimos que el campo puede contener valores nulos.

Por ejemplo, en nuestra tabla de libros, podemos tener valores nulos en el campo "precio" porque es posible que para
algunos libros no le hayamos establecido el precio para la venta.

En contraposición, tenemos campos que no pueden estar vacíos jamás.

Veamos un ejemplo. Tenemos nuestra tabla "libros". El campo "titulo" no debería estar vacío nunca, igualmente el
campo "autor". Para ello, al crear la tabla, debemos especificar que tales campos no admitan valores nulos:

create table libros(


titulo varchar2(30) not null,
autor varchar2(20) not null,
editorial varchar2(15) null,
precio number(5,2)
);

Para especificar que un campo NO admita valores nulos, debemos colocar "not null" luego de la definición del campo.

En el ejemplo anterior, los campos "editorial" y "precio" si admiten valores nulos.


Cuando colocamos "null" estamos diciendo que admite valores nulos (caso del campo "editorial"); por defecto, es
decir, si no lo aclaramos, los campos permiten valores nulos (caso del campo "precio").

Cualquier campo, de cualquier tipo de dato permite ser definido para aceptar o no valores nulos. Un valor "null" NO
es lo mismo que un valor 0 (cero) o una cadena de espacios en blanco (" ").

Si ingresamos los datos de un libro, para el cual aún no hemos definido el precio podemos colocar "null" para mostrar
que no tiene precio:

insert into libros (titulo,autor,editorial,precio)


values('El aleph','Borges','Emece',null);

Note que el valor "null" no es una cadena de caracteres, NO se coloca entre comillas.

Entonces, si un campo acepta valores nulos, podemos ingresar "null" cuando no conocemos el valor.

También podemos colocar "null" en el campo "editorial" si desconocemos el nombre de la editorial a la cual pertenece
el libro que vamos a ingresar:

insert into libros (titulo,autor,editorial,precio)


values('Alicia en el pais','Lewis Carroll',null,25);

Una cadena vacía es interpretada por Oracle como valor nulo; por lo tanto, si ingresamos una cadena vacía, se
almacena el valor "null".

Si intentamos ingresar el valor "null" (o una cadena vacía) en campos que no admiten valores nulos (como "titulo" o
"autor"), Oracle no lo permite, muestra un mensaje y la inserción no se realiza; por ejemplo:

insert into libros (titulo,autor,editorial,precio)


values(null,'Borges','Siglo XXI',25);

Cuando vemos la estructura de una tabla con "describe", en la columna "Null", aparece "NOT NULL" si el campo no
admite valores nulos y no aparece en caso que si los permita.

Para recuperar los registros que contengan el valor "null" en algún campo, no podemos utilizar los operadores
relacionales vistos anteriormente: = (igual) y <> (distinto); debemos utilizar los operadores "is null" (es igual a null) y
"is not null" (no es null).

Los valores nulos no se muestran, aparece el campo vacío.

Entonces, para que un campo no permita valores nulos debemos especificarlo luego de definir el campo, agregando
"not null". Por defecto, los campos permiten valores nulos, pero podemos especificarlo igualmente agregando "null".

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla "libros":

drop table libros;

Creamos la tabla especificando que los campos "titulo" y "autor" no admitan valores nulos:

create table libros(


titulo varchar2(30) not null,
autor varchar2(30) not null,
editorial varchar2(15) null,
precio number(5,2)
);

Los campos "editorial" y "precio" si permiten valores nulos; el primero, porque lo especificamos colocando "null" en la
definición del campo, el segundo lo asume por defecto.

Agregamos un registro a la tabla con valor nulo para el campo "precio":


insert into libros (titulo,autor,editorial,precio)
values('El aleph','Borges','Emece',null);

Veamos cómo se almacenó el registro:

select *from libros;

No aparece ningún valor en la columna "precio".

Ingresamos otro registro, con valor nulo para el campo "editorial", campo que admite valores "null":

insert into libros (titulo,autor,editorial,precio)


values('Alicia en el pais','Lewis Carroll',null,0);

Veamos cómo se almacenó el registro:

select *from libros;

No aparece ningún valor en la columna "editorial".

Ingresamos otro registro, con valor nulo para los dos campos que lo admiten:

insert into libros (titulo,autor,editorial,precio)


values('Aprenda PHP','Mario Molina',null,null);

Veamos cómo se almacenó el registro:

select *from libros;

No aparece ningún valor en ambas columnas.

Veamos lo que sucede si intentamos ingresar el valor "null" en campos que no lo admiten, como "titulo":

insert into libros (titulo,autor,editorial,precio)


values(null,'Borges','Siglo XXI',25);

aparece un mensaje indicando que no se puede realizar una inserción "null" y la sentencia no se ejecuta.

Para ver cuáles campos admiten valores nulos y cuáles no, vemos la estructura de la tabla:

describe libros;

nos muestra, en la columna "Null", que los campos "titulo" y "autor" están definidos "not null", es decir, no permiten
valores nulos, los otros dos campos si los admiten.

Dijimos que la cadena vacía es interpretada como valor "null". Vamos a ingresar un registro con cadena vacía para el
campo "editorial":

insert into libros (titulo,autor,editorial,precio)


values('Uno','Richard Bach','',18.50);

Veamos cómo se almacenó el registro:

select *from libros;

No aparece ningún valor en la columna "editorial" del libro "Uno", almacenó "null".

Intentamos ingresar una cadena vacía en el campo "titulo":

insert into libros (titulo,autor,editorial,precio)


values('','Richard Bach','Planeta',22);
Mensaje de error indicando que el campo no admite valores nulos.

Dijimos que una cadena de espacios NO es igual a una cadena vacía o valor "null". Vamos a ingresar un registro y en el
campo "editorial" guardaremos una cadena de 3 espacios:

insert into libros (titulo,autor,editorial,precio)


values('Don quijote','Cervantes',' ',20);

Veamos cómo se almacenó el registro:

select *from libros;

Se muestra la cadena de espacios.

Recuperamos los registros que contengan en el campo "editorial" una cadena de 3 espacios:

select *from libros where editorial=' ';

12 - Operadores relacionales (is null)

Para recuperar los registros que contengan el valor "null" en algún campo, no podemos utilizar los operadores
relacionales vistos anteriormente: = (igual) y <> (distinto); debemos utilizar los operadores "is null" (es igual a null) y
"is not null" (no es null).

Con la siguiente sentencia recuperamos los libros que contienen valor nulo en el campo "editorial":

select *from libros


where editorial is null;

Recuerde que los valores nulos no se muestran, aparece el campo vacío.

Las siguientes sentencias tendrán una salida diferente:

select *from libros where editorial is null;


select *from libros where editorial=' ';

Con la primera sentencia veremos los libros cuya editorial almacena el valor "null" (desconocido); con la segunda, los
libros cuya editorial guarda una cadena de 3 espacios en blanco.

Para obtener los registros que no contienen "null", se puede emplear "is not null", esto mostrará los registros con
valores conocidos.

Para ver los libros que NO tienen valor "null" en el campo "precio" tipeamos:

select *from libros where precio is not null;

Problema:

Trabajamos con la tabla "libros" de una librería.


Eliminamos la tabla y la creamos:

drop table libros;

create table libros(


codigo number(4) not null,
titulo varchar2(40) not null,
autor varchar2(20),
editorial varchar2(20),
precio number(6,2)
);
Ingresamos algunos registros:

insert into libros


values(1,'El aleph','Borges','Emece',15.90);
insert into libros
values(2,'Cervantes y el quijote','Borges','Paidos',null);
insert into libros
values(3,'Alicia en el pais de las maravillas','Lewis Carroll',null,19.90);
insert into libros
values(4,'Martin Fierro','Jose Hernandez','Emece',25.90);
insert into libros (codigo,titulo,autor,precio)
values(5,'Antología poética','Borges',25.50);
insert into libros (codigo,titulo,autor)
values(6,'Java en 10 minutos','Mario Molina');
insert into libros (codigo,titulo,autor)
values(7,'Martin Fierro','Jose Hernandez');
insert into libros (codigo,titulo,autor)
values(8,'Aprenda PHP',null);

Recuperamos los registros en los cuales esté almacenado el valor "null" en el campo "editorial":

select *from libros


where editorial is null;

Seleccionamos los libros que no contiene "null" en "autor":

select *from libros


where editorial is not null;

Dijimos que una cadena de espacios NO es igual a una cadena vacía o valor "null". Vamos a ingresar un registro y en el
campo "editorial" guardaremos una cadena de 3 espacios:

insert into libros (codigo,titulo,autor,editorial,precio)


values(9,'Don quijote','Cervantes',' ',20);

Veamos cómo se almacenó el registro:

select * from libros;

Se muestra la cadena de espacios.

Recuperamos los registros que contengan el valor "null" en el campo "editorial" y luego aquellos que tengan una
cadena de 3 espacios:

select * from libros where editorial is null;


select * from libros where editorial=' ';

Con la primera sentencia veremos los libros cuya editorial es igual a "null" (desconocido); con la segunda, los libros
cuya editorial almacena una cadena de espacios.

13 - Clave primaria (primary key)

Una clave primaria es un campo (o varios) que identifica un solo registro (fila) en una tabla.
Para un valor del campo clave existe solamente un registro.

Veamos un ejemplo, si tenemos una tabla con datos de personas, el número de documento puede establecerse como
clave primaria, es un valor que no se repite; puede haber personas con igual apellido y nombre, incluso el mismo
domicilio (padre e hijo por ejemplo), pero su documento será siempre distinto.

Si tenemos la tabla "usuarios", el nombre de cada usuario puede establecerse como clave primaria, es un valor que no
se repite; puede haber usuarios con igual clave, pero su nombre de usuario será siempre diferente.
Podemos establecer que un campo sea clave primaria al momento de crear la tabla o luego que ha sido creada.
Vamos a aprender a establecerla al crear la tabla. No existe una única manera de hacerlo, por ahora veremos la
sintaxis más sencilla.

Tenemos nuestra tabla "usuarios" definida con 2 campos ("nombre" y "clave").

La sintaxis básica y general es la siguiente:

create table NOMBRETABLA(


CAMPO TIPO,
...,
CAMPO TIPO,
PRIMARY KEY (CAMPO)
);

Lo que hacemos agregar, luego de la definición de cada campo, "primary key" y entre paréntesis, el nombre del
campo que será clave primaria.

En el siguiente ejemplo definimos una clave primaria, para nuestra tabla "usuarios" para asegurarnos que cada
usuario tendrá un nombre diferente y único:

create table usuarios(


nombre varchar2(20),
clave varchar2(10),
primary key(nombre)
);

Una tabla sólo puede tener una clave primaria. Cualquier campo (de cualquier tipo) puede ser clave primaria, debe
cumplir como requisito, que sus valores no se repitan ni sean nulos. Por ello, al definir un campo como clave
primaria, automáticamente Oracle lo convierte a "not null".

Luego de haber establecido un campo como clave primaria, al ingresar los registros, Oracle controla que los valores
para el campo establecido como clave primaria no estén repetidos en la tabla; si estuviesen repetidos, muestra un
mensaje y la inserción no se realiza. Es decir, si en nuestra tabla "usuarios" ya existe un usuario con nombre
"juanperez" e intentamos ingresar un nuevo usuario con nombre "juanperez", aparece un mensaje y la instrucción
"insert" no se ejecuta.

Igualmente, si realizamos una actualización, Oracle controla que los valores para el campo establecido como clave
primaria no estén repetidos en la tabla, si lo estuviese, aparece un mensaje indicando que se viola la clave primaria y
la actualización no se realiza.

Podemos ver el campo establecido como clave primaria de una tabla realizando la siguiente consulta:

select uc.table_name, column_name from user_cons_columns ucc


join user_constraints uc
on ucc.constraint_name=uc.constraint_name
where uc.constraint_type='P' and
uc.table_name='USUARIOS';

No explicaremos la consulta anterior por el momento, sólo la ejecutaremos; si la consulta retorna una tabla vacía,
significa que la tabla especificada no tiene clave primaria. El nombre de la tabla DEBE ir en mayúsculas, sino Oracle
no la encontrará.

Problema:

Trabajamos con la tabla "usuarios".

Eliminamos la tabla:

drop table usuarios;

Creamos la tabla definiendo el campo "nombre" como clave primaria:

create table usuarios(


nombre varchar2(20),
clave varchar2(10),
primary key (nombre)
);

Al campo "nombre" no lo definimos "not null", pero al establecerse como clave primaria, Oracle lo convierte en "not
null", veamos que en la columna "NULL" aparece "NOT NULL":

describe usuarios;

Ingresamos algunos registros:

insert into usuarios (nombre, clave)


values ('juanperez','Boca');
insert into usuarios (nombre, clave)
values ('raulgarcia','River');

Recordemos que cuando un campo es clave primaria, sus valores no se repiten. Intentamos ingresar un valor de clave
primaria existente:

insert into usuarios (nombre, clave)


values ('juanperez','payaso');

aparece un mensaje de error y la sentencia no se ejecuta.

Cuando un campo es clave primaria, sus valores no pueden ser nulos. Intentamos ingresar el valor "null" en el campo
clave primaria:

insert into usuarios (nombre, clave)


values (null,'payaso');

aparece un mensaje de error y la sentencia no se ejecuta.

Si realizamos alguna actualización, Oracle controla que los valores para el campo establecido como clave primaria no
estén repetidos en la tabla. Intentemos actualizar el nombre de un usuario colocando un nombre existente:

update usuarios set nombre='juanperez'


where nombre='raulgarcia';

aparece un mensaje indicando que se viola la clave primaria y la actualización no se realiza.

Corroboramos que la tabla "usuarios" tiene establecido el campo "nombre" como clave primaria realizando la
siguiente consulta (Recuerde colocar el nombre de la tabla en mayúsculas, sino Oracle considerará que no existe la
tabla):

select uc.table_name, column_name from user_cons_columns ucc


join user_constraints uc
on ucc.constraint_name=uc.constraint_name
where uc.constraint_type='P' and
uc.table_name='USUARIOS';

Aparece la siguiente tabla:

TABLE_NAME COLUMN_NAME
--------------------------------------------
USUARIOS NOMBRE

indicando que la tabla "usuarios" tiene establecido el campo "nombre" como clave primaria.

14 - Vaciar la tabla (truncate table)


Aprendimos que para borrar todos los registro de una tabla se usa "delete" sin condición "where".
También podemos eliminar todos los registros de una tabla con "truncate table". Sintaxis:

truncate table NOMBRETABLA;

Por ejemplo, queremos vaciar la tabla "libros", usamos:

truncate table libros;

La sentencia "truncate table" vacía la tabla (elimina todos los registros) y conserva la estructura de la tabla.

La diferencia con "drop table" es que esta sentencia elimina la tabla, no solamente los registros, "truncate table" la
vacía de registros.

La diferencia con "delete" es la siguiente, al emplear "delete", Oracle guarda una copia de los registros borrados y son
recuperables, con "truncate table" no es posible la recuperación porque se libera todo el espacio en disco ocupado
por la tabla; por lo tanto, "truncate table" es más rápido que "delete" (se nota cuando la cantidad de registros es muy
grande).

Problema:

Trabajamos con la tabla "libros" que almacena los datos de los libros de una librería.

Eliminamos la tabla:

drop table libros;

Creamos la tabla:

create table libros(


codigo number(4),
titulo varchar2(30),
autor varchar2(20),
editorial varchar2(15),
precio number(5,2)
);

Agregamos algunos registros:

insert into libros (codigo,titulo,autor,editorial,precio)


values (1,'El aleph','Borges','Emece',25.60);
insert into libros (codigo,titulo,autor,editorial,precio)
values (2,'Uno','Richard Bach','Planeta',18);

Seleccionamos todos los registros:

select *from libros;

Truncamos la tabla:

truncate table libros;

Si consultamos la tabla, vemos que aún existe pero ya no tiene registros:

select * from libros;

Ingresamos nuevamente algunos registros:

insert into libros (codigo,titulo,autor,editorial,precio)


values (1,'El aleph','Borges','Emece',25.60);
insert into libros (codigo,titulo,autor,editorial,precio)
values (2,'Uno','Richard Bach','Planeta',18);

Eliminemos todos los registros con "delete":

delete from libros;

Si consultamos la tabla, vemos que aún existe pero ya no tiene registros:

select * from libros;

Ingresamos nuevamente algunos registros:

insert into libros (codigo,titulo,autor,editorial,precio)


values (1,'El aleph','Borges','Emece',25.60);
insert into libros (codigo,titulo,autor,editorial,precio)
values (2,'Uno','Richard Bach','Planeta',18);

Eliminamos la tabla:

drop table libros;

Intentamos seleccionar todos los registros:

select * from libros;

Aparece un mensaje de error, la tabla no existe.

15 - Tipos de datos alfanuméricos

Ya explicamos que al crear una tabla debemos elegir la estructura adecuada, esto es, definir los campos y sus tipos
más precisos, según el caso.

Para almacenar valores alfanuméricos (texto) usamos cadenas de caracteres.

Las cadenas se colocan entre comillas simples.

Podemos almacenar letras, símbolos y dígitos con los que no se realizan operaciones matemáticas, por ejemplo,
códigos de identificación, números de documentos, números telefónicos. Tenemos los siguientes tipos:

1) char(x): define una cadena de caracteres de longitud fija determinada por el argumento "x". Si se omite el
argumento, por defecto coloca 1. "char" viene de character, que significa caracter en inglés. Su rango es de 1 a 2000
caracteres.

Que sea una cadena de longitud fija significa que, si definimos un campo como "char(10)" y almacenamos el valor
"hola" (4 caracteres), Oracle rellenará las 6 posiciones restantes con espacios, es decir, ocupará las 10 posiciones; por
lo tanto, si la longitud es invariable, es conveniente utilizar el tipo char; caso contrario, el tipo varchar2.
Si almacenamos "hola" en un campo definido "char(10)" Oracle almacenará "hola ".

2) varchar2(x): almacena cadenas de caracteres de longitud variable determinada por el argumento "x" (obligatorio).
Que sea una cadena de longitud variable significa que, si definimos un campo como "varchar2(10)" y almacenamos el
valor "hola" (4 caracteres), Oracle solamente ocupa las 4 posiciones (4 bytes y no 10 como en el caso de "char"); por
lo tanto, si la longitud es variable, es conveniente utilizar este tipo de dato y no "char", así ocupamos menos espacio
de almacenamiento en disco. Su rango es de 1 a 4000 caracteres.

3) nchar(x): es similar a "char" excepto que permite almacenar caracteres ASCII, EBCDIC y Unicode; su rango va de 1
a 1000 caracteres porque se emplean 2 bytes por cada caracter.

4) nvarchar2(x): es similar a "varchar2", excepto que permite almacenar caracteres Unicode; su rango va de 1 a 2000
caracteres porque se emplean 2 bytes por cada caracter.
5 y 6) varchar(x) y char2(x): disponibles en Oracle8.

7) long: guarda caracteres de longitud variable; puede contener hasta 2000000000 caracteres (2 Gb). No admite
argumento para especificar su longitud. En Oracle8 y siguientes versiones conviene emplear "clob" y "nlob" para
almacenar grandes cantidades de datos alfanuméricos.

En general se usarán los 2 primeros.

Si intentamos almacenar en un campo alfanumérico una cadena de caracteres de mayor longitud que la definida,
aparece un mensaje indicando que el valor es demasiado grande y la sentencia no se ejecuta.

Por ejemplo, si definimos un campo de tipo varchar2(10) y le asignamos la cadena 'Aprenda PHP' (11 caracteres),
aparece un mensaje y la sentencia no se ejecuta.

Si ingresamos un valor numérico (omitiendo las comillas), lo convierte a cadena y lo ingresa como tal.

Por ejemplo, si en un campo definido como varchar2(5) ingresamos el valor 12345, lo toma como si hubiésemos
tipeado '12345', igualmente, si ingresamos el valor 23.56, lo convierte a '23.56'. Si el valor numérico, al ser convertido
a cadena supera la longitud definida, aparece un mensaje de error y la sentencia no se ejecuta.

Es importante elegir el tipo de dato adecuado según el caso.

Para almacenar cadenas que varían en su longitud, es decir, no todos los registros tendrán la misma longitud en un
campo determinado, se emplea "varchar2" en lugar de "char".

Por ejemplo, en campos que guardamos nombres y apellidos, no todos los nombres y apellidos tienen la misma
longitud.

Para almacenar cadenas que no varían en su longitud, es decir, todos los registros tendrán la misma longitud en un
campo determinado, se emplea "char".

Por ejemplo, definimos un campo "codigo" que constará de 5 caracteres, todos los registros tendrán un código de 5
caracteres, ni más ni menos.

Para almacenar valores superiores a 4000 caracteres se debe emplear "long".

Problema:

Un comercio que tiene un stand en una feria registra en una tabla llamada "visitantes" algunos datos de las personas
que visitan o compran en su stand para luego enviarle publicidad de sus productos.

Eliminamos la tabla "visitantes":

drop table visitantes;

Creamos con la siguiente estructura:

create table visitantes(


nombre varchar2(30),
edad number(2),
sexo char(1),
domicilio varchar2(30),
ciudad varchar2(20),
telefono varchar2(11)
);

Los campos "nombre", "domicilio" y "ciudad" almacenarán valores cuya longitud varía, por ello elegimos el tipo
"varchar2" y le damos a cada uno una longitud máxima estimando su tamaño. El campo "sexo" se define de tipo
"char", porque necesitamos solamente 1 caracter "f" o "m", que siempre será fijo. El campo "telefono" también se
define como varchar2 porque no todos los números telefónicos tienen la misma longitud.

Ingresamos un registro:

insert into visitantes (nombre,edad,sexo,domicilio,ciudad,telefono)


values ('Ana Acosta',25,'f','Avellaneda 123','Cordoba','4223344');

Intentamos ingresar una cadena de mayor longitud que la definida en el campo "sexo":

insert into visitantes (nombre,edad,sexo,domicilio,ciudad,telefono)


values ('Betina Bustos',32,'fem','Bulnes 234','Cordoba','4515151');

aparece un mensaje de error y la sentencia no se ejecuta.

Ingresamos el mismo registro, esta vez con un sólo caracter para el campo "sexo":

insert into visitantes (nombre,edad,sexo,domicilio,ciudad,telefono)


values ('Betina Bustos',32,'f','Bulnes 234','Cordoba','4515151');

Ingresamos un número telefónico olvidando las comillas, es decir, como un valor numérico:

insert into visitantes (nombre,edad,sexo,domicilio,ciudad,telefono)


values ('Carlos Caseres',43,'m','Colon 345','Cordoba',03514555666);

lo convierte a cadena, veámoslo:

select * from visitantes;

16 - Tipos de datos numéricos

Ya explicamos que al crear una tabla debemos elegir la estructura adecuada, esto es, definir los campos y sus tipos
más precisos, según el caso.

Los valores numéricos no se ingresan entre comillas. Se utiliza el punto como separador de decimales.

Para almacenar valores NUMERICOS Oracle dispone de dos tipos de datos:

1) number(t,d): para almacenar valores enteros o decimales, positivos o negativos. Su rango va de 1.0 x 10-130 hasta
9.999...(38 nueves). Definimos campos de este tipo cuando queremos almacenar valores numéricos con los cuales
luego realizaremos operaciones matemáticas, por ejemplo, cantidades, precios, etc.

El parámetro "t" indica el número total de dígitos (contando los decimales) que contendrá el número como máximo
(es la precisión). Su rango va de 1 a 38. El parámetro "d" indica el máximo de dígitos decimales (escala). La escala
puede ir de -84 a 127. Para definir número enteros, se puede omitir el parámetro "d" o colocar un 0.

Un campo definido "number(5,2)" puede contener cualquier número entre -999.99 y 999.99.

Para especificar número enteros, podemos omitir el parámetro "d" o colocar el valor 0.
Si intentamos almacenar un valor mayor fuera del rango permitido al definirlo, tal valor no se carga, aparece un
mensaje indicando tal situación y la sentencia no se ejecuta.
Por ejemplo, si definimos un campo de tipo "number(4,2)" e intentamos guardar el valor 123.45, aparece un mensaje
indicando que el valor es demasiado grande para la columna. Si ingresamos un valor con más decimales que los
definidos, el valor se carga pero con la cantidad de decimales permitidos, los dígitos sobrantes se omiten.

2) float (x): almacena un número en punto decimal. El parámetro indica la precisión binaria máxima; con un rango de
1 a 126. Si se omite, por defecto es 126.

Para ambos tipos numéricos:

- si ingresamos un valor con más decimales que los permitidos, redondea al más cercano; por ejemplo, si definimos
"float(4,2)" e ingresamos el valor "12.686", guardará "12.69", redondeando hacia arriba; si ingresamos el valor
"12.682", guardará "12.67", redondeando hacia abajo.

- si intentamos ingresar un valor fuera de rango, no lo acepta.


- si ingresamos una cadena, Oracle intenta convertirla a valor numérico, si dicha cadena consta solamente de dígitos,
la conversión se realiza, luego verifica si está dentro del rango, si es así, la ingresa, sino, muestra un mensaje de
error y no ejecuta la sentencia. Si la cadena contiene caracteres que Oracle no puede convertir a valor numérico,
muestra un mensaje de error y la sentencia no se ejecuta.
Por ejemplo, definimos un campo de tipo "numberl(5,2)", si ingresamos la cadena '12.22', la convierte al valor
numérico 12.22 y la ingresa; si intentamos ingresar la cadena '1234.56', la convierte al valor numérico 1234.56, pero
como el máximo valor permitido es 999.99, muestra un mensaje indicando que está fuera de rango. Si intentamos
ingresar el valor '12y.25', Oracle no puede realizar la conversión y muestra un mensaje de error.

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla:

drop table libros;

Creamos la tabla con la siguiente estructura:

create table libros(


codigo number(5) not null,
titulo varchar2(40) not null,
autor varchar2(30),
editorial varchar2(15),
precio number(6,2),
cantidad number(4)
);

Note que definimos el campo "codigo" de tipo "number(5)", esto es porque estimamos que no tendremos más de
99999 libros, y no colocamos decimales porque necesitamos números enteros.
Como en el campo "precio" no almacenaremos valores mayores a 9999.99, definimos el campo de tipo "number(6,2)".

El tipo "float" no es adecuado para representar precios porque no es exacto.

Como los valores para el campo "cantidad" no superarán los 9999, definimos el campo de tipo "number(4)", no
colocamos decimales porque necesitamos valores enteros.

Analicemos la inserción de datos numéricos.

Intentemos ingresar un valor para "cantidad" fuera del rango definido:

insert into libros (codigo,titulo,autor,editorial,precio,cantidad)


values(1,'El aleph','Borges','Emece',25.60,50000);

aparece un mensaje de error y la inserción no se ejecuta.

Ingresamos un valor para "cantidad" con decimales:

insert into libros (codigo,titulo,autor,editorial,precio,cantidad)


values(1,'El aleph','Borges','Emece',25.60,100.2);

Lo acepta, veamos qué se almacenó:

select *from libros;

Truncó el valor.

Ingresamos un precio con 3 decimales:

insert into libros (codigo,titulo,autor,editorial,precio,cantidad)


values(2,'Don quijote','Cervantes','Emece',25.123,100);

Lo acepta, veamos qué se almacenó:


select * from libros;

Truncó el valor.

Intentamos ingresar un código con comillas (una cadena):

insert into libros (codigo,titulo,autor,editorial,precio,cantidad)


values(4,'Uno','Richard Bach','Planeta','50',100);

Oracle lo convierte a número.

Intentamos ingresar una cadena que Oracle no pueda convertir a valor numérico en el campo "precio":

insert into libros (codigo,titulo,autor,editorial,precio,cantidad)


values(5,'Alicia en el pais...','Lewis Carroll','Planeta','50.30',200);

Error.

17 - Ingresar algunos campos

Hemos aprendido a ingresar registros listando todos los campos y colocando valores para todos y cada uno de ellos
luego de "values".

Si ingresamos valores para todos los campos, podemos omitir la lista de nombres de los campos.
Por ejemplo, si tenemos creada la tabla "libros" con los campos "titulo", "autor" y "editorial", podemos ingresar un
registro de la siguiente manera:

insert into libros values ('Uno','Richard Bach','Planeta');

También es posible ingresar valores para algunos campos. Ingresamos valores solamente para los campos "titulo" y
"autor":

insert into libros (titulo, autor)


values ('El aleph','Borges');

Oracle almacenará el valor "null" en el campo "editorial", para el cual no hemos explicitado un valor.

Al ingresar registros debemos tener en cuenta:

- la lista de campos debe coincidir en cantidad y tipo de valores con la lista de valores luego de "values". Si se listan
más (o menos) campos que los valores ingresados, aparece un mensaje de error y la sentencia no se ejecuta.

- si ingresamos valores para todos los campos podemos obviar la lista de campos.

- podemos omitir valores para los campos que permitan valores nulos (se guardará "null"); si omitimos el valor para un
campo "not null", la sentencia no se ejecuta.

Problema:

Trabajamos con la tabla "libros" que almacena los datos de los libros de una librería.

Eliminamos la tabla:

drop table libros;

Creamos la tabla:

create table libros(


codigo number(5) not null,
titulo varchar2(40) not null,
autor varchar2(30),
editorial varchar2(15)
);

Si ingresamos valores para todos los campos, podemos omitir la lista de campos:

insert into libros


values (1,'Uno','Richard Bach','Planeta');

Podemos ingresar valores para algunos de los campos:

insert into libros (codigo, titulo, autor)


values (2,'El aleph','Borges');

Veamos cómo Oracle almacenó los registros:

select * from libros;

En el campo "editorial", para el cual no ingresamos valor, se almacenó "null".

No podemos omitir el valor para un campo declarado "not null", como el campo "codigo":

insert into libros (titulo, autor,editorial)


values ('Alicia en el pais de las maravillas','Lewis Carroll','Planeta');

aparece un mensaje y la inserción no se realiza.

Veamos cómo Oracle almacenó los registros:

select * from libros;

18 - Valores por defecto (default)

Hemos visto que si al insertar registros no se especifica un valor para un campo que admite valores nulos, se ingresa
automáticamente "null". A este valor se le denomina valor por defecto o predeterminado.

Un valor por defecto se inserta cuando no está presente al ingresar un registro.

Para campos de cualquier tipo no declarados "not null", es decir, que admiten valores nulos, el valor por defecto es
"null". Para campos declarados "not null", no existe valor por defecto, a menos que se declare explícitamente con la
cláusula "default".

Podemos establecer valores por defecto para los campos cuando creamos la tabla. Para ello utilizamos "default" al
definir el campo. Por ejemplo, queremos que el valor por defecto del campo "autor" de la tabla "libros" sea
"Desconocido" y el valor por defecto del campo "cantidad" sea "0":

create table libros(


titulo varchar2(40) not null,
autor varchar2(30) default 'Desconocido' not null,
editorial varchar2(20),
precio number(5,2),
cantidad number(3) default 0
);

Si al ingresar un nuevo registro omitimos los valores para el campo "autor" y "cantidad", Oracle insertará los valores
por defecto; en "autor" colocará "Desconocido" y en cantidad "0".

Entonces, si al definir el campo explicitamos un valor mediante la cláusula "default", ése será el valor por defecto.
La cláusula "default" debe ir antes de "not null" (si existiese), sino aparece un mensaje de error.

Para ver si los campos de la tabla "libros" tiene definidos valores por defecto y cuáles son, podemos realizar la
siguiente consulta:

select column_name,nullable,data_default
from user_tab_columns where TABLE_NAME = 'libros';

Muestra una fila por cada campo, en la columna "data_default" aparece el valor por defecto (si lo tiene), en la
columna "nullable" aparece "N" si el campo no está definido "not null" y "Y" si admite valores "null".

También se puede utilizar "default" para dar el valor por defecto a los campos en sentencias "insert", por ejemplo:

insert into libros (titulo,autor,editorial,precio,cantidad)


values ('El gato con botas',default,default,default,100);

Entonces, la cláusula "default" permite especificar el valor por defecto de un campo. Si no se explicita, el valor por
defecto es "null", siempre que el campo no haya sido declarado "not null".

Los campos para los cuales no se ingresan valores en un "insert" tomarán los valores por defecto:

- si permite valores nulos y no tiene cláusula "default", almacenará "null";

- si tiene cláusula "default" (admita o no valores nulos), el valor definido como predeterminado;

- si está declarado explícitamente "not null" y no tiene valor "default", no hay valor por defecto, así que causará un
error y el "insert" no se ejecutará.

Un campo sólo puede tener un valor por defecto. Una tabla puede tener todos sus campos con valores por defecto.
Que un campo tenga valor por defecto no significa que no admita valores nulos, puede o no admitirlos.

Un campo definido como clave primaria acepta un valor "default", pero no tiene sentido ya que el valor por defecto
solamente podrá ingresarse una vez; si intenta ingresarse cuando otro registro ya lo tiene almacenado, aparecerá un
mensaje de error indicando que se intenta duplicar la clave.

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla:

drop table libros;

Creamos la tabla estableciendo valores por defecto para los campos "autor" y "cantidad":

create table libros(


titulo varchar2(40) not null,
autor varchar2(30) default 'Desconocido' not null,
editorial varchar2(20),
precio number(5,2),
cantidad number(3) default 0
);

Ingresamos un registro omitiendo los valores para el campo "autor" y "cantidad":

insert into libros (titulo,editorial,precio)


values('Java en 10 minutos','Paidos',50.40);

Oracle ingresará el registro con el título, editorial y precio especificados, en "autor" colocará "Desconocido" y en
cantidad "0", veámoslo:

select * from libros;

Si ingresamos un registro sin valor para el campo "precio", que admite valores nulos, se ingresará "null" en ese campo:
insert into libros (titulo,editorial)
values('Aprenda PHP','Siglo XXI');

Veamos cómo se almacenó el registro ingresado anteriormente:

select * from libros;

Veamos si los campos de la tabla "libros" tiene definidos valores por defecto y cuáles son:

select column_name,nullable,data_default
from user_tab_columns where TABLE_NAME = 'LIBROS';

Muestra la siguiente tabla:

COLUMN_NAME NULLABLE DATA_DEFAULT


---------------------------------------------
TITULO N
AUTOR N 'Desconocido'
EDITORIAL Y
PRECIO Y
CANTIDAD Y 0

Muestra una fila por cada campo, en la columna "data_default" aparece el valor por defecto (si lo tiene), en la
columna "nullable" aparece "N" si el campo no está definido "not null" y "Y" si permite valores nulos.

Podemos emplear "default" para dar el valor por defecto a algunos campos al ingresar un registro:

insert into libros (titulo,autor,editorial,precio,cantidad)


values ('El gato con botas',default,default,default,default);

Veamos cómo se almacenó el registro:

select *from libros where titulo='El gato con botas';

Todos los campos, excepto "titulo" almacenaron su valor predeterminado.

Que un campo tenga valor por defecto no significa que no admita valores nulos, puede o no admitirlos. Podemos
ingresar el valor "null" en el campo "cantidad":

insert into libros (titulo,autor,cantidad)


values ('Alicia en el pais de las maravillas','Lewis Carroll',null);

Recuperamos el registro ingresado anteriormente:

select * from libros where autor='Lewis Carroll';

En "cantidad" se almacenó "null".

20 - Alias (encabezados de columnas)

Una manera de hacer más comprensible el resultado de una consulta consiste en cambiar los encabezados de las
columnas. Por ejemplo, tenemos la tabla "libros" con un campo "cantidad" (entre otros) en el cual se almacena la
cantidad de libros en stock; queremos que al mostrar la información de dicha tabla aparezca como encabezado del
campo "cantidad" el texto "stock", para ello colocamos un alias de la siguiente manera:

select titulo,
cantidad as stock,
precio
from libros;
Para reemplazar el nombre de un campo del encabezado por otro, se coloca la palabra clave "as" seguido del texto
del encabezado.

Si el alias consta de una sola cadena las comillas no son necesarias, pero si contiene más de una palabra, es necesario
colocarla entre comillas dobles:

select titulo,
cantidad as "stock disponible",
precio
from libros;

También se puede crear un alias para columnas calculadas. Por ejemplo:

select titulo,precio,
precio*0.1 as descuento,
precio-(precio*0.1) as "preciofinal"
from libros;

La palabra clave "as" es opcional, pero es conveniente usarla.

Entonces, un "alias" se usa como nombre de un campo o de una expresión. En estos casos, son opcionales, sirven para
hacer más comprensible el resultado.

Problema:

Trabajamos con nuestra tabla "libros".

Eliminamos la tabla:

drop table libros;

Creamos la tabla:

create table libros(


titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(5,2),
cantidad number(4)
);

Ingresamos algunos registros:

insert into libros


values('Uno','Richard Bach','Planeta',15,100);
insert into libros
values('El aleph','Borges','Emece',24,150);
insert into libros
values('Matematica estas ahi','Paenza','Nuevo siglo',12.5,200);

Mostramos la información con el encabezado "stock" para el campo "cantidad":

select titulo,
cantidad as stock,
precio
from libros;

Mostramos la información con el encabezado "stock disponible" para el campo "cantidad", necesitamos emplear
comillas dobles:

select titulo,
cantidad as "stock disponible",
precio
from libros;
Recuperamos el título, autor, precio, descuento del 10% y precio final con descuento, empleando un alias para las 2
últimas columnas:

select titulo,autor,precio,
precio*0.1 as descuento,
precio-(precio*0.1) as "precio final"
from libros;

La palabra clave "as" es opcional, podemos obviarla:

select titulo,precio,
precio-(precio*0.1) "precio con descuento"
from libros;

21 - Funciones string

Las funciones de manejo de caracteres alfanuméricos aceptan argumentos de tipo caracter y retornan caracteres o
valores numéricos.

Las siguientes son algunas de las funciones que ofrece Oracle para trabajar con cadenas de caracteres:

- chr(x): retorna un caracter equivalente al código enviado como argumento "x". Ejemplo:

select chr(65) from dual;-- retorna 'A'.


select chr(100) from dual;-- retorna 'd'.

- concat(cadena1,cadena2): concatena dos cadenas de caracteres; es equivalente al operador ||. Ejemplo:

select concat('Buenas',' tardes') from dual;--retorna 'Buenas tardes'.

- initcap(cadena): retorna la cadena enviada como argumento con la primera letra (letra capital) de cada palabra en
mayúscula. Ejemplo:

select initcap('buenas tardes alumno') from dual;--retorna 'Buenas Tardes Alumno'.

- lower(cadena): retorna la cadena enviada como argumento en minúsculas. "lower" significa reducir en inglés.
Ejemplo:

select lower('Buenas tardes ALUMNO') from dual;--retorna "buenas tardes alumno".

- upper(cadena): retorna la cadena con todos los caracteres en mayúsculas. Ejemplo:

select upper('www.oracle.com') from dual;-- 'WWW.ORACLE.COM'

- lpad(cadena,longitud,cadenarelleno): retorna la cantidad de caracteres especificados por el argumento "longitud",


de la cadena enviada como primer argumento (comenzando desde el primer caracter); si "longitud" es mayor que el
tamaño de la cadena enviada, rellena los espacios restantes con la cadena enviada como tercer argumento (en caso
de omitir el tercer argumento rellena con espacios); el relleno comienza desde la izquierda. Ejemplos:

select lpad('alumno',10,'xyz') from dual;-- retorna 'xyzxalumno'


select lpad('alumno',4,'xyz') from dual;-- retorna 'alum'

- rpad(cadena,longitud,cadenarelleno): retorna la cantidad de caracteres especificados por el argumento "longitud",


de la cadena enviada como primer argumento (comenzando desde el primer caracter); si "longitud" es mayor que el
tamaño de la cadena enviada, rellena los espacios restantes con la cadena enviada como tercer argumento (en caso
de omitir el tercer argumento rellena con espacios); el relleno comienza desde la derecha (último caracter).
Ejemplos:

select rpad('alumno',10,'xyz') from dual;-- retorna 'alumnoxyzx'


select rpad('alumno',4,'xyz') from dual;-- retorna 'alum'
- ltrim(cadena1,cadena2): borra todas las ocurrencias de "cadena2" en "cadena1", si se encuentran al comienzo; si se
omite el segundo argumento, se eliminan los espacios. Ejemplo:

select ltrim('la casa de la cuadra','la') from dual;-- ' casa de la cuadra'


select ltrim(' es la casa de la cuadra','la') from dual;-- no elimina ningún caracter
select ltrim(' la casa') from dual;-- 'la casa'

- rtrim(cadena1,cadena2): borra todas las ocurrencias de "cadena2" en "cadena1", si se encuentran por la derecha (al
final de la cadena); si se omite el segundo argumento, se borran los espacios. Ejemplo:

select rtrim('la casa lila','la') from dual;-- 'la casa li'


select rtrim('la casa lila ','la') from dual;-- no borra ningún caracter
select rtrim('la casa lila ') from dual; --'la casa lila'

- trim(cadena): retorna la cadena con los espacios de la izquierda y derecha eliminados. "Trim" significa recortar.
Ejemplo:

select trim(' oracle ') from dual;--'oracle'

- replace(cadena,subcade1,subcade2): retorna la cadena con todas las ocurrencias de la subcadena de reemplazo


(subcade2) por la subcadena a reemplazar (subcae1). Ejemplo:

select replace('xxx.oracle.com','x','w') from dual;

retorna "www.oracle.com'.

- substr(cadena,inicio,longitud): devuelve una parte de la cadena especificada como primer argumento, empezando
desde la posición especificada por el segundo argumento y de tantos caracteres de longitud como indica el tercer
argumento. Ejemplo:

select substr('www.oracle.com',1,10) from dual;-- 'www.oracle'


select substr('www.oracle.com',5,6) from dual;-- 'oracle'

- length(cadena): retorna la longitud de la cadena enviada como argumento. "lenght" significa longitud en inglés.
Ejemplo:

select length('www.oracle.com') from dual;-- devuelve 14.

- instr (cadena,subcadena): devuelve la posición de comienzo (de la primera ocurrencia) de la subcadena


especificada en la cadena enviada como primer argumento. Si no la encuentra retorna 0. Ejemplos:

select instr('Jorge Luis Borges','or') from dual;-- 2


select instr('Jorge Luis Borges','ar') from dual;-- 0, no se encuentra

- translate(): reemplaza cada ocurrencia de una serie de caracteres con otra serie de acracteres. La diferencia con
"replace" es que aquella trabaja con cadenas de caracteres y reemplaza una cadena completa por otra, en cambio
"translate" trabaja con caracteres simples y reemplaza varios. En el siguiente ejemplo se especifica que se
reemplacen todos los caracteres "O" por el caracter "0", todos los caracteres "S" por el caracter "5" y todos los
caracteres "G" por "6":

select translate('JORGE LUIS BORGES','OSG','056') from dual;--'J0R6E LUI5 B0R6E5'

Se pueden emplear estas funciones enviando como argumento el nombre de un campo de tipo caracter.

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla:

drop table libros;

Creamos la tabla:
create table libros(
codigo number(5),
titulo varchar2(40) not null,
autor varchar2(20) default 'Desconocido',
editorial varchar2(20),
precio number(6,2),
cantidad number(3)
);

Ingresamos algunos registros:

insert into libros


values(1,'El aleph','Borges','Emece',25,100);
insert into libros
values(2,'Java en 10 minutos','Mario Molina','Siglo XXI',50.40,100);
insert into libros
values(3,'Alicia en el pais de las maravillas','Lewis Carroll','Emece',15.50,200);
insert into libros
values(4,'El pais de las hadas',default,'Emece',25.50,150);

Mostramos sólo los 12 primeros caracteres de los títulos de los libros y sus autores, empleando la función "substr":

select substr(titulo,1,12) as titulo


from libros;

Mostramos sólo los 20 primeros caracteres de los títulos de los libros y rellenando los espacios restantes con "*",
empleando la función "rpad":

select rpad(titulo,20,'*') as titulo


from libros;

Mostramos los títulos de los libros empleando la función "initcap":

select initcap(titulo) as titulo


from libros;

Note que cada palabra comienza con mayúsculas.

Mostramos los títulos de los libros y sus autores en mayúsculas:

select titulo,upper(autor) as autor


from libros;

Concatenamos título y autor empleando "concat":

select concat(titulo, autor)


from libros;

Mostramos el título y el precio de todos los libros concatenando el signo "$" a los precios:

select titulo,concat('$ ',precio) as precio


from libros;

Recuperamos el título y editorial de "libros" reemplazando "Emece" por "Sudamericana":

select titulo,replace(editorial,'Emece','Sudamericana')
from libros;

Recuperamos el autor de todos los libros reemplazando las letras "abc" por "ABC" respectivamente (empleando
"translate"):

select translate(autor,'abc','ABC') from libros;

Note que cada caracter individual es reemplazado por el especificado.


Mostramos la posición de la primera ocurrencia de la cadena "pais" en los títulos de los libros:

select instr(titulo,'pais') from libros;

Note que los títulos que no contienen la subcadena "pais" muestran el valor cero.

22 - Funciones matemáticas.

Las funciones matemáticas realizan operaciones con expresiones numéricas y retornan un resultado, operan con tipos
de datos numéricos.

Las funciones numéricas aceptan parámetros de entrada de tipo numérico y retornan valores numéricos.

Oracle tiene algunas funciones para trabajar con números. Aquí presentamos algunas.

- abs(x): retorna el valor absoluto del argumento "x". Ejemplo:

select abs(-20) from dual;--retorna 20.

La tabla dual es una tabla virtual que existe en todas las Bases de datos Oracle.

- ceil(x): redondea a entero, hacia arriba, el argumento "x". Ejemplo:

select ceil(12.34) from dual;--retorna 13.

- floor(x): redondea a entero, hacia abajo, el argumento "x". Ejemplo:

select floor(12.34) from dual; --12

- mod(x,y): devuelve el resto de la división x/y. Ejemplos:

select mod(10,3) from dual;--retorna 1.


select mod(10,2) from dual;--retorna 0.

- power(x,y): retorna el valor de "x" elevado a la "y" potencia. Ejemplo:

select power(2,3) from dual;--retorna 8.

- round(n,d): retorna "n" redondeado a "d" decimales; si se omite el segundo argumento, redondea todos los
decimales. Si el segundo argumento es positivo, el número de decimales es redondeado según "d"; si es negativo, el
número es redondeado desde la parte entera según el valor de "d". Ejemplos:

select round(123.456,2) from dual;-- retorna "123.46", es decir, redondea desde el


segundo decimal.
select round(123.456,1) from dual;-- 123.5, es decir, redondea desde el primer decimal.
select round(123.456,-1) from dual;-- 120, redondea desde el primer valor entero (hacia
la izquierda).
select round(123.456,-2) from dual;-- 100, redondea desde el segundo valor entero
(hacia la izquierda).
select round(123.456) from dual;-- 123.

- sign(x): si el argumento es un valor positivo, retorna 1, si es negativo, devuelve -1 y 0 si es 0. Ejemplo:

select sign(-120) from dual;--retorna -1


select sign(120) from dual;--retorna 1

- trunc(n,d): trunca un número a la cantidad de decimales especificada por el segundo argumento. Si se omite el
segundo argumento, se truncan todos los decimales. Si "d" es negativo, el número es truncado desde la parte entera.
Ejemplo:
select trunc(1234.5678,2) from dual;--retorna 1234.56
select trunc(1234.5678,-2) from dual;--retorna 1200
select trunc(1234.5678,-1) from dual;--retorna 1230
select trunc(1234.5678) from dual;--retorna 1234

- sqrt(x): devuelve la raiz cuadrada del valor enviado como argumento. Ejemplo:

select sqrt(9) from dual;--retorna 3

Oracle dispone de funciones trigonométricas que retornan radianes, calculan seno, coseno, inversas, etc.: acos, asin,
atan, atan2, cos, cosh, exp, ln, log, sin, sinh, tan, tanh. No las veremos en detalle.

Se pueden emplear las funciones matemáticas enviando como argumento el nombre de un campo de tipo numérico.

Problema:

Una empresa almacena los datos de sus empleados en una tabla denominada "empleados".

Eliminamos la tabla:

drop table empleados;

Creamos la tabla:

create table empleados(


legajo number(5),
documento char(8) not null,
nombre varchar2(30) not null,
domicilio varchar2(30),
sueldo number(6,2),
hijos number(2),
primary key (legajo)
);

Ingresamos algunos registros:

insert into empleados


values(1,'22333444','Ana Acosta','Avellaneda 213',870.79,2);
insert into empleados
values(20,'27888999','Betina Bustos','Bulnes 345',950.85,1);
insert into empleados
values(31,'30111222','Carlos Caseres','Caseros 985',1190,0);
insert into empleados
values(39,'33444555','Daniel Duarte','Dominicana 345',1250.56,3);

Vamos a mostrar los sueldos de los empleados redondeando el valor hacia abajo y luego hacia arriba (empleamos
"floor" y "ceil"):

select floor(sueldo) as "sueldo hacia abajo",


ceil(sueldo) as "sueldo hacia arriba"
from empleados;

Mostramos los nombre de cada empleado, su respectivo sueldo, y el sueldo redondeando el valor a entero ("round") y
truncado a entero ("trunc"):

select nombre, sueldo, round(sueldo) as "sueldo redondeado",


trunc(sueldo) as "sueldo truncado"
from empleados;

Note que los valores devueltos según la función empleada, son diferentes.

Mostramos el resultado de "2 elevado a la potencia 5" ("power"):

select power(2,5) from dual;


Retorna el valor 32.

Mostramos el resto de la división "1234 / 5" ("mod"):

select mod(1234,5) from dual;

Devuelve el valor 4.

Calculamos la raíz cuadrada de 81:

select sqrt(81) from dual;

Retorna 9.

23 - Funciones de fechas y horas

Oracle dispone de varias funciones que operan con tipos de datos "date". Estas son algunas:

- add_months(f,n): agrega a una fecha, un número de meses. Si el segundo argumento es positivo, se le suma a la
fecha enviada tal cantidad de meses; si es negativo, se le resta a la fecha enviada tal cantidad de meses. Ejemplo:

select add_months('10/06/2007',5) from dual; --retorna "10/11/07"


select add_months('10/06/2007',-5) from dual; --retorna "10/01/07"
select add_months('30/01/2007',1) from dual;-- retorna "25/02/07" ya que es el último
día de ese mes.

- last_day(f): retorna el ultimo día de mes de la fecha enviada como argumento. Ejemplo:

select last_day('10/02/2007') from dual;-- "28/02/07"


select last_day('10/08/2007') from dual;-- "31/08/07"

- months_between(f1,f2): retorna el numero de meses entre las fechas enviadas como argumento. Ejemplo:

select months_between('19/05/2003','21/06/05') from dual;-- retorna

- next_day(fecha,dia): retorna una fecha correspondiente al primer día especificado en "dia" luego de la fecha
especificada. En el siguiente ejemplo se busca el lunes siguiente a la fecha especificada:

select next_day('10/08/2007','LUNES') from dual;

- current_date: retorna la fecha actual. Ejemplo:

select current_date from dual;

- current_timestamp: retorna la fecha actual

select current_timestamp from dual;

Retorna: 10/08/07 09:59:44,109000000 AMERICA/BUENOS_AIRES

- sysdate: retorna la fecha y hora actuales en el servidor de Oracle.

-systimestamp: retorna fecha y hora actuales.

select systimestamp from dual;

Retorna 10/08/07 10:33:48,984000000 -03:00


- to_date: convierte una cadena a tipo de dato "date". Ejemplo:

select to_date ('05-SEP-2007 10:00 AM','DD-MON-YYYY HH:MI AM') from dual;

Retorna 05/09/07

- to_char: convierte una fecha a cadena de caracteres. Ejemplo:

select to_char('10/10/2007')from dual;

- extract(parte,fecha): retorna la parte (especificada por el primer argumento) de una fecha. Puede extraer el año
(year), mes (month), día (day), hora (hour), minuto (minute), segundo (second), etc. Ejemplo:

select extract(month from sysdate) from dual;

retorna el número mes de la fecha actual.

En Oracle: Los operadores aritméticos "+" (más) y "-" (menos) pueden emplearse con fechas. Por ejemplos:

select sysdate-3:

Retorna 3 días antes de la fecha actual.

select to_date('15/12/2007')-5 from dual;

Retorna 10/12/07

Se pueden emplear estas funciones enviando como argumento el nombre de un campo de tipo date.

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla:

drop table libros;

Creamos la tabla:

create table libros(


titulo varchar2(40) not null,
autor varchar2(20) default 'Desconocido',
editorial varchar2(20),
edicion date,
precio number(6,2)
);

Ingresamos algunos registros:

insert into libros values('El aleph','Borges','Emece','10/10/1980',25.33);


insert into libros values('Java en 10 minutos','Mario Molina','Siglo
XXI','05/05/2000',50.65);
insert into libros
values('Alicia en el pais de las maravillas','Lewis
Carroll','Emece','08/09/2000',19.95);
insert into libros values('Aprenda PHP','Mario Molina','Siglo XXI','02/04/2000',45);

Mostramos el título del libro y el año de edición:

select titulo, extract (year from edicion) from libros;

Mostramos el título del libro y el mes de edición:


select titulo, extract (month from edicion) from libros;

Mostramos el título del libro y los años que tienen de editados:

select titulo, extract(year from sysdate)-extract(year from edicion) as "años de


editado"
from libros;

Mostramos los títulos de los libros que se editaron en el año 2000:

select titulo from libros


where extract(year from edicion)=2000;

Calcule 3 meses luego de la fecha actual empleando ""add_months":

select add_months(sysdate,3) from dual;

Muestre la fecha del primer martes desde la fecha actual:

select next_day(sysdate,'martes') from dual;

Muestre la fecha que será 15 días después de "24/08/2007" empleando el operador "+":

select to_date('24/08/2007')+15 from dual;

Retorna 08/09/07.

Muestre la fecha que 20 días antes del "12/08/2007" empleando el operador "-":

select to_date('12/08/2007')-20 from dual;

Retorna 23/07/07.

24 - Ordenar registros (order by)

Podemos ordenar el resultado de un "select" para que los registros se muestren ordenados por algún campo, para ello
usamos la cláusula "order by".

La sintaxis básica es la siguiente:

select *from NOMBRETABLA


order by CAMPO;

Por ejemplo, recuperamos los registros de la tabla "libros" ordenados por el título:

select *from libros


order by titulo;

Aparecen los registros ordenados alfabéticamente por el campo especificado.

También podemos colocar el número de orden del campo por el que queremos que se ordene en lugar de su nombre,
es decir, referenciar a los campos por su posición en la lista de selección. Por ejemplo, queremos el resultado del
"select" ordenado por "precio":

select titulo,autor,precio
from libros order by 3;
Si colocamos un número mayor a la cantidad de campos de la lista de selección, aparece un mensaje de error y la
sentencia no se ejecuta.

Por defecto, si no aclaramos en la sentencia, los ordena de manera ascendente (de menor a mayor). Podemos
ordenarlos de mayor a menor, para ello agregamos la palabra clave "desc":

select *libros
order by editorial desc;

También podemos ordenar por varios campos, por ejemplo, por "titulo" y "editorial":

select *from libros


order by titulo,editorial;

Incluso, podemos ordenar en distintos sentidos, por ejemplo, por "titulo" en sentido ascendente y "editorial" en
sentido descendente:

select *from libros


order by titulo asc, editorial desc;

Debe aclararse al lado de cada campo, pues estas palabras claves afectan al campo inmediatamente anterior.

Es posible ordenar por un campo que no se lista en la selección incluso por columnas calculados.

Se puede emplear "order by" con campos de tipo caracter, numérico y date.

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla y la creamos con la siguiente estructura:

drop table libros;

create table libros(


titulo varchar2(40) not null,
autor varchar2(20) default 'Desconocido',
editorial varchar2(20),
edicion date,
precio number(6,2)
);

Ingresamos algunos registros:

insert into libros values('El aleph','Borges','Emece','10/10/1980',25.33);


insert into libros values('Java en 10 minutos','Mario Molina','Siglo
XXI','05/12/2005',50.65);
insert into libros values('Alicia en el pais de las maravillas','Lewis
Carroll','Emece','29/11/2000',19.95);
insert into libros values('Alicia en el pais de las maravillas','Lewis
Carroll','Planeta','27/11/2004',15);

Recuperamos los registros ordenados por el título:

select *from libros


order by titulo;

Ordenamos los registros por el campo "precio", referenciando el campo por su posición en la lista de selección:

select titulo,autor,precio
from libros order by 3;

Los ordenamos por "editorial", de mayor a menor empleando "desc":

select *from libros


order by editorial desc;

Ordenamos por dos campos:

select *from libros


order by titulo,editorial;

Ordenamos en distintos sentidos:

select *from libros


order by titulo asc, editorial desc;

Podemos ordenar por un campo que no se lista en la selección:

select titulo, autor


from libros
order by precio;

Está permitido ordenar por valores calculados, lo hacemos:

select titulo, editorial,


precio+(precio*0.1) as "precio con descuento"
from libros
order by 3;

Ordenamos los libros por la fecha de edición:

select titulo, editorial, edicion


from libros
order by edicion;

Mostramos el título y año de edición de todos los libros, ordenados por año de edición:

select titulo, extract (year from edicion) as edicion


from libros
order by 2;

25 - Operadores lógicos (and - or - not)

Hasta el momento, hemos aprendido a establecer una condición con "where" utilizando operadores relacionales.
Podemos establecer más de una condición con la cláusula "where", para ello aprenderemos los operadores lógicos.

Son los siguientes:

- and, significa "y",


- or, significa "y/o",
- not, significa "no", invierte el resultado
- (), paréntesis

Los operadores lógicos se usan para combinar condiciones.

Si queremos recuperar todos los libros cuyo autor sea igual a "Borges" y cuyo precio no supere los 20 pesos,
necesitamos 2 condiciones:

select *from libros


where (autor='Borges') and
(precio<=20);

Los registros recuperados en una sentencia que une dos condiciones con el operador "and", cumplen con las 2
condiciones.
Queremos ver los libros cuyo autor sea "Borges" y/o cuya editorial sea "Planeta":

select *from libros


where autor='Borges' or
editorial='Planeta';

En la sentencia anterior usamos el operador "or"; indicamos que recupere los libros en los cuales el valor del campo
"autor" sea "Borges" y/o el valor del campo "editorial" sea "Planeta", es decir, seleccionará los registros que cumplan
con la primera condición, con la segunda condición y con ambas condiciones.

Los registros recuperados con una sentencia que une dos condiciones con el operador "or", cumplen una de las
condiciones o ambas.

Queremos recuperar los libros que NO cumplan la condición dada, por ejemplo, aquellos cuya editorial NO sea
"Planeta":

select *from libros


where not editorial='Planeta';

El operador "not" invierte el resultado de la condición a la cual antecede.

Los registros recuperados en una sentencia en la cual aparece el operador "not", no cumplen con la condición a la
cual afecta el "NOT".

Los paréntesis se usan para encerrar condiciones, para que se evalúen como una sola expresión.

Cuando explicitamos varias condiciones con diferentes operadores lógicos (combinamos "and", "or") permite
establecer el orden de prioridad de la evaluación; además permite diferenciar las expresiones más claramente.

Por ejemplo, las siguientes expresiones devuelven un resultado diferente:

select *from libros


where (autor='Borges') or
(editorial='Paidos' and precio<20);

select *from libros


where (autor='Borges' or editorial='Paidos') and
(precio<20);

Si bien los paréntesis no son obligatorios en todos los casos, se recomienda utilizarlos para evitar confusiones.

El orden de prioridad de los operadores lógicos es el siguiente: "not" se aplica antes que "and" y "and" antes que "or",
si no se especifica un orden de evaluación mediante el uso de paréntesis. El orden en el que se evalúan los
operadores con igual nivel de precedencia es indefinido, por ello se recomienda usar los paréntesis.

Entonces, para establecer más de una condición en un "where" es necesario emplear operadores lógicos. "and"
significa "y", indica que se cumplan ambas condiciones; "or" significa "y/o", indica que se cumpla una u otra condición
(o ambas); "not" significa "no.", indica que no se cumpla la condición especificada.

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla y la creamos:

drop table libros;

create table libros(


codigo number(5),
titulo varchar2(40) not null,
autor varchar2(20) default 'Desconocido',
editorial varchar2(20),
precio number(6,2)
);

Ingresamos algunos registros:


insert into libros
values(1,'El aleph','Borges','Emece',15.90);
insert into libros
values(2,'Antología poética','Borges','Planeta',39.50);
insert into libros
values(3,'Java en 10 minutos','Mario Molina','Planeta',50.50);
insert into libros
values(4,'Alicia en el pais de las maravillas','Lewis Carroll','Emece',19.90);
insert into libros
values(5,'Martin Fierro','Jose Hernandez','Emece',25.90);
insert into libros
values(6,'Martin Fierro','Jose Hernandez','Paidos',16.80);
insert into libros
values(7,'Aprenda PHP','Mario Molina','Emece',19.50);
insert into libros
values(8,'Cervantes y el quijote','Borges','Paidos',18.40);

Recuperamos los libros cuyo autor sea igual a "Borges" y cuyo precio no supere los 20 pesos:

select *from libros


where (autor='Borges') and
(precio<=20);

Aparecen 2 libros, los únicos que cumplen con ambas condiciones.

Seleccionamos los libros cuyo autor es "Borges" y/o cuya editorial es "Planeta":

select *from libros


where autor='Borges' or
editorial='Planeta';

Note que aparecen todos los libros de "Borges" y todos los libros de "Planeta", algunos cumplen ambas condiciones.

Recuperamos los libros cuya editorial NO es "Planeta":

select *from libros


where not editorial='Planeta';

Veamos un ejemplo de cómo el empleo de paréntesis permite a Oracle evaluar en forma diferente ciertas consultas
aparentemente iguales:

select *from libros


where (autor='Borges') or
(editorial='Paidos' and precio<20);

select *from libros


where (autor='Borges' or editorial='Paidos') and
(precio<20);

Note que el primer resultado retorna todos los libros de "Borges" (primera condición) y todos los libros de "Paidos" con
precio inferior a 20 (segunda condición) (registros 1,2,6 y 8); la segunda recupera todos los libros de "Borges" o de
"Paidos" (primera condición) cuyo precio sea inferior a 20 (segunda condición) (registros 1,6 y 8).

26 - Otros operadores relacionales (between)

Hemos visto los operadores relacionales: = (igual), <> (distinto), > (mayor), < (menor), >= (mayor o igual), <= (menor
o igual), is null/is not null (si un valor es NULL o no).

Otro operador relacional es "between", trabajan con intervalos de valores.

Hasta ahora, para recuperar de la tabla "libros" los libros con precio mayor o igual a 20 y menor o igual a 40, usamos
2 condiciones unidas por el operador lógico "and":
select *from libros
where precio>=20 and
precio<=40;

Podemos usar "between" y así simplificar la consulta:

select *from libros


where precio between 20 and 40;

Averiguamos si el valor de un campo dado (precio) está entre los valores mínimo y máximo especificados (20 y 40
respectivamente).

"between" significa "entre". Trabaja con intervalo de valores.

Este operador no tiene en cuenta los valores "null".

Si agregamos el operador "not" antes de "between" el resultado se invierte, es decir, se recuperan los registros que
están fuera del intervalo especificado. Por ejemplo, recuperamos los libros cuyo precio NO se encuentre entre 20 y
30, es decir, los menores a 20 y mayores a 30:

select *from libros


where precio not between 20 and 30;

Podemos especificar un intervalo de valores de tipo fecha con "between":

select *from libros


where edicion between '01/05/2000' and '01/05/2007';

Entonces, empleamos el operador "between" para reducir las condiciones "where".

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla y la creamos con la siguiente estructura:

drop table libros;

create table libros(


codigo number(5) not null,
titulo varchar2(40) not null,
autor varchar2(20) default 'Desconocido',
editorial varchar2(20),
edicion date,
precio number(6,2)
);

Ingresamos algunos registros:

insert into libros


values(1,'El aleph','Borges','Emece','15/01/2000',15.90);
insert into libros
values(2,'Cervantes y el quijote','Borges','Paidos',null,null);
insert into libros
values(3,'Alicia en el pais de las maravillas','Lewis
Carroll',null,'25/03/2000',19.90);
insert into libros
values(4,'Martin Fierro','Jose Hernandez','Emece','18/05/2000',25.90);
insert into libros (codigo,titulo,autor,edicion,precio)
values(5,'Antología poética','Borges','25/08/2000',32);
insert into libros (codigo,titulo,autor,edicion,precio)
values(6,'Java en 10 minutos','Mario Molina','11/02/2007',45.80);
insert into libros (codigo,titulo,autor,edicion,precio)
values(7,'Martin Fierro','Jose Hernandez','23/11/2006',40);
insert into libros (codigo,titulo,autor,edicion,precio)
values(8,'Aprenda PHP','Mario Molina','01/06/2007',56.50);
Recuperamos los registros cuyo precio esté entre 20 y 40 empleando "between":

select *from libros


where precio between 20 and 40;

Note que si el campo tiene el valor "null", no aparece en la selección.

Para seleccionar los libros cuyo precio NO esté entre un intervalo de valores antecedemos "not" al "between":

select *from libros


where precio not between 20 and 40;

Note que si el campo tiene el valor "null", no aparece en la selección.

Recuperamos los títulos y edición de los libros cuya fecha de edición se encuentre entre '01/05/2000' y '01/05/2007',
ordenados por fecha de edición:

select titulo, edicion from libros


where edicion between '01/05/2000' and '01/05/2007'
order by edicion;

27 - Otros operadores relacionales (in)

Se utiliza "in" para averiguar si el valor de un campo está incluido en una lista de valores especificada.

En la siguiente sentencia usamos "in" para averiguar si el valor del campo autor está incluido en la lista de valores
especificada (en este caso, 2 cadenas).
Hasta ahora, para recuperar los libros cuyo autor sea 'Paenza' o 'Borges' usábamos 2 condiciones:

select * from libros


where autor='Borges' or autor='Paenza';

Podemos usar "in" y simplificar la consulta:

select *from libros


where autor in('Borges','Paenza');

Para recuperar los libros cuyo autor no sea 'Paenza' ni 'Borges' usábamos:

select *from libros


where autor<>'Borges' and
autor<>'Paenza';

También podemos usar "in" anteponiendo "not":

select *from libros


where autor not in ('Borges','Paenza');

Empleando "in" averiguamos si el valor del campo está incluido en la lista de valores especificada; con "not"
antecediendo la condición, invertimos el resultado, es decir, recuperamos los valores que no se encuentran (no
coinciden) con la lista de valores.
Los valores "null" no se consideran.

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla:

drop table libros;


Creamos la tabla:

create table libros(


codigo number (5),
titulo varchar2(40) not null,
autor varchar2(20),
editorial varchar2(20),
precio number(6,2)
);

Ingresamos algunos registros:

insert into libros


values(1,'El aleph','Borges','Emece',15.90);
insert into libros
values(2,'Cervantes y el quijote','Borges','Paidos',null);
insert into libros
values(3,'Alicia en el pais de las maravillas','Lewis Carroll',null,19.90);
insert into libros
values(4,'Matematica estas ahi','Paenza','Siglo XXI',15);
insert into libros
values(5,'Antología poética',default,default,32);
insert into libros
values(6,'Martin Fierro','Jose Hernandez',default,40);
insert into libros
values(7,'Aprenda PHP','Mario Molina',default,56.50);

Recuperamos los libros cuyo autor es "Paenza" o "Borges":

select *from libros


where autor in('Borges','Paenza');

Note que los valores "null" no se consideran.

Recuperamos los libros cuyo autor NO es "Paenza" ni "Borges":

select *from libros


where autor not in ('Borges','Paenza');

Note que los valores "null" no se consideran.

Recuperamos los libros cuyo código se encuentre en la siguiente lista de valores (1,3,5,7,9):

select *from libros


where codigo in (1,3,5,7,9);

28 - Búsqueda de patrones (like - not like)

Existe un operador relacional que se usa para realizar comparaciones exclusivamente de cadenas, "like" y "not like".

Hemos realizado consultas utilizando operadores relacionales para comparar cadenas. Por ejemplo, sabemos
recuperar los libros cuyo autor sea igual a la cadena "Borges":

select *from libros


where autor='Borges';

El operador igual ("=") nos permite comparar cadenas de caracteres, pero al realizar la comparación, busca
coincidencias de cadenas completas, realiza una búsqueda exacta.

Imaginemos que tenemos registrados estos 2 libros:

"El Aleph", "Borges";


"Antologia poetica", "J.L. Borges";
Si queremos recuperar todos los libros de "Borges" y especificamos la siguiente condición:

select *from libros


where autor='Borges';

sólo aparecerá el primer registro, ya que la cadena "Borges" no es igual a la cadena "J.L. Borges".

Esto sucede porque el operador "=" (igual), también el operador "<>" (distinto) comparan cadenas de caracteres
completas. Para comparar porciones de cadenas utilizamos los operadores "like" y "not like".

Entonces, podemos comparar trozos de cadenas de caracteres para realizar consultas. Para recuperar todos los
registros cuyo autor contenga la cadena "Borges" debemos tipear:

select *from libros


where autor like "%Borges%";

El símbolo "%" (porcentaje) reemplaza cualquier cantidad de caracteres (incluyendo ningún caracter). Es un caracter
comodín. "like" y "not like" son operadores de comparación que señalan igualdad o diferencia.

Para seleccionar todos los libros que comiencen con "M":

select *from libros


where titulo like 'M%';

Note que el símbolo "%" ya no está al comienzo, con esto indicamos que el título debe tener como primera letra la "M"
y luego, cualquier cantidad de caracteres.

Para seleccionar todos los libros que NO comiencen con "M":

select *from libros


where titulo not like 'M%';

Así como "%" reemplaza cualquier cantidad de caracteres, el guión bajo "_" reemplaza un caracter, es otro caracter
comodín. Por ejemplo, queremos ver los libros de "Lewis Carroll" pero no recordamos si se escribe "Carroll" o
"Carrolt", entonces tipeamos esta condición:

select *from libros


where autor like "%Carrol_";

"like" se emplea con tipos de datos caracter y date. Si empleamos "like" con tipos de datos que no son caracteres,
Oracle convierte (si es posible) el tipo de dato a caracter. Por ejemplo, queremos buscar todos los libros cuyo precio
se encuentre entre 10.00 y 19.99:

select titulo,precio from libros


where precio like '1_,%';

Queremos los libros que NO incluyen centavos en sus precios:

select titulo,precio from libros


where precio not like '%,%';

Los valores nulos no se incluyen en las búsquedas con "like" y "not like".

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla:

drop table libros;

Creamos la tabla:
create table libros(
titulo varchar2(40),
autor varchar2(20) default 'Desconocido',
editorial varchar2(20),
edicion date,
precio number(6,2)
);

Ingresamos algunos registros:

insert into libros


values('El aleph','Borges','Emece','12/05/2005',15.90);
insert into libros
values('Antología poética','J. L. Borges','Planeta','16/08/2000',null);
insert into libros
values('Alicia en el pais de las maravillas','Lewis Carroll',null,'25/04/2000',19.90);
insert into libros
values('Matematica estas ahi','Paenza','Siglo XXI','21/12/2006',15);
insert into libros
values('Martin Fierro','Jose Hernandez',default,'22/09/2001',40);
insert into libros
values('Aprenda PHP','Mario Molina','Nuevo siglo','22/05/1999',56.50);
insert into libros
values(null,'Mario Molina','Nuevo siglo',null,45);

Recuperamos todos los libros que contengan en el campo "autor" la cadena "Borges":

select *from libros


where autor like '%Borges%';

Seleccionamos los libros cuyos títulos comienzan con la letra "M":

select *from libros


where titulo like 'M%';

Note que los valores nulos no se incluyen en la búsqueda.

Seleccionamos todos los títulos que NO comienzan con "M":

select *from libros


where titulo not like 'M%';

Si queremos ver los libros de "Lewis Carroll" pero no recordamos si se escribe "Carroll" o "Carrolt", podemos emplear
el comodín "_" (guión bajo) y establecer la siguiente condición:

select *from libros


where autor like '%Carrol_';

Recuperamos todos los libros que contengan en el campo "edicion", en la parte correspondiente al mes, la cadena
"05":

select titulo,edicion from libros


where edicion like '__/05%';

Note que especificamos que los 2 primeros caracteres (2 guiones bajos) pueden ser cualquier caracter, luego una
barra seguida de "05" y que finalice con cualquier número de caracteres.

Recuperamos todos los libros cuyo precio se encuentra entre 10.00 y 19.99:

select titulo,precio from libros


where precio like '1_,%';

Note que especificamos que el segundo caracter (1 guión bajo) puede ser cualquier valor, luego una coma y que
finalice con cualquier número de caracteres.

Recuperamos los libros que NO incluyen centavos en sus precios:


select titulo,precio from libros
where precio not like '%,%';

29 - Contar registros (count)

Existen en Oracle funciones que nos permiten contar registros, calcular sumas, promedios, obtener valores máximos y
mínimos. Estas funciones se denominan funciones de grupo y operan sobre un conjunto de valores (registros), no con
datos individuales y devuelven un único valor.

Imaginemos que nuestra tabla "libros" contiene muchos registros. Para averiguar la cantidad sin necesidad de
contarlos manualmente usamos la función "count()":

select count(*)
from libros;

La función "count()" cuenta la cantidad de registros de una tabla, incluyendo los que tienen valor nulo.

También podemos utilizar esta función junto con la clausula "where" para una consulta más específica. Queremos
saber la cantidad de libros de la editorial "Planeta":

select count(*)
from libros
where editorial='Planeta';

Para contar los registros que tienen precio (sin tener en cuenta los que tienen valor nulo), usamos la función "count()"
y en los paréntesis colocamos el nombre del campo que necesitamos contar:

select count(precio)
from libros;

Note que "count(*)" retorna la cantidad de registros de una tabla (incluyendo los que tienen valor "null") mientras que
"count(precio)" retorna la cantidad de registros en los cuales el campo "precio" no es nulo. No es lo mismo. "count(*)"
cuenta registros, si en lugar de un asterisco colocamos como argumento el nombre de un campo, se contabilizan los
registros cuyo valor en ese campo NO es nulo.

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla:

drop table libros;

Creamos la tabla:

create table libros(


titulo varchar2(40) not null,
autor varchar2(20) default 'Desconocido',
editorial varchar(20),
precio number(6,2)
);

Ingresamos algunos registros:

insert into libros


values('El aleph','Borges','Emece',15.90);
insert into libros
values('Antología poética',null,'Planeta',null);
insert into libros
values('Alicia en el pais de las maravillas','Lewis Carroll',null,19.90);
insert into libros
values('Matematica estas ahi','Paenza','Siglo XXI',15);
insert into libros
values('Martin Fierro','Jose Hernandez',default,40);
insert into libros
values('Aprenda PHP',default,'Nuevo siglo',null);
insert into libros
values('Uno','Richard Bach','Planeta',20);

Averiguemos la cantidad de libros usando la función "count()":

select count(*)
from libros;

Note que incluye todos los libros aunque tengan valor nulo en algún campo.

Contamos los libros de editorial "Planeta":

select count(*)
from libros
where editorial='Planeta';

Existen 2 libros de editorial "Planeta".

Contamos los registros que tienen precio (sin tener en cuenta los que tienen valor nulo), usando la función
"count(precio)":

select count(precio)
from libros;

La consulta nos retorna el valor 5.

Contamos los registros que tienen valor diferente de "null" en "editorial":

select count(editorial)
from libros;

La consulta nos retorna el valor 5.

30 - Funciones de grupo (count - max - min - sum - avg)

Hemos visto que Oracle dispone de funciones que nos permiten contar registros, calcular sumas, promedios, obtener
valores máximos y mínimos, las funciones de grupo. Las funciones de grupo operan sobre un conjunto de valores
(registros) y retornan un solo valor.

Ya hemos aprendido una de ellas, "count()", veamos otras.

Se pueden usar en una instrucción "select" y combinarlas con la cláusula "group by" (la veremos posteriormente).

Todas estas funciones retornan "null" si ningún registro cumple con la condicion del "where" (excepto "count" que en
tal caso retorna cero).

El tipo de dato del campo determina las funciones que se pueden emplear con ellas.

Las relaciones entre las funciones de agrupamiento y los tipos de datos es la siguiente:

- count: se puede emplear con cualquier tipo de dato.

- min y max: con cualquier tipo de dato.


- sum y avg: sólo en campos de tipo numérico.

La función "sum()" retorna la suma de los valores que contiene el campo especificado. Si queremos saber la cantidad
total de libros que tenemos disponibles para la venta, debemos sumar todos los valores del campo "cantidad":

select sum(cantidad)
from libros;

Para averiguar el valor máximo o mínimo de un campo usamos las funciones "max()" y "min()" respectivamente.
Queremos saber cuál es el mayor precio de todos los libros:

select max(precio)
from libros;

Entonces, dentro del paréntesis de la función colocamos el nombre del campo del cuál queremos el máximo valor.

La función "avg()" retorna el valor promedio de los valores del campo especificado. Queremos saber el promedio del
precio de los libros referentes a "PHP":

select avg(precio)
from libros
where titulo like '%PHP%';

Ahora podemos entender porque estas funciones se denominan "funciones de grupo", porque operan sobre conjuntos
de registros, no con datos individuales.

Tratamiento de los valores nulos:

Si realiza una consulta con la función "count" incluyendo entre paréntesis un campo y la tabla contiene 18 registros, 2
de los cuales contienen valor nulo en "precio", el resultado devuelve un total de 16 filas porque no considera aquellos
con valor nulo.

Todas las funciones de grupo, excepto "count(*)", excluye los valores nulos de los campos;

"count(*)" cuenta todos los registros, incluidos los que contienen "null".

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla:

drop table libros;

Creamos la tabla:

create table libros(


codigo number(4) not null,
titulo varchar2(40) not null,
autor varchar2(30) default 'Desconocido',
editorial varchar2(15),
edicion date,
precio number(5,2),
cantidad number(3),
primary key(codigo)
);

Ingresamos algunos registros:

insert into libros


values(1562,'El aleph','Borges','Planeta','10/10/1980',15,null);
insert into libros
values(1878,'Martin Fierro','Jose Hernandez','Emece',null,22.20,200);
insert into libros
values(2587,'Antologia poetica','J.L. Borges','Planeta',null,null,150);
insert into libros
values(3654,'Aprenda PHP','Mario Molina',null,'05/05/1999',18.20,null);
insert into libros
values(3921,'Cervantes y el quijote',default,'Paidos','15/02/2000',null,null);
insert into libros
values(4582,'Manual de PHP', 'J.C. Paez', 'Siglo XXI','21/04/2005',31.80,120);
insert into libros
values(5963,'Harry Potter y la piedra filosofal','J.K.
Rowling',default,null,45.00,90);
insert into libros
values(6211,'Harry Potter y la camara secreta','J.K. Rowling','Emece',null,0,100);
insert into libros
values(8851,'Alicia en el pais de las maravillas','Lewis Carroll',null,null,null,220);
insert into libros
values(9920,'PHP de la A a la Z',default,default,default,null,0);

Para conocer la cantidad total de libros, sumamos las cantidades de cada uno:

select sum(cantidad)
from libros;

Retorna 880; verifique la suma, sumando los valores de todos los registros del campo "cantidad".

Queremos saber cuántos libros tenemos de la editorial "Emece":

select sum(cantidad)
from libros
where editorial='Emece';

retorna 300.

Queremos saber el precio del libro más costoso; usamos la función "max()":

select max(precio)
from libros;

retorna 45.

Para conocer el precio mínimo de los libros de "Rowling" tipeamos:

select min(precio)
from libros
where autor like '%Rowling%';

retorna 0 (cero).

Queremos saber el promedio del precio de los libros referentes a "PHP":

select avg(precio)
from libros
where titulo like '%PHP%';

Devuelve 25. Note que hay 3 libros sobre "PHP", pero uno de ellos tiene precio nulo entonces Oracle no lo incluye
para calcular el promedio.

Averiguamos el mínimo valor del campo "edicion":

select min(edicion)
from libros;

Retorna 10/10/80.

Necesitamos conocer el mayor valor de "codigo":

select max(codigo)
from libros;
Retorna 9920.

31 - Agrupar registros (group by)

Hemos aprendido que las funciones de grupo permiten realizar varios cálculos operando con conjuntos de registros.

Las funciones de grupo solas producen un valor de resumen para todos los registros de un campo.

Podemos generar valores de resumen para un solo campo, combinando las funciones de agregado con la cláusula
"group by", que agrupa registros para consultas detalladas.

Queremos saber la cantidad de libros de cada editorial, podemos tipear la siguiente sentencia:

select count(*) from libros


where editorial='Planeta';

y repetirla con cada valor de "editorial":

select count(*) from libros


where editorial='Emece';
select count(*) from libros
where editorial='Paidos';
...

Pero hay otra manera, utilizando la cláusula "group by":

select editorial, count(*)


from libros
group by editorial;

La instrucción anterior solicita que muestre el nombre de la editorial y cuente la cantidad agrupando los registros por
el campo "editorial". Como resultado aparecen los nombres de las editoriales y la cantidad de registros para cada
valor del campo.

Los valores nulos se procesan como otro grupo.

Entonces, para saber la cantidad de libros que tenemos de cada editorial, utilizamos la función "count()", agregamos
"group by" (que agrupa registros) y el campo por el que deseamos que se realice el agrupamiento, también colocamos
el nombre del campo a recuperar; la sintaxis básica es la siguiente:

select CAMPO, FUNCIONDEAGREGADO


from NOMBRETABLA
group by CAMPO;

También se puede agrupar por más de un campo, en tal caso, luego del "group by" se listan los campos, separados por
comas. Todos los campos que se especifican en la cláusula "group by" deben estar en la lista de selección.

select CAMPO1, CAMPO2, FUNCIONDEAGREGADO


from NOMBRETABLA
group by CAMPO1,CAMPO2;

Para obtener la cantidad libros con precio no nulo, de cada editorial utilizamos la función "count()" enviándole como
argumento el campo "precio", agregamos "group by" y el campo por el que deseamos que se realice el agrupamiento
(editorial):

select editorial, count(precio)


from libros
group by editorial;

Como resultado aparecen los nombres de las editoriales y la cantidad de registros de cada una, sin contar los que
tienen precio nulo.
Recuerde la diferencia de los valores que retorna la función "count()" cuando enviamos como argumento un asterisco
o el nombre de un campo: en el primer caso cuenta todos los registros incluyendo los que tienen valor nulo, en el
segundo, los registros en los cuales el campo especificado es no nulo.

Para conocer el total de libros agrupados por editorial:

select editorial, sum(cantidad)


from libros
group by editorial;

Para saber el máximo y mínimo valor de los libros agrupados por editorial:

select editorial,
max(precio) as mayor,
min(precio) as menor
from libros
group by editorial;

Para calcular el promedio del valor de los libros agrupados por editorial:

select editorial, avg(precio)


from libros
group by editorial;

Es posible limitar la consulta con "where".

Si incluye una cláusula "where", sólo se agrupan los registros que cumplen las condiciones.

Vamos a contar y agrupar por editorial considerando solamente los libros cuyo precio sea menor a 30 pesos:

select editorial, count(*)


from libros
where precio<30
group by editorial;

Note que las editoriales que no tienen libros que cumplan la condición, no aparecen en la salida.

Entonces, usamos "group by" para organizar registros en grupos y obtener un resumen de dichos grupos. Oracle
produce una columna de valores por cada grupo, devolviendo filas por cada grupo especificado.

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla:

drop table libros;

Creamos la tabla:

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30) default 'Desconocido',
editorial varchar2(15),
precio number(5,2),
cantidad number(3),
primary key(codigo)
);

Ingresamos algunos registros:

insert into libros


values(100,'El aleph','Borges','Planeta',15,null);
insert into libros
values(234,'Martin Fierro','Jose Hernandez','Emece',22.20,200);
insert into libros
values(354,'Antologia poetica',default,'Planeta',null,150);
insert into libros
values(478,'Aprenda PHP','Mario Molina','Emece',18.20,null);
insert into libros
values(512,'Cervantes y el quijote','Bioy Casares- J.L. Borges','Paidos',28,100);
insert into libros
values(643,'Manual de PHP', default, 'Siglo XXI',31.80,120);
insert into libros
values(646,'Harry Potter y la piedra filosofal','J.K. Rowling',default,45.00,90);
insert into libros
values(753,'Harry Potter y la camara secreta','J.K. Rowling','Emece',null,100);
insert into libros
values(889,'Alicia en el pais de las maravillas','Lewis Carroll','Paidos',22.50,200);
insert into libros
values(893,'PHP de la A a la Z',null,null,55.90,0);

Queremos saber la cantidad de libros de cada editorial, utilizando la cláusula "group by":

select editorial, count(*)


from libros
group by editorial;

Aparece el siguiente resultado:

EDITORIAL COUNT(*)
--------------------------
Paidos 2
2
Planeta 2
Emece 3
Siglo XXI 1

El resultado muestra los nombres de las editoriales y la cantidad de registros para cada valor del campo. Note que los
valores nulos se procesan como otro grupo.

Obtenemos la cantidad libros con precio no nulo de cada editorial:

select editorial, count(precio)


from libros
group by editorial;

La salida es la siguiente:

EDITORIAL COUNT(PRECIO)
------------------------------
Paidos 2
2
Planeta 1
Emece 2
Siglo XXI 1

Aparecen los nombres de las editoriales y la cantidad de registros de cada una, sin contar los que tienen precio nulo.

Para conocer el total de libros agrupados por editorial, tipeamos:

select editorial, sum(cantidad)


from libros
group by editorial;

Obtenemos el máximo y mínimo valor de los libros agrupados por editorial, en una sola sentencia:

select editorial,
max(precio) as mayor,
min(precio) as menor
from libros
group by editorial;
Calculamos el promedio del valor de los libros agrupados por editorial:

select editorial, avg(precio)


from libros
group by editorial;

Es posible limitar la consulta con "where". Vamos a contar y agrupar por editorial considerando solamente los libros
cuyo precio es menor a 30 pesos:

select editorial, count(*)


from libros
where precio<30
group by editorial;

Note que las editoriales que no tienen libros que cumplan la condición (sus precios superan los 30 pesos), no
aparecen en la salida.

32 - Seleccionar grupos (Having)

Así como la cláusula "where" permite seleccionar (o rechazar) registros individuales; la cláusula "having" permite
seleccionar (o rechazar) un grupo de registros.

Si queremos saber la cantidad de libros agrupados por editorial usamos la siguiente instrucción ya aprendida:

select editorial, count(*)


from libros
group by editorial;

Si queremos saber la cantidad de libros agrupados por editorial pero considerando sólo algunos grupos, por ejemplo,
los que devuelvan un valor mayor a 2, usamos la siguiente instrucción:

select editorial, count(*) from libros


group by editorial
having count(*)>2;

Se utiliza "having", seguido de la condición de búsqueda, para seleccionar ciertas filas retornadas por la cláusula
"group by".

Veamos otros ejemplos. Queremos el promedio de los precios agrupados por editorial, pero solamente de aquellos
grupos cuyo promedio supere los 25 pesos:

select editorial, avg(precio) from libros


group by editorial
having avg(precio)>25;

En algunos casos es posible confundir las cláusulas "where" y "having". Queremos contar los registros agrupados por
editorial sin tener en cuenta a la editorial "Planeta".

Analicemos las siguientes sentencias:

select editorial, count(*) from libros


where editorial<>'Planeta'
group by editorial;
select editorial, count(*) from libros
group by editorial
having editorial<>'Planeta';

Ambas devuelven el mismo resultado, pero son diferentes. La primera, selecciona todos los registros rechazando los
de editorial "Planeta" y luego los agrupa para contarlos. La segunda, selecciona todos los registros, los agrupa para
contarlos y finalmente rechaza fila con la cuenta correspondiente a la editorial "Planeta".
No debemos confundir la cláusula "where" con la cláusula "having"; la primera establece condiciones para la selección
de registros de un "select"; la segunda establece condiciones para la selección de registros de una salida "group by".

Veamos otros ejemplos combinando "where" y "having". Queremos la cantidad de libros, sin considerar los que tienen
precio nulo, agrupados por editorial, sin considerar la editorial "Planeta":

select editorial, count(*) from libros


where precio is not null
group by editorial
having editorial<>'Planeta';

Aquí, selecciona los registros rechazando los que no cumplan con la condición dada en "where", luego los agrupa por
"editorial" y finalmente rechaza los grupos que no cumplan con la condición dada en el "having".

Se emplea la cláusula "having" con funciones de grupo, esto no puede hacerlo la cláusula "where". Por ejemplo
queremos el promedio de los precios agrupados por editorial, de aquellas editoriales que tienen más de 2 libros:

select editorial, avg(precio) from libros


group by editorial
having count(*) > 2;

En una cláusula "having" puede haber varias condiciones. Cuando utilice varias condiciones, tiene que combinarlas
con operadores lógicos (and, or, not).

Podemos encontrar el mayor valor de los libros agrupados y ordenados por editorial y seleccionar las filas que tengan
un valor menor a 100 y mayor a 30:

select editorial, max(precio) as mayor


from libros
group by editorial
having min(precio)<100 and
min(precio)>30
order by editorial;

Entonces, usamos la clausula "having" para restringir las filas que devuelve una salida "group by". Va siempre después
de la cláusula "group by" y antes de la cláusula "order by" si la hubiere.

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla:

drop table libros;

Creamos la tabla:

create table libros(


titulo varchar2(40),
autor varchar2(30),
editorial varchar2(15),
precio number(5,2),
cantidad number(3)
);

Ingresamos algunos registros:

insert into libros


values('El aleph','Borges','Planeta',35,null);
insert into libros
values('Martin Fierro','Jose Hernandez','Emece',22.20,200);
insert into libros
values('Martin Fierro','Jose Hernandez','Planeta',40,200);
insert into libros
values('Antologia poetica','J.L. Borges','Planeta',null,150);
insert into libros
values('Aprenda PHP','Mario Molina','Emece',18,null);
insert into libros
values('Manual de PHP', 'J.C. Paez', 'Siglo XXI',56,120);
insert into libros
values('Cervantes y el quijote','Bioy Casares- J.L. Borges','Paidos',null,100);
insert into libros
values('Harry Potter y la piedra filosofal','J.K. Rowling',default,45.00,90);
insert into libros
values('Harry Potter y la camara secreta','J.K. Rowling','Emece',null,100);
insert into libros
values('Alicia en el pais de las maravillas','Lewis Carroll','Paidos',42,80);
insert into libros
values('PHP de la A a la Z',null,null,110,0);
insert into libros
values('Uno','Richard Bach','Planeta',25,null);

Queremos saber la cantidad de libros agrupados por editorial:

select editorial, count(*) from libros


group by editorial;

Nos devuelve:

EDITORIAL COUNT(*)
------------------------
Paidos 2
2
Planeta 4
Emece 3
Siglo XXI 1

Queremos saber la cantidad de libros agrupados por editorial pero considerando sólo algunos grupos, por ejemplo, los
que devuelvan un valor mayor a 2, usamos la siguiente instrucción:

select editorial, count(*) from libros


group by editorial
having count(*)>2;

La salida es la siguiente:

EDITORIAL COUNT(*)
-------------------------
Planeta 4
Emece 3

Compare esta salida con la de la sentencia anterior.

Queremos el promedio de los precios de los libros agrupados por editorial, pero solamente de aquellos grupos cuyo
promedio supere los 25 pesos:

select editorial, avg(precio) from libros


group by editorial
having avg(precio)>25;

Queremos la cantidad de libros, sin considerar los que tienen precio nulo (where), agrupados por editorial (group by),
sin considerar la editorial "Planeta" (having):

select editorial, count(*) from libros


where precio is not null
group by editorial
having editorial<>'Planeta';

Necesitamos el promedio de los precios agrupados por editorial, de aquellas editoriales que tienen más de 2 libros:

select editorial, avg(precio) from libros


group by editorial
having count(*) > 2;
Buscamos el mayor valor de los libros agrupados y ordenados por editorial y seleccionamos las filas que tienen un
valor menor a 100 y mayor a 30:

select editorial, max(precio) as mayor


from libros
group by editorial
having max(precio)<100 and
max(precio)>30
order by editorial;

33 - Registros duplicados (Distinct)

Con la cláusula "distinct" se especifica que los registros con ciertos datos duplicados sean obviadas en el resultado.
Por ejemplo, queremos conocer todos los autores de los cuales tenemos libros, si utilizamos esta sentencia:

select autor from libros;

Aparecen repetidos. Para obtener la lista de autores sin repetición usamos:

select distinct autor from libros;

También podemos tipear:

select autor from libros


group by autor;

Note que en los tres casos anteriores aparece "null" como un valor para "autor"· Si sólo queremos la lista de autores
conocidos, es decir, no queremos incluir "null" en la lista, podemos utilizar la sentencia siguiente:

select distinct autor from libros


where autor is not null;

Para contar los distintos autores, sin considerar el valor "null" usamos:

select count(distinct autor)


from libros;

Note que si contamos los autores sin "distinct", no incluirá los valores "null" pero si los repetidos:

select count(autor)
from libros;

Esta sentencia cuenta los registros que tienen autor.

Podemos combinarla con "where". Por ejemplo, queremos conocer los distintos autores de la editorial "Planeta":

select distinct autor from libros


where editorial='Planeta';

También puede utilizarse con "group by" para contar los diferentes autores por editorial:

select editorial, count(distinct autor)


from libros
group by editorial;

La cláusula "distinct" afecta a todos los campos presentados. Para mostrar los títulos y editoriales de los libros sin
repetir títulos ni editoriales, usamos:

select distinct titulo,editorial


from libros
order by titulo;
Note que los registros no están duplicados, aparecen títulos iguales pero con editorial diferente, cada registro es
diferente.

Entonces, "distinct" elimina registros duplicados.

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla:

drop table libros;

Creamos la tabla:

create table libros(


titulo varchar2(40),
autor varchar2(30),
editorial varchar2(15)
);

Ingresamos algunos registros:

insert into libros


values('El aleph','Borges','Planeta');
insert into libros
values('Martin Fierro','Jose Hernandez','Emece');
insert into libros
values('Martin Fierro','Jose Hernandez','Planeta');
insert into libros
values('Antologia poetica','Borges','Planeta');
insert into libros
values('Aprenda PHP','Mario Molina','Emece');
insert into libros
values('Aprenda PHP','Lopez','Emece');
insert into libros
values('Manual de PHP', 'J. Paez', null);
insert into libros
values('Cervantes y el quijote',null,'Paidos');
insert into libros
values('Harry Potter y la piedra filosofal','J.K. Rowling','Emece');
insert into libros
values('Harry Potter y la camara secreta','J.K. Rowling','Emece');
insert into libros
values('Alicia en el pais de las maravillas','Lewis Carroll','Paidos');
insert into libros
values('Alicia en el pais de las maravillas','Lewis Carroll','Planeta');
insert into libros
values('PHP de la A a la Z',null,null);
insert into libros
values('Uno','Richard Bach','Planeta');

Para obtener la lista de autores sin repetición tipeamos:

select distinct autor from libros;

Note que aparece "null" como un valor para "autor"· Para obtener la lista de autores conocidos, es decir, no
incluyendo "null" en la lista:

select distinct autor from libros


where autor is not null;

Contamos los distintos autores:

select count(distinct autor)


from libros;

Queremos los nombres de las editoriales sin repetir:


select distinct editorial from libros;

Queremos saber la cantidad de editoriales distintas:

select count(distinct editorial) from libros;

La combinamos con "where" para obtener los distintos autores de la editorial "Planeta":

select distinct autor from libros


where editorial='Planeta';

Contamos los distintos autores que tiene cada editorial empleando "group by":

select editorial,count(distinct autor)


from libros
group by editorial;

Mostramos los títulos y editoriales de los libros sin repetir títulos ni editoriales:

select distinct titulo,editorial


from libros
order by titulo;

Note que los registros no están duplicados, aparecen títulos iguales pero con editorial diferente, cada registro es
diferente.

34 - Clave primaria compuesta

Las claves primarias pueden ser simples, formadas por un solo campo o compuestas, más de un campo.

Recordemos que una clave primaria identifica un solo registro en una tabla.

Para un valor del campo clave existe solamente un registro. Los valores no se repiten ni pueden ser nulos.

Existe una playa de estacionamiento que almacena cada día los datos de los vehículos que ingresan en la tabla
llamada "vehiculos" con los siguientes campos:

- patente char(6) not null,


- tipo char (1), 'a'= auto, 'm'=moto,
- horallegada date,
- horasalida date,

Necesitamos definir una clave primaria para una tabla con los datos descriptos arriba. No podemos usar solamente la
patente porque un mismo auto puede ingresar más de una vez en el día a la playa; tampoco podemos usar la hora de
entrada porque varios autos pueden ingresar a una misma hora. Tampoco sirven los otros campos.
Como ningún campo, por si sólo cumple con la condición para ser clave, es decir, debe identificar un solo registro, el
valor no puede repetirse, debemos usar dos campos.

Definimos una clave compuesta cuando ningún campo por si solo cumple con la condición para ser clave.

En este ejemplo, un auto puede ingresar varias veces en un día a la playa, pero siempre será a distinta hora.

Usamos 2 campos como clave, la patente junto con la hora de llegada, así identificamos unívocamente cada registro.

Para establecer más de un campo como clave primaria usamos la siguiente sintaxis:

create table vehiculos(


patente char(6) not null,
tipo char(1),--'a'=auto, 'm'=moto
horallegada date,
horasalida date,
primary key(patente,horallegada)
);

Nombramos los campos que formarán parte de la clave separados por comas.

Al ingresar los registros, Oracle controla que los valores para los campos establecidos como clave primaria no estén
repetidos en la tabla; si estuviesen repetidos, muestra un mensaje y la inserción no se realiza. Lo mismo sucede si
realizamos una actualización.

Para ver la clave primaria de una tabla podemos realizar la siguiente consulta:

select uc.table_name, column_name, position from user_cons_columns ucc


join user_constraints uc
on ucc.constraint_name=uc.constraint_name
where uc.constraint_type='P' and
uc.table_name='VEHICULOS';

Entonces, si un solo campo no identifica unívocamente un registro podemos definir una clave primaria compuesta, es
decir formada por más de un campo.

Problema:

Una playa de estacionamiento almacena cada día los datos de los vehículos que ingresan en la tabla llamada
"vehiculos".

Seteamos el formato de "date" para que nos muestre únicamente la hora y los minutos, ya que en esta playa, se
almacenan los datos de los vehículos diariamente:

ALTER SESSION SET NLS_DATE_FORMAT = 'HH24:MI';

Eliminamos la tabla:

drop table vehiculos;

Creamos la tabla estableciendo dos campos como clave primaria:

create table vehiculos(


patente char(6) not null,
tipo char(1),--'a'=auto, 'm'=moto
horallegada date,
horasalida date,
primary key(patente,horallegada)
);

Ingresamos algunos registros:

insert into vehiculos values('AIC124','a','8:05','12:30');


insert into vehiculos values('CAA258','a','8:05',null);
insert into vehiculos values('DSE367','m','8:30','18:00');
insert into vehiculos values('FGT458','a','9:00',null);
insert into vehiculos values('AIC124','a','16:00',null);
insert into vehiculos values('LOI587','m','18:05','19:55');

Si intentamos ingresar un registro con clave primaria repetida:

insert into vehiculos values('LOI587','m','18:05',null);

aparece un mensaje de error y la inserción no se realiza.

Si ingresamos un registro repitiendo el valor de uno de los campos que forman parte de la clave, si lo acepta:

insert into vehiculos values('LOI587','m','21:30',null);


Recuperamos todos los registros:

select *from vehiculos;

Note que cada registro es único, dos de ellos tienen la misma patente, pero diferente hora de llegada.

Si intentamos actualizar un registro repitiendo la clave primaria:

update vehiculos set horallegada='8:05'


where patente='AIC124' and horallegada='16:00';

aparece un mensaje de error y la actualización no se realiza.

Recordemos que los campos que forman parte de la clave primaria no aceptan valores nulos, aunque no se haya
aclarado en la definición de la tabla:

insert into vehiculos values('HUO690','m',null,null);

Si mostramos la estructura de la tabla:

describe vehiculos;

vemos que los campos que forman parte de la clave primaria (patente y horallegada) tienen "NOT NULL" en la
columna "Null", es decir, no admiten valores nulos.

Para ver la clave primaria de una tabla podemos realizar la siguiente consulta:

select uc.table_name, column_name, position from user_cons_columns ucc


join user_constraints uc
on ucc.constraint_name=uc.constraint_name
where uc.constraint_type='P' and
uc.table_name='VEHICULOS';

Nos retorna la siguiente información:

TABLE_NAME COLUMN_NAME POSITION


----------------------------------------
VEHICULOS PATENTE 1
VEHICULOS HORALLEGADA 2

Los dos campos son clave primaria, "POSITION" indica el orden en que fueron definidos los campos.

35 - Secuencias (create sequence - currval - nextval - drop


sequence)

Hemos aprendido que existen varios objetos de base de datos, hasta ahora hemos visto TABLAS y algunas FUNCIONES
predefinidas. Otro objeto de base de datos es la secuencia.

Una secuencia (sequence) se emplea para generar valores enteros secuenciales únicos y asignárselos a campos
numéricos; se utilizan generalmente para las claves primarias de las tablas garantizando que sus valores no se
repitan.

Una secuencia es una tabla con un campo numérico en el cual se almacena un valor y cada vez que se consulta, se
incrementa tal valor para la próxima consulta.

Sintaxis general:

create sequence NOMBRESECUENCIA


start with VALORENTERO
increment by VALORENTERO
maxvalue VALORENTERO
minvalue VALORENTERO
cycle | nocycle;

- La cláusula "start with" indica el valor desde el cual comenzará la generación de números secuenciales. Si no se
especifica, se inicia con el valor que indique "minvalue".

- La cláusula "increment by" especifica el incremento, es decir, la diferencia entre los números de la secuencia; debe
ser un valor numérico entero positivo o negativo diferente de 0. Si no se indica, por defecto es 1.

- "maxvalue" define el valor máximo para la secuencia. Si se omite, por defecto es 99999999999999999999999999.

- "minvalue" establece el valor mínimo de la secuencia. Si se omite será 1.

- La cláusula "cycle" indica que, cuando la secuencia llegue a máximo valor (valor de "maxvalue") se reinicie,
comenzando con el mínimo valor ("minvalue") nuevamente, es decir, la secuencia vuelve a utilizar los números. Si se
omite, por defecto la secuencia se crea "nocycle".

Si no se especifica ninguna cláusula, excepto el nombre de la secuencia, por defecto, comenzará en 1, se


incrementará en 1, el mínimo valor será 1, el máximo será 999999999999999999999999999 y "nocycle".

En el siguiente ejemplo creamos una secuencia llamada "sec_codigolibros", estableciendo que comience en 1, sus
valores estén entre 1 y 99999 y se incrementen en 1, por defecto, será "nocycle":

create sequence sec_codigolibros


start with 1
increment by 1
maxvalue 99999
minvalue 1;

Si bien, las secuencias son independientes de las tablas, se utilizarán generalmente para una tabla específica, por lo
tanto, es conveniente darle un nombre que referencie a la misma.

Otro ejemplo:

create sequence sec_numerosocios


increment by 5
cycle;

La secuencia anterior, "sec_numerosocios", incrementa sus valores en 5 y al llegar al máximo valor recomenzará la
secuencia desde el valor mínimo; no se especifican las otras cláusulas, por lo tanto, por defecto, el valor mínimo es
1, el máximo 999999999999999999999999999 y el valor inicial es 1.

Dijimos que las secuencias son tablas; por lo tanto se accede a ellas mediante consultas, empleando "select". La
diferencia es que utilizamos pseudocolumnas para recuperar el valor actual y el siguiente de la secuencia. Estas
pseudocolumnas pueden incluirse en el "from" de una consulta a otra tabla o de la tabla "dual".

Para recuperar los valores de una secuencia empleamos las pseudocolumnas "currval" y "nextval".

Primero debe inicializarse la secuencia con "nextval". La primera vez que se referencia "nextval" retorna el valor de
inicio de la secuencia; las siguientes veces, incrementa la secuencia y nos retorna el nuevo valor:

NOMBRESECUENCIA.NEXTVAL;

se coloca el nombre de la secuencia seguido de un punto y la pseudocolumna "nextval" (que es una forma abreviada
de "next value", siguiente valor).

Para recuperar el valor actual de una secuencia usamos:

NOMBRESECUENCIA.CURRVAL;

es decir, el nombre de la secuencia, un punto y la pseudocolumna "currval" (que es una forma abreviada de "current
value", valor actual).
Los valores retornados por "currval" y "nextval" pueden usarse en sentencias "insert" y "update".

Veamos un ejemplo completo:

Creamos una secuencia para el código de la tabla "libros", especificando el valor máximo, el incremento y que no sea
circular:

create sequence sec_codigolibros


maxvalue 999999
increment by 1
nocycle;

Luego inicializamos la secuencia

select sec_codigolibros.nextval from dual;

Recuerde que la primera vez que se referencie la secuencia debe emplearse "nextval" para inicializarla.

Ingresamos un registro en "libros", almacenando en el campo "codigo" el valor actual de la secuencia:

insert into libros values


(sec_codigolibros.currval,'El aleph', 'Borges','Emece');

Ingresamos otro registro en "libros", almacenando en el campo "codigo" el valor siguiente de la secuencia:

insert into libros values


(sec_codigolibros.nextval,'Matematica estas ahi', 'Paenza','Nuevo siglo');

Para ver todas las secuencias de la base de datos actual realizamos la siguiente consulta:

select *from all_sequences;

Nos muestra el propietario de la secuencia, el nombre de la misma, los valores mínimo y máximo, el valor de
incremento y si es circular o no, entre otros datos que no analizaremos por el momento.

También podemos ver todos los objetos de la base de datos actual tipeando;

select *from all_objects;

En la tabla resultado aparecen todos los objetos de la base de datos, incluidas las secuencias; si es una secuencia en
la columna OBJECT_TYPE se muestra "SEQUENCE".

Podemos consultar "all_objects" especificando que nos muestre el nombre de todas las secuencias:

select object_name from all_objects


where object_type='SEQUENCE';

Para eliminar una secuencia empleamos "drop sequence". Sintaxis:

drop sequence NOMBRESECUENCIA;

Si la secuencia no existe aparecerá un mensaje indicando tal situación.

En el siguiente ejemplo se elimina la secuencia "sec_codigolibros":

drop sequence sec_codigolibros;

Problema:

Veamos las secuencias existentes:


select *from all_sequences;

Aparece una tabla que nos muestra todas las secuencias; la columna "SEQUENCE_NAME" contiene el nombre de cada
secuencia; las demás columnas nos informan sobre cada una de las secuencias de la base de datos actual
(propietario, valores mínimo y máximo, valor de incremento, si es circular o no, etc.).

Vamos a crear una secuencia denominada "sec_codigolibros" para utilizarla en la clave primaria de la tabla "libros".

En primer lugar vamos a eliminar la secuencia "sec_codigolibros" porque si ya existe no podremos crear otra con el
mismo nombre:

drop sequence sec_codigolibros;

Si la secuencia no existe aparecerá un mensaje indicando tal situación.

Creamos la secuencia llamada "sec_codigolibros", estableciendo que comience en 1, sus valores estén entre 1 y 99999
y se incrementen en 1, por defecto, será "nocycle":

create sequence sec_codigolibros


start with 1
increment by 1
maxvalue 99999
minvalue 1;

Para acceder a las secuencias (que son tablas) empleamos "select" y la tabla "dual".

En primer lugar, debemos inicializar la secuencia:

select sec_codigolibros.nextval from dual;

Nos retorna el valor 1.

Recuperamos el valor actual de nuestra secuencia:

select sec_codigolibros.currval from dual;

Retorna 1.

Eliminamos la tabla "libros" y la creamos con la siguiente estructura:

drop table libros;


create table libros(
codigo number(5) not null,
titulo varchar2(40) not null,
autor varchar2(30),
editorial varchar2(20),
primary key(codigo)
);

Note que al crear la tabla no se hace referencia en ningún momento a la secuencia que luego servirá para dar valores
secuenciales a su clave primaria.

Ingresamos un registro en "libros", almacenando en el campo "codigo" el valor actual de la secuencia:

insert into libros values


(sec_codigolibros.currval,'El aleph', 'Borges','Emece');

Ingresamos otro registro en "libros", almacenando en el campo "codigo" el valor siguiente de la secuencia:

insert into libros values


(sec_codigolibros.nextval,'Matematica estas ahi', 'Paenza','Nuevo siglo');

Recuerde que "nextval" incrementa la secuencia y retorna el nuevo valor.


Recuperamos todos los registros para ver qué se ha almacenado en "codigo":

select *from libros;

Veamos todos los objetos de la base de datos actual que contengan en su nombre la cadena "LIBROS":

select object_name,object_type
from all_objects
where object_name like '%LIBROS%';

En la tabla resultado aparecen la tabla "libros" y la secuencia "sec_codigolibros".

Eliminamos la secuencia creada:

drop sequence sec_codigolibros;

Un mensaje indica que la secuencia ha sido eliminada.

Si consultamos todos los objetos de la base de datos veremos que tal secuencia ya no existe:

select object_name,object_type
from all_objects
where object_name like '%LIBROS%';

36 - Alterar secuencia (alter sequence)

Es posible modificar una secuencia, su valor de incremento, los valores mínimo y máximo y el atributo "cycle" (el
valor de inicio no puede modificarse); para ello empleamos la sentencia "alter sequence". Sintaxis:

alter sequence NOMBRESECUENCIA ATRIBUTOSAMODIFICAR;

Definimos una secuencia denominada "sec_codigolibros" con los siguientes atributos:

create sequence sec_codigolibros


start with 1
increment by 1
maxvalue 999
minvalue 1
nocycle;

Para modificar el máximo valor a 99999 y el incremento a 2, tipeamos:

alter sequence sec_codigolibros


increment by 2
maxvalue 99999;

Los valores de incremento y mínimo han sido modificados, los demás atributos no especificados en la sentencia "alter
sequence" se mantienen.

Problema:

Eliminamos la secuencia "sec_codigolibros":

drop sequence sec_codigolibros;

La creamos definiendo 1 como valor de inicio, 1 de incremento, 999 como valor máximo, 1 como mínimo valor y no
circular:
create sequence sec_codigolibros
start with 1
increment by 1
maxvalue 999
minvalue 1
nocycle;

Vemos la información sobre la secuencia creada anteriormente:

select *from all_sequences where sequence_name='SEC_CODIGOLIBROS';

Alteramos la secuencia para que el máximo valor sea 99999 y se incremente de a 2:

alter sequence sec_codigolibros


increment by 2
maxvalue 99999;

Veamos la información de la secuencia modificada consultando "all_sequences":

select *from all_sequences where sequence_name='SEC_CODIGOLIBROS';

Ahora el valor de incremento es 2 y el máximo 99999, los demás valores permanecen como fueron definidos.

37 - Integridad de datos

Es importante, al diseñar una base de datos y las tablas que contiene, tener en cuenta la integridad de los datos,
esto significa que la información almacenada en las tablas debe ser válida, coherente y exacta.

Hasta el momento, hemos controlado y restringido la entrada de valores a un campo mediante el tipo de dato que le
definimos (cadena, numéricos, etc.), la aceptación o no de valores nulos, el valor por defecto. También hemos
asegurado que cada registro de una tabla sea único definiendo una clave primaria y empleando secuencias.

Oracle ofrece más alternativas, además de las aprendidas, para restringir y validar los datos, las veremos
ordenadamente y al finalizar haremos un resumen de las mismas.

Comenzamos por las restricciones.

Las restricciones (constraints) son un método para mantener la integridad de los datos, asegurando que los valores
ingresados sean válidos y que las relaciones entre las tablas se mantenga.

Las restricciones pueden establecerse a nivel de campo o de tabla.

Pueden definirse al crear la tabla ("create table") o agregarse a una tabla existente (empleando "alter table") y se
pueden aplicar a un campo o a varios. También es posible habilitarlas y deshabilitarlas.

Oracle ofrece varios tipos de restricciones:

- not null: a nivel de campo.

- primary key: a nivel de tabla. Es un campo o varios que identifican cada registro de una tabla.

- foreign key: a nivel de tabla. Establece que un campo (o varios) relacione una clave primaria de una tabla con otra.

- check: a nivel de tabla. Restringe los valores que pueden ingresarse en un campo especifico.

- unique: a nivel de tabla.

Se pueden crear, modificar y eliminar las restricciones sin eliminar la tabla y volver a crearla.
Para obtener información de las restricciones podemos consultar los catálogos "all_objects", "all_constraints" y
"all_cons_columns".

El catálogo "all_constraints" retorna varias columnas, entre ellas: OWNER (propietario), CONSTRAINT_NAME (nombre
de la restricción), CONSTRAINT_TYPE (tipo de restricción, si es primary key (P), foreign key (), unique (U), etc.),
TABLE_NAME (nombre de la tabla), SEARCH_CONDITION (en caso de ser Check u otra), DELETE_RULE (), STATUS
(estado), DEFERRABLE (), DEFERRED (), VALIDATED (), GENERATED (), INDEX_OWNER (), INDEX_NAME ().

El catálogo "all_cons_columnas" retorna las siguientes columnas: OWNER (propietario), CONSTRAINT_NAME (nombre),
TABLE_NAME (nombre de la tabla), COLUMN_NAME (campo), POSITION (posición).

38 - Restricción primary key

La restricción "primary key" asegura que los valores sean únicos para cada registro.

Anteriormente, para establecer una clave primaria para una tabla empleábamos la siguiente sintaxis al crear la tabla,
por ejemplo:

create table libros(


codigo int not null,
titulo varchar(30),
autor varchar(30),
editorial varchar(20),
primary key(codigo)
);

Cada vez que establecíamos la clave primaria para la tabla, Oracle creaba automáticamente una restricción "primary
key" para dicha tabla. Dicha restricción, a la cual no le dábamos un nombre, recibía un nombre dado por Oracle que
consta de una serie de letras y números aleatorios.

Podemos agregar una restricción "primary key" a una tabla existente con la sintaxis básica siguiente:

alter table NOMBRETABLA


add constraint NOMBRECONSTRAINT
primary key (CAMPO,...);

En el siguiente ejemplo definimos una restricción "primary key" para nuestra tabla "libros" para asegurarnos que cada
libro tendrá un código diferente y único:

alter table libros


add constraint PK_libros_codigo
primary key(codigo);

Con esta restricción, si intentamos ingresar un registro con un valor para el campo "codigo" que ya existe o el valor
"null", aparece un mensaje de error, porque no se permiten valores duplicados ni nulos. Igualmente, si actualizamos.

Por convención, cuando demos el nombre a las restricciones "primary key" seguiremos el formato
"PK_NOMBRETABLA_NOMBRECAMPO".

Cuando agregamos una restricción a una tabla que contiene información, Oracle controla los datos existentes para
confirmar que cumplen las exigencias de la restricción, si no los cumple, la restricción no se aplica y aparece un
mensaje de error. Por ejemplo, si intentamos definir la restricción "primary key" para "libros" y hay registros con
códigos repetidos o con un valor "null", la restricción no se establece.

Cuando establecíamos una clave primaria al definir la tabla, automáticamente Oracle redefinía el campo como "not
null"; lo mismo sucede al agregar una restricción "primary key", los campos que se establecen como clave primaria se
redefinen automáticamente "not null".

Se permite definir solamente una restricción "primary key" por tabla, que asegura la unicidad de cada registro de una
tabla.
Si consultamos el catálogo "user_constraints", podemos ver las restricciones "primary key" (y todos los tipos de
restricciones) de todas las tablas del usuario actual. El resultado es una tabla que nos informa el propietario de la
restricción (OWNER), el nombre de la restricción (CONSTRAINT_NAME), el tipo (CONSTRAINT_TYPE, si es "primary key"
muestra una "P"), el nombre de la tabla en la cual se aplica (TABLE_NAME), y otra información que no analizaremos
por el momento.

También podemos consultar el catálogo "user_cons_columns"; nos mostrará el propietario de la restricción (OWNER),
el nombre de la restricción (CONSTRAINT_NAME), la tabla a la cual se aplica (TABLE_NAME), el campo
(COLUMN_NAME) y la posición (POSITION).

Problema:

Trabajamos con la tabla "libros" de una librería.


Eliminamos la tabla:

drop table libros;

La creamos estableciendo el campo código como clave primaria:

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(15),
primary key (codigo)
);

Veamos la restricción "primary key" que creó automáticamente Oracle:

select *from user_constraints where table_name='LIBROS';

Aparece la siguiente información simplificada:

OWNER CONSTRAINT_NAME CONSTRAINT_TYPE TABLE_NAME


------------------------------------------------------------------
SYSTEM SYS_C004427 P LIBROS

Nos informa que la tabla "libros" (TABLE_NAME) tiene una restricción de tipo "primary key" (muestra "P" en
"CONSTRAINT_TYPE") creada por "SYSTEM" (OWNER) cuyo nombre es "SYS_C004427" (nombre dado por Oracle).

Vamos a eliminar la tabla y la crearemos nuevamente, sin establecer la clave primaria:

drop table libros;


create table libros(
codigo number(5),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(15)
);

Ingresamos 2 registros con valor de código repetido:

insert into libros values(1,'El aleph','Borges','Emece');


insert into libros values(1,'Ilusiones','Bach','Planeta');

Si intentamos agregar una restricción "primary key" a la tabla, aparecerá un mensaje indicando que la clave primaria
se viola y proponiendo que se elimine la clave repetida.

Modificamos el código repetido:

update libros set codigo=2 where titulo='Ilusiones';

Ahora si podremos definir la restricción "primary key" para nuestra tabla "libros":

alter table libros


add constraint PK_libros_codigo
primary key(codigo);

Veamos la información respecto a ella:

select *from user_constraints where table_name='LIBROS';

Aparece la siguiente información simplificada:

OWNER CONSTRAINT_NAME CONSTRAINT_TYPE TABLE_NAME


------------------------------------------------------------------
SYSTEM PK_LIBROS_CODIGO P LIBROS

Nos informa que la tabla "libros" (TABLE_NAME) tiene una restricción de tipo "primary key" (muestra "P" en
"CONSTRAINT_TYPE") creada por "SYSTEM" (OWNER) cuyo nombre es "PK_libros_codigo" (nombre dado por nosotros al
agregarla).

Si intentamos ingresar un registro con un valor para el campo "codigo" que ya existe, no lo permite:

insert into libros values(1,'El quijote de la mancha','Cervantes','Emece');

Tampoco permite modificar un código colocando uno existente:

update libros set codigo=1 where titulo='Ilusiones';

Tampoco podemos ingresar en "codigo" un valor nulo:

insert into libros values(null,'El quijote de la mancha','Cervantes','Emece');

El campo, luego de agregarse la restricción "primary key" se estableció como "not null"; podemos verificarlo:

describe libros;

Si intentamos agregar otra restricción "primary key", Oracle no lo permite:

alter table libros


add constraint PK_libros_titulo
primary key(titulo);

Un mensaje nos indica que la tabla solamente puede tener UNA clave primaria.

Veamos lo que nos informa el catálogo "user_const_columns":

select *from user_cons_columns where table_name='LIBROS';

Nos muestra la siguiente información:

OWNER CONSTRAINT_NAME TABLE_NAME COLUMN_NAME POSITION


-------------------------------------------------------------------------
SYSTEM PK_LIBROS_CODIGO LIBROS CODIGO 1

39 - Restricción unique

Anteriormente aprendimos la restricción "primary key", otra restricción que asegura valores únicos para cada registro
es "unique".

La restricción "unique" impide la duplicación de claves alternas (no primarias), es decir, especifica que dos registros
no puedan tener el mismo valor en un campo. Se permiten valores nulos.
Se pueden aplicar varias restricciones de este tipo a una misma tabla, y pueden aplicarse a uno o varios campos que
no sean clave primaria.

Se emplea cuando ya se estableció una clave primaria (como un número de legajo) pero se necesita asegurar que
otros datos también sean únicos y no se repitan (como número de documento).

La sintaxis general es la siguiente:

alter table NOMBRETABLA


add constraint NOMBRERESTRICCION
unique (CAMPO);

Ejemplo:

alter table alumnos


add constraint UQ_alumnos_documento
unique (documento);

En el ejemplo anterior se agrega una restricción "unique" sobre el campo "documento" de la tabla "alumnos", esto
asegura que no se pueda ingresar un documento si ya existe. Esta restricción permite valores nulos, asi que si se
ingresa el valor "null" para el campo "documento", se acepta.

Por convención, cuando demos el nombre a las restricciones "unique" seguiremos la misma estructura:

"UQ_NOMBRETABLA_NOMBRECAMPO". Quizá parezca innecesario colocar el nombre de la tabla, pero cuando


empleemos varias tablas verá que es útil identificar las restricciones por tipo, tabla y campo.

Recuerde que cuando agregamos una restricción a una tabla que contiene información, Oracle controla los datos
existentes para confirmar que cumplen la condición de la restricción, si no los cumple, la restricción no se aplica y
aparece un mensaje de error. En el caso del ejemplo anterior, si la tabla contiene números de documento
duplicados, la restricción no podrá establecerse; si podrá establecerse si tiene valores nulos.

Oracle controla la entrada de datos en inserciones y actualizaciones evitando que se ingresen valores duplicados.

Un campo que se estableció como clave primaria no puede definirse como clave única; si una tabla tiene una clave
primaria, puede tener una o varias claves únicas (aplicadas a otros campos que no sean clave primaria).

Si consultamos el catálogo "user_constraints", podemos ver las restricciones "unique" (y todos los tipos de
restricciones) de todas las tablas del usuario actual. El resultado es una tabla que nos informa el propietario de la
restricción (OWNER), el nombre de la restricción (CONSTRAINT_NAME), el tipo (CONSTRAINT_TYPE, si es "unique"
muestra una "U"), el nombre de la tabla en la cual se aplica (TABLE_NAME), y otra información que no analizaremos
por el momento.

También podemos consultar el catálogo "user_cons_columns"; nos mostrará el propietario de la restricción (OWNER),
el nombre de la restricción (CONSTRAINT_NAME), la tabla a la cual se aplica (TABLE_NAME), el campo
(COLUMN_NAME) y la posición (POSITION).

Problema:

Trabajamos con la tabla "alumnos".


Eliminamos la tabla:

drop table alumnos;

Creamos la tabla:

create table alumnos(


legajo char(4),
apellido varchar2(20),
nombre varchar2(20),
documento char(8)
);

Agregamos una restricción "primary" para el campo "legajo":


alter table alumnos
add constraint PK_alumnos_legajo
primary key(legajo);

Agregamos una restricción "unique" para el campo "documento":

alter table alumnos


add constraint UQ_alumnos_documento
unique (documento);

Ingresamos algunos registros:

insert into alumnos values('A111','Lopez','Ana','22222222');


insert into alumnos values('A123','Garcia','Maria','23333333');

Intentamos ingresar un documento repetido:

insert into alumnos values('A230','Perez','Juan','23333333');

aparece un mensaje de error indicando que se viola la restricción única.

Intentamos ingresar un legajo repetido:

insert into alumnos values('A123','Suarez','Silvana','30111222');

aparece un mensaje de error indicando que se viola la restricción "primary key".

Veamos las restricciones de la tabla "alumnos" consultando el catálogo "user_constraints":

select *from user_constraints where table_name='ALUMNOS';

Aparecen las dos restricciones creadas anteriormente:

OWNER CONSTRAINT_NAME CONSTRAINT_TYPE TABLE_NAME


--------------------------------------------------------------------------
SYSTEM PK_ALUMNOS_LEGAJO P ALUMNOS
SYSTEM UQ_ALUMNOS_DOCUMENTO U ALUMNOS

Veamos la información de "user_cons_columns":

select *from user_cons_columns where table_name='ALUMNOS';

Nos informa:

OWNER CONSTRAINT_NAME TABLE_NAME COLUMN_NAME


POSITION
--------------------------------------------------------------------------------
SYSTEM UQ_ALUMNOS_DOCUMENTO ALUMNOS DOCUMENTO 1
SYSTEM PK_ALUMNOS_LEGAJO ALUMNOS LEGAJO 1

40 - Restriccion check

La restricción "check" especifica los valores que acepta un campo, evitando que se ingresen valores inapropiados.

La sintaxis básica es la siguiente:

alter table NOMBRETABLA


add constraint NOMBRECONSTRAINT
check CONDICION;
Trabajamos con la tabla "libros" de una librería que tiene los siguientes campos: codigo, titulo, autor, editorial,
preciomin (que indica el precio para los minoristas) y preciomay (que indica el precio para los mayoristas).

Los campos correspondientes a los precios (minorista y mayorista) se definen de tipo number(5,2), es decir, aceptan
valores entre -999.99 y 999.99. Podemos controlar que no se ingresen valores negativos para dichos campos
agregando una restricción "check":

alter table libros


add constraint CK_libros_precio_positivo
check (preciomin>=0 and preciomay>=0);

Este tipo de restricción verifica los datos cada vez que se ejecuta una sentencia "insert" o "update", es decir, actúa
en inserciones y actualizaciones.

Si la tabla contiene registros que no cumplen con la restricción que se va a establecer, la restricción no se puede
establecer, hasta que todos los registros cumplan con dicha restricción.

La condición puede hacer referencia a otros campos de la misma tabla. Por ejemplo, podemos controlar que el precio
mayorista no sea mayor al precio minorista:

alter table libros


add constraint CK_libros_preciominmay
check (preciomay<=preciomin);

Por convención, cuando demos el nombre a las restricciones "check" seguiremos la misma estructura: comenzamos
con "CK", seguido del nombre de la tabla, del campo y alguna palabra con la cual podamos identificar fácilmente de
qué se trata la restricción, por si tenemos varias restricciones "check" para el mismo campo.

Un campo puede tener varias restricciones "check" y una restricción "check" puede incluir varios campos.

Las condiciones para restricciones "check" también pueden incluir una lista de valores. Por ejemplo establecer que
cierto campo asuma sólo los valores que se listan:

...
check (CAMPO in ('lunes','miercoles','viernes'));

Si un campo permite valores nulos, "null" es un valor aceptado aunque no esté incluido en la condición de restricción.

Si intentamos establecer una restricción "check" para un campo que entra en conflicto con otra restricción "check"
establecida al mismo campo, Oracle no lo permite. Pero si establecemos una restricción "check" para un campo que
entra en conflicto con un valor "default" establecido para el mismo campo, Oracle lo permite; pero al intentar
ingresar un registro, aparece un mensaje de error.

En las condiciones de chequeo no es posible incluir funciones (como "sysdate").

Un campo con una restricción "primary key" o "unique" puede tener una (o varias) restricciones "check".

En la condición de una restricción "check" se puede establecer que un campo no admita valores nulos:

alter table libros


add constraint CK_libros_titulo
check (titulo is not null);

Problema:

Trabajamos con la tabla "libros" de una librería.


Eliminamos la tabla:

drop table libros;

La creamos e ingresamos algunos registros:

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(15),
preciomin number(5,2),
preciomay number(5,2)
);

insert into libros values (1,'Uno','Bach','Planeta',22,20);


insert into libros values (2,'El quijote','Cervantes','Emece',15,13);
insert into libros values (3,'Aprenda PHP','Mario Molina','Siglo XXI',53,48);
insert into libros values (4,'Java en 10 minutos','Garcia','Siglo XXI',35,40);

Agregamos una restricción "primary key" para el campo "codigo":

alter table libros


add constraint PK_libros_codigo
primary key (codigo);

Agregamos una restricción única, la clave única consta de 3 campos, "titulo", "autor" y "editorial":

alter table libros


add constraint UQ_libros
unique (titulo,codigo,editorial);

Agregamos una restricción "check" para asegurar que los valores de los campos correspondientes a precios no puedan
ser negativos:

alter table libros


add constraint CK_libros_precios_positivo
check (preciomin>=0 and preciomay>=0);

Intentamos ingresar un valor inválido para algún campo correspondiente al precio, que vaya en contra de la
restricción (por ejemplo el valor "-15"):

insert into libros values (5,'Matematica estas ahi','Paenza','Siglo XXI',-15,30);

aparecerá un mensaje de error indicando que hay conflicto con la restricción de control creada anteriormente y la
inserción no se realiza.

Igualmente si intentamos actualizar un precio, que vaya en contra de la restricción:

update libros set preciomay=-20 where titulo='Uno';

Si intentamos agregar una restricción que no permita que el precio mayorista supere el precio minorista:

alter table libros


add constraint CK_libros_preciominmay
check (preciomay<=preciomin);

aparece un mensaje de error y la sentencia no se ejecuta, porque hay un registro que no cumple con la restricción
que intentamos establecer. Podemos modificar los datos que no cumplen la condición de la restricción:

update libros set preciomay=30


where titulo='Java en 10 minutos';

Ahora Oracle si nos permite agregar la restricción "check" que impida que se ingresen valores para "preciomay"
superiores a "preciomin":

alter table libros


add constraint CK_libros_preciominmay
check (preciomay<=preciomin);

Veamos las restricciones de la tabla:

select *from user_constraints where table_name='LIBROS';


Aparece la siguiente tabla (simplificada) resultado:

OWNER CONSTRAINT_NAME CONSTRAINT_TYPE TABLE_NAME SEARCH_CONDITION


----------------------------------------------------------------------------------------
----------
SYSTEM PK_LIBROS_CODIGO P LIBROS
SYSTEM UQ_LIBROS U LIBROS
SYSTEM CK_LIBROS_PRECIOS_POSITIVO C LIBROS
preciomin>=0 and preciomay>=0
SYSTEM CK_LIBROS_PRECIOMINMAY C LIBROS preciomay<=precio

Note que en el caso de las restricciones de control, en las cuales muestra "C" en el tipo de constraint, la columna
"SEARCH_CONDITION" muestra la regla que debe cumplirse; en caso de ser una restricción "primary key" o unique",
esa columna queda vacía.

Intentamos ingresar un registro que infrinja la restricción "CK_libros_preciominmax":

insert into libros values (6,'El gato con botas',null,'Planeta',25,30);

Mensaje de error.

Consultamos "user_cons_columns":

select *from user_cons_columns where table_name='LIBROS';

Aparece la siguiente información simplificada:

OWNER CONSTRAINT_NAME TABLE_NAME COLUMN_NAME POSITION


----------------------------------------------------------------------------------------
-
SYSTEM PK_LIBROS_CODIGO LIBROS CODIGO 1
SYSTEM UQ_LIBROS LIBROS EDITORIAL 3
SYSTEM UQ_LIBROS LIBROS CODIGO 2
SYSTEM UQ_LIBROS LIBROS TITULO 1
SYSTEM CK_LIBROS_PRECIOS_POSITIVO LIBROS PRECIOMAY
SYSTEM CK_LIBROS_PRECIOS_POSITIVO LIBROS PRECIOMIN
SYSTEM CK_LIBROS_PRECIOMINMAY LIBROS PRECIOMAY
SYSTEM CK_LIBROS_PRECIOMINMAY LIBROS PRECIOMIN

Analizamos la información: la tabla tiene 4 restricciones, 1 "primary key", 1 "unique" y 2 "check". La restricción
"primarykey" ocupa una sola fila porque fue definida para 1 solo campo, por ello, en la columna "POSITION" aparece
"1". La restricción única ocupa tres filas porque fue definida con 3 campos cuyo orden está indicado en la columna
"POSITION". La columna "POSITION" muestra información si la restricción es "primary key" o "unique" indicando el
orden de los campos. La restricción de control "CK_libros_precios_positivo" ocupa 2 filas porque en su definición se
nombran 2 campos (indicados en "COLUMN_NAME"). La restricción de control "CK_libros_preciominmax" ocupa 2 filas
porque en su definición se nombran 2 campos (indicados en "COLUMN_NAME").

41 - Restricciones: validación y estados (validate - novalidate -


enable - disable)

Sabemos que si agregamos una restricción a una tabla que contiene datos, Oracle los controla para asegurarse que
cumplen con la condición de la restricción, si algún registro no la cumple, la restricción no se establecece.

Es posible deshabilitar esta comprobación estableciendo una restricción sin comprobar los datos existentes en la
tabla.

Podemos hacerlo cuando agregamos la restricción (de cualquier tipo) a una tabla para que Oracle acepte los valores
ya almacenados que infringen la restricción. Para ello debemos incluir la opción "novalidate" en la instrucción "alter
table":

alter table libros


add constraint PK_libros_codigo
primary key (codigo) novalidate;

La restricción no se aplica en los datos existentes, pero si intentamos ingresar un nuevo valor que no cumpla la
restricción (o actualizarlo), Oracle no lo permite.

Para saber si una restricción está validada o no, podemos consultar el catálogo "user_constraints" y fijarnos lo que
informa la columna "validated".

También podemos deshabilitar las restricciones para agregar o actualizar datos sin comprobarla:

alter table libros


add constraint PK_libros_codigo
primary key (codigo) disable;

Entonces, para evitar la comprobación de datos existentes y futuros al crear la restricción, la sintaxis básica es la
siguiente:

alter table TABLA


add constraint NOMBRERESTRICCION
TIPOdeRESTRICCION (CAMPO o CONDICION)--campo si es primary key o unique; condición si
es check
disable novalidate;

Por defecto (si no especificamos) la opción es "validate", es decir, controla los datos existentes y "enable", es decir,
controla futuros ingresos y actualizaciones.

También es posible alterar la restricción luego de haberla creado. Sintaxis:

alter table NOMBRETABLA


ESTADO VALIDACION
constraint NOMBRERESTRICCION;

En el ejemplo siguiente deshabilitamos la restricción "PK_libros_codigo" para poder ingresar un valor que infrija la
restricción:

alter table libros


disable validate
constraint PK_libros_codigo;

Para habilitar una restricción deshabilitada se ejecuta la misma instrucción pero con la cláusula "enable":

alter table libros


enable validate
constraint PK_libros_codigo;

Para saber si una restricción está habilitada o no, podemos consultar el catálogo "user_constraints" y fijarnos lo que
informa la columna "status".

Los estados "validate" y "novalidate" son relativamente independientes de los estados "enabled" y "disabled".

Cuando habilitamos una restricción "primary key" o "unique" con "enable", los datos existentes DEBEN cumplir con la
restricción; aunque coloquemos "novalidate" junto a "enable", Oracle no permite que se habilite la restrición y valida
los datos existentes de todas maneras. No sucede lo mismo con una restricción "check"; podemos habilitar una
restricción de control con "enable" y "novalidate", Oracle habilita la restricción para futuros ingresos y actualizaciones
y NO valida los datos existentes.

Entonces, "enable" o "disable" activa o desactiva la restricción para los nuevos datos ("enable" es la opción
predeterminada si no se especifica); "validate" o "novalidate" es la opción para validar la restricción en los datos
existentes ("validate" es la predetermidada si se omite).

Una restricción puede estar en los siguientes estados:

- validate y enabled: comprueba los valores existentes y los posteriores ingresos y actualizaciones;

- validate y disable: comprueba los valores existentes pero no las posteriores inserciones y actualizaciones;
- novalidate y enabled: no comprueba los datos existentes, pero si los posteriores ingresos y actualizaciones;

- novalidate y disabled: no comprueba los valores existentes ni los posteriores ingresos y actualizaciones.

Problema:

Trabajamos con la tabla "libros" de una librería.


Eliminamos la tabla:

drop table libros;

La creamos e ingresamos algunos registros:

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(15),
precio number(5,2)
);

insert into libros values (1,'Uno','Bach','Planeta',22);


insert into libros values (2,'El quijote','Cervantes','Emece',15);
insert into libros values (2,'Aprenda PHP','Mario Molina','Siglo XXI',-40);

Intentamos agregar una restricción "primary key" para asegurar que los códigos no se repitan, pero como ya tenemos
almacenado registros que infringen la restricción, Oracle nos mostrará un mensaje de error:

alter table libros


add constraint PK_libros_codigo
primary key (codigo);

Vamos a especificar que no haya comprobación de datos existentes agregando "disable" y "novalidate":

alter table libros


add constraint PK_libros_codigo
primary key (codigo) disable novalidate;

Veamos lo que nos informa "user_constraints":

select constraint_name, constraint_type, status, validated


from user_constraints
where table_name='LIBROS';

Aparece la siguiente tabla:

CONSTRAINT_NAME CONSTRAINT_TYPE STATUS VALIDATED


--------------------------------------------------------------------------
PK_LIBROS_CODIGO P DISABLED NOT VALIDATED

La columna "status" nos informa que está deshabilitada (Disabled).

Si ingresamos un registro con código existente, Oracle lo permite, porque la restricción está en estado "disabled":

insert into libros values (2,'Momo','Michael Ende','Alfaragua',25);

Intentamos habilitar la restricción sin verificar los datos ya almacenados:

alter table libros


enable novalidate constraint PK_libros_codigo;

No lo permite, aun cuando especificamos que no valide los datos existentes, Oracle realiza la verificación
igualmente.

Eliminamos los registros con clave duplicada:


delete libros where titulo='El quijote';
delete libros where titulo='Momo';

Ahora Oracle permite habilitar la restricción:

alter table libros


enable novalidate constraint PK_libros_codigo;

Si intentamos actualizar un registro repitiendo la clave primaria, Oracle no lo permite:

insert into libros values (2,'Momo','Michael Ende','Alfaragua',25);

Veamos lo que nos informa "user_constraints":

select constraint_name, constraint_type, status, validated


from user_constraints
where table_name='LIBROS';

Aparece la siguiente tabla:

CONSTRAINT_NAME CONSTRAINT_TYPE STATUS VALIDATED


--------------------------------------------------------------------------
PK_LIBROS_CODIGO P ENABLED NOT VALIDATED

Intentamos agregamos una restricción "check" que no permita valores negativos para el precio:

alter table libros


add constraint CK_libros_precio
check(precio>=0);

Oracle no lo permite porque, por defecto, la opción es "validate" y existen precios que violan la restricción que
intentamos establecer.

Agregamos la restricción especificando que no valide los datos almacenados:

alter table libros


add constraint CK_libros_precio
check(precio>=0) novalidate;

Veamos el estado de la restricción de control:

select constraint_type, status, validated


from user_constraints
where table_name='LIBROS' and
constraint_name='CK_LIBROS_PRECIO';

El resultado es el siguiente:

CONSTRAINT_TYPE STATUS VALIDATED


-----------------------------------------------------
C ENABLED NOT VALIDATED

Si intentamos ingresar un valor negativo para el precio, aparecerá un mensaje de error, porque la restricción de
control creada está habilitada:

insert into libros values (3,'Momo','Michael Ende','Alfaragua',-25);

Deshabilitamos la restricción "CK_libros_precio":

alter table libros


disable constraint CK_libros_precio;

Veamos el estado actual:


select constraint_type, status, validated
from user_constraints
where table_name='LIBROS' and
constraint_name='CK_LIBROS_PRECIO';

Nos muestra que está deshabilitada y no valida los datos existentes.

Ahora si podemos ingresar el registro:

insert into libros values (3,'Momo','Michael Ende','Alfaragua',-25);

Habilitamos la restricción para futuros ingresos pero no para los existentes:

alter table libros


enable novalidate constraint CK_libros_precio;

Note que Oracle lo permite, no valida los datos existentes, pero si fuera otro tipo de restricción, no lo permitiría.

Consultamos "user_constraints":

select constraint_type, status, validated


from user_constraints
where table_name='LIBROS' and
constraint_name='CK_LIBROS_PRECIO';

Nos muestra que está habilitada y no valida los datos existentes.

42 - Restricciones: información (user_constraints -


user_cons_columns)

El catálogo "user_constraints" muestra la información referente a todas las restricciones establecidas en las tablas del
usuario actual, devuelve varias columnas, explicaremos algunas de ellas:

- owner: propietario de la restricción;

- constraints_name: el nombre de la restricción;

- constraint_type: tipo de restricción. Si es una restricción de control muestra el caracter "C", si es "primary key"
muestra "P", si es "unique" el caracter "U".

- table_name: nombre de la tabla en la cual se estableció la restricción;

- search_condition: solamente es aplicable a restricciones de control; indica la condición de chequeo a cumplirse.

- status: indica si está habilitada (enabled) para futuras inserciones y actualizaciones o deshabilitada (disabled)

- validated: indica si valida los datos existentes en la tabla (validated) o no (no validate)

El catálogo "user_cons_columns" muestra la información referente a todas las restricciones establecidas en las tablas
del usuario actual, devuelve las siguientes columnas:

- owner: propietario d la restricción;

- constraints_name: el nombre de la restricción;

- table_name: nombre de la tabla en la cual se estableció;

- column_name: muestra cada campo en el cual la restricción fue aplicada.


- position: solamente es aplicable a restricciones "primary key" y "unique"; indica el orden en que fueron definidos los
campos que componen la clave (primaria o única).

43 - Restricciones: eliminación (alter table - drop constraint)

Para eliminar una restricción, la sintaxis básica es la siguiente:

alter table NOMBRETABLA


drop constraint NOMBRERESTRICCION;

Para eliminar la restricción "PK_libros_codigo" de la tabla libros tipeamos:

alter table libros


drop constraint PK_LIBROS_CODIGO;

Recuerde colocar el nombre de la restricción en mayúsculas, sino Oracle no la reconocerá.

Cuando eliminamos una tabla, todas las restricciones que fueron establecidas en ella, se eliminan también.

La condición de control que debe cumplir una restricción de control no puede modificarse, hay que eliminar la
restricción y volver a crearla; igualmente con las restricciones "primary key" y "unique", no pueden modificarse los
campos.

Problema:

Trabajamos con la tabla "libros" de una librería.


Eliminamos la tabla:

drop table libros;

La creamos estableciendo el campo código como clave primaria:

create table libros(


codigo number(5) not null,
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(15),
precio number(6,2)
);

Definimos una restricción "primary key" para nuestra tabla "libros" para asegurarnos que cada libro tendrá un código
diferente y único:

alter table libros


add constraint PK_libros_codigo
primary key(codigo);

Definimos una restricción "check" para asegurarnos que el precio no será negativo:

alter table libros


add constraint CK_libros_precio
check (precio>=0);

Definimos una restricción "unique" para los campos "titulo", "autor" y "editorial":

alter table libros


add constraint UQ_libros
unique(titulo,autor,editorial);
Vemos las restricciones:

select *from user_constraints where table_name='LIBROS';

Aparecen 4 restricciones:

- 1 "check" que controla que el precio sea positivo;

- 1 "check" , que se creó al definir "not null" el campo "codigo", el nombre le fue dado por Oracle;

- 1 "primary key" y

- 1 "unique".

Eliminamos la restricción "PK_libros_codigo":

alter table libros


drop constraint PK_LIBROS_CODIGO;

Eliminamos la restricción de control "CK_libros_precio":

alter table libros


drop constraint CK_LIBROS_PRECIO;

Vemos si se eliminaron:

select *from user_constraints where table_name='LIBROS';

Aparecen 2 restricciones.

44 - Indices.

Otros objetos de base de datos son los índices.

Los índices sirven para acceder a los registros de una tabla rápidamente, acelerando la localización de la
información.

Los índices se emplean para facilitar la obtención de información de una tabla. El indice de una tabla desempeña la
misma función que el índice de un libro: permite encontrar datos rápidamente; en el caso de las tablas, localiza
registros.

Oracle accede a los datos de dos maneras:

1) recorriendo las tablas; comenzando el principio y extrayendo los registros que cumplen las condiciones de la
consulta; lo cual implica posicionar las cabezas lectoras, leer el dato, controlar si coincide con lo que se busca (como
si pasáramos una a una las páginas de un libro buscando un tema específico).

2) empleando índices; recorriendo la estructura de árbol del índice para localizar los registros y extrayendo los que
cumplen las condiciones de la consulta (comparando con un libro, diremos que es como leer el índice y luego de
encontrar el tema buscado, ir directamente a la página indicada).

Un índice posibilita el acceso directo y rápido haciendo más eficiente las búsquedas. Sin índice, Oracle debe recorrer
secuencialmente toda la tabla para encontrar un registro.

Los índices son estructuras asociadas a tablas, una tabla que almacena los campos indexados y se crean para acelerar
las consultas.
Entonces, el objetivo de un indice es acelerar la recuperación de información. La indexación es una técnica que
optimiza el acceso a los datos, mejora el rendimiento acelerando las consultas y otras operaciones. Es útil cuando la
tabla contiene miles de registros, cuando se realizan operaciones de ordenamiento y agrupamiento y cuando se
combinan varias tablas (tema que veremos más adelante).

La desventaja es que consume espacio en el disco en disco y genera costo de mantenimiento (tiempo y recursos).

Es importante identificar el o los campos por los que sería útil crear un índice, aquellos campos por los cuales se
realizan búsquedas con frecuencia: claves primarias, claves externas o campos que combinan tablas.

No se recomienda crear índices sobre campos que no se usan con frecuencia en consultas o en tablas muy pequeñas.

Los cambios sobre la tabla, como inserción, actualización o eliminación de registros, son incorporados
automáticamente.

Cuando creamos una restricción "primary key" o "unique" a una tabla, Oracle automáticamente crea un índice sobre el
campo (o los campos) de la restricción y le da el mismo nombre que la restricción. En caso que la tabla ya tenga un
índice, Oracle lo usa, no crea otro.

Oracle permite crear distintos tipos de índices. "Normal" es el standard de Oracle, son índices tipo árbol binario;
contiene una entrada por cada valor de clave que almacena la dirección donde se encuentra el dato. Es el tipo
predeterminado y el más común (el único que estudiaremos).

45 - Indices (Crear - Información)

Dijimos que el objetivo de un indice es acelerar la recuperación de información y que es útil cuando la tabla contiene
miles de registros, cuando se realizan operaciones de ordenamiento y agrupamiento, etc.

Es importante identificar el o los campos por los que sería útil crear un índice, aquellos campos por los cuales se
realizan búsquedas con frecuencia: claves primarias, claves externas o campos que combinan tablas.

No se recomienda crear índices sobre campos que no se usan con frecuencia en consultas o en tablas muy pequeñas.

Para crear índices empleamos la instrucción "create index".

La sintaxis básica es la siguiente:

create TIPOdeINDICE index NOMBREINDICE


on NOMBRETABLA(CAMPOS);

Los índices pueden ser: no únicos (los valores pueden estar repetidos) o únicos (los valores no pueden duplicarse). De
modo predeterminado, si no se especifica el tipo de índice, se crea uno no único.

En el siguiente ejemplo creamos un índice único sobre el campo "documento" de la tabla "empleados":

create unique index I_empleados_documento


on empleados(documento);

Para identificar los índices fácilmente, podemos agregar un prefijo al nombre del índice, por ejemplo "I" y luego el
nombre de la tabla y/o campo.

Si se intenta crear un índice único para un campo que tiene valores duplicados, Oracle no lo permite.

Los campos de tipo "long" y "long raw" no pueden indexarse.

Una tabla puede indexarse por un campo (o varios).

Creamos un índice compuesto para los campos "apellido" y "nombre":


create index I_empleados_apellidonombre
on empleado(apellido,nombre);

Cuando creamos una restricción "primary key" o "unique" sobre una tabla, Oracle automáticamente crea un índice
sobre el campo (o los campos) de la restricción y le da el mismo nombre que la restricción. En caso que la tabla ya
tenga un índice, Oracle lo usa, no crea otro.

Para obtener información sobre los índices podemos consultar varios diccionarios.

1) "user_indexes": nos muestra las siguientes columnas (entre otras que no analizaremos):

- INDEX_NAME (nombre del índice),


- INDEX_TYPE (tipo de índice, nosotros crearemos el stardart normal),
- TABLE_NAME (nombre de la tabla),
- UNIQUENESS (si es único o no).

2) "user_ind_columns": nos muestra las siguientes columnas (entre otras que no analizaremos):

- INDEX_NAME (nombre del índice),


- TABLE_NAME (nombre de la tabla),
- COLUMN_NAME (nombre del campo),
- COLUMN_POSITION (posición del campo),

3) "user_objects": en la columna "OBJECT_TYPE" muestra "index" si es un índice.

4) "user_constraints": si la restricción tiene un índice asociado, aparece su nombre en la columna "INDEX_NAME".

Problema:

Trabajamos con la tabla "empleados" de una librería.


Eliminamos la tabla y la creamos:

drop table empleados;


create table empleados(
legajo number(5),
documento char(8),
apellido varchar2(25),
nombre varchar2(25),
domicilio varchar2(30)
);

Agregamos una restricción "primary key" sobre el campo "legajo":

alter table empleados


add constraint PK_empleados_legajo
primary key (legajo);

Consultamos "user_constraints":

select constraint_name, constraint_type, index_name


from user_constraints
where table_name='EMPLEADOS';

Note que Oracle creó un índice con el mismo nombre de la restricción.

Veamos los índices de "empleados":

select index_name, index_type, uniqueness


from user_indexes
where table_name='EMPLEADOS';

Aparece 1 fila, mostrando el nombre del índice, indicando que es normal y único.

Creamos un índice único sobre el campo "documento":


create unique index I_empleados_documento
on empleados(documento);

Verificamos que se creó el índice:

select index_name, index_type, uniqueness


from user_indexes where table_name='EMPLEADOS';

Aparecen 2 filas, una por cada índice.

Agregamos a la tabla una restricción única sobre el campo "documento":

alter table empleados


add constraint UQ_empleados_documento
unique(documento);

Analicemos la información que nos muestra "user_constraints":

select constraint_name, constraint_type, index_name


from user_constraints
where table_name='EMPLEADOS';

En la columna "index_name" correspondiente a la restricción única, aparece "I_empleados_documento", Oracle usa


para esta restricción el índice existente, no crea otro nuevo.

Creamos un índice no único, compuesto (para los campos "apellido" y "nombre"):

create index I_empleados_apellidonombre


on empleados(apellido,nombre);

Consultamos el diccionario "user_indexes":

select index_name, index_type, uniqueness


from user_indexes
where table_name='EMPLEADOS';

Nos muestra información sobre los 3 índices de la tabla.

Veamos todos los índices de la base de datos activa consultando "user_objects":

select *from user_objects


where object_type='INDEX';

Aparecen varios índices, entre ellos, los de nuestra tabla "empleados".

Obtenemos información de "user_ind_columns":

select index_name,column_name,column_position
from user_ind_columns
where table_name='EMPLEADOS';

Nos muestra la siguiente tabla:

INDEX_NAME COLUMN_NAME COLUMN_POSITION


-----------------------------------------------------------------
PK_EMPLEADOS_LEGAJO LEGAJO 1
I_EMPLEADOS_DOCUMENTO DOCUMENTO 1
I_EMPLEADOS_APELLIDONOMBRE APELLIDO 1
I_EMPLEADOS_APELLIDONOMBRE NOMBRE 2

La tabla tiene 3 índices, 2 filas corresponden al índice compuesto "I_empleados_apellidonombre"; la columna


"position" indica el orden de los campos indexados.

Agregamos algunos registros:


insert into empleados values(1,'22333444','Lopez','Juan','Colon 123');
insert into empleados values(1,'23444555','Lopez','Luis','Lugones 1234');
insert into empleados values(1,'24555666','Garcia','Pedro','Avellaneda 987');
insert into empleados values(1,'25666777','Garcia','Ana','Caseros 678');

Si intentamos crear un índice único para el campo "apellido" (que contiene valores duplicados") Oracle no lo permite:

create unique index I_empleados_apellido


on empleados(apellido);

Igualmente, si hay un índice único sobre un campo y luego intentamos ingresar un registro con un valor repetido para
el campo indexado, Oracle no lo permite.

Creamos un índice único sobre el campo "nombre":

create unique index I_empleados_nombre


on empleados(nombre);

Oracle lo permite porque no hay valores duplicados.

Intentamos agregamos un registro que repita un nombre:

insert into empleados values(5,'30111222','Perez','Juan','Bulnes 233');

Oracle no lo permite.

46 - Indices (eliminar)

Los índices se eliminan con "drop index"; la siguiente es la sintaxis básica:

drop index NOMBREINDICE;

Eliminamos el índice "I_empleados_documento":

drop index I_empleados_documento;

Los índices usados por las restricciones "primary key" y "unique" no pueden eliminarse con "drop index", se eliminan
automáticamente cuando quitamos la restricción.

Si eliminamos una tabla, todos los índices asociados a ella se eliminan.

Problema:

Trabajamos con la tabla "empleados".


Eliminamos la tabla y la creamos:

drop table empleados;

create table empleados(


legajo number (5),
documento char(8),
apellido varchar2(40),
nombre varchar2(40)
);

Creamos un índice único para el campo "legajo":

create unique index I_empleados_legajo


on empleados(legajo);
Agregamos una restricción "unique" sobre "legajo":

alter table empleados


add constraint UQ_empleados_legajo
unique (legajo);

Verificamos que la restricción usa el índice creado anteriormente, no crea otro:

select constraint_name, constraint_type, index_name


from user_constraints
where table_name='EMPLEADOS';

Agregamos una restricción "primary key" sobre "documento":

alter table empleados


add constraint PK_empleados_documento
primary key(documento);

Verificamos que Oracle creó un índice para el campo "documento":

select constraint_name, constraint_type, index_name


from user_constraints
where table_name='EMPLEADOS';

Consultamos todos los índices y sus tipos consultando "user_indexes":

select index_name,uniqueness
from user_indexes
where table_name='EMPLEADOS';

Creamos un índice no único sobre "nombre":

create index I_empleados_nombre


on empleados(nombre);

Creamos un índice no único sobre "apellido":

create index I_empleados_apellido


on empleados(apellido);

Si intentamos eliminar un índice que utiliza una restricción Oracle no lo permite:

drop index I_empleados_legajo;

Verificamos que tal índice es utilizado por una restricción:

select constraint_name, constraint_type, index_name


from user_constraints
where index_name='I_EMPLEADOS_LEGAJO';

Eliminamos el índice "I_empleados_nombre":

drop index I_empleados_nombre;

Corroboremos que se eliminó:

select *from user_objects


where object_type='INDEX';

No aparece en la lista.

Eliminamos la tabla:
drop table empleados;

Verificamos que se eliminaron todos los índices establecidos sobre la tabla:

select *from user_indexes where table_name='EMPLEADOS';

No aparece ninguno cuyo nombre de la tabla sea "empleados".

Lo verificamos nuevamente consultando el diccionario de todos los objetos:

select *from user_objects


where object_type='INDEX';

No aparecen los índices.

47 - Varias tablas (join)

Hasta el momento hemos trabajado con una sola tabla, pero generalmente, se trabaja con más de una.

Para evitar la repetición de datos y ocupar menos espacio, se separa la información en varias tablas. Cada tabla
almacena parte de la información que necesitamos registrar.

Por ejemplo, los datos de nuestra tabla "libros" podrían separarse en 2 tablas, una llamada "libros" y otra "editoriales"
que guardará la información de las editoriales. En nuestra tabla "libros" haremos referencia a la editorial colocando
un código que la identifique. Veamos:

create table libros(


codigo number(4),
titulo varchar2(40) not null,
autor varchar2(30),
codigoeditorial number(3) not null,
precio number(5,2),
primary key (codigo)
);

create table editoriales(


codigo number(3),
nombre varchar2(20) not null,
primary key(codigo)
);

De esta manera, evitamos almacenar tantas veces los nombres de las editoriales en la tabla "libros" y guardamos el
nombre en la tabla "editoriales"; para indicar la editorial de cada libro agregamos un campo que hace referencia al
código de la editorial en la tabla "libros" y en "editoriales".

Al recuperar los datos de los libros con la siguiente instrucción:

select* from libros;

vemos que en el campo "editorial" aparece el código, pero no sabemos el nombre de la editorial. Para obtener los
datos de cada libro, incluyendo el nombre de la editorial, necesitamos consultar ambas tablas, traer información de
las dos.

Cuando obtenemos información de más de una tabla decimos que hacemos un "join" (combinación).

Veamos un ejemplo:

select *from libros


join editoriales
on libros.codigoeditorial=editoriales.codigo;
Resumiendo: si distribuimos la información en varias tablas evitamos la redundancia de datos y ocupamos menos
espacio físico en el disco. Un join es una operación que relaciona dos o más tablas para obtener un resultado que
incluya datos (campos y registros) de ambas; las tablas participantes se combinan según los campos comunes a ambas
tablas.

Hay tres tipos de combinaciones. En los siguientes capítulos explicamos cada una de ellas.

48 - Combinación interna (join)

Un join es una operación que relaciona dos o más tablas para obtener un resultado que incluya datos (campos y
registros) de ambas; las tablas participantes se combinan según los campos comunes a ambas tablas.

Hay tres tipos de combinaciones:

1) combinaciones internas (inner join o join),


2) combinaciones externas y
3) combinaciones cruzadas.

También es posible emplear varias combinaciones en una consulta "select", incluso puede combinarse una tabla
consigo misma.

La combinación interna emplea "join", que es la forma abreviada de "inner join". Se emplea para obtener información
de dos tablas y combinar dicha información en una salida.

La sintaxis básica es la siguiente:

select CAMPOS
from TABLA1
join TABLA2
on CONDICIONdeCOMBINACION;

Ejemplo:

select *from libros


join editoriales
on codigoeditorial=editoriales.codigo;

Analicemos la consulta anterior.

- especificamos los campos que aparecerán en el resultado en la lista de selección;

- indicamos el nombre de la tabla luego del "from" ("libros");

- combinamos esa tabla con "join" y el nombre de la otra tabla ("editoriales"); se especifica qué tablas se van a
combinar y cómo;

- cuando se combina información de varias tablas, es necesario especificar qué registro de una tabla se combinará
con qué registro de la otra tabla, con "on". Se debe especificar la condición para enlazarlas, es decir, el campo por el
cual se combinarán, que tienen en común. "on" hace coincidir registros de ambas tablas basándose en el valor de tal
campo, en el ejemplo, el campo "codigoeditorial" de "libros" y el campo "codigo" de "editoriales" son los que
enlazarán ambas tablas. Se emplean campos comunes, que deben tener tipos de datos iguales o similares.

La condicion de combinación, es decir, el o los campos por los que se van a combinar (parte "on"), se especifica según
las claves primarias y externas.

Note que en la consulta, al nombrar el campo usamos el nombre de la tabla también. Cuando las tablas referenciadas
tienen campos con igual nombre, esto es necesario para evitar confusiones y ambiguedades al momento de
referenciar un campo. En el ejemplo, si no especificamos "editoriales.codigo" y solamente tipeamos "codigo", Oracle
no sabrá si nos referimos al campo "codigo" de "libros" o de "editoriales" y mostrará un mensaje de error indicando
que "codigo" es ambiguo.
Entonces, si las tablas que combinamos tienen nombres de campos iguales, DEBE especificarse a qué tabla pertenece
anteponiendo el nombre de la tabla al nombre del campo, separado por un punto (.).

Si una de las tablas tiene clave primaria compuesta, al combinarla con la otra, en la cláusula "on" se debe hacer
referencia a la clave completa, es decir, la condición referenciará a todos los campos clave que identifican al
registro.

Se puede incluir en la consulta join la cláusula "where" para restringir los registros que retorna el resultado; también
"order by", "distinct", etc..

Se emplea este tipo de combinación para encontrar registros de la primera tabla que se correspondan con los
registros de la otra, es decir, que cumplan la condición del "on". Si un valor de la primera tabla no se encuentra en la
segunda tabla, el registro no aparece; si en la primera tabla el valor es nulo, tampoco aparece.

Para simplificar la sentencia podemos usar un alias para cada tabla:

select l.codigo,titulo,autor,nombre
from libros l
join editoriales e
on l.codigoeditorial=e.codigo;

En algunos casos (como en este ejemplo) el uso de alias es para fines de simplificación y hace más legible la consulta
si es larga y compleja, pero en algunas consultas es absolutamente necesario.

Problema:

Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".
Eliminamos ambas tablas:

drop table libros;


drop table editoriales;

Creamos las tablas:

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
codigoeditorial number(3)
);
create table editoriales(
codigo number(3),
nombre varchar2(20),
primary key (codigo)
);

Ingresamos algunos registros en ambas tablas:

insert into editoriales values(1,'Planeta');


insert into editoriales values(2,'Emece');
insert into editoriales values(3,'Siglo XXI');

insert into libros values(100,'El aleph','Borges',1);


insert into libros values(200,'Martin Fierro','Jose Hernandez',2);
insert into libros values(300,'Aprenda PHP','Mario Molina',3);
insert into libros values(400,'Java en 10 minutos',null,5);
insert into libros values(500,'Matematica estas ahi','Paenza',null);

Recuperamos los datos de libros:

select *from libros;

vemos que en el campo "editorial" aparece el código, pero no sabemos el nombre de la editorial. Realizamos un join
para obtener datos de ambas tablas (titulo, autor y nombre de la editorial):

select titulo, autor, nombre


from libros
join editoriales
on codigoeditorial=editoriales.codigo;

Note que los libros cuyo código de editorial NO se encuentra en "editoriales" no aparecen en el resultado de la
consulta. El libro "Java en 10 minutos" tiene código de editorial 5, y ese código no está presente en "editoriales"; el
libro "Matemática estas ahi" tiene valor nulo en "codigoeditorial", por lo tanto, tampoco se muestra en el join.

Mostramos el código del libro, título, autor y nombre de la editorial realizando un join y empleando alias:

select l.codigo,titulo,autor,nombre
from libros l
join editoriales e
on codigoeditorial=e.codigo;

Note que al listar el campo "codigo" especificamos a qué tabla pertenece; si no lo hacemos Oracle no sabrá si nos
referimos al de la tabla "libros" o "editoriales". Los demás campos no tienen referencia a la tabla porque tienen
nombres que no se repiten.

Realizamos la misma consulta anterior agregando un "where" para obtener solamente los libros de la editorial "Siglo
XXI":

select l.codigo,titulo,autor,nombre
from libros l
join editoriales e
on codigoeditorial=e.codigo
where e.nombre='Siglo XXI';

Obtenemos título, autor y nombre de la editorial, esta vez ordenados por título:

select titulo,autor,nombre
from libros l
join editoriales e
on codigoeditorial=e.codigo
order by titulo;

49 - Combinación externa izquierda (left join)

Vimos que una combinación interna (join) encuentra registros de la primera tabla que se correspondan con los
registros de la segunda, es decir, que cumplan la condición del "on" y si un valor de la primera tabla no se encuentra
en la segunda tabla, el registro no aparece.

Si queremos saber qué registros de una tabla NO encuentran correspondencia en la otra, es decir, no existe valor
coincidente en la segunda, necesitamos otro tipo de combinación, "outer join" (combinación externa).

Las combinaciones externas combinan registros de dos tablas que cumplen la condición, más los registros de la
segunda tabla que no la cumplen; es decir, muestran todos los registros de las tablas relacionadas, aún cuando no
haya valores coincidentes entre ellas.

Este tipo de combinación se emplea cuando se necesita una lista completa de los datos de una de las tablas y la
información que cumple con la condición. Las combinaciones externas se realizan solamente entre 2 tablas.

Hay tres tipos de combinaciones externas: "left outer join", "right outer join" y "full outer join"; se pueden abreviar
con "left join", "right join" y "full join" respectivamente.
Vamos a estudiar las primeras.

Se emplea una combinación externa izquierda para mostrar todos los registros de la tabla de la izquierda. Si no
encuentra coincidencia con la tabla de la derecha, el registro muestra los campos de la segunda tabla seteados a
"null".

En el siguiente ejemplo solicitamos el título y nombre de la editorial de los libros:

select titulo,nombre
from editoriales e
left join libros l
on codigoeditorial = e.codigo;

El resultado mostrará el título y nombre de la editorial; las editoriales de las cuales no hay libros, es decir, cuyo
código de editorial no está presente en "libros" aparece en el resultado, pero con el valor "null" en el campo "titulo".

Es importante la posición en que se colocan las tablas en un "left join", la tabla de la izquierda es la que se usa para
localizar registros en la tabla de la derecha.

Entonces, un "left join" se usa para hacer coincidir registros en una tabla (izquierda) con otra tabla (derecha); si un
valor de la tabla de la izquierda no encuentra coincidencia en la tabla de la derecha, se genera una fila extra (una
por cada valor no encontrado) con todos los campos correspondientes a la tabla derecha seteados a "null". La sintaxis
básica es la siguiente:

select CAMPOS
from TABLAIZQUIERDA
left join TABLADERECHA
on CONDICION;

En el siguiente ejemplo solicitamos el título y el nombre la editorial, la sentencia es similar a la anterior, la


diferencia está en el orden de las tablas:

select titulo,nombre
from libros l
left join editoriales e
on codigoeditorial = e.codigo;

El resultado mostrará el título del libro y el nombre de la editorial; los títulos cuyo código de editorial no está
presente en "editoriales" aparecen en el resultado, pero con el valor "null" en el campo "nombre".

Un "left join" puede tener clausula "where" que restringa el resultado de la consulta considerando solamente los
registros que encuentran coincidencia en la tabla de la derecha, es decir, cuyo valor de código está presente en
"libros":

select titulo,nombre
from editoriales e
left join libros l
on e.codigo=codigoeditorial
where codigoeditorial is not null;

También podemos mostrar las editoriales que NO están presentes en "libros", es decir, que NO encuentran
coincidencia en la tabla de la derecha:

select titulo,nombre
from editoriales e
left join libros l
on e.codigo=codigoeditorial
where codigoeditorial is null;

Problema:

Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".
Eliminamos ambas tablas, las creamos y agregamos a cada una de ellas restricciones únicas para los campos "codigo"
de ambas tablas:

drop table libros;


drop table editoriales;

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
codigoeditorial number(3)
);
create table editoriales(
codigo number(3),
nombre varchar2(20)
);
alter table editoriales
add constraints UQ_editoriales_codigo
unique (codigo);

alter table libros


add constraints UQ_libros_codigo
unique (codigo);

Ingresamos algunos registros en ambas tablas:

insert into editoriales values(1,'Planeta');


insert into editoriales values(2,'Emece');
insert into editoriales values(3,'Siglo XXI');
insert into editoriales values(null,'Sudamericana');
insert into editoriales values(null,'Norma');

insert into libros values(100,'El aleph','Borges',1);


insert into libros values(200,'Martin Fierro','Jose Hernandez',1);
insert into libros values(300,'Aprenda PHP','Mario Molina',2);
insert into libros values(400,'Java en 10 minutos',default,4);
insert into libros values(500,'El quijote de la mancha','Cervantes',null);

Realizamos una combinación izquierda para obtener los títulos de los libros, incluyendo el nombre de la editorial:

select titulo,nombre
from editoriales e
left join libros l
on codigoeditorial = e.codigo;

Las editoriales de las cuales no hay libros, es decir, cuyo código de editorial no está presente en "libros" o tienen
valor nulo, aparece en el resultado, pero con el valor "null" en el campo "titulo" (caso de "Siglo XXI", "Sudamericana" y
"Norma").

Realizamos la misma consulta anterior pero cambiamos el orden de las tablas:

select titulo,nombre
from libros l
left join editoriales e
on codigoeditorial = e.codigo;

El resultado mostrará el título del libro y el nombre de la editorial; los títulos cuyo código de editorial no está
presente en "editoriales" o tienen valor nulo, aparecen en el resultado, pero con el valor "null" en el campo "nombre"
(caso de los libros "El quijote..." y "Java...").

Restringimos el resultado de una consulta considerando solamente los registros que encuentran coincidencia en la
tabla de la derecha, es decir, cuyo valor de código está presente en "libros":

select titulo,nombre
from editoriales e
left join libros l
on e.codigo=codigoeditorial
where codigoeditorial is not null;

Mostramos las editoriales que NO están presentes en "libros", es decir, que NO encuentran coincidencia en la tabla de
la derecha:

select nombre
from editoriales e
left join libros l
on e.codigo=codigoeditorial
where codigoeditorial is null;

Aparecen 3 editoriales.
50 - Combinación externa derecha (right join)
Vimos que una combinación externa izquierda (left join) encuentra registros de la tabla izquierda que se
correspondan con los registros de la tabla derecha y si un valor de la tabla izquierda no se encuentra en la tabla
derecha, el registro muestra los campos correspondientes a la tabla de la derecha seteados a "null".

Una combinación externa derecha ("right outer join" o "right join") opera del mismo modo sólo que la tabla derecha
es la que localiza los registros en la tabla izquierda.

En el siguiente ejemplo solicitamos el título y nombre de la editorial de los libros empleando un "right join":

select titulo,nombre as editorial


from libros l
right join editoriales e
on codigoeditorial = e.codigo;

El resultado mostrará el título y nombre de la editorial; las editoriales de las cuales no hay libros, es decir, cuyo
código de editorial no está presente en "libros" aparece en el resultado, pero con el valor "null" en el campo "titulo".

Es FUNDAMENTAL tener en cuenta la posición en que se colocan las tablas en los "outer join". En un "left join" la
primera tabla (izquierda) es la que busca coincidencias en la segunda tabla (derecha); en el "right join" la segunda
tabla (derecha) es la que busca coincidencias en la primera tabla (izquierda).

En la siguiente consulta empleamos un "left join" para conseguir el mismo resultado que el "right join" anterior":

select titulo,nombre
from editoriales e
left join libros l
on codigoeditorial = e.codigo;

Note que la tabla que busca coincidencias ("editoriales") está en primer lugar porque es un "left join"; en el "right
join" precedente, estaba en segundo lugar.

Un "right join" hace coincidir registros en una tabla (derecha) con otra tabla (izquierda); si un valor de la tabla de la
derecha no encuentra coincidencia en la tabla izquierda, se genera una fila extra (una por cada valor no encontrado)
con todos los campos correspondientes a la tabla izquierda seteados a "null". La sintaxis básica es la siguiente:

select CAMPOS
from TABLAIZQUIERDA
right join TABLADERECHA
on CONDICION;

Un "right join" también puede tener cláusula "where" que restringa el resultado de la consulta considerando
solamente los registros que encuentran coincidencia en la tabla izquierda:

select titulo,nombre
from libros l
right join editoriales e
on e.codigo=codigoeditorial
where codigoeditorial is not null;

Mostramos las editoriales que NO están presentes en "libros", es decir, que NO encuentran coincidencia en la tabla de
la derecha empleando un "right join":

select titulo,nombre
from libros l
right join editoriales e
on e.codigo=codigoeditorial
where codigoeditorial is null;

Problema:

Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".
Eliminamos ambas tablas, las creamos y agregamos dos restricciones "primary key" sobre los campos "codigo" de las
dos tablas:
drop table libros;
drop table editoriales;

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
codigoeditorial number(3)
);
create table editoriales(
codigo number(3),
nombre varchar2(20)
);

alter table libros


add constraint PK_libros
primary key(codigo);

alter table editoriales


add constraint PK_editoriales
primary key(codigo);

Ingresamos algunos registros en ambas tablas:

insert into editoriales values(1,'Planeta');


insert into editoriales values(2,'Emece');
insert into editoriales values(3,'Siglo XXI');
insert into editoriales values(4,'Norma');

insert into libros values(100,'El aleph','Borges',1);


insert into libros values(101,'Martin Fierro','Jose Hernandez',1);
insert into libros values(102,'Aprenda PHP','Mario Molina',2);
insert into libros values(103,'Java en 10 minutos',null,4);
insert into libros values(104,'El anillo del hechicero','Carol Gaskin',4);

Solicitamos el título y nombre de la editorial de los libros empleando un "right join":

select titulo,nombre as editorial


from libros l
right join editoriales e
on codigoeditorial = e.codigo;

Las editoriales de las cuales no hay libros, es decir, cuyo código de editorial no está presente en "libros" aparece en
el resultado, pero con el valor "null" en el campo "titulo"; caso de la editorial "Siglo XXI".

Realizamos la misma consulta anterior agregando un "where" que restringa el resultado considerando solamente los
registros que encuentran coincidencia en la tabla izquierda:

select titulo,nombre as editorial


from libros l
right join editoriales e
on e.codigo=codigoeditorial
where codigoeditorial is not null;

Ya no aparece la editorial "Siglo XXI".

Mostramos las editoriales que NO están presentes en "libros" (que NO encuentran coincidencia en "editoriales"):

select nombre
from libros l
right join editoriales e
on e.codigo=codigoeditorial
where codigoeditorial is null;

Solamente aparece la editorial "Siglo XXI".


51 - Combinación externa completa (full join)

Vimos que un "left join" encuentra registros de la tabla izquierda que se correspondan con los registros de la tabla
derecha y si un valor de la tabla izquierda no se encuentra en la tabla derecha, el registro muestra los campos
correspondientes a la tabla de la derecha seteados a "null". Aprendimos también que un "right join" opera del mismo
modo sólo que la tabla derecha es la que localiza los registros en la tabla izquierda.

Una combinación externa completa ("full outer join" o "full join") retorna todos los registros de ambas tablas. Si un
registro de una tabla izquierda no encuentra coincidencia en la tabla derecha, las columnas correspondientes a
campos de la tabla derecha aparecen seteadas a "null", y si la tabla de la derecha no encuentra correspondencia en
la tabla izquierda, los campos de esta última aparecen conteniendo "null".

Veamos un ejemplo:

select titulo,nombre
from editoriales e
full join libros l
on codigoeditorial = e.codigo;

La salida del "full join" precedente muestra todos los registros de ambas tablas, incluyendo los libros cuyo código de
editorial no existe en la tabla "editoriales" y las editoriales de las cuales no hay correspondencia en "libros".

Problema:

Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".

Eliminamos ambas tablas, las creamos y agregamos dos claves únicas sobre los campos "codigo" de ambas tablas:

drop table libros;


drop table editoriales;

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
codigoeditorial number(3)
);
create table editoriales(
codigo number(3),
nombre varchar2(20)
);

alter table libros


add constraint UQ_libros_codigo
unique (codigo);

alter table editoriales


add constraint UQ_editoriales_codigo
unique (codigo);

Ingresamos algunos registros en ambas tablas:

insert into editoriales values(1,'Alfaragua');


insert into editoriales values(2,'Emece');
insert into editoriales values(3,'Siglo XXI');
insert into editoriales values(4,'Norma');
insert into editoriales values(null,'Sudamericana');

insert into libros values(100,'El aleph','Borges',null);


insert into libros values(101,'Martin Fierro','Jose Hernandez',1);
insert into libros values(102,'Aprenda PHP','Mario Molina',2);
insert into libros values(103,'Java en 10 minutos',default,4);
insert into libros values(104,'El anillo del hechicero','Carol Gaskin',1);

Realizamos una combinación externa completa para obtener todos los registros de ambas tablas, incluyendo los libros
cuyo código de editorial no existe en la tabla "editoriales" y las editoriales de las cuales no hay correspondencia en
"libros":
select titulo,nombre as editorial
from editoriales e
full join libros l
on codigoeditorial = e.codigo;

Note que el libro "El aleph" cuyo valor de "codigoeditorial" es null, muestra "null" en la columna "editorial" y las
editoriales "Sudamericana" y "Siglo XXI" muestran "null" en el campo "titulo".

52 - Combinaciones cruzadas (cross)

Vimos que hay tres tipos de combinaciones: 1) combinaciones internas (join), 2) combinaciones externas (left, outer
y full join) y 3) combinaciones cruzadas.

Las combinaciones cruzadas (cross join) muestran todas las combinaciones de todos los registros de las tablas
combinadas. Para este tipo de join no se incluye una condición de enlace. Se genera el producto cartesiano en el que
el número de filas del resultado es igual al número de registros de la primera tabla multiplicado por el número de
registros de la segunda tabla, es decir, si hay 3 registros en una tabla y 4 en la otra, retorna 12 filas.

La sintaxis básica es ésta:

select CAMPOS
from TABLA1
cross join TABLA2;

Veamos un ejemplo. Un pequeño restaurante almacena los nombres y precios de sus comidas en una tabla llamada
"comidas" y en una tabla denominada "postres" los mismos datos de sus postres.
Si necesitamos conocer todas las combinaciones posibles para un menú, cada comida con cada postre, empleamos un
"cross join":

select c.nombre as "plato principal", p.nombre as "postre"


from comidas c
cross join postres p;

La salida muestra cada plato combinado con cada uno de los postres.

Como cualquier tipo de "join", puede emplearse una cláusula "where" que condicione la salida.

Este tipo de join no es muy utilizado.

Problema:

Un pequeño restaurante tiene almacenados los nombres y precios de sus comidas en una tabla llamada "comidas" y en
una tabla denominada "postres" los mismos datos de sus postres.

Eliminamos las tablas:

drop table comidas;


drop table postres;

Creamos las tablas:

create table comidas(


codigo number(2),
nombre varchar2(30),
precio number(4,2)
);

create table postres(


codigo number(2),
nombre varchar2(30),
precio number(4,2)
);
Ingresamos algunos registros:

insert into comidas values(1,'ravioles',5);


insert into comidas values(2,'tallarines',4);
insert into comidas values(3,'milanesa',7);
insert into comidas values(4,'cuarto de pollo',6);

insert into postres values(1,'flan',2.5);


insert into postres values(2,'porcion torta',3.5);

El restaurante quiere combinar los registros de ambas tablas para mostrar los distintos menúes que ofrece. Lo
hacemos usando un "cross join":

select c.nombre as "plato principal",


p.nombre as "postre"
from comidas c
cross join postres p;

La salida muestra cada plato combinado con cada uno de los postres. Se obtienen 8 registros.

En la siguiente combinación cruzada, agregamos una columna que calcula el precio total de cada menú:

select c.nombre as "plato principal",


p.nombre as "postre",
c.precio+p.precio as "total"
from comidas c
cross join postres p;

La salida muestra cada plato combinado con cada uno de los postres y el precio total de cada menú. Se obtienen 8
registros.

53 - Autocombinación

Dijimos que es posible combinar una tabla consigo misma.


Un pequeño restaurante tiene almacenadas sus comidas en una tabla llamada "comidas" que consta de los siguientes
campos:

- nombre varchar(20),
- precio decimal (4,2) y
- rubro char(6)-- que indica con 'plato' si es un plato principal y 'postre' si es
postre.

Podemos obtener la combinación de todos los platos empleando un "cross join" con una sola tabla:

select c1.nombre,
c2.nombre,
c1.precio+c2.precio as total
from comidas c1
cross join comidas c2;

En la consulta anterior aparecerán filas duplicadas, para evitarlo debemos emplear un "where":

select c1.nombre as "plato principal",


c2.nombre as postre,
c1.precio+c2.precio as total
from comidas c1
cross join comidas c2
where c1.rubro='plato' and
c2.rubro='postre';

En la consulta anterior se empleó un "where" que especifica que se combine "plato" con "postre".
En una autocombinación se combina una tabla con una copia de si misma. Para ello debemos utilizar 2 alias para la
tabla. Para evitar que aparezcan filas duplicadas, debemos emplear un "where".

También se puede realizar una autocombinación con "join":

select c1.nombre as "plato principal",


c2.nombre as postre,
c1.precio+c2.precio as total
from comidas c1
join comidas c2
on c1.codigo<>c2.codigo
where c1.rubro='plato' and
c2.rubro='postre';

Para que no aparezcan filas duplicadas se agrega un "where".

Problema:

Un pequeño restaurante tiene almacenados los nombres, precios y rubro de sus comidas en una tabla llamada
"comidas".

Eliminamos la tabla:

drop table comidas;

Creamos la tabla:

create table comidas(


codigo number(2),
nombre varchar2(30),
precio number(4,2),
rubro char(6),-- 'plato'=plato principal', 'postre'=postre
primary key(codigo)
);

Ingresamos algunos registros:

insert into comidas values(1,'ravioles',5,'plato');


insert into comidas values(2,'tallarines',4,'plato');
insert into comidas values(3,'milanesa',7,'plato');
insert into comidas values(4,'cuarto de pollo',6,'plato');
insert into comidas values(5,'flan',2.5,'postre');
insert into comidas values(6,'porcion torta',3.5,'postre');

Realizamos un "cross join":

select c1.nombre,
c2.nombre,
c1.precio+c2.precio as total
from comidas c1
cross join comidas c2;

Note que aparecen filas duplicadas, por ejemplo, "ravioles" se combina con "ravioles" y la combinación "ravioles- flan"
se repite como "flan- ravioles". Debemos especificar que combine el rubro "plato" con "postre":

select c1.nombre as "plato principal",


c2.nombre as postre,
c1.precio+c2.precio as total
from comidas c1
cross join comidas c2
where c1.rubro='plato' and
c2.rubro='postre';

La salida muestra cada plato combinado con cada postre, y una columna extra que calcula el total del menú.
También se puede realizar una autocombinación con "join":

select c1.nombre as "plato principal",


c2.nombre as postre,
c1.precio+c2.precio total
from comidas c1
join comidas c2
on c1.codigo<>c2.codigo
where c1.rubro='plato' and
c2.rubro='postre';

Para que no aparezcan filas duplicadas se agrega un "where".

54 - Combinaciones y funciones de agrupamiento

Podemos usar "group by" y las funciones de agrupamiento con combinaciones de tablas.

Para ver la cantidad de libros de cada editorial consultando la tabla "libros" y "editoriales", tipeamos:

select nombre as editorial,


count(*) as cantidad
from editoriales e
join libros l
on codigoeditorial=e.codigo
group by e.nombre;

Las editoriales que no tienen libros no aparecen en la salida porque empleamos un "join".

Empleamos otra función de agrupamiento con "left join". Para conocer el mayor precio de los libros de cada editorial
usamos la función "max()", hacemos un "left join" y agrupamos por nombre de la editorial:

select nombre as editorial,


max(precio) as "mayor precio"
from editoriales e
left join libros l
on codigoeditorial=e.codigo
group by nombre;

En la sentencia anterior, mostrará, para la editorial de la cual no haya libros, el valor "null" en la columna calculada.

Problema:

Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".

Eliminamos ambas tablas y las creamos:

drop table libros;


drop table editoriales;

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
codigoeditorial number(3),
precio number(5,2),
primary key(codigo)
);

create table editoriales(


codigo number(3),
nombre varchar2(20),
primary key (codigo)
);

Ingresamos algunos registros en ambas tablas:

insert into editoriales values(1,'Planeta');


insert into editoriales values(2,'Emece');
insert into editoriales values(3,'Siglo XXI');

insert into libros values(100,'El aleph','Borges',1,20);


insert into libros values(200,'Martin Fierro','Jose Hernandez',1,30);
insert into libros values(300,'Aprenda PHP','Mario Molina',3,50);
insert into libros values(400,'Uno','Richard Bach',3,15);
insert into libros values(500,'Java en 10 minutos',default,4,45);

Contamos la cantidad de libros de cada editorial consultando ambas tablas:

select nombre as editorial,


count(*) as cantidad
from editoriales e
join libros l
on codigoeditorial=e.codigo
group by e.nombre;

Note que las editoriales que no tienen libros no aparecen en la salida porque empleamos un "join".

Buscamos el libro más costoso de cada editorial con un "left join":

select nombre as editorial,


max(precio) as "mayor precio"
from editoriales e
left join libros l
on codigoeditorial=e.codigo
group by nombre;

La sentencia anterior mostrará, para la editorial de la cual no haya libros, el valor "null" en la columna calculada.

55 - Combinar más de 2 tablas

Podemos hacer un "join" con más de dos tablas.

La librería almacena los datos de sus libros en tres tablas: libros, editoriales y autores.
En la tabla "libros" un campo "codigoautor" hace referencia al autor y un campo "codigoeditorial" referencia la
editorial.

Para recuperar todos los datos de los libros empleamos la siguiente consulta:

select titulo,a.nombre,e.nombre
from autores a
join libros l
on codigoautor=a.codigo
join editoriales e
on codigoeditorial=e.codigo;

Analicemos la consulta anterior. Indicamos el nombre de la tabla luego del "from" ("autores"), combinamos esa tabla
con la tabla "libros" especificando con "on" el campo por el cual se combinarán; luego debemos hacer coincidir los
valores para el enlace con la tabla "editoriales" enlazándolas por los campos correspondientes. Utilizamos alias para
una sentencia más sencilla y comprensible.

Note que especificamos a qué tabla pertenecen los campos cuyo nombre se repiten en las tablas, esto es necesario
para evitar confusiones y ambiguedades al momento de referenciar un campo.
Los libros cuyo código de autor no se encuentra en "autores" y cuya editorial no existe en "editoriales", no aparecen
porque realizamos una combinación interna.
Podemos combinar varios tipos de join en una misma sentencia:

select titulo,a.nombre,e.nombre
from autores a
right join libros l
on codigoautor=a.codigo
left join editoriales e
on codigoeditorial=e.codigo;

En la consulta anterior solicitamos el título, autor y editorial de todos los libros que encuentren o no coincidencia con
"autores" ("right join") y a ese resultado lo combinamos con "editoriales", encuentren o no coincidencia.

Es posible realizar varias combinaciones para obtener información de varias tablas. Las tablas deben tener claves
externas relacionadas con las tablas a combinar.

En consultas en las cuales empleamos varios "join" es importante tener en cuenta el orden de las tablas y los tipos de
"join".

Problema:

Una librería almacena la información de sus libros para la venta en tres tablas, "libros", "autores" y "editoriales".

Eliminamos ambas tablas y las creamos:

drop table libros;


drop table autores;
drop table editoriales;

create table libros(


codigo number(5),
titulo varchar2(40),
codigoautor number(4) not null,
codigoeditorial number(3),
primary key(codigo)
);

create table autores(


codigo number(4),
nombre varchar2(20),
primary key (codigo)
);

create table editoriales(


codigo number(3),
nombre varchar2(20),
primary key (codigo)
);

Ingresamos algunos registros:

insert into editoriales values(1,'Planeta');


insert into editoriales values(2,'Emece');
insert into editoriales values(3,'Siglo XXI');
insert into editoriales values(4,'Norma');

insert into autores values (1,'Richard Bach');


insert into autores values (2,'Borges');
insert into autores values (3,'Jose Hernandez');
insert into autores values (4,'Mario Molina');
insert into autores values (5,'Paenza');

insert into libros values(100,'El aleph',2,2);


insert into libros values(101,'Martin Fierro',3,1);
insert into libros values(102,'Aprenda PHP',4,3);
insert into libros values(103,'Uno',1,1);
insert into libros values(104,'Java en 10 minutos',0,3);
insert into libros values(105,'Matematica estas ahi',10,null);
insert into libros values(106,'Java de la A a la Z',4,0);
Recuperamos todos los datos de los libros consultando las tres tablas:

select titulo,a.nombre as autor,e.nombre as editorial


from autores a
join libros l
on codigoautor=a.codigo
join editoriales e
on codigoeditorial=e.codigo;

Note que no aparecen los libros cuyo código de autor no se encuentra en "autores" (caso de "Java en 10 minutos" y
"Matematica estas ahi") y cuya editorial no existe en "editoriales" (caso de "Matematica estas ahi" y "Java de la A a la
Z"), esto es porque realizamos una combinación interna.

Podemos combinar varios tipos de join en una misma sentencia:

select titulo,a.nombre as autor,e.nombre as editorial


from autores a
right join libros l
on codigoautor=a.codigo
left join editoriales e
on codigoeditorial=e.codigo;

56 - Otros tipos de combinaciones

Hemos aprendido que existen varios tipos de combinaciones en Oracle:

1) combinaciones internas (inner join o simplemente join),

2) combinaciones externas (left join, right join y full join)

3) combinaciones cruzadas (cross join).

También vimos que es posible emplear varios tipos de combinaciones en una consulta, incluso puede combinarse una
tabla consigo misma.

Existen otros tipos de "join" en Oracle, que veremos rápidamente, ya que se resuelven con los que vimos
anteriormente, básicamente lo que cambia es la sintaxis.

1) combinación natural: realiza un join entre dos tablas cuando los campos por los cuales se enlazan tienen el mismo
nombre. Involucra claves primarias y foráneas.

Sintaxis:

select CAMPOS
from TABLA1
natural join TABLA2;

Ejemplo:

select titulo,nombre as editorial


from libros
natural join
editoriales;

En el ejemplo anterior la tabla "libros" combina su campo "codigoeditorial" con el campo "codigoeditorial" de
"editoriales". La cláusula "on" no aparece, este "join" no necesita condición de enlace porque Oracle busca los campos
con nombres iguales de ambas tablas (ambas tablas deben tener un único campo con idéntico nombre, si tiene más
de un campo con igual nombre, Oracle no podrá realizar el enlace y mostrará un mensaje de error).

2) combinación empleando la cláusula "using": permite especificar el campo (o los campos) por el cual se enlazarán
las tablas; los campos de ambas tablas DEBEN tener el mismo nombre y ser de tipos compatibles.
Sintaxis:

select CAMPOS
from TABLA1
join TABLA2
using (CAMPOenCOMUN);

Ejemplo:

select titulo,nombre as editorial


from libros
join editoriales
using (codigoeditorial);

En el ejemplo anterior la tabla "libros" combina su campo "codigoeditorial" con el campo "codigoeditorial" de
"editoriales". La cláusula "on" no aparece, es reemplazada por "using" seguido del nombre del campo en común por el
cual se enlazan.

3) combinación izquierda empleando "join" y el operador o modificador "(+)": Podemos obtener el mismo resultado
que un "left join" empleando "join" y el modificador "(+)", con lo cual se indica que se consideran los registros con
valor nulo. La sintaxis es la siguiente:

select CAMPOS
from TABLA1
join TABLA2
on CAMPOTABLA1=CAMPOTABLA2(+);

Es decir, se coloca el modificador "(+)" luego del campo de la tabla de la derecha para indicar que se incluyan los que
tienen valor nulo.

Las siguientes consultas retornan el mismo resultado. Una de ellas emplea "left join" y la otra un "join" con el
modificador "(+)":

select titulo,nombre as editorial


from libros l
left join editoriales l
on l.codigoeditorial = e.codigoeditorial;

select titulo,nombre as editorial


from libros l
join editoriales e
on l.codigoeditorial = e.codigoeditorial(+);

Ambas mostrarán el título y nombre de la editorial; los libros cuyo código de editorial no esté presente en
"editoriales" aparecerán con el valor "null" en la columna "editorial".

4) combinación derecha empleando "join" y el modificador "(+)": de modo similar al anterior, podemos obtener el
mismo resultado que un "right join" empleando "join" y el modificador "(+)", con lo cual se indica que se consideren
los registros con valor nulo. La sintaxis es la siguiente:

select CAMPOS
from TABLA1
join TABLA2
on CAMPOTABLA1(+)=CAMPOTABLA2;

Entonces, se coloca el modificador "(+)" luego del campo de la tabla de la izquierda para indicar que se incluyan los
que tienen valor nulo.

Las siguientes consultas retornan el mismo resultado. Una de ellas emplea "right join"· y la otra un "join" con el
modificador "(+)":

select titulo,nombre as editorial


from editoriales e
right join libros l
on e.codigoeditorial = l.codigoeditorial;

select titulo,nombre as editorial


from editoriales e
join libros l
on e.codigoeditorial(+) = l.codigoeditorial;

Ambas mostrarán el título y nombre de la editorial; las editoriales que no encuentran coincidencia en "libros",
aparecen con el valor "null" en la columna "titulo".

Si la condición de combinación es compuesta (más de un campo), DEBE colocarse el modificador "(+)" en todos los
campos que forman parte del enlace.

No se puede colocar el modificador en campos de distintas tablas. La siguiente combinación producirá un mensaje de
error:

select titulo,nombre as editorial


from libros l
join editoriales e
on l.codigoeditorial(+)= e.codigoeditorial(+);

Problema:

Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".

Eliminamos ambas tablas y las creamos:

drop table libros;


drop table editoriales;

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
codigoeditorial number(3)
);
create table editoriales(
codigoeditorial number(3),
nombre varchar2(20)
);

Ingresamos algunos registros en ambas tablas:

insert into editoriales values(1,'Planeta');


insert into editoriales values(2,'Emece');
insert into editoriales values(3,'Siglo XXI');
insert into editoriales values(null,'Norma');

insert into libros values(100,'El aleph','Borges',1);


insert into libros values(101,'Martin Fierro','Jose Hernandez',1);
insert into libros values(102,'Aprenda PHP','Mario Molina',2);
insert into libros values(103,'Java en 10 minutos',null,4);
insert into libros values(104,'El anillo del hechicero','Carol Gaskin',null);

Realizamos un natural join entre las dos tablas:

select titulo,nombre as editorial


from libros
natural join
editoriales;

En el ejemplo anterior la tabla "libros" combina su campo "codigoeditorial" con el campo "codigoeditorial" de
"editoriales".

Realizamos una combinación empleando la cláusula "using":

select titulo,nombre as editorial


from libros
join editoriales
using (codigoeditorial);
En el ejemplo anterior la tabla "libros" combina su campo "codigoeditorial" con el campo "codigoeditorial" de
"editoriales".

Realizamos una combinación izquierda y luego un "join" con el modificador "(+)"; ambas consultas retornan el mismo
resultado:

select titulo,nombre as editorial


from libros l
left join editoriales e
on l.codigoeditorial = e.codigoeditorial;

select titulo,nombre as editorial


from libros l
join editoriales e
on l.codigoeditorial = e.codigoeditorial(+);

Ambas mostrarán el título y nombre de la editorial; los libros cuyo código de editorial no esté presente en
"editoriales" aparecerán con el valor "null" en la columna "editorial".

Realizamos una combinación derecha y luego obtenemos el mismo resultado empleando "join" y el modificador "(+)":

select titulo,nombre as editorial


from editoriales e
right join libros l
on e.codigoeditorial = l.codigoeditorial;

select titulo,nombre as editorial


from editoriales e
join libros l
on e.codigoeditorial(+) = l.codigoeditorial;

Ambas mostrarán el título y nombre de la editorial; las editoriales que no encuentran coincidencia en "libros",
aparecen con el valor "null" en la columna "titulo".

Si intentamos emplear el modificador en campos de distintas tablas Oracle mostrará un mensaje de error:

select titulo,nombre as editorial


from libros l
join editoriales e
on l.codigoeditorial(+)= e.codigoeditorial(+);

57 - Clave foránea

Un campo que no es clave primaria en una tabla y sirve para enlazar sus valores con otra tabla en la cual es clave
primaria se denomina clave foránea, externa o ajena.

En el ejemplo de la librería en que utilizamos las tablas "libros" y "editoriales" con estos campos:

libros: codigo (clave primaria), titulo, autor, codigoeditorial, precio y


editoriales: codigo (clave primaria), nombre.

el campo "codigoeditorial" de "libros" es una clave foránea, se emplea para enlazar la tabla "libros" con "editoriales" y
es clave primaria en "editoriales" con el nombre "codigo".

Las claves foráneas y las claves primarias deben ser del mismo tipo para poder enlazarse. Si modificamos una,
debemos modificar la otra para que los valores se correspondan.

Cuando alteramos una tabla, debemos tener cuidado con las claves foráneas. Si modificamos el tipo, longitud o
atributos de una clave foránea, ésta puede quedar inhabilitada para hacer los enlaces.

Entonces, una clave foránea es un campo (o varios) empleados para enlazar datos de 2 tablas, para establecer un
"join" con otra tabla en la cual es clave primaria.
58 - Restricciones (foreign key)

Hemos visto que una de las alternativas que Oracle ofrece para asegurar la integridad de datos es el uso de
restricciones (constraints). Aprendimos que las restricciones se establecen en tablas y campos asegurando que los
datos sean válidos y que las relaciones entre las tablas se mantengan.

Vimos tres tipos de restricciones:

primary key, unique y check. Ahora veremos "foreign key".

Con la restricción "foreign key" se define un campo (o varios) cuyos valores coinciden con la clave primaria de la
misma tabla o de otra, es decir, se define una referencia a un campo con una restricción "primary key" o "unique" de
la misma tabla o de otra.

La integridad referencial asegura que se mantengan las referencias entre las claves primarias y las externas. Por
ejemplo, controla que si se agrega un código de editorial en la tabla "libros", tal código exista en la tabla
"editoriales".

También controla que no pueda eliminarse un registro de una tabla ni modificar la clave primaria si una clave
externa hace referencia al registro. Por ejemplo, que no se pueda eliminar o modificar un código de "editoriales" si
existen libros con dicho código.

La siguiente es la sintaxis parcial general para agregar una restricción "foreign key":

alter table NOMBRETABLA1


add constraint NOMBRERESTRICCION
foreign key (CAMPOCLAVEFORANEA)
references NOMBRETABLA2 (CAMPOCLAVEPRIMARIA);

Analicémosla:

- NOMBRETABLA1 referencia el nombre de la tabla a la cual le aplicamos la restricción,

- NOMBRERESTRICCION es el nombre que le damos a la misma,

- luego de "foreign key", entre paréntesis se coloca el campo de la tabla a la que le aplicamos la restricción que será
establecida como clave foránea,

- luego de "references" indicamos el nombre de la tabla referenciada y el campo que es clave primaria en la misma, a
la cual hace referencia la clave foránea. El campo de la tabla referenciada debe tener definida una restricción
"primary key" o "unique"; si no la tiene, aparece un mensaje de error.

Para agregar una restricción "foreign key" al campo "codigoeditorial" de "libros", tipeamos:

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo);

En el ejemplo implementamos una restricción "foreign key" para asegurarnos que el código de la editorial de la de la
tabla "libros" ("codigoeditorial") esté asociada con un código válido en la tabla "editoriales" ("codigo").

Cuando agregamos cualquier restricción a una tabla que contiene información, Oracle controla los datos existentes
para confirmar que cumplen con la restricción, si no los cumple, la restricción no se aplica y aparece un mensaje de
error. Por ejemplo, si intentamos agregar una restricción "foreign key" a la tabla "libros" y existe un libro con un valor
de código para editorial que no existe en la tabla "editoriales", la restricción no se agrega.

Actúa en inserciones. Si intentamos ingresar un registro (un libro) con un valor de clave foránea (codigoeditorial) que
no existe en la tabla referenciada (editoriales), Oracle muestra un mensaje de error. Si al ingresar un registro (un
libro), no colocamos el valor para el campo clave foránea (codigoeditorial), almacenará "null", porque esta
restricción permite valores nulos (a menos que se haya especificado lo contrario al definir el campo).
Actúa en eliminaciones y actualizaciones. Si intentamos eliminar un registro o modificar un valor de clave primaria de
una tabla si una clave foránea hace referencia a dicho registro, Oracle no lo permite (excepto si se permite la acción
en cascada, tema que veremos posteriormente). Por ejemplo, si intentamos eliminar una editorial a la que se hace
referencia en "libros", aparece un mensaje de error.

Esta restricción (a diferencia de "primary key" y "unique") no crea índice automáticamente.

La cantidad y tipo de datos de los campos especificados luego de "foreign key" DEBEN coincidir con la cantidad y tipo
de datos de los campos de la cláusula "references".

Esta restricción se puede definir dentro de la misma tabla (lo veremos más adelante) o entre distintas tablas.

Una tabla puede tener varias restricciones "foreign key".

No se puede eliminar una tabla referenciada en una restricción "foreign key", aparece un mensaje de error.

Una restriccion "foreign key" no puede modificarse, debe eliminarse (con "alter table" y "drop constraint") y volverse a
crear.

Las restricciones "foreign key" se eliminan automáticamente al eliminar la tabla en la que fueron definidas.

Para ver información acerca de esta restricción podemos consultar los diccionarios "user_constraints" y
"user_cons_columns".

Problema:

Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".

Eliminamos ambas tablas:

drop table libros;


drop table editoriales;

Creamos las tablas:

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
codigoeditorial number(3)
);
create table editoriales(
codigo number(3),
nombre varchar2(20)
);

Ingresamos algunos registros en ambas tablas:

insert into editoriales values(1,'Emece');


insert into editoriales values(2,'Planeta');
insert into editoriales values(3,'Siglo XXI');

insert into libros values(100,'El aleph','Borges',1);


insert into libros values(101,'Martin Fierro','Jose Hernandez',2);
insert into libros values(102,'Aprenda PHP','Mario Molina',5);

Intentamos establecer una restricción "foreign key" sobre "codigoeditorial":

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo);

Mensaje de error; pues el campo "codigo" de la tabla "editoriales" no fue definida clave primaria ni única.
Agregamos una restricción "primary key" sobre "codigo" de "editoriales":

alter table editoriales


add constraint PK_editoriales
primary key (codigo);

Intentamos nuevamente establecer una restricción "foreign key" sobre "codigoeditorial":

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo);

Mensaje de error. Oracle controla que los datos existentes no violen la restricción que intentamos establecer, como
existe un valor de "codigoeditorial" inexistente en "editoriales", la restricción no puede establecerse.

Eliminamos el registro que infringe la regla:

delete from libros where codigoeditorial=5;

Ahora si podemos establecer una restricción "foreign key" sobre "codigoeditorial":

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo);

Veamos las restricciones de "libros" consultando "user_constraints":

select constraint_name, constraint_type


from user_constraints
where table_name='LIBROS';

aparece la restricción "FK_libros_codigoeditorial" indicando que es una "foreign key" con el caracter "R" en el tipo de
restricción.

Consultamos "user_cons_columns":

select constraint_name, column_name


from user_cons_columns
where table_name='LIBROS';

Aparece la siguiente tabla:

CONSTRAINT_NAME COLUMN_NAME
-------------------------------------------
FK_LIBROS_CODIGOEDITORIAL EDITORIALEDITORIAL

Veamos las restricciones de "editoriales":

select constraint_name, constraint_type


from user_constraints
where table_name='EDITORIALES';

aparece la restricción "primary key".

Ingresamos un libro sin especificar un valor para el código de editorial:

insert into libros values(103,'El experto en laberintos','Gaskin',default);

Veamos todos los registros de "libros":

select *from libros;


Note que en "codigoeditorial" almacenó "null", porque esta restricción permite valores nulos (a menos que se haya
especificado lo contrario al definir el campo).

Intentamos agregar un libro con un código de editorial inexistente en "editoriales":

insert into libros values(104,'El anillo del hechicero','Gaskin',8);

Nos muestra un mensaje indicando que la restricción FK_LIBROS_EDITORIAL está siendo violada, que no encuentra el
valor de clave primaria en "editoriales".

Intentamos eliminar una editorial cuyo código esté presente en "libros":

delete from editoriales where codigo=2;

Un mensaje nos informa que la restricción de clave externa está siendo violada, existen registros que hacen
referencia al que queremos eliminar.

Intente eliminar la tabla "editoriales":

drop table editoriales;

Un mensaje de error indica que la acción no puede realizarse porque la tabla es referenciada por una "foreign key".

59 - Restricciones foreign key en la misma tabla

La restricción "foreign key", que define una referencia a un campo con una restricción "primary key" o "unique" se
puede definir entre distintas tablas (como hemos aprendido) o dentro de la misma tabla.

Veamos un ejemplo en el cual definimos esta restricción dentro de la misma tabla.

Una mutual almacena los datos de sus afiliados en una tabla llamada "afiliados". Algunos afiliados inscriben a sus
familiares. La tabla contiene un campo que hace referencia al afiliado que lo incorporó a la mutual, del cual
dependen.

La estructura de la tabla es la siguiente:

create table afiliados(


numero number(5),
documento char(8) not null,
nombre varchar2(30),
afiliadotitular number(5),
primary key (documento),
unique (numero)
);

En caso que un afiliado no haya sido incorporado a la mutual por otro afiliado, el campo "afiliadotitular" almacenará
"null".

Establecemos una restricción "foreign key" para asegurarnos que el número de afiliado que se ingrese en el campo
"afiliadotitular" exista en la tabla "afiliados":

alter table afiliados


add constraint FK_afiliados_afiliadotitular
foreign key (afiliadotitular)
references afiliados (numero);

La sintaxis es la misma, excepto que la tabla se autoreferencia.

Luego de aplicar esta restricción, cada vez que se ingrese un valor en el campo "afiliadotitular", Oracle controlará
que dicho número exista en la tabla, si no existe, mostrará un mensaje de error.
Si intentamos eliminar un afiliado que es titular de otros afiliados, no se podrá hacer, a menos que se haya
especificado la acción en cascada (próximo tema).

Si intentamos modificar un afiliado que es titular de otros afiliados, no se podrá hacer, a menos que se haya
especificado la acción en cascada para actualizaciones (próximo tema).

Problema:

Una mutual almacena los datos de sus afiliados en una tabla llamada "afiliados". Algunos afiliados inscriben a sus
familiares. La tabla contiene un campo que hace referencia al afiliado que lo incorporó a la mutual, del cual
dependen.

Eliminamos la tabla "afiliados" y la creamos:

drop table afiliados;

create table afiliados(


numero number(5),
documento char(8) not null,
nombre varchar2(30),
afiliadotitular number(5),
primary key (documento),
unique (numero)
);

En caso que un afiliado no haya sido incorporado a la mutual por otro afiliado, el campo "afiliadotitular" almacenará
"null".

Establecemos una restricción "foreign key" para asegurarnos que el número de afiliado que se ingrese en el campo
"afiliadotitular" exista en la tabla "afiliados":

alter table afiliados


add constraint FK_afiliados_afiliadotitular
foreign key (afiliadotitular)
references afiliados (numero);

Ingresamos algunos registros:

insert into afiliados values(1,'22222222','Perez Juan',null);


insert into afiliados values(2,'23333333','Garcia Maria',null);
insert into afiliados values(3,'24444444','Lopez Susana',null);
insert into afiliados values(4,'30000000','Perez Marcela',1);
insert into afiliados values(5,'31111111','Garcia Luis',2);
insert into afiliados values(6,'32222222','Garcia Maria',2);

Podemos eliminar un afiliado, siempre que no haya otro afiliado que haga referencia a él en "afiliadotitular", es
decir, si el "numero" del afiliado está presente en algún registro en el campo "afiliadotitular":

delete from afiliados where numero=5;

Veamos la información referente a "afiliados":

select constraint_name, constraint_type,search_condition


from user_constraints
where table_name='AFILIADOS';

Aparece la siguiente tabla:

CONSTRAINT_NAME CONSTRAINT_TYPE SEARCH_CONDITION


---------------------------------------------------------------------------------
SYS_C004816 C "DOCUMENTO" IS NOT
NULL
SYS_C004817 P
SYS_C004818 U
FK_AFILIADOS_AFILIADOTITULAR R

Los nombres de las tres primeras restricciones son dadas por Oracle.
La tabla tiene una restricción "check", una "primary key", una "unique" y una "foreign key".

Veamos sobre qué campos están establecidas:

select *from user_cons_columns


where table_name='AFILIADOS';

Aparece la siguiente tabla:

CONSTRAINT_NAME COLUMN_NAME POSITION


----------------------------------------------------------
SYS_C004818 NUMERO 1
SYS_C004817 DOCUMENTO 1
SYS_C004816 DOCUMENTO
FK_AFILIADOS_AFILIADOTITULAR AFILIADOTITULAR 1

Nos informa que la restricción única está establecida sobre "numero"; la "primary key" sobre "documento", la
restricción de chequeo sobre "documento" y la "foreign key" sobre "afiliadotitular".

Ingresamos un nuevo registro con un valor para "afiliadotitular" existente:

insert into afiliados values(7,'33333333','Lopez Juana',3);

Intentamos ingresar un nuevo registro con un valor para "afiliadotitular" inexistente:

insert into afiliados values(8,'34555666','Marconi Julio',9);

Oracle no lo permite porque se violaría la restricción "foreign key".

Igresamos un nuevo registro con el valor "null" para "afiliadotitular":

insert into afiliados values(8,'34555666','Marconi Julio',null);

60 - Restricciones foreign key (eliminación)

Podemos eliminar una restricción "foreign key" con "alter table". La sintaxis básica es la misma que para cualquier
otra restricción:

alter table TABLA


drop constraint NOMBRERESTRICCION;

Eliminamos la restricción "foreign key" de "libros":

alter table libros


drop constraint FK_libros_codigoeditorial;

No se puede eliminar una tabla si una restricción "foreign key" hace referencia a ella.

Cuando eliminamos una tabla que tiene una restricción "foreign key", la restricción también se elimina.

61 - Restricciones foreign key deshabilitar y validar

Aprendimos (cuando vimos los otros tipos de restricciones) que si agregamos una restricción a una tabla que contiene
datos, Oracle los controla para asegurarse que cumplen con la restricción y que es posible deshabilitar esta
comprobación. Lo hacemos incluyendo la opción "novalidate" en la instrucción "alter table"; en tal caso, La restricción
no se aplica en los datos existentes, pero si intentamos ingresar un nuevo valor que no cumpla la restricción (o
actualizarlo), Oracle no lo permite.

En el siguiente ejemplo agregamos una restricción "foreign key" sobre el campo "codigoeditorial" de "libros"
especificando que no valide los datos existentes:

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales novalidate;

La restricción no se aplica en los datos existentes, pero si intentamos ingresar un nuevo registro en "libros" cuyo
código de editorial no exista en "editoriales", Oracle no lo permitirá.

Para saber si una restricción está validada o no, podemos consultar el catálogo "user_constraints" y fijarnos lo que
informa la columna "validated".

También aprendimos que podemos deshabilitar las restricciones para agregar o actualizar datos sin comprobarla.
Para evitar la comprobación de datos en inserciones y actualizaciones agregamos "disable" en la instrucción "alter
table".

En el ejemplo siguiente deshabilitamos la restricción "FK_libros_codigoeditorial" para poder ingresar un valor que
infrija la restricción:

alter table libros


disable validate
constraint FK_libros_codigoeditorial;

Para habilitar una restricción "foreign key" deshabilitada se ejecuta la misma instrucción pero con la cláusula
"enable".

Por defecto (si no se especifica) las opciones son "validate" (es decir, controla los datos existentes) y "enable"
(controla futuros ingresos y actualizaciones).

Para saber si una restricción está habilitada o no, podemos consultar el catálogo "user_constraints" y fijarnos lo que
informa la columna "status".

Podemos habilitar una restricción "foreign key" con "enable" y "novalidate", en tal caso Oracle habilita la restricción
para futuros ingresos y actualizaciones y NO valida los datos existentes.

Entonces, "enable" o "disable" activa o desactiva la restricción para los nuevos datos ("enable" es la opción
predeterminada si no se especifica); "validate" o "novalidate" es la opción para validar la restricción en los datos
existentes ("validate" es la predeterminada si se omite).

La sintaxis básica al agregar la restriccción "foreign key" es la siguiente:

alter table NOMBRETABLA1


add constraint NOMBRECONSTRAINT
foreign key (CAMPOCLAVEFORANEA)
references NOMBRETABLA2 (CAMPOCLAVEPRIMARIA)
ESTADO VALIDACION;

La sintaxis para modificar una restricción es:

alter table NOMBRETABLA


ESTADO VALIDACION
constraint NOMBRERESTRICCION;

Problema:

Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".

Eliminamos ambas tablas:


drop table libros;
drop table editoriales;

Creamos las tablas:

create table libros(


codigo number(5),
titulo varchar2(40),
codigoeditorial number(3),
primary key (codigo)
);
create table editoriales(
codigo number(3),
nombre varchar2(20),
primary key (codigo)
);

Ingresamos algunos registros:

insert into editoriales values(1,'Planeta');


insert into editoriales values(2,'Emece');
insert into editoriales values(3,'Paidos');

insert into libros values(1,'Uno',1);


insert into libros values(2,'El aleph',2);
insert into libros values(3,'Aprenda PHP',5);

Agregamos una restricción "foreign key" a la tabla "libros" para evitar que se ingresen códigos de editoriales
inexistentes en "editoriales". Incluimos la opción "novalidate" para evitar la comprobación de la restricción en los
datos existentes (note que hay un libro que tiene un código de editorial inválido):

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo) novalidate;

La deshabilitación de la comprobación de la restricción no afecta a los siguientes ingresos, modificaciones y


actualizaciones. Para poder ingresar, modificar o eliminar datos a una tabla sin que Oracle compruebe la restricción
debemos deshabilitarla:

alter table libros


disable novalidate
constraint FK_LIBROS_CODIGOEDITORIAL;

Veamos si la restricción está habilitada o no:

select constraint_name, constraint_type, status, validated


from user_constraints
where table_name='LIBROS';

En la columna "status" de la restricción "foreign key" aparece "Disabled" y en "Validated" muestra "not validated".

Ahora podemos ingresar un registro en "libros" con código de editorial inválido:

insert into libros values(4,'Ilusiones',6);

Habilitamos la restricción:

alter table libros


enable novalidate constraint FK_libros_codigoeditorial;

Veamos si la restricción está habilitada o no y si valida los datos existentes:

select constraint_name, constraint_type, status, validated


from user_constraints where table_name='LIBROS';

En la columna "status" aparece "Enabled" y en "Validated" "not validate".


Intentamos alterar la restricción para que se validen los datos existentes:

alter table libros


enable validate constraint FK_libros_codigoeditorial;

Oracle mostrará un mensaje indicando que no se pueden validar los datos existentes porque existen valores inválidos.

Truncamos la tabla y alteramos la restricción:

truncate table libros;

alter table libros


enable validate constraint FK_libros_codigoeditorial;

Solicitamos información sobre la restricción:

select constraint_name, constraint_type, status, validated


from user_constraints where table_name='LIBROS';

En la columna "status" aparece "Enabled" y en "Validated" "Validate".

62 - Restricciones foreign key (acciones)

Continuamos con la restricción "foreign key". Si intentamos eliminar un registro de la tabla referenciada por una
restricción "foreign key" cuyo valor de clave primaria existe referenciada en la tabla que tiene dicha restricción, la
acción no se ejecuta y aparece un mensaje de error. Esto sucede porque, por defecto, para eliminaciones, la opción
de la restricción "foreign key" es "no action".

La restricción "foreign key" tiene la cláusula "on delete", que son opcionales. Esta cláusula especifica cómo debe
actuar Oracle frente a eliminaciones en las tablas referenciadas en la restricción.

Las opciones para estas cláusulas son las siguientes:

- "set null": indica que si eliminamos un registro de la tabla referenciada (TABLA2) cuyo valor existe en la tabla
principal (TABLA1), dicho registro se elimine y los valores coincidentes en la tabla principal se seteen a "null".

- "cascade": indica que si eliminamos un registro de la tabla referenciada en una "foreign key" (TABLA2), los registros
coincidentes en la tabla principal (TABLA1), también se eliminen; es decir, si eliminamos un registro al cual una clave
foránea referencia, dicha eliminación se extiende a la otra tabla (integridad referencial en cascada).

- "no action": es la predeterminada; indica que si se intenta eliminar un registro de la tabla referenciada por una
"foreign key", Oracle no lo permita y muestre un mensaje de error. Se establece omitiendo la cláusula "on delete" al
establecer la restricción.

La sintaxis completa paar agregar esta restricción a una tabla es la siguiente:

alter table TABLA1


add constraint NOMBRERESTRICCION
foreign key (CAMPOCLAVEFORANEA)
references TABLA2(CAMPOCLAVEPRIMARIA)
on delete OPCION;

Veamos un ejemplo. Definimos una restricción "foreign key" a la tabla "libros" estableciendo el campo
"codigoeditorial" como clave foránea que referencia al campo "codigo" de la tabla "editoriales". La tabla "editoriales"
tiene como clave primaria el campo "codigo". Especificamos la acción en cascada para las eliminaciones:

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo)
on delete cascade;
Si luego de establecer la restricción anterior, eliminamos una editorial de "editoriales" cuyo valor de código está
presente en "libros", se elimina dicha editorial y todos los libros de tal editorial.

Si consultamos "user_constraints", en la columna "delete_rule" mostrará "cascade".

Para definir una restricción "foreign key" sobre la tabla "libros" estableciendo el campo "codigoeditorial" como clave
foránea que referencia al campo "codigo" de la tabla "editoriales" especificando la acción de seteo a "null" tipeamos:

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo)
on delete set null;

Si luego de establecer la restricción anterior, eliminamos una editorial de "editoriales" cuyo valor de código está
presente en "libros", se elimina dicha editorial y todos los valores de libros que coinciden con tal editorial se setean a
null. Si consultamos "user_constraints", en la columna "delete_rule" mostrará "set null".

Sintetizando, si al agregar una restricción "foreign key":

- no se especifica acción para eliminaciones, y se intenta eliminar un registro de la tabla referenciada en la "foreign
key" (editoriales) cuyo valor de clave primaria (codigo) existe en la tabla principal (libros), la acción no se realiza.

- se especifica "cascade" para eliminaciones ("on delete cascade") y elimina un registro de la tabla referenciada
(editoriales) cuyo valor de clave primaria (codigo) existe en la tabla principal(libros), la eliminación de la tabla
referenciada (editoriales) se realiza y se eliminan de la tabla principal (libros) todos los registros cuyo valor coincide
con el registro eliminado de la tabla referenciada (editoriales).

- se especifica acción para eliminaciones ("on delete set null") y se elimina un registro de la tabla referenciada en la
"foreign key" (editoriales) cuyo valor de clave primaria (codigo) existe en la tabla principal (libros), la acción se
realiza y se setean a "null" todos los valores coincidentes en la tabla principal (libros).

La restricción "foreign key" NO tiene una cláusula para especificar acciones para actualizaciones.

Si intentamos actualizar un registro de la tabla referenciada por una restricción "foreign key" cuyo valor de clave
primaria existe referenciada en la tabla que tiene dicha restricción, la acción no se ejecuta y aparece un mensaje de
error. Esto sucede porque, por defecto (y como única opción), para actualizaciones existe "no action".

Continuamos con la restricción "foreign key". Si intentamos eliminar un registro de la tabla referenciada por una
restricción "foreign key" cuyo valor de clave primaria existe referenciada en la tabla que tiene dicha restricción, la
acción no se ejecuta y aparece un mensaje de error. Esto sucede porque, por defecto, para eliminaciones, la opción
de la restricción "foreign key" es "no action".

La restricción "foreign key" tiene la cláusula "on delete", que son opcionales. Esta cláusula especifica cómo debe
actuar Oracle frente a eliminaciones en las tablas referenciadas en la restricción.

Las opciones para estas cláusulas son las siguientes:

- "set null": indica que si eliminamos un registro de la tabla referenciada (TABLA2) cuyo valor existe en la tabla
principal (TABLA1), dicho registro se elimine y los valores coincidentes en la tabla principal se seteen a "null".

- "cascade": indica que si eliminamos un registro de la tabla referenciada en una "foreign key" (TABLA2), los registros
coincidentes en la tabla principal (TABLA1), también se eliminen; es decir, si eliminamos un registro al cual una clave
foránea referencia, dicha eliminación se extiende a la otra tabla (integridad referencial en cascada).

- "no action": es la predeterminada; indica que si se intenta eliminar un registro de la tabla referenciada por una
"foreign key", Oracle no lo permita y muestre un mensaje de error. Se establece omitiendo la cláusula "on delete" al
establecer la restricción.

La sintaxis completa paar agregar esta restricción a una tabla es la siguiente:

alter table TABLA1


add constraint NOMBRERESTRICCION
foreign key (CAMPOCLAVEFORANEA)
references TABLA2(CAMPOCLAVEPRIMARIA)
on delete OPCION;

Veamos un ejemplo. Definimos una restricción "foreign key" a la tabla "libros" estableciendo el campo
"codigoeditorial" como clave foránea que referencia al campo "codigo" de la tabla "editoriales". La tabla "editoriales"
tiene como clave primaria el campo "codigo". Especificamos la acción en cascada para las eliminaciones:

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo)
on delete cascade;

Si luego de establecer la restricción anterior, eliminamos una editorial de "editoriales" cuyo valor de código está
presente en "libros", se elimina dicha editorial y todos los libros de tal editorial.

Si consultamos "user_constraints", en la columna "delete_rule" mostrará "cascade".

Para definir una restricción "foreign key" sobre la tabla "libros" estableciendo el campo "codigoeditorial" como clave
foránea que referencia al campo "codigo" de la tabla "editoriales" especificando la acción de seteo a "null" tipeamos:

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo)
on delete set null;

Si luego de establecer la restricción anterior, eliminamos una editorial de "editoriales" cuyo valor de código está
presente en "libros", se elimina dicha editorial y todos los valores de libros que coinciden con tal editorial se setean a
null. Si consultamos "user_constraints", en la columna "delete_rule" mostrará "set null".

Sintetizando, si al agregar una restricción "foreign key":

- no se especifica acción para eliminaciones, y se intenta eliminar un registro de la tabla referenciada en la "foreign
key" (editoriales) cuyo valor de clave primaria (codigo) existe en la tabla principal (libros), la acción no se realiza.

- se especifica "cascade" para eliminaciones ("on delete cascade") y elimina un registro de la tabla referenciada
(editoriales) cuyo valor de clave primaria (codigo) existe en la tabla principal(libros), la eliminación de la tabla
referenciada (editoriales) se realiza y se eliminan de la tabla principal (libros) todos los registros cuyo valor coincide
con el registro eliminado de la tabla referenciada (editoriales).

- se especifica acción para eliminaciones ("on delete set null") y se elimina un registro de la tabla referenciada en la
"foreign key" (editoriales) cuyo valor de clave primaria (codigo) existe en la tabla principal (libros), la acción se
realiza y se setean a "null" todos los valores coincidentes en la tabla principal (libros).

La restricción "foreign key" NO tiene una cláusula para especificar acciones para actualizaciones.

Si intentamos actualizar un registro de la tabla referenciada por una restricción "foreign key" cuyo valor de clave
primaria existe referenciada en la tabla que tiene dicha restricción, la acción no se ejecuta y aparece un mensaje de
error. Esto sucede porque, por defecto (y como única opción), para actualizaciones existe "no action".

Continuamos con la restricción "foreign key". Si intentamos eliminar un registro de la tabla referenciada por una
restricción "foreign key" cuyo valor de clave primaria existe referenciada en la tabla que tiene dicha restricción, la
acción no se ejecuta y aparece un mensaje de error. Esto sucede porque, por defecto, para eliminaciones, la opción
de la restricción "foreign key" es "no action".

La restricción "foreign key" tiene la cláusula "on delete", que son opcionales. Esta cláusula especifica cómo debe
actuar Oracle frente a eliminaciones en las tablas referenciadas en la restricción.

Las opciones para estas cláusulas son las siguientes:


- "set null": indica que si eliminamos un registro de la tabla referenciada (TABLA2) cuyo valor existe en la tabla
principal (TABLA1), dicho registro se elimine y los valores coincidentes en la tabla principal se seteen a "null".

- "cascade": indica que si eliminamos un registro de la tabla referenciada en una "foreign key" (TABLA2), los registros
coincidentes en la tabla principal (TABLA1), también se eliminen; es decir, si eliminamos un registro al cual una clave
foránea referencia, dicha eliminación se extiende a la otra tabla (integridad referencial en cascada).

- "no action": es la predeterminada; indica que si se intenta eliminar un registro de la tabla referenciada por una
"foreign key", Oracle no lo permita y muestre un mensaje de error. Se establece omitiendo la cláusula "on delete" al
establecer la restricción.

La sintaxis completa paar agregar esta restricción a una tabla es la siguiente:

alter table TABLA1


add constraint NOMBRERESTRICCION
foreign key (CAMPOCLAVEFORANEA)
references TABLA2(CAMPOCLAVEPRIMARIA)
on delete OPCION;

Veamos un ejemplo. Definimos una restricción "foreign key" a la tabla "libros" estableciendo el campo
"codigoeditorial" como clave foránea que referencia al campo "codigo" de la tabla "editoriales". La tabla "editoriales"
tiene como clave primaria el campo "codigo". Especificamos la acción en cascada para las eliminaciones:

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo)
on delete cascade;

Si luego de establecer la restricción anterior, eliminamos una editorial de "editoriales" cuyo valor de código está
presente en "libros", se elimina dicha editorial y todos los libros de tal editorial.

Si consultamos "user_constraints", en la columna "delete_rule" mostrará "cascade".

Para definir una restricción "foreign key" sobre la tabla "libros" estableciendo el campo "codigoeditorial" como clave
foránea que referencia al campo "codigo" de la tabla "editoriales" especificando la acción de seteo a "null" tipeamos:

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo)
on delete set null;

Si luego de establecer la restricción anterior, eliminamos una editorial de "editoriales" cuyo valor de código está
presente en "libros", se elimina dicha editorial y todos los valores de libros que coinciden con tal editorial se setean a
null. Si consultamos "user_constraints", en la columna "delete_rule" mostrará "set null".

Sintetizando, si al agregar una restricción "foreign key":

- no se especifica acción para eliminaciones, y se intenta eliminar un registro de la tabla referenciada en la "foreign
key" (editoriales) cuyo valor de clave primaria (codigo) existe en la tabla principal (libros), la acción no se realiza.

- se especifica "cascade" para eliminaciones ("on delete cascade") y elimina un registro de la tabla referenciada
(editoriales) cuyo valor de clave primaria (codigo) existe en la tabla principal(libros), la eliminación de la tabla
referenciada (editoriales) se realiza y se eliminan de la tabla principal (libros) todos los registros cuyo valor coincide
con el registro eliminado de la tabla referenciada (editoriales).

- se especifica acción para eliminaciones ("on delete set null") y se elimina un registro de la tabla referenciada en la
"foreign key" (editoriales) cuyo valor de clave primaria (codigo) existe en la tabla principal (libros), la acción se
realiza y se setean a "null" todos los valores coincidentes en la tabla principal (libros).

La restricción "foreign key" NO tiene una cláusula para especificar acciones para actualizaciones.

Si intentamos actualizar un registro de la tabla referenciada por una restricción "foreign key" cuyo valor de clave
primaria existe referenciada en la tabla que tiene dicha restricción, la acción no se ejecuta y aparece un mensaje de
error. Esto sucede porque, por defecto (y como única opción), para actualizaciones existe "no action".
Problema:

Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".

Eliminamos ambas tablas:

drop table libros;


drop table editoriales;

Creamos las tablas:

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
codigoeditorial number(3),
primary key (codigo)
);
create table editoriales(
codigo number(3),
nombre varchar2(20),
primary key (codigo)
);

Ingresamos algunos registros en ambas tablas:

insert into editoriales values(1,'Emece');


insert into editoriales values(2,'Planeta');
insert into editoriales values(3,'Siglo XXI');

insert into libros values(1,'El aleph','Borges',1);


insert into libros values(2,'Martin Fierro','Jose Hernandez',2);
insert into libros values(3,'Aprenda PHP','Mario Molina',2);
insert into libros values(4,'El anillo del hechicero','Gaskin',3);

Establecemos una restricción "foreign key" para evitar que se ingrese en "libros" un código de editorial inexistente en
"editoriales" con la opción "on cascade" para eliminaciones:

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo)
on delete cascade;

Consultamos "user_constraints":

select constraint_name, constraint_type, delete_rule


from user_constraints
where table_name='LIBROS';

En la columna "delete_rule" de la restricción "foreign key" mostrará "cascade".

Si eliminamos una editorial, se borra tal editorial de "editoriales" y todos los registros de "libros" de dicha editorial:

delete from editoriales where codigo=1;

Veamos si la eliminación se extendió a "libros":

select *from libros;

El libro "El aleph", de la editorial con código 1 se ha eliminado.

Eliminamos la restricción "foreign key" de "libros":

alter table libros


drop constraint FK_LIBROS_CODIGOEDITORIAL;
Establecemos una restricción "foreign key" sobre "codigoeditorial" de "libros" con la opción "set null" para
eliminaciones:

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo)
on delete set null;

Consultamos "user_constraints":

select constraint_name, constraint_type, delete_rule


from user_constraints
where table_name='LIBROS';

En la columna "delete_rule" de la restricción "foreign key" mostrará "set null".

Si eliminamos una editorial cuyo código está presente en "libros", se borra tal editorial de "editoriales" y todos los
registros de "libros" de dicha editorial se setean con el valor "null":

delete from editoriales where codigo=2;


select *from libros;

Ahora, los libros "Martin Fierro" y "Aprenda PHP" tiene valor nulo en "codigoeditorial".

Eliminamos la restricción "foreign key" de "libros":

alter table libros


drop constraint FK_LIBROS_CODIGOEDITORIAL;

Establecemos una restricción "foreign key" sobre "codigoeditorial" de "libros" sin especificar opción para
eliminaciones:

alter table libros


add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo);

Consultamos "user_constraints":

select constraint_name, constraint_type, delete_rule


from user_constraints
where table_name='LIBROS';

En la columna "delete_rule" de la restricción "foreign key" mostrará "no action".

Intentamos eliminar una editorial cuyo código esté presente en "libros":

delete from editoriales where codigo=3;

Un mensaje de error indica que la acción no se ha realizado porque existen registros coincidentes.

63 - Información de user_constraints

El diccionario "user_constraints" devuelve mucha información referente a las restricciones, las que estudiamos son las
siguientes columnas:

- owner: propietario de la restricción;

- constraint_name: nombre de la restricción;


- constraint_type: tipo de restricción. Si es una restricción "primary key" aparece "P", si es de control, "C", si es única,
"U", si es una "foreign key" "R";

- table_name: nombre de la tabla sobre la cual está aplicada la restricción;

- search_condition: solamente es aplicable a restricciones de control; indica la condición de chequeo a cumplirse.

- delete_rule: solamente aplicable a restricciones "foreign key". Puede contener 3 valores: 1) "set null": indica que si
eliminamos un registro de la tabla referenciada (TABLA2) cuyo valor existe en la tabla principal (TABLA1), dicho
registro se elimina y los valores coincidentes en la tabla principal se seteen a "null"; 2) "cascade": indica que si
eliminamos un registro de la tabla referenciada en una "foreign key" (TABLA2), los registros coincidentes en la tabla
principal (TABLA1), también se eliminan; 3) "no action": indica que si se intenta eliminar un registro de la tabla
referenciada por una "foreign key", Oracle no lo permite.

- status: indica si está habilitada (enabled) para futuras inserciones y actualizaciones o deshabilitada (disabled)

- validated: indica si valida los datos existentes en la tabla (validated) o no (no validate).

64 - Restricciones al crear la tabla

Hasta el momento hemos agregado restricciones a tablas existentes con "alter table"; también pueden establecerse al
momento de crear una tabla (en la instrucción "create table").

En el siguiente ejemplo creamos la tabla "libros" con varias restricciones:

create table libros(


codigo number(5),
titulo varchar2(40),
codigoautor number(4),
codigoeditorial number(3),
precio number(5,2) default 0,
constraint PK_libros_codigo
primary key (codigo),
constraint UQ_libros_tituloautor
unique (titulo,codigoautor),
constraint CK_libros_codigoeditorial
check (codigoeditorial is not null),
constraint FK_libros_editorial
foreign key (codigoeditorial)
references editoriales(codigo)
on delete cascade,
constraint FK_libros_autores
foreign key (codigoautor)
references autores(codigo)
on delete set null,
constraint CK_libros_preciononulo
check (precio is not null) disable,
constraint CK_precio_positivo
check (precio>=0)
);

En el ejemplo definimos las siguientes restricciones:

- "primary key" sobre el campo "codigo";

- "unique" para los campos "titulo" y "codigoautor";

- de control sobre "codigoeditorial" que no permite valores nulos;

- "foreign key" para establecer el campo "codigoeditorial" como clave externa que haga referencia al campo "codigo"
de "editoriales y permita eliminaciones en cascada;
- "foreign key" para establecer el campo "codigoautor" como clave externa que haga referencia al campo "codigo" de
"autores" y permita eliminaciones "set null";

- de control sobre "precio" para que no admita valores nulos, deshabilitada;

- "check" para el campo "precio" que no admita valores negativos.

Las restricciones se agregan a la tabla, separadas por comas; colocando "constraint" seguido del nombre de la
restricción, el tipo y los campos (si es una "primary key", "unique" o "foreign key") o la condición (si es de control);
también puede especificarse el estado y la validación de datos (por defecto es "enable" y "validate"); y en el caso de
las "foreign key", la opción para eliminaciones.

Si definimos una restricción "foreign key" al crear una tabla, la tabla referenciada debe existir y debe tener definida
la clave primaria o única a la cual hace referencia la "foreign key".

Problema:

Trabajamos con las tablas "libros", "autores" y "editoriales" de una librería:

Eliminamos las tablas:

drop table libros;


drop table editoriales;
drop table autores;

Recuerde eliminar en primer lugar "libros", pues si las tablas existen y "libros" hace referencia con una restricción
"foreign key" a "editoriales" y "autores", tales tablas no podrán eliminarse hasta que ninguna restricción las
referencie.

Creamos la tabla "editoriales" con una restricción "primary key":

create table editoriales(


codigo number(3) not null,
nombre varchar2(30),
constraint PK_editoriales
primary key (codigo)
);

Creamos la tabla "autores" con una restricción "primary key", una "unique" y una "check":

create table autores(


codigo number(4) not null
constraint CK_autores_codigo
check (codigo>=0),
nombre varchar2(30) not null,
constraint PK_autores_codigo
primary key (codigo),
constraint UQ_autores_nombre
unique (nombre)
);

Aplicamos varias restricciones cuando creamos la tabla "libros":

create table libros(


codigo number(5),
titulo varchar2(40),
codigoautor number(4),
codigoeditorial number(3),
precio number(5,2) default 0,
constraint PK_libros_codigo
primary key (codigo),
constraint UQ_libros_tituloautor
unique (titulo,codigoautor),
constraint CK_libros_codigoeditorial
check (codigoeditorial is not null),
constraint FK_libros_editorial
foreign key (codigoeditorial)
references editoriales(codigo)
on delete cascade,
constraint FK_libros_autores
foreign key (codigoautor)
references autores(codigo)
on delete set null,
constraint CK_libros_preciononulo
check (precio is not null) disable,
constraint CK_precio_positivo
check (precio>=0)
);

Recuerde que si definimos una restricción "foreign key" al crear una tabla, la tabla referenciada debe existir, por ello
creamos las tablas "editoriales" y "autores" antes que "libros".

Veamos las restricciones de "editoriales":

select constraint_name, constraint_type, search_condition, status, validated


from user_constraints
where table_name='EDITORIALES';

Una tabla nos informa que hay una restricción de control y una "primary key", ambas habilitadas y validan los datos
existentes.

Veamos las restricciones de "autores":

select constraint_name, constraint_type, search_condition, status, validated


from user_constraints
where table_name='AUTORES';

Oracle nos informa que hay 3 restricciones de control, una "primary key" y una única.

Veamos las restricciones de "libros":

select constraint_name, constraint_type, search_condition, status, validated


from user_constraints
where table_name='LIBROS';

la tabla tiene 7 restricciones: 3 de control (2 "enabled" y "validated" y 1 "disabled" y "not validated"), 1 "primary key"
("enabled" "validated"), 1 "unique" ("enabled" "validated") y 2 "foreign key" ("enabled" "validated").

Ingresamos algunos registros en las tres tablas.

Recuerde que debemos ingresar registros en las tablas "autores" y "editoriales" antes que en "libros", a menos que
deshabilitemos las restricciones "foreign key".

insert into editoriales values(1,'Emece');


insert into editoriales values(2,'Planeta');
insert into editoriales values(3,'Norma');

insert into autores values(100,'Borges');


insert into autores values(101,'Bach Richard');
insert into autores values(102,'Cervantes');
insert into autores values(103,'Gaskin');

insert into libros values(200,'El aleph',100,1,40);


insert into libros values(300,'Uno',101,2,20);
insert into libros values(400,'El quijote',102,3,20);
insert into libros values(500,'El experto en laberintos',103,3,null);

Note que un libro tiene precio nulo, la tabla "libros" tiene una restricción de control que no admite precios nulos,
pero está deshabilitada.

Realizamos un "join" para mostrar todos los datos de los libros:

select l.codigo,a.nombre as autor,e.nombre as editorial,precio


from libros l
join autores a
on codigoautor=a.codigo
join editoriales e
on codigoeditorial=e.codigo;

Habilitamos la restricción de control deshabilitada sin controlar los datos ya cargados:

alter table libros


enable novalidate
constraint CK_LIBROS_PRECIONONULO;

Intentamos ingresar un libro con precio nulo:

insert into libros values(600,'El anillo del hechicero',103,3,null);

Oracle no lo permite, la restricción está habilitada.

Eliminamos un autor:

delete autores where codigo=100;

Veamos si se setearon a "null" los libros de tal autor (la restricción "FK_LIBROS_AUTORES" así lo especifica):

select *from libros;

El libro con código 200 tiene el valor "null" en "autor".

Eliminamos una editorial:

delete editoriales where codigo=1;

Veamos si se eliminaron los libros de tal editorial (la restricción "FK_LIBROS_EDITORIALES" fue establecida "cascade"):

select *from libros;

Ya no está el libro "200".

65 - Unión

Las operaciones de conjuntos combinan los resultados de dos o más consultas "select" en un único resultado.

Se usan cuando los datos que se quieren obtener pertenecen a distintas tablas y no se puede acceder a ellos con una
sola consulta.

Es necesario que las tablas referenciadas tengan tipos de datos similares, la misma cantidad de campos y el mismo
orden de campos en la lista de selección de cada consulta.

Hay tres operadores de conjuntos: union, intersect y minus. Veremos en primer lugar "union".

La sintaxis para unir dos consultas con el operador "union" es la siguiente:

CONSULTA1
union
CONSULTA2;

Recuerde que las consultas DEBEN tener el mismo numero de valores retornados y los valores deben ser del mismo
tipo.
Veamos un ejemplo. Una academia de enseñanza de idiomas da clases de inglés y frances; almacena los datos de los
alumnos que estudian inglés en una tabla llamada "ingles" y los que están inscriptos en "francés" en una tabla
denominada "frances".

La academia necesita el nombre y domicilio de todos los alumnos, de todos los cursos para enviarles una tarjeta de
felicitación para el día del alumno.

Para obtener los datos necesarios de ambas tablas en una sola consulta necesitamos realizar una unión:

select nombre, domicilio from ingles


union
select nombre, domicilio from frances;

El primer "select" devuelve el nombre y domicilio de todos los alumnos de "ingles"; el segundo, el nombre y domicilio
de todos los alumnos de "frances". Esta sentencia de unión retornará la combinacion de los resultados de ambas
consultas "select", mostrando el nombre y domicilio de los registros de ambas tablas.

Los encabezados del resultado de una unión son los que se especifican en el primer "select". El operador de conjunto
"union" no retorna duplicados; es decir, si un registro de la primer consulta es igual a otro registro de la segunda
consulta, tal registro aparece una sola vez. Si queremos que se incluyan TODOS los registros, aún duplicados,
debemos emplear "union all":

select nombre,domicilio from ingles


union all
select nombre,domicilio from frances;

En el ejemplo anterior, si un registro de la primer consulta es igual a otro registro de la segunda consulta, tal registro
aparece dos veces; es decir, si un alumno está cursando ambos idiomas, aparecerá dos veces en el resultado.

"union" puede combinarse con la cláusula "order by".

Problema:

Una academia de enseñanza de idiomas da clases de inglés y frances; almacena los datos de los alumnos que estudian
inglés en una tabla llamada "ingles" y los que están inscriptos en "francés" en una tabla denominada "frances".

Eliminamos las tablas:

drop table ingles;


drop table frances;

Creamos las tablas:

create table ingles(


documento varchar2(8) not null,
nombre varchar2(30),
domicilio varchar2(30),
primary key(documento)
);

create table frances(


documento varchar2(8) not null,
nombre varchar2(30),
domicilio varchar2(30),
primary key(documento)
);

Ingresamos algunos registros:

insert into ingles values('20111222','Ana Acosta','Avellaneda 111');


insert into ingles values('21222333','Betina Bustos','Bulnes 222');
insert into ingles values('22333444','Carlos Caseros','Colon 333');
insert into ingles values('23444555','Daniel Duarte','Duarte Quiros 444');
insert into ingles values('24555666','Estela Esper','Esmeralda 555');
insert into frances values('23444555','Daniel Duarte','Duarte Quiros 444');
insert into frances values('24555666','Estela Esper','Esmeralda 555');
insert into frances values('30111222','Fabiana Fuentes','Famatina 666');
insert into frances values('30222333','Gaston Gonzalez','Guemes 777');

La academia necesita el nombre y domicilio de todos los alumnos, de todos los cursos para enviarles una tarjeta de
invitación para un festejo el día del alumno.

Empleamos el operador "union" para obtener dicha información de ambas tablas:

select nombre, domicilio from ingles


union
select nombre, domicilio from frances;

Note que existen dos alumnos (Daniel Duarte y Estela Esper) que cursan ambos idiomas, están presentes en la tabla
"ingles" y "frances"; tales registros aparecen una sola vez en el resultado de "union". Si queremos que los registros
duplicados aparezcan, debemos emplear "all":

select nombre, domicilio from ingles


union all
select nombre, domicilio from frances;

Ordenamos por nombre:

select nombre, domicilio from ingles


union all
select nombre, domicilio from frances
order by nombre;

Podemos agregar una columna extra a la consulta con el encabezado "curso" en la que aparezca el literal "inglés" o
"francés" según si la persona cursa uno u otro idioma:

select nombre, domicilio, 'ingles' as curso from ingles


union
select nombre, domicilio,'frances' from frances
order by curso;

Recuerde que los encabezados de los campos son los que se especifican en el primer "select". Si queremos que el
nombre tenga un encabezado "alumno" debemos especificar un alias en la primer consulta. Si ordenamos por un
campo que tiene un alias, debemos especificar el alias no el nombre del campo. En la siguiente consulta realizamos
una unión, colocamos un alias al campo "nombre" y ordenamos el resultado por tal alias:

select nombre as alumno, domicilio from ingles


union
select nombre, domicilio from frances
order by alumno;

66 - Intersección

Continuamos aprendiendo las operaciones de conjuntos. Aprendimos "union" y "union all", ahora veremos "intersect".

Como cualquier otra operación de conjuntos, "intersect" se emplea cuando los datos que se quieren obtener
pertenecen a distintas tablas y no se puede acceder a ellos con una sola consulta.
Del mismo modo, las tablas referenciadas DEBEN tener tipos de datos similares, la misma cantidad de campos y el
mismo orden de campos en la lista de selección de cada consulta que intervenga en la intersección.

"intersect" devuelve la intersección de las consultas involucradas; es decir, el resultado retornará los registros que se
encuentran en la primera y segunda consulta (y demás si las hubiere), o sea, los registros que todas las consultas
tienen en común.

Sintaxis:

SENTENCIASELECT1
intersect
SENTENCIASELECT2;

No olvide que las consultas DEBEN tener el mismo numero de valores retornados y los valores deben ser del mismo
tipo.

Una academia de enseñanza de idiomas da clases de inglés, frances y portugues; almacena los datos de los alumnos
que estudian inglés en una tabla llamada "ingles", los que están inscriptos en "francés" en una tabla denominada
"frances" y los que aprenden portugues en la tabla "portugues".
La academia necesita el nombre y domicilio de todos los alumnos que cursan los tres idiomas para enviarles una
tarjeta de descuento.

Para obtener los datos necesarios de las tres tablas en una sola consulta necesitamos realizar una intresección:

select nombre, domicilio from ingles


intersect
select nombre, domicilio from frances
intersect
select nombre, domicilio from portugues;

El primer "select" devuelve el nombre y domicilio de todos los estudiantes de inglés; el segundo, el nombre y
domicilio de todos los inscriptos a francés y la tercera los mismos campos de los alumnos de "portugues". Esta
sentencia de intersección retornará los registros que coinciden en las tres consultas "select".

"intersect" puede combinarse con la cláusula "order by".

Problema:

Una academia de enseñanza de idiomas da clases de inglés, frances y portugues; almacena los datos de los alumnos
que estudian inglés en una tabla llamada "ingles", los que están inscriptos en "francés" en una tabla denominada
"frances" y los que cursan portugues en latabla "portugues".

Eliminamos las tablas:

drop table ingles;


drop table frances;
drop table portugues;

Creamos las tablas:

create table ingles(


documento varchar2(8) not null,
nombre varchar2(30),
domicilio varchar2(30),
primary key(documento)
);

create table frances(


documento varchar2(8) not null,
nombre varchar2(30),
domicilio varchar2(30),
primary key(documento)
);

create table portugues(


documento varchar2(8) not null,
nombre varchar2(30),
domicilio varchar2(30),
primary key(documento)
);

Ingresamos algunos registros:

insert into ingles values('20111222','Ana Acosta','Avellaneda 111');


insert into ingles values('21222333','Betina Bustos','Bulnes 222');
insert into ingles values('22333444','Carlos Caseros','Colon 333');
insert into ingles values('23444555','Daniel Duarte','Duarte Quiros 444');
insert into ingles values('24555666','Estela Esper','Esmeralda 555');

insert into frances values('23444555','Daniel Duarte','Duarte Quiros 444');


insert into frances values('24555666','Estela Esper','Esmeralda 555');
insert into frances values('30111222','Fabiana Fuentes','Famatina 666');
insert into frances values('30222333','Gaston Gonzalez','Guemes 777');

insert into portugues values('23444555','Daniel Duarte','Duarte Quiros 444');


insert into portugues values('22333444','Carlos Caseros','Colon 333');
insert into portugues values('30222333','Gaston Gonzalez','Guemes 777');
insert into portugues values('31222333','Hector Huerta','Homero 888');
insert into portugues values('32333444','Ines Ilara','Inglaterra 999');

La academia necesita el nombre y domicilio de todos los alumnos que cursan los tres idiomas para enviarles una
tarjeta de descuento.

Obtenemos los datos necesarios de las tres tablas en una sola consulta realizando una intersección entre ellas:

select nombre, domicilio from ingles


intersect
select nombre, domicilio from frances
intersect
select nombre, domicilio from portugues;

Aparece solamente un registro, correspondiente a "Daniel Duarte", que está en las tres tablas. Note que los alumnos
que solamente asisten a una clase o solamente a dos, no aparecen en el resultado.

Si queremos los registros que están presentes en más de dos tablas podemos realizar una consulta combinando los
operadores de intersección y de unión:

select nombre, domicilio from ingles


intersect
select nombre, domicilio from frances
union
(select nombre, domicilio from ingles
intersect
select nombre, domicilio from portugues)
union
(select nombre, domicilio from frances
intersect
select nombre, domicilio from portugues);

En la consulta anterior, la primera intersección (inglés con francés) retorna 2 registros (Esper y Duarte); la segunda
intersección (inglés y portugues) retorna 2 registros (Caseros y Duarte); unimos estos dos resultados con "union" y
obtenemos 3 registros (Caseros, Duarte y Esper); la tercera intersección (francés y portugués) retorna 2 registros
(Duarte y Gozalez) que al "unirlos" al resultado de la primera unión (Caseros, Duarte y Esper) nos devuelve 4
registros. Note que algunas consultas se encierran entre paréntesis para especificar que la operación se realiza entre
las tablas incluidas en ellos.

67 - Minus

Continuamos aprendiendo las operaciones de conjuntos. Aprendimos "union", "union all", "intersect", nos resta ver
"minus" (resta, diferencia).

Como cualquier otra operación de conjuntos, "minus" se emplea cuando los datos que se quieren obtener pertenecen
a distintas tablas y no se puede acceder a ellos con una sola consulta. Del mismo modo, las tablas referenciadas
DEBEN tener tipos de datos similares, la misma cantidad de campos y el mismo orden de campos en la lista de
selección de cada consulta que intervenga en la operación de resta.

"minus" (diferencia) devuelve los registros de la primera consulta que no se encuentran en segunda consulta, es decir,
aquellos registros que no coinciden. Es el equivalente a "except" en SQL.

Sintaxis:
SENTENCIASELECT1
minus
SENTENCIASELECT2;

No olvide que las consultas DEBEN tener el mismo numero de valores retornados y los valores deben ser del mismo
tipo.

Una academia de enseñanza de idiomas da clases de inglés y frances; almacena los datos de los alumnos que estudian
inglés en una tabla llamada "ingles" y los que están inscriptos en "francés" en una tabla denominada "frances".

La academia necesita el nombre y domicilio de todos los alumnos que cursan solamente inglés (no presentes en la
tabla "frances") para enviarles publicidad referente al curso de francés. Empleamos el operador "minus" para obtener
dicha información:

select nombre, domicilio from ingles


minus
select nombre,domicilio from frances;

Obtenemos los registros de la primer consulta que NO coinciden con ningún registro de la segunda consulta.

"minus" puede combinarse con la cláusula "order by".

Se pueden combinar más de dos sentencias con "minus".

Problema:

Una academia de enseñanza de idiomas da clases de inglés y frances; almacena los datos de los alumnos que estudian
inglés en una tabla llamada "ingles" y los que están inscriptos en "francés" en una tabla denominada "frances".

Eliminamos las tablas:

drop table ingles;


drop table frances;

Creamos las tablas:

create table ingles(


documento varchar2(8) not null,
nombre varchar2(30),
domicilio varchar2(30),
primary key(documento)
);

create table frances(


documento varchar2(8) not null,
nombre varchar2(30),
domicilio varchar2(30),
primary key(documento)
);

Ingresamos algunos registros:

insert into ingles values('20111222','Ana Acosta','Avellaneda 111');


insert into ingles values('21222333','Betina Bustos','Bulnes 222');
insert into ingles values('22333444','Carlos Caseros','Colon 333');
insert into ingles values('23444555','Daniel Duarte','Duarte Quiros 444');
insert into ingles values('24555666','Estela Esper','Esmeralda 555');

insert into frances values('23444555','Daniel Duarte','Duarte Quiros 444');


insert into frances values('24555666','Estela Esper','Esmeralda 555');
insert into frances values('30111222','Fabiana Fuentes','Famatina 666');
insert into frances values('30222333','Gaston Gonzalez','Guemes 777');
La academia necesita el nombre y domicilio de todos los alumnos que cursan solamente inglés (no presentes en la
tabla "frances") para enviarles publicidad referida al curso de francés. Empleamos el operador "minus" para obtener
dicha información:

select nombre, domicilio from ingles


minus
select nombre,domicilio from frances;

El resultado muestra los registros de la primer consulta que NO coinciden con ningún registro de la segunda consulta.

Los registros presentes en ambas tablas (Daniel Duarte y Estela Esper), no aparecen en el resultado final.

La academia necesita el nombre y domicilio de todos los alumnos que cursan solamente francés (no presentes en la
tabla "ingles") para enviarles publicidad referida al curso de inglés. Empleamos el operador "minus" para obtener
dicha información:

select nombre, domicilio from frances


minus
select nombre,domicilio from ingles;

El resultado muestra los registros de la primer consulta que NO coinciden con ningún registro de la segunda consulta.
Los registros presentes en ambas tablas (Daniel Duarte y Estela Esper), no aparecen en el resultado final.

Si queremos los alumnos que cursan un solo idioma (registros de "ingles" y de "frances" que no coinciden), podemos
unir ambas tablas y luego restarle la intersección:

select nombre from ingles


union
select nombre from frances
minus
(select nombre from ingles
intersect
select nombre from frances);

Podemos obtener el mismo resultado anterior con la siguiente consulta en la cual se buscan los registros de "ingles"
que no coinciden con "frances" y los registros de "frances" que no coinciden con "ingles" y luego se unen ambos
resultados:

select nombre from ingles


minus
select nombre from frances
union
(select nombre from frances
minus
select nombre from ingles);

68 - Agregar campos (alter table- add)

"alter table" permite modificar la estructura de una tabla. Podemos utilizarla para agregar, modificar y eliminar
campos de una tabla.

Para agregar un nuevo campo a una tabla empleamos la siguiente sintaxis básica:

alter table NOMBRETABLA


add NOMBRENUEVOCAMPO DEFINICION;

En el siguiente ejemplo agregamos el campo "cantidad" a la tabla "libros", de tipo number(4), con el valor por defecto
cero y que NO acepta valores nulos:

alter table libros


add cantidad number(4) default 0 not null;

Puede verificarse la alteración de la estructura de la tabla tipeando:


describe libros;

Para agregar un campo "not null", la tabla debe estar vacía o debe especificarse un valor por defecto. Esto es sencillo
de entender, ya que si la tabla tiene registros, el nuevo campo se llenaría con valores nulos; si no los admite, debe
tener un valor por defecto para llenar tal campo en los registros existentes.

Problema:

Trabajamos con la tabla "libros" de una librería.


Eliminamos la tabla:

drop table libros;

Creamos la tabla:

create table libros(


titulo varchar2(30),
editorial varchar2(20)
);

Agregamos el campo "cantidad" a la tabla "libros", de tipo number(4), con el valor por defecto cero y que NO acepta
valores nulos:

alter table libros


add cantidad number(4) default 0 not null;

Verificamos la estructura de la tabla:

describe libros;

aparece el nuevo campo.

Agregamos un nuevo campo "precio" a la tabla "libros", de tipo number(4) que acepta valores nulos:

alter table libros


add precio number(4);

Verificamos la estructura de la tabla:

describe libros;

aparece el nuevo campo.

Ingresamos algunos registros:

insert into libros values('El aleph','Emece',100,25.5);


insert into libros values('Uno','Planeta',150,null);

Intentamos agregar un nuevo campo "autor" de tipo varchar2(30) que no admita valores nulos:

alter table libros


add autor varchar2(30) not null;

Mensaje de error. Si el campo no aceptará valores nulos y no tiene definido un valor por defecto, no se pueden llenar
los registros existentes con ningún valor. Por ello, debemos definirlo con un valor por defecto:

alter table libros


add autor varchar2(30) default 'Desconocido' not null;

Veamos qué sucedió con los registros existentes:


select *from libros;

contienen el valor por defecto en "autor".

69 - Modificar campos (alter table- modify)

"alter table" permite modificar la estructura de una tabla. Hemos aprendido a agregar campos, también podemos
modificarlos.

Para modificar un campo empleamos la siguiente sintaxis:

alter table NOMBRETABLA


modify NOMBRECAMPO NUEVADEFINICION;

En el siguiente ejemplo modificamos el campo "precio" de la tabla "libros" para que tome valores de 6 dígitos
incluyendo 2 decimales y no acepte valores nulos:

alter table libros


modify precio number(6,2) not null;

Puede verificarse la alteración de la estructura de la tabla tipeando:

describe libros;

Podemos modificar de un campo: el tipo de dato, la escala y/o precisión, la longitud, la aceptación de valores nulos,
el valor por defecto.

Se pueden modificar todos los atributos o algunos; los que no se especifican, no cambian.

Algunas consideraciones para tener en cuenta al modificar los campos de una tabla:

a) si se cambia el tipo de dato de un campo, la tabla debe estar vacía. Por ejemplo, de number a caracteres o
viceversa.

b) no puede modificarse el tipo de dato de un campo "foreign key" o referenciado por una "foreign key", a menos que
el cambio no afecte la restricción.

c) no se puede cambiar el tipo de dato de un campo que es "foreign key" o que es referenciado por una "foreign key".

d) para modificar un campo disminuyendo la longitud (precisión o escala) del tipo de dato, la tabla DEBE estar vacía,
los registros DEBEN tener valores nulos en tal campo o los datos existentes deben ser inferiores o iguales a la nueva
longitud. Para alterar la longitud (escala o precisión) aumentándola, no es necesario que la tabla esté vacía.

e) se puede modificar un campo definido "null" a "not null", siempre que la tabla esté vacía o no contenga valores
nulos.

f) no puede redefinirse como "not null" un campo que es clave primaria.

g) si un campo tiene un valor por defecto y se modifica el tipo de dato de tal campo, Oracle analiza que el valor por
defecto pueda convertirse al nuevo tipo de dato cuando sea necesario insertarlo; si el valor por defecto no se puede
convertir al nuevo tipo de dato que se intenta modificar, la modificación del campo no se realiza. Por ejemplo, si un
campo definido char(8) tiene un valor por defecto '00000000' y se modifica tal campo a tipo number(8), Oracle
permite el cambio ya que al insertar el valor por defecto, lo convierte a número (0) automáticamente; si el valor por
defecto no se puede convertir (por ejemplo 'a000000') a valor numérico, la modificación del campo no se realiza.

Problema:
Trabajamos con la tablas "libros" y "editoriales" de una librería.
Eliminamos las tablas:

drop table libros;


drop table editoriales;

Creamos las tablas:

create table editoriales(


codigo number(3),
nombre varchar2(30),
primary key(codigo)
);

create table libros(


titulo varchar2(40),
editorial number(3),
autor varchar2(30),
precio number(4),
constraint FK_libros_editorial
foreign key(editorial)
references editoriales(codigo)
);

Modificamos el campo precio para que tome valores de 6 dígitos incluyendo 2 decimales y acepte valores nulos:

alter table libros


modify precio number(6,2);

Verificamos el cambio viendo la estructura de la tabla:

describe libros;

Ingresamos algunos registros:

insert into editoriales values(1, 'Emece');

insert into libros values('Uno',1,'Richard Bach',24.6);

Intentamos modificar el campo "precio" a "varchar(8)":

alter table libros modify precio varchar(8);

No lo permite, porque existe un registro con un valor numérico en tal campo.

Actualizamos el registro de "libros" con precio no nulo a nulo:

update libros set precio= null;

Ahora si podemos cambiar el tipo de dato de "precio", los registros existentes contienen "null" en tal campo:

alter table libros modify precio varchar(8);

Verificamos el cambio:

describe libros;

Intentamos modificar el campo "codigo" de "editoriales" a "char(3)":

alter table editoriales modify codigo char(3);

No lo permite porque tal campo es referenciado por una clave externa.

Modificamos un atributo del campo "codigo" de "editoriales":


alter table editoriales modify codigo number(4);

Oracle permite el cambio pues no afecta a la restricción.

Intentamos redefinir "precio" para que no acepte valores nulos:

alter table libros


modify precio not null;

No lo permite porque existe un registro con valor nulo en "precio".

Eliminamos el registro y modificamos el campo "precio" a "no nulo":

delete from libros;

alter table libros


modify precio not null;

Intentamos redefinir como no nulo el campo "codigo" de "editoriales":

alter table editoriales


modify codigo not null;

No aparece mensaje de error, pero si verificamos la estructura de la tabla veremos que continua siendo "not null", ya
que es clave primaria:

describe editoriales;

Redefinimos el campo "precio" como number(6,2), con un valor por defecto 0:

alter table libros


modify precio number(6,2) default 0;

Oracle permite modificar el campo "precio" a "char(8)". Si luego ingresamos un registro sin valor para "precio",
guardará el valor por defecto (0) convertido a cadena ('0'):

alter table libros


modify precio char(8) default 0;

insert into libros values('El aleph',1,'Borges',default);

select *from libros;

Redefinimos el valor por defecto del campo "precio" (que ahora es de tipo char) a "cero":

alter table libros


modify precio default 'cero';

Oracle no permite modificar el campo "precio" a "number(8,2)" porque si luego ingresamos un registro sin valor para
tal campo, el valor por defecto ('cero') no podrá convertirse a número:

alter table libros


modify precio number(8,2);

Mensaje de error.

Modificamos el valor por defecto para que luego pueda ser convertido:

alter table libros


modify precio default '0';

Vaciamos la tabla:

truncate table libros;


Oracle permite modificar el campo "precio" a "number(8,2)" porque si luego ingresamos un registro sin valor para tal
campo, el valor por defecto ('0') podrá convertirse a número (0):

alter table libros


modify precio number(8,2);

Oracle permite modificar el campo "precio" a "char(8)". Si luego ingresamos un registro sin valor para "precio",
guardará el valor por defecto (0) convertido a cadena ('0'):

alter table libros


modify precio char(8) default 0;

insert into libros values('El aleph',1,'Borges',default);

select *from libros;

70 - Eliminar campos (alter table- drop)

Vimos que "alter table" permite modificar la estructura de una tabla, agregando, modificando y eliminando campos.

Para eliminar campos de una tabla la sintaxis básica es la siguiente:

alter table NOMBRETABLA


drop column NOMBRECAMPO;

En el siguiente ejemplo eliminamos el campo "precio" de la tabla "libros":

alter table libros


drop column precio;

No pueden eliminarse los campos a los cuales hace referencia una restricción "foreign key".

Si eliminamos un campo que tiene una restricción "primary key", "unique", "check" o "foreign key", la restricción
también se elimina.

Si eliminamos un campo indexado, el índice también se elimina.

NO puede eliminarse un campo si es el único en la tabla.

Puede verificarse la alteración de la estructura de la tabla tipeando:

describe libros;

Problema:

Trabajamos con la tablas "libros" y "editoriales" de una librería.


Eliminamos las tablas:

drop table libros;


drop table editoriales;

Creamos las tablas:

create table editoriales(


codigo number(3),
nombre varchar2(30),
primary key(codigo)
);

create table libros(


titulo varchar2(30),
editorial number(3),
autor varchar2(30),
precio number(6,2),
constraint FK_libros_editorial
foreign key(editorial)
references editoriales(codigo)
);

Eliminamos un campo de la tabla "libros":

alter table libros


drop column precio;

Vemos la estructura de la tabla "libros":

describe libros;

El campo "precio" ya no existe.

Recuerde que no pueden eliminarse los campos referenciados por una "foreign key". Intentamos eliminar el campo
"codigo" de "editoriales":

alter table editoriales


drop column codigo;

Un mensaje indica que la sentencia no fue ejecutada.

Eliminamos el campo "editorial" de "libros":

alter table libros


drop column editorial;

Verificamos que el campo no existe:

describe libros;

El campo se ha eliminado y junto con él la restricción "foreign key":

select *from user_constraints


where table_name='LIBROS';

Ahora si podemos eliminar el campo "codigo" de "editoriales", pues la restricción "foreign key" que hacía referencia a
ella ya no existe:

alter table editoriales


drop column codigo;

El campo "codigo" de "editoriales" se ha eliminado y junto con él la restricción "primary key":

select *from user_constraints


where table_name='EDITORIALES';

Agregamos un índice compuesto sobre "titulo" y "autor" de "libros":

create unique index I_libros_titulo


on libros(titulo,autor);

Veamos si existe tal índice:

select index_name,column_name,column_position
from user_ind_columns
where table_name='LIBROS';
Recuerde que si elimina un campo indizado, su índice también se elimina. Eliminamos el campo "autor" de "libros":

alter table libros


drop column autor;

Veamos si existe el índice compuesto creado anteriormente sobre los campos "titulo" y "autor" de "libros":

select index_name,column_name,column_position
from user_ind_columns
where table_name='LIBROS';

Ya no existe.

La tabla ahora solamente consta de un campo, por lo tanto, no puede eliminarse, pues la tabla no puede quedar
vacía de campos:

alter table libros


drop column titulo;

Mensaje de error.

71 - Agregar campos y restricciones (alter table)

Podemos agregar un campo a una tabla y en el mismo momento aplicarle una restricción.
Para agregar un campo y establecer una restricción, la sintaxis básica es la siguiente:

alter table TABLA


add CAMPO DEFINICION
constraint NOMBRERESTRICCION TIPO;

Agregamos a la tabla "libros", el campo "titulo" de tipo varchar2(30) y una restricción "unique":

alter table libros


add titulo varchar2(30)
constraint UQ_libros_autor unique;

Agregamos a la tabla "libros", el campo "codigo" de tipo number(4) not null y una restricción "primary key":

alter table libros


add codigo number(4) not null
constraint PK_libros_codigo primary key;

Agregamos a la tabla "libros", el campo "precio" de tipo number(6,2) y una restricción "check":

alter table libros


add precio number(6,2)
constraint CK_libros_precio check (precio>=0);

Problema:

Trabajamos con la tabla "libros" de una librería.


Eliminamos la tabla:

drop table libros;

Creamos la tabla con la siguiente estructura:

create table libros(


autor varchar2(30),
editorial varchar2(15)
);

Agregamos el campo "titulo" de tipo varchar2(30) y una restricción "unique":

alter table libros


add titulo varchar2(30)
constraint UQ_libros_autor unique;

Veamos si la estructura cambió:

describe libros;

Agregamos el campo "codigo" de tipo number(4) not null y en la misma sentencia una restricción "primary key":

alter table libros


add codigo number(4) not null
constraint PK_libros_codigo primary key;

Agregamos el campo "precio" de tipo number(6,2) y una restricción "check" que no permita valores negativos para
dicho campo:

alter table libros


add precio number(6,2)
constraint CK_libros_precio check (precio>=0);

Veamos la estructura de la tabla y las restricciones:

describe libros;
select *from user_constraints where table_name='LIBROS';

La tabla contiene 5 campos y 4 restricciones.

72 - Subconsultas

Una subconsulta (subquery) es una sentencia "select" anidada en otra sentencia "select", "insert", "update" o "delete"
(o en otra subconsulta).

Las subconsultas se emplean cuando una consulta es muy compleja, entonces se la divide en varios pasos lógicos y se
obtiene el resultado con una única instrucción y cuando la consulta depende de los resultados de otra consulta.

Generalmente, una subconsulta se puede reemplazar por combinaciones y estas últimas son más eficientes.

Las subconsultas generalmente se incluyen entre paréntesis.

Puede haber subconsultas dentro de subconsultas.

Generalmente las subconsultas se colocan en la cláusula "where".

Una subconsulta puede retornar:

- un solo valor,

- una lista de valores de una sola columna,

- un conjunto de registros de varios campos.


Podemos averiguar si un valor de la consulta externa pertenece o no al conjunto devuelto por una subconsulta ("in",
"not in"), si es mayor, menor o igual a todos ("all") o a algunos valores ("some", "any") del conjunto devuelto.

Se pueden emplear subconsultas:

- en lugar de una expresión, siempre que devuelvan un solo valor o una lista de valores.

- que retornen un conjunto de registros de varios campos en lugar de una tabla o para obtener el mismo resultado
que una combinación (join).

Hay varios tipos básicos de subconsultas:

- las que retornan un solo valor escalar que se utiliza con un operador de comparación o en lugar de una expresión.

- las que retornan una lista de valores, se combinan con "in", o los operadores "any", "some" y "all".

- los que testean la existencia con "exists".

Una subconsulta puede reemplazar una expresión. Dicha subconsulta debe devolver un valor escalar (o una lista de
valores de un campo).

Las subconsultas que retornan un solo valor escalar se utiliza con un operador de comparación o en lugar de una
expresión:

select CAMPOS
from TABLA
where CAMPO OPERADOR (SUBCONSULTA);

select CAMPO OPERADOR (SUBCONSULTA)


from TABLA;

Si queremos saber el precio de un determinado libro y la diferencia con el precio del libro más costoso,
anteriormente debíamos averiguar en una consulta el precio del libro más costoso y luego, en otra consulta, calcular
la diferencia con el valor del libro que solicitamos. Podemos conseguirlo en una sola sentencia combinando dos
consultas:

select titulo,precio,
precio-(select max(precio) from libros) as diferencia
from libros
where titulo='Uno';

En el ejemplo anterior se muestra el título, el precio de un libro y la diferencia entre el precio del libro y el máximo
valor de precio.

Queremos saber el título, autor y precio del libro más costoso:

select titulo,autor, precio


from libros
where precio=
(select max(precio) from libros);

Note que el campo del "where" de la consulta exterior es compatible con el valor retornado por la expresión de la
subconsulta.

Se pueden emplear en "select", "insert", "update" y "delete".

Para actualizar un registro empleando subconsulta la sintaxis básica es la siguiente:

update TABLA set CAMPO=NUEVOVALOR


where CAMPO= (SUBCONSULTA);

Para eliminar registros empleando subconsulta empleamos la siguiente sintaxis básica:


delete from TABLA
where CAMPO=(SUBCONSULTA);

Problema:

Trabajamos con la tabla "libros" de una librería.


Eliminamos la tabla y la creamos:

drop table libros;

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(5,2)
);

Ingresamos los siguientes registros:

insert into libros values(1,'Alicia en el pais de las maravillas','Lewis


Carroll','Emece',20.00);
insert into libros values(2,'Alicia en el pais de las maravillas','Lewis
Carroll','Plaza',35.00);
insert into libros values(3,'Aprenda PHP','Mario Molina','Siglo XXI',40.00);
insert into libros values(4,'El aleph','Borges','Emece',10.00);
insert into libros values(5,'Ilusiones','Richard Bach','Planeta',15.00);
insert into libros values(6,'Java en 10 minutos','Mario Molina','Siglo XXI',50.00);
insert into libros values(7,'Martin Fierro','Jose Hernandez','Planeta',20.00);
insert into libros values(8,'Martin Fierro','Jose Hernandez','Emece',30.00);
insert into libros values(9,'Uno','Richard Bach','Planeta',10.00);

Obtenemos el título, precio de un libro específico y la diferencia entre su precio y el máximo valor:

select titulo,precio,
precio-(select max(precio) from libros) as diferencia
from libros
where titulo='Uno';

Mostramos el título y precio del libro más costoso:

select titulo,autor, precio


from libros
where precio=
(select max(precio) from libros);

Actualizamos el precio del libro con máximo valor:

update libros set precio=45


where precio=
(select max(precio) from libros);

Eliminamos los libros con precio menor:

delete from libros


where precio=
(select min(precio) from libros);

74 - Subconsultas con in

Vimos que una subconsulta puede reemplazar una expresión. Dicha subconsulta debe devolver un valor escalar o una
lista de valores de un campo; las subconsultas que retornan una lista de valores reemplazan a una expresión en una
cláusula "where" que contiene la palabra clave "in".
El resultado de una subconsulta con "in" (o "not in") es una lista. Luego que la subconsulta retorna resultados, la
consulta exterior los usa.

Podemos averiguar si un valor de la consulta externa pertenece o no al conjunto devuelto por una subconsulta
empleando "in" y "not in".

La sintaxis básica es la siguiente:

...where EXPRESION in (SUBCONSULTA);

Este ejemplo muestra los nombres de las editoriales que ha publicado libros de un determinado autor:

select nombre
from editoriales
where codigo in
(select codigoeditorial
from libros
where autor='Richard Bach');

La subconsulta (consulta interna) retorna una lista de valores de un solo campo (codigoeditorial) que la consulta
exterior luego emplea al recuperar los datos.

Se averigua si el código devuelto por la consulta externa se encuentra dentro del conjunto de valores retornados por
la consulta interna.

Podemos reemplazar por un "join" la consulta anterior:

select distinct nombre


from editoriales e
join libros
on codigoeditorial=e.codigo
where autor='Richard Bach';

Una combinación (join) siempre puede ser expresada como una subconsulta; pero una subconsulta no siempre puede
reemplazarse por una combinación que retorne el mismo resultado. Si es posible, es aconsejable emplear
combinaciones en lugar de subconsultas, son más eficientes.

Se recomienda probar las subconsultas antes de incluirlas en una consulta exterior, así puede verificar que retorna lo
necesario, porque a veces resulta difícil verlo en consultas anidadas.

También podemos buscar valores No coincidentes con una lista de valores que retorna una subconsulta; por ejemplo,
las editoriales que no han publicado libros de un autor específico:

select nombre
from editoriales
where codigo not in
(select codigoeditorial
from libros
where autor='Richard Bach');

Problema:

Trabajamos con las tablas "libros" y "editoriales" de una librería.


Eliminamos las tablas y las creamos:

drop table libros;


drop table editoriales;

create table editoriales(


codigo number(3),
nombre varchar2(30),
primary key (codigo)
);

create table libros (


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
codigoeditorial number(3),
primary key(codigo),
constraint FK_libros_editorial
foreign key (codigoeditorial)
references editoriales(codigo)
);

Ingresamos algunos registros:

insert into editoriales values(1,'Planeta');


insert into editoriales values(2,'Emece');
insert into editoriales values(3,'Paidos');
insert into editoriales values(4,'Siglo XXI');

insert into libros values(100,'Uno','Richard Bach',1);


insert into libros values(101,'Ilusiones','Richard Bach',1);
insert into libros values(102,'Aprenda PHP','Mario Molina',4);
insert into libros values(103,'El aleph','Borges',2);
insert into libros values(104,'Puente al infinito','Richard Bach',2);

Queremos conocer el nombre de las editoriales que han publicado libros del autor "Richard Bach":

select nombre
from editoriales
where codigo in
(select codigoeditorial
from libros
where autor='Richard Bach');

Probamos la subconsulta separada de la consulta exterior para verificar que retorna una lista de valores de un solo
campo:

select codigoeditorial
from libros
where autor='Richard Bach';

Podemos reemplazar por un "join" la primera consulta:

select distinct nombre


from editoriales e
join libros
on codigoeditorial=e.codigo
where autor='Richard Bach';

También podemos buscar las editoriales que no han publicado libros de "Richard Bach":

select nombre
from editoriales
where codigo not in
(select codigoeditorial
from libros
where autor='Richard Bach');

75 - Subconsultas any- some- all

"any" y "some" son sinónimos. Chequean si alguna fila de la lista resultado de una subconsulta se encuentra el valor
especificado en la condición.

Compara un valor escalar con los valores de un campo y devuelven "true" si la comparación con cada valor de la lista
de la subconsulta es verdadera, sino "false".

El tipo de datos que se comparan deben ser compatibles.


La sintaxis básica es:

...VALORESCALAR OPERADORDECOMPARACION
any (SUBCONSULTA);

Queremos saber los títulos de los libros de "Borges" que pertenecen a editoriales que han publicado también libros de
"Richard Bach", es decir, si los libros de "Borges" coinciden con ALGUNA de las editoriales que publicó libros de
"Richard Bach":

select titulo
from libros
where autor='Borges' and
codigoeditorial = any
(select e.codigo
from editoriales e
join libros l
on codigoeditorial=e.codigo
where l.autor='Richard Bach');

La consulta interna (subconsulta) retorna una lista de valores de un solo campo (puede ejecutar la subconsulta como
una consulta para probarla), luego, la consulta externa compara cada valor de "codigoeditorial" con cada valor de la
lista devolviendo los títulos de "Borges" que coinciden.

"all" también compara un valor escalar con una serie de valores. Chequea si TODOS los valores de la lista de la
consulta externa se encuentran en la lista de valores devuelta por la consulta interna.

Sintaxis:

VALORESCALAR OPERADORDECOMPARACION all (SUBCONSULTA);

Queremos saber si TODAS las editoriales que publicaron libros de "Borges" coinciden con TODAS las editoriales que
publicaron libros de "Richard Bach":

select titulo
from libros
where autor='Borges' and
codigoeditorial = all
(select e.codigo
from editoriales e
join libros l
on codigoeditorial=e.codigo
where l.autor='Richard Bach');

La consulta interna (subconsulta) retorna una lista de valores de un solo campo (puede ejecutar la subconsulta como
una consulta para probarla), luego, la consulta externa compara cada valor de "codigoeditorial" con cada valor de la
lista, si TODOS coinciden, devuelve los títulos.

Veamos otro ejemplo con un operador de comparación diferente:

Queremos saber si ALGUN precio de los libros de "Borges" es mayor a ALGUN precio de los libros de "Richard Bach":

select titulo,precio
from libros
where autor='Borges' and
precio > any
(select precio
from libros
where autor='Bach');

El precio de cada libro de "Borges" es comparado con cada valor de la lista de valores retornada por la subconsulta; si
ALGUNO cumple la condición, es decir, es mayor a ALGUN precio de "Richard Bach", se lista.

Veamos la diferencia si empleamos "all" en lugar de "any":

select titulo,precio
from libros
where autor='borges' and
precio > all
(select precio
from libros
where autor='bach');

El precio de cada libro de "Borges" es comparado con cada valor de la lista de valores retornada por la subconsulta; si
cumple la condición, es decir, si es mayor a TODOS los precios de "Richard Bach" (o al mayor), se lista.

Emplear "= any" es lo mismo que emplear "in".

Emplear "<> all" es lo mismo que emplear "not in".

Problema:

Trabajamos con las tablas "libros" y "editoriales" de una librería.


Eliminamos las tablas y las creamos:

drop table libros;


drop table editoriales;

create table editoriales(


codigo number(3),
nombre varchar2(30),
primary key (codigo)
);

create table libros (


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
codigoeditorial number(3),
precio number(5,2),
primary key(codigo),
constraint FK_libros_editorial
foreign key (codigoeditorial)
references editoriales(codigo)
on delete cascade
);

Ingresamos algunos registros:

insert into editoriales values(1,'Planeta');


insert into editoriales values(2,'Emece');
insert into editoriales values(3,'Paidos');
insert into editoriales values(4,'Siglo XXI');

insert into libros values(100,'Uno','Richard Bach',1,15);


insert into libros values(101,'Ilusiones','Richard Bach',4,18);
insert into libros values(102,'Puente al infinito','Richard Bach',2,20);
insert into libros values(103,'Aprenda PHP','Mario Molina',4,40);
insert into libros values(104,'El aleph','Borges',2,10);
insert into libros values(105,'Antología','Borges',1,20);
insert into libros values(106,'Cervantes y el quijote','Borges',3,25);

Mostramos los títulos de los libros de "Borges" de editoriales que han publicado también libros de "Richard Bach":

select titulo
from libros
where autor like '%Borges%' and
codigoeditorial = any
(select e.codigo
from editoriales e
join libros l
on codigoeditorial=e.codigo
where l.autor like '%Bach%');

Realizamos la misma consulta pero empleando "all" en lugar de "any":

select titulo
from libros
where autor like '%Borges%' and
codigoeditorial = all
(select e.codigo
from editoriales e
join libros l
on codigoeditorial=e.codigo
where l.autor like '%Bach%');

Mostramos los títulos y precios de los libros "Borges" cuyo precio supera a ALGUN precio de los libros de "Richard
Bach":

select titulo,precio
from libros
where autor like '%Borges%' and
precio > any
(select precio
from libros
where autor like '%Bach%');

Veamos la diferencia si empleamos "all" en lugar de "any":

select titulo,precio
from libros
where autor like '%Borges%' and
precio > all
(select precio
from libros
where autor like '%Bach%');

Empleamos la misma subconsulta para eliminación:

delete from libros


where autor like '%Borges%' and
precio > all
(select precio
from libros
where autor like '%Bach%');

76 - Subconsultas correlacionadas

Un almacén almacena la información de sus ventas en una tabla llamada "facturas" en la cual guarda el número de
factura, la fecha y el nombre del cliente y una tabla denominada "detalles" en la cual se almacenan los distintos
items correspondientes a cada factura: el nombre del artículo, el precio (unitario) y la cantidad.

Se necesita una lista de todas las facturas que incluya el número, la fecha, el cliente, la cantidad de artículos
comprados y el total:

select f.*,
(select count(d.numeroitem)
from Detalles d
where f.numero=d.numerofactura) as cantidad,
(select sum(d.preciounitario*cantidad)
from Detalles d
where f.numero=d.numerofactura) as total
from facturas f;

El segundo "select" retorna una lista de valores de una sola columna con la cantidad de items por factura (el número
de factura lo toma del "select" exterior); el tercer "select" retorna una lista de valores de una sola columna con el
total por factura (el número de factura lo toma del "select" exterior); el primer "select" (externo) devuelve todos los
datos de cada factura.

A este tipo de subconsulta se la denomina consulta correlacionada. La consulta interna se evalúa tantas veces como
registros tiene la consulta externa, se realiza la subconsulta para cada registro de la consulta externa. El campo de la
tabla dentro de la subconsulta (f.numero) se compara con el campo de la tabla externa.
En este caso, específicamente, la consulta externa pasa un valor de "numero" a la consulta interna. La consulta
interna toma ese valor y determina si existe en "detalles", si existe, la consulta interna devuelve la suma. El proceso
se repite para el registro de la consulta externa, la consulta externa pasa otro "numero" a la consulta interna y Oracle
repite la evaluación.

Un almacén almacena la información de sus ventas en una tabla llamada "facturas" en la cual guarda el número de
factura, la fecha y el nombre del cliente y una tabla denominada "detalles" en la cual se almacenan los distintos
items correspondientes a cada factura: el nombre del artículo, el precio (unitario) y la cantidad.

Se necesita una lista de todas las facturas que incluya el número, la fecha, el cliente, la cantidad de artículos
comprados y el total:

select f.*,
(select count(d.numeroitem)
from Detalles d
where f.numero=d.numerofactura) as cantidad,
(select sum(d.preciounitario*cantidad)
from Detalles d
where f.numero=d.numerofactura) as total
from facturas f;

El segundo "select" retorna una lista de valores de una sola columna con la cantidad de items por factura (el número
de factura lo toma del "select" exterior); el tercer "select" retorna una lista de valores de una sola columna con el
total por factura (el número de factura lo toma del "select" exterior); el primer "select" (externo) devuelve todos los
datos de cada factura.

A este tipo de subconsulta se la denomina consulta correlacionada. La consulta interna se evalúa tantas veces como
registros tiene la consulta externa, se realiza la subconsulta para cada registro de la consulta externa. El campo de la
tabla dentro de la subconsulta (f.numero) se compara con el campo de la tabla externa.

En este caso, específicamente, la consulta externa pasa un valor de "numero" a la consulta interna. La consulta
interna toma ese valor y determina si existe en "detalles", si existe, la consulta interna devuelve la suma. El proceso
se repite para el registro de la consulta externa, la consulta externa pasa otro "numero" a la consulta interna y Oracle
repite la evaluación.

77 - Exists y No Exists

Los operadores "exists" y "not exists" se emplean para determinar si hay o no datos en una lista de valores.

Estos operadores pueden emplearse con subconsultas correlacionadas para restringir el resultado de una consulta
exterior a los registros que cumplen la subconsulta (consulta interior). Estos operadores retornan "true" (si las
subconsultas retornan registros) o "false" (si las subconsultas no retornan registros).

Cuando se coloca en una subconsulta el operador "exists", Oracle analiza si hay datos que coinciden con la
subconsulta, no se devuelve ningún registro, es como un test de existencia; Oracle termina la recuperación de
registros cuando por lo menos un registro cumple la condición "where" de la subconsulta.

La sintaxis básica es la siguiente:

... where exists (SUBCONSULTA);

En este ejemplo se usa una subconsulta correlacionada con un operador "exists" en la cláusula "where" para devolver
una lista de clientes que compraron el artículo "lapiz":

select cliente,numero
from facturas f
where exists
(select *from Detalles d
where f.numero=d.numerofactura
and d.articulo='lapiz');
Puede obtener el mismo resultado empleando una combinación.

Podemos buscar los clientes que no han adquirido el artículo "lapiz" empleando "if not exists":

select cliente,numero
from facturas f
where not exists
(select *from Detalles d
where f.numero=d.numerofactura
and d.articulo='lapiz');

Problema:

Un comercio que vende artículos de librería y papelería almacena la información de sus ventas en una tabla llamada
"facturas" y otra "detalles".

Eliminamos las tablas:

drop table detalles;


drop table facturas;

Las creamos con las siguientes estructuras:

create table facturas(


numero number(5) not null,
fecha date,
cliente varchar2(30),
primary key(numero)
);

create table detalles(


numerofactura number(5) not null,
numeroitem number(4) not null,
articulo varchar2(30),
precio number(5,2),
cantidad number(3),
primary key(numerofactura,numeroitem),
constraint FK_detalles_numerofactura
foreign key (numerofactura)
references facturas(numero)
on delete cascade
);

Ingresamos algunos registros:

insert into facturas values(1200,'15/01/2007','Juan Lopez');


insert into facturas values(1201,'15/01/2007','Luis Torres');
insert into facturas values(1202,'15/01/2007','Ana Garcia');
insert into facturas values(1300,'20/01/2007','Juan Lopez');

insert into detalles values(1200,1,'lapiz',1,100);


insert into detalles values(1200,2,'goma',0.5,150);
insert into detalles values(1201,1,'regla',1.5,80);
insert into detalles values(1201,2,'goma',0.5,200);
insert into detalles values(1201,3,'cuaderno',4,90);
insert into detalles values(1202,1,'lapiz',1,200);
insert into detalles values(1202,2,'escuadra',2,100);
insert into detalles values(1300,1,'lapiz',1,300);

Empleamos una subconsulta correlacionada con un operador "exists" en la cláusula "where" para devolver la lista de
clientes que compraron el artículo "lapiz":

select cliente,numero
from facturas f
where exists
(select *from detalles d
where f.numero=d.numerofactura
and d.articulo='lapiz');
Obtenemos el mismo resultado empleando una combinación:

select *from facturas


join detalles
on facturas.numero=detalles.numerofactura
where detalles.articulo='lapiz';

Buscamos los clientes que NO han comprado el artículo "lapiz":

select cliente,numero
from facturas f
where not exists
(select *from detalles d
where f.numero=d.numerofactura
and d.articulo='lapiz');

78 - Subconsulta simil autocombinacion

Algunas sentencias en las cuales la consulta interna y la externa emplean la misma tabla pueden reemplazarse por
una autocombinación.

Por ejemplo, queremos una lista de los libros que han sido publicados por distintas editoriales.

select distinct l1.titulo


from libros l1
where l1.titulo in
(select l2.titulo
from libros l2
where l1.editorial <> l2.editorial);

En el ejemplo anterior empleamos una subconsulta correlacionada y las consultas interna y externa emplean la
misma tabla. La subconsulta devuelve una lista de valores por ello se emplea "in" y sustituye una expresión en una
cláusula "where".

Con el siguiente "join" se obtiene el mismo resultado:

select distinct l1.titulo


from libros l1
join libros l2
on l1.titulo=l1.titulo and
l1.autor=l2.autor
where l1.editorial<>l2.editorial;

Otro ejemplo: Buscamos todos los libros que tienen el mismo precio que "El aleph" empleando subconsulta:

select titulo
from libros
where titulo<>'El aleph' and
precio =
(select precio
from libros
where titulo='El aleph');

La subconsulta retorna un solo valor. Podemos obtener la misma salida empleando "join".

Buscamos los libros cuyo precio supere el precio promedio de los libros por editorial:

select l1.titulo,l1.editorial,l1.precio
from libros l1
where l1.precio >
(select avg(l2.precio)
from libros l2
where l1.editorial= l2.editorial);
Por cada valor de l1, se evalúa la subconsulta, si el precio es mayor que el promedio.

Se puede conseguir el mismo resultado empleando un "join" con "having".

Problema:

Trabajamos con la tabla "libros" de una librería.


Eliminamos la tabla y la creamos:

drop table libros;

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(5,2)
);

Ingresamos los siguientes registros:

insert into libros values(1,'Alicia en el pais de las maravillas','Lewis


Carroll','Emece',20.00);
insert into libros values(2,'Alicia en el pais de las maravillas','Lewis
Carroll','Plaza',35.00);
insert into libros values(3,'Aprenda PHP','Mario Molina','Siglo XXI',40.00);
insert into libros values(4,'El aleph','Borges','Emece',10.00);
insert into libros values(5,'Ilusiones','Richard Bach','Planeta',15.00);
insert into libros values(6,'Java en 10 minutos','Mario Molina','Siglo XXI',50.00);
insert into libros values(7,'Martin Fierro','Jose Hernandez','Planeta',20.00);
insert into libros values(8,'Martin Fierro','Jose Hernandez','Emece',30.00);
insert into libros values(9,'Uno','Richard Bach','Planeta',10.00);

Obtenemos la lista de los libros que han sido publicados por distintas editoriales empleando una consulta
correlacionada:

select distinct l1.titulo


from libros l1
where l1.titulo in
(select l2.titulo
from libros l2
where l1.editorial <> l2.editorial);

El siguiente "join" retorna el mismo resultado:

select distinct l1.titulo


from libros l1
join libros l2
on l1.titulo=l2.titulo
where l1.editorial<>l2.editorial;

Buscamos todos los libros que tienen el mismo precio que "El aleph" empleando subconsulta:

select titulo
from libros
where titulo<>'El aleph' and
precio =
(select precio
from libros
where titulo='El aleph');

Obtenemos la misma salida empleando "join":

select l1.titulo
from libros l1
join libros l2
on l1.precio=l2.precio
where l2.titulo='El aleph' and
l1.titulo<>l2.titulo;

Buscamos los libros cuyo precio supera el precio promedio de los libros por editorial:

select l1.titulo,l1.editorial,l1.precio
from libros l1
where l1.precio >
(select avg(l2.precio)
from libros l2
where l1.editorial= l2.editorial);

Obtenemos la misma salida pero empleando un "join" con "having":

select l1.titulo,l1.editorial,l1.precio
from libros l1
join libros l2
on l1.editorial=l2.editorial
group by l1.editorial, l1.titulo, l1.precio
having l1.precio > avg(l2.precio);

79 - Subconsulta con update y delete

Dijimos que podemos emplear subconsultas en sentencias "insert", "update", "delete", además de "select".

La sintaxis básica para realizar actualizaciones con subconsulta es la siguiente:

update TABLA set CAMPO=NUEVOVALOR


where CAMPO= (SUBCONSULTA);

Actualizamos el precio de todos los libros de editorial "Emece":

update libros set precio=precio+(precio*0.1)


where codigoeditorial=
(select codigo
from editoriales
where nombre='Emece');

La subconsulta retorna un único valor. También podemos hacerlo con un join.

La sintaxis básica para realizar eliminaciones con subconsulta es la siguiente:

delete from TABLA


where CAMPO OPERADOR (SUBCONSULTA);

Eliminamos todos los libros de las editoriales que tiene publicados libros de "Juan Perez":

delete from libros


where codigoeditorial in
(select e.codigo
from editoriales e
join libros
on codigoeditorial=e.codigo
where autor='Juan Perez');

La subconsulta es una combinación que retorna una lista de valores que la consulta externa emplea al seleccionar los
registros para la eliminación.

Problema:
Trabajamos con las tablas "libros" y "editoriales" de una librería.
Eliminamos las tablas y las creamos:

drop table libros;


drop table editoriales;

create table editoriales(


codigo number(2),
nombre varchar2(30),
primary key (codigo)
);

create table libros (


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
codigoeditorial number(2),
precio number(5,2),
primary key(codigo),
constraint FK_libros_editorial
foreign key (codigoeditorial)
references editoriales(codigo)
);

Ingresamos algunos registros:

insert into editoriales values(1,'Planeta');


insert into editoriales values(2,'Emece');
insert into editoriales values(3,'Paidos');
insert into editoriales values(4,'Siglo XXI');

insert into libros values(100,'Uno','Richard Bach',1,15);


insert into libros values(101,'Ilusiones','Richard Bach',2,20);
insert into libros values(102,'El aleph','Borges',3,10);
insert into libros values(103,'Aprenda PHP','Mario Molina',4,40);
insert into libros values(104,'Poemas','Juan Perez',1,20);
insert into libros values(105,'Cuentos','Juan Perez',3,25);
insert into libros values(106,'Java en 10 minutos','Marcelo Perez',2,30);

Actualizamos el precio de todos los libros de editorial "Emece" incrementándolos en un 10%:

update libros set precio=precio+(precio*0.1)


where codigoeditorial=
(select codigo
from editoriales
where nombre='Emece');

Eliminamos todos los libros de las editoriales que tiene publicados libros de "Juan Perez":

delete from libros


where codigoeditorial in
(select e.codigo
from editoriales e
join libros
on codigoeditorial=e.codigo
where autor='Juan Perez');

80 - Subconsulta e insert

Aprendimos que una subconsulta puede estar dentro de un "select", "update" y "delete"; también puede estar dentro
de un "insert".

Podemos ingresar registros en una tabla empleando un subselect.

La sintaxis básica es la siguiente:


insert into TABLAENQUESEINGRESA (CAMPOSTABLA1)
select (CAMPOSTABLACONSULTADA)
from TABLACONSULTADA;

Un profesor almacena las notas de sus alumnos en una tabla llamada "alumnos". Tiene otra tabla llamada
"aprobados", con algunos campos iguales a la tabla "alumnos" pero en ella solamente almacenará los alumnos que han
aprobado el ciclo.

Ingresamos registros en la tabla "aprobados" seleccionando registros de la tabla "alumnos":

insert into aprobados (documento,nota)


select (documento,nota)
from alumnos;

Entonces, se puede insertar registros en una tabla con la salida devuelta por una consulta a otra tabla; para ello
escribimos la consulta y le anteponemos "insert into" junto al nombre de la tabla en la cual ingresaremos los registros
y los campos que se cargarán (si se ingresan todos los campos no es necesario listarlos).

La cantidad de columnas devueltas en la consulta debe ser la misma que la cantidad de campos a cargar en el
"insert".

Se pueden insertar valores en una tabla con el resultado de una consulta que incluya cualquier tipo de "join".

Problema:

Un profesor almacena las notas de sus alumnos en una tabla llamada "alumnos" (documento, nombre, nota). Tiene
otra tabla llamada "aprobados" (documento,nota) en la que guarda los alumnos que han aprobado el ciclo.

Eliminamos las tablas:

drop table alumnos;


drop table aprobados;

Creamos las tablas:

create table alumnos(


documento char(8) not null,
nombre varchar2(30),
nota number(4,2)
constraint CK_alumnos_nota_valores check (nota>=0 and nota <=10),
primary key(documento)
);

create table aprobados(


documento char(8) not null,
nota number(4,2)
constraint CK_aprobados_nota_valores check (nota>=0 and nota <=10),
primary key(documento)
);

Ingresamos registros en "alumnos":

insert into alumnos values('30000000','Ana Acosta',8);


insert into alumnos values('30111111','Betina Bustos',9);
insert into alumnos values('30222222','Carlos Caseros',2.5);
insert into alumnos values('30333333','Daniel Duarte',7.7);
insert into alumnos values('30444444','Estela Esper',3.4);

Ingresamos registros en la tabla "aprobados" seleccionando registros de la tabla "alumnos":

insert into aprobados


select documento,nota
from alumnos
where nota>=4;
Note que no se listan los campos en los cuales se cargan los datos porque tienen el mismo nombre que los de la tabla
de la cual extraemos la información.

Veamos si los registros se han cargado:

select *from aprobados;

81 - Crear tabla a partir de otra (create table- select)

Podemos crear una tabla e insertar datos en ella en una sola sentencia consultando otra tabla (o varias) con esta
sintaxis:

create table NOMBRENUEVATABLA


as SUBCONSULTA;

Es decir, se crea una nueva tabla y se inserta en ella el resultado de una consulta a otra tabla.

Tenemos la tabla "libros" de una librería y queremos crear una tabla llamada "editoriales" que contenga los nombres
de las editoriales.

La tabla "editoriales", que no existe, contendrá solamente un campo llamado "nombre". La tabla libros contiene
varios registros.

Podemos crear la tabla "editoriales" con el campo "nombre" consultando la tabla "libros" y en el mismo momento
insertar la información:

create table editoriales


as (select distinct editorial as nombre from libros);

La tabla "editoriales" se ha creado con el campo "nombre" seleccionado del campo "editorial" de "libros".

Los campos de la nueva tabla tienen el mismo nombre, tipo de dato y valores almacenados que los campos listados
de la tabla consultada; si se quiere dar otro nombre a los campos de la nueva tabla se deben especificar alias.

Podemos emplear "group by", funciones de agrupamiento y "order by" en las consultas. También podemos crear una
tabla que contenga datos de 2 o más tablas empleando combinaciones.

Problema:

Tenemos la tabla "libros" de una librería y queremos crear una tabla llamada "editoriales" que contenga los nombres
de las editoriales.

Eliminamos las tablas "libros" y "editoriales":

drop table libros;


drop table editoriales;

Creamos la tabla "libros":

create table libros(


codigo number(5),
titulo varchar2(40) not null,
autor varchar2(30),
editorial varchar2(20),
precio number(5,2),
primary key(codigo)
);

Ingresamos algunos registros;


insert into libros values(1,'Uno','Richard Bach','Planeta',15);
insert into libros values(2,'El aleph','Borges','Emece',25);
insert into libros values(3,'Matematica estas ahi','Paenza','Nuevo siglo',18);
insert into libros values(4,'Aprenda PHP','Mario Molina','Nuevo siglo',45);
insert into libros values(5,'Ilusiones','Richard Bach','Planeta',14);
insert into libros values(6,'Java en 10 minutos','Mario Molina','Nuevo siglo',50);

Creamos una tabla llamada "editoriales" que contenga los nombres de las editoriales obteniendo tales nombres de la
tabla "libros":

create table editoriales as


(select distinct editorial as nombre
from libros);

Veamos la nueva tabla:

select *from editoriales;

Necesitamos una nueva tabla llamada "librosporeditorial" que contenga la cantidad de libros de cada editorial.
Primero eliminamos la tabla:

drop table cantidadporeditorial;

Creamos la nueva tabla empleando una subconsulta:

create table cantidadporeditorial as


(select editorial as nombre,count(*) as cantidad
from libros
group by editorial);

Veamos los registros de la nueva tabla:

select *from cantidadporeditorial;

La tabla "cantidadporeditorial" se ha creado con el campo llamado "nombre" seleccionado del campo "editorial" de
"libros" y con el campo "cantidad" con el valor calculado con count(*) de la tabla "libros".

Queremos una tabla llamada "ofertas20" que contenga los mismos campos que "libros" y guarde los libros con un
precio menor o igual a 20. Primero eliminamos la tabla "ofertas20":

drop table ofertas20;

Creamos "ofertas20" e insertamos la consulta de "libros":

create table ofertas20 as


(select *from libros
where precio<=20)
order by precio desc;

La consulta anterior retorna los libros de la tabla "libros" cuyo precio es menor o igual a 20 y los almacena en la
nueva tabla ("ofertas20") ordenados en forma descendente por precio. Note que no se listan los campos a extraer, se
coloca un asterisco para indicar que se incluyen todos los campos.

Veamos los registros de la nueva tabla:

select *from ofertas20;

Agregamos una columna a la tabla "editoriales" que contiene la ciudad en la cual está la casa central de cada
editorial:

alter table editoriales add ciudad varchar2(30);

Actualizamos dicho campo:


update editoriales set ciudad='Cordoba' where nombre='Planeta';
update editoriales set ciudad='Cordoba' where nombre='Emece';
update editoriales set ciudad='Buenos Aires' where nombre='Nuevo siglo';

Queremos una nueva tabla llamada "librosdecordoba" que contenga los títulos y autores de los libros de editoriales de
Cordoba. En primer lugar, la eliminamos:

drop table librosdecordoba;

Consultamos las 2 tablas y guardamos el resultado en la nueva tabla que estamos creando:

create table librosdecordoba as


(select titulo,autor from libros
join editoriales
on editorial=nombre
where ciudad='Cordoba');

Consultamos la nueva tabla:

select *from librosdecordoba;

82 - Vistas (create view)


Una vista es un objeto. Una vista es una alternativa para mostrar datos de varias tablas; es como una tabla virtual
que almacena una consulta. Los datos accesibles a través de la vista no están almacenados en la base de datos, en la
base de datos se guarda la definición de la vista y no el resultado de ella.

Entonces, una vista almacena una consulta como un objeto para utilizarse posteriormente. Las tablas consultadas en
una vista se llaman tablas base. En general, se puede dar un nombre a cualquier consulta y almacenarla como una
vista.

Una vista suele llamarse también tabla virtual porque los resultados que retorna y la manera de referenciarlas es la
misma que para una tabla.

Las vistas permiten:

- simplificar la administración de los permisos de usuario: se pueden dar al usuario permisos para que solamente
pueda acceder a los datos a través de vistas, en lugar de concederle permisos para acceder a ciertos campos, así se
protegen las tablas base de cambios en su estructura.

- mejorar el rendimiento: se puede evitar tipear instrucciones repetidamente almacenando en una vista el resultado
de una consulta compleja que incluya información de varias tablas.

Podemos crear vistas con: un subconjunto de registros y campos de una tabla; una unión de varias tablas; una
combinación de varias tablas; un subconjunto de otra vista, combinación de vistas y tablas.

Una vista se define usando un "select".

La sintaxis básica para crear una vista es la siguiente:

create view NOMBREVISTA as


SUBCONSULTA;

El contenido de una vista se muestra con un "select":

select *from NOMBREVISTA;

En el siguiente ejemplo creamos la vista "vista_empleados", que es resultado de una combinación en la cual se
muestran 4 campos:

create view vista_empleados as


select (apellido||' '||e.nombre) as nombre,sexo,
s.nombre as seccion, cantidadhijos
from empleados e
join secciones s
on codigo=seccion;

Para ver la información contenida en la vista creada anteriormente tipeamos:

select *from vista_empleados;

Podemos realizar consultas a una vista como si se tratara de una tabla:

select seccion,count(*) as cantidad


from vista_empleados;

Los nombres para vistas deben seguir las mismas reglas que cualquier identificador. Para distinguir una tabla de una
vista podemos fijar una convención para darle nombres, por ejemplo, colocar el sufijo “vista” y luego el nombre de
las tablas consultadas en ellas.

Los campos y expresiones de la consulta que define una vista DEBEN tener un nombre. Se debe colocar nombre de
campo cuando es un campo calculado o si hay 2 campos con el mismo nombre. Note que en el ejemplo, al concatenar
los campos "apellido" y "nombre" colocamos un alias; si no lo hubiésemos hecho aparecería un mensaje de error
porque dicha expresión DEBE tener un encabezado, Oracle no lo coloca por defecto.

Los nombres de los campos y expresiones de la consulta que define una vista DEBEN ser únicos (no puede haber dos
campos o encabezados con igual nombre). Note que en la vista definida en el ejemplo, al campo "s.nombre" le
colocamos un alias porque ya había un encabezado (el alias de la concatenación) llamado "nombre" y no pueden
repetirse, si sucediera, aparecería un mensaje de error.

Otra sintaxis es la siguiente:

create view NOMBREVISTA (NOMBRESDEENCABEZADOS)


as
SUBCONSULTA;

Creamos otra vista de "empleados" denominada "vista_empleados_ingreso" que almacena la cantidad de empleados
por año:

create view vista_empleados_ingreso (fecha,cantidad)


as
select extract(year from fechaingreso),count(*)
from empleados
group by extract(year from fechaingreso);

La diferencia es que se colocan entre paréntesis los encabezados de las columnas que aparecerán en la vista. Si no
los colocamos y empleamos la sintaxis vista anteriormente, se emplean los nombres de los campos o alias (que en
este caso habría que agregar) colocados en el "select" que define la vista. Los nombres que se colocan entre
paréntesis deben ser tantos como los campos o expresiones que se definen en la vista.

Las vistas se crean en la base de datos activa.

Al crear una vista, Oracle verifica que existan las tablas a las que se hacen referencia en ella; no se puede crear una
vista que referencie tablas inexistentes. No se puede crear una vista si existe un objeto con ese nombre.

Se aconseja probar la sentencia "select" con la cual definiremos la vista antes de crearla para asegurarnos que el
resultado que retorna es el imaginado.

Una vista siempre está actualizada; si modificamos las tablas base (a las cuales referencia la vista), la vista mostrará
los cambios.

Se pueden construir vistas sobre otras vistas.

Problema:

Una empresa almacena la información de sus empleados en dos tablas llamadas "empleados" y "secciones".

Eliminamos las tablas:


drop table empleados;
drop table secciones;

Creamos las tablas:

create table secciones(


codigo number(2),
nombre varchar2(20),
sueldo number(5,2)
constraint CK_secciones_sueldo check (sueldo>=0),
constraint PK_secciones primary key (codigo)
);

create table empleados(


legajo number(5),
documento char(8),
sexo char(1)
constraint CK_empleados_sexo check (sexo in ('f','m')),
apellido varchar2(20),
nombre varchar2(20),
domicilio varchar2(30),
seccion number(2) not null,
cantidadhijos number(2)
constraint CK_empleados_hijos check (cantidadhijos>=0),
estadocivil char(10)
constraint CK_empleados_estadocivil check (estadocivil in
('casado','divorciado','soltero','viudo')),
fechaingreso date,
constraint PK_empleados primary key (legajo),
constraint FK_empleados_seccion
foreign key (seccion)
references secciones(codigo),
constraint UQ_empleados_documento
unique(documento)
);

Ingresamos algunos registros:

insert into secciones values(1,'Administracion',300);


insert into secciones values(2,'Contaduría',400);
insert into secciones values(3,'Sistemas',500);

insert into empleados values(100,'22222222','f','Lopez','Ana','Colon


123',1,2,'casado','10/10/1990');
insert into empleados values(102,'23333333','m','Lopez','Luis','Sucre
235',1,0,'soltero','02/10/1990');
insert into empleados values(103,'24444444','m','Garcia','Marcos','Sarmiento
1234',2,3,'divorciado','12/07/1998');
insert into empleados values(104,'25555555','m','Gomez','Pablo','Bulnes
321',3,2,'casado','10/09/1998');
insert into empleados values(105,'26666666','f','Perez','Laura','Peru
1254',3,3,'casado','05/09/2000');

Eliminamos la vista "vista_empleados". Aún no hemos aprendido a eliminar vistas, lo veremos próximamente:

drop view vista_empleados;

Creamos la vista "vista_empleados", que es resultado de una combinación en la cual se muestran 5 campos:

create view vista_empleados as


select (apellido||' '||e.nombre) as nombre,sexo,
s.nombre as seccion, cantidadhijos
from empleados e
join secciones s
on codigo=seccion;

Vemos la información de la vista:

select *from vista_empleados;


Realizamos una consulta a la vista como si se tratara de una tabla:

select seccion,count(*) as cantidad


from vista_empleados
group by seccion;

Eliminamos la vista "vista_empleados_ingreso":

drop view vista_empleados_ingreso;

Creamos otra vista de "empleados" denominada "vista_empleados_ingreso" que almacena la cantidad de empleados
por año:

create view vista_empleados_ingreso (fecha,cantidad)


as
select extract(year from fechaingreso),count(*)
from empleados
group by extract(year from fechaingreso);

Vemos la información:

select *from vista_empleados_ingreso;

Hemos aprendido que los registros resultantes de una vista no se almacena en la base de datos, sino la definición de
la vista, por lo tanto, al modificar las tablas referenciadas por la vista, el resultado de la vista cambia.

Modificamos una fecha en la tabla "empleados" y luego consultamos la vista para verificar que está actualizada:

update empleados set fechaingreso='10/09/2000' where fechaingreso='10/09/1998';

select *from vista_empleados_ingreso;

83 - Vistas (información)

Las vistas son objetos, así que para obtener información de ellos pueden consultarse los siguientes catálogos.

"user_catalog" nos muestra todos los objetos del usuario actual, incluidas las vistas. En la columna "table_type"
aparece "view" si es una vista. Ejemplo:

select *from user_catalog where table_type='VIEW';

"user_objects" nos muestra información sobre todos los objetos del usuario actual. En la columna "OBJECT_TYPE"
muestra "view" si es una vista, aparece la fecha de creación y demás información que no analizaremos por el
momento.

Para ver todos los objetos del usuario actual que son vistas tipeamos:

select *from user_catalog where object_type='VIEW';

"user_views" nos muestra información referente a todas las vistas del usuario actual, el nombre de la vista, la
longitud del texto, el texto que la define, etc.

Con la siguiente sentencia obtenemos información sobre todas las vistas cuyo nombre comience con la cadena
"VISTA":

select *from user_views where view_name like 'VISTA%';

84 - Vistas eliminar (drop view)


Para quitar una vista se emplea "drop view":

drop view NOMBREVISTA;

Eliminamos la vista denominada "vista_empleados":

drop view vista_empleados;

Si se elimina una tabla a la que hace referencia una vista, la vista no se elimina, hay que eliminarla explícitamente.

Problema:

Una empresa almacena la información de sus empleados en dos tablas llamadas "empleados" y "secciones".

Eliminamos las tablas:

drop table empleados;


drop table secciones;

Creamos las tablas:

create table secciones(


codigo number(2),
nombre varchar2(20),
sueldo number(5,2),
constraint CK_secciones_sueldo check (sueldo>=0),
constraint PK_secciones primary key (codigo)
);

create table empleados(


legajo number(3),
documento char(8),
sexo char(1),
constraint CK_empleados_sexo check (sexo in ('f','m')),
apellido varchar2(20),
nombre varchar2(20),
domicilio varchar2(30),
seccion number(2) not null,
cantidadhijos number(2),
constraint CK_empleados_hijos check (cantidadhijos>=0),
estadocivil char(10),
constraint CK_empleados_estadocivil check (estadocivil in
('casado','divorciado','soltero','viudo')),
fechaingreso date,
constraint PK_empleados primary key (legajo),
constraint FK_empleados_seccion
foreign key (seccion)
references secciones(codigo),
constraint UQ_empleados_documento
unique(documento)
);

Ingresamos algunos registros:

insert into secciones values(1,'Administracion',300);


insert into secciones values(2,'Contaduría',400);
insert into secciones values(3,'Sistemas',500);

insert into empleados values(100,'22222222','f','Lopez','Ana','Colon


123',1,2,'casado','10/10/1990');
insert into empleados values(101,'23333333','m','Lopez','Luis','Sucre
235',1,0,'soltero','02/10/1990');
insert into empleados values(102,'24444444','m','Garcia','Marcos','Sarmiento
1234',2,3,'divorciado','07/12/1998');
insert into empleados values(103,'25555555','m','Gomez','Pablo','Bulnes
321',3,2,'casado','10/09/1998');
insert into empleados values(104,'26666666','f','Perez','Laura','Peru
1254',3,3,'casado','05/09/2000');

Eliminamos la vista "vista_empleados":

drop view vista_empleados;

Creamos la vista "vista_empleados", que es resultado de una combinación en la cual se muestran 5 campos:

create view vista_empleados as


select (apellido||' '||e.nombre) as nombre,sexo,
s.nombre as seccion, cantidadhijos
from empleados e
join secciones s
on codigo=seccion;

Veamos la información de la vista:

select *from vista_empleados;

Eliminamos la tabla "empleados":

drop table empleados;

Verificamos que la vista aún existe consultando "user_objects":

select *from user_objects where object_name='VISTA_EMPLEADOS';

Verificamos que la vista "vista_empleados" aún existe consultando "user_catalog":

select *from user_catalog where table_type='VIEW';

Si consultamos la vista, aparecerá un mensaje de error, pues la tabla "empleados" a la cual hace referencia la vista,
no existe:

select *from vista_empleados;

Eliminamos la vista:

drop view vista_empleados;

Verificamos que la vista ya no existe:

select *from user_catalog where table_name='VISTA_EMPLEADOS';

85 - Vistas (modificar datos a través de ella)

Si se modifican los datos de una vista, se modifica la tabla base.

Se puede insertar, actualizar o eliminar datos de una tabla a través de una vista, teniendo en cuenta lo siguiente, las
modificaciones que se realizan a las vistas:

- no pueden afectar a más de una tabla consultada. Pueden modificarse y eliminarse datos de una vista que combina
varias tablas pero la modificación o eliminación solamente debe afectar a una sola tabla.

- no se pueden cambiar los campos resultado de un cálculo.


- pueden generar errores si afectan a campos a las que la vista no hace referencia. Por ejemplo, si se ingresa un
registro en una vista que consulta una tabla que tiene campos not null que no están incluidos en la vista.

Problema:

Una empresa almacena la información de sus empleados en dos tablas llamadas "empleados" y "secciones".

Eliminamos las tablas:

drop table empleados;


drop table secciones;

Creamos las tablas:

create table secciones(


codigo number(2),
nombre varchar2(20),
constraint PK_secciones primary key (codigo)
);

create table empleados(


legajo number(4) not null,
documento char(8),
sexo char(1),
constraint CK_empleados_sexo check (sexo in ('f','m')),
apellido varchar2(20),
nombre varchar2(20),
domicilio varchar2(30),
seccion number(2) not null,
cantidadhijos number(2),
constraint CK_empleados_hijos check (cantidadhijos>=0),
estadocivil char(10),
constraint CK_empleados_estadocivil check (estadocivil in
('casado','divorciado','soltero','viudo')),
fechaingreso date,
constraint PK_empleados primary key (legajo),
sueldo number(6,2),
constraint CK_empleados_sueldo check (sueldo>=0),
constraint FK_empleados_seccion
foreign key (seccion)
references secciones(codigo),
constraint UQ_empleados_documento
unique(documento)
);

Ingresamos algunos registros:

insert into secciones values(1,'Administracion');


insert into secciones values(2,'Contaduría');
insert into secciones values(3,'Sistemas');

insert into empleados values(100,'22222222','f','Lopez','Ana','Colon


123',1,2,'casado','10/10/1990',600);
insert into empleados values(101,'23333333','m','Lopez','Luis','Sucre
235',1,0,'soltero','02/10/1990',650);
insert into empleados values(103,'24444444', 'm', 'Garcia', 'Marcos', 'Sarmiento 1234',
2, 3, 'divorciado', '07/12/1998',800);
insert into empleados values(104,'25555555','m','Gomez','Pablo','Bulnes
321',3,2,'casado','10/09/1998',900);
insert into empleados values(105,'26666666','f','Perez','Laura','Peru
1254',3,3,'casado','05/09/2000',700);

Eliminamos la vista "vista_empleados":

drop view vista_empleados;

Creamos la vista "vista_empleados", que es resultado de una combinación en la cual se muestran 5 campos:
create view vista_empleados as
select (apellido||' '||e.nombre) as nombre,sexo,
s.nombre as seccion, cantidadhijos
from empleados e
join secciones s
on codigo=seccion;

Vemos la información contenida en la vista:

select *from vista_empleados;

Eliminamos la vista "vista_empleados2":

drop view vista_empleados2;

Creamos otra vista de "empleados" denominada "vista_empleados2" que consulta solamente la tabla "empleados":

create view vista_empleados2


as
select legajo,nombre,apellido,fechaingreso,seccion,sueldo
from empleados
where sueldo>=600;

Consultamos la vista:

select *from vista_empleados2;

No podemos ingresar un registro en la vista "vista_empleados" porque tal vista tiene campos calculados ("nombre",
que es una concatenación de "apellido" y "nombre"), además afecta a 2 tablas ("empleados" y "secciones") y hay
campos no accesibles desde la vista que no admiten valores nulos. Si ejecutamos el siguiente "insert", Oracle
mostrará un mensaje de error:

insert into vista_empleados values('Pedro Perez','m','Sistemas',2);

Podemos ingresar un registro en la vista "vista_empleados2" porque tal vista afecta a una sola tabla y los campos de
""empleados" no accesibles desde la vista admiten valores nulos:

insert into vista_empleados2 values(200,'Pedro','Perez','10/10/2000',2,800);

Vemos la tabla "empleados" para comprobar que el nuevo registro insertado desde la vista está presente en
"empleados", los campos para los cuales no se ingresaron datos, almacenan el valor "null":

select *from empleados;

Actualizamos el campo "nombre" de un registro de la vista "vista_empleados2":

update vista_empleados2 set nombre='Beatriz' where nombre='Ana';

Verificamos que se actualizó la tabla:

select *from empleados;

Si intentamos actualizar el campo "nombre" de un empleado a través de la vista "vista_empleados", Oracle no lo


permite porque es una columna calculada (concatenación de dos campos):

update vista_empleados set nombre='Lopez Carmen' where nombre='Lopez Beatriz';

Si podemos actualizar otros campos, por ejemplo, el campo "cantidadhijos" de un empleado a través de la vista
"vista_empleados":

update vista_empleados set cantidadhijos=3 where nombre='Lopez Beatriz';

Verificamos que se actualizó la tabla:


select *from empleados;

Eliminamos un registro de "empleados" a través de la vista "vista_empleados2":

delete from vista_empleados2 where apellido='Lopez' and nombre='Beatriz';

Verificamos que se eliminó tal registro de la tabla "empleados":

select *from empleados;

Podemos eliminar registros de empleados a través de la vista "vista_empleados":

delete from vista_empleados where seccion='Administracion';

Verificamos que no hay registros en "empleados" de la sección "1" ("Administracion"):

select *from empleados;

86 - Vistas (with read only)

Con la cláusula "with read only" (sólo lectura) evitamos que se puedan realizar inserciones, actualizaciones y
eliminaciones mediante una vista.

Sintaxis:

create view NOMBREVISTA


as SUBCONSULTA
with read only;

Evitamos que Oracle acepte "insert", "update" o "delete" sobre la vista si colocamos "with read only" luego de la
subconsulta que define una vista.

Por ejemplo, creamos la siguiente vista:

create view vista_empleados


as
select apellido, nombre, sexo, seccion
from empleados
with read only;

Oracle responde con un mensaje de error ante cualquier "insert", "update" o "delete" realizado sobre la vista.

Problema:

Una empresa almacena la información de sus empleados en una tabla llamada "empleados".

Eliminamos la tabla:

drop table empleados;

Creamos las tablas:

create table empleados(


documento char(8),
sexo char(1)
constraint CK_empleados_sexo check (sexo in ('f','m')),
apellido varchar2(20),
nombre varchar2(20),
domicilio varchar2(30),
seccion varchar2(30),
cantidadhijos number(2),
constraint CK_empleados_hijos check (cantidadhijos>=0),
estadocivil char(10)
constraint CK_empleados_estadocivil check (estadocivil in
('casado','divorciado','soltero','viudo')),
fechaingreso date
);

Ingresamos algunos registros:

insert into empleados values('22222222','f','Lopez','Ana','Colon


123','Administracion',2,'casado','10/10/1990');
insert into empleados values('23333333','m','Lopez','Luis','Sucre
235','Administracion',0,'soltero','02/10/1990');
insert into empleados values('24444444','m','Garcia','Marcos','Sarmiento
1234','Contaduria',3,'divorciado','07/12/1998');
insert into empleados values('25555555','m','Gomez','Pablo','Bulnes
321','Contaduria',2,'casado','10/09/1998');
insert into empleados values('26666666','f','Perez','Laura','Peru
1254','Sistemas',3,'casado','05/09/2000');

Eliminamos las vistas "vista_empleados" y "vista_empleados2":

drop view vista_empleados;


drop view vista_empleados2;

Creamos la vista "vista_empleados", que muestra solamente algunos campos de "empleados":

create view vista_empleados


as
select apellido, nombre, sexo, seccion
from empleados;

Creamos la vista "vista_empleados2", igual que "vista_empleados", pero ahora colocamos "with read only" para
impedir que puedan ejecutarse "insert", "update" y "delete" sobre esta vista:

create view vista_empleados2


as
select apellido, nombre, sexo, seccion
from empleados
with read only;

Actualizamos el nombre de un empleado a través de la vista "vista_empleados":

update vista_empleados set nombre='Beatriz' where nombre='Ana';

Veamos si la modificación se realizó en la tabla:

select *from empleados;

Intentamos actualizar el nombre de un empleado a través de la vista "vista_empleados2":

update vista_empleados2 set nombre='Pedro' where nombre='Marcos';

No lo permite.

Ingresamos un registro en la tabla "empleados" a través de la vista "vista_empleados":

insert into vista_empleados values('Juarez','Juan','m','Sistemas');

Oracle acepta la inserción. Verificamos que la inserción se realizó en la tabla:

select *from empleados;


Intentamos ingresar un registro a través de la vista "vista_empleados2":

insert into vista_empleados2 values('Gimenez','Julieta','f','Sistemas');

Oracle no lo permite porque la vista fue definida con "with read only".

Eliminamos un registro en la tabla "empleados" a través de la vista "vista_empleados":

delete from vista_empleados where apellido='Juarez';

Oracle acepta la eliminación. Verificamos que la eliminación se realizó en la tabla:

select *from empleados;

Intentamos eliminar registros a través de la vista "vista_empleados2":

delete from vista_empleados2 where apellido='Lopez';

Oracle no lo permite porque la vista fue definida con "with read only".

87 - Vistas modificar (create or replace view)

Para modificar una vista puede eliminarla y volver a crearla o emplear "create or replace".

Sintaxis:

create or replace view NOMBREVISTA


as SUBCONSULTA;

Con "create or replace view" se modifica la definición de una vista existente o se crea si no existe.

Problema:

Una empresa almacena la información de sus empleados en dos tablas llamadas "empleados" y "secciones".

Eliminamos las tablas:

drop table empleados;


drop table secciones;

Creamos las tablas:

create table secciones(


codigo number(2),
nombre varchar2(20),
constraint PK_secciones primary key (codigo)
);

create table empleados(


documento char(8),
nombre varchar2(30),
domicilio varchar2(30),
seccion number(2) not null,
constraint FK_empleados_seccion
foreign key (seccion)
references secciones(codigo),
constraint PK_empleados
primary key (documento)
);

Ingresamos algunos registros:

insert into secciones values(1,'Administracion');


insert into secciones values(2,'Contaduría');
insert into secciones values(3,'Sistemas');

insert into empleados values('22222222','Lopez Ana','Colon 123',1);


insert into empleados values('23333333','Lopez Luis','Sucre 235',1);
insert into empleados values('24444444','Garcia Marcos','Sarmiento 1234',2);
insert into empleados values('25555555','Gomez Pablo','Bulnes 321',3);
insert into empleados values('26666666','Perez Laura','Peru 1254',3);

Eliminamos la vista "vista_empleados":

drop view vista_empleados;

Creamos la vista "vista_empleados" que muestre algunos campos de los empleados de la sección 1:

create view vista_empleados


as
select documento,nombre,seccion
from empleados
where seccion=1;

Consultamos la vista:

select *from vista_empleados;

Veamos el texto de la vista consultando "user_views":

select view_name,text from user_views where view_name='VISTA_EMPLEADOS';

Modificamos la vista para que muestre el domicilio:

create or replace view vista_empleados


as
select documento,nombre,seccion, domicilio
from empleados
where seccion=1;

Consultamos la vista para ver si se modificó:

select *from vista_empleados;

Aparece el nuevo campo.

Veamos el texto de la vista consultando "user_views":

select view_name,text from user_views where view_name='VISTA_EMPLEADOS';

88 - Vistas (with check option)

Es posible obligar a todas las instrucciones de modificación de datos que se ejecutan en una vista a cumplir ciertos
criterios.

Por ejemplo, creamos la siguiente vista:

create view vista_empleados


as
select apellido, nombre, sexo, seccion
from empleados
where seccion='Administracion'
with check option;

La vista definida anteriormente muestra solamente algunos registros y algunos campos de "empleados", los de la
sección "Administracion".

Con la cláusula "with check option", no se permiten modificaciones en aquellos campos que afecten a los registros
que retorna la vista. Es decir, no podemos modificar el campo "sección" porque al hacerlo, tal registro ya no
aparecería en la vista; si podemos actualizar los demás campos. Por ejemplo, si intentamos actualizar a "Sistemas" el
campo "seccion" de un registro mediante la vista, Oracle muestra un mensaje de error.

La misma restricción surge al ejecutar un "insert" sobre la vista; solamente podemos ingregar registros con el valor
"Administracion" para "seccion"; si intentamos ingresar un registro con un valor diferente de "Administracion" para el
campo "seccion", Oracle mostrará un mensaje de error.

Sintaxis básica:

create view NOMBREVISTA


as SUBCONSULTA
with check option;

Problema:

Una empresa almacena la información de sus empleados en una tabla llamada "empleados".

Eliminamos la tabla:

drop table empleados;

Creamos la tabla:

create table empleados(


documento char(8),
sexo char(1)
constraint CK_empleados_sexo check (sexo in ('f','m')),
apellido varchar2(20),
nombre varchar2(20),
seccion varchar2(30),
cantidadhijos number(2),
constraint CK_empleados_hijos check (cantidadhijos>=0),
estadocivil char(10)
constraint CK_empleados_estadocivil check (estadocivil in
('casado','divorciado','soltero','viudo'))
);

Ingresamos algunos registros:

insert into empleados values('22222222','f','Lopez','Ana','Administracion',2,'casado');


insert into empleados
values('23333333','m','Lopez','Luis','Administracion',0,'soltero');
insert into empleados
values('24444444','m','Garcia','Marcos','Sistemas',3,'divorciado');
insert into empleados values('25555555','m','Gomez','Pablo','Sistemas',2,'casado');
insert into empleados values('26666666','f','Perez','Laura','Contaduria',3,'casado');

Creamos o reemplazamos (si existe) la vista "vista_empleados", para que muestre el nombre, apellido, sexo y sección
de todos los empleados de "Administracion" agregando la cláusula "with check option" para evitar que se modifique la
sección de tales empleados a través de la vista y que se ingresen empleados de otra sección:

create or replace view vista_empleados


as
select apellido, nombre, sexo, seccion
from empleados
where seccion='Administracion'
with check option;

Consultamos la vista:

select *from vista_empleados;

Actualizarmos el nombre de un empleado a través de la vista:

update vista_empleados set nombre='Beatriz' where nombre='Ana';

Oracle aceptó la actualización porque el campo "nombre" no está restringido.

Veamos si la modificación se realizó en la tabla:

select *from empleados;

Intentamos actualizar la sección de un empleado a través de la vista:

update vista_empleados set seccion='Sistemas' where nombre='Beatriz';

Oracle no aceptó la actualización porque el campo "nombre" está restringido.

Ingresamos un registro mediante la vista:

insert into vista_empleados values('Gomez','Gabriela','f','Administracion');

Oracle acepta la inserción porque ingresamos un valor para "seccion" que incluirá el registro en la vista.

Intentamos ingresar un empleado de otra sección:

insert into vista_empleados values('Torres','Tatiana','f','Sistemas');

Oracle no acepta la inserción porque ingresamos un valor para "seccion" que excluirá el nuevo registro de la vista.

89 - Vistas (otras consideraciones)

Cuando creamos una vista, Oracle verifica que las tablas a las cuales se hace referencia en ella existan. Si la vista
que se intenta crear hace referencia a tablas inexistentes, Oracle muestra un mensaje de error.

Podemos "forzar" a Oracle a crear una vista aunque no existan los objetos (tablas, vistas, etc.) que referenciamos en
la misma. Para ello debemos agregar "force" al crearla:

create force view NOMBREVISTA


as SUBCONSULTA;

De esta manera, podemos crear una vista y después las tablas involucradas; luego, al consultar la vista, DEBEN existir
las tablas.

Al crear la vista la opción predeterminada es "no force". Se recomienda crear las tablas y luego las vistas necesarias.

Otra cuestión a considerar es la siguiente: si crea una vista con "select *" y luego agrega campos a la estructura de las
tablas involucradas, los nuevos campos no aparecerán en la vista; esto es porque los campos se seleccionan al
ejecutar "create view"; debe volver a crear la vista (con "create view" o "create or replace view").

Problema:
Una empresa almacena la información de sus empleados en una tabla llamada "empleados".

Eliminamos la tabla:

drop table empleados;

Eliminamos la vista "vista_empleados":

drop view vista_empleados;

Creamos la vista "vista_empleados" que muestre algunos campos de "empleados", pero la tabla "empleados" no existe,
por ello, debemos agregar, al crear la vista "force":

create force view vista_empleados


as
select documento,nombre,seccion
from empleados;

Creamos la tabla:

create table empleados(


documento char(8),
nombre varchar2(30),
domicilio varchar2(30),
seccion varchar2(30)
);

Ingresamos algunos registros:

insert into empleados values('22222222','Lopez Ana','Colon 123','Sistemas');


insert into empleados values('23333333','Lopez Luis','Sucre 235','Sistemas');
insert into empleados values('24444444','Garcia Marcos','Sarmiento 1234','Contaduria');
insert into empleados values('25555555','Gomez Pablo','Bulnes 321','Contaduria');
insert into empleados values('26666666','Perez Laura','Peru 1254','Secretaria');

Consultamos la vista:

select *from vista_empleados;

Veamos el texto de la vista consultando "user_views":

select view_name,text from user_views where view_name='VISTA_EMPLEADOS';

Creamos o reemplazamos (si existe) la vista "vista_empleados" que muestre todos los campos de la tabla "empleados":

create or replace view vista_empleados


as
select *from empleados;

Consultamos la vista:

select *from vista_empleados;

Agregamos un campo a la tabla "empleados":

alter table empleados


add sueldo number(6,2);

Consultamos la vista "vista_empleados":

select *from vista_empleados;


Note que el nuevo campo agregado a "empleados" no aparece, a pesar que la vista indica que muestre todos los
campos de dicha tabla; esto sucede porque los campos se seleccionan al ejecutar "create view", para que aparezcan
debemos volver a crear la vista:

create or replace view vista_empleados


as
select *from empleados;

Consultemos la vista:

select *from vista_empleados;

Ahora si aparece el nuevo campo "sueldo" de "empleados";

90 - Vistas materializadas (materialized view)

Una vista materializada se define como una vista común, pero en lugar de almacenar la definición de la vista,
almacena el resultado de la consulta, es decir, la materializa, como un objeto persistente en la base de datos.

Sintaxis:

create materialized view NOMBREVISTAMATERIALIZADA


as SUBCONSULTA;

Existen varias cláusulas que podemos agregar al crear una vista materializada, pero no las estudiaremos.

En el "from" de la consulta pueden listarse tablas, vistas y vistas materializadas.

Entonces, una vista materializada almacena su resultado físicamente. Una vista materializada (materialized view) es
una instantánea (snapshot), son sinónimos.

Para obtener información acerca de las vistas materializadas podemos consultar el diccionario "user_objects", en la
columna "object_type" aparecerá "materialized view" si es una vista materializada. Ejemplo:

select *from user_objects where object_type='MATERIALIZED VIEW';

También podemos consultar "user_mviews" para obtener información de todas las vistas materializadas del usuario
actual:

select *from user_mviews;

Este diccionario muestra mucha información que no explicaremos en detalle.

Para eliminar una vista materializada empleamos "drop materialized view":

drop materialized view NOMBREVISTAMATERIALIZADA;

Ejemplo:

drop materialized view vm_promedios;

No se permite realizar "insert", "update" ni "delete" en las vistas materializadas.

Problema:

Un profesor almacena los datos y notas de sus alumnos en dos tablas "alumnos" y notas".
Eliminamos las tablas:

drop table notas;


drop table alumnos;

Creamos las tablas:

create table alumnos(


documento char(8),
apellido varchar(30),
nombre varchar(30),
domicilio varchar2(40),
primary key(documento)
);

create table notas(


documento char(8),
fecha date,
nota number(4,2),
constraints FK_notas_documento
foreign key (documento)
references alumnos(documento)
);

Ingresamos algunos registros:

insert into alumnos values('23333333','Acosta','Ana','Avellaneda 111');


insert into alumnos values('24444444','Bustos','Betina','Bulnes 222');
insert into alumnos values('25555555','Caseros','Carlos','Colon 333');
insert into alumnos values('26666666','Duarte','Daniel','Dinamarca 444');

insert into notas values('23333333','10/05/2007',5.3);


insert into notas values('23333333','15/07/2007',8.3);
insert into notas values('23333333','20/09/2007',7.4);
insert into notas values('24444444','10/05/2007',8.6);
insert into notas values('24444444','15/07/2007',9.4);
insert into notas values('25555555','10/05/2007',9);
insert into notas values('25555555','15/07/2007',6);
insert into notas values('26666666','10/05/2007',3.2);
insert into notas values('26666666','15/07/2007',5.3);
insert into notas values('26666666','20/09/2007',3.5);

Creamos o reemplazamos una vista normal que muestre el documento del alumnos y el promedio de sus notas:

create or replace view vista_promedios


as select a.documento,avg(nota) as promedio
from alumnos a
join notas n
on a.documento=n.documento
group by a.documento;

Eliminamos la vista materializada "vm_promedios:

drop materialized view vm_promedios;

Creamos una vista materializada que muestre el documento del alumnos y el promedio de sus notas:

create materialized view vm_promedios


as
select a.documento,avg(nota) as promedio
from alumnos a
join notas n
on a.documento=n.documento
group by a.documento;

Consultamos ambas vistas:

select *from vista_promedios;


select *from vm_promedios;
El resultado es el mismo.

Ahora agregamos algunas notas:

insert into notas values('23333333','12/10/2007',9);


insert into notas values('24444444','12/10/2007',7.5);
insert into notas values('25555555','12/10/2007',3);
insert into notas values('26666666','12/10/2007',4);

Consultamos ambas vistas y comparamos los promedios:

select *from vista_promedios;


select *from vm_promedios;

Los promedios de la vista actualizable han cambiado porque al ejecutar el primer "select" se consultaron las tablas
"notas" y "alumnos"; los promedios de la vista materializada no han cambiado, porque almacenaron el resultado de la
consulta, al ejecutar el segundo "select" no se consultaron las tablas "alumnos" y "notas".

91 - Procedimientos almacenados

Un procedimiento almacenado es un conjunto de instrucciones a las que se les da un nombre, se almacena en la base
de datos activa. Permiten agrupar y organizar tareas repetitivas.

Ventajas:

- comparten la lógica de la aplicación con las otras aplicaciones, con lo cual el acceso y las modificaciones de los
datos se hacen en un solo sitio.

- permiten realizar todas las operaciones que los usuarios necesitan evitando que tengan acceso directo a las tablas.

- reducen el tráfico de red; en vez de enviar muchas instrucciones, los usuarios realizan operaciones enviando una
única instrucción, lo cual disminuye el número de solicitudes entre el cliente y el servidor.

Un procedimiento almacenados puede hacer referencia a objetos que no existen al momento de crearlo. Los objetos
deben existir cuando se ejecute el procedimiento almacenado.

92 - Procedimientos Almacenados (crear- ejecutar)

Al crear un procedimiento almacenado, las instrucciones que contiene se analizan para verificar si son correctas
sintácticamente. Si se encuentra algún error, el procedimiento se compila, pero aparece un mensaje "con
advertencias" que indica tal situación.

Un procedimiento almacenado se invoca llamándolo.

En primer lugar se deben tipear y probar las instrucciones que se incluyen en el procedimiento almacenado, luego, si
se obtiene el resultado esperado, se crea el procedimiento.

Los procedimientos almacenados pueden hacer referencia a tablas, vistas, a funciones definidas por el usuario, a
otros procedimientos almacenados.

Un procedimiento almacenado pueden incluir cualquier cantidad y tipo de instrucciones DML (de manipulación de
datos, como insert, update, delete), no instrucciones DDL (de definición de datos, como create..., drop... alter...).

Para crear un procedimiento almacenado empleamos la instrucción "create procedure". La sintaxis básica parcial es:
create or replace procedure NOMBREPROCEDIMIENTO
as
begin
INSTRUCCIONES
end;

El bloque de instrucciones comienza luego de "begin" y acaba con "end".

Si empleamos "or replace", se sobreescribe (se reemplaza) un procedimiento existente; si se omite y existe un
procedimiento con el nombre que le asignamos, Oracle mostrará un mensaje de error indicando tal situación.

Para diferenciar los procedimientos almacenados del sistema de los procedimientos almacenados creados por el
usuario use un prefijo, por ejemplo "pa_" cuando les de el nombre.

Con las siguientes instrucciones creamos un procedimiento almacenado llamado "pa_libros_aumentar10" que
incrementa en un 10% el precio de todos los libros:

create procedure pa_libros_aumentar10


as
update libros set precio=precio+precio*0.1;

Entonces, creamos un procedimiento almacenado colocando "create procedure" (o "create or replace", si es que
desea reemplazar el existente), luego el nombre del procedimiento y seguido de "as" las sentencias que definen el
procedimiento.

Para ejecutar el procedimiento almacenado creado anteriormente tipeamos:

execute pa_libros_aumentar10;

Entonces, para ejecutar un procedimiento almacenado colocamos "execute" seguido del nombre del procedimiento.

Problema:

Una librería almacena los datos de sus libros en una tabla denominada "libros".

Eliminamos "libros", creamos la tabla y luego ingresamos algunos registros:

drop table libros;


create table libros(
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(5,2)
);

insert into libros values('Uno','Richard Bach','Planeta',15);


insert into libros values('Ilusiones','Richard Bach','Planeta',18);
insert into libros values('El aleph','Borges','Emece',25);
insert into libros values('Aprenda PHP','Mario Molina','Nuevo siglo',45);
insert into libros values('Matematica estas ahi','Paenza','Nuevo siglo',12);
insert into libros values('Java en 10 minutos','Mario Molina','Paidos',35);

La librería, frecuentemente, aumenta los precios de los libros en un 10%. Necesitamos un procedimiento almacenado
que actualice los precios de los libros aumentándolos en un 10%:

create or replace procedure pa_libros_aumentar10


as
begin
update libros set precio=precio+(precio*0.1);
end;

Lo ejecutamos:

execute pa_libros_aumentar10;
Verificamos que los precios han aumentado:

select *from libros;

Volvemos a ejecutar el procedimiento:

execute pa_libros_aumentar10;

Verificamos que los precios han aumentado nuevamente:

select *from libros;

93 - Procedimientos Almacenados (eliminar)

Los procedimientos almacenados se eliminan con "drop procedure". Sintaxis:

drop procedure NOMBREPROCEDIMIENTO;

Eliminamos el procedimiento almacenado llamado "pa_libros_aumentar10":

drop procedure pa_libros_aumentar10;

Si el procedimiento que queremos eliminar no existe, aparece un mensaje de error indicando tal situación.

Podemos eliminar una tabla referenciada en un procedimiento almacenado, Oracle lo permite, pero luego, al
ejecutar el procedimiento, aparecerá un mensaje de error porque la tabla referenciada no existe.

Si al crear un procedimiento almacenado colocamos "create or replace procedure...", el nuevo procedimiento


reemplaza al anterior.

94 - Procedimientos almacenados (parámetros de entrada)

Los procedimientos almacenados pueden recibir y devolver información; para ello se emplean parámetros.

Veamos los primeros. Los parámetros de entrada posibilitan pasar información a un procedimiento. Para que un
procedimiento almacenado admita parámetros de entrada se deben declarar al crearlo. La sintaxis es:

create or replace procedure NOMBREPROCEDIMIENTO (PARAMETRO in TIPODEDATO)


as
begin
INSTRUCCIONES;
end;

Los parámetros se definen luego del nombre del procedimiento. Pueden declararse varios parámetros por
procedimiento, se separan por comas.

Cuando el procedimiento es ejecutado, deben explicitarse valores para cada uno de los parámetros (en el orden que
fueron definidos), a menos que se haya definido un valor por defecto, en tal caso, pueden omitirse.

Creamos un procedimiento que recibe el nombre de una editorial como parámetro y luego lo utiliza para aumentar
los precios de tal editorial:

create or replace procedure pa_libros_aumentar10(aeditorial in varchar2)


as
begin
update libros set precio=precio+(precio*0.1)
where editorial=aeditorial;
end;

El procedimiento se ejecuta colocando "execute" (o "exec") seguido del nombre del procedimiento y un valor para el
parámetro:

execute pa_libros_aumentar10('Planeta');

Luego de definir un parámetro y su tipo, opcionalmente, se puede especificar un valor por defecto; tal valor es el
que asume el procedimiento al ser ejecutado si no recibe parámetros. Si no se coloca valor por defecto, un
procedimiento definido con parámetros no puede ejecutarse sin valores para ellos. El valor por defecto puede ser
"null" o una constante.

Creamos otro procedimiento que recibe 2 parámetros, el nombre de una editorial y el valor de incremento (que tiene
por defecto el valor 10):

create or replace procedure pa_libros_aumentar(aeditorial in varchar2,aporcentaje in


number default 10)
as
begin
update libros set precio=precio+(precio*aporcentaje/100)
where editorial=aeditorial;
end;

El procedimiento se ejecuta colocando "execute" (o "exec") seguido del nombre del procedimiento y los valores para
los parámetros separados por comas:

execute pa_libros_aumentar('Planeta',30);

Podemos omitir el segundo parámetro al invocar el procedimiento porque tiene establecido un valor por defecto:

execute pa_libros_aumentar('Planeta');

En caso que un procedimiento tenga definidos varios parámetros con valores por defecto y al invocarlo colocamos uno
solo, Oracle asume que es el primero de ellos. si son de tipos de datos diferentes, Oracle los convierte. Por ejemplo,
definimos un procedimiento almacenado de la siguiente manera:

create or replace procedure pa_libros_insertar


(atitulo in varchar2 default null, aautor in varchar2 default 'desconocido',
aeditorial in varchar2 default 'sin especificar', aprecio in number default 0)
as
begin
insert into libros values (atitulo,aautor,aeditorial,aprecio);
end;

Si luego llamamos al procedimiento enviándoles solamente el primer y cuarto parámetro correspondientes al título y
precio:

execute pa_libros_insertar('Uno',100);

Oracle asume que los argumentos son el primero y segundo, el registro que se almacenará será:

Uno,100,sin especificar,0;

Problema:

Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla y la creamos nuevamente:

drop table libros;

create table libros(


titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(5,2)
);

Ingresamos algunos registros:

insert into libros values ('Uno','Richard Bach','Planeta',15);


insert into libros values ('Ilusiones','Richard Bach','Planeta',12);
insert into libros values ('El aleph','Borges','Emece',25);
insert into libros values ('Aprenda PHP','Mario Molina','Nuevo siglo',50);
insert into libros values ('Matematica estas ahi','Paenza','Nuevo siglo',18);
insert into libros values ('Puente al infinito','Bach Richard','Sudamericana',14);
insert into libros values ('Antología','J. L. Borges','Paidos',24);
insert into libros values ('Java en 10 minutos','Mario Molina','Siglo XXI',45);
insert into libros values ('Cervantes y el quijote','Borges- Casares','Planeta',34);

Creamos un procedimiento que recibe el nombre de una editorial y luego aumenta en un 10% los precios de los libros
de tal editorial:

create or replace procedure pa_libros_aumentar10(aeditorial in varchar2)


as
begin
update libros set precio=precio+(precio*0.1)
where editorial=aeditorial;
end;

Ejecutamos el procedimiento:

execute pa_libros_aumentar10('Planeta');

Verificamos que los precios de los libros de la editorial "Planeta" han aumentado un 10%:

select *from libros;

Creamos otro procedimiento que recibe 2 parámetros, el nombre de una editorial y el valor de incremento (que tiene
por defecto el valor 10):

create or replace procedure pa_libros_aumentar(aeditorial in varchar2,aporcentaje in


number default 10)
as
begin
update libros set precio=precio+(precio*aporcentaje/100)
where editorial=aeditorial;
end;

Ejecutamos el procedimiento enviando valores para ambos argumentos:

execute pa_libros_aumentar('Planeta',30);

Consultamos la tabla "libros" para verificar que los precios de los libros de la editorial "Planeta" han sido aumentados
en un 30%:

select *from libros;

Ejecutamos el procedimiento "pa_libros_aumentar" omitiendo el segundo parámetro porque tiene establecido un


valor por defecto:

execute pa_libros_aumentar('Paidos');

Consultamos la tabla "libros" para verificar que los precios de los libros de la editorial "Paidos" han sido aumentados
en un 10% (valor por defecto):

select *from libros;

Definimos un procedimiento almacenado que ingrese un nuevo libro en la tabla "libros":


create or replace procedure pa_libros_insertar
(atitulo in varchar2 default null, aautor in varchar2 default 'desconocido',
aeditorial in varchar2 default 'sin especificar', aprecio in number default 0)
as
begin
insert into libros values (atitulo,aautor,aeditorial,aprecio);
end;

Llamamos al procedimiento sin enviarle valores para los parámetros:

execute pa_libros_insertar;

Veamos cómo se almacenó el registro:

select *from libros;

Llamamos al procedimiento enviándole valores solamente para el primer y cuarto parámetros correspondientes al
título y precio:

execute pa_libros_insertar('Uno',100);

Oracle asume que los argumentos son el primero y segundo, vea cómo se almacenó el nuevo registro:

select *from libros;

95 - Procedimientos almacenados (variables)

Los procedimientos almacenados pueden contener en su definición, variables locales, que existen durante el
procedimiento.

La sintaxis para declarar variables dentro de un procedimiento almacenado es la siguiente:

create or replace procedure NOMBREPROCEDIMIENTO (PARAMETRO in TIPODEDATO)


as
NOMBREVARIABLE TIPO;
begin
INSTRUCCIONES;
end;

Las variables se definen antes del bloque de sentencias; pueden declararse varias.

Creamos un procedimiento que recibe el nombre de un libro, en una variable guardamos el nombre del autor de tal
libro, luego buscamos todos los libros de ese autor y los almacenamos en una tabla:

create or replace procedure pa_autorlibro(atitulo in varchar2)


as
vautor varchar2;
begin
vautor= select autor from libros where titulo=atitulo;
drop table tabla1;
create table tabla1(
titulo varchar2(40),
precio number(6,2)
);
insert into tabla1
select titulo,precio
from libros
where autor=vautor;
end;

Ejecutamos el procedimiento: execute pa_autorlibro('Ilusiones');


Luego de definir un parámetro y su tipo, opcionalmente, se puede especificar un valor por defecto; tal valor es el
que asume el procedimiento al ser ejecutado si no recibe parámetros. Si no se coloca valor por defecto, un
procedimiento definido con parámetros no puede ejecutarse sin valores para ellos.

96 - Procedimientos Almacenados (informacion)

Los procedimientos almacenados son objetos, así que para obtener información de ellos pueden consultarse los
siguientes diccionarios:

- "user_objects": nos muestra todos los objetos de la base de datos seleccionada, incluidos los procedimientos. En la
columna "object_type" aparece "procedure" si es un procedimiento almacenado.

En el siguiente ejemplo solicitamos todos los objetos que son procedimientos:

select *from user_objects where object_type='PROCEDURE';

- "user_procedures": nos muestra todos los procedimientos almacenados de la base de datos actual. En el siguiente
ejemplo solicitamos información de todos los procedimientos que comienzan con "PA":

select *from user_procedures where object_name like 'PA_%';

97 - Funciones

Oracle ofrece varios tipos de funciones para realizar distintas operaciones. Hemos empleado varias de ellas.

Se pueden emplear las funciones del sistema en cualquier lugar en el que se permita una expresión en una sentencia
"select".

Ahora aprenderemos a crear nuestras propias funciones.

Las funciones, como los procedimientos almacenados son bloques de código que permiten agrupar y organizar
sentencias SQL que se ejecutan al invocar la función.

Las funciones tienen una estructura similar a la de los procedimientos. Como los procedimientos, las funciones tienen
una cabecera, una sección de declaración de variables y el bloque "begin...end" que encierra las acciones. Una
función, además contiene la cláusula "return".

Una función acepta parámetros, se invoca con su nombre y retorna un valor.

Para crear una función empleamos la instrucción "create function" o "create or replace function". Si empleamos "or
replace", se sobreescribe (se reemplaza) una función existente; si se omite y existe una función con el nombre que le
asignamos, Oracle mostrará un mensaje de error indicando tal situación.

La sintaxis básica parcial es:

create o replace function NOMBREFUNCION(PARAMETRO1 TIPODATO, PARAMETRON TIPODATO)


return TIPODEDATO is
DECLARACION DE VARIABLES
begin
ACCIONES;
return VALOR;
end;

La siguiente funcion recibe 1 parámetro, un valor a incrementar y retorna el valor ingresado como argumento con el
incremento del 10%:

create or replace function f_incremento10 (avalor number)


return number
is
begin
return avalor+(avalor*0.1);
end;

Podemos emplear las funciones en cualquier lugar en el que se permita una expresión en una sentencia "select", por
ejemplo:

select titulo,precio,f_incremento10(precio) from libros;

El resultado mostrará el título de cada libro, el precio y el precio incrementado en un 10% devuelto por la función.

La siguiente funcion recibe 2 parámetros, un valor a incrementar y el incremento y retorna el valor ingresado como
primer argumento con el incremento especificado por el segundo argumento:

create or replace function f_incremento (avalor number, aincremento number)


return number
is
begin
return avalor+(avalor*aincremento/100);
end;

Realizamos un "select" y llamamos a la función creada anteriormente, enviando como primer argumento el campo
"precio" y como segundo argumento el valor "20", es decir, incrementará en un 20" los precios de los libros:

select titulo,precio,f_incremento(precio,20) from libros;

El resultado nos mostrará el título de cada libro, el precio y el precio incrementado en un 20% devuelto por la
función.

Podemos realizar otros "select" llamando a la función con otro valor como segundo argumento, por ejemplo:

select titulo,precio,f_incremento(precio,50) from libros;

La siguiente función recibe un parámetro de tipo numérico y retorna una cadena de caracteres. Se define una
variable en la zona de definición de variables denominada "valorretornado" de tipo varchar. En el cuerpo de la
función empleamos una estructura condicional (if) para averiguar si el valor enviado como argumento es menor o
igual a 20, si lo es, almacenamos en la variable "valorretornado" la cadena "economico", en caso contrario guardamos
en tal variable la cadena "costoso"; al finalizar la estructura condicional retornamos la variable "valorretornado":

create or replace function f_costoso (avalor number)


return varchar
is
valorretornado varchar(20);
begin
valorretornado:='';
if avalor<=20 then
valorretornado:='economico';
else valorretornado:='costoso';
end if;
return valorretornado;
end;

Realizamos un "select" y llamamos a la función creada anteriormente, enviando como argumento el campo "precio":

select titulo,precio,f_costoso(precio) from libros;

El resultado mostrará el título de cada libro, el precio y el resultado devuelto por la función (si el precio es menor o
mayor a $20, la cadena "economico" o "costoso" respectivamente).

Entonces, una función es un bloque de código que implementa acciones y que es referenciado por un nombre. Puede
recibir argumentos. La diferencia con los procedimientos es que retornan un valor siempre.

Para asignar un valor a una variable, dentro de una función DEBE usarse ":=" (dos puntos e igual).
Si no se le definen parámetros a una función, no deben colocarse los paréntesis.

Podemos emplear una función sin incluir campos de una tabla. Por ejemplo:

select f_costoso (10) from dual;

Para almacenar los valores de un "select" debemos tipear:

select ...into VARIABLE from...

Por ejemplo:

select sum(precio) into variable from libros where autor='';

La siguiente función recibe dos valores numéricos como parámetros y retorna el promedio:

create or replace function f_promedio (avalor1 number, avalor2 number)


return number
is
begin
return (avalor1+avalor2)/2;
end;

Llamamos a la función "f_promedio":

exec f_promedio (10,20);

Problema:

Una librería almacena la información de sus libros en una tabla llamada "libros".

Eliminamos la tabla:

drop table libros;

Creamos la tabla "libros" con la siguiente estructura:

create table libros(


codigo number(3),
titulo varchar2(40),
autor varchar2(30),
precio number(5,2)
);

Ingresamos algunos registros:

insert into libros values(100,'Uno','Richard Bach',15);


insert into libros values(300,'Aprenda PHP','Mario Molina',55);
insert into libros values(102,'Matematica estas ahi','Paenza',18);
insert into libros values(105,'El aleph','Borges',25);
insert into libros values(109,'El experto en laberintos','Gaskin',20);
insert into libros values(204,'Alicia en el pais de las maravillas','Carroll',31);

Creamos o reemplazamos una función que reciba 1 parámetro (un valor numérico a incrementar) y retorne el valor
ingresado como argumento con el incremento del 10%:

create or replace function f_incremento10 (avalor number)


return number
is
begin
return avalor+(avalor*0.1);
end;
Realizamos un "select" sobre "libros" que muestre el título, precio y llamamos a la función creada anteriormente para
que nos devuelva el precio incrementado en un 10%:

select titulo,precio,f_incremento10(precio) from libros;

Creamos otra función que reciba 2 parámetros, un valor a incrementar y el incremento, y que nos retorne el valor
ingresado como primer argumento con el incremento especificado por el segundo argumento:

create or replace function f_incremento (avalor number, aincremento number)


return number
is
begin
return avalor+(avalor*aincremento/100);
end;

Realizamos un "select" sobre "libros" que muestre el título, precio y el precio incrementado en un 20% (llamando a la
función creada anteriormente, enviando como primer argumento el campo "precio" y como segundo argumento el
valor "20"):

select titulo,precio,f_incremento(precio,20) from libros;

Realizamos otro "select" similar al anterior, pero en esta ocasión le enviamos a la función otro valor como segundo
argumento:

select titulo,precio,f_incremento(precio,50) from libros;

Creamos o reemplazamos una función que recibe un parámetro de tipo numérico y retorna una cadena de caracteres.
Se define una variable en la zona de definición de variables denominada "valorretornado" de tipo varchar. En el
cuerpo de la función empleamos una estructura condicional (if) para averiguar si el valor enviado como argumento es
menor o igual a 20, si lo es, almacenamos en la variable "valorretornado" la cadena "economico", en caso contrario
guardamos en tal variable la cadena "costoso"; al finalizar la estructura condicional retornamos la variable
"valorretornado":

create or replace function f_costoso (avalor number)


return varchar2
is
valorretornado varchar2(20);
begin
valorretornado:='';
if avalor<=20 then
valorretornado:='economico';
else valorretornado:='costoso';
end if;
return valorretornado;
end;

Realizamos un "select" para mostrar el título, precio y una cadena que indique si el libro es económico o costoso
(llamando a la función creada anteriormente):

select titulo,precio,f_costoso(precio) from libros;.

Podemos emplear la función "f_costoso" sin incluir campos de una tabla. Por ejemplo:

select f_costoso (10) from dual;

98 - Control de flujo (if)

Las estructuras de control pueden ser condicionales y repetitivas.

Estructuras de control de flujo, bifurcaciones condicionales y bucles. Veremos las condicionales. existen 2: if y case.
Existen palabras especiales que pertenecen al lenguaje de control de flujo que controlan la ejecución de las
sentencias, los bloques de sentencias y procedimientos almacenados. Tales palabras son: "begin... end", "if... else",
"while", "break" y "continue", "return" y "waitfor".

"if... else" testea una condición; se emplea cuando un bloque de sentencias debe ser ejecutado si una condición se
cumple y si no se cumple, se debe ejecutar otro bloque de sentencias diferente.

Sintaxis:

if (CONDICION) then
SENTENCIAS-- si la condición se cumple
else
SENTENCIAS-- si la condición resulta falsa
end if;

Problema:

Un profesor almacena las notas de sus alumnos en una tabla denominada "notas".

Eliminamos la tabla:

drop table notas;

Creamos la tabla con la siguiente estructura:

create table notas(


nombre varchar2(30),
nota number(4,2)
);

Ingresamos algunos registros:

insert into notas values('Acosta Ana', 6.7);


insert into notas values('Bustos Brenda', 9.5);
insert into notas values('Caseros Carlos', 3.7);
insert into notas values('Dominguez Daniel', 2);
insert into notas values('Fuentes Federico', 8);
insert into notas values('Gonzalez Gaston', 7);
insert into notas values('Juarez Juana', 4);
insert into notas values('Lopez Luisa',5.3);

Creamos o reemplazamos la función "f_condicion" que recibe una nota y retorna una cadena de caracteres indicando
si aprueba o no:

create or replace function f_condicion (anota number)


return varchar2
is
condicion varchar2(20);
begin
condicion:='';
if anota<4 then
condicion:='desaprobado';
else condicion:='aprobado';
end if;
return condicion;
end;

Realizamos un "select" sobre "notas" mostrando el nombre y nota del alumno y en una columna su condición
(empleando la función creada anteriormente):

select nombre, nota, f_condicion(nota) from notas;

En el siguiente ejemplo omitimos la cláusula "else" porque sólo indicaremos acciones en caso que el "if" sea
verdadero:

create or replace function f_condicion (anota number)


return varchar2
is
condicion varchar2(20);
begin
condicion:='aprobado';
if anota<4 then
condicion:='desaprobado';
end if;
return condicion;
end;

Realizamos el "select" sobre "notas" mostrando la misma información que antes:

select nombre, nota, f_condicion(nota) from notas;

En el siguiente ejemplo colocamos un "if" dentro de otro "if". En el cuerpo de la función controlamos si la nota es
menor a 4 (retorna "desaprobado"), luego, dentro del "else", controlamos si la nota es menor a 8 (retorna "regular") y
si no lo es ("else"), retorna "promocionado":

create or replace function f_condicion (anota number)


return varchar2
is
condicion varchar2(20);
begin
condicion:='';
if anota<4 then
condicion:='desaprobado';
else
if anota<8 then
condicion:='regular';
else
condicion:='promocionado';
end if;
end if;
return condicion;
end;

Realizamos el "select" sobre "notas" mostrando la misma información que antes:

select nombre, nota, f_condicion(nota) from notas;

Simplificamos la función anteriormente creada empleando la sintaxis "if...elsif":

create or replace function f_condicion (anota number)


return varchar2
is
condicion varchar2(20);
begin
condicion:='';
if anota<4 then
condicion:='desaprobado';
elsif anota<8 then
condicion:='regular';
else
condicion:='promocionado';
end if;
return condicion;
end;

Realizamos el "select" sobre "notas" mostrando la misma información que antes:

select nombre, nota, f_condicion(nota) from notas;

99 - Control de flujo (case)

Vimos que hay dos estructuras condicionales, aprendimos "if", nos detendremos ahora en "case".
Aprendimos que el "if" se empleaba cuando teníamos 2 cursos de acción, es decir, se evalúa una condición y se
ejecutan diferentes bloques de sentencias según el resultado de la condición sea verdadero o falso.

La estructura "case" es similar a "if", sólo que se pueden establecer varias condiciones a cumplir. Con el "if" solamente
podemos obtener dos salidas, cuando la condición resulta verdadera y cuando es falsa, si queremos más opciones
podemos usar "case".

Sintaxis:

case VALORACOMPARAR
when VALOR1 then SENTENCIAS;
when VALOR2 then SENTENCIAS;
when VALOR3 then SENTENCIAS;
else SENTENCIAS;
end case;

Entonces, se emplea "case" cuando tenemos varios cursos de acción; es decir, por cada valor hay un "when... then"; si
encuentra un valor coincidente en algún "where" ejecuta el "then" correspondiente a ese "where", si no hay ninguna
coincidencia, se ejecuta el "else". Finalmente se coloca "end case" para indicar que el "case" ha finalizado.

Necesitamos, dada una fecha, obtener el nombre del mes en español. Podemos utilizar la estructura condicional
"case". Para ello crearemos una función que reciba una fecha y retorne una cadena de caracteres indicando el
nombre del mes de la fecha enviada como argumento:

create or replace function f_mes(afecha date)


return varchar2
is
mes varchar2(20);
begin
mes:='enero';
case extract(month from afecha)
when 1 then mes:='enero';
when 2 then mes:='febrero';
when 3 then mes:='marzo';
when 4 then mes:='abril';
when 5 then mes:='mayo';
when 6 then mes:='junio';
when 7 then mes:='julio';
when 8 then mes:='agosto';
when 9 then mes:='setiembre';
when 10 then mes:='octubre';
when 11 then mes:='noviembre';
else mes:='diciembre';
end case;
return mes;
end;

Si probamos la función anterior enviándole la siguiente fecha:

select f_mes('10/10/2008') from dual;

obtendremos como resultado "octubre".

La siguiente función recibe una fecha y retorna si se encuentra en el 1º, 2º, 3º ó 4º trimestre del año:

create or replace function f_trimestre(afecha date)


return varchar2
is
mes varchar2(20);
trimestre number;
begin
mes:=extract(month from afecha);
trimestre:=4;
case mes
when 1 then trimestre:=1;
when 2 then trimestre:=1;
when 3 then trimestre:=1;
when 4 then trimestre:=2;
when 5 then trimestre:=2;
when 6 then trimestre:=2;
when 7 then trimestre:=3;
when 8 then trimestre:=3;
when 9 then trimestre:=3;
else trimestre:=4;
end case;
return trimestre;
end;

La cláusula "else" puede omitirse, en caso que no se encuentre coincidencia con ninguno de los "where", se sale del
"case" sin modificar el valor de la variable "trimestre", con lo cual, retorna el valor 4, que es que que almacenaba
antes de entrar a la estructura "case".

Otra diferencia con "if" es que el "case" toma valores puntuales, no expresiones. No es posible realizar comparaciones
en cada "where". La siguiente sintaxis provocaría un error:

...
case mes
when >=1 then trimestre:=1;
when >=4 then trimestre:=2;
when >=7 then trimestre:=3;
when >=10 then trimestre:=4;
end case;

Si hay más de una sentencia en un "when...then" NO es necesario delimitarlas con "begin... end".

Podemos emplear "case" dentro de un "select". Veamos un ejemplo similar a la función anterior:

select nombre,fechanacimiento,
case extract(month from fechanacimiento)
when 1 then 1
when 2 then 1
when 3 then 1
when 4 then 2
when 5 then 2
when 6 then 2
when 7 then 3
when 8 then 3
when 9 then 3
else 4
end as trimestre
from empleados
order by trimestre;

Problema:

Trabajamos con la tabla "empleados". Eliminamos la tabla y luego la creamos con la siguiente estructura:

drop table empleados;

create table empleados (


documento char(8),
nombre varchar(30),
fechanacimiento date
);

Ingresamos algunos registros:

insert into empleados values('20111111','Acosta Ana','10/05/1968');


insert into empleados values('22222222','Bustos Bernardo','09/07/1970');
insert into empleados values('22333333','Caseros Carlos','15/10/1971');
insert into empleados values('23444444','Fuentes Fabiana','25/01/1972');
insert into empleados values('23555555','Gomez Gaston','28/03/1979');
insert into empleados values('24666666','Juarez Julieta','18/02/1981');
insert into empleados values('25777777','Lopez Luis','17/09/1978');
insert into empleados values('26888888','Morales Marta','22/12/1975');
Nos interesa el nombre del mes en el cual cada empleado cumple años. Podemos utilizar la estructura condicional
"case". Para ello crearemos una función que reciba una fecha y retorne una cadena de caracteres indicando el
nombre del mes de la fecha enviada como argumento:

create or replace function f_mes(afecha date)


return varchar2
is
mes varchar2(20);
begin
mes:='enero';
case extract(month from afecha)
when 1 then mes:='enero';
when 2 then mes:='febrero';
when 3 then mes:='marzo';
when 4 then mes:='abril';
when 5 then mes:='mayo';
when 6 then mes:='junio';
when 7 then mes:='julio';
when 8 then mes:='agosto';
when 9 then mes:='setiembre';
when 10 then mes:='octubre';
when 11 then mes:='noviembre';
else mes:='diciembre';
end case;
return mes;
end;

Recuperamos el nombre del empleado y el mes de su cumpleaños realizando un "select":

select nombre, f_mes(fechanacimiento) as cumpleaños from empleados;

Podemos probar la función creada anteriormente enviándole la siguiente fecha:

select f_mes('10/10/2008') from dual;

obtenemos como resultado "octubre".

Realizamos una función que reciba una fecha y retorne si se encuentra en el 1º, 2º, 3º ó 4º trimestre del año:

create or replace function f_trimestre(afecha date)


return varchar2
is
mes varchar2(20);
trimestre number;
begin
mes:=extract(month from afecha);
trimestre:=4;
case mes
when 1 then trimestre:=1;
when 2 then trimestre:=1;
when 3 then trimestre:=1;
when 4 then trimestre:=2;
when 5 then trimestre:=2;
when 6 then trimestre:=2;
when 7 then trimestre:=3;
when 8 then trimestre:=3;
when 9 then trimestre:=3;
else trimestre:=4;
end case;
return trimestre;
end;

Recuperamos el nombre del empleado y el trimestre de su cumpleaños empleando la función creada anteriormente:

select nombre, f_trimestre(fechanacimiento) from empleados;

Vamos a emplear "case" dentro de un "select". Veamos un ejemplo similar a la función anterior:

select nombre,fechanacimiento,
case extract(month from fechanacimiento)
when 1 then 1
when 2 then 1
when 3 then 1
when 4 then 2
when 5 then 2
when 6 then 2
when 7 then 3
when 8 then 3
when 9 then 3
else 4
end as trimestre
from empleados
order by trimestre;

100 - Control de flujo (loop)

Las estructuras repetitivas permiten ejecutar una secuencia de sentencias varias veces. Hay tres estructuras
repetitivas, o bucles: loop, for y while.

Comenzamos por "loop", que es la más simple. Veremos la sintaxis del bucle "loop" que combina una condición y la
palabra "exit".

Sintaxis:

loop
SENTENCIAS;
exit when CONDICION;
SENTENCIAS;
end loop;

Cuando se llega a la línea de código en la que se encuentra la condición "exit when", se evalúa dicha condición, si
resulta cierta, se salta a la línea donde se encuentra "end loop", saliendo del bucle, omitiendo las sentencias
existentes antes del "end loop"; en caso contrario, si la condición resulta falsa, se continúa con las siguientes
sentencias y al llegar a "end loop" se repite el bucle.

La sintaxis anterior es equivalente a la siguiente:

loop
SENTENCIAS
if CONDICION then
exit;
end if;
SENTENCIAS
end loop;

En este ejemplo se muestra la tabla del 3. Se va incrementando la variable "multiplicador" y se almacena en una
variable "resultado"; el ciclo se repite hasta que el multiplicador llega a 5, es decir, 6 veces.

set serveroutput on;


declare
resultado number;
multiplicador number:=0;
begin
loop
resultado:=3*multiplicador;
dbms_output.put_line('3x'||to_char(multiplicador)||'='||to_char(resultado));
multiplicador:=multiplicador+1;
exit when multiplicador>5;
end loop;
end;

En el siguiente ejemplo se muestra la tabla del 4. Se va incrementando la variable "multiplicador" y se almacena en


una variable "resultado"; el ciclo se repite hasta que la variable resultado llega o supera el valor 50.

declare
resultado number;
multiplicador number:=0;
begin
loop
resultado:=4*multiplicador;
exit when resultado>=50;
dbms_output.put_line('4x'||to_char(multiplicador)||'='||to_char(resultado));
multiplicador:=multiplicador+1;
end loop;
end;

Cuando se cumple la condición del "exit when" (supera el valor 50), no se ejecutan las sentencias siguientes (líneas
de salida y de incremento de "multiplicador"), se salta a "end loop" finalizando el bucle.

Problema:

Mostramos la tabla del 3.

En primer lugar activamos el paquete que contiene los procedimientos necesarios para la salida de datos por pantalla
(set serveroutput on) y habilitamos las llamadas a tales procedimientos.

Declaramos dos variables, "resultado" almacenará el resultado de las multiplicaciones (le asignamos el valor cero) y
"multiplicador" será la que contenga los diferentes valores por los cuales multiplicaremos 3 (asignándole el valor
cero).

En el ciclo repetitivo se almacena en "resultado" el primer valor ("multiplicador" contiene 0 así que el resultado es
cero), luego se imprime, se incrementa la variable "multiplicador" (ahora contiene 1) y se evalúa la condición, dado
que "multiplicador" no es mayor a 5, el ciclo se repite.

Cuando la condición resulta cierta, es decir, cuando "multiplicador" sea igual a 6, el ciclo acabará:

set serveroutput on;


execute dbms_output.enable (1000000);
declare
resultado number;
multiplicador number:=0;
begin
loop
resultado:=3*multiplicador;
dbms_output.put_line('3x'||to_char(multiplicador)||'='||to_char(resultado));
multiplicador:=multiplicador+1;
exit when multiplicador>5;
end loop;
end;

En el siguiente ejemplo se muestra la tabla del 4. Se almacena en una variable "resultado" el resultado de la
multiplicación, se chequea la condición, se imprime el resultado y se va incrementando la variable "multiplicador"; el
ciclo se repite hasta que la variable "resultado" llega o supera el valor 50:

declare
resultado number;
multiplicador number:=0;
begin
loop
resultado:=4*multiplicador;
exit when resultado>=50;
dbms_output.put_line('4x'||to_char(multiplicador)||'='||to_char(resultado));
multiplicador:=multiplicador+1;
end loop;
end;

Note que, cuando "resultado" cumple la condición del "exit when" (supera el valor 50), no se ejecutan las líneas de
salida y de incremento de "multiplicador", se salta a "end loop" finalizando el bucle.
101 - Control de flujo (for)

Vimos que hay tres estructuras repetitivas, o bucles, ya estudiamos "loop". Continuamos con "for".

En la sentencia "for... loop" se especifican dos enteros, un límite inferior y un límite superior, es decir, un rango de
enteros, las sentencias se ejecutan una vez por cada entero; en cada repetición del bucle, la variable contador del
"for" se incrementa en uno.

Sintaxis:

for VARIABLECONTADOR in LIMITEINFERIOR..LIMITESUPERIOR loop


SENTENCIAS;
end loop;

"Variablecontador" debe ser una variable numérica entera; "limiteinferior" y "limitesuperior" son expresiones
numéricas. La variable que se emplea como contador NO se define, se define automáticamente de tipo entero al
iniciar el bucle y se liberará al finalizarlo.

En el siguiente ejemplo se muestra la tabla del 3. La variable "f" comienza en cero (límite inferior del for) y se va
incrementando de a uno; el ciclo se repite hasta que "f" llega a 5 (límite superior del for), cuando llega a 6, el bucle
finaliza.

set serveroutput on;


begin
for f in 0..5 loop
dbms_output.put_line('3x'||to_char(f)||'='||to_char(f*3));
end loop;
end;

Si queremos que el contador se decremente en cada repetición, en lugar de incrementarse, debemos colocar
"reverse" luego de "in" y antes del límite inferior; el contador comenzará por el valor del límite superior y finalizará al
llegar al límite inferior decrementando de a uno. En este ejemplo mostramos la tabla del 3 desde el 5 hasta el cero:

begin
for f in reverse 0..5 loop
dbms_output.put_line('3*'||to_char(f)||'='||to_char(f*3));
end loop;
end;

Se pueden colocar "for" dentro de otro "for". Por ejemplo, con las siguientes líneas imprimimos las tablas del 2 y del 3
del 1 al 9:

begin
for f in 2..3 loop
dbms_output.put_line('tabla del '||to_char(f));
for g in 1..9 loop
dbms_output.put_line(to_char(f)||'x'||to_char(g)||'='||to_char(f*g));
end loop;--fin del for g
end loop;--fin del for f
end;

Problema:

En el siguiente ejemplo se muestra la tabla del 3. La variable "f" comienza en cero (límite inferior del for) y se va
incrementando de a uno; el ciclo se repite hasta que "f" llega a 5 (límite superior del for), cuando llega a 6, el bucle
finaliza.

set serveroutput on;


execute dbms_output.enable(20000);
begin
for f in 0..5 loop
dbms_output.put_line('3x'||to_char(f)||'='||to_char(f*3));
end loop;
end;
Para que el contador "f" se decremente en cada repetición, colocamos "reverse"; el contador comenzará por el valor
del límite superior (5) y finalizará al llegar al límite inferior (0) decrementando de a uno. En este ejemplo mostramos
la tabla del 3 desde el 5 hasta el 0:

begin
for f in reverse 0..5 loop
dbms_output.put_line('3*'||to_char(f)||'='||to_char(f*3));
end loop;
end;

Se pueden colocar "for" dentro de otro "for". Por ejemplo, con las siguientes líneas imprimimos las tablas del 2 y del 3
del 1 al 9:

begin
for f in 2..3 loop
dbms_output.put_line('tabla del '||to_char(f));
for g in 1..9 loop
dbms_output.put_line(to_char(f)||'x'||to_char(g)||'='||to_char(f*g));
end loop;--fin del for g
end loop;--fin del for f
end;

102 - Control de flujo (while loop)

Ya estudiamos dos de las tres estructuras repetitivas. Continuamos con "while".

Vimos que las sentencias repetitivas permiten ejecutar una secuencia de sentencias varias veces.

Se coloca la palabra "loop" antes de las sentencias y al final "end loop".

"while...loop" (mientras) ejecuta repetidamente una instrucción (o bloque de instrucciones) siempre que la condición
sea verdadera.

Sintaxis básica:

while CONDICION loop


SENTENCIAS
end loop;

La diferencia entre "while...loop" y "for...loop" es que en la segunda se puede establecer la cantidad de repeticiones
del bucle con el valor inicial y final. Además, el segundo siempre se ejecuta, al menos una vez, en cambio el primero
puede no ejecutarse nunca, caso en el cual al evaluar la condicion por primera vez resulte falsa.

En el siguiente ejemplo se muestra la tabla del 3 hasta el 5:

set server output on;


execute dbms_output.enable (20000);
declare
numero number:=0;
resultado number;
begin
while numero<=5 loop
resultado:=3*numero;
dbms_output.put_line('3*'||to_char(numero)||'='||to_char(resultado));
numero:=numero+1;
end loop;
end;

La condición establece que se multiplique la variable "numero" por 3 mientras "numero" sea menor o igual a 5. En el
bloque de sentencias se almacena en "resultado" la multiplicación, luego se escribe tal valor y finalmente se
incrementa la variable "numero" en 1.

Hacemos el seguimiento: cuando se inicia el bucle, la variable "numero" tiene el valor 0, se ejecuta la primer linea de
sentencias y "resultado" almacena el valor 0, se imprime (0) y se incrementa la variable "numero", ahora contiene el
valor 1; se vuelve a la condición, como la condición es verdadera (1 es menor que 5), se vuelven a ejecutar las
sentencias (resultado almacena 3, se imprime (3), se guarda en numero el valor 2), se vuelve a evaluar la condición,
como resulta cierta (2 es menor que 5), se vuelven a ejecutar las sentencias (resultado almacena 6, se imprime, se
guarda en numero el valor 3), se vuelve a evaluar la condición, como resulta cierta (3 es menor que 5), se vuelven a
ejecutar las sentencias (resultado almacena 9, se imprime, se guarda en numero el valor 4), se vuelve a evaluar la
condición, como resulta cierta (4 es menor que 5), se vuelven a ejecutar las sentencias (resultado almacena 12, se
imprime, se guarda en numero el valor 5), se vuelve a evaluar la condición, como resulta cierta (5 es menor o igual a
5), se vuelven a ejecutar las sentencias (resultado almacena 15, se imprime, se guarda en numero el valor 6), se
vuelve a evaluar la condición, como resulta falsa (6 NO es menor ni igual a 5), se sale de la estructura.

Problema:

Mostramos la tabla del 3 hasta el 5. En primer lugar activamos el paquete "dbms_output" para poder emplear los
procedimientos de dicho paquete, luego ejecutamos el procedimiento "dbms_output.enable" para habilitar las
llamadas a los procedimientos y funciones de tal paquete, así podremos emplear la función de salida
"dbms_output.put_line".

set server output on;


execute dbms_output.enable (1000000);

Luego, declaramos la variable numérica "numero" y le asignamos el valor cero; tal variable contendrá el
multiplicando. También declaramos la variable "resultado" de tipo numérico que almacenará el resultado de cada
multiplicación. Comenzamos el bloque "begin... end" con la estructura repetitiva "while... loop". La condición
chequea si el valor de la variable "numero" es menmor o igual a 5; las sentencias que se repetirán serán:

- multiplicar "numero" por 3 y asignárselo a "resultado",

- imprimir "resultado" y

- incrementar la variable "numero" para que la siguiente vez que se entre al bucle repetitivo se multiplique 3 por otro
número.

declare
numero number:=0;
resultado number;
begin
while numero<=5 loop
resultado:=3*numero;
dbms_output.put_line('3*'||to_char(numero)||'='||to_char(resultado));
numero:=numero+1;
end loop;
end;

103 - Disparador (trigger)

Un "trigger" (disparador o desencadenador) es un bloque de código que se ejecuta automáticamente cuando ocurre
algún evento (como inserción, actualización o borrado) sobre una determinada tabla (o vista); es decir, cuando se
intenta modificar los datos de una tabla (o vista) asociada al disparador.

Se crean para conservar la integridad referencial y la coherencia entre los datos entre distintas tablas; para registrar
los cambios que se efectúan sobre las tablas y la identidad de quien los realizó; para realizar cualquier acción cuando
una tabla es modificada; etc.

Si se intenta modificar (agregar, actualizar o eliminar) datos de una tabla asociada a un disparador, el disparador se
ejecuta (se dispara) en forma automática.

La diferencia con los procedimientos almacenados del sistema es que los triggers:

- no pueden ser invocados directamente; al intentar modificar los datos de una tabla asociada a un disparador, el
disparador se ejecuta automáticamente.
- no reciben y retornan parámetros.

- son apropiados para mantener la integridad de los datos, no para obtener resultados de consultas.

Sintaxis general para crear un disparador:

create or replace trigger NOMBREDISPARADOR


MOMENTO-- before, after o instead of
EVENTO-- insert, update o delete
of CAMPOS-- solo para update
on NOMBRETABLA
NIVEL--puede ser a nivel de sentencia (statement) o de fila (for each row)
when CONDICION--opcional
begin
CUERPO DEL DISPARADOR--sentencias
end NOMBREDISPARADOR;

Los triggers se crean con la instrucción "create trigger" seguido del nombre del disparador. Si se agrega "or replace" al
momento de crearlo y ya existe un trigger con el mismo nombre, tal disparador será borrado y vuelto a crear.

"MOMENTO" indica cuando se disparará el trigger en relación al evento, puede ser BEFORE (antes), AFTER (después) o
INSTEAD OF (en lugar de). "before" significa que el disparador se activará antes que se ejecute la operación (insert,
update o delete) sobre la tabla, que causó el disparo del mismo. "after" significa que el trigger se activará después
que se ejecute la operación que causó el disparo. "instead of" sólo puede definirse sobre vistas, anula la sentencia
disparadora, se ejecuta en lugar de tal sentencia (ni antes ni después).

"EVENTO" especifica la operación (acción, tipo de modificación) que causa que el trigger se dispare (se active), puede
ser "insert", "update" o "delete"; DEBE colocarse al menos una acción, puede ser más de una, en tal caso se separan
con "or". Si "update" lleva una lista de atributos, el trigger sólo se ejecuta si se actualiza algún atributo de la lista.

"on NOMBRETABLA" indica la tabla (o vista) asociada al disparador;

"NIVEL" puede ser a nivel de sentencia o de fila. "for each row" indica que el trigger es a nivel de fila, es decir, se
activa una vez por cada registro afectado por la operación sobre la tabla, cuando una sola operación afecta a varios
registros. Los triggers a nivel de sentencia, se activan una sola vez (antes o después de ejecutar la operación sobre la
tabla). Si no se especifica, o se especifica "statement", es a nivel de sentencia.

"CUERPO DEL DISPARADOR" son las acciones que se ejecutan al dispararse el trigger, las condiciones que determinan
cuando un intento de inserción, actualización o borrado provoca las acciones que el trigger realizará. El bloque se
delimita con "begin... end".

Entonces, un disparador es un bloque de código asociado a una tabla que se dispara automáticamente antes o
después de una sentencia "insert", "update" o "delete" sobre la tabla.

Se crean con la instrucción "create trigger" especificando el momento en que se disparará, qué evento lo
desencadenará (inserción, actualización o borrado), sobre qué tabla (o vista) y las instrucciones que se ejecutarán.

Los disparadores pueden clasificarse según tres parámetros:

- el momento en que se dispara: si se ejecutan antes (before) o después (after) de la sentencia.

- el evento que los dispara: insert, update o delete, según se ejecute una de estas sentencias sobre la tabla.

- nivel: dependiendo si se ejecuta para cada fila afectada en la sentencia (por cada fila) o bien una única vez por
sentencia independientemente de la filas afectadas (nivel de sentencia).

Consideraciones generales:

- Las siguientes instrucciones no están permitidas en un desencadenador: create database, alter database, drop
database, load database, restore database, load log, reconfigure, restore log, disk init, disk resize.

- Se pueden crear varios triggers para cada evento, es decir, para cada tipo de modificación (inserción, actualización
o borrado) para una misma tabla. Por ejemplo, se puede crear un "insert trigger" para una tabla que ya tiene otro
"insert trigger".
A continuación veremos la creación de disparadores para los distintos sucesos (inserción, borrado, actualización) y
todas las cláusulas adicionales.

104 - Disparador (información)

Los triggers son objetos, así que para obtener información de ellos pueden consultarse los siguientes diccionarios:

- "user_objects": nos muestra todos los objetos de la base de datos seleccionada, incluidos los triggers. En la columna
"object_type" aparece "trigger" si es un disparador. En el siguiente ejemplo solicitamos todos los objetos que son
disparadores:

select *from user_objects where object_type='TRIGGER';

- "user_triggers": nos muestra todos los triggers de la base de datos actual. Muestra el nombre del desencadenador
(trigger_name), si es before o after y si es a nivel de sentencia o por fila (trigger_type), el evento que lo
desencadena (triggering_event), a qué objeto está asociado, si tabla o vista (base_object_type), el nombre de la
tabla al que está asociado (table_name), los campos, si hay referencias, el estado, la descripción, el cuerpo
(trigger_body), etc. En el siguiente ejemplo solicitamos información de todos los disparadores que comienzan con
"TR":

select trigger_name, triggering_event from user_triggers where trigger_name like 'TR%';

- "user_source": se puede visualizar el código fuente almacenado en un disparador consultando este diccionario: En el
siguiente ejemplo solicitamos el código fuente del objeto "TR_insertar_libros":

select *from user_source where name='TR_INSERTAR_LIBROS';

105 - Disparador de inserción a nivel de sentencia

Dijimos que un disparador está asociado a una tabla y a una operación específica (inserción, actualización o borrado).

A continuación veremos la creación de un disparador para el evento de inserción: "insert triger".

La siguiente es la sintaxis para crear un trigger de inserción que se dispare cada vez que se ejecute una sentencia
"insert" sobre la tabla especificada, es decir, cada vez que se intenten ingresar datos en la tabla:

create or replace trigger NOMBREDISPARADOR


MOMENTO insert
on NOMBRETABLA
begin
CUERPO DEL TRIGGER;
end NOMBREDISPARADOR;

Analizamos la sintaxis:

Luego de la instrucción "create trigger" se coloca el nombre del disparador. Si se agrega "or replace" al momento de
crearlo y ya existe un trigger con el mismo nombre, tal disparador será borrado y vuelto a crear.

"MOMENTO" indica cuando se disparará el trigger en relación al evento, puede se BEFORE (antes) o AFTER (después).
Se especifica el evento que causa que el trigger se dispare, en este caso "insert", ya que el trigger se activará cada
vez que se ejecute la sentencia "insert" sobre la tabla especificada luego de "on".

Es un trigger a nivel de sentencia, es decir, se dispara una sola vez por cada sentencia "insert", aunque la sentencia
ingrese varios registros. Es el valor por defecto si no se especifica.
Finalmente se coloca el cuerpo del trigger dentro del bloque "begin.. end".

Las siguientes sentencias disparan un trigger de inserción:

insert into TABLA values ...;


insert into TABLA select ... from...;

Ejemplo: Creamos un desencadenador que se dispara cada vez que se ejecuta un "insert" sobre la tabla "libros":

create or replace trigger tr_ingresar_libros


before insert
on libros
begin
insert into Control values(user,sysdate);
end tr_ingresar_libros;

Analizamos el trigger anterior:

"create or replace trigger" junto al nombre del disparador que tiene un prefijo "tr" para reconocer que es un trigger,
referencia el evento que lo disparará y la tabla asociada.

Para identificar fácilmente los disparadores de otros objetos se recomienda usar un prefijo y darles el nombre de la
tabla para la cual se crean junto al tipo de acción.

El disparador se activará antes ("before") del evento "insert" sobre la tabla "libros", es decir, se disparará ANTES que
se realice una inserción en "libros". El trigger está definido a nivel de sentencia, se activa una vez por cada
instrucción "insert" sobre "libros". El cuerpo del disparador se delimita con "begin... end", allí se especifican las
acciones que se realizarán al ocurrir el evento de inserción, en este caso, ingresar en la tabla "control" el nombre del
usuario que alteró la tabla "libros" (obtenida mediante la función "user") y la fecha en que lo hizo (mediante la
función "sysdate").

Problema:

Una librería almacena los datos de sus libros en una tabla denominada "libros" y controla las acciones que los
empleados realizan sobre dicha tabla almacenando en la tabla "control" el nombre del usuario y la fecha, cada vez
que se ingresa un registro en la tabla "libros".

Eliminamos la tabla:

drop table libros;

Creamos la tabla con la siguiente estructura:

create table libros(


codigo number(6),
titulo varchar2(40),
autor varchar2(30),
precio number(6,2)
);

Creamos la tabla "control", antes la eliminamos:

drop table control;


create table control(
usuario varchar2(30),
fecha date
);

Creamos un disparador que se dispare cada vez que se ingrese un registro en "libros"; el trigger debe ingresar en la
tabla "control", el nombre del usuario, la fecha y la hora en la cual se realizó un "insert" sobre "libros":

create or replace trigger tr_ingresar_libros


before insert
on libros
begin
insert into Control values(user,sysdate);
end tr_ingresar_libros;

Establecemos el formato de fecha para que muestre "DD/MM/YYYY HH24:MI":

alter session set NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI';

Veamos qué nos informa el diccionario "user_triggers" respecto del trigger anteriormente creado:

select *from user_triggers where trigger_name ='TR_INGRESAR_LIBROS';

obtenemos la siguiente información:

- trigger_name: nombre del disparador;

- trigger_type: momento y nivel, en este caso es un desencadenador "before" y a nivel de sentencia (statement);

- triggering_event: evento que lo dispara, en este caso, "insert";

- base_object_type: a qué objeto está asociado, puede ser una tabla o una vista, en este caso, una tabla (table);

- table_name: nombre de la tabla al que está asociado (libros);

- y otras columnas que no analizaremos por el momento.

Ingresamos un registro en "libros":

insert into libros values(100,'Uno','Richard Bach',25);

Verificamos que el trigger se disparó consultando la tabla "control" para ver si tiene un nuevo registro:

select *from control;

Ingresamos dos registros más en "libros":

insert into libros values(150,'Matematica estas ahi','Paenza',12);


insert into libros values(185,'El aleph','Borges',42);

Verificamos que el trigger se disparó consultando la tabla "control" para ver si tiene dos nuevos registros:

select *from control;

106 - Disparador de insercion a nivel de fila (insert trigger for


each row)

Vimos la creación de un disparador para el evento de inserción a nivel de sentencia, es decir, se dispara una vez por
cada sentencia "insert" sobre la tabla asociada.

En caso que una sola sentencia "insert" ingrese varios registros en la tabla asociada, el trigger se disparará una sola
vez; si queremos que se active una vez por cada registro afectado, debemos indicarlo con "for each row".

La siguiente es la sintaxis para crear un trigger de inserción a nivel de fila, se dispare una vez por cada fila ingresada
sobre la tabla especificada:

create or replace trigger NOMBREDISPARADOR


MOMENTO insert
on NOMBRETABLA
for each row
begin
CUERPO DEL TRIGGER;
end NOMBREDISPARADOR;

Creamos un desencadenador que se dispara una vez por cada registro ingresado en la tabla "ofertas":

create or replace trigger tr_ingresar_ofertas


before insert
on ofertas
for each row
begin
insert into Control values(user,sysdate);
end tr_ingresar_ofertas;

Si al ejecutar un "insert" sobre "ofertas" empleamos la siguiente sentencia:

insert into ofertas select titulo,autor,precio from libros where precio<30;

y se ingresan 5 registros en "ofertas", en la tabla "control" se ingresarán 5 registros, uno por cada inserción en
"ofertas". Si el trigger hubiese sido definido a nivel de sentencia (statement), se agregaría una sola fila en "control".

Problema:

Una librería almacena los datos de sus libros en una tabla denominada "libros" y en una tabla "ofertas", algunos datos
de los libros cuyo precio no supera los $30. Además, controla las inserciones que los empleados realizan sobre
"ofertas", almacenando en la tabla "control" el nombre del usuario, la fecha y hora, cada vez que se ingresa un nuevo
registro en la tabla "ofertas".

Creamos un desencadenador que se dispara una vez por cada registro ingresado en la tabla "ofertas":

Eliminamos las tres tablas:

drop table libros;


drop table ofertas;
drop table control;

Creamos las tablas con las siguientes estructuras:

create table libros(


codigo number(6),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(6,2)
);

create table ofertas(


titulo varchar2(40),
autor varchar2(30),
precio number(6,2)
);

create table control(


usuario varchar2(30),
fecha date
);

Establecemos el formato de fecha para que muestre "DD/MM/YYYY HH24:MI":

alter session set NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI';

Creamos un disparador que se dispare una vez por cada registro ingresado en "ofertas"; el trigger debe ingresar en la
tabla "control", el nombre del usuario, la fecha y la hora en la cual se realizó un "insert" sobre "ofertas":

create or replace trigger tr_ingresar_ofertas


before insert
on ofertas
for each row
begin
insert into Control values(user,sysdate);
end tr_ingresar_ofertas;

Veamos qué nos informa el diccionario "user_triggers" respecto del trigger anteriormente creado:

select *from user_triggers where trigger_name ='TR_INGRESAR_OFERTAS';

Ingresamos algunos registros en "libros":

insert into libros values(100,'Uno','Richard Bach','Planeta',25);


insert into libros values(102,'Matematica estas ahi','Paenza','Nuevo siglo',12);
insert into libros values(105,'El aleph','Borges','Emece',32);
insert into libros values(120,'Aprenda PHP','Molina Mario','Nuevo siglo',55);

Ingresamos en "ofertas" los libros de "libros" cuyo precio no superen los $30, utilizando la siguiente sentencia:

insert into ofertas select titulo,autor,precio from libros where precio<30;

Verificamos que el trigger se disparó 2 veces, una por cada fila afectada en la sentencia "insert" anteriormente
ejecutada; consultamos la tabla "control":

select * from control;

Si el trigger hubiese sido creado a nivel de sentencia, no de fila, el "insert" anterior se hubiese activado una sola vez
aun cuando se ingresaron 2 registros.

107 - Disparador de borrado (nivel de sentencia y de fila)

Dijimos que un disparador está asociado a una tabla y a una operación específica (inserción, actualización o borrado).

A continuación veremos la creación de un disparador para el evento de borrado: "delete triger".

La siguiente es la sintaxis para crear un trigger de eliminación que se dispare cada vez que se ejecute una sentencia
"delete" sobre la tabla especificada, es decir, cada vez que se eliminen registros de la tabla:

create or replace trigger NOMBREDISPARADOR


MOMENTO delete
on NOMBRETABLA
NIVEL-- statement o for each row
begin
CUERPO DEL TRIGGER;
end NOMBREDISPARADOR;

Luego de la instrucción "create trigger" o "create or replace trigger" se coloca el nombre del disparador.

"MOMENTO" indica cuando se disparará el trigger en relación al evento, puede se BEFORE (antes) o AFTER (después).
Se especifica el evento que causa que el trigger se dispare, en este caso "delete", ya que el trigger se activará cada
vez que se ejecute la sentencia "delete" sobre la tabla especificada luego de "on".

En "NIVEL" se especifica si será un trigger a nivel de sentencia (se dispara una sola vez por cada sentencia "delete",
aunque la sentencia elimine varios registros) o a nivel de fila (se dispara tantas veces como filas se eliminan en la
sentencia "delete").

Finalmente se coloca el cuerpo del tigger dentro del bloque "begin.. end".
Creamos un desencadenador a nivel de fila que se dispara cada vez que se ejecuta un "delete" sobre la tabla "libros",
en el cuerpo del trigger se especifican las acciones, en este caso, por cada fila eliminada de la tabla "libros", se
ingresa un registro en "control" con el nombre del usuario que realizó la eliminación y la fecha:

create or replace trigger tr_borrar_libros


before delete
on libros
for each row
begin
insert into Control values(user,sysdate);
end tr_borrar_libros;

Problema:

Una librería almacena los datos de sus libros en una tabla denominada "libros" y controla las acciones que los
empleados realizan sobre dicha tabla almacenando en la tabla "control" el nombre del usuario y la fecha, cada vez
que se elimina un registro de la tabla "libros".

Eliminamos la tabla "libros" y la tabla "control":

drop table libros;


drop table control;

Creamos las tablas con las siguientes estructuras:

create table libros(


codigo number(6),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(6,2)
);

create table control(


usuario varchar2(30),
fecha date
);

Ingresamos algunos registros en "libros":

insert into libros values(97,'Uno','Richard Bach','Planeta',25);


insert into libros values(98,'El aleph','Borges','Emece',28);
insert into libros values(99,'Matematica estas ahi','Paenza','Nuevo siglo',12);
insert into libros values(100,'Aprenda PHP','Molina Mario','Nuevo siglo',55);
insert into libros values(101,'Alicia en el pais de las
maravillas','Carroll','Planeta',35);
insert into libros values(102,'El experto en laberintos','Gaskin','Planeta',20);

Establecemos el formato de fecha para que muestre "DD/MM/YYYY HH24:MI":

alter session set NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI';

Creamos un disparador a nivel de fila, que se dispare cada vez que se borre un registro de "libros"; el trigger debe
ingresar en la tabla "control", el nombre del usuario, la fecha y la hora en la cual se realizó un "delete" sobre "libros":

create or replace trigger tr_borrar_libros


before delete
on libros
for each row
begin
insert into control values(user,sysdate);
end tr_borrar_libros;

Veamos qué nos informa el diccionario "user_triggers" respecto del trigger anteriormente creado:

select *from user_triggers where trigger_name ='TR_BORRAR_LIBROS';


obtenemos la siguiente información:

- trigger_name: nombre del disparador;

- trigger_type: momento y nivel, en este caso es un desencadenador "before" y a nivel de fila (each row);

- triggering_event: evento que lo dispara, en este caso, "delete";

- base_object_type: a qué objeto está asociado, puede ser una tabla o una vista, en este caso, una tabla (table);

- table_name: nombre de la tabla al que está asociado (libros);

- y otras columnas que no analizaremos por el momento.

Eliminamos todos los libros cuyo código sea inferior a 100:

delete from libros where codigo<100;

Veamos si el trigger se disparó consultando la tabla "control":

select *from control;

Se eliminaron 3 registros, como el trigger fue definido a nivel de fila, se disparó 3 veces, una vez por cada registro
eliminado. Si el trigger hubiese sido definido a nivel de sentencia, se hubiese disparado una sola vez.

Reemplazamos el disparador creado anteriormente por otro con igual código pero a nivel de sentencia:

create or replace trigger tr_borrar_libros


before delete
on libros
begin
insert into control values(user,sysdate);
end tr_borrar_libros;

Veamos qué nos informa el diccionario "user_triggers" respecto del trigger anteriormente creado:

select *from user_triggers where trigger_name ='TR_BORRAR_LIBROS';

en este caso es un desencadenador a nivel de sentencia.

Eliminamos todos los libros cuya editorial sea "Planeta":

delete from libros where editorial='Planeta';

Se han eliminado 2 registros, pero el trigger se ha disparado una sola vez, consultamos la tabla "control":

select *from control;

Si el trigger hubiese sido definido a nivel de fila, se hubiese disparado dos veces.

108 - Disparador de actualizacion a nivel de sentencia (update


trigger)

Dijimos que un disparador está asociado a una tabla y a una operación específica (inserción, actualización o borrado).

A continuación veremos la creación de un disparador para el evento de actualización: "update triger".


La siguiente es la sintaxis para crear un trigger de actualización a nivel de sentencia, que se dispare cada vez que se
ejecute una sentencia "update" sobre la tabla especificada, es decir, cada vez que se intenten modificar datos en la
tabla:

create or replace trigger NOMBREDISPARADOR


MOMENTO update
on NOMBRETABLA
statement
begin
CUERPO DEL TRIGGER;
end NOMBREDISPARADOR;

Luego de la instrucción "create trigger" se coloca el nombre del disparador. Si agregamos "or replace" al momento de
crearlo y ya existe un trigger con el mismo nombre, tal disparador será borrado y vuelto a crear.

"MOMENTO" indica cuando se disparará el trigger en relación al evento, puede ser BEFORE (antes) o AFTER (después).
Se especifica el evento que causa que el trigger se dispare, en este caso "update", ya que el trigger se activará cada
vez que se ejecute la sentencia "update" sobre la tabla especificada luego de "on".

"statement" significa que es un trigger a nivel de sentencia, es decir, se dispara una sola vez por cada sentencia
"update", aunque la sentencia modifique varios registros; como en cualquier trigger, es el valor por defecto si no se
especifica.

Finalmente se coloca el cuerpo del tigger dentro del bloque "begin.. end".

Las siguientes sentencias disparan un trigger de inserción:

update TABLA set CAMPO=NUEVOVALOR;


update TABLA set CAMPO=NUEVOVALOR where CONDICION;

Ejemplo: Creamos un desencadenador que se dispara cada vez que se ejecuta un "update" sobre la tabla "libros":

create or replace trigger tr_actualizar_libros


before update
on libros
begin
insert into control values(user,sysdate);
end tr_actualizar_libros;

Al ocurrir el evento de actualización sobre "libros", se ingresa en la tabla "control" el nombre del usuario que alteró la
tabla "libros" (obtenida mediante la función "user") y la fecha en que lo hizo (mediante la función "sysdate").

Problema:

Una librería almacena los datos de sus libros en una tabla denominada "libros" y controla las acciones que los
empleados realizan sobre dicha tabla almacenando en la tabla "control" el nombre del usuario y la fecha, cada vez
que se modifica un registro en la tabla "libros".

Eliminamos la tabla "libros" y la tabla "control":

drop table libros;


drop table control;

Creamos la tabla con la siguiente estructura:

create table libros(


codigo number(6),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(6,2)
);
Creamos la tabla "control":

create table control(


usuario varchar2(30),
fecha date
);

Ingresamos algunos registros en "libros":

insert into libros values(100,'Uno','Richard Bach','Planeta',25);


insert into libros values(103,'El aleph','Borges','Emece',28);
insert into libros values(105,'Matematica estas ahi','Paenza','Nuevo siglo',12);
insert into libros values(120,'Aprenda PHP','Molina Mario','Nuevo siglo',55);
insert into libros values(145,'Alicia en el pais de las
maravillas','Carroll','Planeta',35);

Establecemos el formato de fecha para que muestre "DD/MM/YYYY HH24:MI":

alter session set NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI';

Creamos un disparador a nivel de sentencia, que se dispare cada vez que se actualice un registro en "libros"; el
trigger debe ingresar en la tabla "control", el nombre del usuario, la fecha y la hora en la cual se realizó un "update"
sobre "libros":

create or replace trigger tr_actualizar_libros


before update
on libros
begin
insert into control values(user,sysdate);
end tr_actualizar_libros;

Veamos qué nos informa el diccionario "user_triggers" respecto del trigger anteriormente creado:

select *from user_triggers where trigger_name ='TR_ACTUALIZAR_LIBROS';

obtenemos la siguiente información:

- trigger_name: nombre del disparador;

- trigger_type: momento y nivel, en este caso es un desencadenador "before" y a nivel de sentencia (statement);

- triggering_event: evento que lo dispara, en este caso, "update";

- base_object_type: a qué objeto está asociado, puede ser una tabla o una vista, en este caso, una tabla (table);

- table_name: nombre de la tabla al que está asociado (libros);

- y otras columnas que no analizaremos por el momento.

Actualizamos un registro en "libros":

update libros set codigo=99 where codigo=100;

Veamos si el trigger se disparó consultando la tabla "control":

select *from control;

Actualizamos varios registros de "libros":

update libros set precio=precio+precio*0.1 where editorial='Nuevo siglo';

Veamos si el trigger se disparó consultando la tabla "control":


select *from control;

Note que se modificaron 2 registros de "libros", pero como la modificación se realizó con una sola sentencia "update"
y el trigger es a nivel de sentencia, se agregó solamente una fila a la tabla "control"; si el trigger hubiese sido creado
a nivel de fila, la sentencia anterior, hubiese disparado el trigger 2 veces y habría ingresado en "control" 2 filas.

109 - Disparador de actualización a nivel de fila (update trigger)

Vimos la creación de un disparador para el evento de actualización a nivel de sentencia, es decir, se dispara una vez
por cada sentencia "update" sobre la tabla asociada.

En caso que una sola sentencia "update" modifique varios registros en la tabla asociada, el trigger se disparará una
sola vez; si queremos que se active una vez por cada registro afectado, debemos indicarlo con "for each row".

La siguiente es la sintaxis para crear un trigger de actualización a nivel de fila, se dispare una vez por cada fila
modificada sobre la tabla especificada:

create or replace trigger NOMBREDISPARADOR


MOMENTO update
on NOMBRETABLA
for each row
begin
CUERPO DEL TRIGGER;
end NOMBREDISPARADOR;

Creamos un desencadenador a nivel de fila, que se dispara una vez por cada fila afectada por un "update" sobre la
tabla "libros". Se ingresa en la tabla "control" el nombre del usuario que altera la tabla "libros" (obtenida mediante la
función "user") y la fecha en que lo hizo (mediante la función "sysdate"):

create or replace trigger tr_actualizar_libros


before update
on libros
for each row
begin
insert into control values(user,sysdate);
end tr_actualizar_libros;

Si al ejecutar un "update" sobre "libros" se actualizan 5 registros, en la tabla "control" se ingresarán 5 registros, uno
por cada modificación. Si el trigger hubiese sido definido a nivel de sentencia (statement), se agregaría una sola fila
en "control".

Problema:

Una librería almacena los datos de sus libros en una tabla denominada "libros" y controla las acciones que los
empleados realizan sobre dicha tabla almacenando en la tabla "control" el nombre del usuario y la fecha, cada vez
que se modifica un registro en la tabla "libros".

Eliminamos las tablas:

drop table control;


drop table libros;

Creamos las tablas con las siguientes estructuras:

create table libros(


codigo number(6),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(6,2)
);

create table control(


usuario varchar2(30),
fecha date
);

Ingresamos algunos registros en "libros":

insert into libros values(100,'Uno','Richard Bach','Planeta',25);


insert into libros values(103,'El aleph','Borges','Emece',28);
insert into libros values(105,'Matematica estas ahi','Paenza','Nuevo siglo',12);
insert into libros values(120,'Aprenda PHP','Molina Mario','Nuevo siglo',55);
insert into libros values(145,'Alicia en el pais de las
maravillas','Carroll','Planeta',35);

Establecemos el formato de fecha para que muestre "DD/MM/YYYY HH24:MI":

alter session set NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI';

Creamos un disparador que se dispare cada vez que se actualice un registro en "libros"; el trigger debe ingresar en la
tabla "control", el nombre del usuario, la fecha y la hora en la cual se realizó un "update" sobre "libros":

create or replace trigger tr_actualizar_libros


before update
on libros
for each row
begin
insert into control values(user,sysdate);
end tr_actualizar_libros;

Actualizamos varios registros de "libros". Aumentamos en un 10% el precio de todos los libros de editorial "Nuevo
siglo':

update libros set precio=precio+precio*0.1 where editorial='Nuevo siglo';

Veamos cuántas veces se disparó el trigger consultando la tabla "control":

select *from control;

El trigger se disparó 2 veces, una vez por cada registro modificado en "libros". Si el trigger hubiese sido creado a nivel
de sentencia, el "update" anterior hubiese disparado el trigger 1 sola vez aún cuando se modifican 2 filas.

Veamos qué nos informa el diccionario "user_triggers" respecto del trigger anteriormente creado:

select *from user_triggers where trigger_name ='TR_ACTUALIZAR_LIBROS';

obtenemos el nombre del disparador, el momento y nivel (before each row), evento que lo dispara (update), a qué
objeto está asociado (table), nombre de la tabla al que está asociado (libros) y otras columnas que no analizaremos
por el momento.

110 - Disparador de actualización - lista de campos (update


trigger)

El trigger de actualización (a nivel de sentencia o de fila) permite incluir una lista de campos. Si se incluye el nombre
de un campo (o varios) luego de "update", el trigger se disparará únicamente cuando alguno de esos campos
(incluidos en la lista) es actualizado. Si se omite la lista de campos, el trigger se dispara cuando cualquier campo de
la tabla asociada es modificado, es decir, por defecto toma todos los campos de la tabla.
La lista de campos solamente puede especificarse en disparadores de actualización, nunca en disparadores de
inserción o borrado.

Sintaxis general:

create or replace trigger NOMBREDISPARADOR


MOMENTO update of CAMPOS
on TABLA
NIVEL--statement o for each row
begin
CUERPODEL DISPARADOR;
end NOMBREDISPARADOR;

"CAMPOS" son los campos de la tabla asociada que activarán el trigger si son modificados. Pueden incluirse más de
uno, en tal caso, se separan con comas.

Creamos un desencadenador a nivel de fila que se dispara cada vez que se actualiza el campo "precio" de la tabla
"libros":

create or replace trigger tr_actualizar_precio_libros


before update of precio
on libros
for each row
begin
insert into control values(user,sysdate);
end tr_actualizar_precio_libros;

Si realizamos un "update" sobre el campo "precio" de "libros", el trigger se dispara. Pero si realizamos un "update"
sobre cualquier otro campo, el trigger no se dispara, ya que está definido solamente para el campo "precio".

Problema:

Una librería almacena los datos de sus libros en una tabla denominada "libros" y controla las acciones que los
empleados realizan sobre dicha tabla almacenando en la tabla "control" el nombre del usuario y la fecha, cada vez
que se modifica el "precio" de un libro.

Eliminamos las tablas:

drop table control;


drop table libros;

Creamos las tablas con las siguientes estructuras:

create table libros(


codigo number(6),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(6,2)
);

create table control(


usuario varchar2(30),
fecha date
);

Ingresamos algunos registros en "libros":

insert into libros values(100,'Uno','Richard Bach','Planeta',25);


insert into libros values(103,'El aleph','Borges','Emece',28);
insert into libros values(105,'Matematica estas ahi','Paenza','Nuevo siglo',12);
insert into libros values(120,'Aprenda PHP','Molina Mario','Nuevo siglo',55);
insert into libros values(145,'Alicia en el pais de las
maravillas','Carroll','Planeta',35);
Establecemos el formato de fecha para que muestre "DD/MM/YYYY HH24:MI":

alter session set NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI';

Creamos un desencadenador a nivel de fila que se dispare cada vez que se actualiza el campo "precio"; el trigger
debe ingresar en la tabla "control", el nombre del usuario, la fecha y la hora en la cual se realizó un "update" sobre
"precio" de "libros":

create or replace trigger tr_actualizar_precio_libros


before update of precio
on libros
for each row
begin
insert into control values(user,sysdate);
end tr_actualizar_precio_libros;

Veamos qué nos informa el diccionario "user_triggers" respecto del trigger anteriormente creado:

select *from user_triggers where trigger_name ='TR_ACTUALIZAR_PRECIO_LIBROS';

Aumentamos en un 10% el precio de todos los libros de editorial "Nuevo siglo':

update libros set precio=precio+precio*0.1 where editorial='Nuevo siglo';

Veamos cuántas veces se disparó el trigger consultando la tabla "control":

select * from control;

El trigger se disparó 2 veces, una vez por cada registro modificado en "libros". Si el trigger hubiese sido creado a nivel
de sentencia, el "update" anterior hubiese disparado el trigger 1 sola vez aún cuando se modifican 2 filas.

Modificamos otro campo, diferente de "precio":

update libros set autor='Lewis Carroll' where autor='Carroll';

Veamos si el trigger se disparó consultando la tabla "control":

select * from control;

El trigger no se disparó (no hay nuevas filas en "control"), pues está definido solamente sobre el campo "precio".

111 - Disparador de múltiples eventos

Hasta el momento hemos aprendido a crear un disparador (trigger) asociado a una única operación (inserción,
actualización o borrado).

Un trigger puede definirse sobre más de un evento; en tal caso se separan con "or".

Sintaxis para crear un disparador para múltiples eventos:

create or replace trigger NOMBREDISPARADOR


MOMENTO-- before, after o instead of
of CAMPO--si alguno de los eventos es update
EVENTOS-- insert, update y/o delete
on NOMBRETABLA
NIVEL--puede ser a nivel de sentencia (statement) o de fila (for each row)
begin
CUERPO DEL DISPARADOR--sentencias
end NOMBREDISPARADOR;
Si el trigger se define para más de un evento desencadenante, en el cuerpo del mismo se puede emplear un
condicional para controlar cuál operación disparó el trigger. Esto permite ejecutar bloques de código según la clase
de acción que disparó el desencadenador.

Para identificar el tipo de operación que disparó el trigger empleamos "inserting", "updating" y "deleting".

Veamos un ejemplo. El siguiente trigger está definido a nivel de sentencia, para los eventos "insert", "update" y
"delete"; cuando se modifican los datos de "libros", se almacena en la tabla "control" el nombre del usuario, la fecha y
el tipo de modificación que alteró la tabla:

- si se realizó una inserción, se almacena "inserción";

- si se realizó una actualización (update), se almacena "actualización" y

- si se realizó una eliminación (delete) se almacena "borrado".

create or replace trigger tr_cambios_libros


before insert or update or delete
on libros
for each row
begin
if inserting then
insert into control values (user, sysdate,'inserción');
end if;
if updating then
insert into control values (user, sysdate,'actualización');
end if;
if deleting then
insert into control values (user, sysdate,'borrado');
end if;
end tr_cambios_libros;

Si ejecutamos un "insert" sobre "libros", el disparador se activa entrando por el primer "if"; si ejecutamos un "update"
el trigger se dispara entrando por el segundo "if"; si ejecutamos un "delete" el desencadenador se dispara entrando
por el tercer "if".

Las siguientes sentencias disparan el trigger creado anteriormente:

insert into TABLA values ...;


insert into TABLA select ... from...;
update TABLA set...;
delete from TABLA...;

Problema:

Una librería almacena los datos de sus libros en una tabla denominada "libros" y controla las acciones que los
empleados realizan sobre dicha tabla almacenando en la tabla "control" el nombre del usuario, la fecha, y el tipo de
modificación que se realizó sobre la tabla "libros".

Eliminamos la tabla "libros" y la tabla "control":

drop table libros;


drop table control;

Creamos la tabla con la siguiente estructura:

create table libros(


codigo number(6),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(6,2)
);

Creamos la tabla "control":


create table control(
usuario varchar2(30),
fecha date,
operacion varchar2(20)
);

Ingresamos algunos registros en "libros":

insert into libros values(100,'Uno','Richard Bach','Planeta',25);


insert into libros values(103,'El aleph','Borges','Emece',28);
insert into libros values(105,'Matematica estas ahi','Paenza','Nuevo siglo',12);
insert into libros values(120,'Aprenda PHP','Molina Mario','Nuevo siglo',55);
insert into libros values(145,'Alicia en el pais de las
maravillas','Carroll','Planeta',35);

Establecemos el formato de fecha para que muestre "DD/MM/YYYY HH24:MI":

alter session set NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI';

Creamos un disparador a nivel de sentencia, que se dispare cada vez que se ingrese, actualice o elimine un registro
de la tabla "libros". El trigger ingresa en la tabla "control", el nombre del usuario, la fecha y la hora en la cual se
realizó la modificación y el tipo de operación que se realizó:

- si se realizó una inserción (insert), se almacena "inserción";

- si se realizó una actualización (update), se almacena "actualización" y

- si se realizó una eliminación (delete) se almacena "borrado".

create or replace trigger tr_cambios_libros


before insert or update or delete
on libros
for each row
begin
if inserting then
insert into control values (user, sysdate,'inserción');
end if;
if updating then
insert into control values (user, sysdate,'actualización');
end if;
if deleting then
insert into control values (user, sysdate,'borrado');
end if;
end tr_cambios_libros;

Veamos qué nos informa el diccionario "user_triggers" respecto del trigger anteriormente creado:

select *from user_triggers where trigger_name ='TR_CAMBIOS_LIBROS';

obtenemos la siguiente información:

- trigger_name: nombre del disparador;

- trigger_type: momento y nivel, en este caso es un desencadenador "before" y a nivel de fila (each row);

- triggering_event: evento que lo dispara, en este caso, "insert or update or delete";

- base_object_type: a qué objeto está asociado, puede ser una tabla o una vista, en este caso, una tabla (table);

- table_name: nombre de la tabla al que está asociado (libros);

Ingresamos un registro en "libros":

insert into libros values(150,'El experto en laberintos','Gaskin','Planeta',23);


Veamos si el trigger se disparó consultando la tabla "control":

select *from control;

Vemos que se ingresó un registro que muestra que el usuario "system", el día y hora actual realizó una inserción sobre
"libros".

Actualizamos algunos registros de "libros":

update libros set precio=precio+precio*0.1 where editorial='Planeta';

Veamos cuántas veces el trigger se disparó consultando la tabla "control":

select *from control;

Vemos que se ingresaron 3 nuevos registros que muestran que el usuario "system", el día y hora actual actualizó tres
registros de "libros". Si el trigger se hubiese definido a nivel de sentencia, el "update" anterior se hubiese disparado
una sola vez.

Eliminamos un registro de "libros":

delete from libros where codigo=145;

Veamos si el trigger se disparó consultando la tabla "control":

select *from control;

Vemos que se eliminó 1 registro.

112 - Disparador (old y new)

Cuando trabajamos con trigger a nivel de fila, Oracle provee de dos tablas temporales a las cuales se puede acceder,
que contienen los antiguos y nuevos valores de los campos del registro afectado por la sentencia que disparó el
trigger. El nuevo valor es ":new" y el viejo valor es ":old". Para referirnos a ellos debemos especificar su campo
separado por un punto ":new.CAMPO" y ":old.CAMPO".

El acceso a estos campos depende del evento del disparador.

En un trigger disparado por un "insert", se puede acceder al campo ":new" unicamente, el campo ":old" contiene
"null".

En una inserción se puede emplear ":new" para escribir nuevos valores en las columnas de la tabla.

En un trigger que se dispara con "update", se puede acceder a ambos campos. En una actualizacion, se pueden
comparar los valores de ":new" y ":old".

En un trigger de borrado, unicamente se puede acceder al campo "old", ya que el campo ":new" no existe luego que el
registro es eliminado, el campo ":new" contiene "null" y no puede ser modificado.

Los valores de "old" y "new" están disponibles en triggers after y before.

El valor de ":new" puede modificarse en un trigger before, es decir, se puede acceder a los nuevos valores antes que
se ingresen en la tabla y cambiar los valores asignando a ":new.CAMPO" otro valor.

El valor de ":new" NO puede modificarse en un trigger after, esto es porque el trigger se activa luego que los valores
de "new" se almacenaron en la tabla.

El campo ":old" nunca se modifica, sólo puede leerse.


Pueden usarse en una clásula "when" (que veremos posteriormente).

En el cuerpo el trigger, los campos "old" y "new" deben estar precedidos por ":" (dos puntos), pero si está en "when"
no.

Veamos un ejemplo.

Creamos un trigger a nivel de fila que se dispara "antes" que se ejecute un "update" sobre el campo "precio" de la
tabla "libros". En el cuerpo del disparador se debe ingresar en la tabla "control", el nombre del usuario que realizó la
actualización, la fecha, el código del libro que ha sido modificado, el precio anterior y el nuevo:

create or replace trigger tr_actualizar_precio_libros


before update of precio
on libros
for each row
begin
insert into control values(user,sysdate,:new.codigo,:old.precio,:new.precio);
end tr_actualizar_precio_libros;

Cuando el trigger se dispare, antes de ingresar los valores a la tabla, almacenará en "control", además del nombre del
usuario y la fecha, el precio anterior del libro y el nuevo valor.

El siguiente trigger debe controlar el precio que se está actualizando, si supera los 50 pesos, se debe redondear tal
valor a entero, hacia abajo (empleando "floor"), es decir, se modifica el valor ingresado accediendo a ":new.precio"
asignándole otro valor:

create or replace trigger tr_actualizar_precio_libros


before update of precio
on libros
for each row
begin
if (:new.precio>50) then
:new.precio:=floor(:new.precio);
end if;
insert into control values(user,sysdate,:new.codigo,:old.precio,:new.precio);
end tr_actualizar_precio_libros;

Si al actualizar el precio de un libro colocamos un valor superior a 50, con decimales, tal valor se redondea al entero
más cercano hacia abajo. Por ejemplo, si el nuevo valor es "54.99", se almacenará "54".

Podemos crear un disparador para múltiples eventos, que se dispare al ejecutar "insert", "update" y "delete" sobre
"libros". En el cuerpo del trigger se realiza la siguiente acción: se almacena el nombre del usuario, la fecha y los
antiguos y viejos valores del campo "precio":

create or replace trigger tr_libros


before insert or update or delete
on libros
for each row
begin
insert into control values(user,sysdate,:old.codigo,:old.precio,:new.precio);
end tr_libros;

Si ingresamos un registro, el campo ":old.codigo" y el campo ":old.precio" contendrán "null". Si realizamos una
actualización del campo de un campo que no sea "precio", los campos ":old.precio" y ":new.precio" guardarán el
mismo valor.

Si eliminamos un registro, el campo ":new.precio" contendrá "null".

Entonces, se puede acceder a los valores de ":new" y ":old" en disparadores a nivel de fila (no en disparadores a nivel
de sentencia). Están disponibles en triggers after y before. Los valores de ":old" solamente pueden leerse, nunca
modificarse. Los valores de ":new" pueden modificarse únicamente en triggers before (nunca en triggers after).

Problema:
Una librería almacena los datos de sus libros en una tabla denominada "libros" y controla las actualizaciones del
precio de los libros almacenando en la tabla "control" el nombre del usuario, la fecha, el precio anterior y el nuevo.

Eliminamos las tablas:

drop table control;


drop table libros;

Creamos las tablas con las siguientes estructuras:

create table libros(


codigo number(6),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(6,2)
);

create table control(


usuario varchar2(30),
fecha date,
codigo number(6),
precioanterior number(6,2),
precionuevo number(6,2)
);

Ingresamos algunos registros en "libros":

insert into libros values(100,'Uno','Richard Bach','Planeta',25);


insert into libros values(103,'El aleph','Borges','Emece',28);
insert into libros values(105,'Matematica estas ahi','Paenza','Nuevo siglo',12);
insert into libros values(120,'Aprenda PHP','Molina Mario','Nuevo siglo',55);
insert into libros values(145,'Alicia en el pais de las
maravillas','Carroll','Planeta',35);

Creamos un trigger a nivel de fila que se dispara "antes" que se ejecute un "update" sobre el campo "precio" de la
tabla "libros". En el cuerpo del disparador se debe ingresar en la tabla "control", el nombre del usuario que realizó la
actualización, la fecha, el código del libro que ha sido modificado, el precio anterior y el nuevo:

create or replace trigger tr_actualizar_precio_libros


before update of precio
on libros
for each row
begin
insert into control values(user,sysdate,:new.codigo,:old.precio,:new.precio);
end tr_actualizar_precio_libros;

Cuando el trigger se dispare, antes de ingresar los valores a la tabla, almacenará en "control", además del nombre del
usuario y la fecha, el precio anterior del libro y el nuevo valor.

Actualizamos el precio del libro con código 100:

update libros set precio=30 where codigo=100;

Veamos lo que se almacenó en "control" al dispararse el trigger:

select *from control;

Los campos "precioanterior" y "precionuevo" de la tabla "control" almacenaron los valores de ":old.precio" y
":new.precio" respectivamente.

Actualizamos varios registros:

update libros set precio=precio+precio*0.1 where editorial='Planeta';

Veamos lo que se almacenó en "control" al dispararse el trigger:


select *from control;

Los campos "precioanterior" y "precionuevo" de la tabla "control" almacenaron los valores de ":old.precio" y
":new.precio" respectivamente de cada registro afectado por la actualización.

Modificamos la editorial de un libro:

update libros set editorial='Sudamericana' where editorial='Planeta';

El trigger no se disparó, pues fue definido para actualizaciones del campo "precio" unicamente.

Verifiquémoslo:

select *from control;

Vamos a reemplazar el trigger anteriormente creado. Ahora el disparador "tr_actualizar_precio_libros" debe controlar
el precio que se está actualizando, si supera los 50 pesos, se debe redondear tal valor a entero hacia abajo
(empleando "floor"), es decir, se modifica el valor ingresado accediendo a ":new.precio" asignándole otro valor:

create or replace trigger tr_actualizar_precio_libros


before update of precio
on libros
for each row
begin
if (:new.precio>50) then
:new.precio:=floor(:new.precio);
end if;
insert into control values(user,sysdate,:new.codigo,:old.precio,:new.precio);
end tr_actualizar_precio_libros;

Vaciamos la tabla "control":

truncate table control;

Actualizamos el precio del libro con código 100:

update libros set precio=54.99 where codigo=100;

Veamos cómo se actualizó tal libro en "libros":

select *from libros where codigo=100;

El nuevo precio actualizado se redondeó a 54.

Veamos lo que se almacenó en "control" al dispararse el trigger:

select *from control;

Los campos "precioanterior" y "precionuevo" de la tabla "control" almacenaron los valores de ":old.precio" y
":new.precio" respectivamente.

Truncamos la tabla "control" nuevamente:

truncate table control;

Creamos un disparador para múltiples eventos, que se dispare al ejecutar "insert", "update" y "delete" sobre "libros".
En el cuerpo del trigger se realiza la siguiente acción: se almacena el nombre del usuario, la fecha y los antiguos y
viejos valores de "precio":

create or replace trigger tr_libros


before insert or update or delete
on libros
for each row
begin
insert into control values(user,sysdate,:old.codigo,:old.precio,:new.precio);
end tr_libros;

Ingresamos un registro:

insert into libros values (150,'El gato con botas','Anonimo','Emece',21);

Veamos lo que se almacenó en "control":

select *from control;

Resultado:

USUARIO FECHA CODIGO PRECIOANTERIOR


PRECIONUEVO
-----------------------------------------------------------------------------------
SYSTEM 20/03/08 21

La sentencia disparadora fue una inserción, por lo tanto, los campos ":old.codigo" y ":old.precio" contienen "null", así
que en "codigo" y en "precioanterior" se almacena "null"; el único campo con valor diferente de "null" es "precionuevo"
correspondiente al valor de ":new.precio".

Actualizamos el campo "precio" de un libro:

update libros set precio=12 where codigo=103;

Veamos lo que se almacenó en "control":

select *from control;

Resultado:

USUARIO FECHA CODIGO PRECIOANTERIOR


PRECIONUEVO
-----------------------------------------------------------------------------------
SYSTEM 20/03/08 103 28 12

Analicemos: actualizamos el precio, por lo tanto, ninguno de los campos consultados contiene "null".

Actualizamos un campo diferente de "precio" de un libro:

update libros set autor='J.L.Borges' where autor='Borges';

Veamos lo que se almacenó en "control":

select *from control;

Resultado:

USUARIO FECHA CODIGO PRECIOANTERIOR


PRECIONUEVO
-----------------------------------------------------------------------------------
SYSTEM 20/03/08 103 12 12

Actualizamos el autor, por lo tanto, los campos ":old.precio" y ":new.precio" son iguales.

Eliminamos un registro de "libros":

delete from libros where codigo=100;

Veamos lo que se almacenó en "control":

select *from control;


Resultado:

USUARIO FECHA CODIGO PRECIOANTERIOR


PRECIONUEVO
-----------------------------------------------------------------------------------
SYSTEM 20/03/08 100 54

Analicemos: la sentencia que disparó el trigger fue un "delete", por lo tanto, el campo ":new.precio" contiene "null".

Veamos qué informa el diccionario "user_triggers" respecto del trigger anteriormente creado:

select *from user_triggers where trigger_name ='TR_LIBROS';

113 - Disparador condiciones (when)

En los triggers a nivel de fila, se puede incluir una restricción adicional, agregando la clausula "when" con una
condición que se evalúa para cada fila que afecte el disparador; si resulta cierta, se ejecutan las sentencias del
trigger para ese registro; si resulta falsa, el trigger no se dispara para ese registro.

Limitaciones de "when":

- no puede contener subconsultas, funciones agregadas ni funciones definidas por el usuario;

- sólo se puede hacer referencia a los parámetros del evento;

- no se puede especificar en los trigers "instead of" ni en trigger a nivel de sentencia.

Creamos el siguiente disparador:

create or replace trigger tr_precio_libros


before insert or update of precio
on libros
for each row when(new.precio>50)
begin
:new.precio := round(:new.precio);
end tr_precio_libros;

El disparador anterior se dispara ANTES (before) que se ejecute un "insert" sobre "libros" o un "update" sobre "precio"
de "libros". Se ejecuta una vez por cada fila afectada (for each row) y solamente si cumple con la condición del
"when", es decir, si el nuevo precio que se ingresa o modifica es superior a 50. Si el precio es menor o igual a 50, el
trigger no se dispara. Si el precio es mayor a 50, se modifica el valor ingresado redondeándolo a entero.

Note que cuando hacemos referencia a "new" (igualmente con "old") en la condición "when", no se colocan los dos
puntos precediéndolo; pero en el cuerpo del trigger si.

Si ingresamos un registro con el valor 30.80 para "precio", el trigger no se dispara.

Si ingresamos un registro con el valor "55.6" para "precio", el trigger se dispara modificando tal valor a "56".

Si actualizamos el precio de un libro a "40.30", el trigger no se activa.

Si actualizamos el precio de un libro a "50.30", el trigger se activa y modifica el valor a "50".

Si actualizamos el precio de 2 registros a valores que superen los "50", el trigger se activa 2 veces redondeando los
valores a entero.

Si actualizamos en una sola sentencia el precio de 2 registros y solamente uno de ellos supera los "50", el trigger se
activa 1 sola vez.

El trigger anterior podría haberse creado de la siguiente manera:


create or replace trigger tr_precio_libros
before insert or update of precio
on libros
for each row
begin
if :new.precio>50 then
:new.precio := round(:new.precio);
end if;
end tr_precio_libros;

En este caso, la condición se chequea en un "if" dentro del cuerpo del trigger. La diferencia con el primer trigger que
contiene "when" es que la condición establecida en el "when" se testea antes que el trigger se dispare y si resulta
verdadera, se dispara el trigger, sino no. En cambio, si la condición está dentro del cuerpo del disparador, el trigger
se dispara y luego se controla el precio, si cumple la condición, se modifica el precio, sino no.

Por ejemplo, la siguiente sentencia:

update libros set precio=40 where...;

no dispara el primer trigger, ya que no cumple con la condición del "when"; pero si dispara el segundo trigger, que no
realiza ninguna acción ya que al evaluarse la condición del "if", resulta falsa.

Problema:

Una librería almacena los datos de sus libros en una tabla denominada "libros".

Eliminamos la tabla:

drop table libros;

Creamos la tabla con la siguiente estructura:

create table libros(


codigo number(6),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(6,2)
);

Ingresamos algunos registros en "libros":

insert into libros values(100,'Uno','Richard Bach','Planeta',25);


insert into libros values(103,'El aleph','Borges','Planeta',40);
insert into libros values(105,'Matematica estas ahi','Paenza','Nuevo siglo',12);
insert into libros values(120,'Aprenda PHP','Molina Mario','Nuevo siglo',55);

Creamos un trigger a nivel de fila que se dispara "antes" que se ejecute un "insert" o un "update" sobre el campo
"precio" de la tabla "libros". Se activa solamente si el nuevo precio que se ingresa o se modifica es superior a 50, en
caso de serlo, se modifica el valor ingresado redondeándolo a entero:

create or replace trigger tr_precio_libros


before insert or update of precio
on libros
for each row when(new.precio>50)
begin
:new.precio := round(:new.precio);
end tr_precio_libros;

Ingresamos un registro con el valor 30.80 para "precio":

insert into libros values(250,'El experto en laberintos','Gaskin','Emece',30.80);

El trigger no se dispara.
Veamos si el precio ingresado se redondeó:

select *from libros where titulo like '%experto%';

El precio no se redondeó porque no es superior a 50, el trigger no se disparó.

Ingresamos un registro con el valor "55.6" para "precio":

insert into libros values(300,'Alicia en el pais de las


maravillas','Carroll','Emece',55.6);

Consultamos "libros":

select *from libros where titulo like '%maravillas%';

El trigger se disparó y se redondeó el nuevo precio a 56.

Actualizamos el precio de un libro a "40.30":

update libros set precio=40.30 where codigo=105;

Consultamos "libros":

select *from libros where codigo =105;

Se almacenó el valor tal como se solicitó, el trigger no se disparó ya que ":new.precio" no cumplió con la condición
del "when".

Actualizamos el precio de un libro a "50.30":

update libros set precio=50.30 where codigo=105;

Consultamos la tabla:

select *from libros where codigo=105;

El trigger se activa porque ":new.precio" cumple con la condición "when" y modifica el valor a "50".

Actualizamos el precio de 2 registros a "50.30":

update libros set precio=50.30 where editorial='Nuevo siglo';

Consultamos:

select *from libros where editorial='Nuevo siglo';

El trigger se activa 2 veces redondeado el valor a 50.

Ejecutamos el siguiente "update":

update libros set precio=precio+15.8 where editorial='Planeta';

Consultamos:

select *from libros where editorial='Planeta';

De los dos libros de editorial "Planeta" solamente uno supera el valor 50, por lo tanto, el trigger se dispara una sola
vez.

Activamos el paquete "dbms_output":


set serveroutput on;
execute dbms_output.enable(20000);

Reemplazamos el trigger anterior por uno sin condición "when". La condición se controla en un "if" en el interior del
trigger. En este caso, el trigger se dispara SIEMPRE que se actualice un precio en "libros", dentro del trigger se
controla el precio, si cumple la condición, se modifica, sino no:

create or replace trigger tr_precio_libros


before insert or update of precio
on libros
for each row
begin
dbms_output.put_line('Trigger disparado');
if :new.precio>50 then
dbms_output.put_line('Precio redondeado');
:new.precio:= round(:new.precio);
end if;
end tr_precio_libros;

Note que agregamos una salida de texto para controlar si el trigger se ha disparado y otra, para controlar si entra por
la condición "if".

Ingresamos un registro con un valor inferior a 50 para "precio":

insert into libros values(350,'Ilusiones','Bach','Planeta',20.35);

El trigger se dispara (aparece el primer mensaje), pero no realiza ninguna acción ya que al evaluarse la condición del
"if", resulta falsa.

Ingresamos un registro con un valor superior a 50 para "precio":

insert into libros values(380,'El anillo del hechicero','Gaskin','Planeta',60.35);

El trigger se dispara (aparece el primer mensaje) y al evaluarse como cierta la condición, realiza la acción (aparece
el segundo mensaje).

Consultamos el diccionario para ver qué nos informa sobre el disparador recientemente creado:

select *from user_triggers where trigger_name ='TR_LIBROS';

114 - Disparador de actualizacion - campos (updating)

En un trigger de actualización a nivel de fila, se puede especificar el nombre de un campo en la condición "updating"
para determinar cuál campo ha sido actualizado.

Sintaxis básica:

create or replace trigger NOMBRETRIGGER


MOMENTO update...
begin
if updating ('CAMPO') then
...
end if;
end NOMBREDISPARADOR;

El siguiente trigger se dispara cuando se actualiza la tabla "libros". Dentro del cuerpo del trigger se averigua el campo
que ha sido modificado; en caso de modificarse el "precio", se ingresa en la tabla "controlPrecios" la fecha, el código
del libro y el antiguo y nuevo precio; en caso de actualizarse cualquier otro campo, se almacena en la tabla "control"
el nombre del usuario que realizó la modificación, la fecha y el código del libro modificado.

create or replace trigger tr_actualizar_libros


before update
on libros
for each row
begin
if updating ('precio') then
insert into controlprecios values(sysdate,:old.codigo,:old.precio,:new.precio);
else
insert into control values(user,sysdate,:old.codigo);
end if;
end tr_actualizar_libros;

Problema:

Una librería almacena los datos de sus libros en una tabla denominada "libros" y controla las actualizaciones de los
precios de los libros almacenando en la tabla "controlprecios", la fecha, el código del libro, el antiguo y nuevo precio.
También controla cualquier otra actualización almacenando en "control" el nombre del usuario, la fecha y el código
del libro.

Elimine las tablas:

drop table controlprecios;


drop table libros;
drop table control;

Cree las tablas con las siguientes estructuras:

create table libros(


codigo number(6),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(6,2),
stock number(4)
);

create table control(


usuario varchar2(30),
fecha date,
codigo number(6)
);

create table controlprecios(


fecha date,
codigo number(6),
precioanterior number(6,2),
precionuevo number(6,2)
);

Ingrese algunos registros en "libros":

insert into libros values(100,'Uno','Richard Bach','Planeta',25,100);


insert into libros values(103,'El aleph','Borges','Emece',28,0);
insert into libros values(105,'Matematica estas ahi','Paenza','Nuevo siglo',12,50);
insert into libros values(120,'Aprenda PHP','Molina Mario','Nuevo siglo',55,200);
insert into libros values(145,'Alicia en el pais de las
maravillas','Carroll','Planeta',35,10);

Cree un trigger a nivel de fila que se dispare "antes" que se ejecute un "update" sobre la tabla "libros".

En el cuerpo del trigger se debe averiguar el campo que ha sido modificado; en caso de modificarse el "precio", se
ingresa en la tabla "controlPrecios" la fecha, el código del libro y el antiguo y nuevo precio; en caso de actualizarse
cualquier otro campo, se almacena en la tabla "control" el nombre del usuario que realizó la modificación, la fecha y
el código del libro modificado.

create or replace trigger tr_actualizar_libros


before update
on libros
for each row
begin
if updating ('precio') then
insert into controlprecios values(sysdate,:old.codigo,:old.precio,:new.precio);
else
insert into control values(user,sysdate,:old.codigo);
end if;
end tr_actualizar_libros;

Actualice el precio de un libro:

update libros set precio=35 where codigo=100;

Verifique que el trigger se ha disparado consultando la tabla "controlprecios":

select *from controlprecios;

Se ha insertado una fila.

Verifique que la tabla "control" no tiene registros.

select *from control;

Actualice un campo diferente de precio:

update libros set stock=0 where codigo=145;

Verifique que el trigger se ha disparado consultando la tabla "control":

select *from control;

Se ha insertado una fila.

Verifique que la tabla "controlprecios" no tiene nuevos registros:

select *from controlprecios;

115 - Disparadores (habilitar y deshabilitar)

Un disparador puede estar en dos estados: habilitado (enabled) o deshabilitado (disabled).

Cuando se crea un trigger, por defecto está habilitado.

Se puede deshabilitar un trigger para que no se ejecute. Un trigger deshabilitado sigue existiendo, pero al ejecutar
una instrucción que lo dispara, no se activa.

Sintaxis para deshabilitar un trigger:

alter trigger NOMBREDISPARADOR disable;

Ejemplo: Deshabilitamos el trigger "tr_ingresar_empleados":

alter trigger tr_ingresar_empleados disable;

Sintaxis para habilitar un trigger que está deshabilitado:

alter trigger NOMBREDISPARADOR enable;

Ejemplo: Habilitamos el trigger "tr_actualizar_empleados":

alter trigger tr_actualizar_empleados enable;


Se pueden habilitar o deshabilitar todos los trigger establecidos sobre una tabla especifica, se emplea la siguiente
sentencia;

alter table TABLA disable all triggers;--deshabilita


alter table TABLA enable all triggers;-- habilita

La siguiente sentencia deshabilita todos los triggers de la tabla "empleados":

alter table empleados enable all triggers;

Podemos saber si un trigger está o no habilitado cosultando el diccionario "user_triggers", en la columna "status"
aparece "enabled" si está habilitado y "disabled" si está deshabilitado.

Problema:

Una empresa almacena los datos de sus empleados en una tabla denominada "empleados". En una tabla denominada
"controlCambios" guarda los cambios que se realizan en la tabla "empleados", en ella almacena el nombre del usuario
que realiza la modificación, la fecha, el valor anterior del campo modificado y el nuevo valor.

Eliminamos las tablas:

drop table empleados;


drop table controlCambios;

Creamos las tablas, con las siguientes estructuras:

create table empleados(


documento char(8) not null,
nombre varchar2(30) not null,
domicilio varchar2(30),
seccion varchar2(20)
);

create table controlCambios(


usuario varchar2(30),
fecha date,
datoanterior varchar2(30),
datonuevo varchar2(30)
);

Ingresamos algunos registros:

insert into empleados values('22222222','Ana Acosta','Bulnes 56','Secretaria');


insert into empleados values('23333333','Bernardo Bustos','Bulnes 188','Contaduria');
insert into empleados values('24444444','Carlos Caseres','Caseros 364','Sistemas');
insert into empleados values('25555555','Diana Duarte','Colon 1234','Sistemas');
insert into empleados values('26666666','Diana Duarte','Colon 897','Sistemas');
insert into empleados values('27777777','Matilda Morales','Colon 542','Gerencia');

Creamos un disparador que se active cuando modificamos algún campo de "empleados" y almacene en
"controlCambios" el nombre del usuario que realiza la actualización, la fecha, el dato que se cambia y el nuevo valor:

create or replace trigger tr_actualizar_empleados


before update
on empleados
for each row
begin
if updating('documento') then
insert into controlCambios values(user,sysdate, :old.documento, :new.documento);
end if;
if updating('nombre') then
insert into controlCambios values(user,sysdate, :old.nombre, :new.nombre);
end if;
if updating('domicilio') then
insert into controlCambios values(user,sysdate, :old.domicilio, :new.domicilio);
end if;
if updating('seccion') then
insert into controlCambios values(user,sysdate, :old.seccion, :new.seccion);
end if;
end tr_actualizar_empleados;

Creamos otro desencadenador que se active cuando ingresamos un nuevo registro en "empleados", debe almacenar en
"controlCambios" el nombre del usuario que realiza el ingreso, la fecha, "null" en "datoanterior" (porque se dispara
con una inserción) y en "datonuevo" el documento:

create or replace trigger tr_ingresar_empleados


before insert
on empleados
for each row
begin
insert into controlCambios values(user,sysdate, null, :new.documento);
end tr_ingresar_empleados;

Creamos un tercer trigger sobre "empleados" que se active cuando eliminamos un registro en "empleados", debe
almacenar en "controlCambios" el nombre del usuario que realiza la eliminación, la fecha, el documento en
"datoanterior" y "null" en "datonuevo":

create or replace trigger tr_eliminar_empleados


before delete
on empleados
for each row
begin
insert into controlCambios values(user,sysdate, :old.documento, null);
end tr_eliminar_empleados;

Los tres triggers están habilitados. Consultamos el diccionario "user_triggers" para corroborarlo:

select trigger_name, triggering_event, status


from user_triggers
where trigger_name like 'TR%EMPLEADOS';

Vamos a ingresar un empleado y comprobar que el trigger "tr_ingresar_empleados" se dispara recuperando los
registros de "controlCambios":

insert into empleados values('28888888','Pedro Perez','Peru 374','Secretaria');


select *from controlCambios;

Deshabilitamos el trigger "tr_ingresar_empleados":

alter trigger tr_ingresar_empleados disable;

Consultamos el diccionario "user_triggers" para corroborarlo:

select trigger_name, status


from user_triggers
where trigger_name like 'TR%EMPLEADOS';

El trigger "tr_ingresar_empleados" está deshabilitado, "tr_actualizar_empleados" y "tr_elimnar_empleados" están


habilitados.

Vamos a ingresar un empleado y comprobar que el trigger de inserción no se dispara recuperando los registros de
"controlCambios":

insert into empleados values('29999999','Rosa Rodriguez','Rivadavia 627','Secretaria');


select *from controlCambios;

Vamos a actualizar el domicilio de un empleado y comprobar que el trigger de actualización se dispara recuperando
los registros de "controlCambios":

update empleados set domicilio='Bulnes 567' where documento='22222222';


select *from controlCambios;

Deshabilitamos el trigger "tr_actualizar_empleados":


alter trigger tr_actualizar_empleados disable;

Consultamos el diccionario "user_triggers" para corroborarlo:

select trigger_name, status


from user_triggers
where trigger_name like 'TR%EMPLEADOS';

Los triggers "tr_ingresar_empleados" y "tr_actualizar_empleados" están deshabilitados, "tr_eliminar_empleados" está


habilitado.

Vamos a borrar un empleado de "empleados" y comprobar que el trigger de borrado se disparó recuperando los
registros de "controlCambios":

delete from empleados where documento= '29999999';


select *from controlCambios;

Deshabilitamos el trigger "tr_eliminar_empleados":

alter trigger tr_eliminar_empleados disable;

Consultamos el diccionario "user_triggers" para comprobarlo:

select trigger_name, status


from user_triggers
where table_name = 'EMPLEADOS';

Los tres trigger establecidos sobre "empleados" están deshabilitados.

Eliminamos un empleado de "empleados" y comprobamos que el trigger de borrado no se dispara recuperando los
registros de "controlCambios":

delete from empleados where documento= '28888888';


select *from controlCambios;

Habilitamos el trigger "tr_actualizar_empleados":

alter trigger tr_actualizar_empleados enable;

Actualizamos la sección de un empleado y comprobamos que el trigger de actualización se dispara recuperando los
registros de "controlCambios":

update empleados set seccion='Sistemas' where documento='23333333';


select *from controlCambios;

Habilitamos todos los triggers establecidos sobre "empleados":

alter table empleados enable all triggers;

Consultamos el diccionario "user_triggers" para comprobar que el estado (status) de todos los triggers establecidos
sobre "empleados" es habilitado:

select trigger_name, triggering_event, status


from user_triggers
where table_name = 'EMPLEADOS';

Los tres trigger establecidos sobre "empleados" han sido habilitados. Se activarán ante cualquier sentencia "insert",
"update" y "delete".

116 - Disparador (eliminar)


Para eliminar un trigger se emplea la siguiente sentencia:

drop trigger NOMBRETRIGGER;

Ejemplo:

drop trigger tr_insertar_libros;

Si eliminamos una tabla, se eliminan todos los triggers establecidos sobre ella.

Problema:

Una librería almacena los datos de sus libros en una tabla denominada "libros" y controla las acciones que los
empleados realizan sobre dicha tabla almacenando en la tabla "control" el nombre del usuario, la fecha, y el tipo de
modificación que se realizó sobre la tabla "libros".

Eliminamos la tabla "libros" y la tabla "control":

drop table libros;


drop table control;

Creamos las tablas con las siguientes estructuras:

create table libros(


codigo number(6),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(6,2)
);

create table control(


usuario varchar2(30),
fecha date,
operacion varchar2(20)
);

Creamos un desencadenador que se active cuando ingresamos un nuevo registro en "libros", debe almacenar en
"control" el nombre del usuario que realiza el ingreso, la fecha e "insercion" en "operacion":

create or replace trigger tr_ingresar_libros


before insert
on libros
for each row
begin
insert into control values(user,sysdate,'insercion');
end tr_ingresar_libros;

Creamos un segundo disparador que se active cuando modificamos algún campo de "libros" y almacene en "control" el
nombre del usuario que realiza la actualización, la fecha y en "operacion" coloque el nombre del campo actualizado:

create or replace trigger tr_actualizar_libros


before update
on libros
for each row
begin
if updating('codigo') then
insert into control values(user,sysdate,'codigo');
end if;
if updating('titulo') then
insert into control values(user,sysdate,'titulo');
end if;
if updating('autor') then
insert into control values(user,sysdate,'autor');
end if;
if updating('editorial') then
insert into control values(user,sysdate,'editorial');
end if;
if updating('precio') then
insert into control values(user,sysdate,'precio');
end if;
end tr_actualizar_libros;

Creamos un tercer trigger sobre "libros" que se active cuando eliminamos un registro de "libros", debe almacenar en
"control" el nombre del usuario que realiza la eliminación, la fecha y "borrado" en "operacion":

create or replace trigger tr_eliminar_libros


before delete
on libros
for each row
begin
insert into control values(user,sysdate,'borrado');
end tr_eliminar_libros;

Vemos cuántos triggers están asociados a "libros"; consultamos el diccionario "user_triggers":

select trigger_name, triggering_event, status


from user_triggers
where table_name = 'LIBROS';

Hay tres.

Ingresamos algunos registros en "libros":

insert into libros values(100,'Uno','Richard Bach','Planeta',25);


insert into libros values(101,'El aleph','Borges','Emece',28);
insert into libros values(102,'Matematica estas ahi','Paenza','Nuevo siglo',12);
insert into libros values(103,'Aprenda PHP','Molina Mario','Nuevo siglo',55);
insert into libros values(144,'Alicia en el pais de las
maravillas','Carroll','Planeta',35);

Comprobamos que el trigge "tr_ingresar_libros" se disparó recuperando los registros de "control":

select *from control;

Hay 5 registros.

Actualizamos la editorial de varios libros y comprobamos que el trigger de actualización se disparó recuperando los
registros de "control":

update libros set editorial='Sudamericana' where editorial='Planeta';


select *from control;

2 nuevos registros.

Borramos un libro de "libros" y comprobamos que el trigger de borrado se disparó recuperando los registros de
"control":

delete from libros where codigo=101;


select *from control;

Actualizamos el autor de un libro y comprobamos que el trigger de actualización se dispara recuperando los registros
de "control":

update libros set autor='Adrian Paenza' where autor='Paenza';


select *from control;

Eliminamos la tabla "libros":


drop table libros;

Consultamos el diccionario "user_triggers" para comprobar que al eliminar "libros" se eliminaron también los triggers
asociados a ella:

select trigger_name, triggering_event, status


from user_triggers
where table_name = 'LIBROS';

Los tres trigger asociados a "libros" han sido eliminados.

117 - Errores definidos por el usuario

Oracle permite definir a los usuarios errores especificando un número de error y un mensaje empleando el
procedimiento "raise_application_error".

Sintaxis:

raise_application_error (NUMERO,TEXTO);

El procedimiento "raise_application_error" permite emitir un mensaje de error. El NUMERO de mensaje debe ser un
número negativo entre -20000 y -20999 y el mensaje de TEXTO una cadena de caracteres de hasta 2048 bytes.

Si durante la ejecución de un trigger se produce un error definido por el usuario, se anulan todas las actualizaciones
realizadas por la acción del trigger así como el evento que la activó, es decir, se reanuda cualquier efecto retornando
un mensaje y se deshace la orden ejecutada.

En caso que se incluya en el cuerpo de un disparador "after" (que se ejecuta después de la sentencia, es decir,
cuando los datos ya han sido actualizados), la sentencia será deshecha (rollback). Si es un disparador "before" (que se
ejecuta antes de la sentencia, o sea, cuando los datos aún no han sido actualizados), la sentencia no se ejecuta.

Veamos un ejemplo: Creamos un trigger de actualización a nivel de fila sobre la tabla "libros". Ante cualquier
modificación de los registros de "libros", se debe ingresar en la tabla "control", el nombre del usuario que realizó la
actualización y la fecha; pero, controlamos que NO se permita modificar el campo "codigo", en caso de suceder, la
acción no debe realizarse y debe mostrarse un mensaje de error indicándolo:

create or replace trigger tr_actualizar_libros


before update
on libros
for each row
begin
if updating('codigo') then
raise_application_error(-20001,'No se puede modificar el código de los libros');
else
insert into control values(user,sysdate);
end if;
end;

Si se actualiza cualquier campo de "libros", se dispara el trigger; si se actualiza el campo "codigo", aparece un
mensaje de error y la actualización no se realiza; en caso de actualizarse cualquier otro campo, se almacenará en
"control", el nombre del usuario y la fecha.

Problema:

Una librería almacena en "libros" los datos de sus libros para la venta. En una tabla denominada "control" almacena el
nombre del usuario y la fecha, cada vez que se actualiza la tabla "libros".

Eliminamos las tablas "libros" y "control":


drop table libros;
drop table control;

Creamos las tablas:

create table libros(


codigo number(5),
titulo varchar2(40),
autor varchar2(30),
editorial varchar2(20),
precio number(6,2)
);

create table control(


usuario varchar2(30),
fecha date
);

Ingresamos algunos registros en "libros":

insert into libros values (101,'Uno','Richard Bach','Planeta',25);


insert into libros values (102,'Matematica estas ahi','Paenza','Nuevo siglo',12);
insert into libros values (103,'El aleph','Borges','Emece',28);
insert into libros values (104,'Aprenda PHP','Molina','Nuevo siglo',55);
insert into libros values (105,'El experto en laberintos','Gaskin','Planeta',23);

Creamos un trigger de actualización a nivel de fila sobre la tabla "libros" que se dispare antes que se ejecute una
actualización. Ante cualquier modificación de los registros de "libros", se debe ingresar en la tabla "control", el
nombre del usuario que realizó la actualización y la fecha. Pero, controlamos que NO se permita modificar el campo
"codigo", en caso de suceder, la acción no debe realizarse y debe mostrarse un mensaje de error indicándolo:

create or replace trigger tr_actualizar_libros


before update
on libros
for each row
begin
if updating('codigo') then
raise_application_error(-20001,'No se puede modificar el código de los libros');
else
insert into control values(user,sysdate);
end if;
end;

Aumentamos el precio de todos los libros de editorial "Planeta":

update libros set precio=precio+precio*0.1 where editorial='Planeta';

Controlamos que los precios se han modificado y el trigger se ha disparado almacenando en "control" 2 registros:

select *from libros;


select *from control;

Intentamos modificar el código de un libro:

update libros set codigo=109 where codigo=101;

Note que muestra el mensaje de error definido por nosotros. El trigger se disparó y se ejecutó
"raise_application_error", por lo tanto, el código no se modificó. Controlamos que el código no se modificó:

select *from libros;

Reemplazamos el trigger creado anteriormente para que ahora se dispare DESPUES (after) de cualquier modificación
de los registros de "libros"; debe realizar lo mismo que el anterior, ingresar en la tabla "control", el nombre del
usuario que realizó la actualización y la fecha. Pero, controlando que NO se permita modificar el campo "codigo", en
caso de suceder, la acción no debe revertirse y debe mostrarse un mensaje de error indicándolo:

create or replace trigger tr_actualizar_libros


after update
on libros
for each row
begin
if updating('codigo') then
raise_application_error(-20001,'No se puede modificar el código de los libros');
else
insert into control values(user,sysdate);
end if;
end;

Intentamos modificar el código de un libro:

update libros set codigo=109 where codigo=101;

Note que muestra el mensaje de error definido por nosotros. El trigger fue definido "after", es decir, se disparó luego
de ejecutarse la actualización, pero también se ejecutó "raise_application_error", por lo tanto, la sentencia "update"
se deshizo.

Controlamos que el código no se modificó:

select * from libros;

118 - Seguridad y acceso a Oracle

Una de las tareas al administrar Oracle es permitir el acceso a bases de datos y asignar permisos sobre los objetos
que conforman una base de datos.

Para conectarnos con un servidor Oracle necesitamos un modo de acceso que incluye los permisos que dispondremos
durante la conexión; estos permisos se definen a partir de un nombre de usuario.

Un USUARIO es un identificador necesario para acceder a una base de datos. Un usuario es un conjunto de permisos
que se aplican a una conexión de base de datos. Un usuario es además propietario de ciertos objetos.

Los PRIVILEGIOS (permisos) especifican qué operaciones puede realizar un usuario y sobre qué objetos de la base de
datos tiene autorización, es decir, qué tarea puede realizar con esos objetos y si puede emitir determinadas
instrucciones. Estas operaciones pueden ser de dos tipos: de sistema y sobre objeto.

Un rol de base de datos es una agrupación de permisos de sistema y de objeto.

Un ROL (role) es un grupo de usuarios; permite agrupar usuarios para aplicarles permisos; así, al agregar un nuevo
usuario a la base de datos, no es necesario concederle permiso para cada objeto, sino que lo agregamos a un rol;
cuando asignamos permisos sobre un objeto al rol, automáticamente el permiso afectará a los usuarios que
pertenezcan a tal rol.

Los permisos controlan el acceso a los distintos objetos de una base de datos; pueden concederse a nivel de usuario
(individualmente) o a nivel de rol (a todos los usuarios de un grupo).

Los permisos que un usuario tiene en una base de datos dependen de los permisos de usuario y de los roles al que
pertenezca dicho usuario.

Usuarios, roles y permisos son la base de los mecanismos de seguridad de Oracle.

119 - Usuarios (crear)

Puede haber varios usuarios diferentes de la base de datos. Cada uno es propietario de sus objetos.
Para crear un usuario debemos conectarnos a la base datos como administradores (por ejemplo "system").

Sintaxis básica para crear un usuario:

create user NOMBREUSUARIO identified by CONTRASEÑA


default tablespace NOMBRETABLESPACEPORDEFECTO
quota CANTIDAD on TABLEESPACE;
** [default role ROLE, ALL];

La cláusula "identified by" permite indicar una contraseña.

La cláusula "default tablespace" será el tablespace (espacio de tablas) por defecto en la creación de objetos del
usuario. Si se omite se utilizará el tablespace SYSTEM. Los tablespaces son unidades lógicas en las cuales de divide
una base de datos, en las cuales se almacenan los objetos (tablas, secuencias, etc.); todos los objetos están
almacenados dentro de un tablespace.

La cláusula "quota" permite configurar un espacio en bytes, Kb o Mb en la base de datos. Si no se especifica, por
defecto es cero y no podrá crear objetos.

La cláusula "default role" permite asignar roles de permisos durante la creación del usuario.

Ejemplo:

create user ana identified by anita;

Con la sentencia anterior se crea un usuario denominado "ana" con la clave "anita", el tablespace por defecto es
"system" porque no se especificó otro.

Con la siguiente sentencia se crea un usuario denominado "juan" con la clave "juancito", se le asigna un espacio de
100 mb:

create user juan identified by juancito


default tablespace system
quota 100M on system;

Si intentamos crear un usuario que ya existe, Oracle muestra un mensaje de error indicando tal situación.

El diccionario "dba_users" muestra información sobre todos los usuarios; el nombre de usuario (username), contraseña
(password), estado (account_status), espacio (default_tablespace), fecha de expiración (expiry_date), fecha de
creación (created), entre otros.

Luego de crear un usuario, aún no podemos conectarnos, ya que no tenemos permiso para crear una sesión. Los
permisos se aprenderán próximamente.

Problema:

Sabemos que para crear un usuario debemos conectarnos a la base datos como administradores (por ejemplo
"system").

Necesitamos crear un usuario "ana"; antes vamos a eliminarlo por si existe (luego veremos detenidamente cómo
eliminar usuarios y explicaremos la siguiente sentencia):

drop user ana cascade;

Creamos un usuario denominado "ana" con la contraseña "anita":

create user ana identified by anita;

Aparece un mensaje indicando que el usuario "ana" ha sido creado.

Necesitamos crear un usuario denominado "juan"; antes vamos a eliminarlo por si existe:
drop user juan cascade;

Creamos el usuario "juan" con la contraseña "juancito", le asignamos un espacio de 100 mb en "system":

create user juan identified by juancito


default tablespace system
quota 100M on system;

Si intentamos crear un usuario que ya existe, Oracle muestra un mensaje de error indicando tal situación.

create user juan identified by juancito;

Mensaje de error.

Consultamos el diccionario "dba_users" y analizamos la información que nos muestra:

select username, password, default_tablespace, created from dba_users;

El resultado nos muestra el nombre de usuario, si tiene o no contraseña, el espacio asignado (tablespace) y fecha de
creación.

120 - Permiso de conexión

Los usuarios necesitan permisos para poder acceder a la base de datos y a los objetos de la misma.

Los privilegios pueden ser de dos tipos: del sistema y sobre objetos.

Como mínimo, un usuario debe tener permiso para conectarse.

El permiso "create session" es un privilegio de sistema.

Para conceder permiso de conexión a un usuario empleamos la instrucción "grant".

Sintaxis básica:

grant create session


to USUARIO;

En el siguiente ejemplo concedemos al usuario "juan" permiso para conectarse:

grant create session to juan;

Podemos consultar el diccionario "dba_sys_privs" para encontrar los privilegios concedidos a los usuarios. Nos
mostrará el nombre del usuario (grantee) y el permiso (privilege), entre otra información que analizaremos
próximamente.

Luego de tener permiso para crear sesión, puede crear una sesión presionando el ícono "new connection" en la solapa
"connections"; se abrirá una ventana en la cual deberá colocar un nombre de conexión ("connection name", puede ser
el mismo nombre de usuario), el nombre del usuario ("username") y la contraseña ("password"), luego presionar el
botón "connect"; se abrirá una nueva solapa (nueva conexión) con el nombre del usuario; no se abrirá la nueva
conexión si:

a) el usuario para quien quiere abrir una nueva sesión no existe,

b) la contraseña es incorrecta o

c) el usuario existe pero no tiene permiso "create session".


Si consultamos el diccionario "user_sys_privs" obtendremos la misma información que "dba_sys_privs" pero
únicamente del usuario actual.

Podemos averiguar el nombre del usuario conectado con la siguiente sentencia:

select user from dual;

Problema:

Creamos un usuario denominado "ana", con contraseña "anita", le asignamos espacio en "system" (100M). Antes lo
eliminamos por si existe:

drop user ana cascade;

create user ana identified by anita


default tablespace system
quota 100M on system;

Creamos un usuario denominado "juan", con contraseña "juancito", le asignamos espacio en "system" (100M). Antes lo
eliminamos por si existe:

drop user juan cascade;

create user juan identified by juancito


default tablespace system
quota 100M on system;

Consultamos el diccionario "dba_users" y analizamos la información que nos muestra:

select username, password, default_tablespace, created from dba_users;

Verificamos que los usuarios "ana" y "juan" existen.

Consultamos el diccionario "dba_sys_privs" para encontrar los privilegios concedidos a nuestros usuarios. Nos mostrará
el nombre del usuario (grantee) y el permiso (si lo tiene):

select grantee, privilege from dba_sys_privs where GRANTEE='ANA' or grantee='JUAN';

Nos muestra que estos usuarios no tienen ningún privilegio concedido.

Concedemos a "juan" permiso para conectarse:

grant create session


to juan;

Consultamos el diccionario "dba_sys_privs" para encontrar los privilegios concedidos a "juan":

select grantee,privilege from dba_sys_privs


where grantee='JUAN';

Tiene permiso "create session".

Abrimos una nueva conexión para "juan":

Presionamos el ícono "new connection" en la solapa "connections"; se abre una ventana en la cual colocamos:

- "connection name" (nombre de la conexión): juan;

- "username" (nombre del usuario): juan y

- "password" (contraseña): juancito.


Luego presionamos "connect"; se abre una nueva solapa (nueva conexión) con el nombre del usuario (juan).

En la conexión de "juan" podemos consultar sus privilegios:

select username, privilege from user_sys_privs;

Note que únicamente aparecen los permisos del usuario actual.

Para obtener el nombre del usuario conectado, empleamos la siguiente sentencia:

select user from dual;

Aparece Juan.

Volvemos a la conexión "system" (la otra solapa).

Comprobamos el usuario actual:

select user from dual;

Aparece System.

Ya sabemos abrir una nueva sessión de usuario. Aprendimos que existen 3 razones por las cuales una nueva sesión no
se pueda iniciar; una de ellas es que el usuario no exista. Intentemos abrir una nueva conexión para un usuario
inexistente:

Presionamos el ícono "new connection" en la solapa "connections"; se abre una ventana en la cual colocamos:

- "connection name" (nombre de la conexión): pedro;

- "username" (nombre del usuario): pedro y

- "password" (contraseña): pedrito.

Luego presionamos "connect"; la sessión no se abre, un mensaje de error indica que el nombre de usuario o la
contraseña son inválidas y que la conexión se deniega.

Cancelamos.

Otra razón por la cual la apertura de una nueva sesión puede fallar es que el usuario no tenga permiso de conexión.
Intentemos abrir una nueva conexión para un usuario que no tenga tal permiso, caso de "ana":

Presionamos el ícono "new connection" en la solapa "connections"; se abre una ventana en la cual colocamos:

- "connection name" (nombre de la conexión): ana;

- "username" (nombre del usuario): ana y

- "password" (contraseña): anita.

Luego presionamos "connect"; la sessión no se abre, un mensaje de error indica que el usuario "ana" no tiene permiso
"create session" por lo cual se deniega la conexión. Cancelamos.

Concedemos a "ana" permiso de conexión:

grant create session


to ana;

Consultamos el diccionario "dba_sys_privs" para encontrar los privilegios concedidos a "ana":

select grantee,privilege from dba_sys_privs


where grantee='ANA';

Tiene permiso "create session".

La tercera razón por la cual puede no iniciarse una nueva sesión es que coloquemos la contraseña incorrecta.
Intentemos abrir una nueva conexión para un usuario que tenga permiso, pero le demos una contraseña incorrecta:

Presionamos el ícono "new connection" en la solapa "connections"; se abre una ventana en la cual colocamos:

- "connection name" (nombre de la conexión): ana;

- "username" (nombre del usuario): ana y

- "password" (contraseña): ana.

Luego presionamos "connect"; la sessión no se abre, un mensaje de error indica que el nombre de usuario o la
contraseña son inválidas y que la conexión se deniega.

Abramos una nueva conexión para "ana" colocando los datos correctos:

Presionamos el ícono "new connection" en la solapa "connections"; se abre una ventana en la cual colocamos:

- "connection name" (nombre de la conexión): ana;

- "username" (nombre del usuario): ana y

- "password" (contraseña): anita.

Presionamos "connect"; se abre una nueva solapa (nueva conexión) con el nombre del usuario (ana).

Consultamos el diccionario "user_sys_privs":

select username,privilege from user_sys_privs;

Note que únicamente aparecen los permisos del usuario actual.

Comprobamos que estamos en la sesión de "ana":

select user from dual;

121 - Privilegios del sistema (conceder)

Aprendimos que los usuarios necesitan permisos para poder acceder a la base de datos y a los objetos de la misma.
Dijimos que los privilegios pueden ser de dos tipos: a) del sistema y b) sobre objetos.

Hemos aprendido a conceder un privilegio de sistema: "create session", que es necesario para poder conectarse a la
base de datos, es decir, para iniciar una sesión.

Pero teniendo únicamente este permiso, no podemos hacer mucho, solamente iniciar una sesión, pero no podemos
crear tablas, ni ningún otro objeto; por ello son importantes los permisos de creación de objetos.

Aprendamos más sobre los privilegios de sistema.

Los privilegios de sistema son permisos para realizar ciertas operaciones en la base de datos.

Los siguientes son algunos de los privilegios de sistema existentes:

- create session: para conectarse a la base de datos;


- create table: crear tablas;

- create sequence: crear secuencias;

- create view: crear vistas;

- create trigger: crear disparadores en su propio esquema;

- create procedure: crear procedimientos y funciones;

- execute any procedure: ejecutar cualquier procedimiento en cualquier esquema;

- create user: crear usuarios y especificar claves;

- create role: crear roles;

- drop user: eliminar usuarios.

Se asignan privilegios de sistema a un usuario mediante la instrucción "grant":

Sintaxis básica:

grant PERMISODESISTEMA
to USUARIO;

Oracle permite conceder múltiples privilegios a múltiples usuarios en una misma sentencia, debemos separarlos por
comas.

En el siguiente ejemplo se concede el permiso para crear sesión a los usuarios "juan" y "ana":

grant create sesion


to juan, ana;

En el siguiente ejemplo se conceden los permisos para crear tablas y vistas al usuario "ana":

grant create table, create view


to ana;

En el siguiente ejemplo se conceden 2 permisos a 2 usuarios en una sola sentencia:

grant create trigger, create procedure


to juan, ana;

Consultando el diccionario "dba_sys_privs" encontramos los privilegios concedidos a los distintos usuarios; y
consultando "user_sys_privs" obtendremos la misma información pero únicamente del usuario actual.

Problema:

Creamos un usuario denominado "ana", con contraseña "anita", le asignamos espacio en "system" (100M). Antes lo
eliminamos por si existe:

drop user ana cascade;

create user ana identified by anita


default tablespace system
quota 100M on system;

Creamos un usuario denominado "juan", con contraseña "juancito", le asignamos espacio en "system" (100M). Antes lo
eliminamos por si existe:
drop user juan cascade;

create user juan identified by juancito


default tablespace system
quota 100M on system;

Concedemos a ambos usuarios permiso para conectarse:

grant create session


to ana, juan;

Concedemos permiso para crear tablas y vistas al usuario "ana":

grant create table, create view


to ana;

Concedemos permiso para crear disparadores y procedimientos a ambos usuarios:

grant create trigger, create procedure


to juan, ana;

Consultamos el diccionario "dba_sys_privs" para ver los privilegios concedidos a "ana" y "juan":

select grantee, privilege from dba_sys_privs


where grantee='ANA' or grantee='JUAN'
order by grantee; Obtenemos la siguiente información:
GRANTEE PRIVILEGE
-------------------------------
ANA CREATE VIEW
ANA CREATE PROCEDURE
ANA CREATE TRIGGER
ANA CREATE SESSION
ANA CREATE TABLE
JUAN CREATE SESSION
JUAN CREATE PROCEDURE
JUAN CREATE TRIGGER

Iniciamos una nueva sesión como "ana". Como "ana" creamos una tabla:

create table prueba(


nombre varchar2(30),
apellido varchar2(30)
);

La tabla ha sido creada, porque "ana" tiene pivilegio "create table".

Podemos consultar el diccionario "user_sys_privs" para corroborar sus privilegios:

select privilege from user_sys_privs;Obtenemos la siguiente información:


PRIVILEGE
---------
CREATE TRIGGER
CREATE TABLE
CREATE SESSION
CREATE VIEW
CREATE PROCEDURE

Iniciamos una nueva sesión como "juan". Como "juan" intentamos crear una tabla:

create table prueba(


nombre varchar2(30),
apellido varchar2(30)
);

Mensaje de error "privilegios insuficientes". Esto sucede porque "juan", no tiene permiso para crear tablas.

Vemos los permisos de "juan":


select privilege from user_sys_privs;

No tiene permiso para crear tablas.

Cambiamos a la conexión "system" y concedemos a "juan" permiso para crear tablas:

grant create table


to juan;

Cambiamos a la solapa "juan" y creamos una tabla:

create table prueba(


nombre varchar2(30),
apellido varchar2(30)
);

Podemos hacerlo porque "juan" ahora tiene el permiso.

Vemos los permisos de "juan":

select privilege from user_sys_privs;

Cambiamos a la conexión "system". Veamos todas las tablas denominadas "PRUEBA":

select *from dba_objects where object_name='PRUEBA';

Note que hay una tabla propiedad de "ana" y otra que pertenece a "juan".

122 - Privilegios del sistema (with admin option)

Hemos aprendido la sintaxis básica para conceder permisos de sistema a los usuarios, mediante la instrucción "grant".

Agregando a la sentencia "grant", la cláusula "with admin option" concedemos permiso para ceder a terceros los
privilegios de sistema obtenidos. Es decir, la cláusula "with admin option" permite que el privilegio concedido a un
usuario (o rol) pueda ser otorgado a otros usuarios por el usuario al que estamos asignándoselo; es decir, se concede
permiso para conceder el permiso obtenido, a otros usuarios.

Sintaxis:

grant PERMISODESISTEMA
to USUARIO
with admin option;

En el siguiente ejemplo, concedemos el permiso de crear tablas al usuario "juan" y con "with admin option", el
usuario "juan" podrá conceder este permiso de crear tablas a otros usuarios:

grant create table


to juan
with grant option;

Podemos consultar el diccionario "dba_sys_privs" para encontrar los privilegios concedidos a los usuarios. Nos
mostrará una tabla con las siguientes columnas:

- grantee: el nombre del usuario,

- privilege: el permiso y

- admin_option: si el permiso adquirido puede ser cedido a otros o no, YES o NO.

También podría gustarte