Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
El curso brinda un concepto terico corto, luego un problema resuelto que invito a ejecutar, modificar y jugar con el mismo. Por ltimo, y lo ms importante, una serie de ejercicios propuestos que nos permitir saber si podemos aplicar el concepto. La nica herramienta que necesitamos inicialmente es este sitio ya que podr ejecutar todos los problemas como son la creacin de tablas, insert, delete, update, definicin de ndices y restricciones, creacin y ejecucin de procedimientos almacenados, vistas, subconsultas, creacin de trigger etc. La nica restriccin es que todos los visitantes de este sitio comparten la misma base de datos llamada: wi520641_sqlserverya (este nombre un poco singular se debe a que las empresas de hosting es la que lo define) Siempre que lancemos un comando SQL en el sitio www.sqlserverya.com.ar estaremos accediendo a la base de datos wi520641_sqlserverya.
Nosotros trabajaremos con la base de datos llamada wi520641_sqlserverya (este nombre se debe a que las empresas de hosting es la que lo define), que ya he creado en el servidor sqlserverya.com.ar. Para ver las tablas existentes creadas por los usuarios en una base de datos usamos el procedimiento almacenado "sp_tables @table_owner='dbo';": sp_tables @table_owner='dbo'; El parmetro @table_owner='dbo' indica que solo muestre las tablas de usuarios y no las que crea el SQL Server para administracin interna. Finalizamos cada comando con un punto y coma. Al crear una tabla debemos resolver qu campos (columnas) tendr y que tipo de datos almacenarn cada uno de ellos, es decir, su estructura. La sintaxis bsica 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 parntesis definimos los campos y sus tipos: create table usuarios ( nombre varchar(30), clave varchar(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 alfabtico y no puede contener espacios. La longitud mxima es de 128 caracteres. Si intentamos crear una tabla con un nombre ya existente (existe otra tabla con ese nombre), mostrar un mensaje indicando que ya hay un objeto llamado 'usuarios' en la base de datos y la sentencia no se ejecutar. Esto es muy importante ya que cuando haga los ejercicios en este sitio puede haber otra persona que haya creado una tabla con el nombre que usted especifique. Para ver la estructura de una tabla usamos el procedimiento almacenado "sp_columns" junto al nombre de la tabla: sp_columns usuarios; aparece mucha informacin que no analizaremos en detalle, como el nombre de la tabla, su propietario, los campos, el tipo de dato de cada campo, su longitud, etc.: ...COLUMN_NAME TYPE_NAME LENGHT
Para eliminar una tabla usamos "drop table" junto al nombre de la tabla a eliminar: drop table usuarios; Si intentamos eliminar una tabla que no existe, aparece un mensaje de error indicando tal situacin y la sentencia no se ejecuta. Para evitar este mensaje podemos agregar a la instruccin lo siguiente: if object_id('usuarios') is not null drop table usuarios; En la sentencia precedente especificamos que elimine la tabla "usuarios" si existe.
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 bsica 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 parntesis y separados por comas y luego de la clusula "values" colocamos los valores para cada campo, tambin entre parntesis y separados por comas. Para agregar un registro a la tabla tipeamos: insert into usuarios (nombre, clave) values ('Mariano','payaso'); 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". Es importante ingresar los valores en el mismo orden en que se nombran los campos: insert into usuarios (clave, nombre) values ('River','Juan'); En el ejemplo anterior se nombra primero el campo "clave" y luego el campo "nombre" por eso, los valores tambin se colocan en ese orden. 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');
Por ejemplo, si en un campo almacenaremos nmeros enteros, el tipo "float" sera una mala eleccin; si vamos a guardar precios, el tipo "float" es ms adecuado, no as "integer" que no tiene decimales. Otro ejemplo, si en un campo vamos a guardar un nmero telefnico o un nmero de documento, usamos "varchar", no "integer" porque si bien son dgitos, con ellos no
realizamos operaciones matemticas.
Hemos aprendido cmo ver todos los registros de una tabla, empleando la instruccin "select". La sintaxis bsica 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 separndolos 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. Los datos aparecen ordenados segn la lista de seleccin, en dicha lista los nombres de los campos se separan con comas.
Para la siguiente seleccin de registros especificamos una condicin que solicita los usuarios cuya clave es igual a "River": select nombre,clave from usuarios where clave='River'; Si ningn registro cumple la condicin establecida con el "where", no aparecer ningn 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 clusula "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".
Operadores relacionales
Los operadores son smbolos que permiten realizar operaciones matemticas, concatenar cadenas, hacer comparaciones. SQL Server tiene 4 tipos de operadores: relacionales (o de comparacin) aritmticos de concatenacin lgicos. Por ahora veremos solamente los primeros. Los operadores relacionales (o de comparacin) 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 SQL Server 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 condicin: select * from libros where autor<>'Borges'; Podemos comparar valores numricos. Por ejemplo, queremos mostrar los ttulos 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 condicin. No son los nicos, existen otros que veremos mas adelante.
Para eliminar los registros de una tabla usamos el comando "delete": delete from usuarios; Muestra un mensaje indicando la cantidad de registros que ha eliminado. Si no queremos eliminar todos los registros, sino solamente algunos, debemos indicar cul o cules, para ello utilizamos el comando "delete" junto con la clausula "where" con la cual establecemos la condicin 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, ningn registro cumple con la condicin especificada, ningn registro ser eliminado. Tenga en cuenta que si no colocamos una condicin, se eliminan todos los registros de la tabla nombrada.
"Federicolopez", queremos como nueva clave "Boca", necesitamos una condicin "where" que afecte solamente a este registro: update usuarios set clave='Boca' where nombre='Federicolopez'; Si Microsoft SQL Server no encuentra registros que cumplan con la condicin del "where", no se modifica ninguno. Las condiciones no son obligatorias, pero si omitimos la clusula "where", la actualizacin afectar a todos los registros. Tambin podemos actualizar varios campos en una sola instruccin: 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.
Comentarios
Para aclarar algunas instrucciones, en ocasiones, necesitamos agregar comentarios. Es posible ingresar comentarios en la lnea de comandos, es decir, un texto que no se ejecuta; para ello se emplean dos guiones (--) al comienzo de la lnea: select * from libros --mostramos los registros de libros; en la lnea anterior, todo lo que est luego de los guiones (hacia la derecha) no se ejecuta. Para agregar varias lneas 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 ttulos y nombres de los autores*/ from libros; todo lo que est entre los smbolos "/*" y "*/" no se ejecuta.
10
11
Tambin 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); Si intentamos ingresar el valor "null" en campos que no admiten valores nulos (como "titulo" o "autor"), SQL Server no lo permite, muestra un mensaje y la insercin no se realiza; por ejemplo: insert into libros (titulo,autor,editorial,precio) values(null,'Borges','Siglo XXI',25); Para ver cules campos admiten valores nulos y cules no, podemos emplear el procedimiento almacenado "sp_columns" junto al nombre de la tabla. Nos muestra mucha informacin, en la columna "IS_NULLABLE" vemos que muestra "NO" en los campos que no permiten valores nulos y "YES" en los campos que si los permiten. Para recuperar los registros que contengan el valor "null" en algn 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): select * from libros where precio is null; La sentencia anterior tendr una salida diferente a la siguiente: select * from libros where precio=0; Con la primera sentencia veremos los libros cuyo precio es igual a "null" (desconocido); con la segunda, los libros cuyo precio es 0. Igualmente para campos de tipo cadena, las siguientes sentencias "select" no retornan los mismos registros: 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", con la segunda, los libros cuya editorial guarda una cadena vaca. 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".
12
Clave primaria
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 nmero 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. Hay 2 maneras de hacerlo, por ahora veremos la sintaxis ms sencilla. Tenemos nuestra tabla "usuarios" definida con 2 campos ("nombre" y "clave"). La sintaxis bsica y general es la siguiente: create table NOMBRETABLA( CAMPO TIPO, ... primary key (NOMBRECAMPO) ); 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 varchar(20), clave varchar(10), primary key(nombre) ); Lo que hacemos agregar luego de la definicin de cada campo, "primary key" y entre parntesis, el nombre del campo que ser clave primaria. Una tabla slo 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, automticamente SQL Server lo convierte a "not null".
13
Luego de haber establecido un campo como clave primaria, al ingresar los registros, SQL Server controla que los valores para el campo establecido como clave primaria no estn repetidos en la tabla; si estuviesen repetidos, muestra un mensaje y la insercin 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 instruccin "insert" no se ejecuta. Igualmente, si realizamos una actualizacin, SQL Server controla que los valores para el campo establecido como clave primaria no estn repetidos en la tabla, si lo estuviese, aparece un mensaje indicando que se viola la clave primaria y la actualizacin no se realiza.
14
Este primer registro ingresado guardar el valor 1 en el campo correspondiente al cdigo. Si continuamos ingresando registros, el cdigo (dato que no ingresamos) se cargar automticamente siguiendo la secuencia de autoincremento. No est permitido ingresar el valor correspondiente al campo "identity", por ejemplo: insert into libros (codigo,titulo,autor,editorial,precio) values(5,'Martin Fierro','Jose Hernandez','Paidos',25); generar un mensaje de error. "identity" permite indicar el valor de inicio de la secuencia y el incremento, pero lo veremos posteriormente. Un campo definido como "identity" generalmente se establece como clave primaria. Un campo "identity" no es editable, es decir, no se puede ingresar un valor ni actualizarlo. Un campo de identidad no permite valores nulos, aunque no se indique especificamente. Si ejecutamos el procedimiento "sp_columns()" veremos que en el campo "codigo" en la columna "TYPE_NAME" aparece "int identity" y en la columna "IS_NULLABLE" aparece "NO". Los valores secuenciales de un campo "identity" se generan tomando como referencia el ltimo valor ingresado; si se elimina el ltimo registro ingresado (por ejemplo 3) y luego se inserta otro registro, SQL Server seguir la secuencia, es decir, colocar el valor "4".
15
La funcin "ident_seed()" retorna el valor de inicio del campo "identity" de la tabla que nombramos: select ident_seed('libros'); La funcin "ident_incr()" retorna el valor de incremento del campo "identity" de la tabla nombrada: select ident_incr('libros'); Hemos visto que en un campo declarado "identity" no puede ingresarse explcitamente un valor. Para permitir ingresar un valor en un campo de identidad se debe activar la opcin "identity_insert": set identity_insert libros on; Es decir, podemos ingresar valor en un campo "identity" seteando la opcin "identity_insert" en "on". Cuando "identity_insert" est en ON, las instrucciones "insert" deben explicitar un valor: insert into libros (codigo,titulo) values (5,'Alicia en el pais de las maravillas'); Si no se coloca un valor para el campo de identidad, la sentencia no se ejecuta y aparece un mensaje de error: insert into libros (titulo,autor, editorial) values ('Matematica estas ahi','Paenza','Paidos'); El atributo "identity" no implica unicidad, es decir, permite repeticin de valores; por ello hay que tener cuidado al explicitar un valor porque se puede ingresar un valor repetido. Para desactivar la opcin "identity_insert" tipeamos: set identity_insert libros off;
16
Truncate table
Aprendimos que para borrar todos los registro de una tabla se usa "delete" sin condicin "where". Tambin podemos eliminar todos los registros de una tabla con "truncate table". Por ejemplo, queremos vaciar la tabla "libros", usamos: truncate table libros; La sentencia "truncate table" vaca la tabla (elimina todos los registros) y conserva la estructura de la tabla. La diferencia con "drop table" es que esta sentencia borra la tabla, "truncate table" la vaca. La diferencia con "delete" es la velocidad, es ms rpido "truncate table" que "delete" (se nota cuando la cantidad de registros es muy grande) ya que ste borra los registros uno a uno. Otra diferencia es la siguiente: cuando la tabla tiene un campo "identity", si borramos todos los registros con "delete" y luego ingresamos un registro, al cargarse el valor en el campo de identidad, contina con la secuencia teniendo en cuenta el valor mayor que se haba guardado; si usamos "truncate table" para borrar todos los registros, al ingresar otra vez un registro, la secuencia del campo de identidad vuelve a iniciarse en 1. Por ejemplo, tenemos la tabla "libros" con el campo "codigo" definido "identity", y el valor ms alto de ese campo es "2", si borramos todos los registros con "delete" y luego ingresamos un registro, ste guardar el valor de cdigo "3"; si en cambio, vaciamos la tabla con "truncate table", al ingresar un nuevo registro el valor del cdigo se iniciar en 1 nuevamente.
17
Los valores que podemos guardar son: TEXTO: Para almacenar texto usamos cadenas de caracteres. Las cadenas se colocan entre comillas simples. Podemos almacenar letras, smbolos y dgitos con los que no se realizan operaciones matemticas, por ejemplo, cdigos de identificacin, nmeros de documentos, nmeros telefnicos. SQL Server ofrece los siguientes tipos: char, nchar, varchar, nvarchar, text y ntext. NUMEROS: Existe variedad de tipos numricos para representar enteros, decimales, monedas. Para almacenar valores enteros, por ejemplo, en campos que hacen referencia a cantidades, precios, etc., usamos el tipo integer (y sus subtipos: tinyint, smallint y bigint). Para almacenar valores con decimales exactos, utilizamos: numeric o decimal (son equivalentes). Para guardar valores decimales aproximados: float y real. Para almacenar valores monetarios: money y smallmoney. FECHAS y HORAS: para guardar fechas y horas SQL Server dispone de 2 tipos: datetime y smalldatetime.
Existen otros tipos de datos que analizaremos en secciones prximas. Entonces, cuando creamos una tabla y definir sus campos debemos elegir el tipo de dato ms preciso. Por ejemplo, si necesitamos almacenar nombres usamos texto; si un campo numrico almacenar solamente valores enteros el tipo "integer" es ms adecuado que, por ejemplo un "float"; si necesitamos almacenar precios, lo ms lgico es utilizar el tipo "money". A continuacin analizaremos en detalle cada tipo de datos bsicos.
18
Tenemos los siguientes tipos: 1. varchar(x): define una cadena de caracteres de longitud variable en la cual determinamos el mximo de caracteres con el argumento "x" que va entre parntesis. Si se omite el argumento coloca 1 por defecto. Su rango va de 1 a 8000 caracteres. 2. char(x): define una cadena de longitud fija determinada por el argumento "x". Si se omite el argumento coloca 1 por defecto. Su rango es de 1 a 8000 caracteres. Si la longitud es invariable, es conveniente utilizar el tipo char; caso contrario, el tipo varchar. Ocupa tantos bytes como se definen con el argumento "x". "char" viene de character, que significa caracter en ingls. 3. text: guarda datos binarios de longitud variable, puede contener hasta 2000000000 caracteres. No admite argumento para especificar su longitud. 4. nvarchar(x): es similar a "varchar", excepto que permite almacenar caracteres Unicode, su rango va de 0 a 4000 caracteres porque se emplean 2 bytes por cada caracter. 5. nchar(x): es similar a "char" excpeto que acepta caracteres Unicode, su rango va de 0 a 4000 caracteres porque se emplean 2 bytes por cada caracter. 6. ntext: es similar a "text" excepto que permite almacenar caracteres Unicode, puede contener hasta 1000000000 caracteres. No admite argumento para especificar su longitud. En general se usarn los 3 primeros. Si intentamos almacenar en un campo una cadena de caracteres de mayor longitud que la definida, aparece un mensaje indicando tal situacin y la sentencia no se ejecuta. Por ejemplo, si definimos un campo de tipo varchar(10) y le asignamos la cadena 'Aprenda PHP' (11 caracteres), aparece un mensaje y la sentencia no se ejecuta. Si ingresamos un valor numrico (omitiendo las comillas), lo convierte a cadena y lo ingresa como tal. Por ejemplo, si en un campo definido como varchar(5) ingresamos el valor 12345, lo toma como si hubisemos tipeado '12345', igualmente, si ingresamos el valor 23.56, lo convierte a '23.56'. Si el valor numrico, 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 segn el caso, el ms preciso. Para almacenar cadenas que varan en su longitud, es decir, no todos los registros tendrn la misma longitud en un campo determinado, se emplea "varchar" en lugar de "char". Por ejemplo, en campos que guardamos nombres y apellidos, no todos los nombres y apellidos tienen la misma longitud.
19
Para almacenar cadenas que no varan en su longitud, es decir, todos los registros tendrn la misma longitud en un campo determinado, se emplea "char". Por ejemplo, definimos un campo "codigo" que constar de 5 caracteres, todos los registros tendrn un cdigo de 5 caracteres, ni ms ni menos. Para almacenar valores superiores a 8000 caracteres se debe emplear "text". Tipo Bytes de almacenamiento _______________________________________ varchar(x) char(x) text nvarchar(x) nchar(x) ntext 0 a 8K 0 a 8K 0 a 2GB 0 a 8K 0 a 8K 0 a 2GB
20
defecto es "0". Por ejemplo, si definimos "decimal(4)" se pueden guardar valores entre 9999 y 9999. El rango depende de los argumentos, tambin los bytes que ocupa. Se utiliza el punto como separador de decimales. Si ingresamos un valor con ms decimales que los permitidos, redondea al ms cercano; por ejemplo, si definimos "decimal(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. Para almacenar valores numricos APROXIMADOS con decimales utilizamos: 3) float y real: De 1.79E+308 hasta 1.79E+38. Guarda valores aproximados 4) real: Desde 3.40E+308 hasta 3.40E+38. Guarda valores aproximados. Para almacenar valores MONETARIOS empleamos: 5) money: Puede tener hasta 19 digitos y slo 4 de ellos puede ir luego del separador decimal; entre 900000000000000.5808 aprox y 900000000000000.5807. 6) smallmoney: Entre 200000.3648 y 200000.3647 aprox. Para todos los tipos numricos: - si intentamos ingresar un valor fuera de rango, no lo permite. - si ingresamos una cadena, SQL Server intenta convertirla a valor numrico, si dicha cadena consta solamente de dgitos, la conversin 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 SQL Server no puede convertir a valor numrico, muestra un mensaje de error y la sentencia no se ejecuta.
Por ejemplo, definimos un campo de tipo decimal(5,2), si ingresamos la cadena '12.22', la convierte al valor numrico 12.22 y la ingresa; si intentamos ingresar la cadena '1234.56', la convierte al valor numrico 1234.56, pero como el mximo valor permitido es 999.99, muestra un mensaje indicando que est fuera de rango. Si intentamos ingresar el valor '12y.25', SQL Server no puede realizar la conversin y muestra un mensaje de error. Es importante elegir el tipo de dato adecuado segn el caso, el ms preciso. Por ejemplo, si un campo numrico almacenar valores positivos menores a 255, el tipo "int" no es el ms adecuado, conviene el tipo "tinyint", de esta manera usamos el menor espacio de almacenamiento posible. Si vamos a guardar valores monetarios menores a 200000 conviene emplear "smallmoney" en lugar de "money".
21
Tipo Bytes de almacenamiento _______________________________________ int smallint tinyint bigint decimal float real money smallmoney 4 2 1 8 2 a 17 4u8 4u8 8 4
22
Para ingresar una fecha con formato "da-mes-ao", tipeamos: set dateformat dmy; El formato por defecto es "mdy". Todos los valores de tipo "datetime" se muestran en formato "ao-mes-da hora:minuto:segundo .milisegundos", independientemente del formato de ingreso que hayamos seteado. Podemos ingresar una fecha, sin hora, en tal caso la hora se guarda como "00:00:00". Por ejemplo, si ingresamos '25-12-01' (ao de 2 dgitos), lo mostrar as: '2001-12-25 00:00:00.000'. Podemos ingresar una hora sin fecha, en tal caso, coloca la fecha "1900-01-01". Por ejemplo, si ingresamos '10:15', mostrar '1900-01-01 10:15.000'. Podemos emplear los operadores relacionales vistos para comparar fechas. Tipo Bytes de almacenamiento _______________________________________ datetime 8 smalldatetime 4
23
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 ms (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 NO hayan sido declarados "not null", es decir, que permitan valores nulos (se guardar "null"); si omitimos el valor para un campo "not null", la sentencia no se ejecuta. se DEBE omitir el valor para el campo"identity". Salvo que identity_insert este en on. se pueden omitir valores para campos declarados "not null" siempre que tengan definido un valor por defecto con la clusula "default" (tema que veremos a continuacin).
24
cantidad tinyint default 0 ); Si al ingresar un nuevo registro omitimos los valores para el campo "autor" y "cantidad", Sql Server insertar los valores por defecto; el siguiente valor de la secuencia en "codigo", en "autor" colocar "Desconocido" y en cantidad "0". Entonces, si al definir el campo explicitamos un valor mediante la clusula "default", se ser el valor por defecto. Ahora, al visualizar la estructura de la tabla con "sp_columns" podemos entender lo que informa la columna "COLUMN_DEF", muestra el valor por defecto del campo. Tambin se puede utilizar "default" para dar el valor por defecto a los campos en sentencias "insert", por ejemplo: insert into libros (titulo,autor,precio,cantidad) values ('El gato con botas',default,default,100); Si todos los campos de una tabla tienen valores predeterminados (ya sea por ser "identity", permitir valores nulos o tener un valor por defecto), se puede ingresar un registro de la siguiente manera: insert into libros default values; La sentencia anterior almacenar un registro con los valores predetermiandos para cada uno de sus campos. Entonces, la clusula "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" tomarn los valores por defecto: si tiene el atributo "identity": el valor de inicio de la secuencia si es el primero o el siguiente valor de la secuencia, no admite clusula "default"; si permite valores nulos y no tiene clusula "default", almacenar "null"; si est declarado explcitamente "not null", no tiene valor "default" y no tiene el atributo "identity", no hay valor por defecto, as que causar un error y el "insert" no se ejecutar. si tiene clusula "default" (admita o no valores nulos), el valor definido como predeterminado; para campos de tipo fecha y hora, si omitimos la parte de la fecha, el valor predeterminado para la fecha es "1900-01-01" y si omitimos la parte de la hora, "00:00:00".
25
Un campo slo 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.
26
Todas las operaciones matemticas retornan "null" en caso de error. Ejemplo: select 5/0; Los operadores de concatenacin: permite concatenar cadenas, el ms (+). Para concatenar el ttulo, el autor y la editorial de cada libro usamos el operador de concatenacin ("+"): select titulo+'-'+autor+'-'+editorial from libros; Note que concatenamos adems unos guiones para separar los campos.
Alias
Una manera de hacer ms comprensible el resultado de una consulta consiste en cambiar los encabezados de las columnas. Por ejemplo, tenemos la tabla "agenda" con un campo "nombre" (entre otros) en el cual se almacena el nombre y apellido de nuestros amigos; queremos que al mostrar la informacin de dicha tabla aparezca como encabezado del campo "nombre" el texto "nombre y apellido", para ello colocamos un alias de la siguiente manera: select nombre as NombreYApellido, domicilio,telefono from agenda; Para reemplazar el nombre de un campo 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 ms de una palabra, es necesario colocarla entre comillas simples: select nombre as 'Nombre y apellido', domicilio,telefono from agenda; Un alias puede contener hasta 128 caracteres. Tambin se puede crear un alias para columnas calculadas. La palabra clave "as" es opcional en algunos casos, pero es conveniente usarla.
27
Entonces, un "alias" se usa como nombre de un campo o de una expresin. En estos casos, son opcionales, sirven para hacer ms comprensible el resultado; en otros casos, que veremos ms adelante, son obligatorios.
Funciones
Una funcin es un conjunto de sentencias que operan como una unidad lgica. Una funcin tiene un nombre, retorna un parmetro de salida y opcionalmente acepta parmetros de entrada. Las funciones de SQL Server no pueden ser modificadas, las funciones definidas por el usuario si. SQL Server ofrece varios tipos de funciones para realizar distintas operaciones. Se pueden clasificar de la siguiente manera: 1) de agregado: realizan operaciones que combinan varios valores y retornan un nico valor. Son "count", "sum", "min" y "max". 2) escalares: toman un solo valor y retornan un nico valor. Pueden agruparse de la siguiente manera: - de configuracin: retornan informacin referida a la configuracin. Ejemplo: select @@version; - retorna la fecha, versin y tipo de procesador de SQL Server. - de cursores: retornan informacin sobre el estado de un cursor. - de fecha y hora: operan con valores "datetime" y "smalldatetime". Reciben un parmetro de tipo fecha y hora y retornan un valor de cadena, numrico o de fecha y hora. - matemticas: realizan operaciones numricas, geomtricas y trigonomtricas. - de metadatos: informan sobre las bases de datos y los objetos. - de seguridad: devuelven informacin referente a usuarios y funciones. - de cadena: operan con valores "char", "varchar", "nchar", "nvarchar", "binary" y "varbinary" y devuelven un valor de cadena o numrico. - del sistema: informan sobre opciones, objetos y configuraciones del sistema. Ejemplo: select user_name(); - estadsticas del sistema: retornan informacin referente al rendimiento del sistema. - texto e imagen: realizan operaciones con valor de entrada de tipo text o image y retornan informacin referente al mismo.
28
3) de conjuntos de filas: retornan conjuntos de registros. Se pueden emplear las funciones del sistema en cualquier lugar en el que se permita una expresin en una sentencia "select". Estudiaremos algunas de ellas.
Ejemplo: se convierte el valor numrico "123.456" a cadena, especificando 7 de longitud y 3 decimales: select str(123.456,7,3); select str(-123.456,7,3); retorna '-123.46'; Si no se colocan el segundo y tercer argumeno, la longitud predeterminada es 10 y la cantidad de decimales 0 y se redondea a entero. Ejemplo: se convierte el valor numrico "123.456" a cadena: select str(123.456); retorna '123'; select str(123.456,3); retorna '123';
29
Si el segundo parmetro es menor a la parte entera del nmero, devuelve asteriscos (*). Ejemplo: select str(123.456,2,3); retorna "**". stuff(cadena1,inicio,cantidad,cadena2): inserta la cadena enviada como cuarto argumento, en la posicin indicada en el segundo argumento, reemplazando la cantidad de caracteres indicada por el tercer argumento en la cadena que es primer parmetro. Stuff significa rellenar en ingls. Ejemplo: select stuff('abcde',3,2,'opqrs'); retorna "abopqrse". Es decir, coloca en la posicin 2 la cadena "opqrs" y reemplaza 2 caracteres de la primer cadena. Los argumentos numricos deben ser positivos y menor o igual a la longitud de la primera cadena, caso contrario, retorna "null". Si el tercer argumento es mayor que la primera cadena, se elimina hasta el primer carcter. len(cadena): retorna la longitud de la cadena enviada como argumento. "len" viene de length, que significa longitud en ingls. Ejemplo: select len('Hola'); devuelve 4. char(x): retorna un caracter en cdigo ASCII del entero enviado como argumento. Ejemplo: select char(65); retorna "A". left(cadena,longitud): retorna la cantidad (longitud) de caracteres de la cadena comenzando desde la izquierda, primer caracter. Ejemplo: select left('buenos dias',8); retorna "buenos d". right(cadena,longitud): retorna la cantidad (longitud) de caracteres de la cadena comenzando desde la derecha, ltimo caracter. Ejemplo: select right('buenos dias',8); retorna "nos dias".
30
lower(cadena): retornan la cadena con todos los caracteres en minsculas. lower significa reducir en ingls. Ejemplo: select lower('HOLA ESTUDIAnte'); retorna "hola estudiante".
-upper(cadena): retornan la cadena con todos los caracteres en maysculas. Ejemplo: select upper('HOLA ESTUDIAnte');
-ltrim(cadena): retorna la cadena con los espacios de la izquierda eliminados. Trim significa recortar. Ejemplo: select ltrim(' Hola retorna "Hola ". ');
rtrim(cadena): retorna la cadena con los espacios de la derecha eliminados. Ejemplo: select rtrim(' Hola '); retorna " Hola".
replace(cadena,cadenareemplazo,cadenareemplazar): retorna la cadena con todas las ocurrencias de la subcadena reemplazo por la subcadena a reemplazar. Ejemplo: select replace('xxx.sqlserverya.com','x','w'); retorna "www.sqlserverya.com'.
reverse(cadena): devuelve la cadena invirtiendo el order de los caracteres. Ejemplo: select reverse('Hola'); retorna "aloH".
patindex(patron,cadena): devuelve la posicin de comienzo (de la primera ocurrencia) del patrn especificado en la cadena enviada como segundo argumento. Si no la encuentra retorna 0. Ejemplos: select patindex('%Luis%', 'Jorge Luis Borges'); retorna 7. select patindex('%or%', 'Jorge Luis Borges'); retorna 2. select patindex('%ar%', 'Jorge Luis Borges'); retorna 0.
31
charindex(subcadena,cadena,inicio): devuelve la posicin donde comienza la subcadena en la cadena, comenzando la bsqueda desde la posicin indicada por "inicio". Si el tercer argumento no se coloca, la bsqueda se inicia desde 0. Si no la encuentra, retorna 0. Ejemplos: select charindex('or','Jorge Luis Borges',5); retorna 13. select charindex('or','Jorge Luis Borges'); retorna 2. select charindex('or','Jorge Luis Borges',14); retorna 0. select charindex('or', 'Jorge Luis Borges'); retorna 0.
replicate(cadena,cantidad): repite una cadena la cantidad de veces especificada. Ejemplo: select replicate ('Hola',3); retorna "HolaHolaHola";
space(cantidad): retorna una cadena de espacios de longitud indicada por "cantidad", que debe ser un valor positivo. Ejemplo: select 'Hola'+space(1)+'que tal'; retorna "Hola que tal".
Se pueden emplear estas funciones enviando como argumento el nombre de un campo de tipo caracter.
Funciones matemtica
Las funciones matemticas realizan operaciones con expresiones numricas y retornan un resultado, operan con tipos de datos numricos. Microsoft SQL Server tiene algunas funciones para trabajar con nmeros. Aqu presentamos algunas. abs(x): retorna el valor absoluto del argumento "x". Ejemplo: select abs(-20); retorna 20.
32
ceiling(x): redondea hacia arriba el argumento "x". Ejemplo: select ceiling(12.34); retorna 13.
floor(x): redondea hacia abajo el argumento "x". Ejemplo: select floor(12.34); retorna 12.
%: %: devuelve el resto de una divisin. Ejemplos: select 10%3; retorna 1. select 10%2; retorna 0.
power(x,y): retorna el valor de "x" elevado a la "y" potencia. Ejemplo: select power(2,3); retorna 8.
round(numero,longitud): retorna un nmero redondeado a la longitud especificada. "longitud" debe ser tinyint, smallint o int. Si "longitud" es positivo, el nmero de decimales es redondeado segn "longitud"; si es negativo, el nmero es redondeado desde la parte entera segn el valor de "longitud". Ejemplos: select round(123.456,1); retorna "123.400", es decir, redondea desde el primer decimal. select round(123.456,2); retorna "123.460", es decir, redondea desde el segundo decimal. select round(123.456,-1); retorna "120.000", es decir, redondea desde el primer valor entero (hacia la izquierda). select round(123.456,-2); retorna "100.000", es decir, redondea desde el segundo valor entero (hacia la izquierda).
33
sign(x): si el argumento es un valor positivo devuelve 1;-1 si es negativo y si es 0, 0. square(x): retorna el cuadrado del argumento. Ejemplo: select square(3); retorna 9.
SQL Server dispone de funciones trigonomtricas que retornan radianes. Se pueden emplear estas funciones enviando como argumento el nombre de un campo de tipo numrico.
Los valores para "partedefecha" pueden ser: year (ao), quarter (cuarto), month (mes), day (dia), week (semana), hour (hora), minute (minuto), second (segundo) y millisecond (milisegundo). Ejemplos: select datepart(month,getdate()); retorna el nmero de mes actual; select datepart(day,getdate()); retorna el da actual; select datepart(hour,getdate()); retorna la hora actual;
34
datename(partedefecha,fecha): retorna el nombre de una parte especfica de una fecha. Los valores para "partedefecha" pueden ser los mismos que se explicaron anteriormente. Ejemplos: select datename(month,getdate()); retorna el nombre del mes actual; select datename(day,getdate());
dateadd(partedelafecha,numero,fecha): agrega un intervalo a la fecha especificada, es decir, retorna una fecha adicionando a la fecha enviada como tercer argumento, el intervalo de tiempo indicado por el primer parmetro, tantas veces como lo indica el segundo parmetro. Los valores para el primer argumento pueden ser: year (ao), quarter (cuarto), month (mes), day (dia), week (semana), hour (hora), minute (minuto), second (segundo) y millisecond (milisegundo). Ejemplos: select dateadd(day,3,'1980/11/02'); retorna "1980/11/05", agrega 3 das. select dateadd(month,3,'1980/11/02'); retorna "1981/02/02", agrega 3 meses. select dateadd(hour,2,'1980/11/02'); retorna "1980/02/02 2:00:00", agrega 2 horas. select dateadd(minute,16,'1980/11/02'); retorna "1980/02/02 00:16:00", agrega 16 minutos.
datediff(partedelafecha,fecha1,fecha2): calcula el intervalo de tiempo (segn el primer argumento) entre las 2 fechas. El resultado es un valor entero que corresponde a fecha2fecha1. Los valores de "partedelafecha) pueden ser los mismos que se especificaron anteriormente. Ejemplos: select datediff (day,'2005/10/28','2006/10/28'); retorna 365 (das). select datediff(month,'2005/10/28','2006/11/29'); retorna 13 (meses).
35
Se pueden emplear estas funciones enviando como argumento el nombre de un campo de tipo datetime o smalldatetime.
36
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 seleccin. Se permite ordenar por valores calculados o expresiones. La clusula "order by" no puede emplearse para campos text, ntext e image.
Los operadores lgicos 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 2 condiciones con el operador "and", cumplen con las 2 condiciones.
37
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 condicin, con la segunda condicin o con ambas condiciones. Los registros recuperados con una sentencia que une 2 condiciones con el operador "or", cumplen 1 de las condiciones o ambas. Queremos recuperar los libros que NO cumplan la condicin dada, por ejemplo, aquellos cuya editorial NO sea "Planeta": select *from libros where not editorial='Planeta'; El operador "not" invierte el resultado de la condicin a la cual antecede. Los registros recuperados en una sentencia en la cual aparece el operador "not", no cumplen con la condicin a la cual afecta el "NOT". Los parntesis se usan para encerrar condiciones, para que se evalen como una sola expresin. Cuando explicitamos varias condiciones con diferentes operadores lgicos (combinamos "and", "or") permite establecer el orden de prioridad de la evaluacin; adems permite diferenciar las expresiones ms 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 parntesis no son obligatorios en todos los casos, se recomienda utilizarlos para evitar confusiones. El orden de prioridad de los operadores lgicos es el siguiente: "not" se aplica antes que "and" y "and" antes que "or", si no se especifica un orden de evaluacin mediante el uso de parntesis. El orden en el que se evalan los operadores con igual nivel de precedencia es indefinido, por ello se recomienda usar los parntesis.
38
Entonces, para establecer ms de una condicin en un "where" es necesario emplear operadores lgicos. "and" significa "y", indica que se cumplan ambas condiciones; "or" significa "y/o", indica que se cumpla una u otra condicin (o ambas); "not" significa "no", indica que no se cumpla la condicin especificada.
39
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 mnimo y mximo especificados (20 y 40 respectivamente). "between" significa "entre". Trabaja con intervalo de valores. Este operador se puede emplear con tipos de datos numricos y money (en tales casos incluyen los valores mnimo y mximo) y tipos de datos fecha y hora (incluye slo el valor mnimo). 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 estn fuera del intervalo especificado. Por ejemplo, recuperamos los libros cuyo precio NO se encuentre entre 20 y 35, es decir, los menores a 15 y mayores a 25: select *from libros where precio not between 20 and 35; Siempre que sea posible, emplee condiciones de bsqueda positivas ("between"), evite las negativas ("not between") porque hace ms lenta la recuperacin de los datos. Entonces, se puede usar el operador "between" para reducir las condiciones "where".
40
Para recuperar los libros cuyo autor no sea 'Paenza' ni 'Borges' usbamos: select *from libros where autor<>'Borges' and autor<>'Paenza'; Tambin 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 condicin, invertimos el resultado, es decir, recuperamos los valores que no se encuentran (coindicen) con la lista de valores. Los valores "null" no se consideran. Recuerde: siempre que sea posible, emplee condiciones de bsqueda positivas ("in"), evite las negativas ("not in") porque con ellas se evaln todos los registros y esto hace ms lenta la recuperacin de los datos.
41
Si queremos recuperar todos los libros de "Borges" y especificamos la siguiente condicin: select *from libros where autor='Borges'; slo aparecer el primer registro, ya que la cadena "Borges" no es igual a la cadena "J.L. Borges". Esto sucede porque el operador "=" (igual), tambin 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 smbolo "%" (porcentaje) reemplaza cualquier cantidad de caracteres (incluyendo ningn caracter). Es un caracter comodn. "like" y "not like" son operadores de comparacin que sealan igualdad o diferencia. Para seleccionar todos los libros que comiencen con "M": select *from libros where titulo like 'M%'; Note que el smbolo "%" ya no est al comienzo, con esto indicamos que el ttulo 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 guin bajo "_" reemplaza un caracter, es otro caracter comodn. Por ejemplo, queremos ver los libros de "Lewis Carroll" pero no recordamos si se escribe "Carroll" o "Carrolt", entonces tipeamos esta condicin: select *from libros where autor like "%Carrol_"; Otro caracter comodn es [] reemplaza cualquier carcter contenido en el conjunto especificado dentro de los corchetes.
42
Para seleccionar los libros cuya editorial comienza con las letras entre la "P" y la "S" usamos la siguiente sintaxis: select titulo,autor,editorial from libros where editorial like '[P-S]%'; Ejemplos: ... like '[a-cf-i]%': busca cadenas que comiencen con a,b,c,f,g,h o i; ... like '[-acfi]%': busca cadenas que comiencen con -,a,c,f o i; ... like 'A[_]9%': busca cadenas que comiencen con 'A_9'; ... like 'A[nm]%': busca cadenas que comiencen con 'An' o 'Am'. El cuarto caracter comodn es [^] reemplaza cualquier caracter NO presente en el conjunto especificado dentro de los corchetes. Para seleccionar los libros cuya editorial NO comienza con las letras "P" ni "N" tipeamos: select titulo,autor,editorial from libros where editorial like '[^PN]%'; "like" se emplea con tipos de datos char, nchar, varchar, nvarchar o datetime. Si empleamos "like" con tipos de datos que no son caracteres, SQL Server 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 like '%.00'; Para bsquedas de caracteres comodines como literales, debe incluirlo dentro de corchetes, por ejemplo, si busca: ... like '%[%]%': busca cadenas que contengan el signo '%'; ... like '%[_]%': busca cadenas que contengan el signo '_'; ... like '%[[]%': busca cadenas que contengan el signo '[';
43
44
"count_big(CAMPO)" retorna la cantidad de registros cuyo valor en el campo especificado entre parntesis no es nulo. "count_big(distinct CAMPO)" retorna la cantidad de registros cuyo valor en el campo especificado no es nulo, sin considerar los repetidos. Averiguemos la cantidad de libros usando la funcin "count_big()": select count_big(*) from libros; Note que incluye todos los libros aunque tengan valor nulo en algn campo. Contamos los libros de editorial "Planeta": select count_big(*) from libros where editorial='Planeta'; Contamos los registros que tienen precio (sin tener en cuenta los que tienen valor nulo): select count_big(precio) from libros; Contamos las editoriales (sin repetir): select count_big(distinct editorial) from libros;
45
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: slo en campos de tipo numrico. La funcin "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 mximo o mnimo de un campo usamos las funciones "max()" y "min()" respectivamente. Queremos saber cul es el mayor precio de todos los libros: select max(precio) from libros; Entonces, dentro del parntesis de la funcin colocamos el nombre del campo del cul queremos el mximo valor. La funcin "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 agrupamiento", porque operan sobre conjuntos de registros, no con datos individuales. Tratamiento de los valores nulos: Si realiza una consulta con la funcin "count" de un campo que contiene 18 registros, 2 de los cuales contienen valor nulo, el resultado devuelve un total de 16 filas porque no considera aquellos con valor nulo. Todas las funciones de agregado, excepto "count(*)", excluye los valores nulos de los campos. "count(*)" cuenta todos los registros, incluidos los que contienen "null".
46
47
Tambin se puede agrupar por ms 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 clusula "group by" deben estar en la lista de seleccin. select CAMPO1, CAMPO2, FUNCIONDEAGREGADO from NOMBRETABLA group by CAMPO1,CAMPO2; Para obtener la cantidad libros con precio no nulo, de cada editorial utilizamos la funcin "count()" envindole 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 funcin "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 en dinero de los libros agrupados por editorial: select editorial, sum(precio) from libros group by editorial; Para saber el mximo y mnimo 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;
48
Es posible limitar la consulta con "where". Si incluye una clusula "where", slo 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 condicin, no aparecen en la salida. Para que aparezcan todos los valores de editorial, incluso los que devuelven cero o "null" en la columna de agregado, debemos emplear la palabra clave "all" al lado de "group by": select editorial, count(*) from libros where precio<30 group by all editorial; Entonces, usamos "group by" para organizar registros en grupos y obtener un resumen de dichos grupos. SQL Server produce una columna de valores por cada grupo, devolviendo filas por cada grupo especificado.
49
Se utiliza "having", seguido de la condicin de bsqueda, para seleccionar ciertas filas retornadas por la clusula "group by". Veamos otros ejemplos. 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; En algunos casos es posible confundir las clusulas "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 clusula "where" con la clusula "having"; la primera establece condiciones para la seleccin de registros de un "select"; la segunda establece condiciones para la seleccin 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 condicin dada en "where", luego los agrupa por "editorial" y finalmente rechaza los grupos que no cumplan con la condicin dada en el "having".
50
Se emplea la clusula "having" con funciones de agrupamiento, esto no puede hacerlo la clusula "where". Por ejemplo queremos el promedio de los precios agrupados por editorial, de aquellas editoriales que tienen ms de 2 libros: select editorial, avg(precio) from libros group by editorial having count(*) > 2; En una clusula "having" puede haber hasta 128 condiciones. Cuando utilice varias condiciones, tiene que combinarlas con operadores lgicos (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 clasula "having" para restringir las filas que devuelve una salida "group by". Va siempre despus de la clusula "group by" y antes de la clusula "order by" si la hubiere.
51
Esta consulta muestra el total de visitantes agrupados por ciudad; pero si queremos adems la cantidad total de visitantes, debemos realizar otra consulta: select count(*) as total from visitantes; Para obtener ambos resultados en una sola consulta podemos usar "with rollup" que nos devolver ambas salidas en una sola consulta: select ciudad,count(*) as cantidad from visitantes group by ciudad with rollup; La consulta anterior retorna los registros agrupados por ciudad y una fila extra en la que la primera columna contiene "null" y la columna con la cantidad muestra la cantidad total. La clusula "group by" permite agregar el modificador "with rollup", el cual agrega registros extras al resultado de una consulta, que muestran operaciones de resumen. Si agrupamos por 2 campos, "ciudad" y "sexo": select ciudad,sexo,count(*) as cantidad from visitantes group by ciudad,sexo with rollup; La salida muestra los totales por ciudad y sexo y produce tantas filas extras como valores existen del primer campo por el que se agrupa ("ciudad" en este caso), mostrando los totales para cada valor, con la columna correspondiente al segundo campo por el que se agrupa ("sexo" en este ejemplo) conteniendo "null", y 1 fila extra mostrando el total de todos los visitantes (con las columnas correspondientes a ambos campos conteniendo "null"). Es decir, por cada agrupacin, aparece una fila extra con el/ los campos que no se consideran, seteados a "null". Con "rollup" se puede agrupar hasta por 10 campos. Es posible incluir varias funciones de agrupamiento, por ejemplo, queremos la cantidad de visitantes y la suma de sus compras agrupados por ciudad y sexo: select ciudad,sexo, count(*) as cantidad, sum(montocompra) as total from visitantes group by ciudad,sexo with rollup;
52
Entonces, "rollup" es un modificador para "group by" que agrega filas extras mostrando resultados de resumen de los subgrupos. Si se agrupa por 2 campos SQL Server genera tantas filas extras como valores existen del primer campo (con el segundo campo seteado a "null") y una fila extra con ambos campos conteniendo "null". Con "rollup" se puede emplear "where" y "having", pero no es compatible con "all".
Si se emplea "cube": select sexo,estadocivil,seccion, count(*) from empleados group by sexo,estadocivil,seccion with cube; Retorna ms filas extras adems de las anteriores: sexo y seccion (estadocivil seteado a "null"), estadocivil y seccion (sexo seteado a "null"), seccion (sexo y estadocivil seteados a "null") y estadocivil (sexo y seccion seteados a "null"),
53
Es decir, "cube" genera filas de resumen de subgrupos para todas las combinaciones posibles de los valores de los campos por los que agrupamos. Se pueden colocar hasta 10 campos en el "group by". Con "cube" se puede emplear "where" y "having", pero no es compatible con "all".
Funcin grouping
La funcin "grouping" se emplea con los operadores "rollup" y "cube" para distinguir los valores de detalle y de resumen en el resultado. Es decir, permite diferenciar si los valores "null" que aparecen en el resultado son valores nulos de las tablas o si son una fila generada por los operadores "rollup" o "cube". Con esta funcin aparece una nueva columna en la salida, una por cada "grouping"; retorna el valor 1 para indicar que la fila representa los valores de resumen de "rollup" o "cube" y el valor 0 para representar los valores de campo. Slo se puede emplear la funcin "grouping" en los campos que aparecen en la clusula "group by". Si tenemos una tabla "visitantes" con los siguientes registros almacenados: Nombre sexo ------------------------------Susana Molina f Marcela Mercado f Roberto Perez f null Alberto Garcia m Teresa Garcia f ciudad Cordoba Cordoba Cordoba Alta Gracia
y contamos la cantidad agrupando por ciudad (note que hay un valor nulo en dicho campo) empleando "rollup": select ciudad, count(*) as cantidad from visitantes group by ciudad with rollup;
54
Aparece la siguiente salida: Ciudad cantidad ------------------------NULL 1 Alta Gracia 1 Cordoba 3 NULL 5 La ltima fila es la de resumen generada por "rollup", pero no es posible distinguirla de la primera fila, en la cual "null" es un valor del campo. Para diferenciarla empleamos "grouping": select ciudad, count(*) as cantidad, grouping(ciudad) as resumen from visitantes group by ciudad with rollup; Aparece la siguiente salida: Ciudad cantidad resumen --------------------------------------NULL 1 0 Alta Gracia 1 0 Cordoba 3 0 NULL 5 1 La ltima fila contiene en la columna generada por "grouping" el valor 1, indicando que es la fila de resumen generada por "rollup"; la primera fila, contiene en dicha columna el valor 0, que indica que el valor "null" es un valor del campo "ciudad". Entonces, si emplea los operadores "rollup" y "cube" y los campos por los cuales agrupa admiten valores nulos, utilice la funcin "grouping" para distinguir los valores de detalle y de resumen en el resultado.
55
56
Con "compute by" se DEBE usar tambin la clusula "order by" y los campos que se incluyan luego de "by" deben estar en el "order by". Listando varios campos luego del "by" corta un grupo en subgrupos y aplica la funcin de agregado en cada nivel de agrupamiento: select nombre,ciudad,provincia from visitantes order by provincia compute count(provincia) by provincia; select nombre,ciudad,provincia from visitantes order by provincia,ciudad compute count(provincia) by provincia,ciudad; Los campos que aparecen luego de la clusula "compute by" DEBEN ser idnticos a un subconjunto de los campos que aparecen despus de "order by" y estar en el mismo orden. Si la clusula "order by" tiene los siguientes campos: ... order by a,b,c... la clusula "compute by" puede incluir los siguientes subconjuntos de campos: ... compute ... by a... o ... compute ... by a,b... o ... compute ... by a,b,c...
En una misma instruccin se pueden colocar varias clusulas "compute" combinadas con varias clusulas "compute by": select *from visitantes order by provincia,ciudad compute avg(edad), sum(montocompra) compute avg(montocompra),count(provincia) by provincia,ciudad;
57
El resultado de la consulta anterior muestra el promedio de la compra y la cantidad al final de cada subgrupo de provincia y ciudad (compute by) y el promedio de las edades y el total del monto de compras de todos (compute). Los tipos de datos ntext, text e image no se pueden incluir en una clusula "compute" o "compute by".
58
Podemos combinarla con "where". Por ejemplo, queremos conocer los distintos autores de la editorial "Planeta": select distinct autor from libros where editorial='Planeta'; Tambin puede utilizarse con "group by" para contar los diferentes autores por editorial: select editorial, count(distinct autor) from libros group by editorial; La clusula "distinct" afecta a todos los campos presentados. Para mostrar los ttulos y editoriales de los libros sin repetir ttulos ni editoriales, usamos: select distinct titulo,editorial from libros order by titulo; Note que los registros no estn duplicados, aparecen ttulos iguales pero con editorial diferente, cada registro es diferente. La palabra clave "distinct" no est permitida con las clusulas "compute" y "compute by". Entonces, "distinct" elimina registros duplicados.
Clusula top
La palabra clave "top" se emplea para obtener slo una cantidad limitada de registros, los primeros n registros de una consulta. Con la siguiente consulta obtenemos todos los datos de los primeros 2 libros de la tabla: select top 2 *from libros; Es decir, luego del "select" se coloca "top" seguido de un nmero entero positivo y luego se contina con la consulta. Se puede combinar con "order by": select top 3 titulo,autor from libros order by autor;
59
En la consulta anterior solicitamos los ttulos y autores de los 3 primeros libros, ordenados por autor. Cuando se combina con "order by" es posible emplear tambin la clusula "with ties". Esta clusula permite incluir en la seleccion, todos los registros que tengan el mismo valor del campo por el que se ordena, que el ltimo registro retornado si el ltimo registro retornado (es decir, el nmero n) tiene un valor repetido en el registro n+1. Es decir, si el valor del campo por el cual se ordena del ltimo registro retornado (el nmero n) est repetido en los siguientes registros (es decir, el n+1 tiene el mismo valor que n, y el n+2, etc.), lo incluye en la seleccin. Veamos un ejemplo: select top 3 with ties *from libros order by autor; Esta consulta solicita el retorno de los primeros 3 registros; en caso que el registro nmero 4 (y los posteriores), tengan el mismo valor en "autor" que el ltimo registro retornado (nmero 3), tambin aparecern en la seleccin. Si colocamos un valor para "top" que supera la cantidad de registros de la tabla, SQL Server muestra todos los registros.
60
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 ms de una vez en el da 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 ningn campo, por si slo cumple con la condicin para ser clave, es decir, debe identificar un solo registro, el valor no puede repetirse, debemos usar 2 campos. Definimos una clave compuesta cuando ningn campo por si solo cumple con la condicin para ser clave. En este ejemplo, un auto puede ingresar varias veces en un da a la playa, pero siempre ser a distinta hora. Usamos 2 campos como clave, la patente junto con la hora de llegada, as identificamos unvocamente cada registro. Para establecer ms 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 datetime, horasalida datetime, primary key(patente,horallegada) ); Nombramos los campos que formarn parte de la clave separados por comas. Al ingresar los registros, SQL Server controla que los valores para los campos establecidos como clave primaria no estn repetidos en la tabla; si estuviesen repetidos, muestra un mensaje y la insercin no se realiza. Lo mismo sucede si realizamos una actualizacin. Entonces, si un solo campo no identifica unvocamente un registro podemos definir una clave primaria compuesta, es decir formada por ms de un campo.
61
62
Restriccin default
La restriccin "default" especifica un valor por defecto para un campo cuando no se inserta explcitamente en un comando "insert". Anteriormente, para establecer un valor por defecto para un campo emplebamos la clusula "default" al crear la tabla, por ejemplo: create table libros( ... autor varchar(30) default 'Desconocido', ... ); Cada vez que establecamos un valor por defecto para un campo de una tabla, SQL Server creaba automticamente una restriccin "default" para ese campo de esa tabla. Dicha restriccin, a la cual no le dbamos un nombre, reciba un nombre dado por SQL Server que consiste "DF" (por default), seguido del nombre de la tabla, el nombre del campo y letras y nmeros aleatorios. Podemos agregar una restriccin "default" a una tabla existente con la sintaxis bsica siguiente: alter table NOMBRETABLA add constraint NOMBRECONSTRAINT default VALORPORDEFECTO for CAMPO; En la sentencia siguiente agregamos una restriccin "default" al campo autor de la tabla existente "libros", que almacena el valor "Desconocido" en dicho campo si no ingresamos un valor en un "insert": alter table libros add constraint DF_libros_autor default 'Desconocido' for autor; Por convencin, cuando demos el nombre a las restricciones "default" emplearemos un formato similar al que le da SQL Server: "DF_NOMBRETABLA_NOMBRECAMPO". Solamente se permite una restriccin "default" por campo y no se puede emplear junto con la propiedad "identity". Una tabla puede tener varias restricciones "default" para sus distintos campos.
63
La restriccin "default" acepta valores tomados de funciones del sistema, por ejemplo, podemos establecer que el valor por defecto de un campo de tipo datetime sea "getdate()". Podemos ver informacin referente a las restriciones de una tabla con el procedimiento almacenado "sp_helpcontraint": sp_helpconstraint libros; Aparecen varias columnas con la siguiente informacin: constraint_type: el tipo de restriccin y sobre qu campo est establecida (DEFAULT on column autor), constraint_name: el nombre de la restriccin (DF_libros_autor), delete_action y update_action: no tienen valores para este tipo de restriccin. status_enabled y status_for_replication: no tienen valores para este tipo de restriccin. constraint_keys: el valor por defecto (Desconocido).
Entonces, la restriccin "default" especifica un valor por defecto para un campo cuando no se inserta explcitamente en un "insert", se puede establecer uno por campo y no se puede emplear junto con la propiedad "identity".
Restriccin check
La restriccin "check" especifica los valores que acepta un campo, evitando que se ingresen valores inapropiados. La sintaxis bsica es la siguiente: alter table NOMBRETABLA add constraint NOMBRECONSTRAINT check CONDICION; Trabajamos con la tabla "libros" de una librera 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).
64
Los campos correspondientes a los precios (minorista y mayorista) se definen de tipo decimal(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 restriccin "check": alter table libros add constraint CK_libros_precio_positivo check (preciomin>=0 and preciomay>=0); Este tipo de restriccin verifica los datos cada vez que se ejecuta una sentencia "insert" o "update", es decir, acta en inserciones y actualizaciones. Si la tabla contiene registros que no cumplen con la restriccin que se va a establecer, la restriccin no se puede establecer, hasta que todos los registros cumplan con dicha restriccin. La condicin 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 convencin, 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 fcilmente de qu se trata la restriccin, por si tenemos varias restricciones "check" para el mismo campo. Un campo puede tener varias restricciones restricciones "check" y una restriccin "check" puede incluir varios campos. Las condiciones para restricciones "check" tambin pueden pueden incluir un patrn o una lista de valores. Por ejemplo establecer que cierto campo conste de 4 caracteres, 2 letras y 2 dgitos: ... check (CAMPO like '[A-Z][A-Z][0-9][0-9]'); O establecer que cierto campo asuma slo los valores que se listan: ... check (CAMPO in ('lunes','miercoles','viernes')); No se puede aplicar esta restriccin junto con la propiedad "identity". Si un campo permite valores nulos, "null" es un valor aceptado aunque no est incluido en la condicin de restriccin.
65
Si intentamos establecer una restriccin "check" para un campo que entra en conflicto con otra restriccin "check" establecida al mismo campo, SQL Server no lo permite. Pero si establecemos una restriccin "check" para un campo que entra en conflicto con una restriccin "default" establecida para el mismo campo, SQL Server lo permite; pero al intentar ingresar un registro, aparece un mensaje de error.
66
En el ejemplo anterior deshabilitamos la restriccin "CK_libros_precio" para poder ingresar un valor negativo para "precio". Para habilitar una restriccin deshabilitada se ejecuta la misma instruccin pero con la clusula "check" o "check all": alter table libros check constraint CK_libros_precio; Si se emplea "check constraint all" no se coloca nombre de restricciones, habilita todas las restricciones que tiene la tabla nombrada. Para habilitar o deshabilitar restricciones la comprobacin de datos en inserciones o actualizaciones, la sintaxis bsica es: alter table NOMBRETABLA OPCIONdeRESTRICCION constraint NOMBRERESTRICCION; Para saber si una restriccin est habilitada o no, podemos ejecutar el procedimiento almacenado "sp_helpconstraint" y fijarnos lo que informa la columna "status_enabled". Entonces, las clusulas "check" y "nocheck" permiten habilitar o deshabilitar restricciones "check" (tambin las restricciones "foreign key" que veremos ms adelante), a las dems se las debe eliminar ("default" y las que veremos posteriormente).
67
Cada vez que establecamos la clave primaria para la tabla, SQL Server creaba automticamente una restriccin "primary key" para dicha tabla. Dicha restriccin, a la cual no le dbamos un nombre, reciba un nombre dado por SQL Server que comienza con "PK" (por primary key), seguido del nombre de la tabla y una serie de letras y nmeros aleatorios. Podemos agregar una restriccin "primary key" a una tabla existente con la sintaxis bsica siguiente: alter table NOMBRETABLA add constraint NOMBRECONSTRAINT primary key (CAMPO,...); En el siguiente ejemplo definimos una restriccin "primary key" para nuestra tabla "libros" para asegurarnos que cada libro tendr un cdigo diferente y nico: alter table libros add constraint PK_libros_codigo primary key(codigo); Con esta restriccin, 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 convencin, cuando demos el nombre a las restricciones "primary key" seguiremos el formato "PK_NOMBRETABLA_NOMBRECAMPO". Sabemos que cuando agregamos una restriccin a una tabla que contiene informacin, SQL Server controla los datos existentes para confirmar que cumplen las exigencias de la restriccin, si no los cumple, la restriccin no se aplica y aparece un mensaje de error. Por ejemplo, si intentamos definir la restriccin "primary key" para "libros" y hay registros con cdigos repetidos o con un valor "null", la restriccin no se establece. Cuando establecamos una clave primaria al definir la tabla, automticamente SQL Server redefina el campo como "not null"; pero al agregar una restriccin "primary key", los campos que son clave primaria DEBEN haber sido definidos "not null" (o ser implcitamente "not null" si se definen identity). SQL Server permite definir solamente una restriccin "primary key" por tabla, que asegura la unicidad de cada registro de una tabla. Si ejecutamos el procedimiento almacenado "sp_helpconstraint" junto al nombre de la tabla, podemos ver las restricciones "primary key" (y todos los tipos de restricciones) de dicha tabla. Un campo con una restriccin "primary key" puede tener una restriccin "check".
68
Un campo "primary key" tambin acepta una restriccin "default" (excepto si es identity), 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.
Restriccin unique
Hemos visto que las restricciones aplicadas a tablas aseguran valores nicos para cada registro. Anteriormente aprendimos la restriccin "primary key", otra restriccin para las tablas es "unique". La restriccin "unique" impide la duplicacin 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 nmero de legajo) pero se necesita asegurar que otros datos tambin sean nicos y no se repitan (como nmero 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 restriccin "unique" sobre el campo "documento" de la tabla "alumnos", esto asegura que no se pueda ingresar un documento si ya existe. Esta restriccin permite valores nulos, asi que si se ingresa el valor "null" para el campo "documento", se acepta. Por convencin, 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.
69
Recuerde que cuando agregamos una restriccin a una tabla que contiene informacin, SQL Server controla los datos existentes para confirmar que cumplen la condicin de la restriccin, si no los cumple, la restriccin no se aplica y aparece un mensaje de error. En el caso del ejemplo anterior, si la tabla contiene nmeros de documento duplicados, la restriccin no podr establecerse; si podr establecerse si tiene valores nulos. SQL Server controla la entrada de datos en inserciones y actualizaciones evitando que se ingresen valores duplicados.
70
de: RESTRICCIONES (constraints), que se establecen en tablas y campos y son controlados automticamente por SQL Server. Hay 3 tipos: 1) DE LOS CAMPOS (hace referencia a los valores vlidos para un campo determinado). Pueden ser: a) DEFAULT: especifica un valor por defecto para un campo cuando no se inserta explcitamente en un comando "insert". b) CHECK: especifica un rango de valores que acepta un campo, se emplea en inserciones y actualizaciones ("insert" y "update"). II) DE LA TABLA (asegura un identificador nico para cada registro de una tabla). Hay 2 tipos: a) PRIMARY KEY: identifica unvocamente cada uno de los registros; asegura que no haya valores duplicados ni valores nulos. Se crea un ndice automticamente. b) UNIQUE: impide la duplicacin de claves alternas (no primarias). Se permiten valores nulos. Se crea un ndice automticamente.
71
III) REFERENCIAL: lo veremos ms adelante. REGLAS (rules) y VALORES PREDETERMINADOS (defaults). Veamos las reglas. Las reglas especifican los valores que se pueden ingresar en un campo, asegurando que los datos se encuentren en un intervalo de valores especfico, coincidan con una lista de valores o sigan un patrn. Una regla se asocia a un campo de una tabla (o a un tipo de dato definido por el usuario, tema que veremos posteriormente). Un campo puede tener solamente UNA regla asociado a l. Sintaxis bsica es la siguiente: create rule NOMBREREGLA as @VARIABLE CONDICION Entonces, luego de "create rule" se coloca el nombre de la regla, luego la palabra clave "as" seguido de una variable (a la cual la precede el signo arroba) y finalmente la condicin. Por convencin, nombraremos las reglas comenzando con "RG", el nombre del campo al que se asocia y alguna palabra que haga referencia a la condicin. La variable puede tener cualquier nombre, pero debe estar precedido por el signo arroba (@), dicha variable ser reemplazada por el valor del campo cuando se asocie. La condicin se refiere a los valores permitidos para inserciones y actualizaciones y puede contener cualquier expresin vlida para una clusula "where"; no puede hacer referencia a los campos de una tabla. Creamos una regla para restringir los valores que se pueden ingresar en un campo "sueldo" de una tabla llamada "empleados", estableciendo un intervalo de valores: create rule RG_sueldo_intervalo as @sueldo between 100 and 1000 Luego de crear la regla, debemos asociarla a un campo ejecutando un procedimiento almacenado del sistema empleando la siguiente sintaxis bsica: exec sp_bindrule NOMBREREGLA, 'TABLA.CAMPO';
72
Asociamos la regla creada anteriormente al campo "sueldo" de la tabla "empleados": exec sp_bindrule RG_sueldo_intervalo, 'empleados.sueldo'; Si intentamos agregar (o actualizar) un registro con valor para el campo "sueldo" que no est en el intervalo de valores especificado en la regla, aparece un mensaje de error indicando que hay conflicto con la regla y la insercin (o actualizacin) no se realiza. SQL Server NO controla los datos existentes para confirmar que cumplen con la regla como lo hace al aplicar restricciones; si no los cumple, la regla se asocia igualmente; pero al ejecutar una instruccin "insert" o "update" muestra un mensaje de error, es decir, acta en inserciones y actualizaciones. La regla debe ser compatible con el tipo de datos del campo al cual se asocia; si esto no sucede, SQL Server no lo informa al crear la regla ni al asociarla, pero al ejecutar una instruccin "insert" o "update" muestra un mensaje de error. No se puede crear una regla para campos de tipo text, image, o timestamp. Si asocia una nueva regla a un campo que ya tiene asociada otra regla, la nueva regla reeemplaza la asociacin anterior; pero la primera regla no desaparece, solamente se deshace la asociacin. La sentencia "create rule" no puede combinarse con otras sentencias en un lote. La funcin que cumple una regla es bsicamente la misma que una restriccin "check", las siguientes caractersticas explican algunas diferencias entre ellas: podemos definir varias restricciones "check" sobre un campo, un campo solamente puede tener una regla asociada a l; una restriccin "check" se almacena con la tabla, cuando sta se elimina, las restricciones tambin se borran. Las reglas son objetos diferentes e independientes de las tablas, si eliminamos una tabla, las asociaciones desaparecen, pero las reglas siguen existiendo en la base de datos; una restriccin "check" puede incluir varios campos; una regla puede asociarse a distintos campos (incluso de distintas tablas); una restriccin "check" puede hacer referencia a otros campos de la misma tabla, una regla no.
Un campo puede tener reglas asociadas a l y restricciones "check". Si hay conflicto entre ellas, SQL Server no lo informa al crearlas y/o asociarlas, pero al intentar ingresar un valor que alguna de ellas no permita, aparece un mensaje de error.
73
Con "sp_helpconstraint" podemos ver las reglas asociadas a los campos de una tabla. Con "sp_help" podemos ver todos los objetos de la base de datos activa, incluyendo las reglas, en tal caso en la columna "Object_type" aparece "rule".
74
Para ver el texto de una regla empleamos el procedimiento almacenado "sp_helptext" seguido del nombre de la regla: sp_helptext NOMBREREGLA; Tambin se puede consultar la tabla del sistema "sysobjects", que nos muestra el nombre y varios datos de todos los objetos de la base de datos actual. La columna "xtype" indica el tipo de objeto, en caso de ser una regla aparece el valor "R": select *from sysobjects; Si queremos ver todas las reglas creadas por nosotros, podemos tipear: select *from sysobjects where xtype='R' and-- tipo regla name like 'RG%';--bsqueda con comodn
75
76
La funcin que cumple un valor predeterminado es bsicamente la misma que una restriccin "default", las siguientes caractersticas explican algunas semejanzas y diferencias entre ellas: un campo solamente puede tener definida UNA restriccin "default", un campo solamente puede tener UN valor predeterminado asociado a l, una restriccin "default" se almacena con la tabla, cuando sta se elimina, las restricciones tambin. Los valores predeterminados son objetos diferentes e independientes de las tablas, si eliminamos una tabla, las asociaciones desaparecen, pero los valores predeterminados siguen existiendo en la base de datos. una restriccin "default" se establece para un solo campo; un valor predeterminado puede asociarse a distintos campos (inclusive, de diferentes tablas). una restriccin "default" no puede establecerse sobre un campo "identity", tampoco un valor predeterminado.
No se puede asociar un valor predeterminado a un campo que tiene una restriccin "default". Un campo con un valor predeterminado asociado puede tener reglas asociadas a l y restricciones "check". Si hay conflicto entre ellas, SQL Server no lo informa al crearlas y/o asociarlas, pero al intentar ingresar un valor que alguna de ellas no permita, aparece un mensaje de error. La sentencia "create default" no puede combinarse con otra sentencia en un mismo lote. Si asocia a un campo que ya tiene asociado un valor predeterminado otro valor predeterminado, la nueva asociacin reemplaza a la anterior. Veamos otros ejemplos. Creamos un valor predeterminado que inserta el valor "0" en un campo de tipo numrico: create default VP_cero as 0; En el siguiente creamos un valor predeterminado que inserta ceros con el formato vlido para un nmero de telfono: create default VP_telefono as '(0000)0-000000'; Con "sp_helpconstraint" podemos ver los valores predeterminados asociados a los campos de una tabla. Con "sp_help" podemos ver todos los objetos de la base de datos activa, incluyendo los valores predeterminados, en tal caso en la columna "Object_type" aparece "default".
77
78
"sp_helpconstraint" retorna una lista de todas las restricciones que tiene una tabla. Tambin los valores predeterminados asociados; muestra la siguiente informacin: constraint_type: indica que es un valor predeterminado con "DEFAULT", nombrando el campo al que est asociado. constraint_name: nombre del valor predeterminado. constraint_keys: muestra el texto del valor predeterminado.
Con "sp_helptext" seguido del nombre de un valor predeterminado podemos ver el texto de cualquier valor predeterminado: sp_helptext NOMBREVALORPREDETERMINADO; Tambin se puede consultar la tabla del sistema "sysobjects", que nos muestra el nombre y varios datos de todos los objetos de la base de datos actual. La columna "xtype" indica el tipo de objeto, en caso de ser un valor predeterminado aparece el valor "D": select *from sysobjects; Si queremos ver todos los valores predeterminados creados por nosotros, podemos tipear: select *from sysobjects where xtype='D' and-- tipo valor predeterminado name like 'VP%';--bsqueda con comodn
ndices
SQL Server 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. 2. empleando ndices; recorriendo la estructura de rbol del ndice para localizar los registros y extrayendo los que cumplen las condiciones de la consulta. Los ndices se emplean para facilitar la obtencin de informacin de una tabla. El indice de una tabla desempea la misma funcin que el ndice de un libro: permite encontrar datos rpidamente; en el caso de las tablas, localiza registros. Una tabla se indexa por un campo (o varios). Un ndice posibilita el acceso directo y rpido haciendo ms eficiente las bsquedas. Sin ndice, SQL Server debe recorrer secuencialmente toda la tabla para encontrar un registro. El objetivo de un indice es acelerar la recuperacin de informacin. La indexacin es una tcnica que optimiza el acceso a los datos, mejora el rendimiento acelerando las consultas y otras
79
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 ms adelante). La desventaja es que consume espacio en el disco en disco y genera costo de mantenimiento (tiempo y recursos). Los ndices ms adecuados son aquellos creados con campos que contienen valores nicos. Es importante identificar el o los campos por los que sera til crear un ndice, aquellos campos por los cuales se realizan bsqueda con frecuencia: claves primarias, claves externas o campos que combinan tablas. No se recomienda crear ndices por campos que no se usan con frecuencia en consultas o no contienen valores nicos. SQL Server permite crear dos tipos de ndices: 1) agrupados y 2) no agrupados.
80
La diferencia bsica entre ndices agrupados y no agrupados es que los registros de un ndice agrupado estn ordenados y almacenados de forma secuencial en funcin de su clave. SQL Server crea automaticamente ndices cuando se crea una restriccin "primary key" o "unique" en una tabla. Es posible crear ndices en las vistas. Resumiendo, los ndices facilitan la recuperacin de datos, permitiendo el acceso directo y acelerando las bsquedas, consultas y otras operaciones que optimizan el rendimiento general.
Creacin de ndices
Para crear ndices empleamos la instruccin "create index". La sintaxis bsica es la siguiente: create TIPODEINDICE index NOMBREINDICE on TABLA(CAMPO); "TIPODEINDICE" indica si es agrupado (clustered) o no agrupado (nonclustered). Si no especificamos crea uno No agrupado. Independientemente de si es agrupado o no, tambin se puede especificar que sea "unique", es decir, no haya valores repetidos. Si se intenta crear un ndice unique para un campo que tiene valores duplicados, SQL Server no lo permite. En este ejemplo se crea un ndice agrupado nico para el campo "codigo" de la tabla "libros": create unique clustered index I_libros_codigo on libros(codigo); Para identificar los ndices fcilmente, podemos agregar un prefijo al nombre del ndice, por ejemplo "I" y luego el nombre de la tabla y/o campo. En este ejemplo se crea un ndice no agrupado para el campo "titulo" de la tabla "libros": create nonclustered index I_libros_titulo on libros(titulo); Un ndice puede tener ms de un campo como clave, son ndices compuestos. Los campos de un ndice compuesto tienen que ser de la misma tabla (excepto cuando se crea en una vista - tema que veremos posteriormente). Creamos un ndice compuesto para el campo "autor" y "editorial": create index I_libros_autoreditorial
81
on libros(autor,editorial); SQL Server crea automticamente ndices cuando se establece una restriccin "primary key" o "unique" en una tabla. Al crear una restriccin "primary key", si no se especifica, el ndice ser agrupado (clustered) a menos que ya exista un ndice agrupado para dicha tabla. Al crear una restriccin "unique", si no se especifica, el ndice ser no agrupado (non-clustered). Ahora podemos entender el resultado del procedimiento almacenado "sp_helpconstraint" cuando en la columna "constraint_type" mostraba el tipo de ndice seguido de las palabras "clustered" o "non_clustered". Puede especificarse que un ndice sea agrupado o no agrupado al agregar estas restricciones. Agregamos una restriccin "primary key" al campo "codigo" de la tabla "libros" especificando que cree un ndice NO agrupado: alter table libros add constraint PK_libros_codigo primary key nonclustered (codigo); Para ver los indices de una tabla: sp_helpindex libros; Muestra el nombre del ndice, si es agrupado (o no), primary (o unique) y el campo por el cual se indexa. Todos los ndices de la base de datos activa se almacenan en la tabla del sistema "sysindexes", podemos consultar dicha tabla tipeando: select name from sysindexes; Para ver todos los ndices de la base de datos activa creados por nosotros podemos tipear la siguiente consulta: select name from sysindexes where name like 'I_%';
82
Regenerar ndices
Vimos que para crear ndices empleamos la instruccin "create index". Empleando la opcin "drop_existing" junto con "create index" permite regenerar un ndice, con ello evitamos eliminarlo y volver a crearlo. La sintaxis es la siguiente: create TIPODEINDICE index NOMBREINDICE on TABLA(CAMPO) with drop_existing; Tambin podemos modificar alguna de las caractersticas de un ndice con esta opcin, a saber: - tipo: cambindolo de no agrupado a agrupado (siempre que no exista uno agrupado para la misma tabla). No se puede convertir un ndice agrupado en No agrupado. - campo: se puede cambiar el campo por el cual se indexa, agregar campos, eliminar algn campo de un ndice compuesto. - nico: se puede modificar un ndice para que los valores sean nicos o dejen de serlo. En este ejemplo se crea un ndice no agrupado para el campo "titulo" de la tabla "libros": create nonclustered index I_libros on libros(titulo); Regeneramos el ndice "I_libros" y lo convertimos a agrupado: create clustered index I_libros on libros(titulo) with drop_existing; Agregamos un campo al ndice "I_libros": create clustered index I_libros on libros(titulo,editorial) with drop_existing; Esta opcin no puede emplearse con ndices creados a partir de una restriccin "primary key" o "unique".
83
Eliminar ndices
Los ndices creados con "create index" se eliminan con "drop index"; la siguiente es la sintaxis bsica: drop index NOMBRETABLA.NOMBREINDICE; Eliminamos el ndice "I_libros_titulo": drop index libros.I_libros_titulo; Los ndices que SQL Server crea automticamente al establecer una restriccin "primary key" o "unique" no pueden eliminarse con "drop index", se eliminan automticamente cuando quitamos la restriccin. Podemos averiguar si existe un ndice para eliminarlo, consultando la tabla del sistema "sysindexes": if exists (select name from sysindexes where name = 'NOMBREINDICE') drop index NOMBRETABLA.NOMBREINDICE; Eliminamos el ndice "I_libros_titulo" si existe: if exists (select *from sysindexes where name = 'I_libros_titulo') drop index libros.I_libros_titulo;
84
Veamos: create table libros( codigo int identity, titulo varchar(40) not null, autor varchar(30) not null default 'Desconocido', codigoeditorial tinyint not null, precio decimal(5,2), primary key (codigo) ); create table editoriales( codigo tinyint identity, nombre varchar(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 cdigo de la editorial en la tabla "libros" y en "editoriales". Al recuperar los datos de los libros con la siguiente instruccin: select* from libros; vemos que en el campo "editorial" aparece el cdigo, 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 informacin de las dos. Cuando obtenemos informacin de ms de una tabla decimos que hacemos un "join" (combinacin). Veamos un ejemplo: select *from libros join editoriales on libros.codigoeditorial=editoriales.codigo; Resumiendo: si distribuimos la informacin en varias tablas evitamos la redundancia de datos y ocupamos menos espacio fsico en el disco. Un join es una operacin que relaciona dos o ms tablas para obtener un resultado que incluya datos (campos y registros) de ambas; las tablas participantes se combinan segn los campos comunes a ambas tablas. Hay hay tres tipos de combinaciones. En los siguientes captulos explicamos cada una de ellas.
85
86
el campo "codigoeditorial" de "libros" y el campo "codigo" de "editoriales" son los que enlazarn ambas tablas. Se emplean campos comunes, que deben tener tipos de datos iguales o similares. La condicion de combinacin, es decir, el o los campos por los que se van a combinar (parte "on"), se especifica segn las claves primarias y externas. Note que en la consulta, al nombrar el campo usamos el nombre de la tabla tambin. 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", SQL Server 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 clusula "on" se debe hacer referencia a la clave completa, es decir, la condicin referenciar a todos los campos clave que identifican al registro. Se puede incluir en la consulta join la clusula "where" para restringir los registros que retorna el resultado; tambin "order by", "distinct", etc.. Se emplea este tipo de combinacin para encontrar registros de la primera tabla que se correspondan con los registros de la otra, es decir, que cumplan la condicin del "on". Si un valor de la primera tabla no se encuentra en la segunda tabla, el registro no aparece. Para simplificar la sentencia podemos usar un alias para cada tabla: select l.codigo,titulo,autor,nombre from libros as l join editoriales as e on l.codigoeditorial=e.codigo; En algunos casos (como en este ejemplo) el uso de alias es para fines de simplificacin y hace ms legible la consulta si es larga y compleja, pero en algunas consultas es absolutamente necesario.
87
88
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 bsica es la siguiente: select CAMPOS from TABLAIZQUIERDA left join TABLADERECHA on CONDICION; En el siguiente ejemplo solicitamos el ttulo 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 as l left join editoriales as e on codigoeditorial = e.codigo; El resultado mostrar el ttulo del libro y el nombre de la editorial; los ttulos cuyo cdigo 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 cdigo est presente en "libros": select titulo,nombre from editoriales as e left join libros as l on e.codigo=codigoeditorial where codigoeditorial is not null; Tambin podemos mostrar las editoriales que NO estn presentes en "libros", es decir, que NO encuentran coincidencia en la tabla de la derecha: select titulo,nombre from editoriales as e left join libros as l on e.codigo=codigoeditorial where codigoeditorial is null;
89
90
on CONDICION; Un "right join" tambin puede tener clusula "where" que restringa el resultado de la consulta considerando solamente los registros que encuentran coincidencia en la tabla izquierda: select titulo,nombre from libros as l right join editoriales as e on e.codigo=codigoeditorial where codigoeditorial is not null; Mostramos las editoriales que NO estn presentes en "libros", es decir, que NO encuentran coincidencia en la tabla de la derecha empleando un "right join": select titulo,nombre from libros as l rightjoin editoriales as e on e.codigo=codigoeditorial where codigoeditorial is null;
91
92
Autocombinacin
Dijimos que es posible combinar una tabla consigo misma. Un pequeo 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 combinacin de platos empleando un "cross join" con una sola tabla: select c1.nombre as 'plato principal', c2.nombre as postre, c1.precio+c2.precio as total from comidas as c1 cross join comidas as c2; En la consulta anterior aparecen 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 as c1 cross join comidas as 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 autocombinacin 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". Tambin se puede realizar una autocombinacin con "join": select c1.nombre as 'plato principal', c2.nombre as postre, c1.precio+c2.precio as total from comidas as c1 join comidas as c2 on c1.codigo<>c2.codigo where c1.rubro='plato' and c2.rubro='postre'; Para que no aparezcan filas duplicadas se agrega un "where".
93
94
Para recuperar todos los datos de los libros empleamos la siguiente consulta: select titulo,a.nombre,e.nombre from autores as a join libros as l on codigoautor=a.codigo join editoriales as 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 combinarn; luego debemos hacer coincidir los valores para el enlace con la tabla "editoriales" enlazndolas por los campos correspondientes. Utilizamos alias para una sentencia ms 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. Note que no aparecen los libros cuyo cdigo de autor no se encuentra en "autores" y cuya editorial no existe en "editoriales", esto es porque realizamos una combinacin interna. Podemos combinar varios tipos de join en una misma sentencia: select titulo,a.nombre,e.nombre from autores as a right join libros as l on codigoautor=a.codigo left join editoriales as e on codigoeditorial=e.codigo; En la consulta anterior solicitamos el ttulo, 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 informacin 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"; recuerde que la tabla resultado del primer join es la que se combina con el segundo join, no la segunda tabla nombrada. En el ejemplo anterior, el "left join" no se realiza entre las tablas "libros" y "editoriales" sino entre el resultado del "right join" y la tabla "editoriales".
95
Clave fornea
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 fornea, externa o ajena. En el ejemplo de la librera 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 fornea, se emplea para enlazar la tabla "libros" con "editoriales" y es clave primaria en "editoriales" con el nombre "codigo". Las claves forneas 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.
96
Cuando alteramos una tabla, debemos tener cuidado con las claves forneas. Si modificamos el tipo, longitud o atributos de una clave fornea, sta puede quedar inhabilitada para hacer los enlaces. Entonces, una clave fornea 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.
97
- luego de "foreign key", entre parntesis se coloca el campo de la tabla a la que le aplicamos la restriccin que ser establecida como clave fornea, - 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 fornea. La tabla referenciada debe tener definida una restriccin "primary key" o "unique"; si no la tiene, aparece un mensaje de error. Para agregar una restriccin "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 restriccin "foreign key" para asegurarnos que el cdigo de la editorial de la de la tabla "libros" ("codigoeditorial") est asociada con un cdigo vlido en la tabla "editoriales" ("codigo"). Cuando agregamos cualquier restriccin a una tabla que contiene informacin, SQL Server controla los datos existentes para confirmar que cumplen con la restriccin, si no los cumple, la restriccin no se aplica y aparece un mensaje de error. Por ejemplo, si intentamos agregar una restriccin "foreign key" a la tabla "libros" y existe un libro con un valor de cdigo para editorial que no existe en la tabla "editoriales", la restriccin no se agrega. Acta en inserciones. Si intentamos ingresar un registro (un libro) con un valor de clave fornea (codigoeditorial) que no existe en la tabla referenciada (editoriales), SQL server muestra un mensaje de error. Si al ingresar un registro (un libro), no colocamos el valor para el campo clave fornea (codigoeditorial), almacenar "null", porque esta restriccin permite valores nulos (a menos que se haya especificado lo contrario al definir el campo). Acta en eliminaciones y actualizaciones. Si intentamos eliminar un registro o modificar un valor de clave primaria de una tabla si una clave fornea hace referencia a dicho registro, SQL Server no lo permite (excepto si se permite la accin 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 restriccin (a diferencia de "primary key" y "unique") no crea ndice automaticamente. 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 clusula "references". Esta restriccin se puede definir dentro de la misma tabla (lo veremos ms adelante) o entre distintas tablas.
98
Una tabla puede tener varias restricciones "foreign key". No se puede eliminar una tabla referenciada en una restriccin "foreign key", aparece un mensaje de error. Una restriccion "foreign key" no puede modificarse, debe eliminarse y volverse a crear. Para ver informacin acerca de esta restriccin podemos ejecutar el procedimiento almacenado "sp_helpconstraint" junto al nombre de la tabla. Nos muestra el tipo, nombre, la opcin para eliminaciones y actualizaciones, el estado (temas que veremos ms adelante), el nombre del campo y la tabla y campo que referencia. Tambin informa si la tabla es referenciada por una clave fornea.
99
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 restriccin, cada vez que se ingrese un valor en el campo "afiliadotitular", SQL Server controlar que dicho nmero 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 accin en cascada (prximo tema).
100
La sintaxis completa para agregar esta restriccin a una tabla es la siguiente: alter table TABLA1 add constraint NOMBRERESTRICCION foreign key (CAMPOCLAVEFORANEA) references TABLA2(CAMPOCLAVEPRIMARIA) on delete OPCION on update OPCION; Sintetizando, si al agregar una restriccin foreign key: - no se especifica accin para eliminaciones (o se especifica "no_action"), y se intenta eliminar un registro de la tabla referenciada (editoriales) cuyo valor de clave primaria (codigo) existe en la tabla principal (libros), la accin 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 eliminacin 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). - no se especifica accin para actualizaciones (o se especifica "no_action"), y se intenta modificar un valor de clave primaria (codigo) de la tabla referenciada (editoriales) que existe en el campo clave fornea (codigoeditorial) de la tabla principal (libros), la accin no se realiza. - se especifica "cascade" para actualizaciones ("on update cascade") y se modifica un valor de clave primaria (codigo) de la tabla referenciada (editoriales) que existe en la tabla principal (libros), SQL Server actualiza el registro de la tabla referenciada (editoriales) y todos los registros coincidentes en la tabla principal (libros). Veamos un ejemplo. Definimos una restriccin "foreign key" a la tabla "libros" estableciendo el campo "codigoeditorial" como clave fornea que referencia al campo "codigo" de la tabla "editoriales". La tabla "editoriales" tiene como clave primaria el campo "codigo". Especificamos la accin en cascada para las actualizaciones y eliminaciones: alter table libros add constraint FK_libros_codigoeditorial foreign key (codigoeditorial) references editoriales(codigo) on update cascade on delete cascade; Si luego de establecer la restriccin anterior, eliminamos una editorial de "editoriales" de las cuales hay libros, se elimina dicha editorial y todos los libros de tal editorial. Y si modificamos el valor de cdigo de una editorial de "editoriales", se modifica en "editoriales" y todos los valores iguales de "codigoeditorial" de libros tambin se modifican.
101
102
En el siguiente ejemplo deshabilitamos la restriccin creada anteriormente: alter table libros nocheck constraint FK_libros_codigoeditorial; Para habilitar una restriccin deshabilitada se ejecuta la misma instruccin pero con la clusula "check" o "check all": alter table libros check constraint FK_libros_codigoeditorial; Si se emplea "check constraint all" no se coloca nombre de restricciones, habilita todas las restricciones que tiene la tabla nombrada ("check" y "foreign key"). Para saber si una restriccin est habilitada o no, podemos ejecutar el procedimiento almacenado "sp_helpconstraint" y entenderemos lo que informa la columna "status_enabled". Entonces, las clusulas "check" y "nocheck" permiten habilitar o deshabilitar restricciones "foreign key" (y "check"). Pueden emplearse para evitar la comprobacin de datos existentes al crear la restriccin o para deshabilitar la comprobacin de datos al ingresar, actualizar y eliminar algn registro que infrinja la restriccin. Podemos eliminar una restriccin "foreign key" con "alter table". La sintaxis bsica es la misma que para cualquier otra restriccin: alter table TABLA drop constraint NOMBRERESTRICCION; Eliminamos la restriccin de "libros": alter table libros drop constraint FK_libros_codigoeditorial; No se puede eliminar una tabla si una restriccin "foreign key" hace referencia a ella. Cuando eliminamos una tabla que tiene una restriccin "foreign key", la restriccin tambin se elimina.
103
104
105
- una restriccin "foreign key" para establecer el campo "codigoautor" como clave externa que haga referencia al campo "codigo" de "autores" y permita actualizaciones en cascada y no eliminaciones; - una restriccin "check" para el campo "precio" que no admita valores negativos; Si definimos una restriccin "foreign key" al crear una tabla, la tabla referenciada debe existir.
Unin
El operador "union" combina el resultado de dos o ms instrucciones "select" en un nico resultado. Se usa 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 seleccin de cada consulta. No se incluyen las filas duplicadas en el resultado, a menos que coloque la opcin "all". Se deben especificar los nombres de los campos en la primera instruccin "select". Puede emplear la clusula "order by". Puede dividir una consulta compleja en varias consultas "select" y luego emplear el operador "union" para combinarlas. Una academia de enseanza almacena los datos de los alumnos en una tabla llamada "alumnos" y los datos de los profesores en otra denominada "profesores". La academia necesita el nombre y domicilio de profesores y alumnos para enviarles una tarjeta de invitacin. Para obtener los datos necesarios de ambas tablas en una sola consulta necesitamos realizar una unin: select nombre, domicilio from alumnos union select nombre, domicilio from profesores; El primer "select" devuelve el nombre y domicilio de todos los alumnos; el segundo, el nombre y domicilio de todos los profesores. Los encabezados del resultado de una unin son los que se especifican en el primer "select".
106
107
Podemos eliminar varios campos en una sola sentencia: alter table libros drop column editorial,edicion;
108
Campos calculados
Un campo calculado es un campo que no se almacena fsicamente en la tabla. SQL Server emplea una frmula que detalla el usuario al definir dicho campo para calcular el valor segn otros campos de la misma tabla. Un campo calculado no puede: - definirse como "not null". - ser una subconsulta. - tener restriccin "default" o "foreign key". - insertarse ni actualizarse.
109
Puede ser empleado como llave de un ndice o parte de restricciones "primary key" o "unique" si la expresin que la define no cambia en cada consulta. Creamos un campo calculado denominado "sueldototal" que suma al sueldo bsico de cada empleado la cantidad abonada por los hijos (100 por cada hijo): create table empleados( documento char(8), nombre varchar(10), domicilio varchar(30), sueldobasico decimal(6,2), cantidadhijos tinyint default 0, sueldototal as sueldobasico + (cantidadhijos*100) ); Tambin se puede agregar un campo calculado a una tabla existente: alter table NOMBRETABLA add NOMBRECAMPOCALCULADO as EXPRESION; alter table empleados add sueldototal as sueldo+(cantidadhijos*100); Los campos de los cuales depende el campo calculado no pueden eliminarse, se debe eliminar primero el campo calculado.
110
Para crear un tipo de datos definido por el usuario se emplea el procedimiento almacenado del sistema "sp_addtype". Sintaxis bsica: exec sp_addtype NOMBRENUEVOTIPO, 'TIPODEDATODELSISTEMA', 'OPCIONNULL'; Creamos un tipo de datos definido por el usuario llamado "tipo_documento" que admite valores nulos: exec sp_addtype tipo_documento, 'char(8)', 'null'; Ejecutando el procedimiento almacenado "sp_help" junto al nombre del tipo de dato definido por el usuario se obtiene informacin del mismo (nombre, el tipo de dato en que se basa, la longitud, si acepta valores nulos, si tiene valor por defecto y reglas asociadas). Tambin podemos consultar la tabla "systypes" en la cual se almacena informacin de todos los tipos de datos: select name from systypes;
111
Para quitar la asociacin, empleamos el mismo procedimiento almacenado que aprendimos cuando quitamos asociaciones a campos, ejecutamos el procedimiento almacenado "sp_unbindrule" seguido del nombre del tipo de dato al que est asociada la regla: exec sp_unbindrule 'TIPODEDATODEFINIDOPORELUSUARIO'; Si asocia una regla a un campo cuyo tipo de dato definido por el usuario ya tiene una regla asociada, la nueva regla se aplica al campo, pero el tipo de dato contina asociado a la regla. La regla asociada al campo prevalece sobre la asociada al tipo de dato. Por ejemplo, tenemos un campo "precio" de un tipo de dato definido por el usuario "tipo_precio", este tipo de dato tiene asociada una regla "RG_precio0a99" (precio entre 0 y 99), luego asociamos al campo "precio" la regla "RG_precio100a500" (precio entre 100 y 500); al ejecutar una instruccin "insert" admitir valores entre 100 y 500, es decir, tendr en cuenta la regla asociada al campo, aunque vaya contra la regla asociada al tipo de dato. Un tipo de dato definido por el usuario puede tener una sola regla asociada. Cuando obtenemos informacin del tipo da dato definido por el usuario ejecutando "sp_help", en la columna "rule_name" se muestra el nombre de la regla asociada a dicho tipo de dato; muestran "none" cuando no tiene regla asociada.
112
sp_unbindefault 'TIPODEDATODEFINIDOPORELUSUARIO'; Debe tener en cuenta que NO se puede aplicar una restriccin "default" en un campo con un tipo de datos definido por el usuario si dicho campo o tipo de dato tienen asociado un valor predeterminado. Si un campo de un tipo de dato definido por el usuario tiene una restriccin "default" y luego se asocia un valor predeterminado al tipo de dato, el valor predeterminado no queda asociado en el campo que tiene la restriccin "default". Un tipo de dato definido por el usuario puede tener un solo valor predeterminado asociado. Cuando obtenemos informacin del tipo da dato definido por el usuario ejecutando "sp_help", en la columna "default_name" se muestra el nombre del valor predeterminado asociado a dicho tipo de dato; muestra "none" cuando no tiene ningn valor predeterminado asociado.
113
No se puede eliminar un tipo de datos definido por el usuario si alguna tabla (u otro objeto) hace uso de l; por ejemplo, si una tabla tiene un campo definido con tal tipo de dato. Si eliminamos un tipo de datos definido por el usuario, desaparecen las asociaciones de las reglas y valores predeterminados, pero tales reglas y valores predeterminados, no se eliminan, siguen existiendo en la base de datos.
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 lgicos y se obtiene el resultado con una nica instruccin y cuando la consulta depende de los resultados de otra consulta. Generalmente, una subconsulta se puede reemplazar por combinaciones y estas ltimas son ms eficientes. Las subconsultas se DEBEN incluir entre parntesis. Puede haber subconsultas dentro de subconsultas, se admiten hasta 32 niveles de anidacin. Se pueden emplear subconsultas: - en lugar de una expresin, 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 combinacin (join). Hay tres tipos bsicos de subconsultas: 1. las que retornan un solo valor escalar que se utiliza con un operador de comparacin o en lugar de una expresin. 2. las que retornan una lista de valores, se combinan con "in", o los operadores "any", "some" y "all". 3. los que testean la existencia con "exists". Reglas a tener en cuenta al emplear subconsultas: - la lista de seleccin de una subconsulta que va luego de un operador de comparacin puede incluir slo una expresin o campo (excepto si se emplea "exists" y "in").
114
- si el "where" de la consulta exterior incluye un campo, este debe ser compatible con el campo en la lista de seleccin de la subconsulta. - no se pueden emplear subconsultas que recuperen campos de tipos text o image. - las subconsultas luego de un operador de comparacin (que no es seguido por "any" o "all") no pueden incluir clusulas "group by" ni "having". - "distinct" no puede usarse con subconsultas que incluyan "group by". - no pueden emplearse las clusulas "compute" y "compute by". - "order by" puede emplearse solamente si se especifica "top" tambin. - una vista creada con una subconsulta no puede actualizarse. - una subconsulta puede estar anidada dentro del "where" o "having" de una consulta externa o dentro de otra subconsulta. - si una tabla se nombra solamente en un subconsulta y no en la consulta externa, los campos no sern incluidos en la salida (en la lista de seleccin de la consulta externa).
115
select titulo,precio, precio-(select max(precio) from libros) as diferencia from libros where titulo='Uno'; En el ejemplo anterior se muestra el ttulo, el precio de un libro y la diferencia entre el precio del libro y el mximo valor de precio. Queremos saber el ttulo, autor y precio del libro ms 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 expresin de la subconsulta. Se pueden emplear en "select", "insert", "update" y "delete". Para actualizar un registro empleando subconsulta la sintaxis bsica es la siguiente: update TABLA set CAMPO=NUEVOVALOR where CAMPO= (SUBCONSULTA); Para eliminar registros empleando subconsulta empleamos la siguiente sintaxis bsica: delete from TABLA where CAMPO=(SUBCONSULTA); Recuerde que la lista de seleccin de una subconsulta que va luego de un operador de comparacin puede incluir slo una expresin o campo (excepto si se emplea "exists" o "in"). No olvide que las subconsultas luego de un operador de comparacin (que no es seguido por "any" o "all") no pueden incluir clusulas "group by".
116
Subconsultas con in
Vimos que una subconsulta puede reemplazar una expresin. 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 expresin en una clusula "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. La sintaxis bsica 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 (codigo) que la consulta exterior luego emplea al recuperar los datos. Podemos reemplazar por un "join" la consulta anterior: select distinct nombre from editoriales as e join libros on codigoeditorial=e.codigo where autor='Richard Bach'; Una combinacin (join) siempre puede ser expresada como una subconsulta; pero una subconsulta no siempre puede reemplazarse por una combinacin que retorne el mismo resultado. Si es posible, es aconsejable emplear combinaciones en lugar de subconsultas, son ms eficientes. Se recomienda probar las subconsultas antes de incluirlas en una consulta exterior, as puede verificar que retorna lo necesario, porque a veces resulta difcil verlo en consultas anidadas. Tambin 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 especfico: select nombre from editoriales where codigo not in (select codigoeditorial from libros where autor='Richard Bach');
117
118
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 as e join libros as 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 ttulos. Veamos otro ejemplo con un operador de comparacin 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 condicin, 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');
119
El precio de cada libro de "Borges" es comparado con cada valor de la lista de valores retornada por la subconsulta; si cumple la condicin, 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". Recuerde que solamente las subconsultas luego de un operador de comparacin al cual es seguido por "any" o "all") pueden incluir clusulas "group by".
Subconsultas correlacionadas
Un almacn almacena la informacin de sus ventas en una tabla llamada "facturas" en la cual guarda el nmero 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 artculo, el precio (unitario) y la cantidad. Se necesita una lista de todas las facturas que incluya el nmero, la fecha, el cliente, la cantidad de artculos comprados y el total: select f.*, (select count(d.numeroitem) from Detalles as d where f.numero=d.numerofactura) as cantidad, (select sum(d.preciounitario*cantidad) from Detalles as d where f.numero=d.numerofactura) as total from facturas as f; El segundo "select" retorna una lista de valores de una sola columna con la cantidad de items por factura (el nmero 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 nmero 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 evala 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, especficamente, 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 SQL Server repite la evaluacin.
120
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", SQL Server analiza si hay datos que coinciden con la subconsulta, no se devuelve ningn registro, es como un test de existencia; SQL Server termina la recuperacin de registros cuando por lo menos un registro cumple la condicin "where" de la subconsulta. La sintaxis bsica es la siguiente: ... where exists (SUBCONSULTA); En este ejemplo se usa una subconsulta correlacionada con un operador "exists" en la clusula "where" para devolver una lista de clientes que compraron el artculo "lapiz": select cliente,numero from facturas as f where exists (select *from Detalles as d where f.numero=d.numerofactura and d.articulo='lapiz'); Puede obtener el mismo resultado empleando una combinacin. Podemos buscar los clientes que no han adquirido el artculo "lapiz" empleando "if not exists": select cliente,numero from facturas as f where not exists (select *from Detalles as d where f.numero=d.numerofactura and d.articulo='lapiz');
121
122
La subconsulta retorna un solo valor. Buscamos los libros cuyo precio supere el precio promedio de los libros por editorial: select l1.titulo,l1.editorial,l1.precio from libros as l1 where l1.precio > (select avg(l2.precio) from libros as l2 where l1.editorial= l2.editorial); Por cada valor de l1, se evala la subconsulta, si el precio es mayor que el promedio.
123
select td.numero,c.nombre,td.total from clientes as c join (select f.*, (select sum(d.precio*cantidad) from Detalles as d where f.numero=d.numerofactura) as total from facturas as f) as td on td.codigocliente=c.codigo; La consulta anterior retorna, de la tabla derivada (referenciada con "td") el nmero de factura y el monto total, y de la tabla "clientes", el nombre del cliente. Note que este "join" no emplea 2 tablas, sino una tabla propiamente dicha y una tabla derivada, que es en realidad una subconsulta.
124
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 as e join libros on codigoeditorial=e.codigo where autor='Juan Perez'); La subconsulta es una combinacin que retorna una lista de valores que la consulta externa emplea al seleccionar los registros para la eliminacin.
Subconsulta (insert)
Aprendimos que una subconsulta puede estar dentro de un "select", "update" y "delete"; tambin puede estar dentro de un "insert". Podemos ingresar registros en una tabla empleando un "select". La sintaxis bsica 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 cargarn (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".
125
126
Go
Esto solo se aplica cuando instale el SQL Server en su mquina. "go" es un signo de finalizacin de un lote de sentencias. No es una sentencia, es un comando. El lote de sentencias est compuesto por todas las sentencias antes de "go" o todas las sentencias entre dos "go". Las sentencias no deben ocupar la misma linea en la que est "go". Habr notado que no se puede ejecutar un procedimiento almacenado luego de otras sentencias a menos que se incluya "execute" (o "exec"). Por ejemplo, si tipeamos: select *from empleados; sp_helpconstraint empleados; muestra un mensaje de error porque no puede procesar ambas sentencias como un solo lote. Para que no ocurra debemos tipear: select *from empleados; exec sp_helpconstraint empleados; o separar los lotes con "go": select *from empleados; go sp_helpconstraint empleados; Las siguientes sentencias no pueden ejecutarse en el mismo lote: create rule, create default,create view, create procedure, create trigger. Cada una de ellas necesita ejecutarse separndolas con "go". Por ejemplo: create table.... go create rule... go Recuerde que si coloca "go" no debe incluir el "punto y coma" (;) al finalizar una instruccin. No est de ms recordar que esto solo se aplica cuando instale el SQL Server en su mquina y ejecute los comandos desde el Query Analyzer.
127
Vistas
Una vista es una alternativa para mostrar datos de varias tablas. Una vista es como una tabla virtual que almacena una consulta. Los datos accesibles a travs de la vista no estn almacenados en la base de datos como un objeto. 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 tambin tabla virtual porque los resultados que retorna y la manera de referenciarlas es la misma que para una tabla. Las vistas permiten: - ocultar informacin: permitiendo el acceso a algunos datos y manteniendo oculto el resto de la informacin que no se incluye en la vista. El usuario opera con los datos de una vista como si se tratara de una tabla, pudiendo modificar tales datos. - simplificar la administracin de los permisos de usuario: se pueden dar al usuario permisos para que solamente pueda acceder a los datos a travs 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 informacin de varias tablas. Podemos crear vistas con: un subconjunto de registros y campos de una tabla; una unin de varias tablas; una combinacin de varias tablas; un resumen estadstico de una tabla; un subconjunto de otra vista, combinacin de vistas y tablas. Una vista se define usando un "select". La sintaxis bsica parcial para crear una vista es la siguiente: create view NOMBREVISTA as SENTENCIASSELECT from TABLA; El contenido de una vista se muestra con un "select": select *from NOMBREVISTA;
128
En el siguiente ejemplo creamos la vista "vista_empleados", que es resultado de una combinacin 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 as e join secciones as s on codigo=seccion Para ver la informacin 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 convencin 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 hubisemos hecho aparecera un mensaje de error porque dicha expresin DEBE tener un encabezado, SQL Server 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 haba un encabezado (el alias de la concatenacin) llamado "nombre" y no pueden repetirse, si sucediera, aparecera un mensaje de error. Otra sintaxis es la siguiente: create view NOMBREVISTA (NOMBRESDEENCABEZADOS) as SENTENCIASSELECT from TABLA;
129
Creamos otra vista de "empleados" denominada "vista_empleados_ingreso" que almacena la cantidad de empleados por ao: create view vista_empleados_ingreso (fecha,cantidad) as select datepart(year,fechaingreso),count(*) from empleados group by datepart(year,fechaingreso) La diferencia es que se colocan entre parntesis los encabezados de las columnas que aparecern 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 habra que agregar) colocados en el "select" que define la vista. Los nombres que se colocan entre parntesis 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, SQL Server verifica que existan las tablas a las que se hacen referencia en ella. 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. Existen algunas restricciones para el uso de "create view", a saber: - no puede incluir las clusulas "compute" ni "compute by" ni la palabra clave "into"; - no se pueden crear vistas temporales ni crear vistas sobre tablas temporales. - no se pueden asociar reglas ni valores por defecto a las vistas. - no puede combinarse con otras instrucciones en un mismo lote. Se pueden construir vistas sobre otras vistas.
130
Vistas (informacin)
Las vistas son objetos, as que para obtener informacin de ellos pueden usarse los siguientes procedimientos almacenados del sistema: "sp_help" sin parmetros nos muestra todos los objetos de la base de datos seleccionada, incluidas las vistas. En la columna "Object_type" aparece "view" si es una vista. Si le enviamos como argumento el nombre de una vista, obtenemos la fecha de creacin, propietario, los campos y dems informacin. "sp_helptext" seguido del nombre de una vista nos muestra el texto que la define, excepto si ha sido encriptado. Ejecutando "sp_depends" seguido del nombre de un objeto, obtenemos 2 resultados: - nombre, tipo, campos, etc. de los objetos de los cuales depende el objeto nombrado y - nombre y tipo de los objetos que dependen del objeto nombrado. Si ejecutamos el procedimiento "sp_depends" seguido del nombre de una vista: sp_depends vista_empleados; aparecen las tablas (y dems objetos) de las cuales depende la vista, es decir, las tablas referenciadas en la misma. Si ejecutamos el procedimiento seguido del nombre de una tabla: sp_depends empleados; aparecen los objetos que dependen de la tabla, vistas, restricciones, etc. Tambin se puede consultar la tabla del sistema "sysobjects": select *from sysobjects; Nos muestra nombre y varios datos de todos los objetos de la base de datos actual. La columna "xtype" indica el tipo de objeto, si es una vista, aparece 'V'. Si queremos ver todas las vistas creadas por nosotros, podemos tipear: select *from sysobjects where xtype='V' and-- tipo vista name like 'vista%';--bsqueda con comodn
131
vistas (encriptar)
Podemos ver el texto que define una vista ejecutando el procedimiento almacenado del sistema "sp_helptext" seguido del nombre de la vista: sp_helptext NOMBREVISTA; Podemos ocultar el texto que define una vista empleando la siguiente sintaxis al crearla: create view NOMBREVISTA with encryption as SENTENCIASSELECT from TABLA; "with encryption" indica a SQL Server que codifique las sentencias que definen la vista. Creamos una vista con su definicin oculta: create view vista_empleados with encryption as select (apellido+' '+e.nombre) as nombre,sexo, s.nombre as seccion, cantidadhijos from empleados as e join secciones as s on codigo=seccion Si ejecutamos el procedimiento almacenado del sistema "sp_helptext" seguido del nombre de una vista encriptada, aparece un mensaje indicando tal situacin y el texto no se muestra.
Vistas (eliminar)
Para quitar una vista se emplea "drop view": drop view NOMBREVISTA; Si se elimina una tabla a la que hace referencia una vista, la vista no se elimina, hay que eliminarla explcitamente. Solo el propietario puede eliminar una vista.
132
Antes de eliminar un objeto, se recomienda ejecutar el procedimiento almacenado de sistema "sp_depends" para averiguar si hay objetos que hagan referencia a l. Eliminamos la vista denominada "vista_empleados": drop view vista_empleados;
133
- no se pueden cambiar los campos resultado de un clculo. - 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 estn incluidos en la vista. - la opcin "with check option" obliga a todas las instrucciones de modificacin que se ejecutan en la vista a cumplir ciertos criterios que se especifican al definir la vista. - para eliminar datos de una vista solamente UNA tabla puede ser listada en el "from" de la definicion de la misma.
134
135
end from alumnos; Note que cada "where" compara un valor puntual, por ello los valores devueltos son iguales para algunos casos. Note que como omitimos la parte "else", en caso que el valor no encuentre coincidencia con ninguno valor "when", retorna "null". Podemos realizar comparaciones en cada "where". La sintaxis es la siguiente: case when VALORACOMPARAR OPERADOR VALOR1 then RESULTADO1 when VALORACOMPARAR OPERADOR VALOR2 then RESULTADO2 ... else RESULTADO3 end Mostramos los nombres de los alumnos y en una columna extra llamada "resultado" empleamos un case que teste si la nota es menor a 4, est entre 4 y 7 o supera el 7: select nombre, nota, condicion= case when nota<4 then 'libre' when nota >=4 and nota<7 then 'regular' when nota>=7 then 'promocionado' else 'sin nota' end from alumnos; Puede utilizar una expresin "case" en cualquier lugar en el que pueda utilizar una expresin. Tambin se puede emplear con "group by" y funciones de agrupamiento.
136
- "while": ejecuta repetidamente una instruccin siempre que la condicin sea verdadera. - "break" y "continue": controlan la operacin de las instrucciones incluidas en el bucle "while". Veamos un ejemplo. Tenemos nuestra tabla "libros"; queremos mostrar todos los ttulos de los cuales no hay libros disponibles (cantidad=0), si no hay, mostrar un mensaje indicando tal situacin: if exists (select *from libros where cantidad=0) (select titulo from libros where cantidad=0) else select 'No hay libros sin stock'; SQL Server ejecuta la sentencia (en este caso, una subconsulta) luego del "if" si la condicin es verdadera; si es falsa, ejecuta la sentencia del "else" (si existe). Podemos emplear "if...else" en actualizaciones. Por ejemplo, queremos hacer un descuento en el precio, del 10% a todos los libros de una determinada editorial; si no hay, mostrar un mensaje: if exists (select *from libros where editorial='Emece') begin update libros set precio=precio-(precio*0.1) where editorial='Emece' select 'libros actualizados' end else select 'no hay registros actualizados'; Note que si la condicin es verdadera, se deben ejecutar 2 sentencias. Por lo tanto, se deben encerrar en un bloque "begin...end". En el siguiente ejemplo eliminamos los libros cuya cantidad es cero; si no hay, mostramos un mensaje: if exists (select *from libros where cantidad=0) delete from libros where cantidad=0 else select 'No hay registros eliminados;
137
Variables de usuario
Las variables nos permiten almacenar un valor y recuperarlo ms adelante para emplearlos en otras sentencias. Las variables de usuario son especficas de cada conexin y son liberadas automticamente al abandonar la conexin. Las variables de usuario comienzan con "@" (arroba) seguido del nombre (sin espacios), dicho nombre puede contener cualquier caracter. Una variable debe ser declarada antes de usarse. Una variable local se declara as: declare @NOMBREVARIABLE TIPO colocando "declare" el nombre de la variable que comienza con el smbolo arroba (@) y el tipo de dato. Ejemplo: declare @nombre varchar(20) Puede declarar varias variables en una misma sentencia: declare @nombre varchar(20), @edad int No existen variables globales en SQL Server. Una variable declarada existe dentro del entorno en que se declara; debemos declarar y emplear la variable en el mismo lote de sentencias, porque si declaramos una variable y luego, en otro bloque de sentencias pretendemos emplearla, dicha variable ya no existe. Por ejemplo, si ejecutamos estas sentencias en diferentes lotes: declare @variable varchar(10); select @variable; aparece un mensaje indicando que la variable "@variable" debe ser declarada. Debemos tipear: declare @variable varchar(10) select @variable; Disponemos punto y coma solo al final de la ltima instruccin del lote. Una variable a la cual no se le ha asignado un valor contiene "null".
138
Se le asigna un valor inicial con "set": set @edad=45 Para almacenar un valor en una variable se coloca el signo igual (=) entre la variable y el valor a asignar. Si le asignamos un valor resultado de una consulta, la sintaxis es: select @nombre = autor from libros where titulo='Uno' Podemos ver el contenido de una variable con: select @nombre; Una variable puede tener comodines: declare @patron varchar(30) set @patron='B%' select autor from libros where autor like @patron; La utilidad de las variables consiste en que almacenan valores para utilizarlos en otras consultas. Por ejemplo, queremos saber todos los datos del libro con mayor precio de la tabla "libros" de una librera. Para ello podemos emplear una variable para almacenar el precio ms alto: declare @mayorprecio select @mayorprecio:=max(precio) from libros y luego mostrar todos los datos de dicho libro empleando la variable anterior: select *from libros where precio=@mayorprecio; Es decir, declaramos la variable y guardamos en ella el precio ms alto y luego, en otra sentencia, mostramos los datos de todos los libros cuyo precio es igual al valor de la variable. Una variable puede ser definida con cualquier tipo de dato, excepto text, ntext e image; incluso de un tipo de dato definido por el usuario.
139
140
141
En el siguiente ejemplo, declaramos una variable de tipo "varbinary" a la cual le asignamos el valor del puntero a texto de un registro y luego vemos si dicho puntero es vlido, empleando la variable: declare @puntero varbinary(16) select @puntero = textptr(sinopsis) from libros where titulo= 'Ilusiones' select textvalid('libros.sinopsis', @puntero); Solo disponemos punto y coma al final para que SQL Server ejecute todas las instrucciones en un solo lote y exista la variable @puntero. Si al insertar registros se ingresa un valor "null" en un campo "text", "ntext" o "image" o no se ingresa valor, no se crea un puntero vlido. Para crear un puntero a texto vlido ejecute un "insert" o "update" con datos que no sean nulos para el campo text, ntext o image.
142
Si al insertar registros se ingresa un valor "null" en un campo "text", "ntext" o "image" o no se ingresan datos, no se crea un puntero vlido y al intentar leer dicho campo ocurre un error, porque la funcin "readtext" requiere un puntero vlido. Para evitarlo podemos chequear el puntero antes de pasrselo a la funcin de lectura: declare @puntero varbinary(16) select @puntero=textptr(sinopsis) from libros where codigo=1 if (textvalid('libros.sinopsis', @puntero)=1) readtext libros.sinopsis @puntero 9 50 else select 'puntero invalido';
143
declare @puntero varbinary(16) select @puntero=textptr(sinopsis) from libros where codigo=1 if (textvalid('libros.sinopsis', @puntero)=1) writetext libros.sinopsis @puntero 'Trata de una gaviota que vuela ms alto que las demas.' else select 'puntero invalido, no se actualiz el registro';
144
- DATOAINSERTAR: el dato que va a ser insertado en el campo. Puede ser char, nchar, varchar, nvarchar, binary, varbinary, text, ntext, image, un literal o una variable. Si el dato es un campo text, ntext o image de otra tabla, se debe indicar el nombre de la tabla junto con el campo y el valor del puntero que apunta al tipo de dato text, ntext o image (retornado por la funcin "textptr"), de esta forma: TABLA.CAMPO PUNTERO; Tenemos la tabla libros, con un campo de tipo text llamado "sinopsis"; hay un registro cargado con el siguiente texto: "Para aprender PHP a paso." Necesitamos agregar antes de "a paso" el texto "paso " para que el texto completo sea "Para aprender PHP paso a paso", tipeamos: declare @puntero binary(16) select @puntero = textptr(sinopsis) from libros where titulo='Aprenda PHP' updatetext libros.sinopsis @puntero 18 0 'paso '; Entonces, declaramos una variable llamada "@puntero"; guardamos en la variable el valor del puntero, obtenido con la funcin "textptr(sinopsis)", tal puntero apunta al campo "sinopsis" del libro "Aprenda PHP". Luego actualizamos el campo, colocando el puntero en la posicin 18, no borramos ningn byte y colocamos el texto a agregar; el campo ahora contendr "Para aprencer PHP paso a paso". Es posible guardar en un campo "text" de una tabla el contenido del campo "text" de otra tabla; para ello debemos utilizar 2 punteros, uno para obtener la direccin del campo que queremos actualizar y otro para obtener la direccin del campo del cual extraemos la informacin. En el siguiente ejemplo guardamos en una variable el valor del puntero a texto al campo "sinopsis" del libro "Aprenda PHP" de la tabla "libros"; en otra variable guardamos el valor del puntero a texto al campo "sinopsis" del libro con cdigo 1 de la tabla "ofertas"; finalmente actualizamos el registro de "ofertas" con el texto de "libros". declare @puntero1 binary(16) select @puntero1 = textptr(sinopsis) from libros where titulo='Aprenda PHP' declare @puntero2 binary(16) select @puntero2 = textptr(sinopsis) from ofertas where titulo='Aprenda PHP' updatetext ofertas.sinopsis @puntero2 0 null libros.sinopsis @puntero1;
145
Entonces, se emplea "updatetext" para modificar datos de campos de tipo text, ntext e image, pudiendo cambiar una porcin del texto.
Procedimientos almacenados
Vimos que SQL Server ofrece dos alternativas para asegurar la integridad de datos, la integridad: 1) DECLARATIVA, mediante el uso de restricciones (constraints), valores predeterminados (defaults) y reglas (rules) y 2) PROCEDIMENTAL, mediante la implementacin de procedimientos almacenados y desencadenadores (triggers).
146
Nos detendremos ahora en procedimientos almacenados. Un procedimiento almacenado es un conjunto de instrucciones a las que se les da un nombre, que se almacena en el servidor. Permiten encapsular tareas repetitivas. SQL Server permite los siguientes tipos de procedimientos almacenados: 1) del sistema: estn almacenados en la base de datos "master" y llevan el prefijo "sp_"; permiten recuperar informacin de las tablas del sistema y pueden ejecutarse en cualquier base de datos. 2) locales: los crea el usuario (prximo tema). 3) temporales: pueden ser locales, cuyos nombres comienzan con un signo numeral (#), o globales, cuyos nombres comienzan con 2 signos numeral (##). Los procedimientos almacenados temporales locales estn disponibles en la sesin de un solo usuario y se eliminan automticamente al finalizar la sesin; los globales estn disponibles en las sesiones de todos los usuarios. 4) extendidos: se implementan como bibliotecas de vnculos dinmicos (DLL, Dynamic-Link Libraries), se ejecutan fuera del entorno de SQL Server. Generalmente llevan el prefijo "xp_". No los estudiaremos. Al crear un procedimiento almacenado, las instrucciones que contiene se analizan para verificar si son correctas sintcticamente. Si no se detectan errores, SQL Server guarda el nombre del procedimiento almacenado en la tabla del sistema "sysobjects" y su contenido en la tabla del sistema "syscomments" en la base de datos activa. Si se encuentra algn error, no se crea. 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. Ventajas: - comparten la lgica de la aplicacin 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 trfico de red; en vez de enviar muchas instrucciones, los usuarios realizan operaciones enviando una nica instruccin, lo cual disminuye el nmero de solicitudes entre el cliente y el servidor.
147
148
"create procedure" debe ser la primera sentencia de un lote. Para ejecutar el procedimiento almacenado creado anteriormente tipeamos: exec pa_libros_limite_stock; Entonces, para ejecutar un procedimiento almacenado colocamos "execute" (o "exec") seguido del nombre del procedimiento. Cuando realizamos un ejercicio nuevo, siempre realizamos las mismas tareas: eliminamos la tabla si existe, la creamos y luego ingresamos algunos registros. Podemos crear un procedimiento almacenado que contenga todas estas instrucciones: create procedure pa_crear_libros as if object_id('libros')is not null drop table libros; create table libros( codigo int identity, titulo varchar(40), autor varchar(30), editorial varchar(20), precio decimal(5,2), primary key(codigo) ); 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); Y luego lo ejecutamos cada vez que comenzamos un nuevo ejercicio y as evitamos tipear tantas sentencias: exec pa_crear_libros;
149
150
create proc NOMBREPROCEDIMIENTO @NOMBREPARAMETRO TIPO =VALORPORDEFECTO as SENTENCIAS; Los parmetros se definen luego del nombre del procedimiento, comenzando el nombre con un signo arroba (@). Los parmetros son locales al procedimiento, es decir, existen solamente dentro del mismo. Pueden declararse varios parmetros por procedimiento, se separan por comas. Cuando el procedimiento es ejecutado, deben explicitarse valores para cada uno de los parmetros (en el orden que fueron definidos), a menos que se haya definido un valor por defecto, en tal caso, pueden omitirse. Pueden ser de cualquier tipo de dato (excepto cursor). Luego de definir un parmetro 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 parmetros. Si no se coloca valor por defecto, un procedimiento definido con parmetros no puede ejecutarse sin valores para ellos. El valor por defecto puede ser "null" o una constante, tambin puede incluir comodines si el procedimiento emplea "like". Creamos un procedimiento que recibe el nombre de un autor como parmetro para mostrar todos los libros del autor solicitado: create procedure pa_libros_autor @autor varchar(30) as select titulo, editorial,precio from libros where autor= @autor; El procedimiento se ejecuta colocando "execute" (o "exec") seguido del nombre del procedimiento y un valor para el parmetro: exec pa_libros_autor 'Borges'; Creamos un procedimiento que recibe 2 parmetros, el nombre de un autor y el de una editorial: create procedure pa_libros_autor_editorial @autor varchar(30), @editorial varchar(20) as select titulo, precio from libros where autor= @autor and editorial=@editorial;
151
El procedimiento se ejecuta colocando "execute" (o "exec") seguido del nombre del procedimiento y los valores para los parmetros separados por comas: exec pa_libros_autor_editorial 'Richard Bach','Planeta'; Los valores de un parmetro pueden pasarse al procedimiento mediante el nombre del parmetro o por su posicin. La sintaxis anterior ejecuta el procedimiento pasando valores a los parmetros por posicin. Tambin podemos emplear la otra sintaxis en la cual pasamos valores a los parmetros por su nombre: exec pa_libros_autor_editorial @editorial='Planeta', @autor='Richard Bach'; Cuando pasamos valores con el nombre del parmetro, el orden en que se colocan puede alterarse. No podramos ejecutar el procedimiento anterior sin valores para los parmetros. Si queremos ejecutar un procedimiento que permita omitir los valores para los parmetros debemos, al crear el procedimiento, definir valores por defecto para cada parmetro: create procedure pa_libros_autor_editorial2 @autor varchar(30)='Richard Bach', @editorial varchar(20)='Planeta' as select titulo, autor,editorial,precio from libros where autor= @autor and editorial=@editorial; Podemos ejecutar el procedimiento anterior sin enviarle valores, usar los predeterminados. Si enviamos un solo parmetro a un procedimiento que tiene definido ms de un parmetro sin especificar a qu parmetro corresponde (valor por posicin), asume que es el primero. Es decir, SQL Server asume que los valores se dan en el orden que fueron definidos, no se puede interrumpir la secuencia. Si queremos especificar solamente el segundo parmetro, debemos emplear la sintaxis de paso de valores a parmetros por nombre: exec pa_libros_autor_editorial2 @editorial='Paidos'; Podemos emplear patrones de bsqueda en la consulta que define el procedimiento almacenado y utilizar comodines como valores por defecto: create proc pa_libros_autor_editorial3 @autor varchar(30) = '%', @editorial varchar(30) = '%'
152
as select titulo,autor,editorial,precio from libros where autor like @autor and editorial like @editorial; La sentencia siguiente ejecuta el procedimiento almacenado "pa_libros_autor_editorial3" enviando un valor por posicin, se asume que es el primero. exec pa_libros_autor_editorial3 'P%'; La sentencia siguiente ejecuta el procedimiento almacenado "pa_libros_autor_editorial3" enviando un valor para el segundo parmetro, para el primer parmetro toma el valor por defecto: exec pa_libros_autor_editorial3 @editorial='P%'; Tambin podramos haber tipeado: exec pa_libros_autor_editorial3 default, 'P%';
153
Al ejecutarlo tambin debe emplearse "output": declare @variable decimal(4,2) execute pa_promedio 5,6, @variable output select @variable; Declaramos una variable para guardar el valor devuelto por el procedimiento; ejecutamos el procedimiento envindole 2 valores y mostramos el resultado. La instruccin que realiza la llamada al procedimiento debe contener un nombre de variable para almacenar el valor retornado. Creamos un procedimiento almacenado que muestre los ttulos, editorial y precio de los libros de un determinado autor (enviado como parmetro de entrada) y nos retorne la suma y el promedio de los precios de todos los libros del autor enviado: create procedure pa_autor_sumaypromedio @autor varchar(30)='%', @suma decimal(6,2) output, @promedio decimal(6,2) output as select titulo,editorial,precio from libros where autor like @autor select @suma=sum(precio) from libros where autor like @autor select @promedio=avg(precio) from libros where autor like @autor; Ejecutamos el procedimiento y vemos el contenido de las variables en las que almacenamos los parmetros de salida del procedimiento: declare @s decimal(6,2), @p decimal(6,2) execute pa_autor_sumaypromedio 'Richard Bach', @s output, @p output select @s as total, @p as promedio;
154
La instruccin "return" sale de una consulta o procedimiento y todas las instrucciones posteriores no son ejecutadas. Creamos un procedimiento que muestre todos los libros de un autor determinado que se ingresa como parmetro. Si no se ingresa un valor, o se ingresa "null", se muestra un mensaje y se sale del procedimiento: create procedure pa_libros_autor @autor varchar(30)=null as if @autor is null begin select 'Debe indicar un autor' return end; select titulo from libros where autor = @autor; Si al ejecutar el procedimiento enviamos el valor "null" o no pasamos valor, con lo cual toma el valor por defecto "null", se muestra un mensaje y se sale; en caso contrario, ejecuta la consulta luego del "else". "return" puede retornar un valor entero. Un procedimiento puede retornar un valor de estado para indicar si se ha ejecutado correctamente o no. Creamos un procedimiento almacenado que ingresa registros en la tabla "libros". Los parmetros correspondientes al ttulo y autor DEBEN ingresarse con un valor distinto de "null", los dems son opcionales. El procedimiento retorna "1" si la insercin se realiza, es decir, si se ingresan valores para ttulo y autor y "0", en caso que ttulo o autor sean nulos: create procedure pa_libros_ingreso @titulo varchar(40)=null, @autor varchar(30)=null, @editorial varchar(20)=null, @precio decimal(5,2)=null as if (@titulo is null) or (@autor is null) return 0 else begin insert into libros values (@titulo,@autor,@editorial,@precio) return 1 end;
155
Para ver el resultado, debemos declarar una variable en la cual se almacene el valor devuelto por el procedimiento; luego, ejecutar el procedimiento asignndole el valor devuelto a la variable, finalmente mostramos el contenido de la variable: declare @retorno int exec @retorno=pa_libros_ingreso 'Alicia en el pais...','Lewis Carroll' select 'Ingreso realizado=1' = @retorno exec @retorno=pa_libros_ingreso select 'Ingreso realizado=1' = @retorno; Tambin podramos emplear un "if" para controlar el valor de la variable de retorno: declare @retorno int; exec @retorno=pa_libros_ingreso 'El gato con botas','Annimo' if @retorno=1 print 'Registro ingresado' else select 'Registro no ingresado porque faltan datos';
156
Aparecen las tablas (y dems objetos) de las cuales depende el procedimiento, es decir, las tablas referenciadas en el mismo. Podemos ejecutar el procedimiento seguido del nombre de una tabla: sp_depends libros; aparecen los procedimientos (y dems objetos) que dependen de ella. - La tabla del sistema "sysobjects": muestra nombre y varios datos de todos los objetos de la base de datos actual. La columna "xtype" indica el tipo de objeto. Si es un procedimiento almacenado, muestra "P". Ejemplo: select *from sysobjects; Si queremos ver todos los procedimientos almacenados creados por nosotros, podemos tipear: select *from sysobjects where xtype='P' and-- tipo procedimiento name like 'pa%';--bsqueda con comodn
157
158
La siguiente instruccin ingresa en la tabla "ofertas" el resultado del procedimiento "pa_ofertas": insert into ofertas exec pa_ofertas; Las tablas deben existir y los tipos de datos deben coincidir.
159
160
Tablas temporales
Las tablas temporales son visibles solamente en la sesin actual. Las tablas temporales se eliminan automticamente al acabar la sesin o la funcin o procedimiento almacenado en el cual fueron definidas. Se pueden eliminar con "drop table". Pueden ser locales (son visibles slo en la sesin actual) o globales (visibles por todas las sesiones). Para crear tablas temporales locales se emplea la misma sintaxis que para crear cualquier tabla, excepto que se coloca un signo numeral (#) precediendo el nombre. create table #NOMBRE( CAMPO DEFINICION, ... ); Para referenciarla en otras consultas, se debe incluir el numeral(#), que es parte del nombre. Por ejemplo: insert into #libros default values; select *from #libros; Una tabla temporal no puede tener una restriccin "foreign key" ni ser indexada, tampoco puede ser referenciada por una vista.
161
Para crear tablas temporales globales se emplea la misma sintaxis que para crear cualquier tabla, excepto que se coloca un signo numeral doble (##) precediendo el nombre. create table ##NOMBRE( CAMPO DEFINICION, ... ); El (o los) numerales son parte del nombre. As que puede crearse una tabla permanente llamada "libros", otra tabla temporal local llamada "#libros" y una tercera tabla temporal global denominada "##libros". No podemos consultar la tabla "sysobjects" para ver las tablas temporales, debemos tipear: select *from tempdb..sysobjects;
Funciones
SQL Server ofrece varios tipos de funciones para realizar distintas operaciones. Hemos visto y empleado varias de ellas. Se pueden emplear las funciones del sistema en cualquier lugar en el que se permita una expresin en una sentencia "select". Las funciones pueden clasificarse en: - deterministicas: siempre retornan el mismo resultado si se las invoca enviando el mismo valor de entrada. Todas las funciones de agregado y string son deterministicas, excepto "charindex" y "patindex". - no deterministicas: pueden retornar distintos resultados cada vez que se invocan con el mismo valor de entrada. Las siguientes son algunas de las funciones no deterministicas: getdate, datename, textptr, textvalid, rand. Todas las funciones de configuracin, cursor, meta data, seguridad y estadsticas del sistema son no deterministicas. SQL Server provee muchas funciones y adems permite que el usuario pueda definir sus propias funciones. Sabemos que una funcin es un conjunto de sentencias que operan como una unidad lgica, una rutina que retorna un valor. Una funcin tiene un nombre, acepta parmetros de entrada y retorna un valor escalar o una tabla. Los parmetros de entrada pueden ser de cualquier tipo, excepto timestamp, cursor y table. Las funciones definidas por el usuario no permiten parmetros de salida.
162
No todas las sentencias SQL son vlidas dentro de una funcin. NO es posible emplear en ellas funciones no determinadas (como getdate()) ni sentencias de modificacin o actualizacin de tablas o vistas. Si podemos emplear sentencias de asignacin, de control de flujo (if), de modificacin y eliminacin de variables locales. SQL Server admite 3 tipos de funciones definidas por el usuario clasificadas segn el valor retornado: 1) escalares: retornan un valor escalar; 2) de tabla de varias instrucciones (retornan una tabla) y 3) de tabla en lnea (retornan una tabla). Las funciones definidas por el usuario se crean con la instruccin "create function" y se eliminan con "drop function".
Funciones (drop)
Las funciones definidas por el usuario se eliminan con la instruccin "drop function": Sintaxis: drop function NOMBREPPROPIETARIO.NOMBREFUNCION; Se coloca el nombre del propietario seguido del nombre de la funcin. Si la funcin que se intenta eliminar no existe, aparece un mensaje indicndolo, para evitarlo, podemos verificar su existencia antes de solicitar su eliminacin (como con cualquier otro objeto): if object_id('NOMBREPROPIETARIO.NOMBREFUNCION') is not null drop function NOMBREPROPIETARIO.NOMBREFUNCION; Eliminamos, si existe, la funcin denominada "f_fechacadena": if object_id('dbo.f_fechacadena') is not null drop function dbo.f_fechacadena;
163
164
Al hacer referencia a una funcin escalar, se debe especificar el propietario y el nombre de la funcin: select dbo.f_promedio(5.5,8.5); Cuando llamamos a funciones que tienen definidos parmetros de entrada DEBEMOS suministrar SIEMPRE un valor para l. Si llamamos a la funcin anterior sin enviarle los valores para los parmetros: select dbo.f_promedio(); SQL Server muestra un mensaje de error indicando que necesita argumentos. Creamos una funcin a la cual le enviamos una fecha y nos retorna el nombre del mes en espaol: create function f_nombreMes (@fecha datetime='2007/01/01') returns varchar(10) as begin declare @nombre varchar(10) set @nombre= case datename(month,@fecha) when 'January' then 'Enero' when 'February' then 'Febrero' when 'March' then 'Marzo' when 'April' then 'Abril' when 'May' then 'Mayo' when 'June' then 'Junio' when 'July' then 'Julio' when 'August' then 'Agosto' when 'September' then 'Setiembre' when 'October' then 'Octubre' when 'November' then 'Noviembre' when 'December' then 'Diciembre' end--case return @nombre end; Analicemos: luego de "create function" y el nombre de la funcin, especificamos los parmetros de entrada con sus tipos de datos (entre parntesis). El parmetro de entrada tiene definido un valor por defecto.
165
Luego de los parmetros de entrada se indica el tipo de dato que retorna luego de "returns"; luego de "as" comienza el bloque "begin...end" dentro del cual se encuentran las instrucciones de procesamiento y el valor retornado luego de "return". Las funciones que retornan un valor escalar pueden emplearse en cualquier consulta donde se coloca un campo. Recuerde que al invocar una funcin escalar, se debe especificar el propietario y el nombre de la funcin: select nombre, dbo.f_nombreMes(fechaingreso) as 'mes de ingreso' from empleados; No olvide que cuando invocamos funciones que tienen definidos parmetros de entrada DEBEMOS suministrar SIEMPRE un valor para l. Podemos colocar un valor por defecto al parmetro, pero al invocar la funcin, para que tome el valor por defecto DEBEMOS especificar "default". Por ejemplo, si llamamos a la funcin anterior sin enviarle un valor: select dbo.f_nombreMes(); SQL Server muestra un mensaje de error indicando que necesita argumento. Para que tome el valor por defecto debemos enviar "default" como argumento: select dbo.f_nombreMes(default); La instruccin "create function" debe ser la primera sentencia de un lote.
166
Tambin es similar a una vista; pero en las vistas solamente podemos emplear "select", mientras que en funciones definidas por el usuario podemos incluir sentencias como "if", llamadas a funciones, procedimientos, etc. Sintaxis: create function NOMBREFUNCION (@PARAMETRO TIPO) returns @NOMBRETABLARETORNO table-- nombre de la tabla --formato de la tabla (CAMPO1 TIPO, CAMPO2 TIPO, CAMPO3 TIPO ) as begin insert @NOMBRETABLARETORNO select CAMPOS from TABLA where campo OPERADOR @PARAMETRO RETURN end Como cualquier otra funcin, se crea con "create function" seguida del nombre de la funcin; luego (opcionalmente) los parmetros de entrada con su tipo de dato. La clusula "returns" define un nombre de variable local para la tabla que retornar, el tipo de datos a retornar (que es "table") y el formato de la misma (campos y tipos). El cuerpo de la funcin se define tambin en un bloque "begin... end", el cual contiene las instrucciones que insertan filas en la variable (tabla que ser retornada) definida en "returns". "return" indica que las filas insertadas en la variable son retornadas; no puede ser un argumento. El siguiente ejemplo crea una funcin denominada "f_ofertas" que recibe un parmetro. La funcin retorna una tabla con el codigo, ttulo, autor y precio de todos los libros cuyo precio sea inferior al parmetro: create function f_ofertas (@minimo decimal(6,2)) returns @ofertas table-- nombre de la tabla --formato de la tabla (codigo int, titulo varchar(40), autor varchar(30), precio decimal(6,2) )
167
as begin insert @ofertas select codigo,titulo,autor,precio from libros where precio < @minimo return end; Las funciones que retornan una tabla pueden llamarse sin especificar propietario: select *from f_ofertas(30); select *from dbo.f_ofertas(30); Dijimos que este tipo de funcin puede ser referenciada en el "from" de una consulta; la siguiente consulta realiza un join entre la tabla "libros" y la tabla retornada por la funcin "f_ofertas": select *from libros as l join dbo.f_ofertas(25) as o on l.codigo=o.codigo; Se puede llamar a la funcin como si fuese una tabla o vista listando algunos campos: select titulo,precio from dbo.f_ofertas(40);
Funciones (modificar)
Las funciones de SQL Server no pueden ser modificadas, las funciones definidas por el usuario si. Las funciones definidas por el usuario pueden modificarse con la instruccin "alter function". Sintaxis general: alter function PROPIETARIO.NOMBREFUNCION NUEVADEFINICION;
168
Sintaxis para modificar funciones escalares: alter funtion PROPIETARIO.NOMBREFUNCION (@PARAMETRO TIPO=VALORPORDEFECTO) returns TIPO as begin CUERPO return EXPRESIONESCALAR end Sintaxis para modificar una funcin de varias instrucciones que retorna una tabla: alter function NOMBREFUNCION (@PARAMETRO TIPO=VALORPORDEFECTO) returns @VARIABLE table (DEFINICION DE LA TABLA A RETORNAR) as begin CUERPO DE LA FUNCION return end Sintaxis para modificar una funcin con valores de tabla en lnea alter function NOMBREFUNCION (@PARAMETRO TIPO) returns TABLE as return (SENTENCIAS SELECT) Veamos un ejemplo. Creamos una funcin que retorna una tabla en lnea: create function f_libros (@autor varchar(30)='Borges') returns table as return ( select titulo,editorial from libros where autor like '%'+@autor+'%' );
169
La modificamos agregando otro campo en el "select": alter table f_libros (@autor varchar(30)='Borges') returns table as return ( select codigo,titulo,editorial from libros where autor like '%'+@autor+'%' );
Funciones (encriptado)
Las funciones definidas por el usuario pueden encriptarse, para evitar que sean ledas con "sp_helptext". Para ello debemos agregar al crearlas la opcin "with encryption" antes de "as". En funciones escalares: create function NOMBREFUNCION (@PARAMETRO TIPO) returns TIPO with encryption as begin CUERPO return EXPRESION end En funciones de tabla de varias sentencias se coloca luego del formato de la tabla a retornar: create function NOMBREFUNCION (@PARAMETRO TIPO) returns @NOMBRETABLARETORNO table-- nombre de la tabla --formato de la tabla (CAMPO1 TIPO, CAMPO2 TIPO, CAMPO3 TIPO ) with encryption as begin insert @NOMBRETABLARETORNO
170
select CAMPOS from TABLA where campo OPERADOR @PARAMETRO RETURN end En funciones con valores de tabla en lnea: create function NOMBREFUNCION (@PARAMETRO TIPO=VALORPORDEFECTO) returns table with encryption as return (SELECT); Veamos un ejemplo: create function f_libros (@autor varchar(30)='Borges') returns table with encryption as return ( select titulo,editorial from libros where autor like '%'+@autor+'%' ); Si ejecutamos el procedimiento almacenado del sistema "sp_helptext" seguido del nombre de la funcin creada anteriormente, SQL Server mostrar un mensaje indicando que tal funcin est encriptada.
Funciones (informacin)
Las funciones son objetos, as que para obtener informacin de ellos pueden usarse los siguientes procedimientos almacenados del sistema y las siguientes tablas: - "sp_help": sin parmetros nos muestra todos los objetos de la base de datos seleccionada, incluidas las funciones definidas por el usuario. En la columna "Object_type" aparece "scalar function" si es una funcin escalar, "table function" si es una funcin de tabla de varias sentencias y "inline function" si es una funcin de tabla en lnea.
171
Si le enviamos como argumento el nombre de una funcin definida por el usuario, obtenemos el propietario, el tipo de funcin y la fecha de creacin; si es una funcin de tabla, los campos de la tabla retornada. - "sp_helptext": seguido del nombre de una funcin definida por el usuario nos muestra el texto que define la funcin, excepto si ha sido encriptado. - "sp_stored_procedures": muestra todos los procedimientos almacenados y funciones definidas por el usuario. - "sp_depends": seguido del nombre de un objeto, nos devuelve 2 resultados: 1) nombre, tipo, campos, etc. de los objetos de los cuales depende el objeto enviado (referenciados por el objeto) y 2) nombre y tipo de los objetos que dependen del objeto nombrado (que lo referencian). Por ejemplo, ejecutamos "sp_depends" seguido del nombre de una funcin definida por el usuario: sp_depends pa_libroslistado; aparecen las tablas (y dems objetos) de las cuales depende el procedimiento, es decir, las tablas (y campos) referenciadas en la misma. No aparecen objetos que dependan de la funcin porque no existe ningn objeto que la referencie. Podemos ejecutar el procedimiento seguido del nombre de una tabla: sp_depends libros; aparecen las funciones (y dems objetos) que dependen de ella (que la referencian). No aparecen objetos de los cuales depende porque la tabla no los tiene. - La tabla del sistema "sysobjects": muestra nombre y varios datos de todos los objetos de la base de datos actual. La columna "xtype" indica el tipo de objeto. Si es una funcin definida por el usuario escalar, muestra "FN", si es una funcin de tabla de varias sentencias, muestra "TF" y si es una funcin de tabla en linea muestra "IF". Si queremos ver el nombre, tipo y fecha de creacin de todas las funciones definidas por el usuario, podemos tipear: select name,xtype as tipo,crdate as fecha from sysobjects where xtype in ('FN','TF','IF');
172
Disparadores (triggers)
Un "trigger" (disparador o desencadenador) es un tipo de procedimiento almacenado que se ejecuta cuando se intenta modificar los datos de una tabla (o vista). Se definen para una tabla (o vista) especfica. Se crean para conservar la integridad referencial y la coherencia entre los datos entre distintas tablas. Si se intenta modificar (agregar, actualizar o eliminar) datos de una tabla en la que se defini un disparador para alguna de estas acciones (insercin, actualizacin y eliminacin), el disparador se ejecuta (se dispara) en forma automtica. Un trigger se asocia a un evento (insercin, actualizacin o borrado) sobre una tabla. 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 para la que se ha definido un disparador, el disparador se ejecuta automticamente. - no reciben y retornan parmetros. - son apropiados para mantener la integridad de los datos, no para obtener resultados de consultas. Los disparadores, a diferencia de las restricciones "check", pueden hacer referencia a campos de otras tablas. Por ejemplo, puede crearse un trigger de insercin en la tabla "ventas" que compruebe el campo "stock" de un artculo en la tabla "articulos"; el disparador controlara que, cuando el valor de "stock" sea menor a la cantidad que se intenta vender, la insercin del nuevo registro en "ventas" no se realice. Los disparadores se ejecutan DESPUES de la ejecucin de una instruccin "insert", "update" o "delete" en la tabla en la que fueron definidos. Las restricciones se comprueban ANTES de la ejecucin de una instruccin "insert", "update" o "delete". Por lo tanto, las restricciones se comprueban primero, si se infringe alguna restriccin, el desencadenador no llega a ejecutarse. Los triggers se crean con la instruccin "create trigger". Esta instruccin especifica la tabla en la que se define el disparador, los eventos para los que se ejecuta y las instrucciones que contiene. Sintaxis bsica: create triggre NOMBREDISPARADOR on NOMBRETABLA for EVENTO- insert, update o delete
173
as SENTENCIAS Analizamos la sintaxis: - "create trigger" junto al nombre del disparador. - "on" seguido del nombre de la tabla o vista para la cual se establece el trigger. - luego de "for", se indica la accin (evento, el tipo de modificacin) sobre la tabla o vista que activar el trigger. Puede ser "insert", "update" o "delete". Debe colocarse al menos UNA accin, si se coloca ms de una, deben separarse con comas. - luego de "as" viene el cuerpo del trigger, se especifican las condiciones y acciones del disparador; es decir, las condiciones que determinan cuando un intento de insercin, actualizacin o borrado provoca las acciones que el trigger realizar. Consideraciones generales: - "create trigger" debe ser la primera sentencia de un bloque y slo se puede aplicar a una tabla. - un disparador se crea solamente en la base de datos actual pero puede hacer referencia a objetos de otra base de datos. - Las siguientes instrucciones no estn 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 modificacin (insercin, actualizacin o borrado) para una misma tabla. Por ejemplo, se puede crear un "insert trigger" para una tabla que ya tiene otro "insert trigger". A continuacin veremos la creacin de un disparador para el suceso de insercin: "insert triger".
174
as SENTENCIAS Analizamos la sintaxis: "create trigger" junto al nombre del disparador; "on" seguido del nombre de la tabla para la cual se establece el trigger. Luego de "for" se coloca el evento (en este caso "insert"), lo que indica que las inserciones sobre la tabla activarn el trigger. Luego de "as" se especifican las condiciones y acciones, es decir, las condiciones que determinan cuando un intento de insercin provoca las acciones que el trigger realizar. Creamos un trigger sobre la tabla "ventas" para el evento se insercin. Cada vez que se realiza un "insert" sobre "ventas", el disparador se ejecuta. El disparador controla que la cantidad que se intenta vender sea menor o igual al stock del libro y actualiza el campo "stock" de "libros", restando al valor anterior la cantidad vendida: create trigger DIS_ventas_insertar on ventas for insert as declare @stock int select @stock= stock from libros join inserted on inserted.codigolibro=libros.codigo where libros.codigo=inserted.codigolibro if (@stock>=(select cantidad from inserted)) update libros set stock=stock-inserted.cantidad from libros join inserted on inserted.codigolibro=libros.codigo where codigo=inserted.codigolibro else begin raiserror ('Hay menos libros en stock de los solicitados para la venta', 16, 1) rollback transaction end Entonces, creamos el disparador ("create trigger") dndole un nombre ("DI_ventas_insertar") sobre ("on") una tabla especfica ("ventas") para ("for") el suceso de insercin ("insert"). Luego se "as" colocamos las sentencias, las acciones que el trigger realizar cuando se ingrese un registro en "ventas" (en este caso, controlar que haya stock y disminuir el stock de "libros").
175
Cuando se activa un disparador "insert", los registros se agregan a la tabla del disparador y a una tabla denominada "inserted". La tabla "inserted" es una tabla virtual que contiene una copia de los registros insertados; tiene una estructura similar a la tabla en que se define el disparador, es decir, la tabla en que se intenta la accin. La tabla "inserted" guarda los valores nuevos de los registros. Dentro del trigger se puede acceder a esta tabla virtual "inserted" que contiene todos los registros insertados, es lo que hicimos en el disparador creado anteriormente, lo que solicitamos es que se le reste al "stock" de "libros", la cantidad ingresada en el nuevo registro de "ventas", valor que recuperamos de la tabla "inserted". "rollback transaction" es la sentencia que deshace la transaccin, es decir, borra todas las modificaciones que se produjeron en la ltima transaccin restableciendo todo al estado anterior. "raiserror" muestra un mensaje de error personalizado. Para identificar fcilmente 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 accin. La instruccin "writetext" no activa un disparador.
176
El disparador del siguiente ejemplo se crea para la tabla "ventas", para que cada vez que se elimine un registro de "ventas", se actualice el campo "stock" de la tabla "libros" (por ejemplo, si el comprador devuelve los libros comprados): create trigger DIS_ventas_borrar on ventas for delete as update libros set stock= libros.stock+deleted.cantidad from libros join deleted on deleted.codigolibro=libros.codigo; Entonces, creamos el disparador ("create trigger") dndole un nombre ("DI_ventas_borrar") sobre ("on") una tabla especfica ("ventas") para ("for") el evento de borrado ("delete"). Luego de "as" colocamos las sentencias, las acciones que el trigger realizar cuando se elimine un registro en "ventas" (en este caso, aumentar el stock de "libros"). Cuando se activa un disparador "delete", los registros eliminados en la tabla del disparador se agregan a una tabla llamada "deleted". La tabla "deleted" es una tabla virtual que conserva una copia de los registros eliminados; tiene una estructura similar a la tabla en que se define el disparador, es decir, la tabla en que se intenta la accin. Dentro del trigger se puede acceder a esta tabla virtual "deleted". El siguiente disparador se crea para controlar que no se elimine ms de un registro de la tabla "libros". El disparador se activa cada vez que se elimina un registro o varios, controlando la cantidad de registros que se estn eliminando; si se est eliminando ms de un registro, el disparador retorna un mensaje de error y deshace la transaccin: create trigger DIS_libros_borrar on libros for delete as if (select count(*) from deleted) > 1 begin raiserror('No puede borrar ms de un libro',16,1) rollback transaction end; Si se ejecuta un "delete" sobre "libros" que afecte a varios registros, se activa el disparador y evita la transaccin.
177
Si se ejecuta el siguiente "delete", que afecta a un solo registro, se activa el disparador y permite la transaccin: delete from libros where codigo=5; La sentencia "truncate table" no puede incluirse en un disparador de borrado (delete trigger).
178
Entonces, creamos el disparador ("create trigger") dndole un nombre ("DI_libros_actualizar") sobre una tabla especfica ("libros") para ("for") el suceso de actualizacin ("update"). Luego de "as" colocamos las sentencias, las acciones que el trigger realizar cuando se intente actualizar uno o varios registros en "libros" (en este caso, impedir las modificaciones). Cuando se ejecuta una instruccin "update" en una tabla que tiene definido un disparador, los registros originales (antes de ser actualizados) se mueven a la tabla virtual "deleted" y los registros actualizados (con los nuevos valores) se copian a la tabla virtual "inserted". Dentro del trigger se puede acceder a estas tablas. En el cuerpo de un trigger se puede emplear la funcin "update(campo)" que recibe un campo y retorna verdadero si el evento involucra actualizaciones (o inserciones) en ese campo; en caso contrario retorna "false". Creamos un disparador que evite que se actualice el campo "precio" de la tabla "libros": create trigger DIS_libros_actualizar_precio on libros for update as if update(precio) begin raiserror('El precio de un libro no puede modificarse.', 10, 1) rollback transaction end; Empleamos "if update()" para que el trigger controle la actualizacin del campo "precio"; as, cuando el disparador detecte una actualizacin en tal campo, realizar las acciones apropiadas (mostrar un mensaje y deshacer la actualizacin); en caso que se actualice otro campo, el disparador se activa, pero permite la transaccin. Creamos un disparador de actualizacin que muestra el valor anterior y nuevo valor de los registros actualizados: create trigger DIS_libros_actualizar2 on libros for update as if (update(titulo) or update(autor) or update(editorial)) and not (update(precio) or update(stock)) begin select d.codigo, (d.titulo+'-'+ d.autor+'-'+d.editorial) as 'registro anterior', (i.titulo+'-'+ i.autor+'-'+i.editorial) as 'registro actualizado' from deleted as d join inserted as i
179
on d.codigo=i.codigo end else begin raiserror('El precio y stock no pueden modificarse. La actualizacin no se realiz.', 10, 1) rollback transaction end; Empleamos "if update" para que el trigger controle si la actualizacin se realiza en ciertos campos permitidos (titulo, autor y editorial) y no en los campos prohibidos (precio y stock)); si se modifican los campos permitidos y ninguno de los no permitidos, mostrar los antiguos y nuevos valores consultando las tablas "deleted" e "inserted", en caso que se actualice un campo no permitido, el disparador muestra un mensaje y deshace la transaccin. Note que el disparador no controla los intentos de actualizacin sobre el campo "codigo", esto es porque tal campo, no puede modificarse porque est definido "identity", si intentamos modificarlo, SQL Server muestra un mensaje de error y el trigger no llega a dispararse.
180
end else if (select matricula from inserted)='n' insert into morosos select documento from inserted; El trigger controla: - si se intenta ingresar una inscripcin de un socio moroso, se deshace la transaccin; - si se intenta eliminar una inscripcin de un socio que est en "morosos", se deshace la transaccin; - si se ingresa una nueva inscripcin y no se paga la matrcula, dicho socio se ingresa a la tabla "morosos".
181
Veamos un ejemplo. Una empresa almacena los datos de sus empleados en una tabla "empleados" y en otra tabla "clientes" los datos de sus clientes. Se crea una vista que muestra los datos de ambas tablas: create view vista_empleados_clientes as select documento,nombre, domicilio, 'empleado' as condicion from empleados union select documento,nombre, domicilio,'cliente' from clientes; Creamos un disparador sobre la vista "vista_empleados_clientes" para insercin, que redirija las inserciones a la tabla correspondiente: create trigger DIS_empleadosclientes_insertar on vista_empleados_clientes instead of insert as insert into empleados select documento,nombre,domicilio from inserted where condicion='empleado' insert into clientes select documento,nombre,domicilio from inserted where condicion='cliente'; El disparador anterior especifica que cada vez que se ingresen registros en la vista "vista_empleados_clientes", en vez de (instead of) realizar la accin (insertar en la vista), se ejecuten las sentencias del trigger, es decir, se ingresen los registros en las tablas correspondientes. Entonces, las opciones de disparo pueden ser: a) "after": el trigger se dispara cuando las acciones especificadas (insert, delete y/o update) son ejecutadas; todas las acciones en cascada de una restriccin "foreign key" y las comprobaciones de restricciones "check" deben realizarse con xito antes de ejecutarse el trigger. Es la opcin por defecto si solamente colocamos "for" (equivalente a "after"). La sintaxis es: create triggre NOMBREDISPARADOR on NOMBRETABLA after | for-- son equivalentes ACCION-- insert, update o delete as SENTENCIAS
182
b) "instead of": sobreescribe la accin desencadenadora del trigger. Se puede definir solamente un disparador de este tipo para cada accin (insert, delete o update) sobre una tabla o vista. Sintaxis: create triggre NOMBREDISPARADOR on NOMBRETABLA o VISTA instead of ACCION-- insert, update o delete as SENTENCIAS Consideraciones: - Se pueden crear disparadores "instead of" en vistas y tablas. - No se puede crear un disparador "instead of" en vistas definidas "with check option". - No se puede crear un disparador "instead of delete" y "instead of update" sobre tablas que tengan una "foreign key" que especifique una accin "on delete cascade" y "on update cascade" respectivamente. - Los disparadores "after" no pueden definirse sobre vistas. - No pueden crearse disparadores "after" en vistas ni en tablas temporales; pero pueden referenciar vistas y tablas temporales. - Si existen restricciones en la tabla del disparador, se comprueban DESPUES de la ejecucin del disparador "instead of" y ANTES del disparador "after". Si se infringen las restricciones, se revierten las acciones del disparador "instead of"; en el caso del disparador "after", no se ejecuta.
Disparador (eliminar)
Los triggers se eliminan con la instruccin "drop trigger": drop trigger NOMBREDISPARADOR; Si el disparador que se intenta eliminar no existe, aparece un mensaje indicndolo, para evitarlo, podemos verificar su existencia antes de solicitar su eliminacin (como con cualquier otro objeto): if object_id('NOMBREDISPARADOR') is not null drop trigger NOMBREDISPARADOR;
183
Eliminamos, si existe, el trigger "dis_libros_insertar": if object_id('dis_libros_insertar') is not null drop trigger dis_libros_insertar; Cuando se elimina una tabla o vista que tiene asociados triggers, todos los triggers asociados se eliminan automticamente.
Disparador (informacin)
Los triggers (disparadores) son objetos, as que para obtener informacin de ellos pueden usarse los siguientes procedimientos almacenados del sistema y las siguientes tablas: - "sp_help": sin parmetros 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. Si le enviamos como argumento el nombre de un disparador, obtenemos el propietario, el tipo de objeto y la fecha de creacin. - "sp_helptext": seguido del nombre de un disparador nos muestra el texto que define el trigger, excepto si ha sido encriptado. - "sp_depends": retorna 2 resultados: 1) el nombre, tipo, campos, etc. de los objetos de los cuales depende el objeto enviado (referenciados por el objeto) y 2) nombre y tipo de los objetos que dependen del objeto nombrado (que lo referencian). Por ejemplo, ejecutamos "sp_depends" seguido del nombre de un disparador: sp_depends dis_inscriptos_insertar; Aparece una tabla similar a la siguiente: name type updated column ----------------------------------------------------------------dbo.condicionales user table yes codigocurso dbo.condicionales user table yes fecha dbo.inscriptos user table yes numerocurso dbo.inscriptos user table yes fecha dbo.condicionales user table yes documento dbo.cursos user table no numero dbo.cursos user table no cantidadmaxima dbo.inscriptos user table yes documento En la columna "name" nos muestra las tablas (y dems objetos si hubiese) de las cuales depende el trigger, es decir, las tablas referenciadas en el mismo; el tipo de objeto en la columna "type" (en este
184
caso, todas tablas); la columna "update" indica si el objeto es actualizado o no (note que la tabla "cursos" no se actualiza, solamente se consulta); la columna "column" muestra el nombre del campo que se referencia. No aparecen objetos que dependen del trigger porque no existe ningn objeto que lo referencie. Tambin podemos ejecutar el mismo procedimiento seguido del nombre de una tabla: sp_depends inscriptos; aparecen los objetos que dependen de ella (que la referencian). En este ejemplo: 1 solo objeto, su nombre y tipo (trigger). No aparecen objetos de los cuales depende porque la tabla no los tiene. - Para conocer los disparadores que hay en una tabla especfica y sus acciones respectivas, podemos ejecutar el procedimiento del sistema "sp_helptrigger" seguido del nombre de la tabla o vista. Por ejemplo: sp_helptrigger inscriptos;
El nombre del trigger, su propietario; en las 3 columnas siguientes indica para qu evento se ha definido (un valor 1 indica que est definido para tal evento); las 2 ltimas columnas indican el momento de disparo (un valor 1 se interpreta como verdadero y un 0 como falso). En el ejemplo, el disparador "dis_inscriptos_insertar" est definido para el evento de insercin (valor 1 en "isinsert") y es "instead of" (valor 1 en "isinsteadof"). - La tabla del sistema "sysobjects": muestra nombre y varios datos de todos los objetos de la base de datos actual. La columna "xtype" indica el tipo de objeto. Si es un trigger muestra "TR". Si queremos ver el nombre, tipo y fecha de creacin de todos los disparadores, podemos tipear: select name,xtype as tipo,crdate as fecha from sysobjects where xtype = 'TR';
Disparador (modificar)
Los triggers pueden modificarse y eliminarse. Al modificar la definicin de un disparador se reemplaza la definicin existente del disparador por la nueva definicin. La sintaxis general es la siguiente: alter trigger NOMBREDISPARADOR NUEVADEFINICION;
185
Asumiendo que hemos creado un disparador llamado "dis_empleados_borrar" que no permita eliminar ms de 1 registro de la tabla empleados; alteramos el disparador, para que cambia la cantidad de eliminaciones permitidas de 1 a 3: alter trigger dis_empleados_borrar on empleados for delete as if (select count(*) from deleted)>3--antes era 1 begin raiserror('No puede borrar mas de 3 empleados',16, 1) rollback transaction end; Se puede cambiar el evento del disparador. Por ejemplo, si cre un disparador para "insert" y luego se modifica el evento por "update", el disparador se ejecutar cada vez que se actualice la tabla.
186
Sintaxis para habilitar (o deshabilitar) todos los disparadores de una tabla especfica: alter table NOMBRETABLA ENABLE | DISABLE TRIGGER all; La siguiente sentencia habilita todos los triggers de la tabla "empleados": alter table empleados enable trigger all;
187
Disparador (condicionales)
Una instruccin "insert", "update" o "delete" que invoque a un disparador puede afectar a varios registros. En tales casos, un trigger rechaza o acepta cada transaccin de modificacin como una totalidad. Podemos optar por: 1) procesar todos los registros: todos los registros afectados debern cumplir los criterios del disparador para que se produzca la accin, o 2) permitir acciones condicionales: puede definir un disparador que controle si cada registro afectado cumple con la condicin; si algn registro no la cumple, la accin no se produce para tal registro pero si para los dems que si la cumplen. Veamos un ejemplo. Tenemos la tabla "libros". Creamos un disparador de actualizacin sobre la tabla "libros". Se permite actualizar el stock de varios libros a la vez; pero ningn "stock" debe tener un valor negativo. Entonces, si algn "stock" queda con un valor negativo, no debe cambiar, los dems si: create trigger dis_libros_actualizar on libros after update as if exists (select *from inserted where stock<0) begin update libros set stock=deleted.stock from libros join deleted on deleted.codigo=libros.codigo join inserted on inserted.codigo=libros.codigo where inserted.stock<0; end; No podemos revertir la transaccin con "rollback transaction" porque en ese caso TODOS los registros modificados volveran a los valores anteriores, y lo que necesitamos es que solamente aquellos que quedaron con valor negativo vuelvan a su valor original. Tampoco podemos evitar que se actualicen todos los registros porque se actualizan antes que las acciones del trigger se ejecuten. Lo que hacemos es, en el cuerpo del trigger, averiguar si alguno de los registros actualizados tiene stock negativo; si es as, volvemos a actualizarlo al valor anterior a la transaccin.
188