Está en la página 1de 456

íNDICE

ÍNDICE..................................................................................................... 1

PRESENTACIÓN......................................................................................... 6

1 - OBJETIVOS Y ALCANCES DEL TUTORIAL DE.............................................7

2 - CREAR UNA TABLA (CREATE TABLE - SP_TABLES - SP_COLUMNS - DROP


TABLE)..................................................................................................... 8

3 - INSERTAR Y RECUPERAR REGISTROS DE UNA TABLA (INSERT INTO -


SELECT).................................................................................................. 12

4 - TIPOS DE DATOS BÁSICOS...................................................................14

5 - RECUPERAR ALGUNOS CAMPOS (SELECT).............................................17

6 - RECUPERAR ALGUNOS REGISTROS (WHERE).........................................20

7- OPERADORES RELACIONALES...............................................................22

8 - BORRAR REGISTROS (DELETE).............................................................25

9 - ACTUALIZAR REGISTROS (UPDATE)......................................................28

10 - COMENTARIOS.................................................................................. 32

11 - VALORES NULL (IS NULL)..................................................................33

12 - CLAVE PRIMARIA..............................................................................38

13 - CAMPO CON ATRIBUTO IDENTITY.......................................................41

14 - OTRAS CARACTERÍSTICAS DEL ATRIBUTO IDENTITY.............................44

15 - TRUNCATE TABLE.............................................................................. 48

16 - OTROS TIPOS DE DATOS EN SQL SERVER............................................51

17 - TIPO DE DATO (TEXTO)......................................................................51

18 - TIPO DE DATO (NUMÉRICO)...............................................................55

19 - TIPO DE DATO (FECHA Y HORA).........................................................59

20 - INGRESAR ALGUNOS CAMPOS (INSERT INTO)......................................61

21 - VALORES POR DEFECTO (DEFAULT).....................................................63

1
22 - COLUMNAS CALCULADAS (OPERADORES ARITMÉTICOS Y DE
CONCATENACIÓN)...................................................................................67

23 - ALIAS..............................................................................................69

25 - FUNCIONES PARA EL MANEJO DE CADENAS.........................................71

26 - FUNCIONES MATEMÁTICAS................................................................75

27 - FUNCIONES PARA EL USO DE FECHAS Y HORAS...................................77

28 - ORDENAR REGISTROS (ORDER BY).....................................................80

29 - OPERADORES LÓGICOS ( AND - OR - NOT)..........................................83

30 - OTROS OPERADORES RELACIONALES (IS NULL)..................................87

31 - OTROS OPERADORES RELACIONALES (BETWEEN)................................89

32 - OTROS OPERADORES RELACIONALES (IN)...........................................92

33 - BÚSQUEDA DE PATRONES (LIKE - NOT LIKE).......................................94

34 - CONTAR REGISTROS (COUNT)............................................................98

35 - CONTAR REGISTROS (COUNT_BIG)....................................................100

36 - FUNCIONES DE AGRUPAMIENTO (COUNT - SUM - MIN - MAX - AVG).....103

37 - AGRUPAR REGISTROS (GROUP BY)...................................................106

38 - SELECCIONAR GRUPOS (HAVING).....................................................111

39 - MODIFICADOR DEL GROUP BY (WITH ROLLUP)..................................116

40 - MODIFICADOR DEL GROUP BY (WITH CUBE)......................................121

41 - FUNCIÓN GROUPING.......................................................................125

42 - CLÁUSULAS COMPUTE Y COMPUTE BY..............................................128

43 - REGISTROS DUPLICADOS (DISTINCT)................................................132

44 - CLÁUSULA TOP...............................................................................137

45 - CLAVE PRIMARIA COMPUESTA..........................................................140

46 - INTEGRIDAD DE LOS DATOS.............................................................143

47 - RESTRICCIÓN DEFAULT....................................................................144

48 - RESTRICCIÓN CHECK.......................................................................148

2
49 - DESHABILITAR RESTRICCIONES (WITH CHECK - NOCHECK).................153

50 - RESTRICCIÓN PRIMARY KEY.............................................................156

51 - RESTRICCIÓN UNIQUE.....................................................................160

52 - INFORMACIÓN DE RESTRICCIONES (SP_HELPCONSTRAINT)................162

53 - ELIMINAR RESTRICCIONES (ALTER TABLE - DROP)..............................164

54 - CREAR Y ASOCIAR REGLAS (CREATE RULE - SP_BINDRULE).................166

55 - ELIMINAR Y DASASOCIAR REGLAS (SP_UNBINDRULE - DROP RULE).....173

56 - INFORMACIÓN DE REGLAS (SP_HELP - SP_HELPCONSTRAINT)............176

57 - VALORES PREDETERMINADOS (CREATE DEFAULT)..............................178

58 - DESASOCIAR Y ELIMINAR VALORES PREDETERMINADOS....................183

59 - INFORMACIÓN DE VALORES PREDETERMINADOS...............................186

60 - INDICES......................................................................................... 189

61 - INDICES AGRUPADOS Y NO AGRUPADOS (CLUSTERED Y NONCLUSTERED)


............................................................................................................ 190

62 - CREACIÓN DE ÍNDICES..................................................................... 190

63 - REGENERAR ÍNDICES.......................................................................194

64 - ELIMINAR ÍNDICES..........................................................................197

65 - TRABAJAR CON VARIAS TABLAS.......................................................199

66 - COMBINACIÓN INTERNA (INNER JOIN)...............................................200

67 - COMBINACIÓN EXTERNA IZQUIERDA (LEFT JOIN)...............................203

68 - COMBINACIÓN EXTERNA DERECHA (RIGHT JOIN)...............................207

69 - COMBINACIÓN EXTERNA COMPLETA (FULL JOIN)...............................210

70 - COMBINACIONES CRUZADAS (CROSS JOIN).......................................212

71 - AUTOCOMBINACIÓN........................................................................215

72 - COMBINACIONES Y FUNCIONES DE AGRUPAMIENTO..........................218

73 - COMBINACIÓN DE MÁS DE DOS TABLAS............................................220

74 - COMBINACIONES CON UPDATE Y DELETE..........................................224

3
75 - CLAVE FORÁNEA.............................................................................226

76 - RESTRICCIONES (FOREIGN KEY).......................................................226

77 - RESTRICCIONES FOREIGN KEY EN LA MISMA TABLA...........................230

78 - RESTRICCIONES FOREIGN KEY (ACCIONES).......................................233

79 - RESTRICCIONES FOREIGN KEY DESHABILITAR Y ELIMINAR (WITH CHECK -


NOCHECK)............................................................................................ 238

80 - RESTRICCIONES FOREIGN KEY (INFORMACIÓN).................................244

81 - RESTRICCIONES AL CREAR LA TABLA................................................244

82 - UNIÓN............................................................................................ 249

83 - AGREGAR Y ELIMINAR CAMPOS ( ALTER TABLE - ADD - DROP)............252

84 - ALTERAR CAMPOS (ALTER TABLE - ALTER).........................................255

85 - AGREGAR CAMPOS Y RESTRICCIONES (ALTER TABLE).........................257

86 - CAMPOS CALCULADOS....................................................................260

87 - TIPO DE DATO DEFINIDO POR EL USUARIO (CREAR - INFORMACION)...262

88 - TIPO DE DATO DEFINIDO POR EL USUARIO (ASOCIACIÓN DE REGLAS). 264

89 - TIPO DE DATO DEFINIDO POR EL USUARIO (VALORES


PREDETERMINADOS).............................................................................269

90 - TIPO DE DATO DEFINIDO POR EL USUARIO (ELIMINAR)......................276

91 - SUBCONSULTAS..............................................................................279

92 - SUBCONSULTAS COMO EXPRESIÓN...................................................280

93 - SUBCONSULTAS CON IN...................................................................282

4 - SUBCONSULTAS ANY - SOME - ALL.....................................................286

95 - SUBCONSULTAS CORRELACIONADAS................................................289

96 - EXISTS Y NO EXISTS........................................................................ 292

97 - SUBCONSULTA SIMIL AUTOCOMBINACIÓN.........................................295

98 - SUBCONSULTA EN LUGAR DE UNA TABLA..........................................297

99 - SUBCONSULTA (UPDATE - DELETE)...................................................301

100 - SUBCONSULTA (INSERT)................................................................303


4
101 - CREAR TABLA A PARTIR DE OTRA (SELECT - INTO)...........................306

102 - GO............................................................................................... 310

103 - VISTAS......................................................................................... 311

104 - VISTAS (INFORMACIÓN).................................................................317

105 - VISTAS (ENCRIPTAR).....................................................................320

106 - VISTAS (ELIMINAR)........................................................................ 321

107 - VISTAS (WITH CHECK OPTION).......................................................323

108 - VISTAS (MODIFICAR DATOS DE UNA TABLA A TRAVÉS DE VISTAS).....326

109 - VISTAS MODIFICAR (ALTER VIEW)...................................................331

110 - LENGUAJE DE CONTROL DE FLUJO (CASE)........................................335

111 - LENGUAJE DE CONTROL DE FLUJO (IF).............................................340

112 - VARIABLES DE USUARIO................................................................343

113 - TIPOS DE DATOS TEXT, NTEXT Y IMAGE..........................................346

114 - TIPO DE DATO TEXT - NTEXT E IMAGE (PUNTEROS)..........................349

115 - TIPO DE DATO TEXT - NTEXT E IMAGE (LEER)..................................351

116 - TIPO DE DATO TEXT - NTEXT E IMAGE (ESCRIBIR)............................352

117 - TIPO DE DATO TEXT - NTEXT E IMAGE (ACTUALIZAR).......................356

118 - TIPO DE DATO TEXT - NTEXT E IMAGE (FUNCIONES).........................360

119 - PROCEDIMIENTOS ALMACENADOS..................................................362

120 - PROCEDIMIENTOS ALMACENADOS (CREAR - EJECUTAR)....................363

121 - PROCEDIMIENTOS ALMACENADOS (ELIMINAR)................................366

122 - PROCEDIMIENTOS ALMACENADOS (PARÁMETROS DE ENTRADA).......368

123 - PROCEDIMIENTOS ALMACENADOS (PARÁMETROS DE SALIDA)..........374

124 - PROCEDIMIENTOS ALMACENADOS (RETURN)..................................378

125 - PROCEDIMIENTOS ALMACENADOS (INFORMACIÓN).........................382

126 - PROCEDIMIENTOS ALMACENADOS (ENCRIPTADO)............................385

127 - PROCEDIMIENTOS ALMACENADOS (MODIFICAR)..............................386

5
128 - PROCEDIMIENTOS ALMACENADOS (INSERTAR)................................388

129 - PROCEDIMIENTOS ALMACENADOS (ANIDADOS)...............................391

130 - PROCEDIMIENTOS ALMACENADOS (RECOMPILAR)...........................393

131 - PROCEDIMIENTOS ALMACENADOS (CON JOIN).................................394

132 - TABLAS TEMPORALES....................................................................394

133 - FUNCIONES..................................................................................395

134 - FUNCIONES (DROP).......................................................................395

135 - FUNCIONES ESCALARES (CREAR Y LLAMAR)....................................396

136 - FUNCIONES DE TABLA DE VARIAS INSTRUCCIONES..........................402

137 - FUNCIONES CON VALORES DE TABLA EN LÍNEA...............................406

138 - FUNCIONES (MODIFICAR)..............................................................408

139 - FUNCIONES (ENCRIPTADO)............................................................410

140 - FUNCIONES (INFORMACIÓN)..........................................................411

141 - DISPARADORES (TRIGGERS)...........................................................412

142 - DISPARADOR DE INSERCIÓN (INSERT TRIGGER)...............................414

143 - DISPARADOR DE BORRADO (DELETE TRIGGER)................................419

144 - DISPARADOR DE ACTUALIZACIÓN (UPDATE TRIGGER)......................424

145 - DISPARADORES (VARIOS EVENTOS)................................................429

146 - DISPARADOR (INSTEAD OFF Y AFTER)............................................432

147 - DISPARADOR (ELIMINAR)...............................................................438

148 - DISPARADOR (INFORMACIÓN)........................................................438

149 - DISPARADOR (MODIFICAR)............................................................440

150 - DISPARADOR (DESHABILITAR Y HABILITAR).....................................441

151 - DISPARADOR (WITH ENCRYPTION)..................................................444

152 - DISPARADOR (CONDICIONALES).....................................................446

DIRECCIÓN WEB DEL CURSO..................................................................449

6
SQL Server Ya

Un curso de Tutoriales

ASP.Net MVC Ya
C# Ya
VB.Net Ya
Donar al sitio
Políticas de privacidad.

Este texto fue escrito por uLoVe-TeCh y esta bajo la licencia de


Creative Commons.

7
Presentación

SQL Server Ya desde CERO. El tutorial está pensado para que pueda ser desarrollado por una persona que

conoce muy poco de programación. El objetivo de este sitio es poder aprender Microsoft SQL Server en

forma sencilla viendo un concepto teórico, luego algunos ejercicios resueltos y por último y lo más

importante efectuar una serie de ejercicios. Puede desarrollar los ejercicios en el sitio, probarlos y ver los

resultados. Un conocimiento profundo de SQL Server nos facilitará el desarrollo de páginas dinámicas con

ASP y ASP.NET que acceden a una base de datos.

8
1 - Objetivos y alcances del tutorial de
El curso brinda un concepto teórico corto, luego un problema resuelto que invito a ejecutar,
modificar y jugar con el mismo. Por último, y lo más 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 creación de tablas, insert, delete, update, definición de índices y
restricciones, creación y ejecución de procedimientos almacenados, vistas, subconsultas,
creación de trigger etc.
La única restricción 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.

9
2 - Crear una tabla (create table - sp_tables -
sp_columns - drop table)
Una base de datos almacena su información en tablas.
Una tabla es una estructura de datos que organiza los datos en columnas y filas; cada columna
es un campo (o atributo) y cada fila, un registro. La intersección de una columna con una fila,
contiene un dato específico, un solo valor.
Cada registro contiene un dato por cada columna de la tabla.
Cada campo (columna) debe tener un nombre. El nombre del campo hace referencia a la
información que almacenará.
Cada campo (columna) también debe definir el tipo de dato que almacenará.
Las tablas forman parte de una base de datos.
Nosotros trabajaremos con la base de datos 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 parámetro @table_owner='dbo' indica que solo muestre las tablas de usuarios y no las que
crea el SQL Server para administración interna.
Finalizamos cada comando con un punto y coma.
Al crear una tabla debemos resolver qué campos (columnas) tendrá y que tipo de datos
almacenarán cada uno de ellos, es decir, su estructura.
La sintaxis básica y general para crear una tabla es la siguiente:
create table NOMBRETABLA(
NOMBRECAMPO1 TIPODEDATO,
...
NOMBRECAMPON TIPODEDATO
);

La tabla debe ser definida con un nombre que la identifique y con el cual accederemos a ella.
Creamos una tabla llamada "usuarios" y entre paréntesis definimos los campos y sus tipos:
create table usuarios (
nombre varchar(30),
clave varchar(10)
);

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

10
Cuando se crea una tabla debemos indicar su nombre y definir al menos un campo con su tipo de
dato. En esta tabla "usuarios" definimos 2 campos:
 nombre: que contendrá una cadena de caracteres de 30 caracteres de longitud, que
almacenará el nombre de usuario y
 clave: otra cadena de caracteres de 10 de longitud, que guardará la clave de cada usuario.
Cada usuario ocupará un registro de esta tabla, con su respectivo nombre y clave.
Para nombres de tablas, se puede utilizar cualquier caracter permitido para nombres de
directorios, el primero debe ser un caracter alfabético y no puede contener espacios. La longitud
máxima es de 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 información 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
_______________________________________
nombre varchar 30
clave varchar 10

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
situación y la sentencia no se ejecuta. Para evitar este mensaje podemos agregar a la instrucción
lo siguiente:
if object_id('usuarios') is not null
drop table usuarios;

En la sentencia precedente especificamos que elimine la tabla "usuarios" si existe.

Vamos a crear una tabla llamada "usuarios". En primer lugar vamos a eliminar la tabla "usuarios"
averiguando si existe (a esto vamos a repetirlo siempre porque puede haber otro usuario que
haya creado una tabla con el mismo nombre):
if object_id('usuarios') is not null
drop table usuarios;

Recordar que debemos finalizar cada comando con un punto y coma.


La tabla "usuarios" contendrá los siguientes campos:

11
- nombre: varchar de 30 caracteres de longitud,
- clave: varchar de 10 caracteres de longitud.

Ahora si creamos la tabla:


create table usuarios (
nombre varchar(30),
clave varchar(10)
);

aparece un mensaje indicando que el comando se completó exitosamente.


Veamos las tablas existentes:
sp_tables @table_owner='dbo';

Veamos la estructura de la tabla "usuarios":


sp_columns usuarios;

aparece mucha información que no analizaremos en detalle, como el nombre de la tabla, su propietario, los
campos y sus tipos de datos, su longitud, etc.:
...COLUMN_NAME TYPE_NAME LENGHT...
_______________________________________
nombre varchar 30
clave varchar 10

Intentemos crear una tabla con el mismo nombre, mostrará un mensaje indicando que ya hay un objeto
llamado 'usuarios' en la base de datos y la sentencia no se ejecutará:
create table usuarios (
nombre varchar(30),
clave varchar(10)
);

Eliminemos la tabla:
drop table usuarios;

Verifiquemos si se ha eliminado:
sp_tables @table_owner='dbo';

no debe aparecer la tabla "usuarios".

problema:

Necesita almacenar los datos de sus amigos en una tabla. Los datos que guardará serán:
apellido,
nombre, domicilio y teléfono.
1- Elimine la tabla "agenda" si existe:
if object_id('agenda') is not null
drop table agenda;

2- Intente crear una tabla llamada "/agenda":


create table /agenda(
apellido varchar(30),
nombre varchar(20),
12
domicilio varchar(30),
telefono varchar(11)
);
aparece un mensaje de error porque usamos un caracter inválido ("/") para el nombre.

3- Cree una tabla llamada "agenda", debe tener los siguientes campos: apellido,
varchar(30); nombre,
varchar(20); domicilio, varchar (30) y telefono, varchar(11):
create table agenda(
apellido varchar(30),
nombre varchar(20),
domicilio varchar(30),
telefono varchar(11)
);

4- Intente crearla nuevamente. Aparece mensaje de error.

5- Visualice las tablas existentes (sp_tables @table_owner='dbo').

6- Visualice la estructura de la tabla "agenda" (sp_columns).

7- Elimine la tabla.

8- Intente eliminar la tabla, sin controlar si existe. Debe aparecer un mensaje de


error.

3 - Insertar y recuperar registros de una tabla


(insert into - select)
Un registro es una fila de la tabla que contiene los datos propiamente dichos. Cada registro tiene
un dato por cada columna (campo). Nuestra tabla "usuarios" consta de 2 campos, "nombre" y
"clave".
Al ingresar los datos de cada registro debe tenerse en cuenta la cantidad y el orden de los
campos.
La sintaxis básica y general es la siguiente:
insert into NOMBRETABLA (NOMBRECAMPO1, ..., NOMBRECAMPOn)
values (VALORCAMPO1, ..., VALORCAMPOn);

Usamos "insert into", luego el nombre de la tabla, detallamos los nombres de los campos entre
paréntesis y separados por comas y luego de la cláusula "values" colocamos los valores para
cada campo, también entre paréntesis y separados por comas.
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":
13
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 también 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');

Problema:

Vamos a crear una tabla llamada "usuarios". En primer lugar vamos a eliminar la tabla "usuarios"
averiguando si existe (recuerde que debemos repetir siempre esto porque puede haber otro usuario que haya
creado una tabla con el mismo nombre en el servidor www.sqlserverya.com.ar:
if object_id('usuarios') is not null
drop table usuarios;

Recordar que debemos finalizar cada comando con un punto y coma.


Creamos la tabla:
create table usuarios(
nombre varchar(30),
clave varchar(10)
);

Agregamos un registro a la tabla:


insert into usuarios (nombre, clave)
values ('Mariano','payaso');

Veamos si el registro se guardó:


select * from usuarios;

Ingresemos otro registro alterando el orden de los campos:


insert into usuarios (clave, nombre)
values ('River','Juan');

Veamos cómo SQL Server almacenó los datos:


select * from usuarios;

Ingresemos otro registro colocando los valores en distinto orden en que se nombran los campos:

14
insert into usuarios (nombre,clave)
values ('Boca','Luis');

Veamos cómo se guardaron los datos:


select * from usuarios;

Note que la cadena "Boca" se almacenó en el campo "nombre" y la cadena "Luis" en el campo "clave".

problema:

Trabaje con la tabla "agenda" que almacena información de sus amigos.


1- Elimine la tabla "agenda", si existe:
if object_id('agenda') is not null
drop table agenda;

2- Cree una tabla llamada "agenda". Debe tener los siguientes campos: apellido (cadena
de 30),
nombre (cadena de 20), domicilio (cadena de 30) y telefono (cadena de 11):

3- Visualice las tablas existentes para verificar la creación de "agenda"


(sp_tables @table_owner='dbo').

4- Visualice la estructura de la tabla "agenda" (sp_columns).

5- Ingrese los siguientes registros:


insert into agenda (apellido, nombre, domicilio, telefono)
values ('Moreno','Alberto','Colon 123','4234567');
insert into agenda (apellido,nombre, domicilio, telefono)
values ('Torres','Juan','Avellaneda 135','4458787');

6- Seleccione todos los registros de la tabla:


select * from agenda;

7- Elimine la tabla "agenda":


drop table agenda;

8- Intente eliminar la tabla nuevamente (aparece un mensaje de error):


drop table agenda;

Segundo problema:

Trabaje con la tabla "libros" que almacena los datos de los libros de su propia
biblioteca.
1- Elimine la tabla "libros", si existe:
if object_id('libros') is not null
drop table libros;

2- Cree una tabla llamada "libros". Debe definirse con los siguientes campos: titulo
(cadena de 20),
autor (cadena de 30) y editorial (cadena de 15).

3- Visualice las tablas existentes (sp_tables @table_owner='dbo').

4- Visualice la estructura de la tabla "libros" (sp_columns).

5- Ingrese los siguientes registros:


insert into libros (titulo,autor,editorial)
values ('El aleph','Borges','Planeta');
insert into libros (titulo,autor,editorial)

15
values ('Martin Fierro','Jose Hernandez','Emece');
insert into libros (titulo,autor,editorial)
values ('Aprenda PHP','Mario Molina','Emece');

6- Muestre todos los registros (select).

4 - Tipos de datos básicos


Ya explicamos que al crear una tabla debemos resolver qué campos (columnas) tendrá y que
tipo de datos almacenará cada uno de ellos, es decir, su estructura.
El tipo de dato especifica el tipo de información que puede guardar un campo: caracteres,
números, etc.
Estos son algunos tipos de datos básicos de SQL Server (posteriormente veremos otros):
 varchar: se usa para almacenar cadenas de caracteres. Una cadena es una secuencia de
caracteres. Se coloca entre comillas (simples); ejemplo: 'Hola', 'Juan Perez'. El tipo
"varchar" define una cadena de longitud variable en la cual determinamos el máximo de
caracteres entre paréntesis. Puede guardar hasta 8000 caracteres. Por ejemplo, para
almacenar cadenas de hasta 30 caracteres, definimos un campo de tipo varchar(30), es
decir, entre paréntesis, junto al nombre del campo colocamos la longitud.
Si asignamos una cadena de caracteres de mayor longitud que la definida, la cadena no se
carga, aparece un mensaje indicando tal situación y la sentencia no se ejecuta.
Por ejemplo, si definimos un campo de tipo varchar(10) e intentamos asignarle la cadena
'Buenas tardes', aparece un mensaje de error y la sentencia no se ejecuta.
 integer: se usa para guardar valores numéricos enteros, de -2000000000 a 2000000000
aprox. Definimos campos de este tipo cuando queremos representar, por ejemplo,
cantidades.
 float: se usa para almacenar valores numéricos con decimales. Se utiliza como separador
el punto (.). Definimos campos de este tipo para precios, por ejemplo.
Antes de crear una tabla debemos pensar en sus campos y optar por el tipo de dato adecuado
para cada uno de ellos.
Por ejemplo, si en un campo almacenaremos números enteros, el tipo "float" sería una mala
elección; si vamos a guardar precios, el tipo "float" es más adecuado, no así "integer" que no
tiene decimales. Otro ejemplo, si en un campo vamos a guardar un número telefónico o un
número de documento, usamos "varchar", no "integer" porque si bien son dígitos, con ellos no
realizamos operaciones matemáticas.

Vamos a crear una tabla llamada "libros". En primer lugar vamos a eliminar la tabla "libros" averiguando si
existe:
if object_id('libros') is not null
drop table libros;

16
Para almacenar información de los libros de una librería necesitamos los siguientes campos:
-titulo, cadena de caracteres de 20 de longitud,
-autor, cadena de caracteres de 15 de longitud,
-editorial, caracteres de 10 de longitud,
-precio, valor numérico con decimales y
-cantidad, valor numérico entero.

Al crear la tabla, entonces, elegimos el tipo de dato más adecuado para cada campo:
create table libros(
titulo varchar(20),
autor varchar(15),
editorial varchar(10),
precio float,
cantidad integer
);

Note que al especificar el tipo de dato de los campos numéricos, no colocamos entre paréntesis la longitud.
Vemos la estructura de la tabla:
sp_columns libros;

Aparece la siguiente información:


...COLUMN_NAME TYPE_NAME LENGHT
_______________________________________
titulo varchar 20
autor varchar 15
editorial varchar 10
precio float 8
cantidad int 4

Ingresamos algunos registros:


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

Note que al ingresar valores numéricos (float e integer) no se utilizan comillas y para el separador de
decimales se usa el caracter punto(.).
Veamos los registros cargados:
select * from libros;

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

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


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

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


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

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


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

17
Veamos los registros cargados:
select * from libros;

problema:

Un videoclub que alquila películas en video almacena la información de sus películas en


una tabla
llamada "peliculas"; para cada película necesita los siguientes datos:
-nombre, cadena de caracteres de 20 de longitud,
-actor, cadena de caracteres de 20 de longitud,
-duración, valor numérico entero.
-cantidad de copias: valor entero.

1- Elimine la tabla, si existe:


if object_id('peliculas')is not null
drop table peliculas;

2- Cree la tabla eligiendo el tipo de dato adecuado para cada campo:


create table peliculas(
nombre varchar(20),
actor varchar(20),
duracion integer,
cantidad integer
);

3- Vea la estructura de la tabla:

4- Ingrese los siguientes registros:


insert into peliculas (nombre, actor, duracion, cantidad)
values ('Mision imposible','Tom Cruise',128,3);
insert into peliculas (nombre, actor, duracion, cantidad)
values ('Mision imposible 2','Tom Cruise',130,2);
insert into peliculas (nombre, actor, duracion, cantidad)
values ('Mujer bonita','Julia Roberts',118,3);
insert into peliculas (nombre, actor, duracion, cantidad)
values ('Elsa y Fred','China Zorrilla',110,2);

5- Muestre todos los registros.

Segundo problema:

Una empresa almacena los datos de sus empleados en una tabla "empleados" que guarda los
siguientes
datos: nombre, documento, sexo, domicilio, sueldobasico.

1- Elimine la tabla, si existe:


if object_id('empleados') is not null
drop table empleados;

2- Cree la tabla eligiendo el tipo de dato adecuado para cada campo:


create table empleados(
nombre varchar(20),
documento varchar(8),
sexo varchar(1),
domicilio varchar(30),
sueldobasico float
);

3- Vea la estructura de la tabla:

4- Ingrese algunos registros:


insert into empleados (nombre, documento, sexo, domicilio, sueldobasico)
18
values ('Juan Perez','22333444','m','Sarmiento 123',500);
insert into empleados (nombre, documento, sexo, domicilio, sueldobasico)
values ('Ana Acosta','24555666','f','Colon 134',650);
insert into empleados (nombre, documento, sexo, domicilio, sueldobasico)
values ('Bartolome Barrios','27888999','m','Urquiza 479',800);

5- Seleccione todos los registros.

5 - Recuperar algunos campos (select)


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

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


Podemos especificar el nombre de los campos que queremos ver separándolos por comas:
select titulo,autor from libros;

La lista de campos luego del "select" selecciona los datos correspondientes a los campos
nombrados. En el ejemplo anterior seleccionamos los campos "titulo" y "autor" de la tabla "libros",
mostrando todos los registros. Los datos aparecen ordenados según la lista de selección, en
dicha lista los nombres de los campos se separan con comas.
Trabajamos con la tabla "libros" que almacena los datos de los libros de una librería.
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
titulo varchar(40),
autor varchar(30),
editorial varchar(15),
precio float,
cantidad integer
);

Veamos la estructura de la tabla:


sp_columns libros;

Ingresamos algunos registros:


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

Veamos todos los campos la tabla:


select *from libros;

Veamos solamente el título, autor y editorial de todos los libros especificando los nombres de los campos
separados por comas:
select titulo,autor,editorial from libros;

Con la siguiente sentencia seleccionamos los títulos y precios de todos los libros:
select titulo,precio from libros;

Veamos solamente la editorial y la cantidad de libros tipeamos:


select editorial,cantidad from libros;

Primer problema:

Un videoclub que alquila películas en video almacena la información de sus películas en


alquiler en
una tabla llamada "peliculas".

1- Elimine la tabla, si existe:


if object_id('peliculas') is not null
drop table peliculas;

2- Cree la tabla:
create table peliculas(
titulo varchar(20),
actor varchar(20),
duracion integer,
cantidad integer
);

3- Vea la estructura de la tabla (sp_columns).

4- Ingrese alos siguientes registros:


insert into peliculas (titulo, actor, duracion, cantidad)
values ('Mision imposible','Tom Cruise',180,3);
insert into peliculas (titulo, actor, duracion, cantidad)
values ('Mision imposible 2','Tom Cruise',190,2);
insert into peliculas (titulo, actor, duracion, cantidad)
values ('Mujer bonita','Julia Roberts',118,3);
insert into peliculas (titulo, actor, duracion, cantidad)
values ('Elsa y Fred','China Zorrilla',110,2);

5- Realice un "select" mostrando solamente el título y actor de todas las películas

6- Muestre el título y duración de todas las peliculas

7- Muestre el título y la cantidad de copias

Segundo problema:

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

1- Elimine la tabla, si existe:


if object_id('empleados') is not null

20
drop table empleados;

2- Cree la tabla:
create table empleados(
nombre varchar(20),
documento varchar(8),
sexo varchar(1),
domicilio varchar(30),
sueldobasico float
);

3- Vea la estructura de la tabla:


sp_columns empleados;

4- Ingrese algunos registros:


insert into empleados (nombre, documento, sexo, domicilio, sueldobasico)
values ('Juan Juarez','22333444','m','Sarmiento 123',500);
insert into empleados (nombre, documento, sexo, domicilio, sueldobasico)
values ('Ana Acosta','27888999','f','Colon 134',700);
insert into empleados (nombre, documento, sexo, domicilio, sueldobasico)
values ('Carlos Caseres','31222333','m','Urquiza 479',850);

5- Muestre todos los datos de los empleados

6- Muestre el nombre, documento y domicilio de los empleados

7- Realice un "select" mostrando el documento, sexo y sueldo básico de todos los


empleados

6 - Recuperar algunos registros (where)


Hemos aprendido a seleccionar algunos campos de una tabla.
También es posible recuperar algunos registros.
Existe una cláusula, "where" con la cual podemos especificar condiciones para una consulta
"select". Es decir, podemos recuperar algunos registros, sólo los que cumplan con ciertas
condiciones indicadas con la cláusula "where". Por ejemplo, queremos ver el usuario cuyo
nombre es "Marcelo", para ello utilizamos "where" y luego de ella, la condición:
select nombre, clave
from usuarios
where nombre='Marcelo';

La sintaxis básica y general es la siguiente:


select NOMBRECAMPO1, ..., NOMBRECAMPOn
from NOMBRETABLA
where CONDICION;

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

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

Si ningún registro cumple la condición establecida con el "where", no aparecerá ningún registro.
Entonces, con "where" establecemos condiciones para recuperar algunos registros.
Para recuperar algunos campos de algunos registros combinamos en la consulta la lista de
campos y la cláusula "where":
select nombre
from usuarios
where clave='River';

En la consulta anterior solicitamos el nombre de todos los usuarios cuya clave sea igual a "River".
Trabajamos con la tabla "usuarios" que consta de 2 campos: nombre de usuario y clave.
Eliminamos la tabla, si existe:

if object_id('usuarios') is not null


drop table usuarios;

Creamos la tabla:
create table usuarios (
nombre varchar(30),
clave varchar(10)
);

Vemos la estructura de la tabla:


sp_columns usuarios;

Ingresamos algunos registros:


insert into usuarios (nombre, clave)
values ('Marcelo','Boca');
insert into usuarios (nombre, clave)
values ('JuanPerez','Juancito');
insert into usuarios (nombre, clave)
values ('Susana','River');
insert into usuarios (nombre, clave)
values ('Luis','River');

Realizamos una consulta especificando una condición, queremos ver el usuario cuyo nombre es "Leonardo":
select *from usuarios
where nombre='Leonardo';

Queremos ver el nombre de los usuarios cuya clave es "River":


select nombre from usuarios
where clave='River';

Realizamos un "select" de los nombres de los usuarios cuya clave es "Santi":


select nombre from usuarios
where clave='Santi';

22
No se muestra ningún registro ya que ninguno cumple la condición.
Primer problema:

Trabaje con la tabla "agenda" en la que registra los datos de sus amigos.

1- Elimine "agenda", si existe:


if object_id('agenda') is not null
drop table agenda;

2- Cree la tabla, con los siguientes campos: apellido (cadena de 30), nombre (cadena de
20),
domicilio (cadena de 30) y telefono (cadena de 11).

3- Visualice la estructura de la tabla "agenda".

4- Ingrese los siguientes registros:


Acosta, Ana, Colon 123, 4234567;
Bustamante, Betina, Avellaneda 135, 4458787;
Lopez, Hector, Salta 545, 4887788;
Lopez, Luis, Urquiza 333, 4545454;
Lopez, Marisa, Urquiza 333, 4545454.

5- Seleccione todos los registros de la tabla

6- Seleccione el registro cuyo nombre sea "Marisa" (1 registro)

7- Seleccione los nombres y domicilios de quienes tengan apellido igual a "Lopez" (3


registros)

8- Muestre el nombre de quienes tengan el teléfono "4545454" (2 registros)

Segundo problema:

Trabaje con la tabla "libros" de una librería que guarda información referente a sus
libros
disponibles para la venta.

1- Elimine la tabla si existe.

2- Cree la tabla "libros". Debe tener la siguiente estructura:


create table libros (
titulo varchar(20),
autor varchar(30),
editorial varchar(15));

3- Visualice la estructura de la tabla "libros".

4- Ingrese los siguientes registros:


El aleph,Borges,Emece;
Martin Fierro,Jose Hernandez,Emece;
Martin Fierro,Jose Hernandez,Planeta;
Aprenda PHP,Mario Molina,Siglo XXI;

5- Seleccione los registros cuyo autor sea "Borges" (1 registro)

6- Seleccione los títulos de los libros cuya editorial sea "Emece" (2 registros)

7- Seleccione los nombres de las editoriales de los libros cuyo titulo sea "Martin
Fierro" (2
registros)

23
7- Operadores relacionales
Los operadores son símbolos que permiten realizar operaciones matemáticas, concatenar
cadenas, hacer comparaciones.
SQL Server tiene 4 tipos de operadores:
1. relacionales (o de comparación)
2. aritméticos
3. de concatenación
4. lógicos.
Por ahora veremos solamente los primeros.
Los operadores relacionales (o de comparación) nos permiten comparar dos expresiones, que
pueden ser variables, valores de campos, etc.
Hemos aprendido a especificar condiciones de igualdad para seleccionar registros de una tabla;
por ejemplo:
select *from libros
where autor='Borges';

Utilizamos el operador relacional de igualdad.


Los operadores relacionales vinculan un campo con un valor para que 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
condición:
select * from libros
where autor<>'Borges';

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

Queremos seleccionar los libros cuyo precio sea menor o igual a 30:
select *from libros
where precio<=30;

24
Los operadores relacionales comparan valores del mismo tipo. Se emplean para comprobar si un
campo cumple con una condición.
No son los únicos, existen otros que veremos mas adelante.
Trabajamos con la tabla "libros" de una librería.

Eliminamos la tabla "libros", si existe:


if object_id('libros') is not null
drop table libros;

La creamos con la siguiente estructura:


create table libros(
titulo varchar(30),
autor varchar(30),
editorial varchar(15),
precio float
);

Agregamos registros a la tabla:


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

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


select *from libros
where autor<>'Borges';

Seleccionamos los registros cuyo precio supere los 20 pesos, sólo el título y precio:
select titulo,precio
from libros
where precio>20;

Note que el valor con el cual comparamos el campo "precio", como es numérico (float), no se coloca entre
comillas. Los libros cuyo precio es menor o igual a 20 pesos no aparece en la selección.
Recuperamos aquellos libros cuyo precio es menor o igual a 30:
select *from libros
where precio<=30;

Primer problema:

Un comercio que vende artículos de computación registra los datos de sus artículos en
una tabla con
ese nombre.

1- Elimine "articulos", si existe:


if object_id('articulos') is not null
drop table articulos;

25
2- Cree la tabla, con la siguiente estructura:
create table articulos(
codigo integer,
nombre varchar(20),
descripcion varchar(30),
precio float,
cantidad integer
);

3- Vea la estructura de la tabla (sp_columns).

4- Ingrese algunos registros:


insert into articulos (codigo, nombre, descripcion, precio,cantidad)
values (1,'impresora','Epson Stylus C45',400.80,20);
insert into articulos (codigo, nombre, descripcion, precio,cantidad)
values (2,'impresora','Epson Stylus C85',500,30);
insert into articulos (codigo, nombre, descripcion, precio,cantidad)
values (3,'monitor','Samsung 14',800,10);
insert into articulos (codigo, nombre, descripcion, precio,cantidad)
values (4,'teclado','ingles Biswal',100,50);
insert into articulos (codigo, nombre, descripcion, precio,cantidad)
values (5,'teclado','español Biswal',90,50);

5- Seleccione los datos de las impresoras (2 registros)

6- Seleccione los artículos cuyo precio sea mayor o igual a 400 (3 registros)

7- Seleccione el código y nombre de los artículos cuya cantidad sea menor a 30 (2


registros)

8- Selecciones el nombre y descripción de los artículos que NO cuesten $100 (4


registros)

Segundo problema:

Un video club que alquila películas en video almacena la información de sus películas
en alquiler en
una tabla denominada "peliculas".

1- Elimine la tabla, si existe.

2- Cree la tabla eligiendo el tipo de dato adecuado para cada campo:


create table peliculas(
titulo varchar(20),
actor varchar(20),
duracion integer,
cantidad integer
);

3- Ingrese los siguientes registros:


insert into peliculas (titulo, actor, duracion, cantidad)
values ('Mision imposible','Tom Cruise',120,3);
insert into peliculas (titulo, actor, duracion, cantidad)
values ('Mision imposible 2','Tom Cruise',180,4);
insert into peliculas (titulo, actor, duracion, cantidad)
values ('Mujer bonita','Julia R.',90,1);
insert into peliculas (titulo, actor, duracion, cantidad)
values ('Elsa y Fred','China Zorrilla',80,2);

4- Seleccione las películas cuya duración no supere los 90 minutos (2 registros)

5- Seleccione el título de todas las películas en las que el actor NO sea "Tom Cruise"
(2
registros)
26
6- Muestre todos los campos, excepto "duracion", de todas las películas de las que haya
más de 2
copias (2 registros)

8 - Borrar registros (delete)


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 cuál o
cuáles, para ello utilizamos el comando "delete" junto con la clausula "where" con la cual
establecemos la condición que deben cumplir los registros a borrar.
Por ejemplo, queremos eliminar aquel registro cuyo nombre de usuario es "Marcelo":
delete from usuarios
where nombre='Marcelo';

Si solicitamos el borrado de un registro que no existe, es decir, ningún registro cumple con la
condición especificada, ningún registro será eliminado.
Tenga en cuenta que si no colocamos una condición, se eliminan todos los registros de la tabla
nombrada.
Trabajamos con la tabla "usuarios".

Eliminamos la tabla "usuarios", si existe:


if object_id('usuarios') is not null
drop table usuarios;

La creamos con la siguiente estructura:


create table usuarios(
nombre varchar(30),
clave varchar(10)
);

Agregamos registros a la tabla:


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

Seleccionamos todos los registros:


27
select *from usuarios;

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


delete from usuarios
where nombre='Marcelo';

Veamos el contenido de la tabla:


select * from usuarios;

Intentamos eliminarlo nuevamente:


delete from usuarios
where nombre='Marcelo';

Veamos el contenido de la tabla:


select * from usuarios;

Como ningún registro cumple con la condición no se borran registros.


Eliminamos todos los registros cuya clave es 'Boca':
delete from usuarios
where clave='Boca';

Veamos el contenido de la tabla:


select * from usuarios;

Eliminemos todos los registros:


delete from usuarios;

Veamos el contenido de la tabla:


select * from usuarios;

No hay registros.
Primer problema:

Trabaje con la tabla "agenda" que registra la información referente a sus amigos.

1- Elimine la tabla si existe:


if object_id('agenda') is not null
drop table agenda;

2- Cree la tabla con los siguientes campos: apellido (cadena de 30), nombre (cadena de
20),
domicilio (cadena de 30) y telefono (cadena de 11):
create table agenda(
apellido varchar(30),
nombre varchar(20),
domicilio varchar(30),
telefono varchar(11)
);

3- Ingrese los siguientes registros (insert into):


Alvarez,Alberto,Colon 123,4234567,

28
Juarez,Juan,Avellaneda 135,4458787,
Lopez,Maria,Urquiza 333,4545454,
Lopez,Jose,Urquiza 333,4545454,
Salas,Susana,Gral. Paz 1234,4123456.

4- Elimine el registro cuyo nombre sea "Juan" (1 registro afectado)

5- Elimine los registros cuyo número telefónico sea igual a "4545454" (2 registros
afectados):

6- Muestre la tabla.

7- Elimine todos los registros (2 registros afectados):

8- Muestre la tabla.

Segundo problema:

Un comercio que vende artículos de computación registra los datos de sus artículos en
una tabla con
ese nombre.

1- Elimine "articulos", si existe:


if object_id('articulos') is not null
drop table articulos;

2- Cree la tabla, con la siguiente estructura:


create table articulos(
codigo integer,
nombre varchar(20),
descripcion varchar(30),
precio float,
cantidad integer
);

3- Vea la estructura de la tabla.

4- Ingrese algunos registros:


insert into articulos (codigo, nombre, descripcion, precio,cantidad)
values (1,'impresora','Epson Stylus C45',400.80,20);
insert into articulos (codigo, nombre, descripcion, precio,cantidad)
values (2,'impresora','Epson Stylus C85',500,30);
insert into articulos (codigo, nombre, descripcion, precio,cantidad)
values (3,'monitor','Samsung 14',800,10);
insert into articulos (codigo, nombre, descripcion, precio,cantidad)
values (4,'teclado','ingles Biswal',100,50);
insert into articulos (codigo, nombre, descripcion, precio,cantidad)
values (5,'teclado','español Biswal',90,50);

5- Elimine los artículos cuyo precio sea mayor o igual a 500 (2 registros)
delete from articulos
where precio>=500;

7- Elimine todas las impresoras (1 registro)


delete from articulos
where nombre='impresora';

8- Elimine todos los artículos cuyo código sea diferente a 4 (1 registro)


delete from articulos
where codigo<>4;

9- Mostrar la tabla después que borra cada registro.

29
9 - Actualizar registros (update)
Decimos que actualizamos un registro cuando modificamos alguno de sus valores.
Para modificar uno o varios datos de uno o varios registros utilizamos "update" (actualizar).
Por ejemplo, en nuestra tabla "usuarios", queremos cambiar los valores de todas las claves, por
"RealMadrid":
update usuarios set clave='RealMadrid';

Utilizamos "update" junto al nombre de la tabla y "set" junto con el campo a modificar y su nuevo
valor.
El cambio afectará a todos los registros.
Podemos modificar algunos registros, para ello debemos establecer condiciones de selección con
"where".
Por ejemplo, queremos cambiar el valor correspondiente a la clave de nuestro usuario llamado
"Federicolopez", queremos como nueva clave "Boca", necesitamos una condición "where" que
afecte solamente a este registro:
update usuarios set clave='Boca'
where nombre='Federicolopez';

Si Microsoft SQL Server no encuentra registros que cumplan con la condición del "where", no se
modifica ninguno.
Las condiciones no son obligatorias, pero si omitimos la cláusula "where", la actualización
afectará a todos los registros.
También podemos actualizar varios campos en una sola instrucción:
update usuarios set nombre='Marceloduarte', clave='Marce'
where nombre='Marcelo';

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

if object_id('usuarios') is not null


drop table usuarios;

Creamos la tabla:
create table usuarios(
nombre varchar(20),
clave varchar(10)
);

30
Ingresamos algunos registros:
insert into usuarios (nombre,clave)
values ('Marcelo','River');
insert into usuarios (nombre,clave)
values ('Susana','chapita');
insert into usuarios (nombre,clave)
values ('Carlosfuentes','Boca');
insert into usuarios (nombre,clave)
values ('Federicolopez','Boca');

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


update usuarios set clave='RealMadrid';

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


select *from usuarios;

Necesitamos cambiar el valor de la clave del usuario llamado "Federicolopez" por "Boca":
update usuarios set clave='Boca'
where nombre='Federicolopez';

Verifiquemos que la actualización se realizó:


select *from usuarios;

Vimos que si Microsoft SQL Server no encuentra registros que cumplan con la condición no se modifican
registros:
update usuarios set clave='payaso'
where nombre='JuanaJuarez';

Si vemos la tabla veremos que no a cambiado:


select *from usuarios;

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


update usuarios set nombre='Marceloduarte', clave='Marce'
where nombre='Marcelo';

Si vemos la tabla:
select *from usuarios;

Primer problema:

Trabaje con la tabla "agenda" que almacena los datos de sus amigos.

1- Elimine la tabla si existe:


if object_id('agenda') is not null
drop table agenda;

2- Cree la tabla:
create table agenda(
apellido varchar(30),
nombre varchar(20),
domicilio varchar(30),
telefono varchar(11)

31
);

3- Ingrese los siguientes registros (1 registro actualizado):


insert into agenda (apellido,nombre,domicilio,telefono)
values ('Acosta','Alberto','Colon 123','4234567');
insert into agenda (apellido,nombre,domicilio,telefono)
values ('Juarez','Juan','Avellaneda 135','4458787');
insert into agenda (apellido,nombre,domicilio,telefono)
values ('Lopez','Maria','Urquiza 333','4545454');
insert into agenda (apellido,nombre,domicilio,telefono)
values ('Lopez','Jose','Urquiza 333','4545454');
insert into agenda (apellido,nombre,domicilio,telefono)
values ('Suarez','Susana','Gral. Paz 1234','4123456');

4- Modifique el registro cuyo nombre sea "Juan" por "Juan Jose" (1 registro afectado)

5- Actualice los registros cuyo número telefónico sea igual a "4545454" por "4445566"
(2 registros afectados)

6- Actualice los registros que tengan en el campo "nombre" el valor "Juan" por "Juan
Jose" (ningún
registro afectado porque ninguno cumple con la condición del "where")

7 - Luego de cada actualización ejecute un select que muestre todos los registros de la
tabla.

Segundo problema:

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

1- Elimine la tabla si existe:


if object_id('libros') is not null
drop table libros;

2- Créela con los siguientes campos: titulo (cadena de 30 caracteres de longitud),


autor (cadena de
20), editorial (cadena de 15) y precio (float):
create table libros (
titulo varchar(30),
autor varchar(20),
editorial varchar(15),
precio float
);

3- Ingrese los siguientes registros:


insert into libros (titulo, autor, editorial, precio)
values ('El aleph','Borges','Emece',25.00);
insert into libros (titulo, autor, editorial, precio)
values ('Martin Fierro','Jose Hernandez','Planeta',35.50);
insert into libros (titulo, autor, editorial, precio)
values ('Aprenda PHP','Mario Molina','Emece',45.50);
insert into libros (titulo, autor, editorial, precio)
values ('Cervantes y el quijote','Borges','Emece',25);
insert into libros (titulo, autor, editorial, precio)
values ('Matematica estas ahi','Paenza','Siglo XXI',15);

4- Muestre todos los registros (5 registros):


select *from libros;

5- Modifique los registros cuyo autor sea igual a "Paenza", por "Adrian Paenza" (1
registro
afectado)
update libros set autor='Adrian Paenza'
where autor='Paenza';
32
6- Nuevamente, modifique los registros cuyo autor sea igual a "Paenza", por "Adrian
Paenza" (ningún
registro afectado porque ninguno cumple la condición)
update libros set autor='Adrian Paenza'
where autor='Paenza';

7- Actualice el precio del libro de "Mario Molina" a 27 pesos (1 registro afectado):


update libros set precio=27
where autor='Mario Molina';

8- Actualice el valor del campo "editorial" por "Emece S.A.", para todos los registros
cuya
editorial sea igual a "Emece" (3 registros afectados):
update libros set editorial='Emece S.A.'
where editorial='Emece';

9 - Luego de cada actualización ejecute un select que mustre todos los registros de la
tabla.

10 - Comentarios
Para aclarar algunas instrucciones, en ocasiones, necesitamos agregar comentarios.
Es posible ingresar comentarios en la línea de comandos, es decir, un texto que no se ejecuta;
para ello se emplean dos guiones (--) al comienzo de la línea:
select * from libros --mostramos los registros de libros;

en la línea anterior, todo lo que está luego de los guiones (hacia la derecha) no se ejecuta.
Para agregar varias líneas de comentarios, se coloca una barra seguida de un asterisco (/*) al
comienzo del bloque de comentario y al finalizarlo, un asterisco seguido de una barra (*/).
select titulo, autor
/*mostramos títulos y
nombres de los autores*/
from libros;

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


Problema:

Trabajamos con la tabla "libros" que almacena los datos de los libros de una librería.
Eliminamos la tabla, si existe:
if object_id('libros') is not null
drop table libros;

Creamos la tabla:
create table libros(
titulo varchar(30),
autor varchar(20),
editorial varchar(15)
);
33
Agregamos un registro:
insert into libros (titulo,autor,editorial)
values ('El aleph','Borges','Emece');

Mostramos todos los libros y agregamos un comentario de linea:


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

vamos a mostrar el título y autor de todos los libros y agregamos un bloque de comentarios:
select titulo, autor
/*mostramos títulos y
nombres de los autores*/
from libros;

Note que lo que está entre los símbolos no se ejecuta.

11 - Valores null (is null)


"null" significa "dato desconocido" o "valor inexistente". No es lo mismo que un valor "0", una
cadena vacía o una cadena literal "null".
A veces, puede desconocerse o no existir el dato correspondiente a algún campo de un registro.
En estos casos decimos que el campo puede contener valores nulos.
Por ejemplo, en nuestra tabla de libros, podemos tener valores nulos en el campo "precio" porque
es posible que para algunos libros no le hayamos establecido el precio para la venta.
En contraposición, tenemos campos que no pueden estar vacíos jamás.
Veamos un ejemplo. Tenemos nuestra tabla "libros". El campo "titulo" no debería estar vacío
nunca, igualmente el campo "autor". Para ello, al crear la tabla, debemos especificar que dichos
campos no admitan valores nulos:
create table libros(
titulo varchar(30) not null,
autor varchar(20) not null,
editorial varchar(15) null,
precio float
);

Para especificar que un campo no admita valores nulos, debemos colocar "not null" luego de la
definición del campo.
En el ejemplo anterior, los campos "editorial" y "precio" si admiten valores nulos.
Cuando colocamos "null" estamos diciendo que admite valores nulos (caso del campo "editorial");
por defecto, es decir, si no lo aclaramos, los campos permiten valores nulos (caso del campo
"precio").
Si ingresamos los datos de un libro, para el cual aún no hemos definido el precio podemos
colocar "null" para mostrar que no tiene precio:

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

Note que el valor "null" no es una cadena de caracteres, no se coloca entre comillas.
Entonces, si un campo acepta valores nulos, podemos ingresar "null" cuando no conocemos el
valor.
También podemos colocar "null" en el campo "editorial" si desconocemos el nombre de la editorial
a la cual pertenece el libro que vamos a ingresar:
insert into libros (titulo,autor,editorial,precio)
values('Alicia en el pais','Lewis Carroll',null,25);

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 inserción no se realiza; por ejemplo:
insert into libros (titulo,autor,editorial,precio)
values(null,'Borges','Siglo XXI',25);

Para ver cuáles campos admiten valores nulos y cuáles no, podemos emplear el procedimiento
almacenado "sp_columns" junto al nombre de la tabla. Nos muestra mucha información, 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 algún campo, no podemos utilizar los
operadores relacionales vistos anteriormente: = (igual) y <> (distinto); debemos utilizar los
operadores "is null" (es igual a null) y "is not null" (no es null):
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 vacía.
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".

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


Eliminamos la tabla "libros", si existe:
35
if object_id('libros') is not null
drop table libros;

Creamos la tabla especificando que los campos "titulo" y "autor" no admitan valores nulos:
create table libros(
titulo varchar(30) not null,
autor varchar(30) not null,
editorial varchar(15) null,
precio float
);

Los campos "editorial" y "precio" si permiten valores nulos; el primero, porque lo especificamos colocando
"null" en la definición del campo, el segundo lo asume por defecto.
Agregamos un registro a la tabla con valor nulo para el campo "precio":
insert into libros (titulo,autor,editorial,precio)
values('El aleph','Borges','Emece',null);

Recuerde que el valor "null" no es una cadena de caracteres, por lo tanto no se coloca entre comillas.
Ingresamos otro registro, con valor nulo para el campo "editorial", campo que admite valores "null":
insert into libros (titulo,autor,editorial,precio)
values('Alicia en el pais','Lewis Carroll',null,0);

Veamos lo que sucede si intentamos ingresar el valor "null" en campos que no lo admiten, como "titulo":
insert into libros (titulo,autor,editorial,precio)
values(null,'Borges','Siglo XXI',25);

aparece un mensaje y la sentencia no se ejecuta.


Para ver cuáles campos admiten valores nulos y cuáles no, empleamos el procedimiento almacenado
"sp_columns":
sp_columns libros;

nos muestra muchas columnas, una de ellas tiene el encabezado "IS_NULLABLE", vemos que aparece
"NO" en los campos que no permiten valores nulos y "YES" en los campos que si los permiten.
Dijimos que el valor "null" no es lo mismo que una cadena vacía. Vamos a ingresar un registro con cadena
vacía para el campo "editorial":
insert into libros (titulo,autor,editorial,precio)
values('Uno','Richard Bach','',18.50);

Ingresamos otro registro, ahora cargamos una cadena vacía en el campo "titulo":
insert into libros (titulo,autor,editorial,precio)
values('','Richard Bach','Planeta',22);

Veamos todos los regiustros ingresados:


select *from libros;

Recuperemos los registros que contengan el valor "null" en el campo "precio":


select *from libros

36
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.
Recuperemos los libros cuyo nombre de editorial es "null":
select *from libros
where editorial is null;

Ahora veamos los libros cuya editorial almacena una cadena vacía:
select *from libros
where editorial='';

Para recuperar los libros cuyo precio no sea nulo tipeamos:


select *from libros
where precio is not null;

Primer problema:

Una farmacia guarda información referente a sus medicamentos en una tabla llamada
"medicamentos".
1- Elimine la tabla, si existe:
if object_id('medicamentos') is not null
drop table medicamentos;

2- Cree la tabla con la siguiente estructura:


create table medicamentos(
codigo integer not null,
nombre varchar(20) not null,
laboratorio varchar(20),
precio float,
cantidad integer not null
);

3- Visualice la estructura de la tabla "medicamentos":


sp_columns medicamentos;
note que los campos "codigo", "nombre" y "cantidad", en la columna "IS_NULLABLE"
aparece "NO" y en
las otras "YES".

4- Ingrese algunos registros con valores "null" para los campos que lo admitan:
insert into medicamentos (codigo,nombre,laboratorio,precio,cantidad)
values(1,'Sertal gotas',null,null,100);
insert into medicamentos (codigo,nombre,laboratorio,precio,cantidad)
values(2,'Sertal compuesto',null,8.90,150);
insert into medicamentos (codigo,nombre,laboratorio,precio,cantidad)
values(3,'Buscapina','Roche',null,200);

5- Vea todos los registros:


select *from medicamentos;

6- Ingrese un registro con valor "0" para el precio y cadena vacía para el laboratorio:
insert into medicamentos (codigo,nombre, laboratorio,precio,cantidad)
values(4,'Bayaspirina','',0,150);

37
7- Ingrese un registro con valor "0" para el código y cantidad y cadena vacía para el
nombre:
insert into medicamentos (codigo,nombre,laboratorio,precio,cantidad)
values(0,'','Bayer',15.60,0);

8- Muestre todos los registros:


select *from medicamentos;

9- Intente ingresar un registro con valor nulo para un campo que no lo admite (aparece
un mensaje de
error):
insert into medicamentos (codigo,nombre,laboratorio,precio,cantidad)
values(null,'Amoxidal jarabe','Bayer',25,120);

10- Recupere los registros que contengan valor "null" en el campo "laboratorio", luego
los que
tengan una cadena vacía en el mismo campo. Note que el resultado es diferente.

11- Recupere los registros que contengan valor "null" en el campo "precio", luego los
que tengan el
valor 0 en el mismo campo. Note que el resultado es distinto.

12- Recupere los registros cuyo laboratorio no contenga una cadena vacía, luego los que
sean
distintos de "null".
Note que la salida de la primera sentencia no muestra los registros con cadenas vacías
y tampoco los
que tienen valor nulo; el resultado de la segunda sentencia muestra los registros con
valor para el
campo laboratorio (incluso cadena vacía).

13- Recupere los registros cuyo precio sea distinto de 0, luego los que sean distintos
de "null":
Note que la salida de la primera sentencia no muestra los registros con valor 0 y
tampoco los que
tienen valor nulo; el resultado de la segunda sentencia muestra los registros con valor
para el
campo precio (incluso el valor 0).

Segundo problema:

Trabaje con la tabla que almacena los datos sobre películas, llamada "peliculas".
1- Elimine la tabla si existe:
if object_id('peliculas') is not null
drop table peliculas;

2- Créela con la siguiente estructura:


create table peliculas(
codigo int not null,
titulo varchar(40) not null,
actor varchar(20),
duracion int
);

3- Visualice la estructura de la tabla


sp_columns peliculas;
note que el campo "codigo" y "titulo", en la columna "IS_NULLABLE" muestra "NO" y los
otros campos "YES".

4- Ingrese los siguientes registros:


insert into peliculas (codigo,titulo,actor,duracion)
values(1,'Mision imposible','Tom Cruise',120);
insert into peliculas (codigo,titulo,actor,duracion)
38
values(2,'Harry Potter y la piedra filosofal',null,180);
insert into peliculas (codigo,titulo,actor,duracion)
values(3,'Harry Potter y la camara secreta','Daniel R.',null);
insert into peliculas (codigo,titulo,actor,duracion)
values(0,'Mision imposible 2','',150);
insert into peliculas (codigo,titulo,actor,duracion)
values(4,'','L. Di Caprio',220);
insert into peliculas (codigo,titulo,actor,duracion)
values(5,'Mujer bonita','R. Gere-J. Roberts',0);

5- Recupere todos los registros para ver cómo SQL Server los almacenó:
select *from peliculas;

6- Intente ingresar un registro con valor nulo para campos que no lo admiten (aparece
un mensaje de
error):
insert into peliculas (codigo,titulo,actor,duracion)
values(null,'Mujer bonita','R. Gere-J. Roberts',190);

7- Muestre los registros con valor nulo en el campo "actor" y luego los que guardan una
cadena vacía
(note que la salida es distinta) (1 registro)

8- Modifique los registros que tengan valor de duración desconocido (nulo) por "120" (1
registro
actualizado)

9- Coloque 'Desconocido' en el campo "actor" en los registros que tengan una cadena
vacía en dicho
campo (1 registro afectado)

10- Muestre todos los registros. Note que el cambio anterior no afectó a los registros
con valor
nulo en el campo "actor".

11- Elimine los registros cuyo título sea una cadena vacía (1 registro)

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 número de documento
puede establecerse como clave primaria, es un valor que no se repite; puede haber personas con
igual apellido y nombre, incluso el mismo domicilio (padre e hijo por ejemplo), pero su documento
será siempre distinto.
Si tenemos la tabla "usuarios", el nombre de cada usuario puede establecerse como clave
primaria, es un valor que no se repite; puede haber usuarios con igual clave, pero su nombre de
usuario será siempre diferente.
Podemos establecer que un campo sea clave primaria al momento de crear la tabla o luego que
ha sido creada. Vamos a aprender a establecerla al crear la tabla. Hay 2 maneras de hacerlo, por
ahora veremos la sintaxis más sencilla.
39
Tenemos nuestra tabla "usuarios" definida con 2 campos ("nombre" y "clave").
La sintaxis básica y general es la siguiente:
create table NOMBRETABLA(
CAMPO TIPO,
...
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 definición de cada campo, "primary key" y entre paréntesis,
el nombre del campo que será clave primaria.
Una tabla sólo puede tener una clave primaria. Cualquier campo (de cualquier tipo) puede ser
clave primaria, debe cumplir como requisito, que sus valores no se repitan ni sean nulos. Por ello,
al definir un campo como clave primaria, automáticamente SQL Server lo convierte a "not null".
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 estén repetidos en la
tabla; si estuviesen repetidos, muestra un mensaje y la inserción no se realiza. Es decir, si en
nuestra tabla "usuarios" ya existe un usuario con nombre "juanperez" e intentamos ingresar un
nuevo usuario con nombre "juanperez", aparece un mensaje y la instrucción "insert" no se
ejecuta.
Igualmente, si realizamos una actualización, SQL Server controla que los valores para el campo
establecido como clave primaria no estén repetidos en la tabla, si lo estuviese, aparece un
mensaje indicando que se viola la clave primaria y la actualización no se realiza.
Trabajamos con la tabla "usuarios".
Eliminamos la tabla, si existe:

if object_id('usuarios') is not null


drop table usuarios;

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


create table usuarios(
nombre varchar(20),
clave varchar(10),
primary key(nombre)
);

Al campo "nombre" no lo definimos "not null", pero al establecerse como clave primaria, SQL Server lo
convierte en "not null", veamos que en la columna "IS_NULLABLE" aparece "NO":
sp_columns usuarios;

Ingresamos algunos registros:

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

Recordemos que cuando un campo es clave primaria, sus valores no se repiten. Intentamos ingresar un valor
de clave primaria existente:
insert into usuarios (nombre, clave)
values ('juanperez','payaso');

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


Cuando un campo es clave primaria, sus valores no pueden ser nulos. Intentamos ingresar el valor "null" en
el campo clave primaria:
insert into usuarios (nombre, clave)
values (null,'payaso');

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


Si realizamos alguna actualización, SQL Server controla que los valores para el campo establecido como
clave primaria no estén repetidos en la tabla. Intentemos actualizar el nombre de un usuario colocando un
nombre existente:
update usuarios set nombre='juanperez'
where nombre='raulgarcia';

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


Primer problema:

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


1- Elimine la tabla si existe:
if object_id('libros') is not null
drop table libros;

2- Créela con los siguientes campos, estableciendo como clave primaria el campo
"codigo":
create table libros(
codigo int not null,
titulo varchar(40) not null,
autor varchar(20),
editorial varchar(15),
primary key(codigo)
);

3- Ingrese los siguientes registros:


insert into libros (codigo,titulo,autor,editorial)
values (1,'El aleph','Borges','Emece');
insert into libros (codigo,titulo,autor,editorial)
values (2,'Martin Fierro','Jose Hernandez','Planeta');
insert into libros (codigo,titulo,autor,editorial)
values (3,'Aprenda PHP','Mario Molina','Nuevo Siglo');

4- Ingrese un registro con código repetido (aparece un mensaje de error)

5- Intente ingresar el valor "null" en el campo "codigo"

6- Intente actualizar el código del libro "Martin Fierro" a "1" (mensaje de error)

41
Segundo problema:

Un instituto de enseñanza almacena los datos de sus estudiantes en una tabla llamada
"alumnos".
1- Elimine la tabla "alumnos" si existe:
if object_id('alumnos') is not null
drop table alumnos;

2- Cree la tabla con la siguiente estructura intentando establecer 2 campos como clave
primaria, el
campo "documento" y "legajo" (no lo permite):
create table alumnos(
legajo varchar(4) not null,
documento varchar(8),
nombre varchar(30),
domicilio varchar(30),
primary key(documento),
primary key(legajo)
);

3- Cree la tabla estableciendo como clave primaria el campo "documento":


create table alumnos(
legajo varchar(4) not null,
documento varchar(8),
nombre varchar(30),
domicilio varchar(30),
primary key(documento)
);

4- Verifique que el campo "documento" no admite valores nulos:


sp_columns alumnos;

5- Ingrese los siguientes registros:


insert into alumnos (legajo,documento,nombre,domicilio)
values('A233','22345345','Perez Mariana','Colon 234');
insert into alumnos (legajo,documento,nombre,domicilio)
values('A567','23545345','Morales Marcos','Avellaneda 348');

6- Intente ingresar un alumno con número de documento existente (no lo permite)

7- Intente ingresar un alumno con documento nulo (no lo permite)

13 - Campo con atributo Identity


Un campo numérico puede tener un atributo extra "identity". Los valores de un campo con este
atributo genera valores secuenciales que se inician en 1 y se incrementan en 1
automáticamente.
Se utiliza generalmente en campos correspondientes a códigos de identificación para generar
valores únicos para cada nuevo registro que se inserta.
Sólo puede haber un campo "identity" por tabla.
Para que un campo pueda establecerse como "identity", éste debe ser entero (también puede ser
de un subtipo de entero o decimal con escala 0, tipos que estudiaremos posteriormente).
42
Para que un campo genere sus valores automáticamente, debemos agregar el atributo "identity"
luego de su definición al crear la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(30),
editorial varchar(15),
precio float
);

Cuando un campo tiene el atributo "identity" no se puede ingresar valor para él, porque se inserta
automáticamente tomando el último valor como referencia, o 1 si es el primero.
Para ingresar registros omitimos el campo definido como "identity", por ejemplo:
insert into libros (titulo,autor,editorial,precio)
values('El aleph','Borges','Emece',23);

Este primer registro ingresado guardará el valor 1 en el campo correspondiente al código.


Si continuamos ingresando registros, el código (dato que no ingresamos) se cargará
automáticamente 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".
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla "libros", si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla especificando que el campos "codigo" genere valores secuenciales comenzando en 1 e
incrementándose en 1 automáticamente:
create table libros(
codigo int identity,
titulo varchar(40) not null,

43
autor varchar(30),
editorial varchar(15),
precio float
);

Ingresamos algunos registros, recordando que si un campo tiene el atributo "identity" debemos omitirlo en la
inserción:
insert into libros (titulo,autor,editorial,precio)
values('El aleph','Borges','Emece',23);

Veamos cómo se almacenó:


select *from libros;

Este primer registro ingresado guarda el valor 1 en el campo correspondiente al código.


Continuemos ingresando registros:
insert into libros (titulo,autor,editorial,precio)
values('Uno','Richard Bach','Planeta',18);
insert into libros (titulo,autor,editorial,precio)
values('Aprenda PHP','Mario Molina','Siglo XXI',45.60);
insert into libros (titulo,autor,editorial,precio)
values('Alicia en el pais de maravillas','Lewis Carroll','Paidos',15.50);

Veamos cómo se almacenaron:


select *from libros;

el código (dato que no ingresamos) se cargó automáticamente siguiendo la secuencia de autoincremento.


Intentemos ingresar un valor para el campo "codigo":
insert into libros (codigo,titulo,autor,editorial,precio)
values(5,'Martin Fierro','Jose Hernandez','Paidos',25);

generará un mensaje de error.


Un campo "identity" tampoco puede ser actualizado. Intentemos cambiar el valor de código de un registro:
update libros set codigo=9
where titulo='Uno';

aparece un mensaje de error.


Vamos a ver la estructura de la tabla ejecutando el siguiente procedimiento almacenado:
sp_columns libros;

Note que en el campo "codigo", en la columna "TYPE_NAME" aparece "int identity" y en la columna
IS_NULLABLE" aparece "NO", porque un campo "identity" automáticamente se convierte en "not null". En
el campo "titulo", en la columna "IS_NULLABLE" aparece "NO" porque explícitamente indicamos que el
campo fuera "not null".
Eliminemos el último registro:
delete from libros
where autor='Lewis Carroll';

44
Ingresamos un quinto registro y luego vemos que en el campo código se guardó el valor secuencial sin
considerar que el valor "4" ya no existe:
insert into libros (titulo, autor, editorial, precio)
values('Martin Fierro','Jose Hernandez','Paidos',25);
select *from libros;

Primer problema:

Una farmacia guarda información referente a sus medicamentos en una tabla llamada
"medicamentos".
1- Elimine la tabla,si existe:
if object_id('medicamentos') is not null
drop table medicamentos;

2- Cree la tabla con un campo "codigo" que genere valores secuenciales automáticamente:
create table medicamentos(
codigo int identity,
nombre varchar(20) not null,
laboratorio varchar(20),
precio float,
cantidad integer
);

3- Visualice la estructura de la tabla "medicamentos":


sp_columns medicamentos;

4- Ingrese los siguientes registros:


insert into medicamentos (nombre, laboratorio,precio,cantidad)
values('Sertal','Roche',5.2,100);
insert into medicamentos (nombre, laboratorio,precio,cantidad)
values('Buscapina','Roche',4.10,200);
insert into medicamentos (nombre, laboratorio,precio,cantidad)
values('Amoxidal 500','Bayer',15.60,100);

5- Verifique que SQL Server generó valores para el campo "código" de modo automático:
select *from medicamentos;

6- Intente ingresar un registro con un valor para el campo "codigo"

7- Intente actualizar un valor de código (aparece un mensaje de error)

8- Elimine el registro con codigo "3" (1 registro eliminado)

9- Ingrese un nuevo registro:


insert into medicamentos (nombre, laboratorio,precio,cantidad)
values('Amoxilina 500','Bayer',15.60,100);

10- Seleccione todos los registros para ver qué valor guardó SQL Server en el campo
código:
select *from medicamentos;

14 - Otras características del atributo Identity


El atributo "identity" permite indicar el valor de inicio de la secuencia y el incremento, para ello
usamos la siguiente sintaxis:
45
create table libros(
codigo int identity(100,2),
titulo varchar(20),
autor varchar(30),
precio float
);

Los valores comenzarán en "100" y se incrementarán de 2 en 2; es decir, el primer registro


ingresado tendrá el valor "100", los siguientes "102", "104", "106", etc.
La función "ident_seed()" retorna el valor de inicio del campo "identity" de la tabla que
nombramos:
select ident_seed('libros');

La función "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 explícitamente un valor.
Para permitir ingresar un valor en un campo de identidad se debe activar la opción
"identity_insert":
set identity_insert libros on;

Es decir, podemos ingresar valor en un campo "identity" seteando la opción "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 repetición de valores; por ello hay que
tener cuidado al explicitar un valor porque se puede ingresar un valor repetido.
Para desactivar la opción "identity_insert" tipeamos:
set identity_insert libros off;

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


Eliminamos la tabla "libros", si existe:
if object_id('libros') is not null
drop table libros;

Creamos la tabla especificando que el campos "codigo" genere valores secuenciales comenzando en 100 e
incrementándose en 2 automáticamente:
create table libros(
codigo int identity(100,2),
titulo varchar(20),

46
autor varchar(30),
precio float
);

Ingresamos algunos registros, recordando que si un campo tiene el atributo "identity" debemos omitirlo en la
inserción:
insert into libros (titulo,autor,precio)
values('El aleph','Borges',23);
insert into libros (titulo,autor,precio)
values('Uno','Richard Bach',18);
insert into libros (titulo,autor,precio)
values('Aprenda PHP','Mario Molina',45.60);

Veamos cómo se almacenaron:


select *from libros;

el código (dato que no ingresamos) se cargó automáticamente, iniciándose en 100 y siguiendo la secuencia
de autoincremento (2).
Para saber cuál es el valor de inicio del campo "identity" de la tabla "libros" tipeamos:
select ident_seed('libros');

retorna "2".
Si intentamos ingresar un valor para el campo "codigo":
insert into libros (codigo,titulo,autor,precio)
values(106,'Martin Fierro','Jose Hernandez',25);

generará un mensaje de error.


Para permitir ingresar un valor en un campo de identidad activamos la opción "identity_insert":
set identity_insert libros on;

Recordemos que si "identity_insert" está en ON, la instrucción "insert" DEBE explicitar un valor:
insert into libros (codigo,titulo,autor)
values (100,'Matematica estas ahi','Paenza');

Note que ingresamos un valor de código que ya existe; esto está permitido porque el atributo "identity" no
implica unicidad.
Ingresamos otro registro:
insert into libros (codigo,titulo,autor)
values (1,'Ilusiones','Richard Bach');

Note que ingresamos un valor de código menor al valor de inicio de la secuencia, está permitido.
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)
values ('Uno','Richard Bach');

Para desactivar la opción "identity_insert" tipeamos:


47
set identity_insert libros off;

Intentemos ingresar un valor para el campo "codigo":


insert into libros (codigo,titulo,autor)
values (300,'Uno','Richard Bach');

aparece un mensaje de error.


Primer problema:
Una farmacia guarda información referente a sus medicamentos en una tabla llamada
"medicamentos".
1- Elimine la tabla,si existe:
if object_id('medicamentos') is not null
drop table medicamentos;

2- Cree la tabla con un campo "codigo" que genere valores secuenciales automáticamente
comenzando en
10 e incrementándose en 1:
create table medicamentos(
codigo integer identity(10,1),
nombre varchar(20) not null,
laboratorio varchar(20),
precio float,
cantidad integer
);

3- Ingrese los siguientes registros:


insert into medicamentos (nombre, laboratorio,precio,cantidad)
values('Sertal','Roche',5.2,100);
insert into medicamentos (nombre, laboratorio,precio,cantidad)
values('Buscapina','Roche',4.10,200);
insert into medicamentos (nombre, laboratorio,precio,cantidad)
values('Amoxidal 500','Bayer',15.60,100);

4- Verifique que SQL Server generó valores para el campo "código" de modo automático:
select *from medicamentos;

5- Intente ingresar un registro con un valor para el campo "codigo".

6- Setee la opción "identity_insert" en "on"

7- Ingrese un nuevo registro sin valor para el campo "codigo" (no lo permite):
insert into medicamentos (nombre, laboratorio,precio,cantidad)
values('Amoxilina 500','Bayer',15.60,100);

8- Ingrese un nuevo registro con valor para el campo "codigo" repetido.

9- Use la función "ident_seed()" para averiguar el valor de inicio del campo "identity"
de la tabla
"medicamentos"

10- Emplee la función "ident_incr()" para saber cuál es el valor de incremento del
campo "identity"
de "medicamentos"

Segundo problema:
Un videoclub almacena información sobre sus películas en una tabla llamada "peliculas".
1- Elimine la tabla si existe:
if object_id('peliculas') is not null
drop table peliculas;

48
2- Créela definiendo un campo "codigo" autoincrementable que comience en 50 y se
incremente en 3:
create table peliculas(
codigo int identity (50,3),
titulo varchar(40),
actor varchar(20),
duracion int
);

3- Ingrese los siguientes registros:


insert into peliculas (titulo,actor,duracion)
values('Mision imposible','Tom Cruise',120);
insert into peliculas (titulo,actor,duracion)
values('Harry Potter y la piedra filosofal','Daniel R.',180);
insert into peliculas (titulo,actor,duracion)
values('Harry Potter y la camara secreta','Daniel R.',190);

4- Seleccione todos los registros y verifique la carga automática de los códigos:


select *from peliculas;

5- Setee la opción "identity_insert" en "on"

6- Ingrese un registro con valor de código menor a 50.

7- Ingrese un registro con valor de código mayor al último generado.

8- Averigue el valor de inicio del campo "identity" de la tabla "peliculas".

9- Averigue el valor de incremento del campo "identity" de "peliculas".

10- Intente ingresar un registro sin valor para el campo código.

11- Desactive la opción se inserción para el campo de identidad.

12- Ingrese un nuevo registro y muestre todos los registros para ver cómo SQL Server
siguió la
secuencia tomando el último valor del campo como referencia.

15 - Truncate table
Aprendimos que para borrar todos los registro de una tabla se usa "delete" sin condición
"where".
También podemos eliminar todos los registros de una tabla con "truncate table".
Por ejemplo, queremos vaciar la tabla "libros", usamos:
truncate table libros;

La sentencia "truncate table" vacía la tabla (elimina todos los registros) y conserva la estructura
de la tabla.
La diferencia con "drop table" es que esta sentencia borra la tabla, "truncate table" la vacía.
La diferencia con "delete" es la velocidad, es más rápido "truncate table" que "delete" (se nota
cuando la cantidad de registros es muy grande) ya que éste borra los registros uno a uno.

49
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, continúa con la secuencia teniendo en cuenta el valor mayor que se había 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 más alto
de ese campo es "2", si borramos todos los registros con "delete" y luego ingresamos un registro,
éste guardará el valor de código "3"; si en cambio, vaciamos la tabla con "truncate table", al
ingresar un nuevo registro el valor del código se iniciará en 1 nuevamente.
Trabajamos con la tabla "libros" que almacena los datos de los libros de una librería.
Eliminamos la tabla, si existe:
if object_id('libros') is not null
drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(30),
autor varchar(20),
editorial varchar(15),
precio float
);

Agregamos algunos registros:


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

Seleccionamos todos los registros:


select *from libros;

Truncamos la tabla:
truncate table libros;

Ingresamos nuevamente algunos registros:


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

Si seleccionamos todos los registros vemos que la secuencia se reinició en 1:


select *from libros;

Eliminemos todos los registros con "delete":


delete from libros;

Ingresamos nuevamente algunos registros:

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

Seleccionamos todos los registros y vemos que la secuencia continuó:


select *from libros;

Primer problema:
Un instituto de enseñanza almacena los datos de sus estudiantes en una tabla llamada
"alumnos".
1- Elimine la tabla "alumnos" si existe:
if object_id('alumnos') is not null
drop table alumnos;

2- Cree la tabla con la siguiente estructura:


create table alumnos(
legajo int identity,
documento varchar(8),
nombre varchar(30),
domicilio varchar(30)
);

3- Ingrese los siguientes registros y muéstrelos para ver la secuencia de códigos:


insert into alumnos (documento,nombre,domicilio)
values('22345345','Perez Mariana','Colon 234');
insert into alumnos (documento,nombre,domicilio)
values('23545345','Morales Marcos','Avellaneda 348');
insert into alumnos (documento,nombre,domicilio)
values('24356345','Gonzalez Analia','Caseros 444');
insert into alumnos (documento,nombre,domicilio)
values('25666777','Torres Ramiro','Dinamarca 209');

4- Elimine todos los registros con "delete".

5- Ingrese los siguientes registros y selecciónelos para ver cómo SQL Server generó los
códigos:
insert into alumnos (documento,nombre,domicilio)
values('22345345','Perez Mariana','Colon 234');
insert into alumnos (documento,nombre,domicilio)
values('23545345','Morales Marcos','Avellaneda 348');
insert into alumnos (documento,nombre,domicilio)
values('24356345','Gonzalez Analia','Caseros 444');
insert into alumnos (documento,nombre,domicilio)
values('25666777','Torres Ramiro','Dinamarca 209');
select *from alumnos;

6- Elimine todos los registros con "truncate table".

7- Ingrese los siguientes registros y muestre todos los registros para ver que SQL
Server reinició
la secuencia del campo "identity":
insert into alumnos (documento,nombre,domicilio)
values('22345345','Perez Mariana','Colon 234');
insert into alumnos (documento,nombre,domicilio)
values('23545345','Morales Marcos','Avellaneda 348');
insert into alumnos (documento,nombre,domicilio)
values('24356345','Gonzalez Analia','Caseros 444');
insert into alumnos (documento,nombre,domicilio)
values('25666777','Torres Ramiro','Dinamarca 209');
select *from alumnos;

51
Segundo problema:
Un comercio que vende artículos de computación registra los datos de sus artículos en
una tabla con
ese nombre.
1- Elimine "articulos", si existe:
if object_id('articulos') is not null
drop table articulos;

2- Cree la tabla, con la siguiente estructura:


create table articulos(
codigo integer identity,
nombre varchar(20),
descripcion varchar(30),
precio float
);

3- Ingrese algunos registros:


insert into articulos (nombre, descripcion, precio)
values ('impresora','Epson Stylus C45',400.80);
insert into articulos (nombre, descripcion, precio)
values ('impresora','Epson Stylus C85',500);

4- Elimine todos los registros con "truncate table".

5- Ingrese algunos registros y muéstrelos para ver que la secuencia de códigos se


reinicia:
insert into articulos (nombre, descripcion, precio)
values ('monitor','Samsung 14',800);
insert into articulos (nombre, descripcion, precio)
values ('teclado','ingles Biswal',100);
insert into articulos (nombre, descripcion, precio)
values ('teclado','español Biswal',90);
select *from articulos;

6- Elimine todos los registros con "delete".

7- Ingrese algunos registros y muéstrelos para ver que la secuencia de códigos


continua:
insert into articulos (nombre, descripcion, precio)
values ('monitor','Samsung 14',800);
insert into articulos (nombre, descripcion, precio)
values ('teclado','ingles Biswal',100);
insert into articulos (nombre, descripcion, precio)
values ('teclado','español Biswal',90);
select *from articulos;

16 - Otros tipos de datos en SQL Server


Ya explicamos que al crear una tabla debemos elegir la estructura adecuada, esto es, definir los
campos y sus tipos más precisos, según el caso.
El tipo de dato especificado en la definición de cada campo indica los valores permitidos para
cada uno de ellos.
Hasta ahora hemos visto 3 tipos de datos: varchar, integer y float. Hay más tipos, incluso,
subtipos.
52
Los valores que podemos guardar son:
1. TEXTO: Para almacenar texto usamos cadenas de caracteres.
Las cadenas se colocan entre comillas simples. Podemos almacenar letras, símbolos y
dígitos con los que no se realizan operaciones matemáticas, por ejemplo, códigos de
identificación, números de documentos, números telefónicos.
SQL Server ofrece los siguientes tipos: char, nchar, varchar, nvarchar, text y ntext.
2. NUMEROS: Existe variedad de tipos numéricos 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.
3. 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 próximas.
Entonces, cuando creamos una tabla y definir sus campos debemos elegir el tipo de dato más
preciso. Por ejemplo, si necesitamos almacenar nombres usamos texto; si un campo numérico
almacenará solamente valores enteros el tipo "integer" es más adecuado que, por ejemplo un
"float"; si necesitamos almacenar precios, lo más lógico es utilizar el tipo "money".
A continuación analizaremos en detalle cada tipo de dato básicos.

17 - Tipo de dato (texto)


Ya explicamos que al crear una tabla debemos elegir la estructura adecuada, esto es, definir los
campos y sus tipos más precisos, según el caso.
Para almacenar TEXTO usamos cadenas de caracteres.
Las cadenas se colocan entre comillas simples.
Podemos almacenar letras, símbolos y dígitos con los que no se realizan operaciones
matemáticas, por ejemplo, códigos de identificación, números de documentos, números
telefónicos.
Tenemos los siguientes tipos:
1. varchar(x): define una cadena de caracteres de longitud variable en la cual determinamos
el máximo de caracteres con el argumento "x" que va entre paréntesis.
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.
53
Ocupa tantos bytes como se definen con el argumento "x".
"char" viene de character, que significa caracter en inglés.
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 usarán los 3 primeros.
Si intentamos almacenar en un campo una cadena de caracteres de mayor longitud que la
definida, aparece un mensaje indicando tal situación 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 numérico (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
hubiésemos tipeado '12345', igualmente, si ingresamos el valor 23.56, lo convierte a '23.56'. Si el
valor numérico, al ser convertido a cadena supera la longitud definida, aparece un mensaje de
error y la sentencia no se ejecuta.
Es importante elegir el tipo de dato adecuado según el caso, el más preciso.
Para almacenar cadenas que varían en su longitud, es decir, no todos los registros tendrán la
misma longitud en un campo determinado, se emplea "varchar" en lugar de "char".
Por ejemplo, en campos que guardamos nombres y apellidos, no todos los nombres y apellidos
tienen la misma longitud.
Para almacenar cadenas que no varían en su longitud, es decir, todos los registros tendrán la
misma longitud en un campo determinado, se emplea "char".
Por ejemplo, definimos un campo "codigo" que constará de 5 caracteres, todos los registros
tendrán un código de 5 caracteres, ni más ni menos.
Para almacenar valores superiores a 8000 caracteres se debe emplear "text".
Tipo Bytes de almacenamiento
_______________________________________
varchar(x) 0 a 8K
char(x) 0 a 8K
text 0 a 2GB

nvarchar(x) 0 a 8K
nchar(x) 0 a 8K
ntext 0 a 2GB

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

54
Eliminamos la tabla "visitantes", si existe:
if object_id('visitantes') is not null
drop table visitantes;

Creamos con la siguiente estructura:


create table visitantes(
nombre varchar(30),
edad integer,
sexo char(1),
domicilio varchar(30),
ciudad varchar(20),
telefono varchar(11)
);

Los campos "nombre", "domicilio" y "ciudad" almacenarán valores cuya longitud varía, por ello elegimos el
tipo "varchar" y le damos a cada uno una longitud máxima estimando su tamaño. El campo "sexo" se define
de tipo "char", porque necesitamos solamente 1 caracter "f" o "m", que siempre será fijo. El campo
"telefono" también se define como varchar porque no todos los números telefónicos tienen la misma
longitud.
Intentamos ingresar una cadena de mayor longitud que la definida:
insert into visitantes (nombre,edad,sexo,domicilio,ciudad,telefono)
values ('Juan Juarez',32,'masc','Avellaneda 789','Cordoba','4234567');

aparece un mensaje de error y la sentencia no se ejecuta


Ingresamos un número telefónico olvidando las comillas, es decir, como un valor numérico:
insert into visitantes (nombre,edad,sexo,domicilio,ciudad,telefono)
values ('Marcela Morales',43,'f','Colon 456','Cordoba',4567890);

lo convierte a cadena, veámoslo:


select *from visitantes;

Primer problema:
Una concesionaria de autos vende autos usados y almacena los datos de los autos en una
tabla llamada
"autos".
1- Elimine la tabla "autos" si existe:
if object_id('autos') is not null
drop table autos;

2- Cree la tabla eligiendo el tipo de dato adecuado para cada campo, estableciendo el
campo
"patente" como clave primaria:
create table autos(
patente char(6),
marca varchar(20),
modelo char(4),
precio float,
primary key (patente)
);

Hemos definido el campo "patente" de tipo "char" y no "varchar" porque la cadena de


caracteres
siempre tendrá la misma longitud (6 caracteres). Lo mismo sucede con el campo "modelo",
en el cual

55
almacenaremos el año, necesitamos 4 caracteres fijos.

3- Ingrese los siguientes registros:


insert into autos
values('ACD123','Fiat 128','1970',15000);
insert into autos
values('ACG234','Renault 11','1990',40000);
insert into autos
values('BCD333','Peugeot 505','1990',80000);
insert into autos
values('GCD123','Renault Clio','1990',70000);
insert into autos
values('BCC333','Renault Megane','1998',95000);
insert into autos
values('BVF543','Fiat 128','1975',20000);

4- Seleccione todos los autos del año 1990:


select *from autos
where modelo='1990';

Segundo problema:
Una empresa almacena los datos de sus clientes en una tabla llamada "clientes".
1- Elimine la tabla "clientes" si existe:
if object_id('clientes') is not null
drop table clientes;

2- Créela eligiendo el tipo de dato más adecuado para cada campo:


create table clientes(
documento char(8),
apellido varchar(20),
nombre varchar(20),
domicilio varchar(30),
telefono varchar (11)
);

3- Analice la definición de los campos. Se utiliza char(8) para el documento porque


siempre constará
de 8 caracteres. Para el número telefónico se usar "varchar" y no un tipo numérico
porque si bien es
un número, con él no se realizarán operaciones matemáticas.

4- Ingrese algunos registros:


insert into clientes
values('2233344','Perez','Juan','Sarmiento 980','4342345');
insert into clientes (documento,apellido,nombre,domicilio)
values('2333344','Perez','Ana','Colon 234');
insert into clientes
values('2433344','Garcia','Luis','Avellaneda 1454','4558877');
insert into clientes
values('2533344','Juarez','Ana','Urquiza 444','4789900');

5- Seleccione todos los clientes de apellido "Perez" (2 registros):


select *from clientes
where apellido='Perez';

18 - Tipo de dato (numérico)

56
Ya explicamos que al crear una tabla debemos elegir la estructura adecuada, esto es, definir los
campos y sus tipos más precisos, según el caso.
Para almacenar valores NUMERICOS SQL Server dispone de varios tipos.
Para almacenar valores ENTEROS, por ejemplo, en campos que hacen referencia a cantidades,
usamos:
1) integer o int: su rango es de -2000000000 a 2000000000 aprox. El tipo "integer" tiene subtipos:
- smallint: Puede contener hasta 5 digitos. Su rango va desde ?32000 hasta 32000 aprox.
- tinyint: Puede almacenar valores entre 0 y 255.
- bigint: De ?9000000000000000000 hasta 9000000000000000000 aprox.
Para almacenar valores numéricos EXACTOS con decimales, especificando la cantidad de cifras
a la izquierda y derecha del separador decimal, utilizamos:
2) decimal o numeric (t,d): Pueden tener hasta 38 digitos, guarda un valor exacto. El primer
argumento indica el total de dígitos y el segundo, la cantidad de decimales.
Por ejemplo, si queremos almacenar valores entre -99.99 y 99.99 debemos definir el campo como
tipo "decimal(4,2)". Si no se indica el valor del segundo argumento, por defecto es "0". Por
ejemplo, si definimos "decimal(4)" se pueden guardar valores entre -9999 y 9999.
El rango depende de los argumentos, también los bytes que ocupa.
Se utiliza el punto como separador de decimales.
Si ingresamos un valor con más decimales que los permitidos, redondea al más 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 numéricos 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 sólo 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 numéricos:
- si intentamos ingresar un valor fuera de rango, no lo permite.
- si ingresamos una cadena, SQL Server intenta convertirla a valor numérico, si dicha cadena
consta solamente de dígitos, la conversión se realiza, luego verifica si está dentro del rango, si es
así, la ingresa, sino, muestra un mensaje de error y no ejecuta la sentencia. Si la cadena contiene
caracteres que SQL Server no puede convertir a valor numérico, muestra un mensaje de error y la
sentencia no se ejecuta.
Por ejemplo, definimos un campo de tipo decimal(5,2), si ingresamos la cadena '12.22', la
convierte al valor numérico 12.22 y la ingresa; si intentamos ingresar la cadena '1234.56', la
convierte al valor numérico 1234.56, pero como el máximo valor permitido es 999.99, muestra un

57
mensaje indicando que está fuera de rango. Si intentamos ingresar el valor '12y.25', SQL Server
no puede realizar la conversión y muestra un mensaje de error.
Es importante elegir el tipo de dato adecuado según el caso, el más preciso. Por ejemplo, si un
campo numérico almacenará valores positivos menores a 255, el tipo "int" no es el más
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".
Tipo Bytes de almacenamiento
_______________________________________
int 4
smallint 2
tinyint 1
bigint 8

decimal 2 a 17

float 4u8
real 4u8

money 8
smallmoney 4

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


Eliminamos la tabla, si existe:
if object_id('libros') is not null
drop table libros;

Creamos la tabla con la siguiente estructura:


create table libros(
codigo smallint identity,
titulo varchar(40) not null,
autor varchar(30),
editorial varchar(15),
precio smallmoney,
cantidad tinyint
);

Note que definimos el campo "codigo" de tipo "smallint", esto es porque estimamos que no tendremos más
de 30000 libros. Si necesitáramos un rango mayor podemos emplear "int".
Como en el campo "precio" no almacenaremos valores mayores a 200000, definimos el campo de tipo
"smallmoney".
También podemos definirlo de tipo "decimal(5,2)" porque el máximo precio no superará los 999.99.
El tipo "float" no es el más adecuado para representar precios porque no es exacto y muestra muchos
decimales innecesarios.
Como los valores para el campo "cantidad" no superarán los 255, definimos el campo de tipo "tinyint". Si
estimamos que tendremos más cantidad de libros podemos emplear "smallint" que tiene un rango mayor; no
es adecuado usar int (cuyo rango llega hasta 4000 millones aprox.), porque ocuparíamos más espacio (4
bytes).

58
Analicemos la inserción de datos numéricos.
Intentemos ingresar un valor fuera del rango definido, una cantidad que supera el rango del tipo "tinyint", el
valor 260:
insert into libros (titulo,autor,editorial,precio,cantidad)
values('El aleph','Borges','Emece',25.60,260);

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


Intentamos ingresar un precio que supera el rango del tipo "smallmoney", el valor 250000:
insert into libros (titulo,autor,editorial,precio,cantidad)
values('El aleph','Borges','Emece',250000,100);

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


Intentamos ingresar una cadena que SQL Server no pueda convertir a valor numérico en el campo "precio"
(error):
insert into libros (titulo,autor,editorial,precio,cantidad)
values('Uno','Richard Bach','Planeta','a50.30',100);

Ingresamos una cadena en el campo "cantidad":


insert into libros (titulo,autor,editorial,precio,cantidad)
values('Uno','Richard Bach','Planeta',50.30,'100');

lo convierte a valor numérico.


Primer problema:
Un banco tiene registrados las cuentas corrientes de sus clientes en una tabla llamada
"cuentas".
La tabla contiene estos datos:
Número de Cuenta Documento Nombre Saldo
______________________________________________________________
1234 25666777 Pedro Perez 500000.60
2234 27888999 Juan Lopez -250000
3344 27888999 Juan Lopez 4000.50
3346 32111222 Susana Molina 1000

1- Elimine la tabla "cuentas" si existe:


if object_id('cuentas') is not null
drop table cuentas;

2- Cree la tabla eligiendo el tipo de dato adecuado para almacenar los datos descriptos
arriba:

- Número de cuenta: entero, no nulo, no puede haber valores repetidos, clave primaria;
- Documento del propietario de la cuenta: cadena de caracteres de 8 de longitud
(siempre 8), no nulo;
- Nombre del propietario de la cuenta: cadena de caracteres de 30 de longitud,
- Saldo de la cuenta: valores altos con decimales.

3- Ingrese los siguientes registros:


insert into cuentas(numero,documento,nombre,saldo)
values('1234','25666777','Pedro Perez',500000.60);
insert into cuentas(numero,documento,nombre,saldo)
values('2234','27888999','Juan Lopez',-250000);
insert into cuentas(numero,documento,nombre,saldo)
values('3344','27888999','Juan Lopez',4000.50);
insert into cuentas(numero,documento,nombre,saldo)

59
values('3346','32111222','Susana Molina',1000);
Note que hay dos cuentas, con distinto número de cuenta, de la misma persona.

4- Seleccione todos los registros cuyo saldo sea mayor a "4000" (2 registros)

5- Muestre el número de cuenta y saldo de todas las cuentas cuyo propietario sea "Juan
Lopez" (2
registros)

6- Muestre las cuentas con saldo negativo (1 registro)

7- Muestre todas las cuentas cuyo número es igual o mayor a "3000" (2 registros):
select *from cuentas
where numero>=3000;

Segundo problema:
Una empresa almacena los datos de sus empleados en una tabla "empleados" que guarda los
siguientes
datos: nombre, documento, sexo, domicilio, sueldobasico.
1- Elimine la tabla, si existe:
if object_id('empleados') is not null
drop table empleados;

2- Cree la tabla eligiendo el tipo de dato adecuado para cada campo:


create table empleados(
nombre varchar(30),
documento char(8),
sexo char(1),
domicilio varchar(30),
sueldobasico decimal(7,2),--máximo estimado 99999.99
cantidadhijos tinyint--no superará los 255
);

3- Ingrese algunos registros:


insert into empleados (nombre,documento,sexo,domicilio,sueldobasico,cantidadhijos)
values ('Juan Perez','22333444','m','Sarmiento 123',500,2);
insert into empleados (nombre,documento,sexo,domicilio,sueldobasico,cantidadhijos)
values ('Ana Acosta','24555666','f','Colon 134',850,0);
insert into empleados (nombre,documento,sexo,domicilio,sueldobasico,cantidadhijos)
values ('Bartolome Barrios','27888999','m','Urquiza 479',10000.80,4);

4- Ingrese un valor de "sueldobasico" con más decimales que los definidos (redondea los
decimales al
valor más cercano 800.89):
insert into empleados (nombre,documento,sexo,domicilio,sueldobasico,cantidadhijos)
values ('Susana Molina','29000555','f','Salta 876',800.888,3);

5- Intente ingresar un sueldo que supere los 7 dígitos (no lo permite)

6- Muestre todos los empleados cuyo sueldo no supere los 900 pesos (1 registro):

7- Seleccione los nombres de los empleados que tengan hijos (3 registros):

19 - Tipo de dato (fecha y hora)


Ya explicamos que al crear una tabla debemos elegir la estructura adecuada, esto es, definir los
campos y sus tipos más precisos, según el caso.
60
Para almacenar valores de tipo FECHA Y HORA SQL Server dispone de dos tipos:
1) datetime: puede almacenar valores desde 01 de enero de 1753 hasta 31 de diciembre de 9999.
2) smalldatetime: el rango va de 01 de enero de 1900 hasta 06 de junio de 2079.
Las fechas se ingresan entre comillas simples.
Para almacenar valores de tipo fecha se permiten como separadores "/", "-" y ".".
SQL Server reconoce varios formatos de entrada de datos de tipo fecha. Para establecer el orden
de las partes de una fecha (dia, mes y año) empleamos "set dateformat". Estos son los formatos:
-mdy: 4/15/96 (mes y día con 1 ó 2 dígitos y año con 2 ó 4 dígitos),
-myd: 4/96/15,
-dmy: 15/4/1996
-dym: 15/96/4,
-ydm: 96/15/4,
-ydm: 1996/15/4,

Para ingresar una fecha con formato "día-mes-año", tipeamos:


set dateformat dmy;

El formato por defecto es "mdy".


Todos los valores de tipo "datetime" se muestran en formato "año-mes-día
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' (año de 2 dígitos), 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

Una empresa almacena los datos de sus empleados en una tabla "empleados".
Eliminamos la tabla, si existe:
if object_id('empleados') is not null
drop table empleados;

Creamos la tabla eligiendo el tipo de dato adecuado para cada campo:


create table empleados(
nombre varchar(20),
documento char(8),
fechaingreso datetime
);

Seteamos el formato de la fecha para que guarde día, mes y año:


set dateformat dmy;

61
Ingresamos algunos registros:
insert into empleados values('Ana Gomez','22222222','12-01-1980');
insert into empleados values('Bernardo Huerta','23333333','15-03-81');
insert into empleados values('Carla Juarez','24444444','20/05/1983');
insert into empleados values('Daniel Lopez','25555555','2.5.1990');

El segundo registro ingresado tiene 2 dígitos correspondientes al año; en el tercero empleamos la barra ('/')
como separador y en el cuarto empleamos como separador el punto ('.') y colocamos un sólo dígito en la part
del día y el mes.
Recuperamos los registros:
select *from empleados;

Note que el formato de visualización es "y-m-d".


Mostramos los datos de los empleados cuya fecha de ingreso es anterior a '01-01-1985':
select *from empleados where fechaingreso<'01-01-1985';

Actualizamos el nombre a "Maria Carla Juarez' del empleado cuya fecha de ingreso es igual a '20/05/1983':
update empleados set nombre='Maria Carla Juarez' where fechaingreso='20.5.83';

Veamos si se actualizó:
select *from empleados;

Borramos los empleados cuya fecha de ingreso es distinta a '20.5.83':


delete from empleados where fechaingreso<>'20/05/1983';

Veamos si se eliminaron:
select *from empleados;

Primer problema:
Una facultad almacena los datos de sus alumnos en una tabla denominada "alumnos".
1- Elimine la tabla, si existe:
if object_id('alumnos') is not null
drop table alumnos;

2- Cree la tabla eligiendo el tipo de dato adecuado para cada campo:


create table alumnos(
apellido varchar(30),
nombre varchar(30),
documento char(8),
domicilio varchar(30),
fechaingreso datetime,
fechanacimiento datetime
);

3- Setee el formato para entrada de datos de tipo fecha para que acepte valores "día-
mes-año":
set dateformat 'dmy';

4- Ingrese un alumno empleando distintos separadores para las fechas:


insert into alumnos values('Gonzalez','Ana','22222222','Colon 123','10-08-
1990','15/02/1972');

62
5- Ingrese otro alumno empleando solamente un dígito para día y mes y 2 para el año:
insert into alumnos values('Juarez','Bernardo','25555555','Sucre 456','03-03-
1991','15/02/1972');

6- Ingrese un alumnos empleando 2 dígitos para el año de la fecha de ingreso y "null"


en
"fechanacimiento":
insert into alumnos values('Perez','Laura','26666666','Bulnes 345','03-03-91',null);

7- Intente ingresar un alumno con fecha de ingreso correspondiente a "15 de marzo de


1990" pero en
orden incorrecto "03-15-90":
insert into alumnos values('Lopez','Carlos','27777777','Sarmiento 1254','03-15-
1990',null);
aparece un mensaje de error porque lo lee con el formato día, mes y año y no reconoce
el mes 15.

8- Muestre todos los alumnos que ingresaron antes del '1-1-91'.


1 registro.

9- Muestre todos los alumnos que tienen "null" en "fechanacimiento":


select *from alumnos where fechanacimiento is null;
1 registro.

10- Intente ingresar una fecha de ingreso omitiendo los separadores:


insert into alumnos values('Rosas','Romina','28888888','Avellaneda
487','03151990',null);
No lo acepta.

11- Setee el formato de entrada de fechas para que acepte valores "mes-dia-año".

12- Ingrese el registro del punto 7.

20 - Ingresar algunos campos (insert into)


Hemos aprendido a ingresar registros listando todos los campos y colocando valores para todos
y cada uno de ellos luego de "values".
Si ingresamos valores para todos los campos, podemos omitir la lista de nombres de los campos.
Por ejemplo, si tenemos creada la tabla "libros" con los campos "titulo", "autor" y "editorial",
podemos ingresar un registro de la siguiente manera:
insert into libros
values ('Uno','Richard Bach','Planeta');

También es posible ingresar valores para algunos campos. Ingresamos valores solamente para
los campos "titulo" y "autor":
insert into libros (titulo, autor)
values ('El aleph','Borges');

SQL Server almacenará el valor "null" en el campo "editorial", para el cual no hemos explicitado
un valor.
Al ingresar registros debemos tener en cuenta:
63
- la lista de campos debe coincidir en cantidad y tipo de valores con la lista de valores luego de
"values". Si se listan más (o menos) campos que los valores ingresados, aparece un mensaje de
error y la sentencia no se ejecuta.
- si ingresamos valores para todos los campos podemos obviar la lista de campos.
- podemos omitir valores para los campos que 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 cláusula "default" (tema que veremos a continuación).
Trabajamos con la tabla "libros" que almacena los datos de los libros de una librería.
Eliminamos la tabla, si existe:
if object_id('libros') is not null
drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(30),
editorial varchar(15)
);

Si ingresamos valores para todos los campos, podemos omitir la lista de campos:
insert into libros
values ('Uno','Richard Bach','Planeta');

Podemos ingresar valores para algunos de los campos:


insert into libros (titulo, autor)
values ('El aleph','Borges');

No podemos omitir el valor para un campo declarado "not null", como el campo "titulo":
insert into libros (autor,editorial)
values ('Lewis Carroll','Planeta');

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


Veamos cómo SQL Server almacenó los registros:
select *from libros;

Primer problema:
Un banco tiene registrados las cuentas corrientes de sus clientes en una tabla llamada
"cuentas".
1- Elimine la tabla "cuentas" si existe:
if object_id('cuentas') is not null
drop table cuentas;

2- Cree la tabla :
create table cuentas(

64
numero int identity,
documento char(8) not null,
nombre varchar(30),
saldo money
);

3- Ingrese un registro con valores para todos sus campos, inclusive el campo identity,
omitiendo la
lista de campos (error, no se debe ingresar para el campo identity):
insert into cuentas
values (1,'25666777','Juan Perez',2500.50);

4- Ingrese un registro con valores para todos sus campos omitiendo la lista de campos
(excepto el
campo "identity"):
insert into cuentas
values ('25666777','Juan Perez',2500.50);

5- Ingrese un registro omitiendo algún campo que admitan valores nulos.

6- Intente ingresar un registro con valor para el campo "numero" (error):


insert into cuentas (numero,documento,nombre,saldo)
values (5,'28999777','Luis Lopez',34000);

7- Intente ingresar un registro listando 3 campos y colocando 4 valores (error)

8- Intente ingresar un registro sin valor para el campo "documento" (error)

9- Vea los registros ingresados:


select *from libros;

21 - Valores por defecto (default)


Hemos visto que si al insertar registros no se especifica un valor para un campo que admite
valores nulos, se ingresa automaticamente "null" y si el campo está declarado "identity", se
inserta el siguiente de la secuencia. A estos valores se les denomina valores por defecto o
predeterminados.
Un valor por defecto se inserta cuando no está presente al ingresar un registro y en algunos
casos en que el dato ingresado es inválido.
Para campos de cualquier tipo no declarados "not null", es decir, que admiten valores nulos, el
valor por defecto es "null". Para campos declarados "not null", no existe valor por defecto, a
menos que se declare explícitamente con la cláusula "default".
Para todos los tipos, excepto los declarados "identity", se pueden explicitar valores por defecto
con la cláusula "default".
Podemos establecer valores por defecto para los campos cuando creamos la tabla. Para ello
utilizamos "default" al definir el campo. Por ejemplo, queremos que el valor por defecto del campo
"autor" de la tabla "libros" sea "Desconocido" y el valor por defecto del campo "cantidad" sea "0":
create table libros(
codigo int identity,

65
titulo varchar(40),
autor varchar(30) not null default 'Desconocido',
editorial varchar(20),
precio decimal(5,2),
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 cláusula "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.
También se puede utilizar "default" para dar el valor por defecto a los campos en sentencias
"insert", por ejemplo:
insert into libros (titulo,autor,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 cláusula "default" permite especificar el valor por defecto de un campo. Si no se
explicita, el valor por defecto es "null", siempre que el campo no haya sido declarado "not null".
Los campos para los cuales no se ingresan valores en un "insert" tomarán los valores por defecto:
- si tiene el atributo "identity": el valor de inicio de la secuencia si es el primero o el siguiente valor
de la secuencia, no admite cláusula "default";
- si permite valores nulos y no tiene cláusula "default", almacenará "null";
- si está declarado explícitamente "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 cláusula "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".
Un campo sólo puede tener un valor por defecto. Una tabla puede tener todos sus campos con
valores por defecto. Que un campo tenga valor por defecto no significa que no admita valores
nulos, puede o no admitirlos.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:

66
if object_id('libros') is not null
drop table libros;

Creamos la tabla definiendo un valor por defecto para el campo "autor" y otro para el campo "cantidad":
create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30) not null default 'Desconocido',
editorial varchar(20),
precio decimal(5,2),
cantidad tinyint default 0
);

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


insert into libros (titulo,editorial,precio)
values('Java en 10 minutos','Paidos',50.40);

SQL Server ingresará el registro con el siguiente valor de la secuencia en "codigo", con el título, editorial y
precio ingresados, en "autor" colocará "Desconocido" y en cantidad "0":
select *from libros;

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

Visualicemos la estructura de la tabla:


sp_columns libros;

La columna "COLUMN_DEF", muestra el valor por defecto de cada campo.


Podemos emplear "default" para dar el valor por defecto a algunos campos:
insert into libros (titulo,autor,precio,cantidad)
values ('El gato con botas',default,default,100);
select *from libros;

Como todos los campos de "libros" tienen valores predeterminados, podemos tipear:
insert into libros default values;
select *from libros;

La sentencia anterior almacenará un registro con los valores predetermiandos para cada uno de sus campos.
Que un campo tenga valor por defecto no significa que no admita valores nulos, puede o no admitirlos.
Podemos ingresar el valor "null" en el campo "cantidad":
insert into libros (titulo,autor,cantidad)
values ('Alicia en el pais de las maravillas','Lewis Carroll',null);

Primer problema:
Un comercio que tiene un stand en una feria registra en una tabla llamada "visitantes"
algunos datos
de las personas que visitan o compran en su stand para luego enviarle publicidad de sus
productos.
67
1- Elimine la tabla "visitantes", si existe:
if object_id('visitantes') is not null
drop table visitantes;

2- Cree la tabla con la siguiente estructura:


create table visitantes(
nombre varchar(30),
edad tinyint,
sexo char(1) default 'f',
domicilio varchar(30),
ciudad varchar(20) default 'Cordoba',
telefono varchar(11),
mail varchar(30) default 'no tiene',
montocompra decimal (6,2)
);

4- Vea la información de las columnas "COLUMN_DEF" y "IS_NULLABLE":


sp_columns visitantes;

5- Ingrese algunos registros sin especificar valores para algunos campos para ver cómo
opera la
cláusula "default":
insert into visitantes (nombre, domicilio, montocompra)
values ('Susana Molina','Colon 123',59.80);
insert into visitantes (nombre, edad, ciudad, mail)
values ('Marcos Torres',29,'Carlos Paz','marcostorres@hotmail.com');
select *from visitantes;

6- Use la palabra "default" para ingresar valores en un insert.

7- Ingrese un registro con "default values".

Segundo problema:
Una pequeña biblioteca de barrio registra los préstamos de sus libros en una tabla
llamada
"prestamos". En ella almacena la siguiente información: título del libro, documento de
identidad del
socio a quien se le presta el libro, fecha de préstamo, fecha en que tiene que devolver
el libro y
si el libro ha sido o no devuelto.
1- Elimine la tabla "prestamos" si existe:
if object_id('prestamos') is not null
drop table prestamos;

2- Cree la tabla:
create table prestamos(
titulo varchar(40) not null,
documento char(8) not null,
fechaprestamo datetime not null,
fechadevolucion datetime,
devuelto char(1) default 'n'
);

3- Ingrese algunos registros omitiendo el valor para los campos que lo admiten:
insert into prestamos (titulo,documento,fechaprestamo,fechadevolucion)
values ('Manual de 1 grado','23456789','2006-12-15','2006-12-18');
insert into prestamos (titulo,documento,fechaprestamo)
values ('Alicia en el pais de las maravillas','23456789','2006-12-16');
insert into prestamos (titulo,documento,fechaprestamo,fechadevolucion)
values ('El aleph','22543987','2006-12-16','2006-08-19');
insert into prestamos (titulo,documento,fechaprestamo,devuelto)
values ('Manual de geografia 5 grado','25555666','2006-12-18','s');

4- Seleccione todos los registros:


68
select *from prestamos;

5- Ingrese un registro colocando "default" en los campos que lo admiten y vea cómo se
almacenó.

6- Intente ingresar un registro con "default values" y analice el mensaje de error (no
se puede)

22 - Columnas calculadas (operadores


aritméticos y de concatenación)
Aprendimos que los operadores son símbolos que permiten realizar distintos tipos de
operaciones.
Dijimos que SQL Server tiene 4 tipos de operadores: 1) relacionales o de comparación (los
vimos), 2) lógicos (lo veremos más adelante, 3) aritméticos y 4) de concatenación.
Los operadores aritméticos permiten realizar cálculos con valores numéricos.
Son: multiplicación (*), división (/) y módulo (%) (el resto de dividir números enteros), suma (+) y
resta (-).
Es posible obtener salidas en las cuales una columna sea el resultado de un cálculo y no un
campo de una tabla.
Si queremos ver los títulos, precio y cantidad de cada libro escribimos la siguiente sentencia:
select titulo,precio,cantidad
from libros;

Si queremos saber el monto total en dinero de un título podemos multiplicar el precio por la
cantidad por cada título, pero también podemos hacer que SQL Server realice el cálculo y lo
incluya en una columna extra en la salida:

select titulo, precio,cantidad,


precio*cantidad
from libros;

Si queremos saber el precio de cada libro con un 10% de descuento podemos incluir en la
sentencia los siguientes cálculos:
select titulo,precio,
precio-(precio*0.1)
from libros;

También podemos actualizar los datos empleando operadores aritméticos:


update libros set precio=precio-(precio*0.1);

Todas las operaciones matemáticas retornan "null" en caso de error. Ejemplo:


select 5/0;

69
Los operadores de concatenación: permite concatenar cadenas, el más (+).
Para concatenar el título, el autor y la editorial de cada libro usamos el operador de concatenación
("+"):
select titulo+'-'+autor+'-'+editorial
from libros;

Note que concatenamos además unos guiones para separar los campos.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe:
if object_id ('libros') is not null
drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(20) default 'Desconocido',
editorial varchar(20),
precio decimal(6,2),
cantidad tinyint default 0,
primary key (codigo)
);

Ingresamos algunos registros:


insert into libros (titulo,autor,editorial,precio)
values('El aleph','Borges','Emece',25);
insert into libros
values('Java en 10 minutos','Mario Molina','Siglo XXI',50.40,100);
insert into libros (titulo,autor,editorial,precio,cantidad)
values('Alicia en el pais de las maravillas','Lewis Carroll','Emece',15,50);

Queremos saber el monto total en dinero de cada libro:


select titulo, precio,cantidad,
precio*cantidad
from libros;

Queremos saber el precio de cada libro con un 10% de descuento:


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

Actualizamos los precios con un 10% de descuento y vemos el resultado:


update libros set precio=precio-(precio*0.1);
select *from libros;

Queremos una columna con el título, el autor y la editorial de cada libro:


select titulo+'-'+autor+'-'+editorial
from libros;

Primer problema:

70
Un comercio que vende artículos de computación registra los datos de sus artículos en
una tabla con
ese nombre.
1- Elimine la tabla si existe:
if object_id ('articulos') is not null
drop table articulos;

2- Cree la tabla:
create table articulos(
codigo int identity,
nombre varchar(20),
descripcion varchar(30),
precio smallmoney,
cantidad tinyint default 0,
primary key (codigo)
);

3- Ingrese algunos registros:


insert into articulos (nombre, descripcion, precio,cantidad)
values ('impresora','Epson Stylus C45',400.80,20);
insert into articulos (nombre, descripcion, precio)
values ('impresora','Epson Stylus C85',500);
insert into articulos (nombre, descripcion, precio)
values ('monitor','Samsung 14',800);
insert into articulos (nombre, descripcion, precio,cantidad)
values ('teclado','ingles Biswal',100,50);

4- El comercio quiere aumentar los precios de todos sus artículos en un 15%. Actualice
todos los
precios empleando operadores aritméticos.

5- Vea el resultado:
select *from articulos;

6- Muestre todos los artículos, concatenando el nombre y la descripción de cada uno de


ellos
separados por coma.

7- Reste a la cantidad de todos los teclados, el valor 5, empleando el operador


aritmético menos ("-")

23 - Alias
Una manera de hacer más comprensible el resultado de una consulta consiste en cambiar los
encabezados de las columnas.
Por ejemplo, tenemos la tabla "agenda" con un campo "nombre" (entre otros) en el cual se
almacena el nombre y apellido de nuestros amigos; queremos que al mostrar la información 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;

71
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 más 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.


También se puede crear un alias para columnas calculadas.
La palabra clave "as" es opcional en algunos casos, pero es conveniente usarla.
Entonces, un "alias" se usa como nombre de un campo o de una expresión. En estos casos, son
opcionales, sirven para hacer más comprensible el resultado; en otros casos, que veremos más
adelante, son obligatorios.
Trabajamos con nuestra tabla "agenda".
Eliminamos la tabla si existe:
if object_id('agenda') is not null
drop table agenda;

Creamos la tabla:
create table agenda(
nombre varchar(30),
domicilio varchar(30),
telefono varchar(11)
);

Ingresamos algunos registros:


insert into agenda
values('Juan Perez','Avellaneda 908','4252525');
insert into agenda
values('Marta Lopez','Sucre 34','4556688');
insert into agenda
values('Carlos Garcia','Sarmiento 1258',null);

Mostramos la información con el encabezado "NombreYApellido" para el campo "nombre":


select nombre as NombreYApellido,
domicilio,telefono
from agenda;

Mostramos la información con el encabezado "Nombre y apellido" para el campo "nombre", necesitamos
emplear comillas:
select nombre as 'Nombre y apellido',
domicilio,telefono
from agenda;

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


select nombre 'Nombre y apellido',
domicilio,telefono
from agenda;
72
Primer problema:
Trabaje con la tabla "libros" de una librería.
1- Elimine la tabla si existe:
if object_id ('libros') is not null
drop table libros;

2- Cree la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(20) default 'Desconocido',
editorial varchar(20),
precio decimal(6,2),
cantidad tinyint default 0,
primary key (codigo)
);

3- Ingrese algunos registros:


insert into libros (titulo,autor,editorial,precio)
values('El aleph','Borges','Emece',25);
insert into libros
values('Java en 10 minutos','Mario Molina','Siglo XXI',50.40,100);
insert into libros (titulo,autor,editorial,precio,cantidad)
values('Alicia en el pais de las maravillas','Lewis Carroll','Emece',15,50);

4- Muestre todos los campos de los libros y un campo extra, con el encabezado "monto
total" en la
que calcule el monto total en dinero de cada libro (precio por cantidad)

5- Muestre el título, autor y precio de todos los libros de editorial "Emece" y agregue
dos columnas
extra en las cuales muestre el descuento de cada libro, con el encabezado "descuento" y
el precio
con un 10% de descuento con el encabezado "precio final".

6- Muestre una columna con el título y el autor concatenados con el encabezado "Título
y autor"

25 - Funciones para el manejo de cadenas


Microsoft SQL Server tiene algunas funciones para trabajar con cadenas de caracteres. Estas
son algunas:
- substring(cadena,inicio,longitud): devuelve una parte de la cadena especificada como primer
argumento, empezando desde la posición especificada por el segundo argumento y de tantos
caracteres de longitud como indica el tercer argumento. Ejemplo:
select substring('Buenas tardes',8,6);

retorna "tardes".
- str(numero,longitud,cantidaddecimales): convierte números a caracteres; el primer parámetro
indica el valor numérico a convertir, el segundo la longitud del resultado (debe ser mayor o igual a
la parte entera del número más el signo si lo tuviese) y el tercero, la cantidad de decimales. El

73
segundo y tercer argumento son opcionales y deben ser positivos. String significa cadena en
inglés.
Ejemplo: se convierte el valor numérico "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 numérico "123.456" a cadena:
select str(123.456);

retorna '123';
select str(123.456,3);

retorna '123';
Si el segundo parámetro es menor a la parte entera del número, devuelve asteriscos (*). Ejemplo:
select str(123.456,2,3);
retorna "**".
- stuff(cadena1,inicio,cantidad,cadena2): inserta la cadena enviada como cuarto argumento, en la
posición indicada en el segundo argumento, reemplazando la cantidad de caracteres indicada por
el tercer argumento en la cadena que es primer parámetro. Stuff significa rellenar en inglés.
Ejemplo:
select stuff('abcde',3,2,'opqrs');

retorna "abopqrse". Es decir, coloca en la posición 2 la cadena "opqrs" y reemplaza 2 caracteres


de la primer cadena.
Los argumentos numéricos 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 carácter.
- len(cadena): retorna la longitud de la cadena enviada como argumento. "len" viene de length,
que significa longitud en inglés. Ejemplo:
select len('Hola');

devuelve 4.
- char(x): retorna un caracter en código ASCII del entero enviado como argumento. Ejemplo:

select char(65);

retorna "A".

74
- 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".


-lower(cadena): retornan la cadena con todos los caracteres en minúsculas. lower significa
reducir en inglés. Ejemplo:
select lower('HOLA ESTUDIAnte');

retorna "hola estudiante".


-upper(cadena): retornan la cadena con todos los caracteres en mayúsculas. 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 posición de comienzo (de la primera ocurrencia) del
patrón especificado en la cadena enviada como segundo argumento. Si no la encuentra retorna 0.
Ejemplos:
select patindex('%Luis%', 'Jorge Luis Borges');

retorna 7.

75
select patindex('%or%', 'Jorge Luis Borges');

retorna 2.
select patindex('%ar%', 'Jorge Luis Borges');

retorna 0.
- charindex(subcadena,cadena,inicio): devuelve la posición donde comienza la subcadena en la
cadena, comenzando la búsqueda desde la posición indicada por "inicio". Si el tercer argumento
no se coloca, la búsqueda 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.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe:
if object_id ('libros') is not null
drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(20) default 'Desconocido',
editorial varchar(20),
precio decimal(6,2),
cantidad tinyint default 0,
primary key (codigo)
76
);

Ingresamos algunos registros:


insert into libros (titulo,autor,editorial,precio)
values('El aleph','Borges','Emece',25);
insert into libros
values('Java en 10 minutos','Mario Molina','Siglo XXI',50.40,100);
insert into libros (titulo,autor,editorial,precio,cantidad)
values('Alicia en el pais de las maravillas','Lewis Carroll','Emece',15,50);

Mostramos sólo los 12 primeros caracteres de los títulos de los libros y sus autores, empleando la función
"substring()":
select substring(titulo,1,12) as titulo
from libros;

Mostramos sólo los 12 primeros caracteres de los títulos de los libros y sus autores, ahora empleando la
función "left()":
select left(titulo,12) as titulo
from libros;

Mostramos los títulos de los libros y sus precios convirtiendo este último a cadena de caracteres con un solo
decimal, empleando la función "str":
select titulo,
str(precio,6,1)
from libros;

Mostramos los títulos de los libros y sus precios convirtiendo este último a cadena de caracteres
especificando un solo argumento:
select titulo,
str(precio)
from libros;

Se redondea a entero.
Mostramos los títulos, autores y editoriales de todos libros, al último campo lo queremos en mayúsculas:
select titulo, autor, upper(editorial)
from libros;

26 - Funciones matemáticas
Las funciones matemáticas realizan operaciones con expresiones numéricas y retornan un
resultado, operan con tipos de datos numéricos.
Microsoft SQL Server tiene algunas funciones para trabajar con números. Aquí presentamos
algunas.
-abs(x): retorna el valor absoluto del argumento "x". Ejemplo:
77
select abs(-20);

retorna 20.
-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.
- %:
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 número redondeado a la longitud especificada. "longitud"
debe ser tinyint, smallint o int. Si "longitud" es positivo, el número de decimales es redondeado
según "longitud"; si es negativo, el número es redondeado desde la parte entera según 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).
-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.

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


78
SQL Server dispone de funciones trigonométricas que retornan radianes.
Se pueden emplear estas funciones enviando como argumento el nombre de un campo de tipo
numérico.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe:

if object_id ('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(20) default 'Desconocido',
editorial varchar(20),
precio decimal(6,2),
primary key (codigo)
);

Ingresamos algunos registros:


insert into libros (titulo,autor,editorial,precio)
values('El aleph','Borges','Emece',25.33);
insert into libros
values('Java en 10 minutos','Mario Molina','Siglo XXI',50.65);
insert into libros (titulo,autor,editorial,precio)
values('Alicia en el pais de las maravillas','Lewis Carroll','Emece',19.95);

Vamos a mostrar los precios de los libros redondeando el valor hacia abajo y hacia arriba:
select titulo,autor,precio,
floor(precio) as abajo,
ceiling(precio) as arriba
from libros;

27 - Funciones para el uso de fechas y horas


Microsoft SQL Server ofrece algunas funciones para trabajar con fechas y horas. Estas son
algunas:
- getdate(): retorna la fecha y hora actuales. Ejemplo:
select getdate();

- datepart(partedefecha,fecha): retorna la parte específica de una fecha, el año, trimestre, día,


hora, etc.
Los valores para "partedefecha" pueden ser: year (año), quarter (cuarto), month (mes), day (dia),
week (semana), hour (hora), minute (minuto), second (segundo) y millisecond (milisegundo).
Ejemplos:
79
select datepart(month,getdate());

retorna el número de mes actual;


select datepart(day,getdate());

retorna el día actual;


select datepart(hour,getdate());

retorna la hora actual;


- datename(partedefecha,fecha): retorna el nombre de una parte específica 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 parámetro, tantas veces como lo indica el segundo parámetro. Los valores
para el primer argumento pueden ser: year (año), 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 días.


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 (según el primer
argumento) entre las 2 fechas. El resultado es un valor entero que corresponde a fecha2-fecha1.
Los valores de "partedelafecha) pueden ser los mismos que se especificaron anteriormente.
Ejemplos:
select datediff (day,'2005/10/28','2006/10/28');

retorna 365 (días).


select datediff(month,'2005/10/28','2006/11/29');

retorna 13 (meses).
- day(fecha): retorna el día de la fecha especificada. Ejemplo:

80
select day(getdate());

- month(fecha): retorna el mes de la fecha especificada. Ejemplo:


select month(getdate());

- year(fecha): retorna el año de la fecha especificada. Ejemplo:


select year(getdate());

Se pueden emplear estas funciones enviando como argumento el nombre de un campo de tipo
datetime o smalldatetime.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe:

if object_id ('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
titulo varchar(40) not null,
autor varchar(20) default 'Desconocido',
editorial varchar(20),
edicion datetime,
precio decimal(6,2)
);

Ingresamos algunos registros:


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

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


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

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


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

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


select titulo, datediff(year,edicion,getdate()) from libros;

Muestre los títulos de los libros que se editaron el día 9, de cualquier mes de cualquier año:
select titulo from libros
where datepart(day,edicion)=9;

Primer problema:

81
Una empresa almacena los datos de sus empleados en una tabla denominada "empleados".
1- Elimine la tabla si existe:
if object_id ('empleados') is not null
drop table empleados;

2- Cree la tabla:
create table empleados(
nombre varchar(30) not null,
apellido varchar(20) not null,
documento char(8),
fechanacimiento datetime,
fechaingreso datetime,
sueldo decimal(6,2),
primary key(documento)
);

3- Ingrese algunos registros:


insert into empleados
values('Ana','Acosta','22222222','1970/10/10','1995/05/05',228.50);
insert into empleados
values('Carlos','Caseres','25555555','1978/02/06','1998/05/05',309);
insert into empleados
values('Francisco','Garcia','26666666','1978/10/15','1998/10/02',250.68);
insert into empleados
values('Gabriela','Garcia','30000000','1985/10/25','2000/12/22',300.25);
insert into empleados
values('Luis','Lopez','31111111','1987/02/10','2000/08/21',350.98);

4- Muestre nombre y apellido concatenados, con el apellido en letras mayúsculas, el


documento
precedido por "DNI Nº " y el sueldo precedido por "$ ".

5- Muestre el documento y el sueldo redondeado hacia arriba y precedido por "$ ".

6- Muestre los nombres y apellidos de los empleados que cumplen años en el mes
"october" (3
registros)

7- Muestre los nombres y apellidos de los empleados que ingresaron en un determinado


año (2
registros).

28 - Ordenar registros (order by)


Podemos ordenar el resultado de un "select" para que los registros se muestren ordenados por
algún campo, para ello usamos la cláusula "order by".
La sintaxis básica es la siguiente:
select *from NOMBRETABLA
order by CAMPO;

Por ejemplo, recuperamos los registros de la tabla "libros" ordenados por el título:
select *from libros
order by titulo;

82
Aparecen los registros ordenados alfabéticamente por el campo especificado.
También podemos colocar el número de orden del campo por el que queremos que se ordene en
lugar de su nombre, es decir, referenciar a los campos por su posición en la lista de selección. Por
ejemplo, queremos el resultado del "select" ordenado por "precio":
select titulo,autor,precio
from libros order by 3;

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

También podemos ordenar por varios campos, por ejemplo, por "titulo" y "editorial":
select *from libros
order by titulo,editorial;

Incluso, podemos ordenar en distintos sentidos, por ejemplo, por "titulo" en sentido ascendente y
"editorial" en sentido descendente:
select *from libros
order by titulo asc, editorial desc;

Debe aclararse al lado de cada campo, pues estas palabras claves afectan al campo
inmediatamente anterior.
Es posible ordenar por un campo que no se lista en la selección.
Se permite ordenar por valores calculados o expresiones.
La cláusula "order by" no puede emplearse para campos text, ntext e image.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe:

if object_id ('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(20) default 'Desconocido',
editorial varchar(20),
precio decimal(6,2),
primary key (codigo)
);

Ingresamos algunos registros:


insert into libros (titulo,autor,editorial,precio)
values('El aleph','Borges','Emece',25.33);
insert into libros
values('Java en 10 minutos','Mario Molina','Siglo XXI',50.65);

83
insert into libros (titulo,autor,editorial,precio)
values('Alicia en el pais de las maravillas','Lewis Carroll','Emece',19.95);
insert into libros (titulo,autor,editorial,precio)
values('Alicia en el pais de las maravillas','Lewis Carroll','Planeta',15);

Recuperamos los registros ordenados por el título:


select *from libros
order by titulo;

Ordenamos los registros por el campo "precio", referenciando el campo por su posición en la lista de
selección:
select titulo,autor,precio
from libros order by 3;

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


select *from libros
order by editorial desc;

Ordenamos por dos campos:


select *from libros
order by titulo,editorial;

Ordenamos en distintos sentidos:


select *from libros
order by titulo asc, editorial desc;

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


select titulo, autor
from libros
order by precio;

Está permitido ordenar por valores calculados:


select titulo, autor, editorial,
precio+(precio*0.1) as 'precio con descuento'
from libros
order by 4;

Primer problema:

En una página web se guardan los siguientes datos de las visitas: número de visita,
nombre, mail,
pais, fecha.
1- Elimine la tabla "visitas", si existe:
if object_id('visitas') is not null
drop table visitas;

2- Créela con la siguiente estructura:


create table visitas (
numero int identity,
nombre varchar(30) default 'Anonimo',
mail varchar(50),
pais varchar (20),
fecha datetime,
primary key(numero)

84
);

3- Ingrese algunos registros:


insert into visitas (nombre,mail,pais,fecha)
values ('Ana Maria Lopez','AnaMaria@hotmail.com','Argentina','2006-10-10 10:10');
insert into visitas (nombre,mail,pais,fecha)
values ('Gustavo Gonzalez','GustavoGGonzalez@hotmail.com','Chile','2006-10-10
21:30');
insert into visitas (nombre,mail,pais,fecha)
values ('Juancito','JuanJosePerez@hotmail.com','Argentina','2006-10-11 15:45');
insert into visitas (nombre,mail,pais,fecha)
values ('Fabiola Martinez','MartinezFabiola@hotmail.com','Mexico','2006-10-12
08:15');
insert into visitas (nombre,mail,pais,fecha)
values ('Fabiola Martinez','MartinezFabiola@hotmail.com','Mexico','2006-09-12
20:45');
insert into visitas (nombre,mail,pais,fecha)
values ('Juancito','JuanJosePerez@hotmail.com','Argentina','2006-09-12 16:20');
insert into visitas (nombre,mail,pais,fecha)
values ('Juancito','JuanJosePerez@hotmail.com','Argentina','2006-09-15 16:25');

4- Ordene los registros por fecha, en orden descendente.

5- Muestre el nombre del usuario, pais y el nombre del mes, ordenado por pais
(ascendente) y nombre
del mes (descendente)

6- Muestre el pais, el mes, el día y la hora y ordene las visitas por nombre del mes,
del día y la
hora.

7- Muestre los mail, país, ordenado por país, de todos los que visitaron la página en
octubre (4
registros)

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


Hasta el momento, hemos aprendido a establecer una condición con "where" utilizando
operadores relacionales. Podemos establecer más de una condición con la cláusula "where",
para ello aprenderemos los operadores lógicos.
Son los siguientes:
- and, significa "y",
- or, significa "y/o",
- not, significa "no", invierte el resultado
- (), paréntesis

Los operadores lógicos se usan para combinar condiciones.


Si queremos recuperar todos los libros cuyo autor sea igual a "Borges" y cuyo precio no supere
los 20 pesos, necesitamos 2 condiciones:
select *from libros
where (autor='Borges') and
(precio<=20);
85
Los registros recuperados en una sentencia que une 2 condiciones con el operador "and",
cumplen con las 2 condiciones.
Queremos ver los libros cuyo autor sea "Borges" y/o cuya editorial sea "Planeta":
select *from libros
where autor='Borges' or
editorial='Planeta';

En la sentencia anterior usamos el operador "or"; indicamos que recupere los libros en los cuales
el valor del campo "autor" sea "Borges" y/o el valor del campo "editorial" sea "Planeta", es decir,
seleccionará los registros que cumplan con la primera condición, con la segunda condición 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 condición dada, por ejemplo, aquellos cuya
editorial NO sea "Planeta":
select *from libros
where not editorial='Planeta';

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


Los registros recuperados en una sentencia en la cual aparece el operador "not", no cumplen con
la condición a la cual afecta el "NOT".
Los paréntesis se usan para encerrar condiciones, para que se evalúen como una sola expresión.

Cuando explicitamos varias condiciones con diferentes operadores lógicos (combinamos "and",
"or") permite establecer el orden de prioridad de la evaluación; además permite diferenciar las
expresiones más claramente.
Por ejemplo, las siguientes expresiones devuelven un resultado diferente:
select*from libros
where (autor='Borges') or
(editorial='Paidos' and precio<20);

select *from libros


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

Si bien los paréntesis no son obligatorios en todos los casos, se recomienda utilizarlos para evitar
confusiones.
El orden de prioridad de los operadores lógicos es el siguiente: "not" se aplica antes que "and" y
"and" antes que "or", si no se especifica un orden de evaluación mediante el uso de paréntesis.
El orden en el que se evalúan los operadores con igual nivel de precedencia es indefinido, por ello
se recomienda usar los paréntesis.
Entonces, para establecer más de una condición en un "where" es necesario emplear operadores
lógicos. "and" significa "y", indica que se cumplan ambas condiciones; "or" significa "y/o", indica
que se cumpla una u otra condición (o ambas); "not" significa "no", indica que no se cumpla la
condición especificada.
86
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe:

if object_id ('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(20) default 'Desconocido',
editorial varchar(20),
precio decimal(6,2),
);

Ingresamos algunos registros:


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

Recuperamos los libros cuyo autor sea igual a "Borges" y cuyo precio no supere los 20 pesos:
select *from libros
where (autor='Borges') and
(precio<=20);

Seleccionamos los libros cuyo autor es "Borges" y/o cuya editorial es "Planeta":
select *from libros
where autor='Borges' or
editorial='Planeta';

Recuperamos los libros cuya editorial NO es "Planeta":


select *from libros
where not editorial='Planeta';

Veamos cómo el uso de paréntesis hace que SQL Server evalúe en forma diferente ciertas consultas
aparentemente iguales:
select*from libros
where (autor='Borges') or
(editorial='Paidos' and precio<20);

select *from libros


where (autor='Borges' or editorial='Paidos') and

87
(precio<20);

Primer problema:

Trabaje con la tabla llamada "medicamentos" de una farmacia.


1- Elimine la tabla, si existe:
if object_id('medicamentos') is not null
drop table medicamentos;

2- Cree la tabla con la siguiente estructura:


create table medicamentos(
codigo int identity,
nombre varchar(20),
laboratorio varchar(20),
precio decimal(5,2),
cantidad tinyint,
primary key(codigo)
);

3- Ingrese algunos registros:


insert into medicamentos
values('Sertal','Roche',5.2,100);
insert into medicamentos
values('Buscapina','Roche',4.10,200);
insert into medicamentos
values('Amoxidal 500','Bayer',15.60,100);
insert into medicamentos
values('Paracetamol 500','Bago',1.90,200);
insert into medicamentos
values('Bayaspirina','Bayer',2.10,150);
insert into medicamentos
values('Amoxidal jarabe','Bayer',5.10,250);

4- Recupere los códigos y nombres de los medicamentos cuyo laboratorio sea 'Roche' y
cuyo precio sea
menor a 5 (1 registro cumple con ambas condiciones)

5- Recupere los medicamentos cuyo laboratorio sea 'Roche' o cuyo precio sea menor a 5
(4 registros):
select * from medicamentos
where laboratorio='Roche' or
precio<5;
Note que el resultado es diferente al del punto 4, hemos cambiado el operador de la
sentencia
anterior.

6- Muestre todos los medicamentos cuyo laboratorio NO sea "Bayer" y cuya cantidad
sea=100 (1
registro)

7- Muestre todos los medicamentos cuyo laboratorio sea "Bayer" y cuya cantidad NO
sea=100 (2 registros):
select * from medicamentos
where laboratorio='Bayer' and
not cantidad=100;
Analice estas 2 últimas sentencias. El operador "not" afecta a la condición a la cual
antecede, no a
las siguientes. Los resultados de los puntos 6 y 7 son diferentes.

8- Elimine todos los registros cuyo laboratorio sea igual a "Bayer" y su precio sea
mayor a 10 (1
registro eliminado)

88
9- Cambie la cantidad por 200, a todos los medicamentos de "Roche" cuyo precio sea
mayor a 5 (1
registro afectado)

10- Borre los medicamentos cuyo laboratorio sea "Bayer" o cuyo precio sea menor a 3 (3
registros
borrados)

Segundo problema:

Trabajamos con la tabla "peliculas" de un video club que alquila películas en video.
1- Elimine la tabla, si existe;
if object_id('peliculas') is not null
drop table peliculas;

2- Créela con la siguiente estructura:


create table peliculas(
codigo int identity,
titulo varchar(40) not null,
actor varchar(20),
duracion tinyint,
primary key (codigo)
);

3- Ingrese algunos registros:


insert into peliculas
values('Mision imposible','Tom Cruise',120);
insert into peliculas
values('Harry Potter y la piedra filosofal','Daniel R.',180);
insert into peliculas
values('Harry Potter y la camara secreta','Daniel R.',190);
insert into peliculas
values('Mision imposible 2','Tom Cruise',120);
insert into peliculas
values('Mujer bonita','Richard Gere',120);
insert into peliculas
values('Tootsie','D. Hoffman',90);
insert into peliculas
values('Un oso rojo','Julio Chavez',100);
insert into peliculas
values('Elsa y Fred','China Zorrilla',110);

4- Recupere los registros cuyo actor sea "Tom Cruise" or "Richard Gere" (3 registros)

5- Recupere los registros cuyo actor sea "Tom Cruise" y duración menor a 100 (ninguno
cumple ambas
condiciones)

6- Cambie la duración a 200, de las películas cuyo actor sea "Daniel R." y cuya
duración sea 180 (1
registro afectado)

7- Borre todas las películas donde el actor NO sea "Tom Cruise" y cuya duración sea
mayor o igual a
100 (2 registros eliminados)

30 - Otros operadores relacionales (is null)


89
Hemos aprendido los operadores relacionales "=" (igual), "<>" (distinto), ">" (mayor), "<"
(menor), ">=" (mayor o igual) y "<=" (menor o igual). Dijimos que no eran los únicos.
Existen otro operador relacional "is null".
Se emplea el operador "is null" para recuperar los registros en los cuales esté almacenado el
valor "null" en un campo específico:
select *from libros
where editorial is null;

Para obtener los registros que no contiene "null", se puede emplear "is not null", esto mostrará los
registros con valores conocidos.
Siempre que sea posible, emplee condiciones de búsqueda positivas ("is null"), evite las
negativas ("is not null") porque con ellas se evalúan todos los registros y esto hace más lenta la
recuperación de los datos.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe:

if object_id ('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(20) default 'Desconocido',
editorial varchar(20),
precio decimal(6,2),
);

Ingresamos algunos registros:


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

Recuperamos los registros en los cuales esté almacenado el valor "null" en el campo "editorial":
select *from libros
where editorial is null;

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

90
select *from libros
where editorial is not null;

Primer problema:

Trabajamos con la tabla "peliculas" de un video club que alquila películas en video.
1- Elimine la tabla, si existe;
if object_id('peliculas') is not null
drop table peliculas;

2- Créela con la siguiente estructura:


create table peliculas(
codigo int identity,
titulo varchar(40) not null,
actor varchar(20),
duracion tinyint,
primary key (codigo)
);

3- Ingrese algunos registros:


insert into peliculas
values('Mision imposible','Tom Cruise',120);
insert into peliculas
values('Harry Potter y la piedra filosofal','Daniel R.',null);
insert into peliculas
values('Harry Potter y la camara secreta','Daniel R.',190);
insert into peliculas
values('Mision imposible 2','Tom Cruise',120);
insert into peliculas
values('Mujer bonita',null,120);
insert into peliculas
values('Tootsie','D. Hoffman',90);
insert into peliculas (titulo)
values('Un oso rojo');

4- Recupere las películas cuyo actor sea nulo (2 registros)

5- Cambie la duración a 0, de las películas que tengan duración igual a "null" (2


registros)

6- Borre todas las películas donde el actor sea "null" y cuya duración sea 0 (1
registro)

31 - Otros operadores relacionales (between)


Hemos visto los operadores relacionales: = (igual), <> (distinto), > (mayor), < (menor), >= (mayor
o igual), <= (menor o igual), is null/is not null (si un valor es NULL o no).
Otro operador relacional es "between", trabajan con intervalos de valores.
Hasta ahora, para recuperar de la tabla "libros" los libros con precio mayor o igual a 20 y menor o
igual a 40, usamos 2 condiciones unidas por el operador lógico "and":
select *from libros
where precio>=20 and
precio<=40;
91
Podemos usar "between" y así simplificar la consulta:
select *from libros
where precio between 20 and 40;

Averiguamos si el valor de un campo dado (precio) está entre los valores mínimo y máximo
especificados (20 y 40 respectivamente).
"between" significa "entre". Trabaja con intervalo de valores.
Este operador se puede emplear con tipos de datos numéricos y money (en tales casos incluyen
los valores mínimo y máximo) y tipos de datos fecha y hora (incluye sólo el valor mínimo).
No tiene en cuenta los valores "null".
Si agregamos el operador "not" antes de "between" el resultado se invierte, es decir, se recuperan
los registros que están fuera del intervalo especificado. Por ejemplo, recuperamos los libros cuyo
precio NO se encuentre entre 20 y 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 búsqueda positivas ("between"), evite las
negativas ("not between") porque hace más lenta la recuperación de los datos.
Entonces, se puede usar el operador "between" para reducir las condiciones "where".
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe:

if object_id ('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(20) default 'Desconocido',
editorial varchar(20),
precio decimal(6,2),
);

Ingresamos algunos registros:


insert into libros
values('El aleph','Borges','Emece',15.90);
insert into libros
values('Cervantes y el quijote','Borges','Paidos',null);
insert into libros
values('Alicia en el pais de las maravillas','Lewis Carroll',null,19.90);
insert into libros
values('Martin Fierro','Jose Hernandez','Emece',25.90);
insert into libros (titulo,autor,precio)
values('Antología poética','Borges',32);
insert into libros (titulo,autor,precio)
values('Java en 10 minutos','Mario Molina',45.80);
insert into libros (titulo,autor,precio)
values('Martin Fierro','Jose Hernandez',40);
insert into libros (titulo,autor,precio)
values('Aprenda PHP','Mario Molina',56.50);
92
Recuperamos los registros cuyo precio esté entre 20 y 40 empleando "between":
select *from libros
where precio between 20 and 40;

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


Para seleccionar los libros cuyo precio NO esté entre un intervalo de valores antecedemos "not" al
"between":
select *from libros
where precio not between 20 and 35;

Primer problema:

En una página web se guardan los siguientes datos de las visitas: número de visita,
nombre, mail,
pais, fechayhora de la visita.
1- Elimine la tabla "visitas", si existe:
if object_id('visitas') is not null
drop table visitas;

2- Créela con la siguiente estructura:


create table visitas (
numero int identity,
nombre varchar(30) default 'Anonimo',
mail varchar(50),
pais varchar (20),
fechayhora datetime,
primary key(numero)
);

3- Ingrese algunos registros:


insert into visitas (nombre,mail,pais,fechayhora)
values ('Ana Maria Lopez','AnaMaria@hotmail.com','Argentina','2006-10-10 10:10');
insert into visitas (nombre,mail,pais,fechayhora)
values ('Gustavo Gonzalez','GustavoGGonzalez@gotmail.com','Chile','2006-10-10
21:30');
insert into visitas (nombre,mail,pais,fechayhora)
values ('Juancito','JuanJosePerez@hotmail.com','Argentina','2006-10-11 15:45');
insert into visitas (nombre,mail,pais,fechayhora)
values ('Fabiola Martinez','MartinezFabiola@hotmail.com','Mexico','2006-10-12
08:15');
insert into visitas (nombre,mail,pais,fechayhora)
values ('Fabiola Martinez','MartinezFabiola@hotmail.com','Mexico','2006-09-12
20:45');
insert into visitas (nombre,mail,pais,fechayhora)
values ('Juancito','JuanJosePerez@gmail.com','Argentina','2006-09-12 16:20');
insert into visitas (nombre,mail,pais,fechayhora)
values ('Juancito','JuanJosePerez@hotmail.com','Argentina','2006-09-15 16:25');
insert into visitas (nombre,mail,pais)
values ('Federico1','federicogarcia@xaxamail.com','Argentina');

4- Seleccione los usuarios que visitaron la página entre el '2006-09-12' y '2006-10-11'


(5
registros)
Note que incluye los de fecha mayor o igual al valor mínimo y menores al valor máximo,
y que los
valores null no se incluyen.

5- Recupere las visitas cuyo número se encuentra entre 2 y 5 (4 registros)


Note que incluye los valores límites.

93
Segundo problema:

Una concesionaria de autos vende autos usados y almacena la información en una tabla
llamada
"autos".
1- Elimine la tabla "autos" si existe:
if object_id('autos') is not null
drop table autos;

2- Cree la tabla con la siguiente estructura:


create table autos(
patente char(6),
marca varchar(20),
modelo char(4),
precio decimal(8,2),
primary key(patente)
);

3- Ingrese algunos registros:


insert into autos
values('ACD123','Fiat 128','1970',15000);
insert into autos
values('ACG234','Renault 11','1980',40000);
insert into autos
values('BCD333','Peugeot 505','1990',80000);
insert into autos
values('GCD123','Renault Clio','1995',70000);
insert into autos
values('BCC333','Renault Megane','1998',95000);
insert into autos
values('BVF543','Fiat 128','1975',20000);

4- Seleccione todos los autos cuyo modelo se encuentre entre '1970' y '1990' usando el
operador
"between" y ordénelos por dicho campo(4 registros)

5- Seleccione todos los autos cuyo precio esté entre 50000 y 100000.

32 - Otros operadores relacionales (in)


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

Podemos usar "in" y simplificar la consulta:


select *from libros
where autor in('Borges','Paenza');
94
Para recuperar los libros cuyo autor no sea 'Paenza' ni 'Borges' usábamos:
select *from libros
where autor<>'Borges' and
autor<>'Paenza';

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


select *from libros
where autor not in ('Borges','Paenza');

Empleando "in" averiguamos si el valor del campo está incluido en la lista de valores especificada;
con "not" antecediendo la condición, invertimos el resultado, es decir, recuperamos los valores
que no se encuentran (coindicen) con la lista de valores.
Los valores "null" no se consideran.
Recuerde: siempre que sea posible, emplee condiciones de búsqueda positivas ("in"), evite las
negativas ("not in") porque con ellas se evalún todos los registros y esto hace más lenta la
recuperación de los datos.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe:

if object_id ('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(20),
editorial varchar(20),
precio decimal(6,2),
);

Ingresamos algunos registros:


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

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


select *from libros
where autor in('Borges','Paenza');

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


95
select *from libros
where autor not in ('Borges','Paenza');

Note que los valores "null" no se consideran.


Primer problema:

Trabaje con la tabla llamada "medicamentos" de una farmacia.


1- Elimine la tabla, si existe:
if object_id('medicamentos') is not null
drop table medicamentos;

2- Cree la tabla con la siguiente estructura:


create table medicamentos(
codigo int identity,
nombre varchar(20),
laboratorio varchar(20),
precio decimal(6,2),
cantidad tinyint,
fechavencimiento datetime not null,
primary key(codigo)
);

3- Ingrese algunos registros:


insert into medicamentos
values('Sertal','Roche',5.2,1,'2005-02-01');
insert into medicamentos
values('Buscapina','Roche',4.10,3,'2006-03-01');
insert into medicamentos
values('Amoxidal 500','Bayer',15.60,100,'2007-05-01');
insert into medicamentos
values('Paracetamol 500','Bago',1.90,20,'2008-02-01');
insert into medicamentos
values('Bayaspirina','Bayer',2.10,150,'2009-12-01');
insert into medicamentos
values('Amoxidal jarabe','Bayer',5.10,250,'2010-10-01');

4- Recupere los nombres y precios de los medicamentos cuyo laboratorio sea "Bayer" o
"Bago"
empleando el operador "in" (4 registros)

5- Seleccione los remedios cuya cantidad se encuentre entre 1 y 5 empleando el operador


"between" y
luego el operador "in" (2 registros):
select *from medicamentos
where cantidad between 1 and 5;
select *from medicamentos
where cantidad in (1,2,3,4,5);
Note que es más conveniente emplear, en este caso, el operador ""between".

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


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

96
Hemos realizado consultas utilizando operadores relacionales para comparar cadenas. Por
ejemplo, sabemos recuperar los libros cuyo autor sea igual a la cadena "Borges":
select *from libros
where autor='Borges';

El operador igual ("=") nos permite comparar cadenas de caracteres, pero al realizar la
comparación, busca coincidencias de cadenas completas, realiza una búsqueda exacta.
Imaginemos que tenemos registrados estos 2 libros:
"El Aleph", "Borges";
"Antologia poetica", "J.L. Borges";

Si queremos recuperar todos los libros de "Borges" y especificamos la siguiente condición:


select *from libros
where autor='Borges';

sólo aparecerá el primer registro, ya que la cadena "Borges" no es igual a la cadena "J.L.
Borges".
Esto sucede porque el operador "=" (igual), también el operador "<>" (distinto) comparan cadenas
de caracteres completas. Para comparar porciones de cadenas utilizamos los operadores "like" y
"not like".
Entonces, podemos comparar trozos de cadenas de caracteres para realizar consultas. Para
recuperar todos los registros cuyo autor contenga la cadena "Borges" debemos tipear:
select *from libros
where autor like "%Borges%";

El símbolo "%" (porcentaje) reemplaza cualquier cantidad de caracteres (incluyendo ningún


caracter). Es un caracter comodín. "like" y "not like" son operadores de comparación que señalan
igualdad o diferencia.
Para seleccionar todos los libros que comiencen con "M":
select *from libros
where titulo like 'M%';

Note que el símbolo "%" ya no está al comienzo, con esto indicamos que el título debe tener
como primera letra la "M" y luego, cualquier cantidad de caracteres.
Para seleccionar todos los libros que NO comiencen con "M":
select *from libros
where titulo not like 'M%';

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

97
Otro caracter comodín es [] reemplaza cualquier carácter contenido en el conjunto especificado
dentro de los corchetes.
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 comodín 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 búsquedas 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 '[';

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


Eliminamos la tabla si existe:

if object_id ('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(20) default 'Desconocido',

98
editorial varchar(20),
precio decimal(6,2),
primary key(codigo)
);

Ingresamos algunos registros:


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

Recuperamos todos los libros que contengan en el campo "autor" la cadena "Borges":
select *from libros
where autor like '%Borges%';

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


select *from libros
where titulo like 'M%';

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


select *from libros
where titulo not like 'M%';

Si queremos ver los libros de "Lewis Carroll" pero no recordamos si se escribe "Carroll" o "Carrolt",
podemos emplear el comodín "_" (guión bajo) y establecer la siguiente condición:
select *from libros
where autor like '%Carrol_';

Buscamos los libros cuya editorial comienza con las letras entre la "P" y la "S":
select titulo,autor,editorial
from libros
where editorial like '[P-S]%';

Seleccionamos los libros cuya editorial NO comienza con las letras "P" ni "N":
select titulo,autor,editorial
from libros
where editorial like '[^PN]%';

Recuperamos todos los libros cuyo precio se encuentra entre 10.00 y 19.99:
select titulo,precio from libros
where precio like '1_.%';

Recuperamos los libros que NO incluyen centavos en sus precios:


select titulo,precio from libros
where precio like '%.00';
99
Primer problema:

Una empresa almacena los datos de sus empleados en una tabla "empleados".
1- Elimine la tabla, si existe:
if object_id('empleados') is not null
drop table empleados;

2- Cree la tabla:
create table empleados(
nombre varchar(30),
documento char(8),
domicilio varchar(30),
fechaingreso datetime,
seccion varchar(20),
sueldo decimal(6,2),
primary key(documento)
);

3- Ingrese algunos registros:


insert into empleados
values('Juan Perez','22333444','Colon 123','1990-10-08','Gerencia',900.50);
insert into empleados
values('Ana Acosta','23444555','Caseros 987','1995-12-18','Secretaria',590.30);
insert into empleados
values('Lucas Duarte','25666777','Sucre 235','2005-05-15','Sistemas',790);
insert into empleados
values('Pamela Gonzalez','26777888','Sarmiento 873','1999-02-12','Secretaria',550);
insert into empleados
values('Marcos Juarez','30000111','Rivadavia 801','2002-09-22','Contaduria',630.70);
insert into empleados
values('Yolanda Perez','35111222','Colon 180','1990-10-08','Administracion',400);
insert into empleados
values('Rodolfo Perez','35555888','Coronel Olmedo 588','1990-05-28','Sistemas',800);

4- Muestre todos los empleados con apellido "Perez" empleando el operador "like" (3
registros)

5- Muestre todos los empleados cuyo domicilio comience con "Co" y tengan un "8" (2
registros)

6- Seleccione todos los empleados cuyo documento finalice en 0,2,4,6 u 8 (4 registros)

7- Seleccione todos los empleados cuyo documento NO comience con 1 ni 3 y cuyo nombre
finalice en
"ez" (2 registros)

8- Recupere todos los nombres que tengan una "y" o una "j" en su nombre o apellido (3
registros)

9- Muestre los nombres y sección de los empleados que pertenecen a secciones que
comiencen con "S" o
"G" y tengan 8 caracteres (3 registros)

10- Muestre los nombres y sección de los empleados que pertenecen a secciones que NO
comiencen con
"S" o "G" (2 registros)

11- Muestre todos los nombres y sueldos de los empleados cuyos sueldos incluyen
centavos (3
registros)

12- Muestre los empleados que hayan ingresado en "1990" (3 registros)

100
34 - Contar registros (count)
Existen en SQL Server funciones que nos permiten contar registros, calcular sumas, promedios,
obtener valores máximos y mínimos. Estas funciones se denominan funciones de agregado y
operan sobre un conjunto de valores (registros), no con datos individuales y devuelven un único
valor.
Imaginemos que nuestra tabla "libros" contiene muchos registros. Para averiguar la cantidad sin
necesidad de contarlos manualmente usamos la función "count()":
select count(*)
from libros;

La función "count()" cuenta la cantidad de registros de una tabla, incluyendo los que tienen valor
nulo.
También podemos utilizar esta función junto con la cláusula "where" para una consulta más
específica. Queremos saber la cantidad de libros de la editorial "Planeta":
select count(*)
from libros
where editorial='Planeta';

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

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

if object_id ('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(20) default 'Desconocido',
editorial varchar(20),
precio decimal(6,2),
primary key(codigo)
);

Ingresamos algunos registros:


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

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


select count(*)
from libros;

Note que incluye todos los libros aunque tengan valor nulo en algún campo.
Contamos los libros de editorial "Planeta":
select count(*)
from libros
where editorial='Planeta';

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

Primer problema:

Trabaje con la tabla llamada "medicamentos" de una farmacia.


1- Elimine la tabla, si existe:
if object_id('medicamentos') is not null
drop table medicamentos;

2- Cree la tabla con la siguiente estructura:


create table medicamentos(
codigo int identity,
nombre varchar(20),
laboratorio varchar(20),
precio decimal(6,2),
cantidad tinyint,
fechavencimiento datetime not null,
numerolote int default null,
primary key(codigo)
);

3- Ingrese algunos registros:


insert into medicamentos
values('Sertal','Roche',5.2,1,'2005-02-01',null);
insert into medicamentos
values('Buscapina','Roche',4.10,3,'2006-03-01',null);
insert into medicamentos
values('Amoxidal 500','Bayer',15.60,100,'2007-05-01',null);
insert into medicamentos
values('Paracetamol 500','Bago',1.90,20,'2008-02-01',null);

102
insert into medicamentos
values('Bayaspirina',null,2.10,null,'2009-12-01',null);
insert into medicamentos
values('Amoxidal jarabe','Bayer',null,250,'2009-12-15',null);

4- Muestre la cantidad de registros empleando la función "count(*)" (6 registros)

5- Cuente la cantidad de medicamentos que tienen laboratorio conocido (5 registros)

6- Cuente la cantidad de medicamentos que tienen precio distinto a "null" y que tienen
cantidad
distinto a "null", disponer alias para las columnas.

7- Cuente la cantidad de remedios con precio conocido, cuyo laboratorio comience con
"B" (2
registros)

8- Cuente la cantidad de medicamentos con número de lote distitno de "null" (0


registros)

35 - Contar registros (count_big)


Retorna la cantidad de registros. Es similar a la función "count(*)", la diferencia es que
"count_big" retorna un valor "bigint" y "count", un "int".
"count_big(*)" cuenta la cantidad de registros de una tabla, incluyendo los valores nulos y
duplicados.
"count_big(CAMPO)" retorna la cantidad de registros cuyo valor en el campo especificado entre
paréntesis 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 función "count_big()":
select count_big(*)
from libros;

Note que incluye todos los libros aunque tengan valor nulo en algún 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)
103
from libros;

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


Eliminamos la tabla si existe:

if object_id ('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(20) default 'Desconocido',
editorial varchar(20),
precio decimal(6,2),
primary key(codigo)
);

Ingresamos algunos registros:


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

Contamos los libros usando "count_big()":


select count_big(*)
from libros;

Note que incluye todos los libros aunque tengan valor nulo en algún campo.
Contamos los libros de editorial "Planeta":
select count_big(*)
from libros
where editorial='Planeta';

Averiguamos la cantidad de libros que tienen precio:


select count_big(precio)
from libros;

Contamos las editoriales (sin repetir):


select count_big(distinct editorial)
from libros;

Primer problema:

Trabaje con la tabla llamada "medicamentos" de una farmacia.


1- Elimine la tabla, si existe:
if object_id('medicamentos') is not null
drop table medicamentos;

2- Cree la tabla con la siguiente estructura:


104
create table medicamentos(
codigo int identity,
nombre varchar(20),
laboratorio varchar(20),
precio decimal(6,2),
cantidad tinyint,
fechavencimiento datetime not null,
numerolote int default null,
primary key(codigo)
);

3- Ingrese algunos registros:


insert into medicamentos
values('Sertal','Roche',5.2,1,'2005-02-01',null);
insert into medicamentos
values('Buscapina','Roche',4.10,3,'2006-03-01',null);
insert into medicamentos
values('Amoxidal 500','Bayer',15.60,100,'2007-05-01',null);
insert into medicamentos
values('Paracetamol 500','Bago',1.90,20,'2008-02-01',null);
insert into medicamentos
values('Bayaspirina',null,2.10,null,'2009-12-01',null);
insert into medicamentos
values('Amoxidal jarabe','Bayer',null,250,'2009-12-15',null);

4- Muestre la cantidad de registros empleando la función "count_big(*)" (6 registros)

5- Cuente la cantidad de laboratorios distintos (3 registros)

6- Cuente la cantidad de medicamentos que tienen precio y cantidad distinto de "null"


(5 y 5)

36 - Funciones de agrupamiento (count - sum -


min - max - avg)
Hemos visto que SQL Server tiene funciones que nos permiten contar registros, calcular sumas,
promedios, obtener valores máximos y mínimos, las funciones de agregado.
Ya hemos aprendido una de ellas, "count()", veamos otras.
Se pueden usar en una instrucción "select" y combinarlas con la cláusula "group by".
Todas estas funciones retornan "null" si ningún registro cumple con la condición del "where",
excepto "count" que en tal caso retorna cero.
El tipo de dato del campo determina las funciones que se pueden emplear con ellas.
Las relaciones entre las funciones de agrupamiento y los tipos de datos es la siguiente:
- count: se puede emplear con cualquier tipo de dato.
- min y max: con cualquier tipo de dato.
- sum y avg: sólo en campos de tipo numérico.

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

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

Entonces, dentro del paréntesis de la función colocamos el nombre del campo del cuál queremos
el máximo valor.
La función "avg()" retorna el valor promedio de los valores del campo especificado. Queremos
saber el promedio del precio de los libros referentes a "PHP":
select avg(precio)
from libros
where titulo like '%PHP%';

Ahora podemos entender porque estas funciones se denominan "funciones de agrupamiento",


porque operan sobre conjuntos de registros, no con datos individuales.
Tratamiento de los valores nulos:
Si realiza una consulta con la función "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".
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(30) default 'Desconocido',
editorial varchar(15),
precio decimal(5,2),
cantidad tinyint,
primary key(codigo)
);

Ingresamos algunos registros:


insert into libros
values('El aleph','Borges','Planeta',15,null);
106
insert into libros
values('Martin Fierro','Jose Hernandez','Emece',22.20,200);
insert into libros
values('Antologia poetica','J.L. Borges','Planeta',null,150);
insert into libros
values('Aprenda PHP','Mario Molina','Emece',18.20,null);
insert into libros
values('Cervantes y el quijote','Bioy Casares- J.L. Borges','Paidos',null,100);
insert into libros
values('Manual de PHP', 'J.C. Paez', 'Siglo XXI',31.80,120);
insert into libros
values('Harry Potter y la piedra filosofal','J.K. Rowling',default,45.00,90);
insert into libros
values('Harry Potter y la camara secreta','J.K. Rowling','Emece',46.00,100);
insert into libros (titulo,autor,cantidad)
values('Alicia en el pais de las maravillas','Lewis Carroll',220);
insert into libros (titulo,autor,cantidad)
values('PHP de la A a la Z',default,0);

Para conocer la cantidad total de libros, sumamos las cantidades de cada uno:
select sum(cantidad)
from libros;

Retorna 980; verifique la suma, sumando los valores de todos los registros del campo "cantidad".
Queremos saber cuántos libros tenemos de la editorial "Emece":
select sum(cantidad)
from libros
where editorial='Emece';

retorna 300.
Queremos saber cuál es el libro más costoso; usamos la función "max()":
select max(precio)
from libros;

retorna 46.00.
Para conocer el precio mínimo de los libros de "Rowling" tipeamos:
select min(precio)
from libros
where autor like '%Rowling%';

retorna 45.00.
Queremos saber el promedio del precio de los libros referentes a "PHP":
select avg(precio)
from libros
where titulo like '%PHP%';

Devuelve 25.00. Note que hay 3 libros sobre "PHP", pero uno de ellos tiene precio nulo entonces SQL
Server no lo incluye para calcular el promedio.
Primer problema:

Una empresa almacena los datos de sus empleados en una tabla "empleados".
1- Elimine la tabla, si existe:
107
if object_id('empleados') is not null
drop table empleados;

2- Cree la tabla:
create table empleados(
nombre varchar(30),
documento char(8),
domicilio varchar(30),
seccion varchar(20),
sueldo decimal(6,2),
cantidadhijos tinyint,
primary key(documento)
);

3- Ingrese algunos registros:


insert into empleados
values('Juan Perez','22333444','Colon 123','Gerencia',5000,2);
insert into empleados
values('Ana Acosta','23444555','Caseros 987','Secretaria',2000,0);
insert into empleados
values('Lucas Duarte','25666777','Sucre 235','Sistemas',4000,1);
insert into empleados
values('Pamela Gonzalez','26777888','Sarmiento 873','Secretaria',2200,3);
insert into empleados
values('Marcos Juarez','30000111','Rivadavia 801','Contaduria',3000,0);
insert into empleados
values('Yolanda Perez','35111222','Colon 180','Administracion',3200,1);
insert into empleados
values('Rodolfo Perez','35555888','Coronel Olmedo 588','Sistemas',4000,3);
insert into empleados
values('Martina Rodriguez','30141414','Sarmiento 1234','Administracion',3800,4);
insert into empleados
values('Andres Costa','28444555',default,'Secretaria',null,null);

4- Muestre la cantidad de empleados usando "count" (9 empleados)

5- Muestre la cantidad de empleados con sueldo no nulo de la sección "Secretaria" (2


empleados)

6- Muestre el sueldo más alto y el más bajo colocando un alias (5000 y 2000)

7- Muestre el valor mayor de "cantidadhijos" de los empleados "Perez" (3 hijos)

8- Muestre el promedio de sueldos de todo los empleados (3400. Note que hay un sueldo
nulo y no es
tenido en cuenta)

9- Muestre el promedio de sueldos de los empleados de la sección "Secretaría" (2100)

10- Muestre el promedio de hijos de todos los empleados de "Sistemas" (2)

37 - Agrupar registros (group by)


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

108
Las funciones de agregado solas producen un valor de resumen para todos los registros de un
campo. Podemos generar valores de resumen para un solo campo, combinando las funciones de
agregado con la cláusula "group by", que agrupa registros para consultas detalladas.
Queremos saber la cantidad de libros de cada editorial, podemos tipear la siguiente sentencia:
select count(*) from libros
where editorial='Planeta';

y repetirla con cada valor de "editorial":


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

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


select editorial, count(*)
from libros
group by editorial;

La instrucción anterior solicita que muestre el nombre de la editorial y cuente la cantidad


agrupando los registros por el campo "editorial". Como resultado aparecen los nombres de las
editoriales y la cantidad de registros para cada valor del campo.
Los valores nulos se procesan como otro grupo.
Entonces, para saber la cantidad de libros que tenemos de cada editorial, utilizamos la función
"count()", agregamos "group by" (que agrupa registros) y el campo por el que deseamos que se
realice el agrupamiento, también colocamos el nombre del campo a recuperar; la sintaxis básica
es la siguiente:
select CAMPO, FUNCIONDEAGREGADO
from NOMBRETABLA
group by CAMPO;

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

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

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

109
Recuerde la diferencia de los valores que retorna la función "count()" cuando enviamos como
argumento un asterisco o el nombre de un campo: en el primer caso cuenta todos los registros
incluyendo los que tienen valor nulo, en el segundo, los registros en los cuales el campo
especificado es no nulo.
Para conocer el total en dinero de los libros agrupados por editorial:
select editorial, sum(precio)
from libros
group by editorial;

Para saber el máximo y mínimo valor de los libros agrupados por editorial:
select editorial,
max(precio) as mayor,
min(precio) as menor
from libros
group by editorial;

Para calcular el promedio del valor de los libros agrupados por editorial:
select editorial, avg(precio)
from libros
group by editorial;

Es posible limitar la consulta con "where".


Si incluye una cláusula "where", sólo se agrupan los registros que cumplen las condiciones.
Vamos a contar y agrupar por editorial considerando solamente los libros cuyo precio sea menor a
30 pesos:
select editorial, count(*)
from libros
where precio<30
group by editorial;

Note que las editoriales que no tienen libros que cumplan la condición, no aparecen en la salida.
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.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla:

110
create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(15),
precio decimal(5,2),
cantidad tinyint,
primary key(codigo)
);

Ingresamos algunos registros:


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

Queremos saber la cantidad de libros de cada editorial, utilizando la cláusula "group by":
select editorial, count(*)
from libros
group by editorial;

El resultado muestra los nombres de las editoriales y la cantidad de registros para cada valor del campo.
Note que los valores nulos se procesan como otro grupo.
Obtenemos la cantidad libros con precio no nulo de cada editorial:
select editorial, count(precio)
from libros
group by editorial;

La salida muestra los nombres de las editoriales y la cantidad de registros de cada una, sin contar los que
tienen precio nulo.
Para conocer el total en dinero de los libros agrupados por editorial, tipeamos:
select editorial, sum(precio)
from libros
group by editorial;

Obtenemos el máximo y mínimo valor de los libros agrupados por editorial, en una sola sentencia:
select editorial,
max(precio) as mayor,
min(precio) as menor

111
from libros
group by editorial;

Calculamos el promedio del valor de los libros agrupados por editorial:


select editorial, avg(precio)
from libros
group by editorial;

Es posible limitar la consulta con "where". Vamos a contar y agrupar por editorial considerando solamente
los libros cuyo precio es menor a 30 pesos:
select editorial, count(*)
from libros
where precio<30
group by editorial;

Note que las editoriales que no tienen libros que cumplan la condición, 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;

Primer problema:

Un comercio que tiene un stand en una feria registra en una tabla llamada "visitantes"
algunos datos
de las personas que visitan o compran en su stand para luego enviarle publicidad de sus
productos.
1- Elimine la tabla "visitantes", si existe:
if object_id('visitantes') is not null
drop table visitantes;

2- Cree la tabla con la siguiente estructura:


create table visitantes(
nombre varchar(30),
edad tinyint,
sexo char(1) default 'f',
domicilio varchar(30),
ciudad varchar(20) default 'Cordoba',
telefono varchar(11),
mail varchar(30) default 'no tiene',
montocompra decimal (6,2)
);

3- Ingrese algunos registros:


insert into visitantes
values ('Susana Molina',35,default,'Colon 123',default,null,null,59.80);
insert into visitantes
values ('Marcos Torres',29,'m',default,'Carlos
Paz',default,'marcostorres@hotmail.com',150.50);
insert into visitantes
values ('Mariana Juarez',45,default,default,'Carlos Paz',null,default,23.90);
insert into visitantes (nombre, edad,sexo,telefono, mail)
values ('Fabian Perez',36,'m','4556677','fabianperez@xaxamail.com');
insert into visitantes (nombre, ciudad, montocompra)
values ('Alejandra Gonzalez','La Falda',280.50);
insert into visitantes (nombre, edad,sexo, ciudad, mail,montocompra)
values ('Gaston Perez',29,'m','Carlos Paz','gastonperez1@gmail.com',95.40);
112
insert into visitantes
values ('Liliana Torres',40,default,'Sarmiento 876',default,default,default,85);
insert into visitantes
values ('Gabriela Duarte',21,null,null,'Rio
Tercero',default,'gabrielaltorres@hotmail.com',321.50);

4- Queremos saber la cantidad de visitantes de cada ciudad utilizando la cláusula


"group by" (4 filas devueltas)

5- Queremos la cantidad visitantes con teléfono no nulo, de cada ciudad (4 filas


devueltas)

6- Necesitamos el total del monto de las compras agrupadas por sexo (3 filas)

7- Se necesita saber el máximo y mínimo valor de compra agrupados por sexo y ciudad (6
filas)

8- Calcule el promedio del valor de compra agrupados por ciudad (4 filas)

9- Cuente y agrupe por ciudad sin tener en cuenta los visitantes que no tienen mail (3
filas):

10- Realice la misma consulta anterior, pero use la palabra clave "all" para mostrar
todos los
valores de ciudad, incluyendo las que devuelven cero o "null" en la columna de agregado
(4 filas)

Segundo problema:

Una empresa almacena los datos de sus empleados en una tabla "empleados".
1- Elimine la tabla, si existe:
if object_id('empleados') is not null
drop table empleados;

2- Cree la tabla:
create table empleados(
nombre varchar(30),
documento char(8),
domicilio varchar(30),
seccion varchar(20),
sueldo decimal(6,2),
cantidadhijos tinyint,
fechaingreso datetime,
primary key(documento)
);

3- Ingrese algunos registros:


insert into empleados
values('Juan Perez','22333444','Colon 123','Gerencia',5000,2,'1980-05-10');
insert into empleados
values('Ana Acosta','23444555','Caseros 987','Secretaria',2000,0,'1980-10-12');
insert into empleados
values('Lucas Duarte','25666777','Sucre 235','Sistemas',4000,1,'1985-05-25');
insert into empleados
values('Pamela Gonzalez','26777888','Sarmiento 873','Secretaria',2200,3,'1990-06-
25');
insert into empleados
values('Marcos Juarez','30000111','Rivadavia 801','Contaduria',3000,0,'1996-05-01');
insert into empleados
values('Yolanda Perez','35111222','Colon 180','Administracion',3200,1,'1996-05-01');
insert into empleados
values('Rodolfo Perez','35555888','Coronel Olmedo 588','Sistemas',4000,3,'1996-05-
01');
insert into empleados
113
values('Martina Rodriguez','30141414','Sarmiento 1234','Administracion',3800,4,'2000-
09-01');
insert into empleados
values('Andres Costa','28444555',default,'Secretaria',null,null,null);

4- Cuente la cantidad de empleados agrupados por sección (5 filas)

5- Calcule el promedio de hijos por sección (5 filas):

6- Cuente la cantidad de empleados agrupados por año de ingreso (6 filas)

7- Calcule el promedio de sueldo por sección de los empleados con hijos (4 filas)

8- Realice la misma consulta anterior pero esta vez incluya las secciones que devuelven
cero o
"null" en la columna de agregado (5 filas)

38 - Seleccionar grupos (having)


Así como la cláusula "where" permite seleccionar (o rechazar) registros individuales; la cláusula
"having" permite seleccionar (o rechazar) un grupo de registros.
Si queremos saber la cantidad de libros agrupados por editorial usamos la siguiente instrucción ya
aprendida:
select editorial, count(*)
from libros
group by editorial;

Si queremos saber la cantidad de libros agrupados por editorial pero considerando sólo algunos
grupos, por ejemplo, los que devuelvan un valor mayor a 2, usamos la siguiente instrucción:
select editorial, count(*) from libros
group by editorial
having count(*)>2;

Se utiliza "having", seguido de la condición de búsqueda, para seleccionar ciertas filas retornadas
por la cláusula "group by".
Veamos otros ejemplos. Queremos el promedio de los precios 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 cláusulas "where" y "having". Queremos contar los
registros agrupados por editorial sin tener en cuenta a la editorial "Planeta".
Analicemos las siguientes sentencias:
select editorial, count(*) from libros
where editorial<>'Planeta'
group by editorial;
select editorial, count(*) from libros
114
group by editorial
having editorial<>'Planeta';

Ambas devuelven el mismo resultado, pero son diferentes. La primera, selecciona todos los
registros rechazando los de editorial "Planeta" y luego los agrupa para contarlos. La segunda,
selecciona todos los registros, los agrupa para contarlos y finalmente rechaza fila con la cuenta
correspondiente a la editorial "Planeta".
No debemos confundir la cláusula "where" con la cláusula "having"; la primera establece
condiciones para la selección de registros de un "select"; la segunda establece condiciones para
la selección de registros de una salida "group by".
Veamos otros ejemplos combinando "where" y "having". Queremos la cantidad de libros, sin
considerar los que tienen precio nulo, agrupados por editorial, sin considerar la editorial "Planeta":
select editorial, count(*) from libros
where precio is not null
group by editorial
having editorial<>'Planeta';

Aquí, selecciona los registros rechazando los que no cumplan con la condición dada en "where",
luego los agrupa por "editorial" y finalmente rechaza los grupos que no cumplan con la condición
dada en el "having".
Se emplea la cláusula "having" con funciones de agrupamiento, esto no puede hacerlo la cláusula
"where". Por ejemplo queremos el promedio de los precios agrupados por editorial, de aquellas
editoriales que tienen más de 2 libros:
select editorial, avg(precio) from libros
group by editorial
having count(*) > 2;

En una cláusula "having" puede haber hasta 128 condiciones. Cuando utilice varias condiciones,
tiene que combinarlas con operadores lógicos (and, or, not).
Podemos encontrar el mayor valor de los libros agrupados y ordenados por editorial y seleccionar
las filas que tengan un valor menor a 100 y mayor a 30:
select editorial, max(precio) as 'mayor'
from libros
group by editorial
having min(precio)<100 and
min(precio)>30
order by editorial;

Entonces, usamos la claúsula "having" para restringir las filas que devuelve una salida "group by".
Va siempre después de la cláusula "group by" y antes de la cláusula "order by" si la hubiere.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla:
115
create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(15),
precio decimal(5,2),
cantidad tinyint,
primary key(codigo)
);

Ingresamos algunos registros:


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

Queremos saber la cantidad de libros agrupados por editorial pero considerando sólo algunos grupos, por
ejemplo, los que devuelvan un valor mayor a 2, usamos la siguiente instrucción:
select editorial, count(*) from libros
group by editorial
having count(*)>2;

Queremos el promedio de los precios de los libros agrupados por editorial, pero solamente de aquellos
grupos cuyo promedio supere los 25 pesos:
select editorial, avg(precio) from libros
group by editorial
having avg(precio)>25;

Queremos la cantidad de libros, sin considerar los que tienen precio nulo (where), agrupados por editorial
(group by), sin considerar la editorial "Planeta" (having):
select editorial, count(*) from libros
where precio is not null
group by editorial
having editorial<>'Planeta';

Necesitamos el promedio de los precios agrupados por editorial, de aquellas editoriales que tienen más de 2
libros:
116
select editorial, avg(precio) from libros
group by editorial
having count(*) > 2;

Buscamos el mayor valor de los libros agrupados y ordenados por editorial y seleccionamos las filas que
tienen un valor menor a 100 y mayor a 30:
select editorial, max(precio) as 'mayor'
from libros
group by editorial
having max(precio)<100 and
max(precio)>30
order by editorial;

Primer problema:

Una empresa tiene registrados sus clientes en una tabla llamada "clientes".
1- Elimine la tabla "clientes", si existe:
if object_id('clientes') is not null
drop table clientes;

2- Créela con la siguiente estructura:


create table clientes (
codigo int identity,
nombre varchar(30) not null,
domicilio varchar(30),
ciudad varchar(20),
provincia varchar (20),
telefono varchar(11),
primary key(codigo)
);

3- Ingrese algunos registros:


insert into clientes
values ('Lopez Marcos','Colon 111','Cordoba','Cordoba','null');
insert into clientes
values ('Perez Ana','San Martin 222','Cruz del Eje','Cordoba','4578585');
insert into clientes
values ('Garcia Juan','Rivadavia 333','Villa del Rosario','Cordoba','4578445');
insert into clientes
values ('Perez Luis','Sarmiento 444','Rosario','Santa Fe',null);
insert into clientes
values ('Pereyra Lucas','San Martin 555','Cruz del Eje','Cordoba','4253685');
insert into clientes
values ('Gomez Ines','San Martin 666','Santa Fe','Santa Fe','0345252525');
insert into clientes
values ('Torres Fabiola','Alem 777','Villa del Rosario','Cordoba','4554455');
insert into clientes
values ('Lopez Carlos',null,'Cruz del Eje','Cordoba',null);
insert into clientes
values ('Ramos Betina','San Martin 999','Cordoba','Cordoba','4223366');
insert into clientes
values ('Lopez Lucas','San Martin 1010','Posadas','Misiones','0457858745');

4- Obtenga el total de los registros agrupados por ciudad y provincia (6 filas)

5- Obtenga el total de los registros agrupados por ciudad y provincia sin considerar
los que tienen
menos de 2 clientes (3 filas)

6- Obtenga el total de los clientes que viven en calle "San Martin" (where), agrupados
por provincia

117
(group by), teniendo en cuenta todos los valores (all), de aquellas ciudades que tengan
menos de 2
clientes (having) y omitiendo la fila correspondiente a la ciudad de "Cordoba" (having)
(4 filas
devueltas)

Segundo problema:

Un comercio que tiene un stand en una feria registra en una tabla llamada "visitantes"
algunos datos
de las personas que visitan o compran en su stand para luego enviarle publicidad de sus
productos.
1- Elimine la tabla "visitantes", si existe:
if object_id('visitantes') is not null
drop table visitantes;

2- Créela con la siguiente estructura:


create table visitantes(
nombre varchar(30),
edad tinyint,
sexo char(1),
domicilio varchar(30),
ciudad varchar(20),
telefono varchar(11),
montocompra decimal(6,2) not null
);

3- Ingrese algunos registros:


insert into visitantes
values ('Susana Molina',28,'f',null,'Cordoba',null,45.50);
insert into visitantes
values ('Marcela Mercado',36,'f','Avellaneda 345','Cordoba','4545454',22.40);
insert into visitantes
values ('Alberto Garcia',35,'m','Gral. Paz 123','Alta Gracia','03547123456',25);
insert into visitantes
values ('Teresa Garcia',33,'f',default,'Alta Gracia','03547123456',120);
insert into visitantes
values ('Roberto Perez',45,'m','Urquiza 335','Cordoba','4123456',33.20);
insert into visitantes
values ('Marina Torres',22,'f','Colon 222','Villa Dolores','03544112233',95);
insert into visitantes
values ('Julieta Gomez',24,'f','San Martin 333','Alta Gracia',null,53.50);
insert into visitantes
values ('Roxana Lopez',20,'f','null','Alta Gracia',null,240);
insert into visitantes
values ('Liliana Garcia',50,'f','Paso 999','Cordoba','4588778',48);
insert into visitantes
values ('Juan Torres',43,'m','Sarmiento 876','Cordoba',null,15.30);

4- Obtenga el total de las compras agrupados por ciudad y sexo de aquellas filas que
devuelvan un
valor superior a 50 (3 filas)

5- Obtenga el total de las compras agrupados por ciudad y sexo (group by), teniendo en
cuenta todos
los valores (all), considerando sólo los montos de compra superiores a 50 (where), los
visitantes
con teléfono (where), sin considerar la ciudad de "Cordoba" (having), ordenados por
ciudad (order
by) (3 filas)

6- Muestre el monto mayor de compra agrupado por ciudad, siempre que dicho valor supere
los 50 pesos

118
(having), considerando sólo los visitantes de sexo femenino y domicilio conocido
(where) (2 filas)

7- Agrupe por ciudad y sexo, muestre para cada grupo el total de visitantes, la suma de
sus compras
y el promedio de compras, ordenado por la suma total y considerando las filas con
promedio superior
a 30 (3 filas)

39 - Modificador del group by (with rollup)


Podemos combinar "group by" con los operadores "rollup" y "cube" para generar valores de
resumen a la salida.
El operador "rollup" resume valores de grupos. representan los valores de resumen de la
precedente.
Tenemos la tabla "visitantes" con los siguientes campos: nombre, edad, sexo, domicilio, ciudad,
telefono, montocompra.
Si necesitamos la cantidad de visitantes por ciudad empleamos la siguiente sentencia:
select ciudad,count(*) as cantidad
from visitantes
group by ciudad;

Esta consulta muestra el total de visitantes agrupados por ciudad; pero si queremos además 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 cláusula "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;

119
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 agrupación,
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;

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".
Un comercio que tiene un stand en una feria registra en una tabla llamada "visitantes" algunos datos de las
personas que visitan o compran en su stand para luego enviarle publicidad de sus productos.

Eliminamos la tabla si existe:


if object_id('visitantes') is not null
drop table visitantes;

La creamos con la siguiente estructura:


create table visitantes(
nombre varchar(30),
edad tinyint,
sexo char(1),
domicilio varchar(30),
ciudad varchar(20),
telefono varchar(11),
montocompra decimal(6,2) not null
);

Ingresamos algunos registros:


insert into visitantes
values ('Susana Molina',28,'f',null,'Cordoba',null,45.50);
insert into visitantes
values ('Marcela Mercado',36,'f','Avellaneda 345','Cordoba','4545454',22.40);
insert into visitantes
values ('Alberto Garcia',35,'m','Gral. Paz 123','Alta Gracia','03547123456',25);
insert into visitantes
values ('Teresa Garcia',33,'f',default,'Alta Gracia','03547123456',120);
insert into visitantes
values ('Roberto Perez',45,'m','Urquiza 335','Cordoba','4123456',33.20);
insert into visitantes
120
values ('Marina Torres',22,'f','Colon 222','Villa Dolores','03544112233',95);
insert into visitantes
values ('Julieta Gomez',24,'f','San Martin 333','Alta Gracia',null,53.50);
insert into visitantes
values ('Roxana Lopez',20,'f','null','Alta Gracia',null,240);
insert into visitantes
values ('Liliana Garcia',50,'f','Paso 999','Cordoba','4588778',48);
insert into visitantes
values ('Juan Torres',43,'m','Sarmiento 876','Cordoba',null,15.30);

Necesitamos la cantidad de visitantes por ciudad y el total de visitantes. Para obtener ambos resultados en
una sola consulta empleamos "with rollup":
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 (la cuarta) en la que la
primera columna contiene "null" y la columna con la cantidad muestra la cantidad total.
Veamos las filas de resumen cuando 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 (5 filas) y produce 4 filas extras, 3 muestran los totales para
cada ciudad (con la columna correspondiente al sexo conteniendo "null") y 1 mostrando el total de todos los
visitantes (con las columnas correspondientes a la ciudad y al sexo conteniendo "null").
Podemos incluir varias funciones de agrupamiento; para conocer la cantidad de visitantes y la suma de sus
compras agrupados por ciudad y sexo, tipeamos:
select ciudad,sexo,
count(*) as cantidad,
sum(montocompra) as total
from visitantes
group by ciudad,sexo
with rollup;

Note que por cada agrupación aparece una fila extra con valores de resumen.
Primer problema:

Una empresa tiene registrados sus clientes en una tabla llamada "clientes".
1- Elimine la tabla "clientes", si existe:
if object_id('clientes') is not null
drop table clientes;

2- Créela con la siguiente estructura:


create table clientes (
codigo int identity,
nombre varchar(30) not null,
domicilio varchar(30),
ciudad varchar(20),
estado varchar (20),
pais varchar(20),
primary key(codigo)
121
);

3- Ingrese algunos registros:


insert into clientes
values ('Lopez Marcos','Colon 111', 'Cordoba','Cordoba','Argentina');
insert into clientes
values ('Perez Ana','San Martin 222', 'Carlos Paz','Cordoba','Argentina');
insert into clientes
values ('Garcia Juan','Rivadavia 333', 'Carlos Paz','Cordoba','Argentina');
insert into clientes
values ('Perez Luis','Sarmiento 444', 'Rosario','Santa Fe','Argentina');
insert into clientes
values ('Gomez Ines','San Martin 987', 'Santa Fe','Santa Fe','Argentina');
insert into clientes
values ('Gomez Ines','San Martin 666', 'Santa Fe','Santa Fe','Argentina');
insert into clientes
values ('Lopez Carlos','Irigoyen 888', 'Cordoba','Cordoba','Argentina');
insert into clientes
values ('Ramos Betina','San Martin 999', 'Cordoba','Cordoba','Argentina');
insert into clientes
values ('Fernando Salas','Mariano Osorio 1234', 'Santiago','Region
metropolitana','Chile');
insert into clientes
values ('German Rojas','Allende 345', 'Valparaiso','Region V','Chile');
insert into clientes
values ('Ricardo Jara','Pablo Neruda 146', 'Santiago','Region
metropolitana','Chile');
insert into clientes
values ('Joaquin Robles','Diego Rivera 147', 'Guadalajara','Jalisco','Mexico');

4- Necesitamos la cantidad de clientes por país y la cantidad total de clientes en una


sola consulta
(4 filas)
Note que la consulta retorna los registros agrupados por pais y una fila extra en la
que la columna
"pais" contiene "null" y la columna con la cantidad muestra la cantidad total.

5- Necesitamos la cantidad de clientes agrupados por pais y estado, incluyendo


resultados paciales
(9 filas)
Note que la salida muestra los totales por pais y estado y produce 4 filas extras: 3
muestran los
totales para cada pais, con la columna "estado" conteniendo "null" y 1 muestra el total
de todos los
clientes, con las columnas "pais" y "estado" conteniendo "null".

6- Necesitamos la cantidad de clientes agrupados por pais, estado y ciudad, empleando


"rollup" (16
filas)
El resultado muestra los totales por pais, estado y ciudad y genera 9 filas extras: 5
muestran los
totales para cada estado, con la columna correspondiente a "ciudad" conteniendo "null",
3 muestran
los totales para cada pais, con las columnas "ciudad" y "estado" conteniendo "null" y 1
muestra el
total de todos los clientes, con las columnas "pais", "estado" y "ciudad" conteniendo
"null".

Segundo problema:

Un instituto de enseñanza guarda las notas de sus alumnos en una tabla llamada "notas".
1- Elimine la tabla si existe:
if object_id('notas') is not null
drop table notas;
122
2- Cree la tabla con la siguiente estructura:
create table notas(
documento char(8) not null,
materia varchar(30),
nota decimal(4,2)
);

3-Ingrese algunos registros:


insert into notas values ('22333444','Programacion',8);
insert into notas values ('22333444','Programacion',9);
insert into notas values ('22333444','Ingles',8);
insert into notas values ('22333444','Ingles',7);
insert into notas values ('22333444','Ingles',6);
insert into notas values ('22333444','Sistemas de datos',10);
insert into notas values ('22333444','Sistemas de datos',9);

insert into notas values ('23444555','Programacion',5);


insert into notas values ('23444555','Programacion',4);
insert into notas values ('23444555','Programacion',3);
insert into notas values ('23444555','Ingles',9);
insert into notas values ('23444555','Ingles',7);
insert into notas values ('23444555','Sistemas de datos',9);

insert into notas values ('24555666','Programacion',1);


insert into notas values ('24555666','Programacion',3.5);
insert into notas values ('24555666','Ingles',4.5);
insert into notas values ('24555666','Sistemas de datos',6);

4- Se necesita el promedio por alumno por materia y el promedio de cada alumno en todas
las materias
cursadas hasta el momento (13 registros):
select documento,materia,
avg(nota) as promedio
from notas
group by documento,materia with rollup;
Note que hay 4 filas extras, 3 con el promedio de cada alumno y 1 con el promedio de
todos los
alumnos de todas las materias.

5- Compruebe los resultados parciales de la consulta anterior realizando otra consulta


mostrando el
promedio de todas las carreras de cada alumno (4 filas)

6- Muestre la cantidad de notas de cada alumno, por materia (9 filas)

7- Realice la misma consulta anterior con resultados parciales incluidos (13 filas)

8- Muestre la nota menor y la mayor de cada alumno y la menor y mayor nota de todos
(use "rollup")
(4 filas)

40 - Modificador del group by (with cube)


Hemos aprendido el modificador "rollup", que agrega filas extras mostrando resultados de
resumen por cada grupo y subgrupo.

123
Por ejemplo, tenemos una tabla llamada "empleados" que contiene, entre otros, los campos
"sexo", "estadocivil" y "seccion".
Si se agrupa por esos tres campos (en ese orden) y se emplea "rollup":
select sexo,estadocivil,seccion,
count(*) from empleados
group by sexo,estadocivil,seccion
with rollup;

SQL Server genera varias filas extras con información de resumen para los siguientes subgrupos:
- sexo y estadocivil (seccion seteado a "null"),
- sexo (estadocivil y seccion seteados a "null") y
- total (todos los campos seteados a "null").

Si se emplea "cube":
select sexo,estadocivil,seccion,
count(*) from empleados
group by sexo,estadocivil,seccion
with cube;

retorna más filas extras además 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"),

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".
Una empresa tiene registrados sus empleados en una tabla llamada "empleados".
Elimine la tabla si existe:

if object_id('empleados') is not null


drop table empleados;

Créela con la siguiente estructura:


create table empleados (
documento varchar(8) not null,
nombre varchar(30),
sexo char(1),
estadocivil char(1),--c=casado, s=soltero,v=viudo
seccion varchar(20),
primary key (documento)
);

Ingrese algunos registros:


insert into empleados
values ('22222222','Alberto Lopez','m','c','Sistemas');
insert into empleados
values ('23333333','Beatriz Garcia','f','c','Administracion');
insert into empleados
124
values ('24444444','Carlos Fuentes','m','s','Administracion');
insert into empleados
values ('25555555','Daniel Garcia','m','s','Sistemas');
insert into empleados
values ('26666666','Ester Juarez','f','c','Sistemas');
insert into empleados
values ('27777777','Fabian Torres','m','s','Sistemas');
insert into empleados
values ('28888888','Gabriela Lopez','f','c','Sistemas');
insert into empleados
values ('29999999','Hector Garcia','m','c','Administracion');
insert into empleados
values ('30000000','Ines Torres','f','c','Administracion');
insert into empleados
values ('11111111','Juan Garcia','m','v','Administracion');
insert into empleados
values ('12222222','Luisa Perez','f','v','Administracion');
insert into empleados
values ('31111111','Marcela Garcia','f','s','Administracion');
insert into empleados
values ('32222222','Nestor Fuentes','m','c','Sistemas');
insert into empleados
values ('33333333','Oscar Garcia','m','s','Sistemas');
insert into empleados
values ('34444444','Patricia Juarez','f','c','Administracion');
insert into empleados
values ('35555555','Roberto Torres','m','c','Sistemas');
insert into empleados
values ('36666666','Susana Torres','f','c','Administracion');

Agrupamos por "sexo", "estadocivil" y "seccion" empleando "rollup":


select sexo,estadocivil,seccion,
count(*) from empleados
group by sexo,estadocivil,seccion
with rollup;

SQL Server genera varias filas extras con información de resumen para los siguientes subgrupos:
- sexo y estadocivil (seccion seteado a "null"),
- sexo (estadocivil e hijos seteados a "null") y
- total (todos los campos seteados a "null").

Si empleamos "cube":
select sexo,estadocivil,seccion,
count(*) from empleados
group by sexo,estadocivil,seccion
with cube;

retorna más filas extras además 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 e hijos seteados a "null").

Primer problema:

Un comercio guarda la información de sus ventas en una tabla llamada "ventas" en la que
se registra
diariamente, el número de venta, el monto total de la compra, el tipo de pago
(c=contado, t=tarjeta)
125
y el nombre del vendedor.
1- Elimine la tabla si existe:
if object_id('ventas') is not null
drop table ventas;

2- Cree la tabla:
create table ventas(
numero int identity,
montocompra decimal(6,2),
tipopago char(1),--c=contado, t=tarjeta
vendedor varchar(30),
primary key (numero)
);

3- Ingrese algunos registros:


insert into ventas
values(100.50,'c','Marisa Perez');
insert into ventas
values(200,'c','Marisa Perez');
insert into ventas
values(50,'t','Juan Lopez');
insert into ventas
values(220,'c','Juan Lopez');
insert into ventas
values(150,'t','Marisa Perez');
insert into ventas
values(550.80,'c','Marisa Perez');
insert into ventas
values(300,'t','Juan Lopez');
insert into ventas
values(25,'c','Marisa Perez');

4- Agrupe por "tipopago" y "vendedor" y cuente la cantidad empleando "rollup".


Las agrupaciones de resumen son las siguientes:
- vendedor (tipopago seteado a "null"), 2 filas y
- total (todos los campos seteados a "null"), 1 fila

5- Agrupe por "tipopago" y "vendedor" y cuente la cantidad empleando "cube".


Las agrupaciones de resumen son las siguientes:
- vendedor (tipopago seteado a "null"), 2 filas,
- total (todos los campos seteados a "null"), 1 fila y
- tipopago (vendedor seteado a "null"), 2 filas.

Segundo problema:

Un comercio que tiene un stand en una feria registra en una tabla llamada "visitantes"
algunos datos
de las personas que visitan o compran en su stand para luego enviarle publicidad de sus
productos.
1- Elimine la tabla "visitantes", si existe:
if object_id('visitantes') is not null
drop table visitantes;

2- Créela con la siguiente estructura:


create table visitantes(
nombre varchar(30),
edad tinyint,
sexo char(1),
domicilio varchar(30),
ciudad varchar(20),
mail varchar(30) default 'no tiene',
montocompra decimal(6,2)
);

126
3- Ingrese algunos registros:
insert into visitantes
values ('Susana Molina',28,'f',null,'Cordoba',null,45.50);
insert into visitantes
values ('Marcela Mercado',36,'f','Avellaneda 345','Cordoba',default,22.40);
insert into visitantes
values ('Alberto Garcia',35,'m',default,'Alta
Gracia','albertogarcia@hotmail.com',25);
insert into visitantes
values ('Teresa Garcia',33,'f',default,'Alta Gracia',default,120);
insert into visitantes
values ('Roberto Perez',45,'m',null,'Cordoba','robertoperez@xaxamail.com',33.20);
insert into visitantes
values ('Marina Torres',22,'f',null,'Villa Dolores',default,95);
insert into visitantes
values ('Julieta Gomez',24,'f','San Martin 333','Alta
Gracia','julietagomez@gmail.com',53.50);
insert into visitantes
values ('Roxana Lopez',20,'f','null','Alta Gracia',default,240);
insert into visitantes
values ('Liliana Garcia',50,'f','Paso 999','Cordoba',default,48);
insert into visitantes
values ('Juan Torres',43,'m','Sarmiento 876','Cordoba',null,15.30);

4- Agrupe por sexo y ciudad y calcule el total de la compra empleando "rollup" y luego
"cube":
select sexo,ciudad,
sum(montocompra) as total
from visitantes
group by sexo,ciudad
with rollup;
Se generan 2 filas con "ciudad" conteniendo "null" y 1 fila con ambos campos seteados a
"null".
select sexo,ciudad,
sum(montocompra) as total
from visitantes
group by sexo,ciudad
with cube;
Se generan 2 filas con "ciudad" conteniendo "null" (total de sexo femenino y
masculino); 1 fila con
ambos campos seteados a "null" (monto total de todos los visitantes) y 3 filas con
"sexo"
conteniendo "null" (monto total de cada ciudad).

5- Calcule la edad promedio de los visitantes agrupando por sexo y ciudad, sin
considerar los que no
tienen mail, use ambos modificadores de "group by" ("rollup" y "cube"):
select sexo,ciudad,
avg(edad) as 'edad promedio'
from visitantes
where mail is not null and
mail <>'no tiene'
group by sexo,ciudad
with rollup;
Se generan 2 filas con el promedio de edad por sexo de cada ciudad y 1 fila con el
promedio de edad
de todos los visitantes.
select sexo,ciudad,
avg(edad) as 'edad promedio'
from visitantes
where mail is not null and
mail <>'no tiene'
group by sexo,ciudad
with cube;

127
Se generan 2 filas con el promedio de edad por sexo de cada ciudad, 1 fila con el
promedio de edad
de todos los visitantes y 3 filas con el promedio de edad por ciudad.

41 - Función grouping
La función "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 función 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.
Sólo se puede emplear la función "grouping" en los campos que aparecen en la cláusula "group
by".
Si tenemos una tabla "visitantes" con los siguientes registros almacenados:
Nombre sexo ciudad
-------------------------------
Susana Molina f Cordoba
Marcela Mercado f Cordoba
Roberto Perez f null
Alberto Garcia m Cordoba
Teresa Garcia f 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;

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

128
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 función "grouping" para distinguir los valores de detalle y de resumen en el
resultado.
Un comercio que tiene un stand en una feria registra en una tabla llamada "visitantes" algunos datos de las
personas que visitan o compran en su stand para luego enviarle publicidad de sus productos.
Eliminamos la tabla si existe:

if object_id('visitantes') is not null


drop table visitantes;

La creamos con la siguiente estructura:


create table visitantes(
nombre varchar(30),
sexo char(1),
ciudad varchar(20)
);

Ingresamos algunos registros:


insert into visitantes values('Susana Molina', 'f', 'Cordoba');
insert into visitantes values('Marcela Mercado', 'f','Cordoba');
insert into visitantes values('Roberto Perez','f',null);
insert into visitantes values('Alberto Garcia','m','Cordoba');
insert into visitantes values('Teresa Garcia','f','Alta Gracia');

Contamos la cantidad de visitantes agrupando por ciudad y empleando "rollup":


select ciudad,
count(*) as cantidad
from visitantes
group by ciudad
with rollup;

Note que la última fila es la de resumen generada por "rollup", pero no es sencillo 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

129
with rollup;

Note que 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, lo cual indica que el
valor "null" es un valor del campo "ciudad".
Primer problema:

Una empresa tiene registrados sus empleados en una tabla llamada "empleados".
1- Elimine la tabla si existe:
if object_id('empleados') is not null
drop table empleados;

2- Créela con la siguiente estructura:


create table empleados (
documento varchar(8) not null,
nombre varchar(30),
sexo char(1),
estadocivil char(1),--c=casado, s=soltero,v=viudo
seccion varchar(20),
primary key (documento)
);

3- Ingrese algunos registros:


insert into empleados
values ('22222222','Alberto Lopez','m','c','Sistemas');
insert into empleados
values ('23333333','Beatriz Garcia','f','c','Administracion');
insert into empleados
values ('24444444','Carlos Fuentes','m','s','Administracion');
insert into empleados
values ('25555555','Daniel Garcia','m','s','Sistemas');
insert into empleados
values ('26666666','Ester Juarez',null,'c','Sistemas');
insert into empleados
values ('27777777','Fabian Torres',null,'s','Sistemas');
insert into empleados
values ('28888888','Gabriela Lopez','f',null,'Sistemas');
insert into empleados
values ('29999999','Hector Garcia','m',null,'Administracion');

4- Cuente la cantidad de empleados agrupados por sexo y estado civil, empleando


"rollup".
Es dificil distinguir los valores de detalle y resumen.

5- Realice la misma consulta anterior pero emplee la función "grouping" para los dos
campos por los
que se agrupa para distinguir los valores de resumen y de detalle.
Note que las columnas de resumen contienen 1 y las de detalle 0.

6- Realice la misma consulta anterior pero con "cube" en lugar de "rollup",


distinguiendo los
valores de resumen y de detalle.
Note que las columnas de resumen contienen 1 y las de detalle 0.

42 - Cláusulas compute y compute by


130
Las cláusulas "compute" y "compute by" generan totales que aparecen en columnas extras al
final del resultado.
Produce filas de detalle y un valor único para una columna.
Se usa con las funciones de agrupamiento: avg(), count(), max(), min(), sum().
La sintaxis básica y general es la siguiente:
select CAMPOS
from TABLA
compute FUNCION(CAMPO);

El campo que se coloque en la cláusula "compute" debe estar incluida en la lista de campos del
"select".
Para ver todos los datos de los visitantes y el promedio del monto de compra de nuestra tabla
"visitantes":
select *from visitantes
compute avg(montocompra);

Produce la misma salida que las siguientes 2 sentencias:


select *from visitantes;
select avg(montocompra) from visitantes;

En una misma instrucción se pueden colocar varias cláusulas "compute":


select edad,ciudad,montocompra
from visitantes
compute avg(edad),sum(montocompra);

"Compute by" genera cortes de control y subtotales. Se generan filas de detalle y varios valores
de resumen cuando cambian los valores del campo.
Con "compute by" se DEBE usar también la cláusula "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 función 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 cláusula "compute by" DEBEN ser idénticos a un
subconjunto de los campos que aparecen después de "order by" y estar en el mismo orden. Si la
cláusula "order by" tiene los siguientes campos:
... order by a,b,c...

la cláusula "compute by" puede incluir los siguientes subconjuntos de campos:


131
... compute ...
by a...
o
... compute ...
by a,b...
o
... compute ...
by a,b,c...

En una misma instrucción se pueden colocar varias cláusulas "compute" combinadas con varias
cláusulas "compute by":
select *from visitantes
order by provincia,ciudad
compute avg(edad), sum(montocompra)
compute avg(montocompra),count(provincia)
by provincia,ciudad;

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 cláusula "compute" o "compute
by".
Un comercio que tiene un stand en una feria registra en una tabla llamada "visitantes" algunos datos de las
personas que visitan o compran en su stand para luego enviarle publicidad de sus productos.
Eliminamos la tabla si existe:

if object_id('visitantes') is not null


drop table visitantes;

La creamos con la siguiente estructura:


create table visitantes(
nombre varchar(30),
edad tinyint,
ciudad varchar(20),
provincia varchar(20),
mail varchar(30),
montocompra decimal(6,2)
);

Ingresamos algunos registros:


insert into visitantes
values ('Susana Molina',28,'Cordoba','Cordoba','susanamolina@gmail.com',50);
insert into visitantes
values ('Marcela Mercado',36,'Carlos Paz','Cordoba',null,20);
insert into visitantes
values ('Alberto Garcia',35,'La Falda','Cordoba','albertgomgarcia@hotmail.com',30);
insert into visitantes
values ('Teresa Garcia',33,'Alta Gracia','Cordoba',null,120);
insert into visitantes
values ('Roberto Perez',45,'Alta
Gracia','Cordoba','robertomarioperez@hotmail.com',30);
insert into visitantes
values ('Marina Torres',22,'Rosario','Santa Fe','marinatorres@xaxamail.com',90);
insert into visitantes
values ('Julieta Gomez',24,'Rosario','Santa Fe',null,50);

132
insert into visitantes
values ('Roxana Lopez',20,'Alta Gracia','Cordoba',null,200);
insert into visitantes
values ('Liliana Garcia',50,'Capital Federal','Buenos
Aires','lilianagarcia@hotmail.com',40);
insert into visitantes
values ('Juan Torres',43,'Carlos Paz','Cordoba',null,10);

Veamos todos los datos de los visitantes y el promedio del monto de compra:
select *from visitantes
compute avg(montocompra);

Empleamos dos cláusulas "compute" en una misma instrucción para averiguar el promedio de las edades y el
total de los montos de las compras:
select edad,ciudad,montocompra
from visitantes
compute avg(edad),sum(montocompra);

Veamos la cantidad de visitantes por provincia y ciudad empleando "compute by". Recuerde que DEBE
usarse "order by" y los campos que se incluyan luego de "by" deben estar en el "order by":
select nombre,ciudad,provincia
from visitantes
order by provincia,ciudad
compute count(provincia)
by provincia,ciudad;

Combinamos dos cláusulas "compute" con dos cláusulas "compute by" para averiguar el promedio de la
compra y la cantidad por provincia y ciudad y el promedio de las edades y el total del monto de compras de
todos los visitantes:
select *from visitantes
order by provincia,ciudad
compute avg(edad), sum(montocompra)
compute avg(montocompra),count(provincia)
by provincia,ciudad;

Primer problema:

La provincia almacena en una tabla llamada "inmuebles" los siguientes datos de los
inmuebles y sus
propietarios para cobrar impuestos:
1- Elimine la tabla si existe:
if object_id('inmuebles') is not null
drop table inmuebles;

2- Créela con la siguiente estructura:


create table inmuebles (
documento varchar(8) not null,
nombre varchar(30),
domicilio varchar(20),
barrio varchar(20),
ciudad varchar(20),
tipo char(1),--b=baldio, e: edificado
superficie decimal (8,2),
monto decimal (8,2)
);

3- Ingrese algunos registros:


133
insert into inmuebles
values ('11111111','Alberto Acosta','Avellaneda
800','Centro','Cordoba','e',100,1200);
insert into inmuebles
values ('11111111','Alberto Acosta','Sarmiento 245','Gral.
Paz','Cordoba','e',200,2500);
insert into inmuebles
values ('22222222','Beatriz Barrios','San Martin
202','Centro','Cordoba','e',250,1900);
insert into inmuebles
values ('33333333','Carlos Caseres','Paso 1234','Alberdi','Cordoba','b',200,1000);
insert into inmuebles
values ('33333333','Carlos Caseres','Guemes 876','Alberdi','Cordoba','b',300,1500);
insert into inmuebles
values ('44444444','Diana Dominguez','Calderon
456','Matienzo','Cordoba','b',200,800);
insert into inmuebles
values ('55555555','Estela Fuentes','San Martin 321','Flores','Carlos
Paz','e',500,4500);
insert into inmuebles
values ('55555555','Estela Fuentes','Lopez y Planes
853','Alberdi','Cordoba','e',350,2200);

4- Muestre todos los datos y el promedio del monto empleando "compute" (1 resultado
parcial)

5- Empleando cláusulas "compute" consulte el promedio de las superficies y el total de


los montos (2
columnas extras)

6- Realice la misma consulta anterior pero empleando "compute by" para obtener
resultados parciales
por documento,barrio y ciudad.

7- Realice la misma consulta anterior pero con resultados parciales por documento y
barrio (6
resultados parciales dobles)

8- Realice la misma consulta anterior pero con resultados parciales por documento (4
resultados
parciales dobles)

9- Intente realizar la misma consulta anterior pero con resultados parciales por
documento y ciudad.
Aparece un mensaje de error indicando que el subgrupo de campos listados luego del "by"
no es
correcto.

10- Combine cláusulas "compute" con "compute by" para averiguar el total de monto a
pagar por
propietario y el promedio de monto de todos (4 resultados parciales y 1 general)

43 - Registros duplicados (distinct)


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

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


select distinct autor from libros;

También podemos tipear:


select autor from libros
group by autor;

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

Para contar los distintos autores, sin considerar el valor "null" usamos:
select count(distinct autor)
from libros;

Note que si contamos los autores sin "distinct", no incluirá los valores "null" pero si los repetidos:
select count(autor)
from libros;

Esta sentencia cuenta los registros que tienen autor.


Podemos combinarla con "where". Por ejemplo, queremos conocer los distintos autores de la
editorial "Planeta":
select distinct autor from libros
where editorial='Planeta';

También puede utilizarse con "group by" para contar los diferentes autores por editorial:
select editorial, count(distinct autor)
from libros
group by editorial;

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

Note que los registros no están duplicados, aparecen títulos iguales pero con editorial diferente,
cada registro es diferente.
La palabra clave "distinct" no está permitida con las cláusulas "compute" y "compute by".
Entonces, "distinct" elimina registros duplicados.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:

135
if object_id('libros') is not null
drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(15),
primary key(codigo)
);

Ingresamos algunos registros:


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

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


select distinct autor from libros;

Note que aparece "null" como un valor para "autor"· Para obtener la lista de autores conocidos, es decir, no
incluyendo "null" en la lista:
select distinct autor from libros
where autor is not null;

Contamos los distintos autores:


select count(distinct autor)
from libros;

Queremos los nombres de las editoriales sin repetir:


select distinct editorial from libros;

136
Queremos saber la cantidad de editoriales distintas:
select count(distinct editorial) from libros;

La combinamos con "where" para obtener los distintos autores de la editorial "Planeta":
select distinct autor from libros
where editorial='Planeta';

Contamos los distintos autores que tiene cada editorial empleando "group by":
select editorial,count(distinct autor)
from libros
group by editorial;

Mostramos los títulos y editoriales de los libros sin repetir títulos ni editoriales:
select distinct titulo,editorial
from libros
order by titulo;

Note que los registros no están duplicados, aparecen títulos iguales pero con editorial diferente, cada registro
es diferente.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(15),
primary key(codigo)
);

Ingresamos algunos registros:


insert into libros
values('El aleph','Borges','Planeta');
insert into libros
values('Martin Fierro','Jose Hernandez','Emece');
insert into libros
values('Martin Fierro','Jose Hernandez','Planeta');
insert into libros
values('Antologia poetica','Borges','Planeta');
insert into libros
values('Aprenda PHP','Mario Molina','Emece');
insert into libros
values('Aprenda PHP','Lopez','Emece');
insert into libros
values('Manual de PHP', 'J. Paez', null);
insert into libros
values('Cervantes y el quijote',null,'Paidos');
insert into libros
values('Harry Potter y la piedra filosofal','J.K. Rowling','Emece');
insert into libros

137
values('Harry Potter y la camara secreta','J.K. Rowling','Emece');
insert into libros
values('Alicia en el pais de las maravillas','Lewis Carroll','Paidos');
insert into libros
values('Alicia en el pais de las maravillas','Lewis Carroll','Planeta');
insert into libros
values('PHP de la A a la Z',null,null);
insert into libros
values('Uno','Richard Bach','Planeta');

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


select distinct autor from libros;

Note que aparece "null" como un valor para "autor"· Para obtener la lista de autores conocidos, es decir, no
incluyendo "null" en la lista:
select distinct autor from libros
where autor is not null;

Contamos los distintos autores:


select count(distinct autor)
from libros;

Queremos los nombres de las editoriales sin repetir:


select distinct editorial from libros;

Queremos saber la cantidad de editoriales distintas:


select count(distinct editorial) from libros;

La combinamos con "where" para obtener los distintos autores de la editorial "Planeta":
select distinct autor from libros
where editorial='Planeta';

Contamos los distintos autores que tiene cada editorial empleando "group by":
select editorial,count(distinct autor)
from libros
group by editorial;

Mostramos los títulos y editoriales de los libros sin repetir títulos ni editoriales:
select distinct titulo,editorial
from libros
order by titulo;

Note que los registros no están duplicados, aparecen títulos iguales pero con editorial diferente, cada registro
es diferente.
Primer problema:

Una empresa tiene registrados sus clientes en una tabla llamada "clientes".
1- Elimine la tabla "clientes", si existe:
if object_id('clientes') is not null
drop table clientes;

2- Créela con la siguiente estructura:


138
create table clientes (
codigo int identity,
nombre varchar(30) not null,
domicilio varchar(30),
ciudad varchar(20),
provincia varchar (20),
primary key(codigo)
);

3- Ingrese algunos registros:


insert into clientes
values ('Lopez Marcos','Colon 111','Cordoba','Cordoba');
insert into clientes
values ('Perez Ana','San Martin 222','Cruz del Eje','Cordoba');
insert into clientes
values ('Garcia Juan','Rivadavia 333','Villa del Rosario','Cordoba');
insert into clientes
values ('Perez Luis','Sarmiento 444','Rosario','Santa Fe');
insert into clientes
values ('Pereyra Lucas','San Martin 555','Cruz del Eje','Cordoba');
insert into clientes
values ('Gomez Ines','San Martin 666','Santa Fe','Santa Fe');
insert into clientes
values ('Torres Fabiola','Alem 777','Villa del Rosario','Cordoba');
insert into clientes
values ('Lopez Carlos',null,'Cruz del Eje','Cordoba');
insert into clientes
values ('Ramos Betina','San Martin 999','Cordoba','Cordoba');
insert into clientes
values ('Lopez Lucas','San Martin 1010','Posadas','Misiones');

4- Obtenga las provincias sin repetir (3 registros)

5- Cuente las distintas provincias.

6- Se necesitan los nombres de las ciudades sin repetir (6 registros)

7- Obtenga la cantidad de ciudades distintas.

8- Combine con "where" para obtener las distintas ciudades de la provincia de Cordoba
(3 registros)

9- Contamos las distintas ciudades de cada provincia empleando "group by" (3 registros)

Segundo problema:

La provincia almacena en una tabla llamada "inmuebles" los siguientes datos de los
inmuebles y sus
propietarios para cobrar impuestos:
1- Elimine la tabla si existe:
if object_id('inmuebles') is not null
drop table inmuebles;

2- Créela con la siguiente estructura:


create table inmuebles (
documento varchar(8) not null,
apellido varchar(30),
nombre varchar(30),
domicilio varchar(20),
barrio varchar(20),
ciudad varchar(20),
tipo char(1),--b=baldio, e: edificado
superficie decimal (8,2)
);
139
3- Ingrese algunos registros:
insert into inmuebles
values ('11000000','Perez','Alberto','San Martin 800','Centro','Cordoba','e',100);
insert into inmuebles
values ('11000000','Perez','Alberto','Sarmiento 245','Gral. Paz','Cordoba','e',200);
insert into inmuebles
values ('12222222','Lopez','Maria','San Martin 202','Centro','Cordoba','e',250);
insert into inmuebles
values ('13333333','Garcia','Carlos','Paso 1234','Alberdi','Cordoba','b',200);
insert into inmuebles
values ('13333333','Garcia','Carlos','Guemes 876','Alberdi','Cordoba','b',300);
insert into inmuebles
values ('14444444','Perez','Mariana','Caseros 456','Flores','Cordoba','b',200);
insert into inmuebles
values ('15555555','Lopez','Luis','San Martin 321','Centro','Carlos Paz','e',500);
insert into inmuebles
values ('15555555','Lopez','Luis','Lopez y Planes 853','Flores','Carlos
Paz','e',350);
insert into inmuebles
values ('16666666','Perez','Alberto','Sucre 1877','Flores','Cordoba','e',150);

4- Muestre los distintos apellidos de los propietarios, sin repetir (3 registros)

5- Muestre los distintos documentos de los propietarios, sin repetir (6 registros)

6- Cuente, sin repetir, la cantidad de propietarios de inmuebles de la ciudad de


Cordoba (5)

7- Cuente la cantidad de inmuebles con domicilio en 'San Martin', sin repetir la ciudad
(2)

8- Muestre los apellidos y nombres, sin repetir (5 registros)


Note que hay 2 personas con igual nombre y apellido que aparece una sola vez.

9- Muestre la cantidad de inmuebles que tiene cada propietario agrupando por documento,
sin repetir
barrio (6 registros)

44 - Cláusula top
La palabra clave "top" se emplea para obtener sólo 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 número entero positivo y luego se
continúa con la consulta.
Se puede combinar con "order by":
select top 3 titulo,autor
from libros
order by autor;

140
En la consulta anterior solicitamos los títulos y autores de los 3 primeros libros, ordenados por
autor.
Cuando se combina con "order by" es posible emplear también la cláusula "with ties". Esta
cláusula 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
número 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 número 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 selección.
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 número 4 (y
los posteriores), tengan el mismo valor en "autor" que el último registro retornado (número 3),
también aparecerán en la selección.
Si colocamos un valor para "top" que supera la cantidad de registros de la tabla, SQL Server
muestra todos los registros.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(20),
editorial varchar(20)
);

Ingresamos algunos registros:


insert into libros values ('Uno','Richard Bach','Planeta');
insert into libros values ('El aleph','Borges','Emece');
insert into libros values ('Alicia en el pais...','Carroll','Planeta');
insert into libros values ('Aprenda PHP','Mario Molina','Siglo XXI');
insert into libros values ('Java en 10 minutos','Mario Molina','Siglo XXI');
insert into libros values ('Java desde cero','Mario Molina','Emece');
insert into libros values ('Ilusiones','Richard Bach','Planeta');

Obtenemos todos los datos de los primeros 2 libros de la tabla:


select top 2 *from libros;

Mostramos los títulos y autores de los 3 primeros libros ordenados por autor:
select top 3 titulo,autor
from libros
order by autor;

141
Realizamos la misma consulta anterior pero empleamos la cláusula "with ties", con lo cual incluiremos en la
selección, todos los registros que tengan el mismo autor que el último registro retornado, aunque pasemos de
3:
select top 3 with ties titulo,autor
from libros
order by autor;

Note que retorna los 5 primeros registros porque incluye los dos siguientes que tienen el mismo valor que el
último en el campo "autor" (por el cual se ordena).
Primer problema:

Una empresa tiene registrados sus empleados en una tabla llamada "empleados".
1- Elimine la tabla si existe:
if object_id('empleados') is not null
drop table empleados;

2- Créela con la siguiente estructura:


create table empleados (
documento varchar(8) not null,
nombre varchar(30),
estadocivil char(1),--c=casado, s=soltero,v=viudo
seccion varchar(20)
);

3- Ingrese algunos registros:


insert into empleados
values ('22222222','Alberto Lopez','c','Sistemas');
insert into empleados
values ('23333333','Beatriz Garcia','c','Administracion');
insert into empleados
values ('24444444','Carlos Fuentes','s','Administracion');
insert into empleados
values ('25555555','Daniel Garcia','s','Sistemas');
insert into empleados
values ('26666666','Ester Juarez','c','Sistemas');
insert into empleados
values ('27777777','Fabian Torres','s','Sistemas');
insert into empleados
values ('28888888','Gabriela Lopez',null,'Sistemas');
insert into empleados
values ('29999999','Hector Garcia',null,'Administracion');

4- Muestre los 5 primeros registros (5 registros)

5- Muestre nombre y seccion de los 4 primeros registros ordenados por sección (4


registros)

6- Realice la misma consulta anterior pero incluya todos los registros que tengan el
mismo valor en
"seccion" que el último (8 registros)

7- Muestre nombre, estado civil y seccion de los primeros 4 empleados ordenados por
estado civil y
sección (4 registros)

8- Realice la misma consulta anterior pero incluya todos los valores iguales al último
registro
retornado (5 registros)

142
45 - Clave primaria compuesta
Las claves primarias pueden ser simples, formadas por un solo campo o compuestas, más de
un campo.
Recordemos que una clave primaria identifica 1 solo registro en una tabla.
Para un valor del campo clave existe solamente 1 registro. Los valores no se repiten ni pueden
ser nulos.
Existe una playa de estacionamiento que almacena cada día los datos de los vehículos que
ingresan en la tabla llamada "vehiculos" con los siguientes campos:
- patente char(6) not null,
- tipo char (1), 'a'= auto, 'm'=moto,
- horallegada datetime,
- horasalida datetime,

Necesitamos definir una clave primaria para una tabla con los datos descriptos arriba. No
podemos usar solamente la patente porque un mismo auto puede ingresar más de una vez en el
día a la playa; tampoco podemos usar la hora de entrada porque varios autos pueden ingresar a
una misma hora.
Tampoco sirven los otros campos.
Como ningún campo, por si sólo cumple con la condición para ser clave, es decir, debe identificar
un solo registro, el valor no puede repetirse, debemos usar 2 campos.
Definimos una clave compuesta cuando ningún campo por si solo cumple con la condición para
ser clave.
En este ejemplo, un auto puede ingresar varias veces en un día a la playa, pero siempre será a
distinta hora.
Usamos 2 campos como clave, la patente junto con la hora de llegada, así identificamos
unívocamente cada registro.
Para establecer más de un campo como clave primaria usamos la siguiente sintaxis:
create table vehiculos(
patente char(6) not null,
tipo char(1),--'a'=auto, 'm'=moto
horallegada datetime,
horasalida datetime,
primary key(patente,horallegada)
);

Nombramos los campos que formarán 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 estén repetidos en la tabla; si estuviesen repetidos, muestra un mensaje y la
inserción no se realiza. Lo mismo sucede si realizamos una actualización.

143
Entonces, si un solo campo no identifica unívocamente un registro podemos definir una clave
primaria compuesta, es decir formada por más de un campo.
Una playa de estacionamiento almacena cada día los datos de los vehículos que ingresan en la tabla llamada
"vehiculos".
Eliminamos la tabla, si existe:

if object_id('vehiculos') is not null


drop table vehiculos;

Creamos la tabla estableciendo dos campos como clave primaria:


create table vehiculos(
patente char(6) not null,
tipo char(1),--'a'=auto, 'm'=moto
horallegada datetime,
horasalida datetime,
primary key(patente,horallegada)
);

Ingresamos algunos registros:


insert into vehiculos values('AIC124','a','8:05','12:30');
insert into vehiculos values('CAA258','a','8:05',null);
insert into vehiculos values('DSE367','m','8:30','18:00');
insert into vehiculos values('FGT458','a','9:00',null);
insert into vehiculos values('AIC124','a','16:00',null);
insert into vehiculos values('LOI587','m','18:05','19:55');

Si intentamos ingresar un registro con clave primaria repetida:


insert into vehiculos values('LOI587','m','18:05',null);

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


Si ingresamos un registro repitiendo el valor de uno de los campos que forman parte de la clave, si lo acepta:
insert into vehiculos values('LOI587','m','21:30',null);

Si intentamos actualizar un registro repitiendo la clave primaria:


update vehiculos set horallegada='8:05'
where patente='AIC124' and horallegada='16:00';

aparece un mensaje de error y la actualización no se realiza.


Recordemos que los campos que forman parte de la clave primaria no aceptan valores nulos, aunque no se
haya aclarado en la definición de la tabla:
insert into vehiculos values('HUO690','m',null,null);

Si mostramos la estructura de la tabla:


sp_columns vehiculos;

vemos que los campos que forman parte de la clave primaria (patente y horallegada) tienen "NO" en la
columna "IS_NULLABLE", es decir, no admiten valores nulos.
Primer problema:
144
Un consultorio médico en el cual trabajan 3 médicos registra las consultas de los
pacientes en una
tabla llamada "consultas".
1- Elimine la tabla si existe:
if object_id('consultas') is not null
drop table consultas;

2- La tabla contiene los siguientes datos:


- fechayhora: datetime not null, fecha y hora de la consulta,
- medico: varchar(30), not null, nombre del médico (Perez,Lopez,Duarte),
- documento: char(8) not null, documento del paciente,
- paciente: varchar(30), nombre del paciente,
- obrasocial: varchar(30), nombre de la obra social (IPAM,PAMI, etc.).
);

3- Un médico sólo puede atender a un paciente en una fecha y hora determianada. En una
fecha y hora
determinada, varios médicos atienden a distintos pacientes. Cree la tabla definiendo
una clave
primaria compuesta:
create table consultas(
fechayhora datetime not null,
medico varchar(30) not null,
documento char(8) not null,
paciente varchar(30),
obrasocial varchar(30),
primary key(fechayhora,medico)
);

4- Ingrese varias consultas para un mismo médico en distintas horas el mismo día.

5- Ingrese varias consultas para diferentes médicos en la misma fecha y hora.

6- Intente ingresar una consulta para un mismo médico en la misma hora el mismo día.

Segundo problema:

Un club dicta clases de distintos deportes. En una tabla llamada "inscriptos" almacena
la
información necesaria.
1- Elimine la tabla "inscriptos" si existe:
if object_id('inscriptos') is not null
drop table inscriptos;

2- La tabla contiene los siguientes campos:


- documento del socio alumno: char(8) not null
- nombre del socio: varchar(30),
- nombre del deporte (tenis, futbol, natación, basquet): varchar(15) not null,
- año de inscripcion: datetime,
- matrícula: si la matrícula ha sido o no pagada ('s' o 'n').

3- Necesitamos una clave primaria que identifique cada registro. Un socio puede
inscribirse en
varios deportes en distintos años. Un socio no puede inscribirse en el mismo deporte el
mismo año.
Varios socios se inscriben en un mismo deporte en distintos años. Cree la tabla con una
clave
compuesta:
create table inscriptos(
documento char(8) not null,
nombre varchar(30),
deporte varchar(15) not null,
año datetime,
matricula char(1),
145
primary key(documento,deporte,año)
);

4- Inscriba a varios alumnos en el mismo deporte en el mismo año:


insert into inscriptos
values ('12222222','Juan Perez','tenis','2005','s');
insert into inscriptos
values ('23333333','Marta Garcia','tenis','2005','s');
insert into inscriptos
values ('34444444','Luis Perez','tenis','2005','n');

5- Inscriba a un mismo alumno en varios deportes en el mismo año:


insert into inscriptos
values ('12222222','Juan Perez','futbol','2005','s');
insert into inscriptos
values ('12222222','Juan Perez','natacion','2005','s');
insert into inscriptos
values ('12222222','Juan Perez','basquet','2005','n');

6- Ingrese un registro con el mismo documento de socio en el mismo deporte en distintos


años:
insert into inscriptos
values ('12222222','Juan Perez','tenis','2006','s');
insert into inscriptos
values ('12222222','Juan Perez','tenis','2007','s');

7- Intente inscribir a un socio alumno en un deporte en el cual ya esté inscripto en un


año en el
cual ya se haya inscripto.

8- Intente actualizar un registro para que la clave primaria se repita.

46 - Integridad de los datos


Es importante, al diseñar una base de datos y las tablas que contiene, tener en cuenta la
integridad de los datos, esto significa que la información almacenada en las tablas debe ser
válida, coherente y exacta.
Hasta el momento, hemos controlado y restringido la entrada de valores a un campo mediante el
tipo de dato que le definimos (cadena, numéricos, etc.), la aceptación o no de valores nulos, el
valor por defecto. También hemos asegurado que cada registro de una tabla sea único definiendo
una clave primaria y empleando la propiedad identity.
SQL Server ofrece más alternativas, además de las aprendidas, para restringir y validar los datos,
las veremos ordenadamente y al finalizar haremos un resumen de las mismas.
Comenzamos por las restricciones.
Las restricciones (constraints) son un método para mantener la integridad de los datos,
asegurando que los valores ingresados sean válidos y que las relaciones entre las tablas se
mantenga. Se establecen a los campos y las tablas.

146
Pueden definirse al crear la tabla ("create table") o agregarse a una tabla existente (empleando
"alter table") y se pueden aplicar a un campo o a varios. Se aconseja crear las tablas y luego
agregar las restricciones.
Se pueden crear, modificar y eliminar las restricciones sin eliminar la tabla y volver a crearla.
El procedimiento almacenado del sistema "sp_helpconstraint" junto al nombre de la tabla, nos
muestra información acerca de las restricciones de dicha tabla.
Cuando se agrega una restricción a una tabla, SQL Server comprueba los datos existentes.
Hay varios tipos de restricciones.

47 - Restricción default
La restricción "default" especifica un valor por defecto para un campo cuando no se inserta
explícitamente en un comando "insert".
Anteriormente, para establecer un valor por defecto para un campo empleábamos la cláusula
"default" al crear la tabla, por ejemplo:
create table libros(
...
autor varchar(30) default 'Desconocido',
...
);

Cada vez que establecíamos un valor por defecto para un campo de una tabla, SQL Server
creaba automáticamente una restricción "default" para ese campo de esa tabla.
Dicha restricción, a la cual no le dábamos un nombre, recibía un nombre dado por SQL Server
que consiste "DF" (por default), seguido del nombre de la tabla, el nombre del campo y letras y
números aleatorios.
Podemos agregar una restricción "default" a una tabla existente con la sintaxis básica siguiente:
alter table NOMBRETABLA
add constraint NOMBRECONSTRAINT
default VALORPORDEFECTO
for CAMPO;

En la sentencia siguiente agregamos una restricción "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 convención, cuando demos el nombre a las restricciones "default" emplearemos un formato
similar al que le da SQL Server: "DF_NOMBRETABLA_NOMBRECAMPO".

147
Solamente se permite una restricción "default" por campo y no se puede emplear junto con la
propiedad "identity". Una tabla puede tener varias restricciones "default" para sus distintos
campos.
La restricción "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 información referente a las restriciones de una tabla con el procedimiento
almacenado "sp_helpcontraint":
sp_helpconstraint libros;

aparecen varias columnas con la siguiente información:


- constraint_type: el tipo de restricción y sobre qué campo está establecida
(DEFAULT on column autor),
- constraint_name: el nombre de la restricción (DF_libros_autor),
- delete_action y update_action: no tienen valores para este tipo de restricción.
- status_enabled y status_for_replication: no tienen valores para este tipo
de restricción.
- constraint_keys: el valor por defecto (Desconocido).

Entonces, la restricción "default" especifica un valor por defecto para un campo cuando no se
inserta explícitamente en un "insert", se puede establecer uno por campo y no se puede emplear
junto con la propiedad "identity".
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:
if object_id('libros') is not null
drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30) default 'Desconocido',
editorial varchar(15),
precio decimal(6,2)
);

Ingresamos algunos registros sin valor para el campo "autor":


insert into libros (titulo,editorial) values('Martin Fierro','Emece');
insert into libros (titulo,editorial) values('Aprenda PHP','Emece');

Veamos que SQL Server creó automáticamente una restricción "default" para el campo "autor":
sp_helpconstraint libros;

aparece la siguiente información:


constraint_type constraint_name ... constraint_keys
--------------------------------------------------------------------------------
DEFAULT on column autor DF_libros_autor (n/a) ('Desconocido')

148
La restricción, a la cual no le dimos un nombre, recibe un nombre dado por SQL Server
"DF_libros_autor_67C95AEA", que consiste en "DF" (por default), seguido del nombre de la tabla, el
nombre del campo y unos números y letras aleatorios.
Vamos a eliminar la tabla y la crearemos nuevamente, sin la cláusula "default":
drop table libros;
create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(15),
precio decimal(6,2)
);

Agregamos una restricción "default" empleando "alter table" para que almacene el valor "Desconocido" en
el campo "autor":
alter table libros
add constraint DF_libros_autor
default 'Desconocido'
for autor;

Veamos la restrición agregada anteriormente con el procedimiento almacenado "sp_helpcontraint":


sp_helpconstraint libros;

aparece la siguiente información:


constraint_type constraint_name ... constraint_keys
---------------------------------------------------------------------------------
DEFAULT on column autor DF_libros_autor (n/a) ('Desconocido')

Agregamos algunos registros:


insert into libros (titulo,editorial) values('Martin Fierro','Emece');
insert into libros default values;

Veamos cómo se almacenaron los registros sin valor explícito para el campo con restricción "default":
select *from libros;

Agregamos otra restricción "default" para el campo "precio" para que almacene el valor 0 en dicho campo:
alter table libros
add constraint DF_libros_precio
default 0
for precio;

Veamos la restrición agregada anteriormente con el procedimiento almacenado "sp_helpcontraint":


sp_helpconstraint libros;

Primer problema:
Un comercio que tiene un stand en una feria registra en una tabla llamada "visitantes"
algunos datos
de las personas que visitan o compran en su stand para luego enviarle publicidad de sus
productos.
1- Elimine la tabla "visitantes", si existe:
if object_id('visitantes') is not null
drop table visitantes;
149
2- Cree la tabla con la siguiente estructura:
create table visitantes(
numero int identity,
nombre varchar(30),
edad tinyint,
domicilio varchar(30),
ciudad varchar(20),
montocompra decimal (6,2) not null
);

3- Defina una restricción "default" para el campo "ciudad" que almacene el valor
"Cordoba" en caso
de no ingresar valor para dicho campo:
alter table visitantes
add constraint DF_visitantes_ciudad
default 'Cordoba'
for ciudad;

4- Defina una restricción "default" para el campo "montocompra" que almacene el valor
"0" en caso de
no ingresar valor para dicho campo:
alter table visitantes
add constraint DF_visitantes_montocompra
default 0
for montocompra;

5- Ingrese algunos registros sin valor para los campos con restricción "default":
insert into visitantes
values ('Susana Molina',35,'Colon 123',default,59.80);
insert into visitantes (nombre,edad,domicilio)
values ('Marcos Torres',29,'Carlos Paz');
insert into visitantes
values ('Mariana Juarez',45,'Carlos Paz',null,23.90);

6- Vea cómo se almacenaron los registros:


select *from visitantes;

7- Vea las restricciones creadas anteriormente.


aparecen dos filas, una por cada restricción.

8- Intente agregar otra restricción "default" al campo "ciudad".


Aparece un mensaje de error indicando que el campo ya tiene una restricción "default" y
sabemos
que no puede establecerse más de una restricción "default" por campo.

9- Intente establecer una restricción "default" al campo "identity".


No se permite.

Segundo problema:
Una playa de estacionamiento almacena cada día los datos de los vehículos que ingresan
en la tabla
llamada "vehiculos".
1- Elimine la tabla, si existe:
if object_id('vehiculos') is not null
drop table vehiculos;

2- Cree la tabla:
create table vehiculos(
patente char(6) not null,
tipo char(1),--'a'=auto, 'm'=moto
horallegada datetime,
horasalida datetime
);
150
3- Establezca una restricción "default" para el campo "tipo" que almacene el valor "a"
en caso de no
ingresarse valor para dicho campo.

4- Ingrese un registro sin valor para el campo "tipo":


insert into vehiculos values('BVB111',default,default,null);

5- Recupere los registros:


select *from vehiculos;

6- Intente establecer otra restricción "default" para el campo "tipo" que almacene el
valor "m" en
caso de no ingresarse valor para dicho campo.
No lo permite porque un campo solamente admite una restricción "default" y ya tiene
una.

7- Establezca una restricción "default" para el campo "horallegada" que almacene la


fecha y hora del
sistema.

8- Ingrese un registro sin valor para los campos de tipo datetime.

9- Recupere los registros:


select *from vehiculos;

10- Vea las restricciones.


2 restricciones.

48 - Restricción check
La restricción "check" especifica los valores que acepta un campo, evitando que se ingresen
valores inapropiados.
La sintaxis básica es la siguiente:
alter table NOMBRETABLA
add constraint NOMBRECONSTRAINT
check CONDICION;

Trabajamos con la tabla "libros" de una librería que tiene los siguientes campos: codigo, titulo,
autor, editorial, preciomin (que indica el precio para los minoristas) y preciomay (que indica el
precio para los mayoristas).
Los campos correspondientes a los precios (minorista y mayorista) se definen de tipo
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 restricción "check":
alter table libros
add constraint CK_libros_precio_positivo
check (preciomin>=0 and preciomay>=0);

Este tipo de restricción verifica los datos cada vez que se ejecuta una sentencia "insert" o
"update", es decir, actúa en inserciones y actualizaciones.
151
Si la tabla contiene registros que no cumplen con la restricción que se va a establecer, la
restricción no se puede establecer, hasta que todos los registros cumplan con dicha restricción.
La condición puede hacer referencia a otros campos de la misma tabla. Por ejemplo, podemos
controlar que el precio mayorista no sea mayor al precio minorista:
alter table libros
add constraint CK_libros_preciominmay
check (preciomay<=preciomin);

Por convención, cuando demos el nombre a las restricciones "check" seguiremos la misma
estructura: comenzamos con "CK", seguido del nombre de la tabla, del campo y alguna palabra
con la cual podamos identificar fácilmente de qué se trata la restricción, por si tenemos varias
restricciones "check" para el mismo campo.
Un campo puede tener varias restricciones restricciones "check" y una restricción "check" puede
incluir varios campos.
Las condiciones para restricciones "check" también pueden pueden incluir un patrón o una lista
de valores. Por ejemplo establecer que cierto campo conste de 4 caracteres, 2 letras y 2 dígitos:
...
check (CAMPO like '[A-Z][A-Z][0-9][0-9]');

O establecer que cierto campo asuma sólo los valores que se listan:
...
check (CAMPO in ('lunes','miercoles','viernes'));

No se puede aplicar esta restricción junto con la propiedad "identity".


Si un campo permite valores nulos, "null" es un valor aceptado aunque no esté incluido en la
condición de restricción.
Si intentamos establecer una restricción "check" para un campo que entra en conflicto con otra
restricción "check" establecida al mismo campo, SQL Server no lo permite.
Pero si establecemos una restricción "check" para un campo que entra en conflicto con una
restricción "default" establecida para el mismo campo, SQL Server lo permite; pero al intentar
ingresar un registro, aparece un mensaje de error.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

La creamos e ingresamos algunos registros:


create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(15),
preciomin decimal(5,2),
preciomay decimal(5,2)
);

152
insert into libros values ('Uno','Bach','Planeta',22,20);
insert into libros values ('El quijote','Cervantes','Emece',15,13);
insert into libros values ('Aprenda PHP','Mario Molina','Siglo XXI',48,53);
insert into libros values ('Java en 10 minutos','Garcia','Siglo XXI',35,40);

Agregamos una restricción "check" para asegurar que los valores de los campos correspondientes a precios
no puedan ser negativos:
alter table libros
add constraint CK_libros_precios_positivo
check (preciomin>=0 and preciomay>=0);

Si intentamos ingresar un valor inválido para algún campo correspondiente al precio, que vaya en contra de
la restricción, por ejemplo el valor "-15" aparecerá un mensaje de error indicando que hay conflicto con la
restricción creada anteriormente y la inserción no se realiza. Igualmente si intentamos actualizar un precio,
que vaya en contra de la restricción.
Si intentamos agregar una restricción que no permita que el precio mayorista supere el precio minorista,
aparece un mensaje de error y la sentencia no se ejecuta, porque hay registros que no cumplen con la
restricción que intentamos establecer. Podemos modificar los datos que no cumplen la condición de la
restricción o eliminar los registros:
update libros set preciomay=48
where titulo='Aprenda PHP';
delete from libros where titulo='Java en 10 minutos';

Ahora SQL Server si nos permite agregar la restricción "check" que impida que se ingresen valores para
"preciomay" superiores a "preciomin":
alter table libros
add constraint CK_libros_preciominmay
check (preciomay<=preciomin);

Veamos las restricciones de la tabla:


sp_helpconstraint libros;

Ingresamos un registro con valores por defecto:


insert into libros default values;

Note que los campos correspondientes a precios admiten valores 0 y 999.99 (por el tipo de dato y la
restricción), además del valor "null".
Primer problema:

Una empresa tiene registrados datos de sus empleados en una tabla llamada "empleados".
1- Elimine la tabla si existe:
if object_id('empleados') is not null
drop table empleados;

2- Créela con la siguiente estructura:


create table empleados (
documento varchar(8),
nombre varchar(30),
fechanacimiento datetime,
cantidadhijos tinyint,
seccion varchar(20),
sueldo decimal(6,2)
153
);

3- Agregue una restricción "check" para asegurarse que no se ingresen valores negativos
para el
sueldo:
alter table empleados
add constraint CK_empelados_sueldo_positivo
check (sueldo>0);

4- Ingrese algunos registros válidos:


insert into empleados values ('22222222','Alberto
Lopez','1965/10/05',1,'Sistemas',1000);
insert into empleados values ('33333333','Beatriz
Garcia','1972/08/15',2,'Administracion',3000);
insert into empleados values ('34444444','Carlos
Caseres','1980/10/05',0,'Contaduría',6000);

5- Intente agregar otra restricción "check" al campo sueldo para asegurar que ninguno
supere el
valor 5000:
alter table empleados
add constraint CK_empleados_sueldo_maximo
check (sueldo<=5000);
La sentencia no se ejecuta porque hay un sueldo que no cumple la restricción.

6- Elimine el registro infractor y vuelva a crear la restricción:


delete from empleados where sueldo=6000;

alter table empleados


add constraint CK_empleados_sueldo_maximo
check (sueldo<=5000);

7- Establezca una restricción para controlar que la fecha de nacimiento que se ingresa
no supere la
fecha actual:
alter table empleados
add constraint CK_fechanacimiento_actual
check (fechanacimiento

Segundo problema:

Una playa de estacionamiento almacena los datos de los vehículos que ingresan en la
tabla llamada
"vehiculos".
1- Elimine la tabla, si existe:
if object_id('vehiculos') is not null
drop table vehiculos;

2- Cree la tabla:
create table vehiculos(
numero int identity,
patente char(6),
tipo char(4),
fechahoraentrada datetime,
fechahorasalida datetime
);

3- Ingresamos algunos registros:


insert into vehiculos values('AIC124','auto','2007/01/17 8:05','2007/01/17 12:30');
insert into vehiculos values('CAA258','auto','2007/01/17 8:10',null);
insert into vehiculos values('DSE367','moto','2007/01/17 8:30','2007/01/17 18:00');

4- Agregue una restricción "check" que especifique un patrón de 3 letras y 3 dígitos


para "patente":
154
alter table vehiculos
add constraint CK_vehiculos_patente_patron
check (patente like '[A-Z][A-Z][A-Z][0-9][0-9][0-9]');

5- Intente ingresar un registro con un valor inapropiado para "patente":


insert into vehiculos values('AB1234','auto',getdate(),null);
No lo permite.

6- Agregue una restricción "check" que especifique que el campo "tipo" acepte solamente
los valores
"auto" y "moto":
alter table vehiculos
add constraint CK_vehiculos_tipo_valores
check (tipo in ('auto','moto'));

7- Intente modificar el valor del campo "tipo" ingresando un valor inexistente en la


lista de
valores permitidos por la restricción establecida a dicho campo:
update vehiculos set tipo='bici' where patente='AIC124';
No lo permite.

8- Agregue una restricción "default" para el campo "tipo" que almacene el valor "bici":
alter table vehiculos
add constraint DF_vehiculos_tipo
default 'bici'
for tipo;
Lo acepta. Pero, note que va en contra de la restricción "check" impuesta en el punto
6.

9- Intente ingresar un registro sin valor para "tipo":


insert into vehiculos values('SDF134',default,null,null);
No lo permite porque va contra la restricción "check" del campo.

10- Agregue una restricción "check" para asegurarse que la fecha de entrada a la playa
no sea
posterior a la fecha y hora actual:
alter table vehiculos
add constraint CK_vehiculos_fechahoraentrada_actual
check (fechahoraentrada<=getdate());

11- Agregue otra restricción "check" al campo "fechahoraentrada" que establezca que sus
valores no
sean posteriores a "fechahorasalida":
alter table vehiculos
add constraint CK_vehiculos_fechahoraentradasalida
check (fechahoraentrada<=fechahorasalida);

12- Intente ingresar un valor que no cumpla con la primera restricción establecida en
el campo
"fechahoraentrada":
insert into vehiculos values('ABC123','auto','2007/05/05 10:10',null);
La inserción no se realiza.

13- Intente modificar un registro para que la salida sea anterior a la entrada:
update vehiculos set fechahorasalida='2007/01/17 7:30'
where patente='CAA258';
Mensaje de error.

14- Vea todas las restricciones para la tabla "vehiculos":


sp_helpconstraint vehiculos;
aparecen 5 filas, 4 correspondientes a restricciones "check" y 1 a "default".

15- Establezca una restricción "default" para el campo "fechahoraentrada" para que
almacene la fecha

155
actual del sistema:
alter table vehiculos
add constraint DF_vehiculos_fechahoraentrada
default getdate()
for fechahoraentrada;

16- Ingrese un registro sin valor para "fechahoraentrada":


insert into vehiculos values('DFR156','moto',default,default);

17- Vea todos los registros:


select *from vehiculos;

18- Vea las restricciones:


sp_helpconstraint vehiculos;
4 restricciones "check" y 2 "default".

49 - Deshabilitar restricciones (with check -


nocheck)
Sabemos que si agregamos una restricción a una tabla que contiene datos, SQL Server los
controla para asegurarse que cumplen con la condición de la restricción, si algún registro no la
cumple, la restricción no se establecece.
Es posible deshabilitar esta comprobación en caso de restricciones "check".
Podemos hacerlo cuando agregamos la restricción "check" a una tabla para que SQL Server
acepte los valores ya almacenados que infringen la restricción. Para ello debemos incluir la
opción "with nocheck" en la instrucción "alter table":
alter table libros
with nocheck
add constraint CK_libros_precio
check (precio>=0);

La restricción no se aplica en los datos existentes, pero si intentamos ingresar un nuevo valor que
no cumpla la restricción, SQL Server no lo permite.
Entonces, para evitar la comprobación de datos existentes al crear la restricción, la sintaxis básica
es la siguiente:
alter table TABLA
with nocheck
add constraint NOMBRERESTRICCION
check (CONDICION);

Por defecto, si no especificamos, la opción es "with check".


También podemos deshabilitar las restricciones para agregar o actualizar datos sin comprobarla:
alter table libros
nocheck constraint CK_libros_precio;

156
En el ejemplo anterior deshabilitamos la restricción "CK_libros_precio" para poder ingresar un
valor negativo para "precio".
Para habilitar una restricción deshabilitada se ejecuta la misma instrucción pero con la cláusula
"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 comprobación de datos en inserciones o
actualizaciones, la sintaxis básica es:
alter table NOMBRETABLA
OPCIONdeRESTRICCION constraint NOMBRERESTRICCION;

Para saber si una restricción está habilitada o no, podemos ejecutar el procedimiento almacenado
"sp_helpconstraint" y fijarnos lo que informa la columna "status_enabled".
Entonces, las cláusulas "check" y "nocheck" permiten habilitar o deshabilitar restricciones "check"
(también las restricciones "foreign key" que veremos más adelante), a las demás se las debe
eliminar ("default" y las que veremos posteriormente).
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

La creamos e ingresamos algunos registros:


create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(15),
precio decimal(5,2)
);

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


insert into libros values ('El quijote','Cervantes','Emece',15);
insert into libros values ('Aprenda PHP','Mario Molina','Siglo XXI',-40);

Agregamos una restricción "check" para asegurar que los precios no puedan ser negativos, pero como ya
tenemos almacenado un precio que infringe la restricción, vamos a especificar que no haya comprobación de
datos existentes:
alter table libros
with nocheck
add constraint CK_libros_precio_positivo
check (precio>=0);

Si intentamos ingresar un registro con precio negativo, no lo permite. Para que lo permita, debemos
dehabilitar la comprobación:
alter table libros
157
nocheck constraint CK_libros_precio_positivo;

Ingresemos un registro con precio negativo:


insert into libros values('Java en 10 minutos',default,'Siglo XXI',-1);

Veamos si la restricción está o no habilitada:


sp_helpconstraint libros;

La columna "status_enabled" nos informa que está deshabilitada (Disabled).


Habilitamos la restricción :
alter table libros
check constraint CK_libros_precio_positivo;

Si ahora intentamos ingresar un precio negativo SQL Server no lo permitirá.


Primer problema:

Una empresa tiene registrados datos de sus empleados en una tabla llamada "empleados".
1- Elimine la tabla (si existe):
if object_id('empleados') is not null
drop table empleados;

2- Créela con la siguiente estructura e ingrese los registros siguientes:


create table empleados (
documento varchar(8),
nombre varchar(30),
seccion varchar(20),
sueldo decimal(6,2)
);

insert into empleados


values ('22222222','Alberto Acosta','Sistemas',-10);
insert into empleados
values ('33333333','Beatriz Benitez','Recursos',3000);
insert into empleados
values ('34444444','Carlos Caseres','Contaduria',4000);

3- Intente agregar una restricción "check" para asegurarse que no se ingresen valores
negativos para
el sueldo:
alter table empleados
add constraint CK_empleados_sueldo_positivo
check (sueldo>=0);
No se permite porque hay un valor negativo almacenado.

5- Vuelva a intentarlo agregando la opción "with nocheck":


alter table empleados
with nocheck
add constraint CK_empleados_sueldo_positivo
check (sueldo>=0);

6- Intente ingresar un valor negativo para sueldo:


insert into empleados
values ('35555555','Daniel Duarte','Administracion',-2000);
No es posible a causa de la restricción.

7- Deshabilite la restricción e ingrese el registro anterior:


alter table empleados
nocheck constraint CK_empleados_sueldo_positivo;
158
insert into empleados
values ('35555555','Daniel Duarte','Administracion',2000);

8- Establezca una restricción "check" para "seccion" que permita solamente los valores
"Sistemas",
"Administracion" y "Contaduría":
alter table empleados
add constraint CK_empleados_seccion_lista
check (seccion in ('Sistemas','Administracion','Contaduria'));
No lo permite porque existe un valor fuera de la lista.

9- Establezca la restricción anterior evitando que se controlen los datos existentes.

10- Vea si las restricciones de la tabla están o no habilitadas:


sp_helpconstraint empleados;
Muestra 2 filas, una por cada restricción.

11- Habilite la restricción deshabilitada.

12- Intente modificar la sección del empleado "Carlos Caseres" a "Recursos".


No lo permite.

13- Deshabilite la restricción para poder realizar la actualización del punto


precedente.

50 - Restricción primary key


Hemos visto las restricciones que se aplican a los campos, "default" y "check".
Ahora veremos las restricciones que se aplican a las tablas, que aseguran valores únicos para
cada registro.
Hay 2 tipos: 1) primary key y 2) unique.
Anteriormente, para establecer una clave primaria para una tabla empleábamos la siguiente
sintaxis al crear la tabla, por ejemplo:
create table libros(
codigo int not null,
titulo varchar(30),
autor varchar(30),
editorial varchar(20),
primary key(codigo)
);

Cada vez que establecíamos la clave primaria para la tabla, SQL Server creaba automáticamente
una restricción "primary key" para dicha tabla. Dicha restricción, a la cual no le dábamos un
nombre, recibía un nombre dado por SQL Server que comienza con "PK" (por primary key),
seguido del nombre de la tabla y una serie de letras y números aleatorios.
Podemos agregar una restricción "primary key" a una tabla existente con la sintaxis básica
siguiente:
alter table NOMBRETABLA
add constraint NOMBRECONSTRAINT
159
primary key (CAMPO,...);

En el siguiente ejemplo definimos una restricción "primary key" para nuestra tabla "libros" para
asegurarnos que cada libro tendrá un código diferente y único:
alter table libros
add constraint PK_libros_codigo
primary key(codigo);

Con esta restricción, si intentamos ingresar un registro con un valor para el campo "codigo" que
ya existe o el valor "null", aparece un mensaje de error, porque no se permiten valores duplicados
ni nulos. Igualmente, si actualizamos.
Por convención, cuando demos el nombre a las restricciones "primary key" seguiremos el formato
"PK_NOMBRETABLA_NOMBRECAMPO".
Sabemos que cuando agregamos una restricción a una tabla que contiene información, SQL
Server controla los datos existentes para confirmar que cumplen las exigencias de la restricción,
si no los cumple, la restricción no se aplica y aparece un mensaje de error. Por ejemplo, si
intentamos definir la restricción "primary key" para "libros" y hay registros con códigos repetidos o
con un valor "null", la restricción no se establece.
Cuando establecíamos una clave primaria al definir la tabla, automáticamente SQL Server
redefinía el campo como "not null"; pero al agregar una restricción "primary key", los campos que
son clave primaria DEBEN haber sido definidos "not null" (o ser implícitamente "not null" si se
definen identity).
SQL Server permite definir solamente una restricción "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 restricción "primary key" puede tener una restricción "check".
Un campo "primary key" también acepta una restricción "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.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

La creamos estableciendo el campo código como clave primaria:


create table libros(
codigo int not null,
titulo varchar(40),
autor varchar(30),
editorial varchar(15),
primary key (codigo)
);

160
Veamos la restricción "primary key" que creó automáticamente SQL Server:
sp_helpconstraint libros;

Aparece la siguiente información:


constraint_type constraint_name constraint_keys
-----------------------------------------------------------------------
PRIMARY KEY (clustered) PK__libros__571DF1D5 codigo

Vamos a eliminar la tabla y la crearemos nuevamente, sin establecer la clave primaria:


drop table libros;
create table libros(
codigo int not null,
titulo varchar(40),
autor varchar(30),
editorial varchar(15)
);

Definimos una restricción "primary key" para nuestra tabla "libros" para asegurarnos que cada libro tendrá
un código diferente y único:
alter table libros
add constraint PK_libros_codigo
primary key(codigo);

Veamos la información respecto a ella:


sp_helpconstraint libros;

Si intentamos ingresar un registro con un valor para el campo "codigo" que ya existe, no lo permite.
Tampoco permite modificar un código colocando uno existente.
Si intentamos definir otra restricción "primary key", SQL Server no lo permite.
Primer problema:

Una empresa tiene registrados datos de sus empleados en una tabla llamada "empleados".
1- Elimine la tabla si existe:
if object_id('empleados') is not null
drop table empleados;

2- Créela con la siguiente estructura:


create table empleados (
documento varchar(8) not null,
nombre varchar(30),
seccion varchar(20)
);

3- Ingrese algunos registros, dos de ellos con el mismo número de documento:


insert into empleados
values ('22222222','Alberto Lopez','Sistemas');
insert into empleados
values ('23333333','Beatriz Garcia','Administracion');
insert into empleados
values ('23333333','Carlos Fuentes','Administracion');

4- Intente establecer una restricción "primary key" para la tabla para que el documento
no se repita
ni admita valores nulos:
alter table empleados
161
add constraint PK_empleados_documento
primary key(documento);
No lo permite porque la tabla contiene datos que no cumplen con la restricción, debemos
eliminar (o
modificar) el registro que tiene documento duplicado:
delete from empleados
where nombre='Carlos Fuentes';

5- Establezca la restricción "primary key" del punto 4.

6- Intente actualizar un documento para que se repita.


No lo permite porque va contra la restricción.

7-Intente establecer otra restricción "primary key" con el campo "nombre".


No lo permite, sólo puede haber una restricción "primary key" por tabla.

8- Intente ingresar un registro con valor nulo para el documento.


No lo permite porque la restricción no admite valores nulos.

9- Establezca una restricción "default" para que almacene "00000000" en el documento en


caso de
omitirlo en un "insert".

10- Ingrese un registro sin valor para el documento.

11- Vea el registro:


select *from empleados;

12- Intente ingresar otro empleado sin documento explícito.


No lo permite porque se duplicaría la clave.

13- Vea las restricciones de la tabla empleados (2 filas):


sp_helpconstraint empleados;

Segundo problema:

Una empresa de remises tiene registrada la información de sus vehículos en una tabla
llamada
"remis".
1- Elimine la tabla si existe:
if object_id('remis') is not null
drop table remis;

2- Cree la tabla con la siguiente estructura:


create table remis(
numero tinyint identity,
patente char(6),
marca varchar(15),
modelo char(4)
);

3- Ingrese algunos registros sin repetir patente:


insert into remis values('ABC123','Renault 12','1990');
insert into remis values('DEF456','Fiat Duna','1995');

4- Intente definir una restricción "primary key" para el campo "patente".


No lo permite porque el campo no fue definido "not null".

5- Establezca una restricción "primary key" para el campo "numero".


Si bien "numero" no fue definido explícitamente "not null", no acepta valores nulos por
ser
"identity".

6- Vea la información de las restricciones (2 filas):


162
sp_helpconstraint remis;

51 - Restricción unique
Hemos visto que las restricciones aplicadas a tablas aseguran valores únicos para cada registro.

Anteriormente aprendimos la restricción "primary key", otra restricción para las tablas es
"unique".
La restricción "unique" impide la duplicación de claves alternas (no primarias), es decir, especifica
que dos registros no puedan tener el mismo valor en un campo. Se permiten valores nulos. Se
pueden aplicar varias restricciones de este tipo a una misma tabla, y pueden aplicarse a uno o
varios campos que no sean clave primaria.
Se emplea cuando ya se estableció una clave primaria (como un número de legajo) pero se
necesita asegurar que otros datos también sean únicos y no se repitan (como número de
documento).
La sintaxis general es la siguiente:
alter table NOMBRETABLA
add constraint NOMBRERESTRICCION
unique (CAMPO);

Ejemplo:
alter table alumnos
add constraint UQ_alumnos_documento
unique (documento);

En el ejemplo anterior se agrega una restricción "unique" sobre el campo "documento" de la tabla
"alumnos", esto asegura que no se pueda ingresar un documento si ya existe. Esta restricción
permite valores nulos, asi que si se ingresa el valor "null" para el campo "documento", se acepta.
Por convención, cuando demos el nombre a las restricciones "unique" seguiremos la misma
estructura: "UQ_NOMBRETABLA_NOMBRECAMPO". Quizá parezca innecesario colocar el
nombre de la tabla, pero cuando empleemos varias tablas verá que es útil identificar las
restricciones por tipo, tabla y campo.
Recuerde que cuando agregamos una restricción a una tabla que contiene información, SQL
Server controla los datos existentes para confirmar que cumplen la condición de la restricción, si
no los cumple, la restricción no se aplica y aparece un mensaje de error. En el caso del ejemplo
anterior, si la tabla contiene números de documento duplicados, la restricción no podrá
establecerse; si podrá establecerse si tiene valores nulos.
SQL Server controla la entrada de datos en inserciones y actualizaciones evitando que se
ingresen valores duplicados.

163
Trabajamos con la tabla "alumnos".
Eliminamos la tabla, si existe:

if object_id('alumnos') is not null


drop table alumnos;

Creamos la tabla:
create table alumnos(
legajo char(4) not null,
apellido varchar(20),
nombre varchar(20),
documento char(8)
);

Agregamos una restricción "primary" para el campo "legajo":


alter table alumnos
add constraint PK_alumnos_legajo
primary key(legajo);

Agregamos una restricción "unique" para el campo "documento":


alter table alumnos
add constraint UQ_alumnos_documento
unique (documento);

Ingresamos algunos registros:


insert into alumnos values('A111','Lopez','Ana','22222222');
insert into alumnos values('A123','Garcia','Maria','23333333');

Si intentamos ingresar un legajo o ducumento repetido, aparece un mensaje de error.


Veamos las restricciones:
sp_helpconstraint alumnos;

Aparecen las dos restricciones creadas anteriormente.


Primer problema:

Una empresa de remises tiene registrada la información de sus vehículos en una tabla
llamada
"remis".
1- Elimine la tabla si existe:
if object_id('remis') is not null
drop table remis;

2- Cree la tabla con la siguiente estructura:


create table remis(
numero tinyint identity,
patente char(6),
marca varchar(15),
modelo char(4)
);

3- Ingrese algunos registros, 2 de ellos con patente repetida y alguno con patente
nula:
insert into remis values('ABC123','Renault clio','1990');
insert into remis values('DEF456','Peugeot 504','1995');

164
insert into remis values('DEF456','Fiat Duna','1998');
insert into remis values('GHI789','Fiat Duna','1995');
insert into remis values(null,'Fiat Duna','1995');

4- Intente agregar una restricción "unique" para asegurarse que la patente del remis no
tomará
valores repetidos.
No se puede porque hay valores duplicados.

5- Elimine el registro con patente duplicada y establezca la restricción.


Note que hay 1 registro con valor nulo en "patente".

6- Intente ingresar un registro con patente repetida (no lo permite)

7- Intente ingresar un registro con valor nulo para el campo "patente".


No lo permite porque la clave estaría duplicada.

8- Muestre la información de las restricciones:


sp_helpconstraint remis;

52 - Información de restricciones
(sp_helpconstraint)
El procedimiento almacenado "sp_helpconstraint" seguido del nombre de una tabla muestra la
información referente a todas las restricciones establecidas en dicha tabla, devuelve las
siguientes columnas:
- constraint_type: tipo de restricción. Si es una restricción de campo (default o check) indica sobre
qué campo fue establecida. Si es de tabla (primary key o unique) indica el tipo de índice creado
(tema que veremos posteriormente).
- constraint_name: nombre de la restricción.
- delete_action: solamente es aplicable para restricciones de tipo "foreign key" (la veremos
posteriormente).
- update_action: sólo es aplicable para restricciones de tipo "foreign key" (la veremos
posteriormente).
- status_enabled: solamente es aplicable para restricciones de tipo "check" y "foreign key". Indica
si está habilitada (Enabled) o no (Disabled). Indica "n/a" en cualquier restricción para la que no se
aplique.
- status_for_replication: solamente es aplicable para restricciones de tipo "check" y "foreign key".
Indica "n/a" en cualquier restricción para la que no se aplique.
- constraint_keys: Si es una restricción "check" muestra la condición de chequeo; si es una
restricción "default", el valor por defecto; si es una "primary key" o "unique" muestra el/ los
campos a los que se aplicaron la restricción.

165
Trabajamos con la tabla "alumnos".
Eliminamos la tabla, si existe:

if object_id('alumnos') is not null


drop table alumnos;

Creamos la tabla:
create table alumnos(
legajo char(4) not null,
apellido varchar(20),
nombre varchar(20),
documento char(8),
domicilio varchar(30),
ciudad varchar(30),
notafinal decimal(4,2)
);

Agregamos una restricción "primary" para el campo "legajo":


alter table alumnos
add constraint PK_alumnos_legajo
primary key(legajo);

Agregamos una restricción "unique" para el campo "documento":


alter table alumnos
add constraint UQ_alumnos_documento
unique (documento);

Agregamos una restricción "check" para que el campo "notafinal" admita solamente valores entre 0 y 10:
alter table alumnos
add constraint CK_alumnos_nota
check (notafinal>=0 and notafinal<=10);

Agregamos una restricción "default" para el campo "ciudad":


alter table alumnos
add constraint DF_alumnos_ciudad
default 'Cordoba'
for ciudad;

Veamos las restricciones:


sp_helpconstraint alumnos;

Aparece la siguiente información:


constraint_type constraint_name status_enabled constraint_keys
---------------------------------------------------------------------------------------
----
CHECK on column notafinal CK_alumos_nota Enabled ([notafinal]>=0
and [notafinal<=10])
DEFAULT on column ciudad DF_alumnos_ciudad (n/a) ('Cordoba')
PRIMARY KEY (clustered) PK_alumnos_legajo (n/a) legajo
UNIQUE (NON-clustered) UQ_alumnos_documento (n/a) documento

Deshabilitamos la restricción "check":


alter table alumnos

166
nocheck constraint CK_alumnos_nota;

Veamos la información que nos retorna "sp_helpconstraint":


sp_helpconstraint alumnos;

constraint_type constraint_name status_enabled constraint_keys


---------------------------------------------------------------------------------------
---
CHECK on column notafinal CK_alumos_nota Disabled
([notafinal]>=0...

Note que la restricción esta deshabilitada.

53 - Eliminar restricciones (alter table - drop)


Para eliminar una restricción, la sintaxis básica es la siguiente:
alter table NOMBRETABLA
drop NOMBRERESTRICCION;

Para eliminar la restricción "DF_libros_autor" de la tabla libros tipeamos:


alter table libros
drop DF_libros_autor;

Pueden eliminarse varias restricciones con una sola instrucción separándolas por comas.
Cuando eliminamos una tabla, todas las restricciones que fueron establecidas en ella, se eliminan
también.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

La creamos estableciendo el campo código como clave primaria:


create table libros(
codigo int not null,
titulo varchar(40),
autor varchar(30),
editorial varchar(15),
precio decimal(6,2)
);

Definimos una restricción "primary key" para nuestra tabla "libros" para asegurarnos que cada libro tendrá
un código diferente y único:
alter table libros
add constraint PK_libros_codigo
primary key(codigo);

167
Definimos una restricción "check" para asegurarnos que el precio no será negativo:
alter table libros
add constraint CK_libros_precio
check (precio>=0);

Definimos una restricción "default" para el campo "autor" para que almacene "Desconocido":
alter table libros
add constraint DF_libros_autor
default 'Desconocido'
for autor;

Definimos una restricción "default" para el campo "precio" para que almacene 0:
alter table libros
add constraint DF_libros_precio
default 0
for precio;

Vemos las restricciones:


sp_helpconstraint libros;

Aparecen 4 restricciones, 1 "check", 2 "default" y 1 "primary key".


Eliminamos la restricción "DF_libros_autor":
alter table libros
drop DF_libros_autor;

Eliminamos la restricción "PK_libros_codigo":


alter table libros
drop PK_libros_codigo;

Vemos si se eliminaron:
sp_helpconstraint libros;

Aparecen 2 restricciones.
Primer problema:

Una playa de estacionamiento almacena cada día los datos de los vehículos que ingresan
en la tabla
llamada "vehiculos".
1- Elimine la tabla, si existe:
if object_id('vehiculos') is not null
drop table vehiculos;

2- Cree la tabla:
create table vehiculos(
patente char(6) not null,
tipo char(1),--'a'=auto, 'm'=moto
horallegada datetime not null,
horasalida datetime
);

3- Establezca una restricción "check" que admita solamente los valores "a" y "m" para
el campo
"tipo":
168
alter table vehiculos
add constraint CK_vehiculos_tipo
check (tipo in ('a','m'));

4- Establezca una restricción "default" para el campo "tipo" que almacene el valor "a"
en caso de no
ingresarse valor para dicho campo:
alter table vehiculos
add constraint DF_vehiculos_tipo
default 'a'
for tipo;

5- Establezca una restricción "check" para el campo "patente" para que acepte 3 letras
seguidas de 3
dígitos:
alter table vehiculos
add constraint CK_vehiculos_patente_patron
check (patente like '[A-Z][A-Z][A-Z][0-9][0-9][0-9]');

6- Agregue una restricción "primary key" que incluya los campos "patente" y
"horallegada":
alter table vehiculos
add constraint PK_vehiculos_patentellegada
primary key(patente,horallegada);

7- Ingrese un vehículo:
insert into vehiculos values('SDR456','a','2005/10/10 10:10',null);

8- Intente ingresar un registro repitiendo la clave primaria:


insert into vehiculos values('SDR456','m','2005/10/10 10:10',null);
No se permite.

9- Ingrese un registro repitiendo la patente pero no la hora de llegada:


insert into vehiculos values('SDR456','m','2005/10/10 12:10',null);

10- Ingrese un registro repitiendo la hora de llegada pero no la patente:


insert into vehiculos values('SDR111','m','2005/10/10 10:10',null);

11- Vea todas las restricciones para la tabla "vehiculos":


sp_helpconstraint vehiculos;
aparecen 4 filas, 2 correspondientes a restricciones "check", 1 a "default" y 1 a
"primary key".

12- Elimine la restricción "default" del campo "tipo".

13- Vea si se ha eliminado:


sp_helpconstraint vehiculos;

14- Elimine la restricción "primary key" y "check".

15- Vea si se han eliminado:


sp_helpconstraint vehiculos;

54 - Crear y asociar reglas (create rule -


sp_bindrule)

169
Vimos que SQL Server ofrece varias alternativas para asegurar la integridad de datos, mediante
el uso de:
1. RESTRICCIONES (constraints), que se establecen en tablas y campos y son controlados
automáticamente por SQL Server. Hay 3 tipos:
I) DE LOS CAMPOS (hace referencia a los valores válidos para un campo determinado).
Pueden ser:
a) DEFAULT: especifica un valor por defecto para un campo cuando no se inserta
explícitamente 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 unívocamente cada uno de los registros; asegura que no haya
valores duplicados ni valores nulos. Se crea un índice automáticamente.
b) UNIQUE: impide la duplicación de claves alternas (no primarias). Se permiten valores
nulos. Se crea un índice automáticamente.
III) REFERENCIAL: lo veremos más adelante.
2. REGLAS (rules) y
3. 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 específico, coincidan con una lista de valores o
sigan un patrón.
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 básica 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 condición.
Por convención, nombraremos las reglas comenzando con "RG", el nombre del campo al que se
asocia y alguna palabra que haga referencia a la condición.
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.

170
La condición se refiere a los valores permitidos para inserciones y actualizaciones y puede
contener cualquier expresión válida para una cláusula "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 básica:
exec sp_bindrule NOMBREREGLA, 'TABLA.CAMPO';

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 inserción (o actualización) 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
instrucción "insert" o "update" muestra un mensaje de error, es decir, actúa 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 instrucción "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 asociación anterior; pero la primera regla no desaparece, solamente se deshace la
asociación.
La sentencia "create rule" no puede combinarse con otras sentencias en un lote.
La función que cumple una regla es básicamente la misma que una restricción "check", las
siguientes características explican algunas diferencias entre ellas:
- podemos definir varias restricciones "check" sobre un campo, un campo solamente puede tener
una regla asociada a él;
- una restricción "check" se almacena con la tabla, cuando ésta se elimina, las restricciones
también 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 restricción "check" puede incluir varios campos; una regla puede asociarse a distintos
campos (incluso de distintas tablas);
- una restricción "check" puede hacer referencia a otros campos de la misma tabla, una regla no.

171
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.
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".
Una empresa tiene registrados datos de sus empleados en una tabla llamada "empleados".
Eliminamos la tabla si existe:

if object_id('empleados') is not null


drop table empleados;

La creamos con la siguiente estructura:


create table empleados (
documento varchar(8) not null,
nombre varchar(30),
seccion varchar(20),
fechaingreso datetime,
fechanacimiento datetime,
hijos tinyint,
sueldo decimal(6,2)
);

Recuerde que las reglas son objetos independientes de las tablas (no se eliminan al borrar la tabla), así que
debemos eliminarlas con las siguientes intrucciones (en el siguiente capítulo explicaremos este tema):
if object_id ('RG_documento_patron') is not null
drop rule RG_documento_patron;
if object_id ('RG_empleados_seccion') is not null
drop rule RG_empleados_seccion;
if object_id ('RG_empleados_fechaingreso') is not null
drop rule RG_empleados_fechaingreso;
if object_id ('RG_hijos') is not null
drop rule RG_hijos;
if object_id ('RG_empleados_sueldo') is not null
drop rule RG_empleados_sueldo;
if object_id ('RG_empleados_sueldo2') is not null
drop rule RG_empleados_sueldo2;

Ingresamos algunos registros:


insert into empleados
values('22222222','Ana Acosta','Contaduria','1990-10-10','1972-10-10',2,700);
insert into empleados
values('23333333','Carlos Costa','Contaduria','1990-12-10','1972-05-04',0,750);
insert into empleados
values('24444444','Daniel Duarte','Sistemas','1995-05-05','1975-10-06',1,880);
insert into empleados
values('25555555','Fabiola Fuentes','Secretaria','1998-02-25','1978-02-08',3,550);
insert into empleados
values('26666666','Gaston Garcia','Secretaria','1999-05-08','1981-01-01',3,670);
insert into empleados
values('27777777','Ines Irala','Gerencia','2000-04-10','1985-12-12',0,6000);

Creamos una regla que establezca un patrón para el documento:


create rule RG_documento_patron
172
as @documento like '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]';

Ejecutamos el procedimiento almacenado del sistema "sp_help" para ver si la regla creada anteriormente fue
creada:
sp_help;

Ejecutamos el procedimiento almacenado del sistema "sp_helpconstraint" para ver si está asociada la regla a
algún campo de "empleados":
sp_helpconstraint empleados;

No aparece porque aún no la asociamos.


Si ingresamos un registro con un documento que no cumpla la regla, SQL Server lo acepta porque la regla
aún no está asociada al campo:
insert into empleados
values('ab888888','Juan Juarez','Secretaria','2001-04-11','1986-11-12',0,600);

Asociamos la regla "RG_documento_patron" al campo "documento":


exec sp_bindrule RG_documento_patron, 'empleados.documento';

Note que hay un documento que no cumple la regla, pero SQL Server no controla los datos existentes, actúa
en inserciones y actualizaciones, si intentamos ingresar un valor para "documento" en el cual incluyamos
caracteres, aparecerá un mensaje de error.
Volvemos a ejecutar "sp_helpconstraint":
sp_helpconstraint empleados;

Aparece la regla.
Creamos una regla para restringir los valores que se pueden ingresar en un campo "seccion":
create rule RG_empleados_seccion
as @seccion in ('Secretaria','Contaduria','Sistemas','Gerencia');

La asociamos al campo "seccion":


exec sp_bindrule RG_empleados_seccion, 'empleados.seccion';

Creamos una regla para restringir los valores que se pueden ingresar en el campo "fechaingreso", para que
no sea posterior a la fecha actual:
create rule RG_empleados_fechaingreso
as @fecha <= getdate();

Asociamos la regla anteriormente creada a los campos "fechaingreso" y "fechanacimiento":


exec sp_bindrule RG_empleados_fechaingreso, 'empleados.fechaingreso';
exec sp_bindrule RG_empleados_fechaingreso, 'empleados.fechanacimiento';

Creamos una regla para restringir los valores que se pueden ingresar en el campo "hijos":
create rule RG_hijos
as @hijos between 0 and 20;

173
La asociamos al campo "hijos":
exec sp_bindrule RG_hijos, 'empleados.hijos';

Creamos una regla para restringir los valores que se pueden ingresar en un campo "sueldo":
create rule RG_empleados_sueldo
as @sueldo>0 and @sueldo<= 5000;

La asociamos al campo "sueldo":


exec sp_bindrule RG_empleados_sueldo, 'empleados.sueldo';

Si intentamos ingresar (o actualizar) un registro con el valor "6000" para "sueldo", SQL Server muestra un
mensaje de error y la acción no se realiza.
Creamos otra regla para restringir los valores que se pueden ingresar en un campo "sueldo":
create rule RG_empleados_sueldo2
as @sueldo>0 and @sueldo<= 7000;

La asociamos al campo "sueldo":


exec sp_bindrule RG_empleados_sueldo2, 'empleados.sueldo';

La nueva regla reeemplaza la asociación anterior. Ahora podemos ingresar el valor "6000" en el campo
"sueldo":
insert into empleados
values('29999999','Luis Lopez','Secretaria','2002-03-03','1990-09-09',0,6000);

La regla "RG_empleados_sueldo" no desaparece, solamente se deshizo la asociación, veámoslo:


sp_help;

La regla "RG_empleados_sueldo" aún existe en la base de datos.


Veamos las reglas asociadas:
sp_helpconstraint empleados;

La regla "RG_empleados_sueldo" ya no está asociada a ningún campo de la tabla "empleados" así que no
aparece; la regla "RG_empleados_sueldo2" si, junto con las otras 5 reglas asociadas.
Primer problema:

Una playa de estacionamiento almacena cada día los datos de los vehículos que ingresan
en la tabla
llamada "vehiculos".
1- Elimine la tabla, si existe:
if object_id('vehiculos') is not null
drop table vehiculos;

2- Elimine las siguientes reglas:


if object_id ('RG_patente_patron') is not null
drop rule RG_patente_patron;
if object_id ('RG_horallegada') is not null
drop rule RG_horallegada;
if object_id ('RG_vehiculos_tipo') is not null
drop rule RG_vehiculos_tipo;
if object_id ('RG_vehiculos_tipo2') is not null
174
drop rule RG_vehiculos_tipo2;
if object_id ('RG_menor_fechaactual') is not null
drop rule RG_menor_fechaactual;

3- Cree la tabla:
create table vehiculos(
patente char(6) not null,
tipo char(1),--'a'=auto, 'm'=moto
horallegada datetime not null,
horasalida datetime
);

4- Ingrese algunos registros:


insert into vehiculos values ('AAA111','a','1990-02-01 08:10',null);
insert into vehiculos values ('BCD222','m','1990-02-01 08:10','1990-02-01 10:10');
insert into vehiculos values ('BCD222','m','1990-02-01 12:00',null);
insert into vehiculos values ('CC1234','a','1990-02-01 12:00',null);

5- Cree una regla para restringir los valores que se pueden ingresar en un campo
"patente" (3 letras
seguidas de 3 dígitos):
create rule RG_patente_patron
as @patente like '[A-Z][A-Z][A-Z][0-9][0-9][0-9]'

6- Ejecute el procedimiento almacenado del sistema "sp_help" para ver que la regla
creada
anteriormente existe:
sp_help;

7- Ejecute el procedimiento almacenado del sistema "sp_helpconstraint" para ver que la


regla creada
anteriormente no está asociada aún a ningún campo de la tabla "vehiculos".

8- Asocie la regla al campo "patente":


Note que hay una patente que no cumple la regla, SQL Server NO controla los datos
existentes, pero
si controla las inserciones y actualizaciones:
select *from empleados;

9- Intente ingresar un registro con valor para el campo "patente" que no cumpla con la
regla.
aparece un mensaje de error indicando que hay conflicto con la regla y la inserción no
se realiza.

10- Cree otra regla que controle los valores para el campo "tipo" para que solamente
puedan
ingresarse los caracteres "a" y "m".

11- Asocie la regla al campo "tipo".

12- Intente actualizar un registro cambiando el valor de "tipo" a un valor que no


cumpla con la
regla anterior.
No lo permite.

13- Cree otra regla llamada "RG_vehiculos_tipo2" que controle los valores para el campo
"tipo" para
que solamente puedan ingresarse los caracteres "a", "c" y "m".

14- Si la asociamos a un campo que ya tiene asociada otra regla, la nueva regla
reeemplaza la
asociación anterior. Asocie la regla creada en el punto anterior al campo "tipo".

15- Actualice el registro que no pudo actualizar en el punto 12:

175
update vehiculos set tipo='c' where patente='AAA111';

16- Cree una regla que permita fechas menores o iguales a la actual.

17- Asocie la regla anterior a los campos "horallegada" y "horasalida":


exec sp_bindrule RG_menor_fechaactual, 'vehiculos.horallegada';
exec sp_bindrule RG_menor_fechaactual, 'vehiculos.horasalida';

18- Ingrese un registro en el cual la hora de entrada sea posterior a la hora de


salida:
insert into vehiculos values ('NOP555','a','1990-02-01 10:10','1990-02-01 08:30');

19- Intente establecer una restricción "check" que asegure que la fecha y hora de
llegada a la playa
no sea posterior a la fecha y hora de salida:
alter table vehiculos
add constraint CK_vehiculos_llegada_salida
check(horallegada<=horasalida);
No lo permite porque hay un registro que no cumple la restricción.

20- Elimine dicho registro:


delete from vehiculos where patente='NOP555';

21- Establezca la restricción "check" que no pudo establecer en el punto 19:


alter table vehiculos
add constraint CK_vehiculos_llegada_salida
check(horallegada<=horasalida);

22- Cree una restricción "default" que almacene el valor "b" en el campo "tipo:
alter table vehiculos
add constraint DF_vehiculos_tipo
default 'b'
for tipo;
Note que esta restricción va contra la regla asociada al campo "tipo" que solamente
permite los
valores "a", "c" y "m". SQL Server no informa el conflicto hasta que no intenta
ingresar el valor
por defecto.

23- Intente ingresar un registro con el valor por defecto para el campo "tipo":
insert into vehiculos values ('STU456',default,'1990-02-01 10:10','1990-02-01 15:30');
No lo permite porque va contra la regla asociada al campo "tipo".

24- Vea las reglas asociadas a "empleados" y las restricciones aplicadas a la misma
tabla ejecutando
"sp_helpconstraint".
Muestra 1 restricción "check", 1 restricción "default" y 4 reglas asociadas.

55 - Eliminar y dasasociar reglas (sp_unbindrule


- drop rule)
Para eliminar una regla, primero se debe deshacer la asociación, ejecutando el procedimiento
almacenado del sistema "sp_unbindrule":
exec sp_unbindrule 'TABLA.CAMPO';

176
No es posible eliminar una regla si está asociada a un campo. Si intentamos hacerlo, aparece un
mensaje de error y la eliminación no se realiza.
Con la instrucción "drop rule" eliminamos la regla:
drop rule NOMBREREGLA;

Quitamos la asociación de la regla "RG_sueldo_intervalo" con el campo "sueldo" de la tabla


"empleados" tipeando:
exec sp_unbindrule 'empleados.sueldo';

Luego de quitar la asociación la eliminamos:


drop rule RG_sueldo_100a1000;

Si eliminamos una tabla, las asociaciones de reglas de sus campos desaparecen, pero las reglas
siguen existiendo.
Una empresa registra los datos de sus empleados en una tabla llamada "empleados".
Eliminamos la tabla "empleados" si existe:

if object_id ('empleados') is not null


drop table empleados;

Recordemos que si eliminamos una tabla, las asociaciones de reglas de sus campos desaparecen, pero las
reglas siguen existiendo. Si intentamos crear una regla con igual nombre que una existente, aparecerá un
mensaje indicándolo, por ello, debemos eliminar las reglas (si existen) para poder crearlas nuevamente:
if object_id ('RG_sueldo_100a1000') is not null
drop rule RG_sueldo_100a1000;

Creamos la tabla:
create table empleados(
documento char(8),
nombre varchar(30) not null,
seccion varchar(20),
sueldo decimal(6,2),
primary key(documento)
);

Creamos una regla para restringir los valores que se pueden ingresar en un campo "sueldo":
create rule RG_sueldo_100a1000
as @sueldo between 100 and 1000;

Asociamos la regla creada anteriormente al campo "sueldo":


exec sp_bindrule RG_sueldo_100a1000, 'empleados.sueldo';

Si intentamos ingresar un registro con valor para el campo "sueldo" de "1200" aparece un mensaje de error
indicando que hay conflicto con la regla y la inserción no se realiza.
Vemos si la regla está asociada a algún campo de "empleados":
sp_helpconstraint empleados;

177
Aparece la regla.
Si intentamos eliminar la regla "RG_sueldo_100a 1000" aparece un mensaje de error indicando que no se
puede eliminar la regla porque está asociada.
Quitamos la asociación:
exec sp_unbindrule 'empleados.sueldo';

Ahora que hemos quitado la asociación, podemos ingresar el valor "1200" en el campo "sueldo":
insert into empleados values ('30111222','Pedro Torres','Contaduria',1200);

Vemos si la regla está asociada a algún campo de "empleados":


sp_helpconstraint empleados;

No aparece la regla.
Ejecutamos el procedimiento "sp_help" para verificar que la regla aún existe:
sp_help;

Aparece la regla.
Ahora si podemos borrar la regla:
drop rule RG_sueldo_100a1000;

Primer problema:

Una playa de estacionamiento almacena cada día los datos de los vehículos que ingresan
en la tabla
llamada "vehiculos".
1- Elimine la tabla, si existe:
if object_id('vehiculos') is not null
drop table vehiculos;

2- Elimine las siguientes reglas, si existen:


if object_id ('RG_patente_patron') is not null
drop rule RG_patente_patron;
if object_id ('RG_vehiculos_tipo') is not null
drop rule RG_vehiculos_tipo;
if object_id ('RG_vehiculos_tipo2') is not null
drop rule RG_vehiculos_tipo2;

3- Cree la tabla:
create table vehiculos(
patente char(6) not null,
tipo char(1),--'a'=auto, 'm'=moto
horallegada datetime not null,
horasalida datetime
);

4- Cree una regla para restringir los valores que se pueden ingresar en un campo
"patente" (3 letras
seguidas de 3 dígitos):
create rule RG_patente_patron
as @patente like '[A-Z][A-Z][A-Z][0-9][0-9][0-9]';

5-Asocie la regla al campo "patente":


sp_bindrule RG_patente_patron,'vehiculos.patente';

178
6- Intente ingresar un registro con valor para el campo "patente" que no cumpla con la
regla:
insert into vehiculos values ('FGHIJK','a','1990-02-01 18:00',null);
aparece un mensaje de error indicando que hay conflicto con la regla y la inserción no
se realiza.

7- Cree otra regla que controle los valores para el campo "tipo" para que solamente
puedan
ingresarse los caracteres "a" y "m":
create rule RG_vehiculos_tipo
as @tipo in ('a','m')

8- Asocie la regla al campo "tipo":


sp_bindrule RG_vehiculos_tipo, 'vehiculos.tipo';

9- Intente ingresar un registro con el valor 'c' para "tipo":


insert into vehiculos values('AAA111','c','2001-10-10 10:10',NULL);
No lo permite.

10- Cree otra regla llamada "RG_vehiculos_tipo2" que controle los valores para el campo
"tipo" para
que solamente puedan ingresarse los caracteres "a", "c" y "m":
create rule RG_vehiculos_tipo2
as @tipo in ('a','c','m');

11- Si la asociamos a un campo que ya tiene asociada otra regla, la nueva regla
reeemplaza la
asociación anterior. Asocie la regla creada en el punto anterior al campo "tipo".

12- Ingrese el registro que no pudo ingresar en el punto 9.

13- Intente eliminar la regla "RG_vehiculos_tipo2".


No es posible porque está asociada a un campo de "vehiculos".

14- Elimine la regla "RG_vehiculos_tipo".


Es posible porque no está asociada a ningún campo.

15- Intente eliminar la regla "RG_patente_patron".


No es posible porque está asociada.

16- Quite la asociación de la regla con el campo "patente" de "vehiculos".

17- Vea si la regla "RG_patente_patron" está asociada a algún campo de "vehiculos".


No lo está.

18- Verifique que la regla aún existe en la base de datos activa:


sp_help;
aparece la regla.

19- Elimine la regla que no pudo eliminar en el punto 15.

20- Verifique que la regla ya no existe en la base de datos activa.


No aparece la regla "RG_patente_patron".

56 - Información de reglas (sp_help -


sp_helpconstraint)
179
Podemos utilizar el procedimiento almacenado "sp_help" con el nombre del objeto del cual
queremos información, en este caso el nombre de una regla:
sp_help NOMBREREGLA;

muestra nombre, propietario, tipo y fecha de creación.


Con "sp_help", no sabemos si las reglas existentes están o no asociadas a algún campo.
"sp_helpconstraint" retorna una lista de todas las restricciones que tiene una tabla. Podemos ver
las reglas asociadas a una tabla con este procedimiento almacenado:
sp_helpconstraint NOMBRETABLA;

muestra la siguiente información:


- constraint_type: indica que es una regla con "RULE", nombrando el campo al que está asociada.
- constraint_name: nombre de la regla.
- constraint_keys: muestra el texto de la regla.
Para ver el texto de una regla empleamos el procedimiento almacenado "sp_helptext" seguido del
nombre de la regla:
sp_helptext NOMBREREGLA;

También 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%';--búsqueda con comodín

Una empresa registra los datos de sus empleados en una tabla llamada "empleados".
Eliminamos la tabla "empleados" (si existe) y las reglas:

if object_id ('empleados') is not null


drop table empleados;
if object_id ('RG_sueldo') is not null
drop rule RG_sueldo;
if object_id ('RG_seccion_lista') is not null
drop rule RG_seccion_lista;

Creamos la tabla:
create table empleados(
documento char(8) not null,
nombre varchar(30) not null,
seccion varchar(20),
sueldo decimal(6,2),
primary key(documento)
);
180
Creamos una regla para el campo "sueldo":
create rule RG_sueldo
as @sueldo between 100 and 1000;

Asociamos la regla creada anteriormente al campo "sueldo":


exec sp_bindrule RG_sueldo, 'empleados.sueldo';

Creamos una regla con una lista de valores para "seccion":


create rule RG_seccion_lista
as @seccion in ('Sistemas','Secretaria','Contaduria');

No la asociamos.
Llamemos al procedimiento almacenado "sp_help" junto al nombre de la regla de la cual queremos
información:
sp_help RG_sueldo;

muestra el nombre, propietario, tipo y fecha de creación.


Para ver las reglas asociadas a la tabla "empleados" tipeamos:
sp_helpconstraint empleados;

retorna una lista de todas las restricciones que tiene una tabla. También las reglas asociadas. Aparecen 2
filas: una con información de la restricción "primary key" y otra con información de la regla asociada, esta
última muestra:
- constraint_type: indica que es una regla con "RULE", nombrando el campo al que está asociada.
- constraint_name: nombre de la regla.
- constraint_keys: el texto de la regla.
- las otras columnas no tienen información.
Note que no aparece la regla "RG_seccion_lista" porque no fue asociada a la tabla.
Si asociamos la regla a la tabla:
exec sp_bindrule RG_seccion_lista, 'empleados.seccion';

y ejecutamos el procedimiento nuevamente:


sp_helpconstraint empleados;

Aparecen ambas reglas.


También podemos ver el texto de una regla empleando "sp_helptext":
sp_helptext "RG_seccion_lista";

Deshacemos la asociación de la regla "RG_sueldo" y la eliminamos:


exec sp_unbindrule 'empleados.sueldo';
drop rule RG_sueldo;

181
Verificamos que ya no existe tal regla:
sp_help RG_sueldo;

Vemos si la regla "RG_seccion_lista" existe consultando la tabla "sysobjects":


select *from sysobjects
where xtype='R' and
name like '%seccion%';

57 - Valores predeterminados (create default)


Hemos visto que para mantener la integridad declarativa se emplean restricciones, reglas (que
hemos estudiado en secciones anteriores) y valores predeterminados.
Veamos los valores predeterminados.
Los valores predeterminados se asocian con uno o varios campos (o tipos de datos definidos por
el usuario); se definen una sola vez y se pueden usar muchas veces.
Si no se coloca un valor cuando se ingresan datos, el valor predeterminado especifica el valor del
campo al que está asociado.
Sintaxis básica:
create default NOMBREVALORPREDETERMINADO
as VALORPREDETERMINADO;

"VALORPREDETERMINADO" no puede hacer referencia a campos de una tabla (u otros objetos)


y debe ser compatible con el tipo de datos y longitud del campo al cual se asocia; si esto no
sucede, SQL Server no lo informa al crear el valor predeterminado ni al asociarlo, pero al ejecutar
una instrucción "insert" muestra un mensaje de error.
En el siguiente ejemplo creamos un valor predeterminado llamado "VP_datodesconocido' con el
valor "Desconocido":
create default VP_datodesconocido
as 'Desconocido'

Luego de crear un valor predeterminado, debemos asociarlo a un campo (o a un tipo de datos


definido por el usuario) ejecutando el procedimiento almacenado del sistema "sp_bindefault":
exec sp_bindefault NOMBRE, 'NOMBRETABLA.CAMPO';

La siguiente sentencia asocia el valor predeterminado creado anteriormente al campo "domicilio"


de la tabla "empleados":
exec sp_bindefault VP_datodesconocido, 'empleados.domicilio';

Podemos asociar un valor predeterminado a varios campos. Asociamos el valor predeterminado


"VP_datodesconocido" al campo "barrio" de la tabla "empleados":

182
exec sp_bindefault VP_datodesconocido, 'empleados.barrio';

La función que cumple un valor predeterminado es básicamente la misma que una restricción
"default", las siguientes características explican algunas semejanzas y diferencias entre ellas:
- un campo solamente puede tener definida UNA restricción "default", un campo solamente puede
tener UN valor predeterminado asociado a él,
- una restricción "default" se almacena con la tabla, cuando ésta se elimina, las restricciones
también. 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 restricción "default" se establece para un solo campo; un valor predeterminado puede
asociarse a distintos campos (inclusive, de diferentes tablas).
- una restricción "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 restricción "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 asociación reemplaza a la anterior.
Veamos otros ejemplos.
Creamos un valor predeterminado que inserta el valor "0" en un campo de tipo numérico:
create default VP_cero
as 0;

En el siguiente creamos un valor predeterminado que inserta ceros con el formato válido para un
número de teléfono:
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".
Una empresa registra los datos de sus empleados en una tabla llamada "empleados".
Eliminamos la tabla "empleados" si existe:
if object_id ('empleados') is not null
drop table empleados;

183
También debemos eliminar los valores predeterminados con las siguientes intrucciones (en el siguiente
capítulo explicaremos este tema):
if object_id ('VP_cero') is not null
drop default VP_cero;
if object_id ('VP_100') is not null
drop default VP_100;
if object_id ('VP_datodesconocido') is not null
drop default VP_datodesconocido;
if object_id ('VP_telefono') is not null
drop default VP_telefono;

Creamos la tabla:
create table empleados(
nombre varchar(30),
domicilio varchar(30),
barrio varchar(15),
telefono char(14),
sueldo decimal(6,2)
);

Ingresamos un registro sin valores para ver qué valores por defecto se almacenan:
insert into empleados default values;
select *from empleados;

Creamos un valor predeterminado con el valor "Desconocido":


create default VP_datodesconocido
as 'Desconocido';

Lo asociamos al campo "domicilio":


exec sp_bindefault VP_datodesconocido, 'empleados.domicilio';

Lo asociamos al campo "barrio":


exec sp_bindefault VP_datodesconocido, 'empleados.barrio';

Ingresamos otro registro sin especificar valores y vemos los datos:


insert into empleados default values;
select *from empleados;

Creamos un valor predeterminado que inserta el valor "0":


create default VP_cero
as 0;

Lo asociamos al campo "sueldo":


exec sp_bindefault VP_cero, 'empleados.sueldo';

Ingresamos un registro y verificamos los datos:


insert into empleados default values;
select *from empleados;

Creamos un valor predeterminado que inserta el valor "100":

184
create default VP_100
as 100;

Lo asociamos al campo "sueldo":


exec sp_bindefault VP_100, 'empleados.sueldo';

Recuerde que si asociamos a un campo que ya tiene asociado un valor predeterminado otro valor
predeterminado, la nueva asociación reemplaza a la anterior. Verificamos:
insert into empleados default values;
select *from empleados;

Veamos los valores predeterminados asociadas a la tabla "empleados":


sp_helpconstraint empleados;

El valor predeterminado "VP_cero" no aparece porque no está asociado a ningún campo de "empleados"; si
aparecen "VP_100", "VP_datodesconocido" que está asociado a 2 campos (domicilio y barrio).
Veamos si "VP_cero" existe, ejecutando el procedimiento almacenado del sistema "sp_help":
sp_help;

Aún existe en la base de datos.


Creamos un valor predeterminado que inserta ceros con el formato válido para un campo número de
teléfono:
create default VP_telefono
as '(0000)0-000000';

La asociamos al campo "telefono" de la tabla "empleados":


exec sp_bindefault VP_telefono,'empleados.telefono';

Ingresamos un registro y verificamos los valores predeterminados almacenados:


insert into empleados default values;
select *from empleados;

Veamos los valores predeterminados asociadas a la tabla "empleados":


sp_helpconstraint empleados;

Aparecen 4 filas, una por cada asociación.


Primer problema:
Una empresa registra los datos de sus clientes en una tabla llamada "clientes".
1- Elimine la tabla si existe:
if object_id ('clientes') is not null
drop table clientes;

2- Recuerde que si elimina una tabla, las asociaciones de reglas y valores


predeterminados de sus
campos desaparecen, pero las reglas y valores predeterminados siguen existiendo. Si
intenta crear
una regla o un valor predeterminado con igual nombre que uno existente, aparecerá un
mensaje
indicándolo, por ello, debe eliminarlos (si existen) para poder crearlos nuevamente:
if object_id ('VP_legajo_patron') is not null
185
drop default VP_legajo_patron;
if object_id ('RG_legajo_patron') is not null
drop rule RG_legajo_patron;
if object_id ('RG_legajo') is not null
drop rule RG_legajo;
if object_id ('VP_datodesconocido') is not null
drop default VP_datodesconocido;
if object_id ('VP_fechaactual') is not null
drop default VP_fechaactual;

3- Cree la tabla:
create table clientes(
legajo char(4),
nombre varchar(30),
domicilio varchar(30),
ciudad varchar(15),
provincia varchar(20) default 'Cordoba',
fechaingreso datetime
);

4- Cree una regla para establecer un patrón para los valores que se ingresen en el
campo "legajo" (2
letras seguido de 2 cifras) llamada "RG_legajo_patron":

5- Asocie la regla al campo "legajo".

6- Cree un valor predeterminado para el campo "legajo" ('AA00') llamado


"VP_legajo_patron".

7- Asócielo al campo "legajo".


Recuerde que un campo puede tener un valor predeterminado y reglas asociados.

8- Cree un valor predeterminado con la cadena "??" llamado "VP_datodesconocido".

9- Asócielo al campo "domicilio".

10- Asócielo al campo "ciudad".


Recuerde que un valor predeterminado puede asociarse a varios campos.

11- Ingrese un registro con valores por defecto para los campos "domicilio" y "ciudad"
y vea qué
almacenaron.

12- Intente asociar el valor predeterminado "VP_datodesconocido" al campo "provincia".


No se puede porque dicho campo tiene una restricción "default".

13- Cree un valor predeterminado con la fecha actual llamado "VP_fechaactual".

14- Asócielo al campo "fechaingreso".

15- Ingrese algunos registros para ver cómo se almacenan los valores para los cuales no
se insertan
datos.

16- Asocie el valor predeterminado "VP_datodesconocido" al campo "fechaingreso".


Note que se asoció un valor predeterminado de tipo caracter a un campo de tipo
"datetime"; SQL
Server lo permite, pero al intentar ingresar el valor aparece un mensaje de error.

17- Ingrese un registro con valores por defecto.


No lo permite porque son de distintos tipos.

18- Cree una regla que entre en conflicto con el valor predeterminado
"VP_legajo_patron".

186
19- Asocie la regla al campo "legajo".
Note que la regla especifica que el campo "legajo" debe comenzar con la letra "B", pero
el valor
predeterminado tiene el valor "AA00"; SQL Server realiza la asociación, pero al
intentar ingresar el
valor predeterminado, no puede hacerlo y muestra un mensaje de error.

20- Intente ingresar un registro con el valor "default" para el campo "legajo".
No lo permite porque al intentar ingresar el valor por defecto establecido con el valor
predeterminado entra en conflicto con la regla "RG_legajo".

58 - Desasociar y eliminar valores


predeterminados
Un valor predeterminado no puede eliminarse si no se ha desasociado previamente.
Para deshacer una asociación empleamos el procedimiento almacenado "sp_unbindefault"
seguido de la tabla y campo al que está asociado:
sp_unbindefault 'TABLA.CAMPO';

Quitamos la asociación al campo "sueldo" de la tabla "empleados":


sp_unbindefault 'empleados.sueldo';

Con la instrucción "drop default" podemos eliminar un valor predeterminado:


drop default NOMBREVALORPREDETERMINADO;

Eliminamos el valor predeterminado llamado "VP_cero":


drop default VP_cero;

Si eliminamos una tabla, las asociaciones de valores predeterminados de sus campos


desaparecen, pero los valores predeterminados siguen existiendo.
Una empresa registra los datos de sus empleados en una tabla llamada "empleados".
Eliminamos la tabla "empleados" si existe:
if object_id ('empleados') is not null
drop table empleados;

Recordemos que si eliminamos una tabla, las asociaciones de los valores predeterminados a sus campos
desaparecen, pero los valores predeterminados siguen existiendo. Si intentamos crear un valor
predeterminado con igual nombre que uno existente, aparecerá un mensaje indicándolo, por ello, debemos
eliminar los valores predeterminados (si existen) para poder crearlos nuevamente:
if object_id ('VP_cero') is not null
drop default VP_cero;
if object_id ('VP_datodesconocido') is not null
drop default VP_datodesconocido;

187
Creamos la tabla:
create table empleados(
nombre varchar(30),
domicilio varchar(30),
barrio varchar(15),
sueldo decimal(6,2)
);

Creamos un valor predeterminado que inserta el valor "0":


create default VP_cero
as 0;

Lo asociamos al campo "sueldo":


exec sp_bindefault VP_cero, 'empleados.sueldo';

Creamos un valor predeterminado con el valor "Desconocido":


create default VP_datodesconocido
as 'Desconocido';

Lo asociamos al campo "domicilio" y al campo "barrio":


exec sp_bindefault VP_datodesconocido, 'empleados.domicilio';
exec sp_bindefault VP_datodesconocido, 'empleados.barrio';

Veamos los valores predeterminados asociados a los campos de la tabla "empleados":


sp_helpconstraint empleados;

Aparecen 3 filas, correspondientes a las asociaciones de valores predeterminados a los campos "sueldo",
"domicilio" y "barrio".
Quitamos la asociación al campo "barrio":
sp_unbindefault 'empleados.barrio';

Ejecutamos nuevamente el procedimiento "sp_helpconstraint":


sp_helpconstraint empleados;

Note que el valor predeterminado "VP_datodesconocido" ya no está asociado al campo "barrio". Pero dicho
valor predeterminado aún existe en la base de datos, verifiquémoslo:
sp_help;

aparece en la lista.
Aun no podemos eliminarlo porque está asociado al campo "domicilio", quitamos la asociación y luego lo
eliminamos:
sp_unbindefault 'empleados.domicilio';
drop default VP_datodesconocido;

Primer problema:
Una librería almacena los datos de sus libros en una tabla llamada "libros".
1- Elimine la tabla si existe:
if object_id ('libros') is not null

188
drop table libros;

2- Recuerde que si elimina una tabla, las asociaciones de reglas y valores


predeterminados de sus
campos desaparecen, pero las reglas y valores predeterminados siguen existiendo. Si
intenta crear
una regla o un valor predeterminado con igual nombre que uno existente, aparecerá un
mensaje
indicándolo, por ello, debe eliminarlos (si existen) para poder crearlos nuevamente:
if object_id ('VP_cero') is not null
drop default VP_cero;
if object_id ('VP_desconocido') is not null
drop default VP_desconocido;
if object_id ('RG_positivo') is not null
drop rule RG_positivo;

3- Cree la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(30),
editorial varchar(20),
precio decimal(5,2),
cantidad smallint
);

4- Cree una regla para impedir que se ingresen valores negativos, llamada
"RG_positivo".

5- Asocie la regla al campo "precio".

6- Asocie la regla al campo "cantidad".

7- Cree un valor predeterminado para que almacene el valor cero, llamado "VP_cero".

8- Asócielo al campo "precio".

9- Asócielo al campo "cantidad".

10- Cree un valor predeterminado con la cadena "Desconocido" llamado "VP_desconocido".

11- Asócielo al campo "autor".

12- Asócielo al campo "editorial".

13- Vea las reglas y valores predeterminados con "sp_help":


sp_help;

14- Vea las reglas y valores predeterminados asociados a "libros".


Aparecen 6 filas, 2 corresponden a la regla "RG_positivo" asociadas a los campos
"precio" y
"cantidad"; 2 al valor predeterminado "VP_cero" asociados a los campos "precio" y
"cantidad" y 2 al
valor predeterminado "VP_desconocido" asociados a los campos "editorial" y "autor".

15- Ingrese un registro con valores por defecto para todos los campos, excepto "titulo"
y vea qué se
almacenó.

15- Quite la asociación del valor predeterminado "VP_cero" al campo "precio".

16- Ingrese otro registro con valor predeterminado para el campo "precio" y vea cómo se
almacenó.

189
17- Vea las reglas y valores predeterminados asociados a "libros".
5 filas; el valor predeterminado "VP_cero" ya no está asociado al campo "precio".

18- Verifique que el valor predeterminado "VP_cero" existe aún en la base de datos.

19- Intente eliminar el valor predeterminado "VP_cero".


No se puede porque está asociado al campo "cantidad".

20- Quite la asociación del valor predeterminado "VP_cero" al campo "cantidad".

21- Verifique que ya no existe asociación de este valor predeterminado con la tabla
"libros".
4 filas.

22- Verifique que el valor predeterminado "VP_cero" aun existe en la base de datos.

23- Elimine el valor predeterminado "VP_cero".

24- Verifique que ya no existe en la base de datos.

59 - Información de valores predeterminados


Para obtener información de los valores predeterminados podemos emplear los mismos
procedimientos almacenados que usamos para las reglas.
Si empleamos "sp_help", vemos todos los objetos de la base de datos activa (incluyendo los
valores predeterminados); en la columna "Object_type" (tipo de objeto) muestra "default".
Si al procedimiento almacenado "sp_help" le agregamos el nombre de un valor predeterminado,
nos muestra el nombre, propietario, tipo y fecha de creación:
sp_help NOMBREVALORPREDETERMINADO;

Con "sp_help", no sabemos si los valores predeterminados existentes están o no asociadas a


algún campo.
"sp_helpconstraint" retorna una lista de todas las restricciones que tiene una tabla. También los
valores predeterminados asociados; muestra la siguiente información:
- 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;

190
También 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%';--búsqueda con comodín

Una empresa registra los datos de sus empleados en una tabla llamada "empleados".
Eliminamos la tabla "empleados" (si existe) y los valores predeterminados:
if object_id ('empleados') is not null
drop table empleados;
if object_id ('VP_sueldo') is not null
drop default VP_sueldo;
if object_id ('VP_seccion') is not null
drop default Vp_seccion;

Creamos la tabla:
create table empleados(
documento char(8) not null,
nombre varchar(30) not null,
seccion varchar(20),
sueldo decimal(6,2),
primary key(documento)
);

Creamos un valor predeterminado para el campo "sueldo":


create default VP_sueldo
as 500;

Asociamos el valor predeterminado creado anteriormente al campo "sueldo":


exec sp_bindefault VP_sueldo, 'empleados.sueldo';

Creamos un valor predeterminado para "seccion":


create default VP_seccion
as 'Secretaria';

No la asociamos.
Veamos todos los objetos de la base de datos activa:
sp_help;

Aparece una tabla con todos los objetos de la base de datos activa, incluyendo los dos valores
predeterminados creados anteriormente; se muestra el nombre, el propietario del objeto y, en caso de los
valores predeterminados, aparece "default".
Si agregamos al procedimiento almacenado "sp_help" el nombre del valor predeterminado del cual
queremos información:
sp_help VP_sueldo;
191
muestra el nombre, propietario, tipo y fecha de creación.
Para ver los valores predeterminados asociados a la tabla "empleados" tipeamos:
sp_helpconstraint empleados;

retorna una lista de todas las restricciones que tiene una tabla. También los valores predeterminados
asociados. Aparecen 2 filas: una con información de la restricción "primary key" y otra con información del
valor predeterminado asociado, este último muestra:
- 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: el texto del valor predeterminado.
- las otras columnas no tienen información.
Note que no aparece el valor predeterminado "VP_seccion" porque no fue asociado a la tabla.
Si asociamos el valor predeterminado a la tabla:
exec sp_bindefault VP_seccion, 'empleados.seccion';

y ejecutamos el procedimiento nuevamente:


sp_helpconstraint empleados;

Aparecen ambos valores predeterminados.


Veamos el texto que define el valor predeterminado "VP_seccion" empleando "sp_helptext":
sp_helptext VP_seccion;

Deshacemos la asociación del valor predeterminado "VP_sueldo" y lo eliminamos:


exec sp_unbindefault'empleados.sueldo';
drop default VP_sueldo;

Verificamos que ya no existe tal valor predeterminado:


sp_help VP_sueldo;

Vemos si el valor predeterminado "VP_seccion" existe consultando la tabla "sysobjects":


select *from sysobjects
where xtype='D' and
name like '%seccion%';

60 - Indices
SQL Server accede a los datos de dos maneras:

192
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 obtención de información de una tabla. El indice de una
tabla desempeña la misma función que el índice de un libro: permite encontrar datos rápidamente;
en el caso de las tablas, localiza registros.
Una tabla se indexa por un campo (o varios).
Un índice posibilita el acceso directo y rápido haciendo más eficiente las búsquedas. Sin índice,
SQL Server debe recorrer secuencialmente toda la tabla para encontrar un registro.
El objetivo de un indice es acelerar la recuperación de información. La indexación es una técnica
que optimiza el acceso a los datos, mejora el rendimiento acelerando las consultas y otras
operaciones. Es útil cuando la tabla contiene miles de registros, cuando se realizan operaciones
de ordenamiento y agrupamiento y cuando se combinan varias tablas (tema que veremos más
adelante).
La desventaja es que consume espacio en el disco en disco y genera costo de mantenimiento
(tiempo y recursos).
Los índices más adecuados son aquellos creados con campos que contienen valores únicos.
Es importante identificar el o los campos por los que sería útil crear un índice, aquellos campos
por los cuales se realizan búsqueda 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.

61 - Indices agrupados y no agrupados


(clustered y nonclustered)
Dijimos que SQL Server permite crear dos tipos de índices: 1) agrupados (clustered) y 2) no
agrupados (nonclustered).
1) Un INDICE AGRUPADO es similar a una guía telefónica, los registros con el mismo valor de
campo se agrupan juntos. Un índice agrupado determina la secuencia de almacenamiento de los
registros en una tabla.
Se utilizan para campos por los que se realizan busquedas con frecuencia o se accede siguiendo
un orden.
Una tabla sólo puede tener UN índice agrupado.
El tamaño medio de un índice agrupado es aproximadamente el 5% del tamaño de la tabla.

193
2) Un INDICE NO AGRUPADO es como el índice de un libro, los datos se almacenan en un lugar
diferente al del índice, los punteros indican el lugar de almacenamiento de los elementos
indizados en la tabla.
Un índice no agrupado se emplea cuando se realizan distintos tipos de busquedas
frecuentemente, con campos en los que los datos son únicos.
Una tabla puede tener hasta 249 índices no agrupados.
Si no se especifica un tipo de índice, de modo predeterminado será no agrupado.
Los campos de tipo text, ntext e image no se pueden indizar.
Es recomendable crear los índices agrupados antes que los no agrupados, porque los primeros
modifican el orden físico de los registros, ordenándolos secuencialmente.
La diferencia básica entre índices agrupados y no agrupados es que los registros de un índice
agrupado están ordenados y almacenados de forma secuencial en función de su clave.
SQL Server crea automaticamente índices cuando se crea una restricción "primary key" o
"unique" en una tabla.
Es posible crear índices en las vistas.
Resumiendo, los índices facilitan la recuperación de datos, permitiendo el acceso directo y
acelerando las búsquedas, consultas y otras operaciones que optimizan el rendimiento general.

62 - Creación de índices
Para crear índices empleamos la instrucción "create index".
La sintaxis básica 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, también 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 fácilmente, 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);

194
Un índice puede tener más 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
on libros(autor,editorial);

SQL Server crea automáticamente índices cuando se establece una restricción "primary key" o
"unique" en una tabla. Al crear una restricción "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
restricción "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 restricción "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_%';

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


Eliminamos la tabla si existe y la creamos:
if object_id('libros') is not null
drop table libros;
create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(15)
);

Creamos un índice agrupado único para el campo "codigo" de la tabla "libros":


195
create unique clustered index I_libros_codigo
on libros(codigo);

Creamos un índice no agrupado para el campo "titulo":


create nonclustered index I_libros_titulo
on libros(titulo);

Veamos los indices de "libros":


sp_helpindex libros;

Aparecen 2 filas, uno por cada índice. Muestra el nombre del índice, si es agrupado (o no), primary (o
unique) y el campo por el cual se indexa.
Creamos una restricción "primary key" al campo "codigo" especificando que cree un índice NO agrupado:
alter table libros
add constraint PK_libros_codigo
primary key nonclustered (codigo);

Verificamos que creó un índice automáticamente:


sp_helpindex libros;

El nuevo índice es agrupado, porque no se especificó.


Analicemos la información que nos muestra "sp_helpconstraint":
sp_helpconstraint libros;

En la columna "constraint_type" aparece "PRIMARY KEY" y entre paréntesis, el tipo de índice creado.
Creamos un índice compuesto para el campo "autor" y "editorial":
create index I_libros_autoreditorial
on libros(autor,editorial);

Se creará uno no agrupado porque no especificamos el tipo, además, ya existe uno agrupado y solamente
puede haber uno por tabla.
Consultamos la tabla "sysindexes":
select name from sysindexes;

Veamos los índices de la base de datos activa creados por nosotros podemos tipear la siguiente consulta:
select name from sysindexes
where name like 'I_%';

Primer problema:
Un profesor guarda algunos datos de sus alumnos en una tabla llamada "alumnos".
1- Elimine la tabla si existe y créela con la siguiente estructura:
if object_id('alumnos') is not null
drop table alumnos;
create table alumnos(
legajo char(5) not null,
documento char(8) not null,
apellido varchar(30),
nombre varchar(30),
notafinal decimal(4,2)
196
);

2- Ingresamos algunos registros:


insert into alumnos values ('A123','22222222','Perez','Patricia',5.50);
insert into alumnos values ('A234','23333333','Lopez','Ana',9);
insert into alumnos values ('A345','24444444','Garcia','Carlos',8.5);
insert into alumnos values ('A348','25555555','Perez','Daniela',7.85);
insert into alumnos values ('A457','26666666','Perez','Fabian',3.2);
insert into alumnos values ('A589','27777777','Gomez','Gaston',6.90);

3- Intente crear un índice agrupado único para el campo "apellido".


No lo permite porque hay valores duplicados.

4- Cree un índice agrupado, no único, para el campo "apellido".

5- Intente establecer una restricción "primary key" al campo "legajo" especificando que
cree un
índice agrupado.
No lo permite porque ya existe un índice agrupado y solamente puede haber uno por
tabla.

6- Establezca la restricción "primary key" al campo "legajo" especificando que cree un


índice NO
agrupado.

7- Vea los índices de "alumnos":


sp_helpindex alumnos;
2 índices: uno "I_alumnos_apellido", agrupado, con "apellido" y otro
"PK_alumnos_legajo", no
agrupado, unique, con "legajo" que se creó automáticamente al crear la restricción
"primary key".

8- Analice la información que muestra "sp_helpconstraint":


sp_helpconstraint libros;
En la columna "constraint_type" aparece "PRIMARY KEY" y entre paréntesis, el tipo de
índice creado.

9- Cree un índice unique no agrupado para el campo "documento".

10- Intente ingresar un alumno con documento duplicado.


No lo permite.

11- Veamos los indices de "alumnos".


Aparecen 3 filas, uno por cada índice.

12- Cree un índice compuesto para el campo "apellido" y "nombre".


Se creará uno no agrupado porque no especificamos el tipo, además, ya existe uno
agrupado y
solamente puede haber uno por tabla.

13- Consulte la tabla "sysindexes", para ver los nombres de todos los índices creados
para
"alumnos":
select name from sysindexes
where name like '%alumnos%';
4 índices.

14- Cree una restricción unique para el campo "documento".

15- Vea la información de "sp_helpconstraint".

16- Vea los índices de "alumnos".


Aparecen 5 filas, uno por cada índice.

197
17- Consulte la tabla "sysindexes", para ver los nombres de todos los índices creados
para
"alumnos":
select name from sysindexes
where name like '%alumnos%';
5 índices.

18- Consulte la tabla "sysindexes", para ver los nombres de todos los índices creados
por usted:
select name from sysindexes
where name like 'I_%';
3 índices. Recuerde que los índices que crea SQL Server automáticamente al agregarse
una restricción
"primary" o "unique" no comienzan con "I_".

63 - Regenerar índices
Vimos que para crear índices empleamos la instrucción "create index".
Empleando la opción "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;

También podemos modificar alguna de las características de un índice con esta opción, a saber:
- tipo: cambiándolo 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 algún
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 opción no puede emplearse con índices creados a partir de una restricción "primary key" o
"unique".
198
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe y la creamos:
if object_id('libros') is not null
drop table libros;
create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(15)
);

Creamos un índice no agrupado para el campo "titulo":


create nonclustered index I_libros_titulo
on libros(titulo);

Veamos la información:
sp_helpindex libros;

Vamos a agregar el campo "autor" al índice "I_libros_titulo" y vemos si se modificó:


create index I_libros_titulo
on libros(titulo,autor)
with drop_existing;

exec sp_helpindex libros;

Lo convertimos en agrupado y ejecutamos "sp_helpindex":


create clustered index I_libros_titulo
on libros(titulo,autor)
with drop_existing;

exec sp_helpindex libros;

Quitamos un campo "autor" y verificamos la modificación:


create clustered index I_libros_titulo
on libros(titulo)
with drop_existing;

exec sp_helpindex libros;

Primer problema:

Un profesor guarda algunos datos de sus alumnos en una tabla llamada "alumnos".
1- Elimine la tabla si existe y créela con la siguiente estructura:
if object_id('alumnos') is not null
drop table alumnos;
create table alumnos(
legajo char(5) not null,
documento char(8) not null,
apellido varchar(30),
nombre varchar(30),
notafinal decimal(4,2)
);

2- Cree un índice no agrupado para el campo "apellido".

3- Vea la información de los índices de "alumnos".


199
4- Modifíquelo agregando el campo "nombre".

5- Verifique que se modificó:


exec sp_helpindex alumnos;

6- Establezca una restricción "unique" para el campo "documento".

7- Vea la información que muestra "sp_helpindex":


sp_helpindex alumnos;

8- Intente modificar con "drop_existing" alguna característica del índice que se creó
automáticamente al agregar la restricción "unique":
create clustered index UQ_alumnos_documento
on alumnos(documento)
with drop_existing;
No se puede emplear "drop_existing" con índices creados a partir de una restricción.

9- Cree un índice no agrupado para el campo "legajo".

10- Muestre todos los índices:


sp_helpindex alumnos;

11- Convierta el índice creado en el punto 9 a agrupado conservando las demás


características.

12- Verifique que se modificó:


exec sp_helpindex alumnos;

13- Intente convertir el índice "I_alumnos_legajo" a no agrupado:


create nonclustered index I_alumnos_legajo
on alumnos(legajo)
with drop_existing;
No se puede convertir un índice agrupado en no agrupado.

14- Modifique el índice "I_alumnos_apellido" quitándole el campo "nombre".

15- Intente convertir el índice "I_alumnos_apellido" en agrupado:


create clustered index I_alumnos_apellido
on alumnos(apellido)
with drop_existing;
No lo permite porque ya existe un índice agrupado.

16- Modifique el índice "I_alumnos_legajo" para que sea único y conserve todas las
otras
características.

17- Verifique la modificación:


sp_helpindex alumnos;

18- Modifique nuevamente el índice "I_alumnos_legajo" para que no sea único y conserve
las demás
características.

19- Verifique la modificación:


sp_helpindex alumnos;

64 - Eliminar índices
200
Los índices creados con "create index" se eliminan con "drop index"; la siguiente es la sintaxis
básica:
drop index NOMBRETABLA.NOMBREINDICE;

Eliminamos el índice "I_libros_titulo":


drop index libros.I_libros_titulo;

Los índices que SQL Server crea automáticamente al establecer una restricción "primary key" o
"unique" no pueden eliminarse con "drop index", se eliminan automáticamente cuando quitamos
la restricción.
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;

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


Eliminamos la tabla si existe y la creamos:

if object_id('libros') is not null


drop table libros;

create table libros(


codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(15)
);

Creamos un índice para el campo "titulo":


create index I_libros_titulo
on libros(titulo);

Verifiquemos que se creó:


sp_helpindex libros;

Eliminamos el índice "I_libros_titulo":


drop index libros.I_libros_titulo;

Corroboremos que se eliminó:


sp_helpindex libros;

Solicitamos que se elimine el índice "I_libros_titulo" si existe:


201
if exists (select name from sysindexes
where name = 'I_libros_titulo')
drop index libros.I_libros_titulo;

Primer problema:

Un profesor guarda algunos datos de sus alumnos en una tabla llamada "alumnos".
1- Elimine la tabla si existe y créela con la siguiente estructura:
if object_id('alumnos') is not null
drop table alumnos;
create table alumnos(
legajo char(5) not null,
documento char(8) not null,
apellido varchar(30),
nombre varchar(30),
notafinal decimal(4,2)
);

2- Cree un índice no agrupado para el campo "apellido".

3- Establezca una restricción "primary" para el campo "legajo" y especifique que cree
un índice
"agrupado".

4- Vea la información que muestra "sp_helpindex":


sp_helpindex alumnos;

5- Intente eliminar el índice "PK_alumnos_legajo" con "drop index":


drop index PK_alumnos_legajo;
No se puede.

6- Intente eliminar el índice "I_alumnos_apellido" sin especificar el nombre de la


tabla:
drop index I_alumnos_apellido;
Mensaje de error.

7- Elimine el índice "I_alumnos_apellido" especificando el nombre de la tabla.

8- Verifique que se eliminó:


sp_helpindex alumnos;

9- Solicite que se elimine el índice "I_alumnos_apellido" si existe:


if exists (select name from sysindexes
where name = 'I_alumnos_apellido')
drop index alumnos.I_alumnos_apellido;

10- Elimine el índice "PK_alumnos_legajo" (quite la restricción).

11- Verifique que el índice "PK_alumnos_legajo" ya no existe:


sp_helpindex alumnos;

65 - Trabajar con varias tablas


Hasta el momento hemos trabajado con una sola tabla, pero generalmente, se trabaja con más
de una.

202
Para evitar la repetición de datos y ocupar menos espacio, se separa la información en varias
tablas. Cada tabla almacena parte de la información que necesitamos registrar.
Por ejemplo, los datos de nuestra tabla "libros" podrían separarse en 2 tablas, una llamada
"libros" y otra "editoriales" que guardará la información de las editoriales.
En nuestra tabla "libros" haremos referencia a la editorial colocando un código que la identifique.
Veamos:
create table libros(
codigo 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 código de la editorial en la tabla "libros" y en
"editoriales".
Al recuperar los datos de los libros con la siguiente instrucción:
select* from libros;

vemos que en el campo "editorial" aparece el código, pero no sabemos el nombre de la editorial.
Para obtener los datos de cada libro, incluyendo el nombre de la editorial, necesitamos consultar
ambas tablas, traer información de las dos.
Cuando obtenemos información de más de una tabla decimos que hacemos un "join"
(combinación).
Veamos un ejemplo:
select *from libros
join editoriales
on libros.codigoeditorial=editoriales.codigo;

Resumiendo: si distribuimos la información en varias tablas evitamos la redundancia de datos y


ocupamos menos espacio físico en el disco. Un join es una operación que relaciona dos o más
tablas para obtener un resultado que incluya datos (campos y registros) de ambas; las tablas
participantes se combinan según los campos comunes a ambas tablas.
Hay hay tres tipos de combinaciones. En los siguientes capítulos explicamos cada una de ellas.

203
66 - Combinación interna (inner join)
Un join es una operación que relaciona dos o más tablas para obtener un resultado que incluya
datos (campos y registros) de ambas; las tablas participantes se combinan según los campos
comunes a ambas tablas.
Hay tres tipos de combinaciones:
1. combinaciones internas (inner join o join),
2. combinaciones externas y
3. combinaciones cruzadas.
También es posible emplear varias combinaciones en una consulta "select", incluso puede
combinarse una tabla consigo misma.
La combinación interna emplea "join", que es la forma abreviada de "inner join". Se emplea para
obtener información de dos tablas y combinar dicha información en una salida.
La sintaxis básica es la siguiente:
select CAMPOS
from TABLA1
join TABLA2
on CONDICIONdeCOMBINACION;

Ejemplo:
select *from libros
join editoriales
on codigoeditorial=editoriales.codigo;

Analicemos la consulta anterior.


- especificamos los campos que aparecerán en el resultado en la lista de selección;
- indicamos el nombre de la tabla luego del "from" ("libros");
- combinamos esa tabla con "join" y el nombre de la otra tabla ("editoriales"); se especifica qué
tablas se van a combinar y cómo;
- cuando se combina información de varias tablas, es necesario especificar qué registro de una
tabla se combinará con qué registro de la otra tabla, con "on". Se debe especificar la condición
para enlazarlas, es decir, el campo por el cual se combinarán, que tienen en común.
"on" hace coincidir registros de ambas tablas basándose en el valor de tal campo, en el ejemplo,
el campo "codigoeditorial" de "libros" y el campo "codigo" de "editoriales" son los que enlazarán
ambas tablas. Se emplean campos comunes, que deben tener tipos de datos iguales o similares.
La condicion de combinación, es decir, el o los campos por los que se van a combinar (parte
"on"), se especifica según las claves primarias y externas.
Note que en la consulta, al nombrar el campo usamos el nombre de la tabla también. Cuando las
tablas referenciadas tienen campos con igual nombre, esto es necesario para evitar confusiones y
ambiguedades al momento de referenciar un campo. En el ejemplo, si no especificamos
204
"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 cláusula "on"
se debe hacer referencia a la clave completa, es decir, la condición referenciará a todos los
campos clave que identifican al registro.
Se puede incluir en la consulta join la cláusula "where" para restringir los registros que retorna el
resultado; también "order by", "distinct", etc..
Se emplea este tipo de combinación para encontrar registros de la primera tabla que se
correspondan con los registros de la otra, es decir, que cumplan la condición del "on". Si un valor
de la primera tabla no se encuentra en la segunda tabla, el registro no aparece.
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 simplificación y hace
más legible la consulta si es larga y compleja, pero en algunas consultas es absolutamente
necesario.
Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".

Eliminamos ambas tablas, si existen:


if object_id('libros') is not null
drop table libros;
if object_id('editoriales') is not null
drop table editoriales;

Creamos las tablas:


create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30) default 'Desconocido',
codigoeditorial tinyint not null,
precio decimal(5,2)
);
create table editoriales(
codigo tinyint identity,
nombre varchar(20),
primary key (codigo)
);

Ingresamos algunos registros en ambas tablas:


insert into editoriales values('Planeta');
insert into editoriales values('Emece');

205
insert into editoriales values('Siglo XXI');

insert into libros values('El aleph','Borges',2,20);


insert into libros values('Martin Fierro','Jose Hernandez',1,30);
insert into libros values('Aprenda PHP','Mario Molina',3,50);
insert into libros values('Java en 10 minutos',default,3,45);

Recuperamos los datos de libros:


select* from libros;

vemos que en el campo "editorial" aparece el código, pero no sabemos el nombre de la editorial.
Realizamos un join para obtener datos de ambas tablas (titulo, autor y nombre de la editorial):
select titulo, autor, nombre
from libros
join editoriales
on codigoeditorial=editoriales.codigo;

Mostramos el código del libro, título, autor, nombre de la editorial y el precio realizando un join y
empleando alias:
select l.codigo,titulo,autor,nombre,precio
from libros as l
join editoriales as e
on codigoeditorial=e.codigo;

Note que al listar el campo "codigo" especificamos a qué tabla pertenece; si no lo hacemos SQL Server no
sabrá si nos referimos al de la tabla "libros" o "editoriales". Los demás campos no tienen referencia a la tabla
porque tienen nombres que no se repiten.
Realizamos la misma consulta anterior agregando un "where" para obtener solamente los libros de la
editorial "Siglo XXI":
select l.codigo,titulo,autor,nombre,precio
from libros as l
join editoriales as e
on codigoeditorial=e.codigo
where e.nombre='Siglo XXI';

Obtenemos título, autor y nombre de la editorial, esta vez ordenados por título:
select titulo,autor,nombre
from libros as l
join editoriales as e
on codigoeditorial=e.codigo
order by titulo;

Primer problema:

Una empresa tiene registrados sus clientes en una tabla llamada "clientes", también
tiene una tabla
"provincias" donde registra los nombres de las provincias.
1- Elimine las tablas "clientes" y "provincias", si existen:
if (object_id('clientes')) is not null
drop table clientes;
if (object_id('provincias')) is not null
drop table provincias;

2- Créelas con las siguientes estructuras:


206
create table clientes (
codigo int identity,
nombre varchar(30),
domicilio varchar(30),
ciudad varchar(20),
codigoprovincia tinyint not null,
primary key(codigo)
);

create table provincias(


codigo tinyint identity,
nombre varchar(20),
primary key (codigo)
);

3- Ingrese algunos registros para ambas tablas:


insert into provincias (nombre) values('Cordoba');
insert into provincias (nombre) values('Santa Fe');
insert into provincias (nombre) values('Corrientes');

insert into clientes values ('Lopez Marcos','Colon 111','Córdoba',1);


insert into clientes values ('Perez Ana','San Martin 222','Cruz del Eje',1);
insert into clientes values ('Garcia Juan','Rivadavia 333','Villa Maria',1);
insert into clientes values ('Perez Luis','Sarmiento 444','Rosario',2);
insert into clientes values ('Pereyra Lucas','San Martin 555','Cruz del Eje',1);
insert into clientes values ('Gomez Ines','San Martin 666','Santa Fe',2);
insert into clientes values ('Torres Fabiola','Alem 777','Ibera',3);

4- Obtenga los datos de ambas tablas, usando alias:


select c.nombre,domicilio,ciudad,p.nombre
from clientes as c
join provincias as p
on c.codigoprovincia=p.codigo;

5- Obtenga la misma información anterior pero ordenada por nombre de provincia.

6- Recupere los clientes de la provincia "Santa Fe" (2 registros devueltos)

67 - Combinación externa izquierda (left join)


Vimos que una combinación interna (join) encuentra registros de la primera tabla que se
correspondan con los registros de la segunda, es decir, que cumplan la condición del "on" y si un
valor de la primera tabla no se encuentra en la segunda tabla, el registro no aparece.
Si queremos saber qué registros de una tabla NO encuentran correspondencia en la otra, es
decir, no existe valor coincidente en la segunda, necesitamos otro tipo de combinación, "outer
join" (combinación externa).
Las combinaciones externas combinan registros de dos tablas que cumplen la condición, más los
registros de la segunda tabla que no la cumplen; es decir, muestran todos los registros de las
tablas relacionadas, aún cuando no haya valores coincidentes entre ellas.

207
Este tipo de combinación se emplea cuando se necesita una lista completa de los datos de una
de las tablas y la información que cumple con la condición. Las combinaciones externas se
realizan solamente entre 2 tablas.
Hay tres tipos de combinaciones externas: "left outer join", "right outer join" y "full outer join"; se
pueden abreviar con "left join", "right join" y "full join" respectivamente.
Vamos a estudiar las primeras.
Se emplea una combinación externa izquierda para mostrar todos los registros de la tabla de la
izquierda. Si no encuentra coincidencia con la tabla de la derecha, el registro muestra los campos
de la segunda tabla seteados a "null".
En el siguiente ejemplo solicitamos el título y nombre de la editorial de los libros:
select titulo,nombre
from editoriales as e
left join libros as l
on codigoeditorial = e.codigo;

El resultado mostrará el título y nombre de la editorial; las editoriales de las cuales no hay libros,
es decir, cuyo código de editorial no está presente en "libros" aparece en el resultado, pero con el
valor "null" en el campo "titulo".
Es importante la posición en que se colocan las tablas en un "left join", la tabla de la izquierda es
la que se usa para localizar registros en la tabla de la derecha.
Entonces, un "left join" se usa para hacer coincidir registros en una tabla (izquierda) con otra tabla
(derecha); si un valor de la tabla de la izquierda no encuentra coincidencia en la tabla de la
derecha, se genera una fila extra (una por cada valor no encontrado) con todos los campos
correspondientes a la tabla derecha seteados a "null". La sintaxis básica es la siguiente:
select CAMPOS
from TABLAIZQUIERDA
left join TABLADERECHA
on CONDICION;

En el siguiente ejemplo solicitamos el título y el nombre la editorial, la sentencia es similar a la


anterior, la diferencia está en el orden de las tablas:
select titulo,nombre
from libros as l
left join editoriales as e
on codigoeditorial = e.codigo;

El resultado mostrará el título del libro y el nombre de la editorial; los títulos cuyo código de
editorial no está presente en "editoriales" aparecen en el resultado, pero con el valor "null" en el
campo "nombre".
Un "left join" puede tener clausula "where" que restringa el resultado de la consulta considerando
solamente los registros que encuentran coincidencia en la tabla de la derecha, es decir, cuyo
valor de código está presente en "libros":
select titulo,nombre
from editoriales as e
left join libros as l
on e.codigo=codigoeditorial
208
where codigoeditorial is not null;

También podemos mostrar las editoriales que NO están presentes en "libros", es decir, que NO
encuentran coincidencia en la tabla de la derecha:
select titulo,nombre
from editoriales as e
left join libros as l
on e.codigo=codigoeditorial
where codigoeditorial is null;

Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".
Eliminamos ambas tablas, si existen y las creamos:

if object_id('libros') is not null


drop table libros;
if object_id('editoriales') is not null
drop table editoriales;

create table libros(


codigo int identity,
titulo varchar(40),
autor varchar(30) default 'Desconocido',
codigoeditorial tinyint not null,
precio decimal(5,2)
);
create table editoriales(
codigo tinyint identity,
nombre varchar(20),
primary key (codigo)
);

Ingresamos algunos registros en ambas tablas:


insert into editoriales values('Planeta');
insert into editoriales values('Emece');
insert into editoriales values('Siglo XXI');

insert into libros values('El aleph','Borges',1,20);


insert into libros values('Martin Fierro','Jose Hernandez',1,30);
insert into libros values('Aprenda PHP','Mario Molina',2,50);
insert into libros values('Java en 10 minutos',default,4,45);

Realizamos una combinación izquierda para obtener los datos de los libros, incluyendo el nombre de la
editorial:
select titulo,nombre
from editoriales as e
left join libros as l
on codigoeditorial = e.codigo;

Las editoriales de las cuales no hay libros, es decir, cuyo código de editorial no está presente en "libros"
aparece en el resultado, pero con el valor "null" en el campo "titulo".
Realizamos la misma consulta anterior pero cambiamos el orden de las tablas:
select titulo,nombre
from libros as l
left join editoriales as e
on codigoeditorial = e.codigo;

209
El resultado mostrará el título del libro y el nombre de la editorial; los títulos cuyo código de editorial no
está presente en "editoriales" aparecen en el resultado, pero con el valor "null" en el campo "nombre".
Restringimos el resultado de una consulta considerando solamente los registros que encuentran coincidencia
en la tabla de la derecha, es decir, cuyo valor de código está presente en "libros":
select titulo,nombre
from editoriales as e
left join libros as l
on e.codigo=codigoeditorial
where codigoeditorial is not null;

Mostramos las editoriales que NO están presentes en "libros", es decir, que NO encuentran coincidencia en
la tabla de la derecha:
select titulo,nombre
from editoriales as e
left join libros as l
on e.codigo=codigoeditorial
where codigoeditorial is null;

Primer problema:

Una empresa tiene registrados sus clientes en una tabla llamada "clientes", también
tiene una tabla
"provincias" donde registra los nombres de las provincias.
1- Elimine las tablas "clientes" y "provincias", si existen y cree las tablas:
if (object_id('clientes')) is not null
drop table clientes;
if (object_id('provincias')) is not null
drop table provincias;

create table clientes (


codigo int identity,
nombre varchar(30),
domicilio varchar(30),
ciudad varchar(20),
codigoprovincia tinyint not null,
primary key(codigo)
);

create table provincias(


codigo tinyint identity,
nombre varchar(20),
primary key (codigo)
);

2- Ingrese algunos registros para ambas tablas:


insert into provincias (nombre) values('Cordoba');
insert into provincias (nombre) values('Santa Fe');
insert into provincias (nombre) values('Corrientes');

insert into clientes values ('Lopez Marcos','Colon 111','Córdoba',1);


insert into clientes values ('Perez Ana','San Martin 222','Cruz del Eje',1);
insert into clientes values ('Garcia Juan','Rivadavia 333','Villa Maria',1);
insert into clientes values ('Perez Luis','Sarmiento 444','Rosario',2);
insert into clientes values ('Gomez Ines','San Martin 666','Santa Fe',2);
insert into clientes values ('Torres Fabiola','Alem 777','La Plata',4);
insert into clientes values ('Garcia Luis','Sucre 475','Santa Rosa',5);

3- Muestre todos los datos de los clientes, incluido el nombre de la provincia:


select c.nombre,domicilio,ciudad, p.nombre

210
from clientes as c
left join provincias as p
on codigoprovincia = p.codigo;

4- Realice la misma consulta anterior pero alterando el orden de las tablas:


select c.nombre,domicilio,ciudad, p.nombre
from provincias as p
left join clientes as c
on codigoprovincia = p.codigo;

5- Muestre solamente los clientes de las provincias que existen en "provincias" (5


registros):
select c.nombre,domicilio,ciudad, p.nombre
from clientes as c
left join provincias as p
on codigoprovincia = p.codigo
where p.codigo is not null;

6- Muestre todos los clientes cuyo código de provincia NO existe en "provincias"


ordenados por
nombre del cliente (2 registros):
select c.nombre,domicilio,ciudad, p.nombre
from clientes as c
left join provincias as p
on codigoprovincia = p.codigo
where p.codigo is null
order by c.nombre;

7- Obtenga todos los datos de los clientes de "Cordoba" (3 registros):


select c.nombre,domicilio,ciudad, p.nombre
from clientes as c
left join provincias as p
on codigoprovincia = p.codigo
where p.nombre='Cordoba';

68 - Combinación externa derecha (right join)


Vimos que una combinación externa izquierda (left join) encuentra registros de la tabla izquierda
que se correspondan con los registros de la tabla derecha y si un valor de la tabla izquierda no
se encuentra en la tabla derecha, el registro muestra los campos correspondientes a la tabla de
la derecha seteados a "null".
Una combinación externa derecha ("right outer join" o "right join") opera del mismo modo sólo que
la tabla derecha es la que localiza los registros en la tabla izquierda.
En el siguiente ejemplo solicitamos el título y nombre de la editorial de los libros empleando un
"right join":
select titulo,nombre
from libros as l
right join editoriales as e
on codigoeditorial = e.codigo;

211
El resultado mostrará el título y nombre de la editorial; las editoriales de las cuales no hay libros,
es decir, cuyo código de editorial no está presente en "libros" aparece en el resultado, pero con el
valor "null" en el campo "titulo".
Es FUNDAMENTAL tener en cuenta la posición en que se colocan las tablas en los "outer join".
En un "left join" la primera tabla (izquierda) es la que busca coincidencias en la segunda tabla
(derecha); en el "right join" la segunda tabla (derecha) es la que busca coincidencias en la
primera tabla (izquierda).
En la siguiente consulta empleamos un "left join" para conseguir el mismo resultado que el "right
join" anterior":
select titulo,nombre
from editoriales as e
left join libros as l
on codigoeditorial = e.codigo;

Note que la tabla que busca coincidencias ("editoriales") está en primer lugar porque es un "left
join"; en el "right join" precedente, estaba en segundo lugar.
Un "right join" hace coincidir registros en una tabla (derecha) con otra tabla (izquierda); si un valor
de la tabla de la derecha no encuentra coincidencia en la tabla izquierda, se genera una fila extra
(una por cada valor no encontrado) con todos los campos correspondientes a la tabla izquierda
seteados a "null". La sintaxis básica es la siguiente:
select CAMPOS
from TABLAIZQUIERDA
right join TABLADERECHA
on CONDICION;

Un "right join" también puede tener cláusula "where" que restringa el resultado de la consulta
considerando solamente los registros que encuentran coincidencia en la tabla izquierda:
select titulo,nombre
from libros as l
right join editoriales as e
on e.codigo=codigoeditorial
where codigoeditorial is not null;

Mostramos las editoriales que NO están presentes en "libros", es decir, que NO encuentran
coincidencia en la tabla de la derecha empleando un "right join":
select titulo,nombre
from libros as l
rightjoin editoriales as e
on e.codigo=codigoeditorial
where codigoeditorial is null;

Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".
Eliminamos ambas tablas, si existen y las creamos:

if object_id('libros') is not null


drop table libros;
if object_id('editoriales') is not null
drop table editoriales;

create table libros(

212
codigo int identity,
titulo varchar(40),
autor varchar(30) default 'Desconocido',
codigoeditorial tinyint not null,
precio decimal(5,2)
);
create table editoriales(
codigo tinyint identity,
nombre varchar(20),
primary key (codigo)
);

Ingresamos algunos registros en ambas tablas:


insert into editoriales values('Planeta');
insert into editoriales values('Emece');
insert into editoriales values('Siglo XXI');

insert into libros values('El aleph','Borges',1,20);


insert into libros values('Martin Fierro','Jose Hernandez',1,30);
insert into libros values('Aprenda PHP','Mario Molina',2,50);
insert into libros values('Java en 10 minutos',default,4,45);

Solicitamos el título y nombre de la editorial de los libros empleando un "right join":


select titulo,nombre
from libros as l
right join editoriales as e
on codigoeditorial = e.codigo;

Las editoriales de las cuales no hay libros, es decir, cuyo código de editorial no está presente en "libros"
aparece en el resultado, pero con el valor "null" en el campo "titulo".
Realizamos la misma consulta anterior agregando un "where" que restringa el resultado considerando
solamente los registros que encuentran coincidencia en la tabla izquierda:
select titulo,nombre
from libros as l
right join editoriales as e
on e.codigo=codigoeditorial
where codigoeditorial is not null;

Mostramos las editoriales que NO están presentes en "libros" (que NO encuentran coincidencia en
"editoriales"):
select titulo,nombre
from libros as l
right join editoriales as e
on e.codigo=codigoeditorial
where codigoeditorial is null;

Primer problema:

Una empresa tiene registrados sus clientes en una tabla llamada "clientes", también
tiene una
tabla "provincias" donde registra los nombres de las provincias.
1- Elimine las tablas "clientes" y "provincias", si existen y cree las tablas:
if (object_id('clientes')) is not null
drop table clientes;
if (object_id('provincias')) is not null
drop table provincias;

213
create table clientes (
codigo int identity,
nombre varchar(30),
domicilio varchar(30),
ciudad varchar(20),
codigoprovincia tinyint not null,
primary key(codigo)
);

create table provincias(


codigo tinyint identity,
nombre varchar(20),
primary key (codigo)
);

2- Ingrese algunos registros para ambas tablas:


insert into provincias (nombre) values('Cordoba');
insert into provincias (nombre) values('Santa Fe');
insert into provincias (nombre) values('Corrientes');

insert into clientes values ('Lopez Marcos','Colon 111','Córdoba',1);


insert into clientes values ('Perez Ana','San Martin 222','Cruz del Eje',1);
insert into clientes values ('Garcia Juan','Rivadavia 333','Villa Maria',1);
insert into clientes values ('Perez Luis','Sarmiento 444','Rosario',2);
insert into clientes values ('Gomez Ines','San Martin 666','Santa Fe',2);
insert into clientes values ('Torres Fabiola','Alem 777','La Plata',4);
insert into clientes values ('Garcia Luis','Sucre 475','Santa Rosa',5);

3- Muestre todos los datos de los clientes, incluido el nombre de la provincia


empleando un "right
join".

4- Obtenga la misma salida que la consulta anterior pero empleando un "left join".

5- Empleando un "right join", muestre solamente los clientes de las provincias que
existen en
"provincias" (5 registros)

6- Muestre todos los clientes cuyo código de provincia NO existe en "provincias"


ordenados por
ciudad (2 registros)

69 - Combinación externa completa (full join)


Vimos que un "left join" encuentra registros de la tabla izquierda que se correspondan con los
registros de la tabla derecha y si un valor de la tabla izquierda no se encuentra en la tabla
derecha, el registro muestra los campos correspondientes a la tabla de la derecha seteados a
"null". Aprendimos también que un "right join" opera del mismo modo sólo que la tabla derecha
es la que localiza los registros en la tabla izquierda.
Una combinación externa completa ("full outer join" o "full join") retorna todos los registros de
ambas tablas. Si un registro de una tabla izquierda no encuentra coincidencia en la tabla derecha,
las columnas correspondientes a campos de la tabla derecha aparecen seteadas a "null", y si la

214
tabla de la derecha no encuentra correspondencia en la tabla izquierda, los campos de esta
última aparecen conteniendo "null".
Veamos un ejemplo:
select titulo,nombre
from editoriales as e
full join libros as l
on codigoeditorial = e.codigo;

La salida del "full join" precedente muestra todos los registros de ambas tablas, incluyendo los
libros cuyo código de editorial no existe en la tabla "editoriales" y las editoriales de las cuales no
hay correspondencia en "libros".
Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".
Eliminamos ambas tablas, si existen y las creamos:

if object_id('libros') is not null


drop table libros;
if object_id('editoriales') is not null
drop table editoriales;

create table libros(


codigo int identity,
titulo varchar(40),
autor varchar(30) default 'Desconocido',
codigoeditorial tinyint not null,
precio decimal(5,2)
);
create table editoriales(
codigo tinyint identity,
nombre varchar(20),
primary key (codigo)
);

Ingresamos algunos registros en ambas tablas:


insert into editoriales values('Planeta');
insert into editoriales values('Emece');
insert into editoriales values('Siglo XXI');

insert into libros values('El aleph','Borges',1,20);


insert into libros values('Martin Fierro','Jose Hernandez',1,30);
insert into libros values('Aprenda PHP','Mario Molina',2,50);
insert into libros values('Java en 10 minutos',default,4,45);

Realizamos una combinación externa completa para obtener todos los registros de ambas tablas, incluyendo
los libros cuyo código de editorial no existe en la tabla "editoriales" y las editoriales de las cuales no hay
correspondencia en "libros":
select titulo,nombre
from editoriales as e
full join libros as l
on codigoeditorial = e.codigo;

Primer problema:

Un club dicta clases de distintos deportes. Almacena la información en una tabla


llamada "deportes"

215
en la cual incluye el nombre del deporte y el nombre del profesor y en otra tabla
llamada
"inscriptos" que incluye el documento del socio que se inscribe, el deporte y si la
matricula está
paga o no.
1- Elimine las tablas si existen y cree las tablas:
if (object_id('deportes')) is not null
drop table deportes;
if (object_id('inscriptos')) is not null
drop table inscriptos;
create table deportes(
codigo tinyint identity,
nombre varchar(30),
profesor varchar(30),
primary key (codigo)
);
create table inscriptos(
documento char(8),
codigodeporte tinyint not null,
matricula char(1) --'s'=paga 'n'=impaga
);

2- Ingrese algunos registros para ambas tablas:


insert into deportes values('tenis','Marcelo Roca');
insert into deportes values('natacion','Marta Torres');
insert into deportes values('basquet','Luis Garcia');
insert into deportes values('futbol','Marcelo Roca');

insert into inscriptos values('22222222',3,'s');


insert into inscriptos values('23333333',3,'s');
insert into inscriptos values('24444444',3,'n');
insert into inscriptos values('22222222',2,'s');
insert into inscriptos values('23333333',2,'s');
insert into inscriptos values('22222222',4,'n');
insert into inscriptos values('22222222',5,'n');

3- Muestre todos la información de la tabla "inscriptos", y consulte la tabla


"deportes" para
obtener el nombre de cada deporte (6 registros)

4- Empleando un "left join" con "deportes" obtenga todos los datos de los inscriptos (7
registros)

5- Obtenga la misma salida anterior empleando un "rigth join".

6- Muestre los deportes para los cuales no hay inscriptos, empleando un "left join" (1
registro)

7- Muestre los documentos de los inscriptos a deportes que no existen en la tabla


"deportes" (1
registro)

8- Emplee un "full join" para obtener todos los datos de ambas tablas, incluyendo las
inscripciones
a deportes inexistentes en "deportes" y los deportes que no tienen inscriptos (8
registros)

70 - Combinaciones cruzadas (cross join)


216
Vimos que hay tres tipos de combinaciones: 1) combinaciones internas (join), 2) combinaciones
externas (left, right y full join) y 3) combinaciones cruzadas.
Las combinaciones cruzadas (cross join) muestran todas las combinaciones de todos los registros
de las tablas combinadas. Para este tipo de join no se incluye una condición de enlace. Se genera
el producto cartesiano en el que el número de filas del resultado es igual al número de registros
de la primera tabla multiplicado por el número de registros de la segunda tabla, es decir, si hay 5
registros en una tabla y 6 en la otra, retorna 30 filas.
La sintaxis básica es ésta:
select CAMPOS
from TABLA1
cross join TABLA2;

Veamos un ejemplo. Un pequeño restaurante almacena los nombres y precios de sus comidas en
una tabla llamada "comidas" y en una tabla denominada "postres" los mismos datos de sus
postres.
Si necesitamos conocer todas las combinaciones posibles para un menú, cada comida con cada
postre, empleamos un "cross join":
select c.nombre as 'plato principal', p.nombre as 'postre'
from comidas as c
cross join postres as p;

La salida muestra cada plato combinado con cada uno de los postres.
Como cualquier tipo de "join", puede emplearse una cláusula "where" que condicione la salida.
Un pequeño restaurante tiene almacenados los nombres y precios de sus comidas en una tabla llamada
"comidas" y en una tabla denominada "postres" los mismos datos de sus postres.
Eliminamos las tablas, si existen:

if object_id('comidas') is not null


drop table comidas;
if object_id('postres') is not null
drop table postres;

Creamos las tablas:


create table comidas(
codigo tinyint identity,
nombre varchar(30),
precio decimal(4,2)
);

create table postres(


codigo tinyint identity,
nombre varchar(30),
precio decimal(4,2)
);

Ingresamos algunos registros:


insert into comidas values('ravioles',5);

217
insert into comidas values('tallarines',4);
insert into comidas values('milanesa',7);
insert into comidas values('cuarto de pollo',6);

insert into postres values('flan',2.5);


insert into postres values('porcion torta',3.5);

El restaurante quiere combinar los registros de ambas tablas para mostrar los distintos menúes que ofrece.
Lo hacemos usando un "cross join":
select c.nombre as 'plato principal',
p.nombre as 'postre',
c.precio+p.precio as 'total'
from comidas as c
cross join postres as p;

La salida muestra cada plato combinado con cada uno de los postres, agregamos una columna que calcula el
precio total de cada menú. Se obtienen 8 registros.
Primer problema:

Una agencia matrimonial almacena la información de sus clientes de sexo femenino en una
tabla
llamada "mujeres" y en otra la de sus clientes de sexo masculino llamada "varones".
1- Elimine las tablas si existen y créelas:
if object_id('mujeres') is not null
drop table mujeres;
if object_id('varones') is not null
drop table varones;
create table mujeres(
nombre varchar(30),
domicilio varchar(30),
edad int
);
create table varones(
nombre varchar(30),
domicilio varchar(30),
edad int
);

2- Ingrese los siguientes registros:


insert into mujeres values('Maria Lopez','Colon 123',45);
insert into mujeres values('Liliana Garcia','Sucre 456',35);
insert into mujeres values('Susana Lopez','Avellaneda 98',41);

insert into varones values('Juan Torres','Sarmiento 755',44);


insert into varones values('Marcelo Oliva','San Martin 874',56);
insert into varones values('Federico Pereyra','Colon 234',38);
insert into varones values('Juan Garcia','Peru 333',50);

3- La agencia necesita la combinación de todas las personas de sexo femenino con las de
sexo
masculino. Use un "cross join" (12 registros)

4- Realice la misma combinación pero considerando solamente las personas mayores de 40


años (6
registros)

5- Forme las parejas pero teniendo en cuenta que no tengan una diferencia superior a 10
años (8
registros)

218
Segundo problema:

Una empresa de seguridad almacena los datos de sus guardias de seguridad en una tabla
llamada
"guardias". también almacena los distintos sitios que solicitaron sus servicios en una
tabla llamada
"tareas".
1- Elimine las tablas "guardias" y "tareas" si existen:
if object_id('guardias') is not null
drop table guardias;
if object_id('tareas') is not null
drop table tareas;

2- Cree las tablas:


create table guardias(
documento char(8),
nombre varchar(30),
sexo char(1), /* 'f' o 'm' */
domicilio varchar(30),
primary key (documento)
);

create table tareas(


codigo tinyint identity,
domicilio varchar(30),
descripcion varchar(30),
horario char(2), /* 'AM' o 'PM'*/
primary key (codigo)
);

3- Ingrese los siguientes registros:


insert into guardias values('22333444','Juan Perez','m','Colon 123');
insert into guardias values('24333444','Alberto Torres','m','San Martin 567');
insert into guardias values('25333444','Luis Ferreyra','m','Chacabuco 235');
insert into guardias values('23333444','Lorena Viale','f','Sarmiento 988');
insert into guardias values('26333444','Irma Gonzalez','f','Mariano Moreno 111');

insert into tareas values('Colon 1111','vigilancia exterior','AM');


insert into tareas values('Urquiza 234','vigilancia exterior','PM');
insert into tareas values('Peru 345','vigilancia interior','AM');
insert into tareas values('Avellaneda 890','vigilancia interior','PM');

4- La empresa quiere que todos sus empleados realicen todas las tareas. Realice una
"cross join" (20
registros)

5- En este caso, la empresa quiere que todos los guardias de sexo femenino realicen las
tareas de
"vigilancia interior" y los de sexo masculino de "vigilancia exterior". Realice una
"cross join" con
un "where" que controle tal requisito (10 registros)

71 - Autocombinación
Dijimos que es posible combinar una tabla consigo misma.

219
Un pequeño restaurante tiene almacenadas sus comidas en una tabla llamada "comidas" que
consta de los siguientes campos:
- nombre varchar(20),
- precio decimal (4,2) y
- rubro char(6)-- que indica con 'plato' si es un plato principal y 'postre' si es postre.

Podemos obtener la combinación de 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 autocombinación se combina una tabla con una copia de si misma. Para ello debemos
utilizar 2 alias para la tabla. Para evitar que aparezcan filas duplicadas, debemos emplear un
"where".
También se puede realizar una autocombinación con "join":
select c1.nombre as 'plato principal',
c2.nombre as postre,
c1.precio+c2.precio as total
from comidas 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".


Un pequeño restaurante tiene almacenados los nombres, precios y rubro de sus comidas en una tabla llamada
"comidas".

Eliminamos la tabla, si existe:


if object_id('comidas') is not null
drop table comidas;

Creamos la tabla:
create table comidas(
codigo int identity,
nombre varchar(30),
precio decimal(4,2),
rubro char(6),-- 'plato'=plato principal', 'postre'=postre
primary key(codigo)

220
);

Ingresamos algunos registros:


insert into comidas values('ravioles',5,'plato');
insert into comidas values('tallarines',4,'plato');
insert into comidas values('milanesa',7,'plato');
insert into comidas values('cuarto de pollo',6,'plato');
insert into comidas values('flan',2.5,'postre');
insert into comidas values('porcion torta',3.5,'postre');

Realizamos un "cross join":


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;

Note que aparecen filas duplicadas, por ejemplo, "ravioles" se combina con "ravioles" y la combinación
"ravioles- flan" se repite como "flan- ravioles". Debemos especificar que combine el rubro "plato" con
"postre":
select c1.nombre as 'plato principal',
c2.nombre as postre,
c1.precio+c2.precio as total
from comidas as c1
cross join comidas as c2
where c1.rubro='plato' and
c2.rubro='postre';

La salida muestra cada plato combinado con cada postre, y una columna extra que calcula el total del menú.
También se puede realizar una autocombinación con "join":
select c1.nombre as 'plato principal',
c2.nombre as postre,
c1.precio+c2.precio 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".


Primer problema:

Una agencia matrimonial almacena la información de sus clientes en una tabla llamada
"clientes".
1- Elimine la tabla si existe y créela:
if object_id('clientes') is not null
drop table clientes;

create table clientes(


nombre varchar(30),
sexo char(1),--'f'=femenino, 'm'=masculino
edad int,
domicilio varchar(30)
);

221
2- Ingrese los siguientes registros:
insert into clientes values('Maria Lopez','f',45,'Colon 123');
insert into clientes values('Liliana Garcia','f',35,'Sucre 456');
insert into clientes values('Susana Lopez','f',41,'Avellaneda 98');
insert into clientes values('Juan Torres','m',44,'Sarmiento 755');
insert into clientes values('Marcelo Oliva','m',56,'San Martin 874');
insert into clientes values('Federico Pereyra','m',38,'Colon 234');
insert into clientes values('Juan Garcia','m',50,'Peru 333');

3- La agencia necesita la combinación de todas las personas de sexo femenino con las de
sexo
masculino. Use un "cross join" (12 registros)

4- Obtenga la misma salida enterior pero realizando un "join".

5- Realice la misma autocombinación que el punto 3 pero agregue la condición que las
parejas no
tengan una diferencia superior a 5 años (5 registros)

Segundo problema:

Varios clubes de barrio se organizaron para realizar campeonatos entre ellos. La tabla
llamada
"equipos" guarda la informacion de los distintos equipos que jugarán.
1- Elimine la tabla, si existe y créela nuevamente:
if object_id('equipos') is not null
drop table equipos;

create table equipos(


nombre varchar(30),
barrio varchar(20),
domicilio varchar(30),
entrenador varchar(30)
);

2- Ingrese los siguientes registros:


insert into equipos values('Los tigres','Gral. Paz','Sarmiento 234','Juan Lopez');
insert into equipos values('Los leones','Centro','Colon 123','Gustavo Fuentes');
insert into equipos values('Campeones','Pueyrredon','Guemes 346','Carlos Moreno');
insert into equipos values('Cebollitas','Alberdi','Colon 1234','Luis Duarte');

4- Cada equipo jugará con todos los demás 2 veces, una vez en cada sede. Realice un
"cross join"
para combinar los equipos teniendo en cuenta que un equipo no juega consigo mismo (12
registros)

5- Obtenga el mismo resultado empleando un "join".

6- Realice un "cross join" para combinar los equipos para que cada equipo juegue con
cada uno de los
otros una sola vez (6 registros)

72 - Combinaciones y funciones de
agrupamiento

222
Podemos usar "group by" y las funciones de agrupamiento con combinaciones de tablas.
Para ver la cantidad de libros de cada editorial consultando la tabla "libros" y "editoriales",
tipeamos:
select nombre as editorial,
count(*) as cantidad
from editoriales as e
join libros as l
on codigoeditorial=e.codigo
group by e.nombre;

Note que las editoriales que no tienen libros no aparecen en la salida porque empleamos un
"join".
Empleamos otra función de agrupamiento con "left join". Para conocer el mayor precio de los
libros de cada editorial usamos la función "max()", hacemos un "left join" y agrupamos por nombre
de la editorial:
select nombre as editorial,
max(precio) as 'mayor precio'
from editoriales as e
left join libros as l
on codigoeditorial=e.codigo
group by nombre;

En la sentencia anterior, mostrará, para la editorial de la cual no haya libros, el valor "null" en la
columna calculada.
Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y "editoriales".
Eliminamos ambas tablas, si existen y las creamos:

if object_id('libros') is not null


drop table libros;
if object_id('editoriales') is not null
drop table editoriales;

create table libros(


codigo int identity,
titulo varchar(40),
autor varchar(30),
codigoeditorial tinyint not null,
precio decimal(5,2)
);
create table editoriales(
codigo tinyint identity,
nombre varchar(20),
primary key (codigo)
);

Ingresamos algunos registros en ambas tablas:


insert into editoriales values('Planeta');
insert into editoriales values('Emece');
insert into editoriales values('Siglo XXI');

insert into libros values('El aleph','Borges',1,20);


insert into libros values('Martin Fierro','Jose Hernandez',1,30);
insert into libros values('Aprenda PHP','Mario Molina',3,50);
insert into libros values('Uno','Richard Bach',3,15);

223
insert into libros values('Java en 10 minutos',default,4,45);

Contamos la cantidad de libros de cada editorial consultando ambas tablas:


select nombre as editorial,
count(*) as cantidad
from editoriales as e
join libros as l
on codigoeditorial=e.codigo
group by e.nombre;

Buscamos el libro más costoso de cada editorial con un "left join":


select nombre as editorial,
max(precio) as 'mayor precio'
from editoriales as e
left join libros as l
on codigoeditorial=e.codigo
group by nombre;

Primer problema:

Un comercio que tiene un stand en una feria registra en una tabla llamada "visitantes"
algunos datos
de las personas que visitan o compran en su stand para luego enviarle publicidad de sus
productos y
en otra tabla llamada "ciudades" los nombres de las ciudades.
1- Elimine las tablas si existen:
if object_id('visitantes') is not null
drop table visitantes;
if object_id('ciudades') is not null
drop table ciudades;

2- Cree las tablas:


create table visitantes(
nombre varchar(30),
edad tinyint,
sexo char(1) default 'f',
domicilio varchar(30),
codigociudad tinyint not null,
mail varchar(30),
montocompra decimal (6,2)
);

create table ciudades(


codigo tinyint identity,
nombre varchar(20)
);

3- Ingrese algunos registros:


insert into ciudades values('Cordoba');
insert into ciudades values('Carlos Paz');
insert into ciudades values('La Falda');
insert into ciudades values('Cruz del Eje');

insert into visitantes values


('Susana Molina', 35,'f','Colon 123', 1, null,59.80);
insert into visitantes values
('Marcos Torres', 29,'m','Sucre 56', 1, 'marcostorres@hotmail.com',150.50);
insert into visitantes values
('Mariana Juarez', 45,'f','San Martin 111',2,null,23.90);
insert into visitantes values
('Fabian Perez',36,'m','Avellaneda 213',3,'fabianperez@xaxamail.com',0);

224
insert into visitantes values
('Alejandra Garcia',28,'f',null,2,null,280.50);
insert into visitantes values
('Gaston Perez',29,'m',null,5,'gastonperez1@gmail.com',95.40);
insert into visitantes values
('Mariana Juarez',33,'f',null,2,null,90);

4- Cuente la cantidad de visitas por ciudad mostrando el nombre de la ciudad (3 filas)

5- Muestre el promedio de gastos de las visitas agrupados por ciudad y sexo (4 filas)

6- Muestre la cantidad de visitantes con mail, agrupados por ciudad (3 filas)

7- Obtenga el monto de compra más alto de cada ciudad (3 filas)

73 - Combinación de más de dos tablas


Podemos hacer un "join" con más de dos tablas.
Cada join combina 2 tablas. Se pueden emplear varios join para enlazar varias tablas. Cada
resultado de un join es una tabla que puede combinarse con otro join.
La librería almacena los datos de sus libros en tres tablas: libros, editoriales y autores.
En la tabla "libros" un campo "codigoautor" hace referencia al autor y un campo "codigoeditorial"
referencia la editorial.
Para recuperar todos los datos de los libros empleamos la siguiente consulta:
select titulo,a.nombre,e.nombre
from autores 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
combinarán; luego debemos hacer coincidir los valores para el enlace con la tabla "editoriales"
enlazándolas por los campos correspondientes. Utilizamos alias para una sentencia más sencilla
y comprensible.
Note que especificamos a qué tabla pertenecen los campos cuyo nombre se repiten en las tablas,
esto es necesario para evitar confusiones y ambiguedades al momento de referenciar un campo.
Note que no aparecen los libros cuyo código de autor no se encuentra en "autores" y cuya
editorial no existe en "editoriales", esto es porque realizamos una combinación 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;

225
En la consulta anterior solicitamos el título, autor y editorial de todos los libros que encuentren o
no coincidencia con "autores" ("right join") y a ese resultado lo combinamos con "editoriales",
encuentren o no coincidencia.
Es posible realizar varias combinaciones para obtener información de varias tablas. Las tablas
deben tener claves externas relacionadas con las tablas a combinar.
En consultas en las cuales empleamos varios "join" es importante tener en cuenta el orden de las
tablas y los tipos de "join"; 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".
Una librería almacena la información de sus libros para la venta en tres tablas, "libros", "autores" y
"editoriales".
Eliminamos las tablas, si existen y las creamos:

if object_id('libros') is not null


drop table libros;
if object_id('autores') is not null
drop table autores;
if object_id('editoriales') is not null
drop table editoriales;

create table libros(


codigo int identity,
titulo varchar(40),
codigoautor int not null,
codigoeditorial tinyint not null,
precio decimal(5,2),
primary key(codigo)
);

create table autores(


codigo int identity,
nombre varchar(20),
primary key (codigo)
);

create table editoriales(


codigo tinyint identity,
nombre varchar(20),
primary key (codigo)
);

Ingresamos algunos registros:


insert into editoriales values('Planeta');
insert into editoriales values('Emece');
insert into editoriales values('Siglo XXI');
insert into editoriales values('Plaza');

insert into autores values ('Richard Bach');


insert into autores values ('Borges');
insert into autores values ('Jose Hernandez');
insert into autores values ('Mario Molina');
insert into autores values ('Paenza');

insert into libros values('El aleph',2,2,20);


insert into libros values('Martin Fierro',3,1,30);
226
insert into libros values('Aprenda PHP',4,3,50);
insert into libros values('Uno',1,1,15);
insert into libros values('Java en 10 minutos',0,3,45);
insert into libros values('Matematica estas ahi',0,0,15);
insert into libros values('Java de la A a la Z',4,0,50);

Recuperamos todos los datos de los libros consultando las tres tablas:
select titulo,a.nombre,e.nombre,precio
from autores as a
join libros as l
on codigoautor=a.codigo
join editoriales as e
on codigoeditorial=e.codigo;

Los libros cuyo código de autor no se encuentra en "autores" (caso de "Java en 10 minutos" y "Matematica
estas ahi") y cuya editorial no existe en "editoriales" (caso de "Matematica estas ahi" y "Java de la A a la
Z"), no aparecen porque realizamos una combinación interna.
Podemos combinar varios tipos de join en una misma sentencia:
select titulo,a.nombre,e.nombre,precio
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 título, autor y editorial de todos los libros que encuentren o no
coincidencia con "autores" ("right join") y a ese resultado lo combinamos con "editoriales", encuentren o no
coincidencia.
Primer problema:

Un club dicta clases de distintos deportes. En una tabla llamada "socios" guarda los
datos de los
socios, en una tabla llamada "deportes" la información referente a los diferentes
deportes que se
dictan y en una tabla denominada "inscriptos", las inscripciones de los socios a los
distintos
deportes.
Un socio puede inscribirse en varios deportes el mismo año. Un socio no puede
inscribirse en el
mismo deporte el mismo año. Distintos socios se inscriben en un mismo deporte en el
mismo año.
1- Elimine las tablas si existen:
if object_id('socios') is not null
drop table socios;
if object_id('deportes') is not null
drop table deportes;
if object_id('inscriptos') is not null
drop table inscriptos;

2- Cree las tablas con las siguientes estructuras:


create table socios(
documento char(8) not null,
nombre varchar(30),
domicilio varchar(30),
primary key(documento)
);
create table deportes(

227
codigo tinyint identity,
nombre varchar(20),
profesor varchar(15),
primary key(codigo)
);
create table inscriptos(
documento char(8) not null,
codigodeporte tinyint not null,
anio char(4),
matricula char(1),--'s'=paga, 'n'=impaga
primary key(documento,codigodeporte,anio)
);

3- Ingrese algunos registros en "socios":


insert into socios values('22222222','Ana Acosta','Avellaneda 111');
insert into socios values('23333333','Betina Bustos','Bulnes 222');
insert into socios values('24444444','Carlos Castro','Caseros 333');
insert into socios values('25555555','Daniel Duarte','Dinamarca 44');
4- Ingrese algunos registros en "deportes":
insert into deportes values('basquet','Juan Juarez');
insert into deportes values('futbol','Pedro Perez');
insert into deportes values('natacion','Marina Morales');
insert into deportes values('tenis','Marina Morales');

5- Inscriba a varios socios en el mismo deporte en el mismo año:


insert into inscriptos values ('22222222',3,'2006','s');
insert into inscriptos values ('23333333',3,'2006','s');
insert into inscriptos values ('24444444',3,'2006','n');

6- Inscriba a un mismo socio en el mismo deporte en distintos años:


insert into inscriptos values ('22222222',3,'2005','s');
insert into inscriptos values ('22222222',3,'2007','n');

7- Inscriba a un mismo socio en distintos deportes el mismo año:


insert into inscriptos values ('24444444',1,'2006','s');
insert into inscriptos values ('24444444',2,'2006','s');

8- Ingrese una inscripción con un código de deporte inexistente y un documento de socio


que no
exista en "socios":
insert into inscriptos values ('26666666',0,'2006','s');

9- Muestre el nombre del socio, el nombre del deporte en que se inscribió y el año
empleando
diferentes tipos de join.

10- Muestre todos los datos de las inscripciones (excepto los códigos) incluyendo
aquellas
inscripciones cuyo código de deporte no existe en "deportes" y cuyo documento de socio
no se
encuentra en "socios".

11- Muestre todas las inscripciones del socio con documento "22222222".

74 - Combinaciones con update y delete

228
Las combinaciones no sólo se utilizan con la sentencia "select", también podemos emplearlas
con "update" y "delete".
Podemos emplear "update" o "delete" con "join" para actualizar o eliminar registros de una tabla
consultando otras tablas.
En el siguiente ejemplo aumentamos en un 10% los precios de los libros de cierta editorial,
necesitamos un "join" para localizar los registros de la editorial "Planeta" en la tabla "libros":
update libros set precio=precio+(precio*0.1)
from libros
join editoriales as e
on codigoeditorial=e.codigo
where nombre='Planeta';

Eliminamos todos los libros de editorial "Emece":


delete libros
from libros
join editoriales
on codigoeditorial = editoriales.codigo
where editoriales.nombre='Emece';

Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y
"editoriales".
Eliminamos ambas tablas, si existen:

if object_id('libros') is not null


drop table libros;
if object_id('editoriales') is not null
drop table editoriales;

Creamos las tablas:


create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30) default 'Desconocido',
codigoeditorial tinyint not null,
precio decimal(5,2)
);
create table editoriales(
codigo tinyint identity,
nombre varchar(20),
primary key (codigo)
);

Ingresamos algunos registros en ambas tablas:


insert into editoriales values('Planeta');
insert into editoriales values('Emece');
insert into editoriales values('Siglo XXI');

insert into libros values('El aleph','Borges',2,20);


insert into libros values('Martin Fierro','Jose Hernandez',1,30);
insert into libros values('Aprenda PHP','Mario Molina',3,50);
insert into libros values('Java en 10 minutos',default,3,45);

Aumentamos en un 10% los precios de los libros de editorial "Planeta":

229
update libros set precio=precio+(precio*0.1)
from libros
join editoriales as e
on codigoeditorial=e.codigo
where nombre='Planeta';

Veamos el resultado:
select titulo,autor,e.nombre,precio
from libros as l
join editoriales as e
on codigoeditorial=e.codigo;

Eliminamos todos los libros de editorial "Emece":


delete libros
from libros
join editoriales
on codigoeditorial = editoriales.codigo
where editoriales.nombre='Emece';

Veamos si se eliminaron:
select titulo,autor,e.nombre,precio
from libros as l
join editoriales as e
on codigoeditorial=e.codigo;

75 - Clave foránea
Un campo que no es clave primaria en una tabla y sirve para enlazar sus valores con otra tabla
en la cual es clave primaria se denomina clave foránea, externa o ajena.
En el ejemplo de la librería en que utilizamos las tablas "libros" y "editoriales" con estos campos:
libros: codigo (clave primaria), titulo, autor, codigoeditorial, precio y
editoriales: codigo (clave primaria), nombre.

el campo "codigoeditorial" de "libros" es una clave foránea, se emplea para enlazar la tabla
"libros" con "editoriales" y es clave primaria en "editoriales" con el nombre "codigo".
Las claves foráneas y las claves primarias deben ser del mismo tipo para poder enlazarse. Si
modificamos una, debemos modificar la otra para que los valores se correspondan.
Cuando alteramos una tabla, debemos tener cuidado con las claves foráneas. Si modificamos el
tipo, longitud o atributos de una clave foránea, ésta puede quedar inhabilitada para hacer los
enlaces.
Entonces, una clave foránea es un campo (o varios) empleados para enlazar datos de 2 tablas,
para establecer un "join" con otra tabla en la cual es clave primaria.

230
76 - Restricciones (foreign key)
Hemos visto que una de las alternativas que SQL Server ofrece para asegurar la integridad de
datos es el uso de restricciones (constraints). Aprendimos que las restricciones se establecen en
tablas y campos asegurando que los datos sean válidos y que las relaciones entre las tablas se
mantengan; vimos que existen distintos tipos de restricciones:
1) de los campos: default y check
2) de la tabla: primary key y unique.
3) referencial: foreign key, la analizaremos ahora.

Con la restricción "foreign key" se define un campo (o varios) cuyos valores coinciden con la clave
primaria de la misma tabla o de otra, es decir, se define una referencia a un campo con una
restricción "primary key" o "unique" de la misma tabla o de otra.
La integridad referencial asegura que se mantengan las referencias entre las claves primarias y
las externas. Por ejemplo, controla que si se agrega un código de editorial en la tabla "libros", tal
código exista en la tabla "editoriales".
También controla que no pueda eliminarse un registro de una tabla ni modificar la clave primaria si
una clave externa hace referencia al registro. Por ejemplo, que no se pueda eliminar o modificar
un código de "editoriales" si existen libros con dicho código.
La siguiente es la sintaxis parcial general para agregar una restricción "foreign key":
alter table NOMBRETABLA1
add constraint NOMBRERESTRICCION
foreign key (CAMPOCLAVEFORANEA)
references NOMBRETABLA2 (CAMPOCLAVEPRIMARIA);

Analicémosla:
- NOMBRETABLA1 referencia el nombre de la tabla a la cual le aplicamos la restricción,
- NOMBRERESTRICCION es el nombre que le damos a la misma,
- luego de "foreign key", entre paréntesis se coloca el campo de la tabla a la que le aplicamos la
restricción que será establecida como clave foránea,
- luego de "references" indicamos el nombre de la tabla referenciada y el campo que es clave
primaria en la misma, a la cual hace referencia la clave foránea. La tabla referenciada debe tener
definida una restricción "primary key" o "unique"; si no la tiene, aparece un mensaje de error.
Para agregar una restricción "foreign key" al campo "codigoeditorial" de "libros", tipeamos:
alter table libros
add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo);

En el ejemplo implementamos una restricción "foreign key" para asegurarnos que el código de la
editorial de la de la tabla "libros" ("codigoeditorial") esté asociada con un código válido en la tabla
"editoriales" ("codigo").
231
Cuando agregamos cualquier restricción a una tabla que contiene información, SQL Server
controla los datos existentes para confirmar que cumplen con la restricción, si no los cumple, la
restricción no se aplica y aparece un mensaje de error. Por ejemplo, si intentamos agregar una
restricción "foreign key" a la tabla "libros" y existe un libro con un valor de código para editorial
que no existe en la tabla "editoriales", la restricción no se agrega.
Actúa en inserciones. Si intentamos ingresar un registro (un libro) con un valor de clave foránea
(codigoeditorial) que no existe en la tabla referenciada (editoriales), SQL server muestra un
mensaje de error. Si al ingresar un registro (un libro), no colocamos el valor para el campo clave
foránea (codigoeditorial), almacenará "null", porque esta restricción permite valores nulos (a
menos que se haya especificado lo contrario al definir el campo).
Actúa en eliminaciones y actualizaciones. Si intentamos eliminar un registro o modificar un valor
de clave primaria de una tabla si una clave foránea hace referencia a dicho registro, SQL Server
no lo permite (excepto si se permite la acción en cascada, tema que veremos posteriormente).
Por ejemplo, si intentamos eliminar una editorial a la que se hace referencia en "libros", aparece
un mensaje de error.
Esta restricción (a diferencia de "primary key" y "unique") no crea índice 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 cláusula "references".
Esta restricción se puede definir dentro de la misma tabla (lo veremos más adelante) o entre
distintas tablas.
Una tabla puede tener varias restricciones "foreign key".
No se puede eliminar una tabla referenciada en una restricción "foreign key", aparece un mensaje
de error.
Una restriccion "foreign key" no puede modificarse, debe eliminarse y volverse a crear.
Para ver información acerca de esta restricción podemos ejecutar el procedimiento almacenado
"sp_helpconstraint" junto al nombre de la tabla. Nos muestra el tipo, nombre, la opción para
eliminaciones y actualizaciones, el estado (temas que veremos más adelante), el nombre del
campo y la tabla y campo que referencia.
También informa si la tabla es referenciada por una clave foránea.
Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y
"editoriales".
Eliminamos ambas tablas, si existen:

if object_id('libros') is not null


drop table libros;
if object_id('editoriales') is not null
drop table editoriales;

Creamos las tablas:


create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),

232
codigoeditorial tinyint
);
create table editoriales(
codigo tinyint,
nombre varchar(20),
primary key (codigo)
);

Ingresamos algunos registros en ambas tablas:


insert into editoriales values(1,'Emece');
insert into editoriales values(2,'Planeta');
insert into editoriales values(3,'Siglo XXI');

insert into libros values('El aleph','Borges',1);


insert into libros values('Martin Fierro','Jose Hernandez',2);
insert into libros values('Aprenda PHP','Mario Molina',2);

Agregamos una restricción "foreign key" a la tabla "libros":


alter table libros
add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo);

Ingresamos un libro sin especificar valores:


insert into libros default values;

Note que en "codigoeditorial" almacenó "null", porque esta restricción permite valores nulos (a menos que
se haya especificado lo contrario al definir el campo).
Veamos las restricciones de "libros":
sp_helpconstraint libros;

aparece la restricción "foreign key".


Veamos las restricciones de "editoriales":
sp_helpconstraint editoriales;

aparece la restricción "primary key" y nos informa que la tabla es rerenciada por una "foreign key" de la
tabla "libros" llamada "FK_libros_codigoeditorial".
Primer problema:

Una empresa tiene registrados sus clientes en una tabla llamada "clientes", también
tiene una tabla
"provincias" donde registra los nombres de las provincias.
1- Elimine las tablas "clientes" y "provincias", si existen y créelas:
if object_id('clientes') is not null
drop table clientes;
if object_id('provincias') is not null
drop table provincias;

create table clientes (


codigo int identity,
nombre varchar(30),
domicilio varchar(30),
ciudad varchar(20),

233
codigoprovincia tinyint
);

create table provincias(


codigo tinyint not null,
nombre varchar(20)
);

En este ejemplo, el campo "codigoprovincia" de "clientes" es una clave foránea, se


emplea para
enlazar la tabla "clientes" con "provincias".

2- Intente agregar una restricción "foreign key" a la tabla "clientes" que haga
referencia al campo
"codigo" de "provincias":
alter table clientes
add constraint FK_clientes_codigoprovincia
foreign key (codigoprovincia)
references provincias(codigo);
No se puede porque "provincias" no tiene restricción "primary key" ni "unique".

3- Establezca una restricción "primary key" al campo "codigo" de "provincias":


alter table provincias
add constraint PK_provincias_codigo
primary key (codigo);

4- Ingrese algunos registros para ambas tablas:


insert into provincias values(1,'Cordoba');
insert into provincias values(2,'Santa Fe');
insert into provincias values(3,'Misiones');
insert into provincias values(4,'Rio Negro');

insert into clientes values('Perez Juan','San Martin 123','Carlos Paz',1);


insert into clientes values('Moreno Marcos','Colon 234','Rosario',2);
insert into clientes values('Acosta Ana','Avellaneda 333','Posadas',3);
insert into clientes values('Luisa Lopez','Juarez 555','La Plata',6);

5- Intente agregar la restricción "foreign key" del punto 2 a la tabla "clientes":


alter table clientes
add constraint FK_clientes_codigoprovincia
foreign key (codigoprovincia)
references provincias(codigo);
No se puede porque hay un registro en "clientes" cuyo valor de "codigoprovincia" no
existe en
"provincias".
6- Elimine el registro de "clientes" que no cumple con la restricción y establezca la
restricción
nuevamente:
delete from clientes where codigoprovincia=6;
alter table clientes
add constraint FK_clientes_codigoprovincia
foreign key (codigoprovincia)
references provincias(codigo);

7- Intente agregar un cliente con un código de provincia inexistente en "provincias".


No se puede.

8- Intente eliminar el registro con código 3, de "provincias".


No se puede porque hay registros en "clientes" al cual hace referencia.

9- Elimine el registro con código "4" de "provincias".


Se permite porque en "clientes" ningún registro hace referencia a él.

10- Intente modificar el registro con código 1, de "provincias".

234
No se puede porque hay registros en "clientes" al cual hace referencia.

11- Vea las restricciones de "clientes".


aparece la restricción "foreign key".

12- Vea las restricciones de "provincias".


aparece la restricción "primary key" y nos informa que la tabla es rerenciada por una
"foreign key"
de la tabla "clientes" llamada "FK_clientes_codigoprovincia".

77 - Restricciones foreign key en la misma tabla


La restricción "foreign key", que define una referencia a un campo con una restricción "primary
key" o "unique" se puede definir entre distintas tablas (como hemos aprendido) o dentro de la
misma tabla.
Veamos un ejemplo en el cual definimos esta restricción dentro de la misma tabla.
Una mutual almacena los datos de sus afiliados en una tabla llamada "afiliados". Algunos afiliados
inscriben a sus familiares. La tabla contiene un campo que hace referencia al afiliado que lo
incorporó a la mutual, del cual dependen.
La estructura de la tabla es la siguiente:
create table afiliados(
numero int identity not null,
documento char(8) not null,
nombre varchar(30),
afiliadotitular int,
primary key (documento),
unique (numero)
);

En caso que un afiliado no haya sido incorporado a la mutual por otro afiliado, el campo
"afiliadotitular" almacenará "null".
Establecemos una restricción "foreign key" para asegurarnos que el número de afiliado que se
ingrese en el campo "afiliadotitular" exista en la tabla "afiliados":
alter table afiliados
add constraint FK_afiliados_afiliadotitular
foreign key (afiliadotitular)
references afiliados (numero);

La sintaxis es la misma, excepto que la tabla se autoreferencia.


Luego de aplicar esta restricción, cada vez que se ingrese un valor en el campo "afiliadotitular",
SQL Server controlará que dicho número exista en la tabla, si no existe, mostrará un mensaje de
error.
Si intentamos eliminar un afiliado que es titular de otros afiliados, no se podrá hacer, a menos que
se haya especificado la acción en cascada (próximo tema).

235
Una mutual almacena los datos de sus afiliados en una tabla llamada "afiliados". Algunos afiliados
inscriben a sus familiares. La tabla contiene un campo que hace referencia al afiliado que lo
incorporó a la mutual, del cual dependen.
Eliminamos la tabla "afiliados" si existe y la creamos:

if object_id('afiliados') is not null


drop table afiliados;

create table afiliados(


numero int identity not null,
documento char(8) not null,
nombre varchar(30),
afiliadotitular int,
primary key (documento),
unique (numero)
);

En caso que un afiliado no haya sido incorporado a la mutual por otro afiliado, el campo "afiliadotitular"
almacenará "null".
Establecemos una restricción "foreign key" para asegurarnos que el número de afiliado que se ingrese en el
campo "afiliadotitular" exista en la tabla "afiliados":
alter table afiliados
add constraint FK_afiliados_afiliadotitular
foreign key (afiliadotitular)
references afiliados (numero);

Ingresamos algunos registros:


insert into afiliados values('22222222','Perez Juan',null);
insert into afiliados values('23333333','Garcia Maria',null);
insert into afiliados values('24444444','Lopez Susana',null);
insert into afiliados values('30000000','Perez Marcela',1);
insert into afiliados values('31111111','Morales Luis',1);
insert into afiliados values('32222222','Garcia Maria',2);

Podemos eliminar un afiliado, siempre que no haya otro afiliado que haga referencia a él en "afiliadotitular",
es decir, si el "numero" del afiliado está presente en algún registro en el campo "afiliadotitular":
delete from afiliados where numero=5;

Veamos la información referente a "afiliados":


sp_helpconstraint afiliados;

Primer problema:

Una empresa registra los datos de sus clientes en una tabla llamada "clientes". Dicha
tabla contiene
un campo que hace referencia al cliente que lo recomendó denominado "referenciadopor".
Si un cliente
no ha sido referenciado por ningún otro cliente, tal campo almacena "null".
1- Elimine la tabla si existe y créela:
if object_id('clientes') is not null
drop table clientes;
create table clientes(
codigo int not null,
nombre varchar(30),
domicilio varchar(30),
236
ciudad varchar(20),
referenciadopor int,
primary key(codigo)
);

2- Ingresamos algunos registros:


insert into clientes values (50,'Juan Perez','Sucre 123','Cordoba',null);
insert into clientes values(90,'Marta Juarez','Colon 345','Carlos Paz',null);
insert into clientes values(110,'Fabian Torres','San Martin 987','Cordoba',50);
insert into clientes values(125,'Susana Garcia','Colon 122','Carlos Paz',90);
insert into clientes values(140,'Ana Herrero','Colon 890','Carlos Paz',9);

3- Intente agregar una restricción "foreign key" para evitar que en el campo
"referenciadopor" se
ingrese un valor de código de cliente que no exista.
No se permite porque existe un registro que no cumple con la restricción que se intenta
establecer.

4- Cambie el valor inválido de "referenciadopor" del registro que viola la restricción


por uno
válido.

5- Agregue la restricción "foreign key" que intentó agregar en el punto 3.

6- Vea la información referente a las restricciones de la tabla "clientes".

7- Intente agregar un registro que infrinja la restricción.


No lo permite.

8- Intente modificar el código de un cliente que está referenciado en


"referenciadopor".
No se puede.

9- Intente eliminar un cliente que sea referenciado por otro en "referenciadopor".


No se puede.

10- Cambie el valor de código de un cliente que no referenció a nadie.

11- Elimine un cliente que no haya referenciado a otros.

78 - Restricciones foreign key (acciones)


Continuamos con la restricción "foreign key".
Si intentamos eliminar un registro de la tabla referenciada por una restricción "foreign key" cuyo
valor de clave primaria existe referenciada en la tabla que tiene dicha restricción, la acción no se
ejecuta y aparece un mensaje de error. Esto sucede porque, por defecto, para eliminaciones, la
opción de la restricción "foreign key" es "no action". Lo mismo sucede si intentamos actualizar un
valor de clave primaria de una tabla referenciada por una "foreign key" existente en la tabla
principal.
La restricción "foreign key" tiene las cláusulas "on delete" y "on update" que son opcionales.
Estas cláusulas especifican cómo debe actuar SQL Server frente a eliminaciones y

237
modificaciones de las tablas referenciadas en la restricción.
Las opciones para estas cláusulas son las siguientes:
- "no action": indica que si intentamos eliminar o actualizar un valor de la clave primaria de la tabla
referenciada (TABLA2) que tengan referencia en la tabla principal (TABLA1), se genere un error y
la acción no se realice; es la opción predeterminada.
- "cascade": indica que si eliminamos o actualizamos un valor de la clave primaria en la tabla
referenciada (TABLA2), los registros coincidentes en la tabla principal (TABLA1), también se
eliminen o modifiquen; es decir, si eliminamos o modificamos un valor de campo definido con una
restricción "primary key" o "unique", dicho cambio se extiende al valor de clave externa de la otra
tabla (integridad referencial en cascada).
La sintaxis completa para agregar esta restricción a una tabla es la siguiente:
alter table TABLA1
add constraint NOMBRERESTRICCION
foreign key (CAMPOCLAVEFORANEA)
references TABLA2(CAMPOCLAVEPRIMARIA)
on delete OPCION
on update OPCION;

Sintetizando, si al agregar una restricción foreign key:


- no se especifica acción 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 acción no se realiza.
- se especifica "cascade" para eliminaciones ("on delete cascade") y elimina un registro de la
tabla referenciada (editoriales) cuyo valor de clave primaria (codigo) existe en la tabla
principal(libros), la eliminación de la tabla referenciada (editoriales) se realiza y se eliminan de la
tabla principal (libros) todos los registros cuyo valor coincide con el registro eliminado de la tabla
referenciada (editoriales).
- no se especifica acción 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 foránea (codigoeditorial) de la tabla principal (libros), la acción 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 restricción "foreign key" a la tabla "libros" estableciendo el
campo "codigoeditorial" como clave foránea que referencia al campo "codigo" de la tabla
"editoriales". La tabla "editoriales" tiene como clave primaria el campo "codigo". Especificamos la
acción en cascada para las actualizaciones y eliminaciones:
alter table libros
add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo)
on update cascade
on delete cascade;

238
Si luego de establecer la restricción 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 código de una editorial de "editoriales", se modifica en "editoriales" y todos los valores
iguales de "codigoeditorial" de libros también se modifican.
Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y
"editoriales".
Eliminamos ambas tablas, si existen:

if object_id('libros') is not null


drop table libros;
if object_id('editoriales') is not null
drop table editoriales;

Creamos las tablas:


create table libros(
codigo int not null,
titulo varchar(40),
autor varchar(30),
codigoeditorial tinyint,
primary key (codigo)
);
create table editoriales(
codigo tinyint not null,
nombre varchar(20),
primary key (codigo)
);

Ingresamos algunos registros en ambas tablas:


insert into editoriales values(1,'Emece');
insert into editoriales values(2,'Planeta');
insert into editoriales values(3,'Siglo XXI');

insert into libros values(1,'El aleph','Borges',1);


insert into libros values(2,'Martin Fierro','Jose Hernandez',2);
insert into libros values(3,'Aprenda PHP','Mario Molina',2);

Establecemos una restricción "foreign key" para evitar que se ingrese en "libros" un código de editorial
inexistente en "editoriales" con la opción "on cascade" para actualizaciones y eliminaciones:
alter table libros
add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo)
on update cascade
on delete cascade;

Si actualizamos un valor de código de "editoriales", la modificación se extiende a todos los registros de la


tabla "libros" que hacen referencia a ella en "codigoeditorial":
update editoriales set codigo=10 where codigo=1;

Veamos si la actualización se extendió a "libros":


select titulo, autor, e.codigo,nombre
from libros as l
join editoriales as e
on codigoeditorial=e.codigo;
239
Si eliminamos una editorial, se borra tal editorial de "editoriales" y todos los registros de "libros" de dicha
editorial:
delete from editoriales where codigo=2;

Veamos si la eliminación se extendió a "libros":


select *from libros;

Primer problema:

Una empresa tiene registrados sus clientes en una tabla llamada "clientes", también
tiene una tabla
"provincias" donde registra los nombres de las provincias.
1- Elimine las tablas "clientes" y "provincias", si existen:
if object_id('clientes') is not null
drop table clientes;
if object_id('provincias') is not null
drop table provincias;

2- Créelas con las siguientes estructuras:


create table clientes (
codigo int identity,
nombre varchar(30),
domicilio varchar(30),
ciudad varchar(20),
codigoprovincia tinyint,
primary key(codigo)
);

create table provincias(


codigo tinyint,
nombre varchar(20),
primary key (codigo)
);

3- Ingrese algunos registros para ambas tablas:


insert into provincias values(1,'Cordoba');
insert into provincias values(2,'Santa Fe');
insert into provincias values(3,'Misiones');
insert into provincias values(4,'Rio Negro');

insert into clientes values('Perez Juan','San Martin 123','Carlos Paz',1);


insert into clientes values('Moreno Marcos','Colon 234','Rosario',2);
insert into clientes values('Acosta Ana','Avellaneda 333','Posadas',3);

4- Establezca una restricción "foreign key" especificando la acción "en cascade" para
actualizaciones y "no_action" para eliminaciones.

5- Intente eliminar el registro con código 3, de "provincias".


No se puede porque hay registros en "clientes" al cual hace referencia y la opción para
eliminaciones se estableció como "no action".

6- Modifique el registro con código 3, de "provincias".

7- Verifique que el cambio se realizó en cascada, es decir, que se modificó en la tabla


"provincias"
y en "clientes":
select *from provincias;
select *from clientes;

8- Intente modificar la restricción "foreign key" para que permita eliminación en


cascada.
240
Mensaje de error, no se pueden modificar las restricciones.

9- Intente eliminar la tabla "provincias".


No se puede eliminar porque una restricción "foreign key" hace referencia a ella.

Segundo problema:

Un club dicta clases de distintos deportes. En una tabla llamada "deportes" guarda la
información de
los distintos deportes que se enseñan; en una tabla "socios", los datos de los socios y
en una tabla
"inscripciones" almacena la información necesaria para las inscripciones de los
distintos socios a
los distintos deportes.
1- Elimine las tablas si existen:
if object_id('inscripciones') is not null
drop table inscripciones;
if object_id('deportes') is not null
drop table deportes;
if object_id('socios') is not null
drop table socios;

2- Cree las tablas:


create table deportes(
codigo tinyint,
nombre varchar(20),
primary key(codigo)
);

create table socios(


documento char(8),
nombre varchar(30),
primary key(documento)
);

create table inscripciones(


documento char(8),
codigodeporte tinyint,
matricula char(1),-- 's' si está paga, 'n' si no está paga
primary key(documento,codigodeporte)
);

3- Establezca una restricción "foreign key" para "inscripciones" que haga referencia al
campo
"codigo" de "deportes" que permita la actualización en cascada:
alter table inscripciones
add constraint FK_inscripciones_codigodeporte
foreign key (codigodeporte)
references deportes(codigo)
on update cascade;

4- Establezca una restricción "foreign key" para "inscripciones" que haga referencia al
campo
"documento" de "socios" que permita la eliminación en cascada (Recuerde que se pueden
establecer
varias retricciones "foreign key" a una tabla):
alter table inscripciones
add constraint FK_inscripciones_documento
foreign key (documento)
references socios(documento)
on delete cascade;

5- Ingrese algunos registros en las tablas:


insert into deportes values(1,'basquet');
241
insert into deportes values(2,'futbol');
insert into deportes values(3,'natacion');
insert into deportes values(4,'tenis');

insert into socios values('30000111','Juan Lopez');


insert into socios values('31111222','Ana Garcia');
insert into socios values('32222333','Mario Molina');
insert into socios values('33333444','Julieta Herrero');

insert into inscripciones values ('30000111',1,'s');


insert into inscripciones values ('30000111',2,'s');
insert into inscripciones values ('31111222',1,'s');
insert into inscripciones values ('32222333',3,'n');

6- Intente ingresar una inscripción con un código de deporte inexistente:


insert into inscripciones values('30000111',6,'s');
Mensaje de error.

7- Intente ingresar una inscripción con un documento inexistente en "socios":


insert into inscripciones values('40111222',1,'s');
Mensaje de error.

8- Elimine un registro de "deportes" que no tenga inscriptos:


delete from deportes where nombre='tenis';
Se elimina porque no hay inscriptos en dicho deporte.

9- Intente eliminar un deporte para los cuales haya inscriptos:


delete from deportes where nombre='natacion';
No se puede porque al no especificarse acción para eliminaciones, por defecto es "no
action" y hay
inscriptos en dicho deporte.

10- Modifique el código de un deporte para los cuales haya inscriptos.


La opción para actualizaciones se estableció en cascada, se modifica el código en
"deportes" y en
"inscripciones".

11- Verifique los cambios:


select *from deportes;
select *from inscripciones;

12- Elimine el socio que esté inscripto en algún deporte.


Se elimina dicho socio de "socios" y la acción se extiende a la tabla "inscripciones".

13- Verifique que el socio eliminado ya no aparece en "inscripciones":


select *from socios;
select *from inscripciones;

14- Modifique el documento de un socio que esté inscripto.


No se puede porque la acción es "no action" para actualizaciones.

15- Intente eliminar la tabla "deportes":


drop table deportes;
No se puede porque una restricción "foreign key" hace referencia a ella.

16- Vea las restricciones de la tabla "socios":


sp_helpconstraint socios;
Muestra la restricción "primary key" y la referencia de una "foreign key" de la tabla
"inscripciones".

17- Vea las restricciones de la tabla "deportes":


sp_helpconstraint deportes;
Muestra la restricción "primary key" y la referencia de una "foreign key" de la tabla
"inscripciones".

242
18- Vea las restricciones de la tabla "inscripciones":
sp_helpconstraint inscripciones;
Muestra 3 restricciones. Una "primary key" y dos "foreign key", una para el campo
"codigodeporte"
que especifica "no action" en la columna "delete_action" y "cascade" en la columna
"update_action";
la otra, para el campo "documento" especifica "cascade" en la columna "delete_action" y
"no action"
en "update_action".

79 - Restricciones foreign key deshabilitar y


eliminar (with check - nocheck)
Sabemos que si agregamos una restricción a una tabla que contiene datos, SQL Server los
controla para asegurarse que cumplen con la restricción; es posible deshabilitar esta
comprobación.
Podemos hacerlo al momento de agregar la restricción a una tabla con datos, incluyendo la
opción "with nocheck" en la instrucción "alter table"; si se emplea esta opción, los datos no van a
cumplir la restricción.
Se pueden deshabilitar las restricciones "check" y "foreign key", a las demás se las debe eliminar.
La sintaxis básica al agregar la restriccción "foreign key" es la siguiente:
alter table NOMBRETABLA1
with OPCIONDECHEQUEO
add constraint NOMBRECONSTRAINT
foreign key (CAMPOCLAVEFORANEA)
references NOMBRETABLA2 (CAMPOCLAVEPRIMARIA)
on update OPCION
on delete OPCION;

La opción "with OPCIONDECHEQUEO" especifica si se controlan los datos existentes o no con


"check" y "nocheck" respectivamente. Por defecto, si no se especifica, la opción es "check".
En el siguiente ejemplo agregamos una restricción "foreign key" que controla que todos los
códigos de editorial tengan un código válido, es decir, dicho código exista en "editoriales". La
restricción no se aplica en los datos existentes pero si en los siguientes ingresos, modificaciones
y actualizaciones:
alter table libros
with nocheck
add constraint FK_libros_codigoeditorial
foreing key (codigoeditorial)
references editoriales(codigo);

La comprobación de restricciones se puede deshabilitar para modificar, eliminar o agregar datos a


una tabla sin comprobar la restricción. La sintaxis general es:
alter table NOMBRETABLA
243
OPCIONDECHEQUEO constraint NOMBRERESTRICCION;

En el siguiente ejemplo deshabilitamos la restricción creada anteriormente:


alter table libros
nocheck constraint FK_libros_codigoeditorial;

Para habilitar una restricción deshabilitada se ejecuta la misma instrucción pero con la cláusula
"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 restricción está habilitada o no, podemos ejecutar el procedimiento almacenado
"sp_helpconstraint" y entenderemos lo que informa la columna "status_enabled".
Entonces, las cláusulas "check" y "nocheck" permiten habilitar o deshabilitar restricciones "foreign
key" (y "check"). Pueden emplearse para evitar la comprobación de datos existentes al crear la
restricción o para deshabilitar la comprobación de datos al ingresar, actualizar y eliminar algún
registro que infrinja la restricción.
Podemos eliminar una restricción "foreign key" con "alter table". La sintaxis básica es la misma
que para cualquier otra restricción:
alter table TABLA
drop constraint NOMBRERESTRICCION;

Eliminamos la restricción de "libros":


alter table libros
drop constraint FK_libros_codigoeditorial;

No se puede eliminar una tabla si una restricción "foreign key" hace referencia a ella.
Cuando eliminamos una tabla que tiene una restricción "foreign key", la restricción también se
elimina.
Una librería almacena la información de sus libros para la venta en dos tablas, "libros" y
"editoriales".
Eliminamos ambas tablas, si existen:

if object_id('libros') is not null


drop table libros;
if object_id('editoriales') is not null
drop table editoriales;

Creamos las tablas:


create table libros(
codigo int not null,
titulo varchar(40),
autor varchar(30),
codigoeditorial tinyint,
primary key (codigo)
);
244
create table editoriales(
codigo tinyint not null,
nombre varchar(20),
primary key (codigo)
);

Ingresamos algunos registros:


insert into editoriales values(1,'Planeta');
insert into editoriales values(2,'Emece');
insert into editoriales values(3,'Paidos');

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


insert into libros values(2,'El aleph','Borges',2);
insert into libros values(3,'Aprenda PHP','Mario Molina',5);

Agregamos una restricción "foreign key" a la tabla "libros" para evitar que se ingresen códigos de editoriales
inexistentes en "editoriales". Incluimos la opción "with nocheck" para evitar la comprobación de la
restricción en los datos existentes (note que hay un libro que tiene un código de editorial inválido):
alter table libros
with nocheck
add constraint FK_libros_codigoeditorial
foreign key (codigoeditorial)
references editoriales(codigo);

La deshabilitación de la comprobación de la restricción no afecta a los siguientes ingresos, modificaciones y


actualizaciones. Para poder ingresar, modificar o eliminar datos a una tabla sin que SQL Server compruebe
la restricción debemos deshabilitarla:
alter table libros
nocheck constraint FK_libros_codigoeditorial;

Veamos si la restricción está habilitada o no:


sp_helpconstraint libros;

En la columna "status_enabled" de la restricción "foreign key" aparece "Disabled".


Veamos las restricciones de "editoriales":
sp_helpconstraint editoriales;

Aparece la restricción "primary key" y nos informa que hay una restricción "foreign key" que hace
referencia a ella de la tabla "libros" (aunque esté deshabilitada).
Ahora podemos ingresar un registro en "libros" con código inválido:
insert into libros values(4,'Ilusiones','Richard Bach',6);

También podemos modificar:


update editoriales set codigo=8 where codigo=1;

También realizar eliminaciones:


delete from editoriales where codigo=2;

Habilitamos la restricción:

245
alter table libros
check constraint FK_libros_codigoeditorial;

Veamos si la restricción está habilitada o no:


sp_helpconstraint libros;

En la columna "status_enabled" aparece "Enabled".


Eliminamos la restricción:
alter table libros
drop constraint FK_libros_codigoeditorial;

Ejecutamos el procedimiento almacenado sp_helpconstraint para ver si la restricción se eliminó:


sp_helpconstraint libros;

No aparece la restricción "foreign key".


Vemos las restricciones de "editoriales":
sp_helpconstraint editoriales;

No aparece la restricción "foreign key" que hace referencia a esta tabla.


Primer problema:

Una empresa tiene registrados sus clientes en una tabla llamada "clientes", también
tiene una tabla
"provincias" donde registra los nombres de las provincias.
1- Elimine las tablas "clientes" y "provincias", si existen:
if object_id('clientes') is not null
drop table clientes;
if object_id('provincias') is not null
drop table provincias;

2- Créelas con las siguientes estructuras:


create table clientes (
codigo int identity,
nombre varchar(30),
domicilio varchar(30),
ciudad varchar(20),
codigoprovincia tinyint,
primary key(codigo)
);

create table provincias(


codigo tinyint,
nombre varchar(20),
primary key (codigo)
);

3- Ingrese algunos registros para ambas tablas:


insert into provincias values(1,'Cordoba');
insert into provincias values(2,'Santa Fe');
insert into provincias values(3,'Misiones');
insert into provincias values(4,'Rio Negro');

insert into clientes values('Perez Juan','San Martin 123','Carlos Paz',1);


insert into clientes values('Moreno Marcos','Colon 234','Rosario',2);
insert into clientes values('Garcia Juan','Sucre 345','Cordoba',1);
insert into clientes values('Lopez Susana','Caseros 998','Posadas',3);
246
insert into clientes values('Marcelo Moreno','Peru 876','Viedma',4);
insert into clientes values('Lopez Sergio','Avellaneda 333','La Plata',5);

4- Intente agregar una restricción "foreign key" para que los códigos de provincia de
"clientes"
existan en "provincias" con acción en cascada para actualizaciones y eliminaciones, sin
especificar
la opción de comprobación de datos:
alter table clientes
add constraint FK_clientes_codigoprovincia
foreign key (codigoprovincia)
references provincias(codigo)
on update cascade
on delete cascade;
No se puede porque al no especificar opción para la comprobación de datos, por defecto
es "check" y
hay un registro que no cumple con la restricción.

5- Agregue la restricción anterior pero deshabilitando la comprobación de datos


existentes:
alter table clientes
with nocheck
add constraint FK_clientes_codigoprovincia
foreign key (codigoprovincia)
references provincias(codigo)
on update cascade
on delete cascade;

6- Vea las restricciones de "clientes":


sp_helpconstraint clientes;
Aparece la restricción "primary key" y "foreign key", las columnas "delete_action" y
"update_action"
contienen "cascade" y la columna "status_enabled" contiene "Enabled".

7- Vea las restricciones de "provincias":


sp_helpconstraint provincias;
Aparece la restricción "primary key" y la referencia a esta tabla de la restricción
"foreign key" de
la tabla "clientes.

8- Deshabilite la restricción "foreign key" de "clientes":


alter table clientes
nocheck constraint FK_clientes_codigoprovincia;

9- Vea las restricciones de "clientes":


sp_helpconstraint clientes;
la restricción "foreign key" aparece inhabilitada.

10- Vea las restricciones de "provincias":


sp_helpconstraint provincias;
informa que la restricción "foreign key" de "clientes" hace referencia a ella, aún
cuando está
deshabilitada.

11- Agregue un registro que no cumpla la restricción "foreign key":


insert into clientes values('Garcia Omar','San Martin 100','La Pampa',6);
Se permite porque la restricción está deshabilitada.

12- Elimine una provincia de las cuales haya clientes:


delete from provincias where codigo=2;

13- Corrobore que el registro se eliminó de "provincias" pero no se extendió a


"clientes":
select *from clientes;

247
select *from provincias;

14- Modifique un código de provincia de la cual haya clientes:


update provincias set codigo=9 where codigo=3;

15- Verifique que el cambio se realizó en "provincias" pero no se extendió a


"clientes":
select *from clientes;
select *from provincias;

16- Intente eliminar la tabla "provincias":


drop table provincias;
No se puede porque la restricción "FK_clientes_codigoprovincia" la referencia, aunque
esté deshabilitada.

17- Habilite la restricción "foreign key":


alter table clientes
check constraint FK_clientes_codigoprovincia;

18- Intente agregar un cliente con código de provincia inexistente en "provincias":


insert into clientes values('Hector Ludueña','Paso 123','La Plata',8);
No se puede.

19- Modifique un código de provincia al cual se haga referencia en "clientes":


update provincias set codigo=20 where codigo=4;
Actualización en cascada.

20- Vea que se modificaron en ambas tablas:


select *from clientes;
select *from provincias;

21- Elimine una provincia de la cual haya referencia en "clientes":


delete from provincias where codigo=1;
Acción en cascada.

22- Vea que los registros de ambas tablas se eliminaron:


select *from clientes;
select *from provincias;

23- Elimine la restriccion "foreign key":


alter table clientes
drop constraint FK_clientes_codigoprovincia;

24- Vea las restriciones de la tabla "provincias":


sp_helpconstraint provincias;
Solamente aparece la restricción "primary key", ya no hay una "foreign key" que la
referencie.

25- Elimine la tabla "provincias":


drop table provincias;
Puede eliminarse porque no hay restricción "foreign key" que la referencie.

80 - Restricciones foreign key (información)


El procedimiento almacenado "sp_helpconstraint" devuelve las siguientes columnas:

248
- constraint_type: tipo de restricción. Si es una restricción de campo (default o check) indica sobre
qué campo fue establecida. Si es de tabla (primary key o unique) indica el tipo de índice creado.
Si es una "foreign key" lo indica.
- constraint_name: nombre de la restricción.
- delete_action: solamente es aplicable para restricciones de tipo "foreign key". Indica si la acción
de eliminación actúa, no actúa o es en cascada. Indica "n/a" en cualquier restricción para la que
no se aplique; "No Action" si no actúa y "Cascade" si es en cascada.
- update_action: sólo es aplicable para restricciones de tipo "foreign key". Indica si la acción de
actualización es: No Action, Cascade, or n/a. Indica "n/a" en cualquier restricción para la que no
se aplique.
- status_enabled: solamente es aplicable para restricciones de tipo "check" y "foreign key". Indica
si está habilitada (Enabled) o no (Disabled). Indica "n/a" en cualquier restricción para la que no se
aplique.
- status_for_replication: solamente es aplicable para restricciones de tipo "check" y "foreign key".
Indica "n/a" en cualquier restricción para la que no se aplique.
- constraint_keys: Si es una restricción "default" muestra la condición de chequeo; si es una
restricción "default", el valor por defecto; si es una "primary key", "unique" o "foreign key" muestra
el/ los campos a los que se aplicaron la restricción. En caso de valores predeterminados y reglas,
el texto que lo define.

81 - Restricciones al crear la tabla


Hasta el momento hemos agregado restricciones a tablas existentes con "alter table" (manera
aconsejada), también pueden establecerse al momento de crear una tabla (en la instrucción
"create table").
Podemos aplicar restricciones a nivel de campo (restricción de campo) o a nivel de tabla
(restricción de tabla).
En el siguiente ejemplo creamos la tabla "libros" con varias restricciones:
create table libros(
codigo int identity,
titulo varchar(40),
codigoautor int not null,
codigoeditorial tinyint not null,
precio decimal(5,2)
constraint DF_precio default (0),
constraint PK_libros_codigo
primary key clustered (codigo),
constraint UQ_libros_tituloautor
unique (titulo,codigoautor),
constraint FK_libros_editorial
foreign key (codigoeditorial)
references editoriales(codigo)

249
on update cascade,
constraint FK_libros_autores
foreign key (codigoautor)
references autores(codigo)
on update cascade,
constraint CK_precio_positivo check (precio>=0)
);

En el ejemplo anterior creamos:


- una restricción "default" para el campo "precio" (restricción a nivel de campo);
- una restricción "primary key" con índice agrupado para el campo "codigo" (a nivel de tabla);
- una restricción "unique" con índice no agrupado (por defecto) para los campos "titulo" y
"codigoautor" (a nivel de tabla);
- una restricción "foreign key" para establecer el campo "codigoeditorial" como clave externa que
haga referencia al campo "codigo" de "editoriales y permita actualizaciones en cascada y no
eliminaciones (por defecto "no action");
- una restricción "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 restricción "check" para el campo "precio" que no admita valores negativos;
Si definimos una restricción "foreign key" al crear una tabla, la tabla referenciada debe existir.
Trabajamos con las tablas "libros", "autores" y "editoriales" de una librería:
Eliminamos las tablas si existen:

if object_id('libros') is not null


drop table libros;
if object_id('editoriales') is not null
drop table editoriales;
if object_id('autores') is not null
drop table autores;

Creamos la tabla "editoriales" con una restricción "primary key":


create table editoriales(
codigo tinyint not null,
nombre varchar(30),
constraint PK_editoriales primary key (codigo)
);

Creamos la tabla "autores" con una restricción "primary key", una "unique" y una "check":
create table autores(
codigo int not null
constraint CK_autores_codigo check (codigo>=0),
nombre varchar(30) not null,
constraint PK_autores_codigo
primary key (codigo),
constraint UQ_autores_nombre
unique (nombre),
);

Aplicamos varias restricciones cuando creamos la tabla "libros":


250
create table libros(
codigo int identity,
titulo varchar(40),
codigoautor int not null,
codigoeditorial tinyint not null,
precio decimal(5,2)
constraint DF_libros_precio default (0),
constraint PK_libros_codigo
primary key clustered (codigo),
constraint UQ_libros_tituloautor
unique (titulo,codigoautor),
constraint FK_libros_editorial
foreign key (codigoeditorial)
references editoriales(codigo)
on update cascade,
constraint FK_libros_autores
foreign key (codigoautor)
references autores(codigo)
on update cascade,
constraint CK_libros_precio_positivo check (precio>=0)
);

Veamos las restricciones de "editoriales":


sp_helpconstraint editoriales;

Aparece la restricción "primary key" para el campo "codigo" y la restricción "foreign key" de "libros"
"FK_libros_editorial" que referencia esta tabla.
Veamos las restricciones de "autores":
sp_helpconstraint autores;

Aparecen 4 restricciones: una restricción "check" para el campo "codigo", una restricción "primary key"
para el campo "codigo", una restricción "unique" para el campo "nombre" y la restricción "foreign key" de
"libros" "FK_libros_autores" que referencia esta tabla.
Veamos las restricciones de "libros":
sp_helpconstraint libros;

Aparecen 6 restricciones: una restricción "check" sobre el campo "precio", una "default" sobre el campo
"precio", una restricción "foreign key" que establece el campo "codigoeditorial" como clave externa que
hace referencia al campo "codigo" de "editoriales" y permite actualizaciones en cascada y no eliminaciones,
una restricción "foreign key" que establece el campo "codigoautor" como clave externa que hace referencia
al campo "codigo" de "autores" y permite actualizaciones en cascada y no eliminaciones, una restricción
"primary key" con índice agrupado para el campo "codigo" y una restricción "unique" con índice no
agrupado para los campos "titulo" y "codigoautor".
Recuerde que si definimos una restricción "foreign key" al crear una tabla, la tabla referenciada debe existir,
por ello creamos las tablas "editoriales" y "autores" antes que "libros".
También debemos ingresar registros en las tablas "autores" y "editoriales" antes que en "libros", a menos que
deshabilitemos la restricción "foreign key".
Primer problema:

Un club de barrio tiene en su sistema 4 tablas:

251
- "socios": en la cual almacena documento, número, nombre y domicilio de cada socio;
- "deportes": que guarda un código, nombre del deporte, día de la semana que se dicta y
documento
del profesor instructor;
- "profesores": donde se guarda el documento, nombre y domicilio de los profesores e
- "inscriptos": que almacena el número de socio, el código de deporte y si la matricula
está paga o
no.
1- Elimine las tablas si existen:
if object_id('inscriptos') is not null
drop table inscriptos;
if object_id('socios') is not null
drop table socios;
if object_id('profesores') is not null
drop table profesores;
if object_id('deportes') is not null
drop table deportes;

2- Considere que:
- un socio puede inscribirse en varios deportes, pero no dos veces en el mismo.
- un socio tiene un documento único y un número de socio único.
- el documento del socio debe contener 8 dígitos.
- un deporte debe tener asignado un profesor que exista en "profesores" o "null" si aún
no tiene un
instructor definido.
- el campo "dia" de "deportes" puede ser: lunes, martes, miercoles, jueves, viernes o
sabado.
- el campo "dia" de "deportes" por defecto debe almacenar 'sabado'.
- un profesor puede ser instructor de varios deportes o puede no dictar ningún deporte.
- un profesor no puede estar repetido en "profesores".
- el documento del profesor debe contener 8 dígitos.
- un inscripto debe ser socio, un socio puede no estar inscripto en ningún deporte.
- una inscripción debe tener un valor en socio existente en "socios" y un deporte que
exista en
"deportes".
- el campo "matricula" de "inscriptos" debe aceptar solamente los caracteres 's' o 'n'.

3- Cree las tablas con las restricciones necesarias:


create table profesores(
documento char(8) not null,
nombre varchar(30),
domicilio varchar(30),
constraint CK_profesores_documento_patron check (documento like '[0-9][0-9][0-9][0-9]
[0-9][0-9][0-9][0-9]'),
constraint PK_profesores_documento
primary key (documento)
);

create table deportes(


codigo tinyint identity,
nombre varchar(20) not null,
dia varchar(30)
constraint DF_deportes_dia default('sabado'),
profesor char(8),--documento del profesor
constraint CK_deportes_dia_lista check (dia in
('lunes','martes','miercoles','jueves','viernes','sabado')),
constraint PK_deportes_codigo
primary key (codigo)
);

create table socios(


numero int identity,
documento char(8),
nombre varchar(30),

252
domicilio varchar(30),
constraint CK_documento_patron check (documento like '[0-9][0-9][0-9][0-9][0-9][0-9]
[0-9][0-9]'),
constraint PK_socios_numero
primary key nonclustered(numero),
constraint UQ_socios_documento
unique clustered(documento)
);

create table inscriptos(


numerosocio int not null,
codigodeporte tinyint,
matricula char(1),
constraint PK_inscriptos_numerodeporte
primary key clustered (numerosocio,codigodeporte),
constraint FK_inscriptos_deporte
foreign key (codigodeporte)
references deportes(codigo)
on update cascade,
constraint FK_inscriptos_socios
foreign key (numerosocio)
references socios(numero)
on update cascade
on delete cascade,
constraint CK_matricula_valores check (matricula in ('s','n'))
);

4- Ingrese registros en "profesores":


insert into profesores values('21111111','Andres Acosta','Avellaneda 111');
insert into profesores values('22222222','Betina Bustos','Bulnes 222');
insert into profesores values('23333333','Carlos Caseros','Colon 333');

5- Ingrese registros en "deportes". Ingrese el mismo día para distintos deportes, un


deporte sin día
confirmado, un deporte sin profesor definido:
insert into deportes values('basquet','lunes',null);
insert into deportes values('futbol','lunes','23333333');
insert into deportes values('natacion',null,'22222222');
insert into deportes values('padle',default,'23333333');
insert into deportes (nombre,dia) values('tenis','jueves');

6- Ingrese registros en "socios":


insert into socios values('30111111','Ana Acosta','America 111');
insert into socios values('30222222','Bernardo Bueno','Bolivia 222');
insert into socios values('30333333','Camila Conte','Caseros 333');
insert into socios values('30444444','Daniel Duarte','Dinamarca 444');

7- Ingrese registros en "inscriptos". Inscriba a un socio en distintos deportes,


inscriba varios
socios en el mismo deporte.
insert into inscriptos values(1,3,'s');
insert into inscriptos values(1,5,'s');
insert into inscriptos values(2,1,'s');
insert into inscriptos values(4,1,'n');
insert into inscriptos values(4,4,'s');

8- Realice un "join" (del tipo que sea necesario) para mostrar todos los datos del
socio junto con
el nombre de los deportes en los cuales está inscripto, el día que tiene que asistir y
el nombre del
profesor que lo instruirá.
5 registros.

253
9- Realice la misma consulta anterior pero incluya los socios que no están inscriptos
en ningún
deporte.
6 registros.

10- Muestre todos los datos de los profesores, incluido el deporte que dicta y el día,
incluyendo
los profesores que no tienen asignado ningún deporte.
4 registros.

11- Muestre todos los deportes y la cantidad de inscriptos, incluyendo aquellos


deportes para los
cuales no hay inscriptos.
5 registros.

12- Muestre las restricciones de "socios".


3 restricciones y 1 "foreign key" de "inscriptos" que la referencia.

13- Muestre las restricciones de "deportes".


3 restricciones y 1 "foreign key" de "inscriptos" que la referencia.

14- Muestre las restricciones de "profesores".


2 restricciones.

15- Muestre las restricciones de "inscriptos".


4 restricciones.

82 - Unión
El operador "union" combina el resultado de dos o más 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 selección de cada consulta. No se incluyen las
filas duplicadas en el resultado, a menos que coloque la opción "all".
Se deben especificar los nombres de los campos en la primera instrucción "select".
Puede emplear la cláusula "order by".
Puede dividir una consulta compleja en varias consultas "select" y luego emplear el operador
"union" para combinarlas.
Una academia de enseñanza 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
invitación.
Para obtener los datos necesarios de ambas tablas en una sola consulta necesitamos realizar
una unión:

254
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 unión son los que se especifican en el primer "select".
Una academia de enseñanza almacena los datos de los alumnos en una tabla llamada "alumnos"
y los datos de los profesores en otra denominada "profesores".
Eliminamos las tablas si existen:

if object_id('alumnos') is not null


drop table alumnos;
if object_id('profesores') is not null
drop table profesores;

Creamos las tablas:


create table profesores(
documento varchar(8) not null,
nombre varchar (30),
domicilio varchar(30),
primary key(documento)
);
create table alumnos(
documento varchar(8) not null,
nombre varchar (30),
domicilio varchar(30),
primary key(documento)
);

Ingresamos algunos registros:


insert into alumnos values('30000000','Juan Perez','Colon 123');
insert into alumnos values('30111111','Marta Morales','Caseros 222');
insert into alumnos values('30222222','Laura Torres','San Martin 987');
insert into alumnos values('30333333','Mariano Juarez','Avellaneda 34');
insert into alumnos values('23333333','Federico Lopez','Colon 987');
insert into profesores values('22222222','Susana Molina','Sucre 345');
insert into profesores values('23333333','Federico Lopez','Colon 987');

La academia necesita el nombre y domicilio de profesores y alumnos para enviarles una tarjeta de
invitación.
Empleamos el operador "union" para obtener dicha información de ambas tablas:
select nombre, domicilio from alumnos
union
select nombre, domicilio from profesores;

Note que existe un profesor que también está presente en la tabla "alumnos"; dicho registro aparece una sola
vez en el resultado de "union". Si queremos que las filas duplicadas aparezcan, debemos emplear "all":
select nombre, domicilio from alumnos
union all
select nombre, domicilio from profesores;

Ordenamos por domicilio:

255
select nombre, domicilio from alumnos
union
select nombre, domicilio from profesores
order by domicilio;

Podemos agregar una columna extra a la consulta con el encabezado "condicion" en la que aparezca el literal
"profesor" o "alumno" según si la persona es uno u otro:
select nombre, domicilio, 'alumno' as condicion from alumnos
union
select nombre, domicilio,'profesor' from profesores
order by condicion;

Primer problema:

Un supermercado almacena en una tabla denominada "proveedores" los datos de las


compañías que le
proveen de mercaderías; en una tabla llamada "clientes", los datos de los comercios que
le compran y
en otra tabla "empleados" los datos de los empleados.
1- Elimine las tablas si existen:
if object_id('clientes') is not null
drop table clientes;
if object_id('proveedores') is not null
drop table proveedores;
if object_id('empleados') is not null
drop table empleados;

2- Cree las tablas:


create table proveedores(
codigo int identity,
nombre varchar (30),
domicilio varchar(30),
primary key(codigo)
);
create table clientes(
codigo int identity,
nombre varchar (30),
domicilio varchar(30),
primary key(codigo)
);
create table empleados(
documento char(8) not null,
nombre varchar(20),
apellido varchar(20),
domicilio varchar(30),
primary key(documento)
);

3- Ingrese algunos registros:


insert into proveedores values('Bebida cola','Colon 123');
insert into proveedores values('Carnes Unica','Caseros 222');
insert into proveedores values('Lacteos Blanca','San Martin 987');
insert into clientes values('Supermercado Lopez','Avellaneda 34');
insert into clientes values('Almacen Anita','Colon 987');
insert into clientes values('Garcia Juan','Sucre 345');
insert into empleados values('23333333','Federico','Lopez','Colon 987');
insert into empleados values('28888888','Ana','Marquez','Sucre 333');
insert into empleados values('30111111','Luis','Perez','Caseros 956');

4- El supermercado quiere enviar una tarjeta de salutación a todos los proveedores,


clientes y

256
empleados y necesita el nombre y domicilio de todos ellos. Emplee el operador "union"
para obtener
dicha información de las tres tablas.

5- Agregue una columna con un literal para indicar si es un proveedor, un cliente o un


empleado y
ordene por dicha columna.

83 - Agregar y eliminar campos ( alter table -


add - drop)
"alter table" permite modificar la estructura de una tabla.
Podemos utilizarla para agregar, modificar y eliminar campos de una tabla.
Para agregar un nuevo campo a una tabla empleamos la siguiente sintaxis básica:
alter table NOMBRETABLA
add NOMBRENUEVOCAMPO DEFINICION;

En el siguiente ejemplo agregamos el campo "cantidad" a la tabla "libros", de tipo tinyint, que
acepta valores nulos:
alter table libros
add cantidad tinyint;

Puede verificarse la alteración de la estructura de la tabla ejecutando el procedimiento


almacenado "sp_columns".
SQL Server no permite agregar campos "not null" a menos que se especifique un valor por
defecto:
alter table libros
add autor varchar(20) not null default 'Desconocido';

En el ejemplo anterior, se agregó una restricción "default" para el nuevo campo, que puede
verificarse ejecutando el procedimiento almacenado "sp_helpconstraint".
Al agregar un campo puede especificarse que sea "identity" (siempre que no exista otro campo
identity).
Para eliminar campos de una tabla la sintaxis básica es la siguiente:
alter table NOMBRETABLA
drop column NOMBRECAMPO;

En el siguiente ejemplo eliminamos el campo "precio" de la tabla "libros":


alter table libros
drop column precio;

No pueden eliminarse los campos que son usados por un índice o tengan restricciones. No puede
eliminarse un campo si es el único en la tabla.
257
Podemos eliminar varios campos en una sola sentencia:
alter table libros
drop column editorial,edicion;

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


Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
titulo varchar(30),
editorial varchar(15),
edicion datetime,
precio decimal(6,2)
);

Agregamos un registro:
insert into libros (titulo,editorial,precio)
values ('El aleph','Emece',25.50);

Agregamos el campo "cantidad" a la tabla "libros", de tipo tinyint, que acepta valores nulos:
alter table libros
add cantidad tinyint;

Verificamos la estructura de la tabla empleando el procedimiento almacenado "sp_columns":


sp_columns libros;

aparece el nuevo campo.


Agregamos un campo "codigo" a la tabla "libros", de tipo int con el atributo "identity":
alter table libros
add codigo int identity;

Intentamos agregar un campo llamado "autor" de tipo varchar(30) que NO acepte valores nulos:
alter table libros
add autor varchar(30) not null;

No es posible, porque SQL Server no permite agregar campos "not null" a menos que se especifique un
valor por defecto:
alter table libros
add autor varchar(20) not null default 'Desconocido';

En el ejemplo anterior, se agregó una restricción "default" para el nuevo campo, que puede verificarse
ejecutando el procedimiento almacenado "sp_helpconstraint".
En el siguiente ejemplo eliminamos el campo "precio" de la tabla "libros":
alter table libros
drop column precio;

258
Verificamos la eliminación:
sp_columns libros;

el campo "precio" ya no existe.


Recuerde que no pueden eliminarse los campos con restricciones, intentémoslo:
alter table libros
drop column autor;

no lo permite.
Podemos eliminar varios campos en una sola sentencia:
alter table libros
drop column editorial,edicion;

Primer problema:

Trabaje con una tabla llamada "empleados".


1- Elimine la tabla, si existe, créela y cargue un registro:
if object_id('empleados') is not null
drop table empleados;

create table empleados(


apellido varchar(20),
nombre varchar(20),
domicilio varchar(30),
fechaingreso datetime
);
insert into empleados(apellido,nombre) values ('Rodriguez','Pablo');

2- Agregue el campo "sueldo", de tipo decimal(5,2).

3- Verifique que la estructura de la tabla ha cambiado.

4- Agregue un campo "codigo", de tipo int con el atributo "identity".

5- Intente agregar un campo "documento" no nulo.


No es posible, porque SQL Server no permite agregar campos "not null" a menos que se
especifique un
valor por defecto.

6- Agregue el campo del punto anterior especificando un valor por defecto:


alter table empleados
add documento char(8) not null default '00000000';

7- Verifique que la estructura de la tabla ha cambiado.

8- Elimine el campo "sueldo".

9- Verifique la eliminación:
sp_columns empleados;

10- Intente eliminar el campo "documento".


no lo permite.

11- Elimine los campos "codigo" y "fechaingreso" en una sola sentencia.

12- Verifique la eliminación de los campos:


sp_columns empleados;

259
84 - Alterar campos (alter table - alter)
Hemos visto que "alter table" permite modificar la estructura de una tabla. También podemos
utilizarla para modificar campos de una tabla.
La sintaxis básica para modificar un campo existente es la siguiente:
alter table NOMBRETABLA
alter column CAMPO NUEVADEFINICION;

Modificamos el campo "titulo" extendiendo su longitud y para que NO admita valores nulos:
alter table libros
alter column titulo varchar(40) not null;

En el siguiente ejemplo alteramos el campo "precio" de la tabla "libros" que fue definido
"decimal(6,2) not null" para que no acepte valores nulos:
alter table libros
alter column precio decimal(6,2) null;

SQL Server tiene algunas excepciones al momento de modificar los campos. No permite
modificar:
- campos de tipo text, image, ntext y timestamp.
- un campo que es usado en un campo calculado.
- campos que son parte de índices o tienen restricciones, a menos que el cambio no afecte al
índice o a la restricción, por ejemplo, se puede ampliar la longitud de un campo de tipo caracter.
- agregando o quitando el atributo "identity".
- campos que afecten a los datos existentes cuando una tabla contiene registros (ejemplo: un
campo contiene valores nulos y se pretende redefinirlo como "not null"; un campo int guarda un
valor 300 y se pretende modificarlo a tinyint, etc.).
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(30),
autor varchar(30),
editorial varchar(15),
precio decimal(6,2) not null default 0
);

260
Ingresamos algunos registros:
insert into libros
values('El aleph','Borges','Planeta',20);
insert into libros
values('Java en 10 minutos',null,'Siglo XXI',30);
insert into libros
values('Uno','Richard Bach','Planeta',15);
insert into libros
values('Martin Fierro','Jose Hernandez',null,30);
insert into libros
values('Aprenda PHP','Mario Molina','Emece',25);

Vamos a efectuar diferentes modificaciones a los campos de esta tabla. Luego de cada una podemos ver la
estructura de la tabla para controlar si los cambios se realizaron o no empleando el procedimiento
almacenado "sp_columns".
Modificamos el campo "titulo" para que acepte una cadena más larga y no admita valores nulos:
alter table libros
alter column titulo varchar(40) not null;

Si intentamos modificar el campo "autor" para que no admita valores nulos SQL Server no lo permite
porque hay registros con valor nulo en "autor".
Eliminamos tal registro y realizamos la modificación:
delete from libros where autor is null;
alter table libros
alter column autor varchar(30) not null;

Intentamos quitar el atributo "identity" del campo "codigo" y lo redefinimos como "smallint":
alter table libros
alter column codigo smallint;

No aparece mensaje de error pero no se realizó el cambio completo, controlémoslo:


sp_columns libros;

el campo "codigo" es "smallint pero aún es "identity".


Aprendimos que no se puede modificar el tipo de dato o atributos de un campo que tiene una restricción si el
cambio afecta a la restricción; pero si el cambio no afecta a la restricción, si se realiza:
alter table libros
alter column precio decimal(6,2) null;

El campo "precio" fue alterado para que acepte valores nulos:


Primer problema:

Trabaje con una tabla llamada "empleados".


1- Elimine la tabla, si existe y créela:
if object_id('empleados') is not null
drop table empleados;

create table empleados(


legajo int not null,
documento char(7) not null,
nombre varchar(10),
261
domicilio varchar(30),
ciudad varchar(20) default 'Buenos Aires',
sueldo decimal(6,2),
cantidadhijos tinyint default 0,
primary key(legajo)
);

2- Modifique el campo "nombre" extendiendo su longitud.

3- Controle la modificación:
sp_columns empleados;

4- Modifique el campo "sueldo" para que no admita valores nulos.

4- Modifique el campo "documento" ampliando su longitud a 8 caracteres.

5- Intente modificar el tipo de datos del campo "legajo" a "tinyint":


alter table empleados
alter column legajo tinyint not null;
No se puede porque tiene una restricción.

6- Ingrese algunos registros, uno con "nombre" nulo:


insert into empleados values(1,'22222222','Juan Perez','Colon 123','Cordoba',500,3);
insert into empleados values(2,'30000000',null,'Sucre 456','Cordoba',600,2);

7- Intente modificar el campo "nombre" para que no acepte valores nulos:


alter table empleados
alter column nombre varchar(30) not null;
No se puede porque hay registros con ese valor.

8- Elimine el registro con "nombre" nulo y realice la modificación del punto 7:


delete from empleados where nombre is null;
alter table empleados
alter column nombre varchar(30) not null;

9- Modifique el campo "ciudad" a 10 caracteres.

10- Intente agregar un registro con el valor por defecto para "ciudad":
insert into empleados values(3,'33333333','Juan Perez','Sarmiento 856',default,500,4);
No se puede porque el campo acepta 10 caracteres y el valor por defecto tiene 12
caracteres.

11- Modifique el campo "ciudad" sin que afecte la restricción dándole una longitud de
15 caracteres.

12- Agregue el registro que no pudo ingresar en el punto 10:


insert into empleados values(3,'33333333','Juan Perez','Sarmiento 856',default,500,4);

13- Intente agregar el atributo identity de "legajo".


No se puede agregar este atributo.

85 - Agregar campos y restricciones (alter table)


Podemos agregar un campo a una tabla y en el mismo momento aplicarle una restricción.
Para agregar un campo y establecer una restricción, la sintaxis básica es la siguiente:
alter table TABLA

262
add CAMPO DEFINICION
constraint NOMBRERESTRICCION TIPO;

Agregamos a la tabla "libros", el campo "titulo" de tipo varchar(30) y una restricción "unique" con
índice agrupado:
alter table libros
add titulo varchar(30)
constraint UQ_libros_autor unique clustered;

Agregamos a la tabla "libros", el campo "codigo" de tipo int identity not null y una restricción
"primary key" con índice no agrupado:
alter table libros
add codigo int identity not null
constraint PK_libros_codigo primary key nonclustered;

Agregamos a la tabla "libros", el campo "precio" de tipo decimal(6,2) y una restricción "check":
alter table libros
add precio decimal(6,2)
constraint CK_libros_precio check (precio>=0);

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


Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla con la siguiente estructura:


create table libros(
autor varchar(30),
editorial varchar(15)
);

Agregamos el campo "titulo" de tipo varchar(30) y una restricción "unique" con índice agrupado:
alter table libros
add titulo varchar(30)
constraint UQ_libros_autor unique clustered;

Veamos si la estructura cambió:


sp_columns libros;

Agregamos el campo "codigo" de tipo int identity not null y en la misma sentencia una restricción "primary
key" con índice no agrupado:
alter table libros
add codigo int identity not null
constraint PK_libros_codigo primary key nonclustered;

Agregamos el campo "precio" de tipo decimal(6,2) y una restricción "check" que no permita valores
negativos para dicho campo:
alter table libros
add precio decimal(6,2)
constraint CK_libros_precio check (precio>=0);

263
Vemos las restricciones:
sp_helpconstraint libros;

Primer problema:

Trabaje con una tabla llamada "empleados".


1- Elimine la tabla, si existe y créela:
if object_id('empleados') is not null
drop table empleados;

create table empleados(


documento char(8) not null,
nombre varchar(10),
domicilio varchar(30),
ciudad varchar(20) default 'Buenos Aires'
);

2- Agregue el campo "legajo" de tipo int identity y una restricción "primary key":
alter table empleados
add legajo int identity
constraint PK_empleados_legajo primary key;

3- Vea si la estructura cambió y si se agregó la restricción:


sp_columns empleados;
exec sp_helpconstraint empleados;

4- Agregue el campo "hijos" de tipo tinyint y en la misma sentencia una restricción


"check" que no
permita valores superiores a 30:
alter table empleados
add hijos tinyint
constraint CK_empleados_hijos check (hijos<=30);

5- Ingrese algunos registros:


insert into empleados values('22222222','Juan Lopez','Colon 123','Cordoba',2);
insert into empleados values('23333333','Ana Garcia','Sucre 435','Cordoba',3);

6- Intente agregar el campo "sueldo" de tipo decimal(6,2) no nulo y una restricción


"check" que no
permita valores negativos para dicho campo:
alter table empleados
add sueldo decimal(6,2) not null
constraint CK_empleados_sueldo check (sueldo>=0);
No lo permite porque no damos un valor por defecto para dicho campo no nulo y los
registros
existentes necesitan cargar un valor.

7- Agregue el campo "sueldo" de tipo decimal(6,2) no nulo, una restricción "check" que
no permita
valores negativos para dicho campo y una restricción "default" que almacene el valor
"0":
alter table empleados
add sueldo decimal(6,2) not null
constraint CK_empleados_sueldo check (sueldo>=0)
constraint DF_empleados_sueldo default 0;

8- Recupere los registros:


select *from empleados;

9- Vea la nueva estructura de la tabla:


sp_columns empleados;

10- Vea las restricciones:


264
sp_helpconstraint empleados;

86 - Campos calculados
Un campo calculado es un campo que no se almacena físicamente en la tabla. SQL Server
emplea una fórmula que detalla el usuario al definir dicho campo para calcular el valor según
otros campos de la misma tabla.
Un campo calculado no puede:
- definirse como "not null".
- ser una subconsulta.
- tener restricción "default" o "foreign key".
- insertarse ni actualizarse.
Puede ser empleado como llave de un índice o parte de restricciones "primary key" o "unique" si
la expresión que la define no cambia en cada consulta.
Creamos un campo calculado denominado "sueldototal" que suma al sueldo básico 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)
);

También 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.
Trabajamos con la tablas "empleados".
Eliminamos la tabla, si existe, y la creamos:

if object_id('empleados') is not null


drop table empleados;

create table empleados(


documento char(8),
nombre varchar(10),
domicilio varchar(30),
265
sueldobasico decimal(6,2),
hijos tinyint not null default 0,
sueldototal as sueldobasico + (hijos*100)
);

El campo "sueldototal" es un campo calculado que suma al sueldo básico de cada empleado y la cantidad
abonada por los hijos (100 por cada hijo).
No puede ingresarse valor para dicho campo:
insert into empleados values('22222222','Juan Perez','Colon 123',300,2);
insert into empleados values('23333333','Ana Lopez','Sucre 234',500,0);

Veamos los registros:


select *from empleados;

Veamos lo que sucede si actualizamos un registro:


update empleados set hijos=1 where documento='23333333';
select *from empleados;

Recuperamos los registros:


select *from empleados;

el campo calculado "sueldototal" recalcula los valores para cada registro automáticamente.
Agregamos un campo calculado:
alter table empleados
add salariofamiliar as hijos*100;

Veamos la estructura de la tabla:


sp_columns empleados;

Recuperemos los registros:


select *from empleados;

Primer problema:

Un comercio almacena los datos de los artículos para la venta en una tabla denominada
"articulos".
1- Elimine la tabla, si existe y créela nuevamente:
if object_id('articulos') is not null
drop table articulos;

create table articulos(


codigo int identity,
descripcion varchar(30),
precio decimal(5,2) not null,
cantidad smallint not null default 0,
montototal as precio *cantidad
);
El campo "montototal" es un campo calculado que multiplica el precio de cada artículo
por la
cantidad disponible.

2- Intente ingresar un registro con valor para el campo calculado:

266
insert into articulos values('birome',1.5,100,150);
No lo permite.

3- Ingrese algunos registros:


insert into articulos values('birome',1.5,100);
insert into articulos values('cuaderno 12 hojas',4.8,150);
insert into articulos values('lapices x 12',5,200);

4- Recupere los registros:


select *from articulos;

5- Actualice un precio y recupere los registros:


update articulos set precio=2 where descripcion='birome';
select *from articulos;
el campo calculado "montototal" recalcula los valores para cada registro
automáticamente.

6- Actualice una cantidad y vea el resultado:


update articulos set cantidad=200 where descripcion='birome';
select *from articulos;
el campo calculado "montototal" recalcula sus valores.

7- Intente actualizar un campo calculado:


update articulos set montototal=300 where descripcion='birome';
No lo permite.

87 - Tipo de dato definido por el usuario (crear -


informacion)
Cuando definimos un campo de una tabla debemos especificar el tipo de datos, sabemos que
los tipos de datos especifican el tipo de información (caracteres, números, fechas) que pueden
almacenarse en un campo. SQL Server proporciona distintos tipos de datos del sistema (char,
varchar, int, decimal, datetime, etc.) y permite tipos de datos definidos por el usuario siempre
que se basen en los tipos de datos existentes.
Se pueden crear y eliminar tipos de datos definidos por el usuario.
Se emplean cuando varias tablas deben almacenar el mismo tipo de datos en un campo y se
quiere garantizar que todas tengan el mismo tipo y longitud.
Para darle un nombre a un tipo de dato definido por el usuario debe considerar las mismas reglas
que para cualquier identificador. No puede haber dos objetos con igual nombre en la misma base
de datos.
Para crear un tipo de datos definido por el usuario se emplea el procedimiento almacenado del
sistema "sp_addtype". Sintaxis básica:
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';
267
Ejecutando el procedimiento almacenado "sp_help" junto al nombre del tipo de dato definido por
el usuario se obtiene información 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).
También podemos consultar la tabla "systypes" en la cual se almacena información de todos los
tipos de datos:
select name from systypes;

Una academia de enseñanza almacena los datos de sus alumnos en una tabla llamada "alumnos".

Borramos la tabla alumno por si otro usuario creo tipos de datos definidos para la misma:
if object_id('alumnos') is not null
drop table alumnos;

Queremos definir un nuevo tipo de dato llamado "tipo_documento". Primero debemos eliminarlo, si existe
para volver a crearlo. Para ello empleamos esta sentencia que explicaremos en el siguiente capítulo:
if exists (select *from systypes
where name = 'tipo_documento')
exec sp_droptype tipo_documento;

Creamos un tipo de dato definido por el usuario llamado "tipo_documento" basado en el tipo "char" que
permita 8 caracteres y valores nulos:
exec sp_addtype tipo_documento, 'char(8)', 'null';

Ejecutamos el procedimiento almacenado "sp_help" junto al nombre del tipo de dato definido anteriormente
para obtener información del mismo:
sp_help tipo_documento;

Aparecen varias columnas: el nombre, el tipo de dato en que se basa, la longitud, si acepta valores nulos; las
columnas "default_name" y "rule_name" muestran "none" porque no tiene valores predeterminados ni reglas
asociados.
Creamos la tabla "alumnos" con 2 campos: documento (del tipo de dato definido anteriormente) y nombre
(30 caracteres). Antes la eliminamos (si existe):
create table alumnos(
documento tipo_documento,
nombre varchar(30)
);

Ingresamos un registro con valor para "documento" permitido por el tipo de dato:
insert into alumnos values('12345678','Ana Acosta');

No podríamos ingresar en "documento" una cadena de 9 caracteres.


Primer problema:

Un comercio almacena los datos de sus empleados en una tabla denominada "empleados".
1- Elimine la tabla si existe:
if object_id ('empleados') is not null
drop table empleados;

268
2- Defina un nuevo tipo de dato llamado "tipo_legajo". Primero debe eliminarlo (si
existe) para
volver a crearlo. Para ello emplee esta sentencia que explicaremos en el siguiente
capítulo:
if exists (select name from systypes
where name = 'tipo_legajo')
exec sp_droptype tipo_legajo;

3- Cree un tipo de dato definido por el usuario llamado "tipo_legajo" basado en el tipo
"char" de 4
caracteres que no permita valores nulos.

4- Ejecute el procedimiento almacenado "sp_help" junto al nombre del tipo de dato


definido
anteriormente para obtener información del mismo.

5- Cree la tabla "empleados" con 3 campos: legajo (tipo_legajo), documento (char de 8)


y nombre (30
caracteres):
create table empleados(
legajo tipo_legajo,
documento char(8),
nombre varchar(30)
);

6- Intente ingresar un registro con valores por defecto:


insert into empleados default values;
No se puede porque el campo "tipo_legajo" no admite valores nulos y no tiene definido
un valor por
defecto.

7- Ingrese un registro con valores válidos.

88 - Tipo de dato definido por el usuario


(asociación de reglas)
Se puede asociar una regla a un tipo de datos definido por el usuario. Luego de crear la regla se
establece la asociación; la sintaxis es la siguiente:
exec sp_bindrule NOMBREREGLA, 'TIPODEDATODEFINIDOPORELUSUARIO', 'futureonly';

El parámetro "futureonly" es opcional, especifica que si existen campos (de cualquier tabla) con
este tipo de dato, no se asocien a la regla; si creamos una nueva tabla con este tipo de dato, si
deberán cumplir la regla. Si no se especifica este parámetro, todos los campos de este tipo de
dato, existentes o que se creen posteriormente (de cualquier tabla), quedan asociados a la regla.
Recuerde que SQL Server NO controla los datos existentes para confirmar que cumplen con la
regla, si no los cumple, la regla se asocia igualmente; pero al ejecutar una instrucción "insert" o
"update" muestra un mensaje de error.
Si asocia una regla a un tipo de dato definido por el usuario que tiene otra regla asociada, esta
última la reemplaza.

269
Para quitar la asociación, 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 continúa 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 instrucción "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 información 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.
Una academia de enseñanza almacena los datos de sus alumnos en una tabla llamada "alumnos"
y en otra tabla denominada "docentes" los datos de los profesores.
Eliminamos ambas tablas, si existen:
if object_id('alumnos') is not null
drop table alumnos;
if object_id('docentes') is not null
drop table docentes;

Queremos definir un nuevo tipo de dato llamado "tipo_documento". Primero debemos eliminarlo, si existe
para volver a crearlo. Para ello empleamos esta sentencia que explicaremos próximamente:
if exists (select *from systypes
where name = 'tipo_documento')
exec sp_droptype tipo_documento;

Creamos un tipo de dato definido por el usuario llamado "tipo_documento" basado en el tipo "char" que
permita 8 caracteres y valores nulos:
exec sp_addtype tipo_documento, 'char(8)', 'null';

Ejecutamos el procedimiento almacenado "sp_help" junto al nombre del tipo de dato definido anteriormente
para obtener información del mismo:
sp_help tipo_documento;

Aparecen varias columnas que nos informan, entre otras cosas: el nombre (tipo_documento), el tipo de dato
en que se basa (char), la longitud (8), si acepta valores nulos (yes); las columnas "default_name" y
"rule_name" muestran "none" porque no tiene valores predeterminados ni reglas asociados.
Creamos la tabla "alumnos" con 2 campos: documento (del tipo de dato definido anteriormente) y nombre
(30 caracteres):
create table alumnos(

270
documento tipo_documento,
nombre varchar(30)
);

Eliminamos si existe, la regla "RG_documento":


if object_id ('RG_documento') is not null
drop rule RG_documento;

Creamos la regla que permita 8 caracteres que solamente pueden ser dígitos del 0 al 5 para el primer dígito y
de 0 al 9 para los siguientes:
create rule RG_documento
as @documento like '[0-5][0-9][0-9][0-9][0-9][0-9][0-9][0-9]';

Asociamos la regla al tipo de datos "tipo_documento" especificando que solamente se aplique a los futuros
campos de este tipo:
exec sp_bindrule RG_documento, 'tipo_documento', 'futureonly';

Ejecutamos el procedimiento almacenado "sp_helpconstraint" para verificar que no se aplicó a la tabla


"alumnos" porque especificamos la opción "futureonly":
sp_helpconstraint alumnos;

Creamos la tabla "docentes" con 2 campos: documento (del tipo de dato definido anteriormente) y nombre
(30 caracteres):
create table docentes(
documento tipo_documento,
nombre varchar(30)
);

Verificamos que se aplicó la regla en la nueva tabla:


sp_helpconstraint docentes;

Ingresamos registros en "alumnos" con valores para documento que infrinjan la regla:
insert into alumnos values('a111111','Ana Acosta');

Lo acepta porque en esta tabla no se aplica la regla. Pero no podríamos ingresar un valor como el anterior en
la tabla "docentes" la cual si tiene asociada la regla.
Quitamos la asociación:
exec sp_unbindrule 'tipo_documento';

Volvemos a asociar la regla, ahora sin el parámetro "futureonly":


exec sp_bindrule RG_documento, 'tipo_documento';

Note que hay valores que no cumplen la regla, recuerde que SQL Server NO controla los datos existentes al
momento de asociar una regla; pero si al ejecutar un "insert" o "update".
Verificamos que se aplicó la regla en ambas tablas:
sp_helpconstraint docentes;
exec sp_helpconstraint alumnos;

271
Eliminamos si existe, la regla "RG_documento2":
if object_id ('RG_documento2') is not null
drop rule RG_documento2;

Creamos la regla llamada "RG_documento2" que permita 8 caracteres que solamente pueden ser dígitos del
0 al 9 para todas las posiciones:
create rule RG_documento2
as @documento like '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]';

Asociamos la regla al tipo de datos "tipo_documento" (ya tiene una regla asociada):
exec sp_bindrule RG_documento2, 'tipo_documento';

Veamos si la asociación fue reemplazada en el tipo de datos:


sp_help tipo_documento;

Note que ahora en la columna "Rule_name" muestra "RG_documento2".


Veamos si la asociación fue reemplazada en las tablas:
sp_helpconstraint alumnos;
exec sp_helpconstraint docentes;

Note que ahora la regla asociada es "RG_documento2".


Asociamos la regla "RG_documento" al campo "documento" de "alumnos":
exec sp_bindrule RG_documento, 'alumnos.documento';

Verificamos que "documento" de "alumnos" tiene asociada la regla "RG_documento":


sp_helpconstraint alumnos;

Verificamos que el tipo de dato "tipo_documento" tiene asociada la regla "RG_documento2":


sp_help tipo_documento;

Intente ingresar un valor para "documento" aceptado por la regla asociada al tipo de dato pero no por la regla
asociada al campo:
insert into alumnos values ('77777777','Juan Lopez');

No lo permite.
Ingrese un valor para "documento" aceptado por la regla asociada al campo:
insert into alumnos values ('55555555','Juan Lopez');

Primer problema:
Un comercio almacena los datos de sus empleados en una tabla denominada "empleados" y
en otra
llamada "clientes" los datos de sus clientes".
1- Elimine ambas tablas, si existen:
if object_id ('empleados') is not null
drop table empleados;
if object_id ('clientes') is not null
drop table clientes;

272
2- Defina un nuevo tipo de dato llamado "tipo_año". Primero debe eliminarlo, si existe,
para volver
a crearlo. Para ello emplee esta sentencia que explicaremos en el siguiente capítulo:
if exists (select *from systypes
where name = 'tipo_año')
exec sp_droptype tipo_año;

3- Cree un tipo de dato definido por el usuario llamado "tipo_año" basado en el tipo
"int" que
permita valores nulos:
exec sp_addtype tipo_año, 'int','null';

4- Ejecute el procedimiento almacenado "sp_help" junto al nombre del tipo de dato


definido
anteriormente para obtener información del mismo:
sp_help tipo_año;
5- Cree la tabla "empleados" con 3 campos: documento (char de 8), nombre (30
caracteres) y
añoingreso (tipo_año):
create table empleados(
documento char(8),
nombre varchar(30),
añoingreso tipo_año
);

6- Elimine la regla llamada "RG_año" si existe:


if object_id ('RG_año') is not null
drop rule RG_año;

7- Cree la regla que permita valores integer desde 1990 (año en que se inauguró el
comercio) y el
año actual:
create rule RG_año
as @año between 1990 and datepart(year,getdate());

8- Asocie la regla al tipo de datos "tipo_año" especificando que solamente se aplique a


los futuros
campos de este tipo:
exec sp_bindrule RG_año, 'tipo_año', 'futureonly';

9- Vea si se aplicó a la tabla empleados:


sp_helpconstraint empleados;
No se aplicó porque especificamos la opción "futureonly":

10- Cree la tabla "clientes" con 3 campos: nombre (30 caracteres), añoingreso
(tipo_año) y domicilio
(30 caracteres):
create table clientes(
documento char(8),
nombre varchar(30),
añoingreso tipo_año
);

11- Vea si se aplicó la regla en la nueva tabla:


sp_helpconstraint clientes;
Si aparece.

12- Ingrese registros con valores para el año que infrinjan la regla en la tabla
"empleados":
insert into empleados values('11111111','Ana Acosta',2050);
select *from empleados;
Lo acepta porque en esta tabla no se aplica la regla.

13- Intente ingresar en la tabla "clientes" un valor de fecha que infrinja la regla:

273
insert into clientes values('22222222','Juan Perez',2050);
No lo permite.

14- Quite la asociación de la regla con el tipo de datos:


exec sp_unbindrule 'tipo_año';

15- Vea si se quitó la asociación:


sp_helpconstraint clientes;
Si se quitó.

16- Vuelva a asociar la regla, ahora sin el parámetro "futureonly":


exec sp_bindrule RG_año, 'tipo_año';
Note que hay valores que no cumplen la regla pero SQL Server NO lo verifica al momento
de asociar
una regla.

17- Intente agregar una fecha de ingreso fuera del intervalo que admite la regla en
cualquiera de
las tablas (ambas tienen la asociación):
insert into empleados values('33333333','Romina Guzman',1900);
Mensaje de error.

18- Vea la información del tipo de dato:


exec sp_help tipo_año;
En la columna que hace referencia a la regla asociada aparece "RG_año".

19- Elimine la regla llamada "RG_añonegativo", si existe:


if object_id ('RG_añonegativo') is not null
drop rule RG_añonegativo;

20- Cree una regla llamada "RG_añonegativo" que admita valores entre -2000 y -1:
create rule RG_añonegativo
as @año between -2000 and -1;

21- Asocie la regla "RG_añonegativo" al campo "añoingreso" de la tabla "clientes":


exec sp_bindrule RG_añonegativo, 'clientes.añoingreso';

22- Vea si se asoció:


sp_helpconstraint clientes;
Se asoció.

23- Verifique que no está asociada al tipo de datos "tipo_año":


sp_help tipo_año;
No, tiene asociada la regla "RG_año".

24- Intente ingresar un registro con valor '-1900' para el campo "añoingreso" de
"empleados":
insert into empleados values('44444444','Pedro Perez',-1900);
No lo permite por la regla asociada al tipo de dato.

25- Ingrese un registro con valor '-1900' para el campo "añoingreso" de "clientes" y
recupere los
registros de dicha tabla:
insert into clientes values('44444444','Pedro Perez',-1900);
select *from clientes;
Note que se ingreso, si bien el tipo de dato de "añoingreso" tiene asociada una regla
que no admite
tal valor, el campo tiene asociada una regla que si lo admite y ésta prevalece.

274
89 - Tipo de dato definido por el usuario (valores
predeterminados)
Se puede asociar un valor predeterminado a un tipo de datos definido por el usuario. Luego de
crear un valor predeterminado, se puede asociar a un tipo de dato definido por el usuario con la
siguiente sintaxis:
exec sp_bindefault NOMBREVALORPREDETERMINADO, 'TIPODEDATODEFINIDOPORELUSUARIO','futureonly';

El parámetro "futureonly" es opcional, especifica que si existen campos (de cualquier tabla) con
este tipo de dato, no se asocien al valor predeterminado; si creamos una nueva tabla con este
tipo de dato, si estará asociado al valor predeterminado. Si no se especifica este parámetro, todos
los campos de este tipo de dato, existentes o que se creen posteriormente (de cualquier tabla),
quedan asociados al valor predeterminado.
Si asocia un valor predeterminado a un tipo de dato definido por el usuario que tiene otro valor
predeterminado asociado, el último lo reemplaza.
Para quitar la asociación, empleamos el mismo procedimiento almacenado que aprendimos
cuando quitamos asociaciones a campos:
sp_unbindefault 'TIPODEDATODEFINIDOPORELUSUARIO';

Debe tener en cuenta que NO se puede aplicar una restricción "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 restricción "default" y luego se
asocia un valor predeterminado al tipo de dato, el valor predeterminado no queda asociado en el
campo que tiene la restricción "default".
Un tipo de dato definido por el usuario puede tener un solo valor predeterminado asociado.
Cuando obtenemos información 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 ningún valor predeterminado asociado.
Una academia de enseñanza almacena los datos de sus alumnos en una tabla llamada "alumnos"
y en otra tabla denominada "docentes" los datos de los profesores.
Eliminamos ambas tablas, si existen:
if object_id('alumnos') is not null
drop table alumnos;
if object_id('docentes') is not null
drop table docentes;

Queremos definir un nuevo tipo de dato llamado "tipo_documento". Primero debemos eliminarlo, si existe
para volver a crearlo. Para ello empleamos esta sentencia que explicaremos próximamente:
if exists (select *from systypes
where name = 'tipo_documento')
275
exec sp_droptype tipo_documento;

Creamos un tipo de dato definido por el usuario llamado "tipo_documento" basado en el tipo "char" que
permita 8 caracteres y valores nulos:
exec sp_addtype tipo_documento, 'char(8)', 'null';

Ejecutamos el procedimiento almacenado "sp_help" junto al nombre del tipo de dato definido anteriormente
para obtener información del mismo:
sp_help tipo_documento;

Aparecen varias columnas que nos informan, entre otras cosas: el nombre (tipo_documento), el tipo de dato
en que se basa (char), la longitud (8), si acepta valores nulos (yes); las columnas "default_name" y
"rule_name" muestran "none" porque no tiene valores predeterminados ni reglas asociados.
Creamos la tabla "alumnos" con 2 campos: documento (del tipo de dato definido anteriormente) y nombre
(30 caracteres):
create table alumnos(
documento tipo_documento,
nombre varchar(30)
);

Eliminamos si existe, el valor predeterminado "VP_documento0":


if object_id ('VP_documento0') is not null
drop default VP_documento0;

Creamos el valor predeterminado "VP_documento0" que almacene el valor '00000000':


create default VP_documento0
as '00000000';

Asociamos el valor predeterminado al tipo de datos "tipo_documento" especificando que solamente se


aplique a los futuros campos de este tipo:
exec sp_bindefault VP_documento0, 'tipo_documento', 'futureonly';

Ejecutamos el procedimiento almacenado "sp_helpconstraint" para verificar que no se aplicó a la tabla


"alumnos" porque especificamos la opción "futureonly":
sp_helpconstraint alumnos;

Creamos la tabla "docentes" con 2 campos: documento (del tipo de dato definido anteriormente) y nombre
(30 caracteres):
create table docentes(
documento tipo_documento,
nombre varchar(30)
);

Verificamos que se aplicó el valor predeterminado creado anteriormente al campo "documento" de la nueva
tabla:
sp_helpconstraint docentes;

Ingresamos un registro en "alumnos" sin valor para documento y vemos qué se almacenó:
276
insert into alumnos default values;
select *from alumnos;

En esta tabla no se aplica el valor predeterminado por ello almacena "null", que es el valor por defecto.
Si ingresamos en la tabla "docentes" un registro con valores por defecto:
insert into docentes default values;
select *from docentes;

Si se almacena el valor predeterminado porque está asociado.


Quitamos la asociación:
exec sp_unbindefault 'tipo_documento';

Volvemos a asociar el valor predeterminado, ahora sin el parámetro "futureonly":


exec sp_bindefault VP_documento0, 'tipo_documento';

Ingresamos un registro en "alumnos" y en "docentes" sin valor para documento y vemos qué se almacenó:
insert into alumnos default values;
select *from alumnos;
insert into docentes default values;
select *from docentes;

En ambas se almacenó '00000000'.


Eliminamos si existe, el valor predeterminado "VP_documentoDesconocido":
if object_id ('VP_documentoDesconocido') is not null
drop default VP_documentoDesconocido;

Creamos el valor predeterminado llamado "VP_documentoDesconocido" que almacene el valor 'SinDatos':


create default VP_documentoDesconocido
as 'SinDatos';

Asociamos el valor predeterminado al tipo de datos "tipo_documento" (ya tiene otro valor predeterminado
asociado):
exec sp_bindefault VP_DocumentoDesconocido, 'tipo_documento';

Veamos si la asociación fue reemplazada en el tipo de datos:


exec sp_help tipo_documento;

Note que ahora en la columna "default_name" muestra "VP_documentoDesconocido".


Veamos si la asociación fue reemplazada en la tabla "alumnos":
sp_helpconstraint alumnos;

Note que ahora el valor predeterminado asociado es "VP_documentoDesconocido".


Quitamos la asociación del valor predeterminado:
sp_unbindefault 'tipo_documento';

Veamos si se quitó de ambas tablas:


277
exec sp_helpconstraint alumnos;
exec sp_helpconstraint docentes;

Ingresamos un registro en "alumnos" y vemos qué se almacenó en el campo "documento":


insert into alumnos default values;
select *from alumnos;

Agregue a la tabla "docentes" una restricción "default" para el campo "documento":


alter table docentes
add constraint DF_docentes_documento
default '--------'
for documento;

Ingrese un registro en "docentes" con valores por defecto y vea qué se almacenó en "documento"
recuperando los registros:
insert into docentes default values;
select *from docentes;

Asocie el valor predeterminado "VP_documento0" al tipo de datos "tipo_documento":


exec sp_bindefault VP_documento0, 'tipo_documento';

Vea qué informa "sp_helpconstraint" acerca de la tabla "docentes":


sp_helpconstraint docentes;

Tiene asociado el valor por defecto establecido con la restricción "default".


Ingrese un registro en "docentes" con valores por defecto y vea qué se almacenó en "documento":
insert into docentes default values;
select *from docentes;

Note que guarda el valor por defecto establecido con la restricción.


Eliminamos la restricción:
alter table docentes
drop DF_docentes_documento;

Vea qué informa "sp_helpconstraint" acerca de la tabla "docentes":


sp_helpconstraint docentes;

No tiene ningún valor por defecto asociado.


Asociamos el valor predeterminado "VP_documento0" al tipo de datos "tipo_documento":
exec sp_bindefault VP_documento0, 'tipo_documento';

Intente agregar una restricción "default" al campo "documento" de "docentes":


alter table docentes
add constraint DF_docentes_documento
default '--------'
for documento;

SQL Server no lo permite porque el tipo de dato de ese campo ya tiene un valor predeterminado asociado.
278
Primer problema:
Un comercio almacena los datos de sus empleados en una tabla denominada "empleados" y
en otra
llamada "clientes" los datos de sus clientes".
1- Elimine ambas tablas, si existen:
if object_id ('empleados') is not null
drop table empleados;
if object_id ('clientes') is not null
drop table clientes;

2- Defina un nuevo tipo de dato llamado "tipo_año". Primero debe eliminarlo, si existe,
para volver
a crearlo. Para ello emplee esta sentencia que explicaremos en el siguiente capítulo:
if exists (select *from systypes
where name = 'tipo_año')
exec sp_droptype tipo_año;

3- Cree un tipo de dato definido por el usuario llamado "tipo_año" basado en el tipo
"int" que
permita valores nulos:
exec sp_addtype tipo_año, 'int','null';

4- Ejecute el procedimiento almacenado "sp_help" junto al nombre del tipo de dato


definido
anteriormente para obtener información del mismo:
sp_help tipo_año;

5- Cree la tabla "empleados" con 3 campos: documento (char de 8), nombre (30
caracteres) y
añoingreso (tipo_año):
create table empleados(
documento char(8),
nombre varchar(30),
añoingreso tipo_año
);

6- Elimine el valor predeterminado "VP_añoactual" si existe:


if object_id ('VP_añoactual') is not null
drop default VP_añoactual;

7- Cree el valor predeterminado "VP_añoactual" que almacene el año actual:


create default VP_añoactual
as datepart(year,getdate());

8- Asocie el valor predeterminado al tipo de datos "tipo_año" especificando que


solamente se aplique
a los futuros campos de este tipo:
exec sp_bindefault VP_añoactual, 'tipo_año', 'futureonly';

9- Vea si se aplicó a la tabla empleados:


sp_helpconstraint empleados;
No se aplicó porque especificamos la opción "futureonly":

10- Cree la tabla "clientes" con 3 campos: nombre (30 caracteres), añoingreso
(tipo_año) y domicilio
(30 caracteres):
create table clientes(
documento char(8),
nombre varchar(30),
añoingreso tipo_año
);

11- Vea si se aplicó la regla en la nueva tabla:


sp_helpconstraint clientes;

279
Si se aplicó.

12- Ingrese un registro con valores por defecto en la tabla "empleados" y vea qué se
almacenó en
"añoingreso":
insert into empleados default values;
select *from empleados;
Se almacenó "null" porque en esta tabla no se aplica el valor predeterminado.

13- Ingrese en la tabla "clientes" un registro con valores por defecto y recupere los
registros:
insert into clientes default values;
select *from clientes;
Se almacenó el valor predeterminado.

14- Elimine el valor predeterminado llamado "VP_año2000", si existe:


if object_id ('VP_año2000') is not null
drop default Vp_año2000;

15- Cree un valor predeterminado llamado "VP_año2000" con el valor 2000:


create default VP_año2000
as 2000;

16- Asócielo al tipo de dato definido sin especificar "futureonly":


exec sp_bindefault VP_año2000, 'tipo_año';

17- Verifique que se asoció a la tabla "empleados":


sp_helpconstraint empleados;

18- Verifique que reemplazó al valor predeterminado anterior en la tabla "clientes":


sp_helpconstraint clientes;

18- Ingrese un registro en ambas tablas con valores por defecto y vea qué se almacenó
en el año de
ingreso:
insert into empleados default values;
select *from empleados;
insert into clientes default values;
select *from clientes;

19- Vea la información del tipo de dato:


exec sp_help tipo_año;
La columna que hace referencia al valor predeterminado asociado muestra "VP_año2000".

20- Intente agregar a la tabla "empleados" una restricción "default":


alter table empleados
add constraint DF_empleados_año
default 1990
for añoingreso;
No lo permite porque el tipo de dato del campo ya tiene un valor predeterminado
asociado.

21- Quite la asociación del valor predeterminado al tipo de dato:


sp_unbindefault 'tipo_año';

22- Agregue a la tabla "empleados" una restricción "default":


alter table empleados
add constraint DF_empleados_año
default 1990
for añoingreso;

23- Asocie el valor predeterminado "VP_añoactual" al tipo de dato "tipo_año":


exec sp_bindefault VP_añoactual, 'tipo_año';

280
24- Verifique que el tipo de dato tiene asociado el valor predeterminado:
sp_help tipo_año;

25- Verifique que la tabla "clientes" tiene asociado el valor predeterminado:


sp_helpconstraint clientes;

26- Verifique que la tabla "empleados" no tiene asociado el valor predeterminado


"VP_añoactual"
asociado al tipo de dato y tiene la restricción "default":
sp_helpconstraint empleados;

90 - Tipo de dato definido por el usuario


(eliminar)
Podemos eliminar un tipo de dato definido por el usuario con el procedimiento almacenado
"sp_droptype":
exec sp_droptype TIPODEDATODEFINIDOPORELUSUARIO;

Eliminamos el tipo de datos definido por el usuario llamado "tipo_documento":


exec sp_droptype tipo_documento;

Si intentamos eliminar un tipo de dato inexistente, aparece un mensaje indicando que no existe.
Los tipos de datos definidos por el usuario se almacenan en la tabla del sistema "systypes".
Podemos averiguar si un tipo de dato definido por el usuario existe para luego eliminarlo:
if exists (select *from systypes
where name = 'NOMBRETIPODEDATODEFINIDOPORELUSUARIO')
exec sp_droptype TIPODEDATODEFINIDOPORELUSUARIO;

Consultamos la tabla "systypes" para ver si existe el tipo de dato "tipo_documento", si es así, lo
eliminamos:
if exists (select *from systypes
where name = 'tipo_documento')
exec sp_droptype tipo_documento;

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.
Una academia de enseñanza almacena los datos de sus alumnos en una tabla llamada
"alumnos".
Eliminamos la tabla si existe:
if object_id('alumnos') is not null
drop table alumnos;
281
Definimos un nuevo tipo de dato llamado "tipo_documento". Primero debemos eliminarlo, si existe, para
volver a crearlo:
if exists (select *from systypes
where name = 'tipo_documento')
exec sp_droptype tipo_documento;

Creamos un tipo de dato definido por el usuario llamado "tipo_documento" basado en el tipo "char" que
permita 8 caracteres y valores nulos:
exec sp_addtype tipo_documento, 'char(8)', 'null';

Eliminamos la regla "RG_documento" si existe:


if object_id ('RG_documento') is not null
drop rule RG_documento;

Creamos la regla que permita 8 caracteres que solamente serán dígitos:


create rule RG_documento
as @documento like '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]';

Asociamos la regla al tipo de datos "tipo_documento":


exec sp_bindrule RG_documento, 'tipo_documento';

Creamos la tabla "alumnos":


create table alumnos(
nombre varchar(30),
documento tipo_documento
);

No podemos eliminar el tipo de dato "tipo_documento" porque hay una tabla "alumnos" que lo utiliza.
Entonces eliminamos la tabla:
drop table alumnos;

Ahora podemos eliminar el tipo de datos:


exec sp_droptype tipo_documento;

Note que no quitamos la asociación de la regla al tipo de datos, pero la asociación se quitó al eliminar el tipo
de dato.
Volvemos a crear el tipo de dato:
exec sp_addtype tipo_documento, 'char(8)', 'null';

Note que no tiene reglas asociadas:


sp_help tipo_documento;

Asociamos la regla nuevamente:


exec sp_bindrule RG_documento, 'tipo_documento';

Note que no hemos vuelto a crear la regla porque la regla subsiste.


Primer problema:
282
Un comercio almacena los datos de sus empleados en una tabla denominada "empleados".
1- Elimine la tabla si existe:
if object_id ('empleados') is not null
drop table empleados;

2- Defina un nuevo tipo de dato llamado "tipo_año". Primero debe eliminarlo, si existe
para volver a
crearlo:
if exists (select *from systypes
where name = 'tipo_año')
exec sp_droptype tipo_año;

3- Cree un tipo de dato definido por el usuario llamado "tipo_año" basado en el tipo
"int" que
permita valores nulos:
exec sp_addtype tipo_año, 'int','null';

4- Elimine la regla llamada "RG_año" si existe:


if object_id ('RG_año') is not null
drop rule RG_año;

5- Cree la regla que permita valores integer desde 1990 (fecha en que se inauguró el
comercio) y el
año actual:
create rule RG_año
as @año between 1990 and datepart(year,getdate());

6- Asocie la regla al tipo de datos "tipo_año":


exec sp_bindrule RG_año, 'tipo_año';

7- Cree la tabla "empleados" con un campo del tipo creado anteriormente:


create table empleados(
documento char(8),
nombre varchar(30),
añoingreso tipo_año
);

8- Intente ingresar un registro con un valor inválido para el campo "añoingreso":


insert into empleados values('22222222','Juan Lopez',1980);
No lo permite.

9- Ingrese un registro con un valor válido para el campo "añoingreso":


insert into empleados values('22222222','Juan Lopez',2000);

10- Intente eliminar la regla asociada al tipo de datos:


drop rule RG_año;
No se puede porque está asociada a un tipo de datos.

11- Elimine la asociación:


exec sp_unbindrule 'tipo_año';

12- Verifique que la asociación ha sido eliminada pero la regla sigue existiendo:
sp_helpconstraint empleados;
exec sp_help tipo_año;

13- Elimine la regla:


drop rule RG_año;

14- Verifique que la regla ya no existe:


sp_help RG_año;

15- Ingrese el registro que no pudo ingresar en el punto 8:


insert into empleados values('22222222','Juan Lopez',1980);
Lo permite porque el tipo de dato ya no tiene asociada la regla.

283
16- Intente eliminar el tipo de datos "tipo_año":
exec sp_droptype tipo_año;
No lo permite porque hay una tabla que lo utiliza.

17- Elimine la tabla "empleados":


drop table empleados;

18- Verifique que el tipo de dato "tipo_año" aún existe:


sp_help tipo_año;

19- Elimine el tipo de datos:


exec sp_droptype tipo_año;

20- Verifique que el tipo de dato "tipo_año" ya no existe:


sp_help tipo_año;

91 - Subconsultas
Una subconsulta (subquery) es una sentencia "select" anidada en otra sentencia "select",
"insert", "update" o "delete" (o en otra subconsulta).
Las subconsultas se emplean cuando una consulta es muy compleja, entonces se la divide en
varios pasos lógicos y se obtiene el resultado con una única instrucción y cuando la consulta
depende de los resultados de otra consulta.
Generalmente, una subconsulta se puede reemplazar por combinaciones y estas últimas son más
eficientes.
Las subconsultas se DEBEN incluir entre paréntesis.
Puede haber subconsultas dentro de subconsultas, se admiten hasta 32 niveles de anidación.
Se pueden emplear subconsultas:
- en lugar de una expresión, siempre que devuelvan un solo valor o una lista de valores.
- que retornen un conjunto de registros de varios campos en lugar de una tabla o para obtener el
mismo resultado que una combinación (join).
Hay tres tipos básicos de subconsultas:
1. las que retornan un solo valor escalar que se utiliza con un operador de comparación o en
lugar de una expresión.
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 selección de una subconsulta que va luego de un operador de comparación puede
incluir sólo una expresión o campo (excepto si se emplea "exists" y "in").

284
- si el "where" de la consulta exterior incluye un campo, este debe ser compatible con el campo en
la lista de selección de la subconsulta.
- no se pueden emplear subconsultas que recuperen campos de tipos text o image.
- las subconsultas luego de un operador de comparación (que no es seguido por "any" o "all") no
pueden incluir cláusulas "group by" ni "having".
- "distinct" no puede usarse con subconsultas que incluyan "group by".
- no pueden emplearse las cláusulas "compute" y "compute by".
- "order by" puede emplearse solamente si se especifica "top" también.
- 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
serán incluidos en la salida (en la lista de selección de la consulta externa).

92 - Subconsultas como expresión


Una subconsulta puede reemplazar una expresión. Dicha subconsulta debe devolver un valor
escalar (o una lista de valores de un campo).
Las subconsultas que retornan un solo valor escalar se utiliza con un operador de comparación o
en lugar de una expresión:
select CAMPOS
from TABLA
where CAMPO OPERADOR (SUBCONSULTA);

select CAMPO OPERADOR (SUBCONSULTA)


from TABLA;

Si queremos saber el precio de un determinado libro y la diferencia con el precio del libro más
costoso, anteriormente debíamos averiguar en una consulta el precio del libro más costoso y
luego, en otra consulta, calcular la diferencia con el valor del libro que solicitamos. Podemos
conseguirlo en una sola sentencia combinando dos consultas:
select titulo,precio,
precio-(select max(precio) from libros) as diferencia
from libros
where titulo='Uno';

En el ejemplo anterior se muestra el título, el precio de un libro y la diferencia entre el precio del
libro y el máximo valor de precio.
Queremos saber el título, autor y precio del libro más costoso:
select titulo,autor, precio

285
from libros
where precio=
(select max(precio) from libros);

Note que el campo del "where" de la consulta exterior es compatible con el valor retornado por la
expresión de la subconsulta.
Se pueden emplear en "select", "insert", "update" y "delete".
Para actualizar un registro empleando subconsulta la sintaxis básica es la siguiente:
update TABLA set CAMPO=NUEVOVALOR
where CAMPO= (SUBCONSULTA);

Para eliminar registros empleando subconsulta empleamos la siguiente sintaxis básica:


delete from TABLA
where CAMPO=(SUBCONSULTA);

Recuerde que la lista de selección de una subconsulta que va luego de un operador de


comparación puede incluir sólo una expresión o campo (excepto si se emplea "exists" o "in").
No olvide que las subconsultas luego de un operador de comparación (que no es seguido por
"any" o "all") no pueden incluir cláusulas "group by".
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe y la creamos:
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)
);

Ingresamos los siguientes registros:


insert into libros values('Alicia en el pais de las maravillas','Lewis
Carroll','Emece',20.00);
insert into libros values('Alicia en el pais de las maravillas','Lewis
Carroll','Plaza',35.00);
insert into libros values('Aprenda PHP','Mario Molina','Siglo XXI',40.00);
insert into libros values('El aleph','Borges','Emece',10.00);
insert into libros values('Ilusiones','Richard Bach','Planeta',15.00);
insert into libros values('Java en 10 minutos','Mario Molina','Siglo XXI',50.00);
insert into libros values('Martin Fierro','Jose Hernandez','Planeta',20.00);
insert into libros values('Martin Fierro','Jose Hernandez','Emece',30.00);
insert into libros values('Uno','Richard Bach','Planeta',10.00);

Obtenemos el título, precio de un libro específico y la diferencia entre su precio y el máximo valor:
select titulo,precio,
precio-(select max(precio) from libros) as diferencia
from libros
where titulo='Uno';

Mostramos el título y precio del libro más costoso:


286
select titulo,autor, precio
from libros
where precio=
(select max(precio) from libros);

Actualizamos el precio del libro con máximo valor:


update libros set precio=45
where precio=
(select max(precio) from libros);

Eliminamos los libros con precio menor:


delete from libros
where precio=
(select min(precio) from libros);

Primer problema:
Un profesor almacena el documento, nombre y la nota final de cada alumno de su clase en
una tabla
llamada "alumnos".
1- Elimine la tabla, si existe:
if object_id('alumnos') is not null
drop table alumnos;

2- Créela con los campos necesarios. Agregue una restricción "primary key" para el
campo "documento"
y una "check" para validar que el campo "nota" se encuentre entre los valores 0 y 10:
create table alumnos(
documento char(8),
nombre varchar(30),
nota decimal(4,2),
primary key(documento),
constraint CK_alumnos_nota_valores check (nota>=0 and nota <=10),
);

3-Ingrese algunos registros:


insert into alumnos values('30111111','Ana Algarbe',5.1);
insert into alumnos values('30222222','Bernardo Bustamante',3.2);
insert into alumnos values('30333333','Carolina Conte',4.5);
insert into alumnos values('30444444','Diana Dominguez',9.7);
insert into alumnos values('30555555','Fabian Fuentes',8.5);
insert into alumnos values('30666666','Gaston Gonzalez',9.70);

4- Obtenga todos los datos de los alumnos con la nota más alta, empleando subconsulta.
2 registros.

5- realice la misma consulta anterior pero intente que la consulta interna retorne,
además del
máximo valor de precio, el título:
select titulo,autor, precio
from libros
where precio=
(select titulo, max(precio) from libros);
Mensaje de error, porque la lista de selección de una subconsulta que va luego de un
operador de
comparación puede incluir sólo un campo o expresión (excepto si se emplea "exists" o
"in").

6- Muestre los alumnos que tienen una nota menor al promedio, su nota, y la diferencia
con el
promedio.
3 registros.

287
7- Cambie la nota del alumno que tiene la menor nota por 4.
1 registro modificado.

8- Elimine los alumnos cuya nota es menor al promedio.


3 registros eliminados.

93 - Subconsultas con in
Vimos que una subconsulta puede reemplazar una expresión. Dicha subconsulta debe devolver
un valor escalar o una lista de valores de un campo; las subconsultas que retornan una lista de
valores reemplazan a una expresión en una cláusula "where" que contiene la palabra clave "in".
El resultado de una subconsulta con "in" (o "not in") es una lista. Luego que la subconsulta retorna
resultados, la consulta exterior los usa.
La sintaxis básica es la siguiente:
...where EXPRESION in (SUBCONSULTA);

Este ejemplo muestra los nombres de las editoriales que ha publicado libros de un determinado
autor:
select nombre
from editoriales
where codigo in
(select codigoeditorial
from libros
where autor='Richard Bach');

La subconsulta (consulta interna) retorna una lista de valores de un solo campo (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 combinación (join) siempre puede ser expresada como una subconsulta; pero una
subconsulta no siempre puede reemplazarse por una combinación que retorne el mismo
resultado. Si es posible, es aconsejable emplear combinaciones en lugar de subconsultas, son
más eficientes.
Se recomienda probar las subconsultas antes de incluirlas en una consulta exterior, así puede
verificar que retorna lo necesario, porque a veces resulta difícil verlo en consultas anidadas.
También podemos buscar valores No coincidentes con una lista de valores que retorna una
subconsulta; por ejemplo, las editoriales que no han publicado libros de un autor específico:
select nombre
288
from editoriales
where codigo not in
(select codigoeditorial
from libros
where autor='Richard Bach');

Trabajamos con las tablas "libros" y "editoriales" de una librería.


Eliminamos las tablas si existen y las creamos:
if object_id('libros') is not null
drop table libros;
if object_id('editoriales') is not null
drop table editoriales;

create table editoriales(


codigo tinyint identity,
nombre varchar(30),
primary key (codigo)
);

create table libros (


codigo int identity,
titulo varchar(40),
autor varchar(30),
codigoeditorial tinyint,
primary key(codigo),
constraint FK_libros_editorial
foreign key (codigoeditorial)
references editoriales(codigo)
on update cascade,
);

Ingresamos algunos registros:


insert into editoriales values('Planeta');
insert into editoriales values('Emece');
insert into editoriales values('Paidos');
insert into editoriales values('Siglo XXI');

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


insert into libros values('Ilusiones','Richard Bach',1);
insert into libros values('Aprenda PHP','Mario Molina',4);
insert into libros values('El aleph','Borges',2);
insert into libros values('Puente al infinito','Richard Bach',2);

Queremos conocer el nombre de las editoriales que han publicado libros del autor "Richard Bach":
select nombre
from editoriales
where codigo in
(select codigoeditorial
from libros
where autor='Richard Bach');

Probamos la subconsulta separada de la consulta exterior para verificar que retorna una lista de valores de un
solo campo:
select codigoeditorial
from libros
where autor='Richard Bach';

Podemos reemplazar por un "join" la primera consulta:


289
select distinct nombre
from editoriales as e
join libros
on codigoeditorial=e.codigo
where autor='Richard Bach';

También podemos buscar las editoriales que no han publicado libros de "Richard Bach":
select nombre
from editoriales
where codigo not in
(select codigoeditorial
from libros
where autor='Richard Bach');

Primer problema:
Una empresa tiene registrados sus clientes en una tabla llamada "clientes", también
tiene una tabla
"ciudades" donde registra los nombres de las ciudades.
1- Elimine las tablas "clientes" y "ciudades", si existen:
if (object_id('ciudades')) is not null
drop table ciudades;
if (object_id('clientes')) is not null
drop table clientes;

2- Cree la tabla "clientes" (codigo, nombre, domicilio, ciudad, codigociudad) y


"ciudades" (codigo,
nombre). Agregue una restricción "primary key" para el campo "codigo" de ambas tablas y
una "foreing
key" para validar que el campo "codigociudad" exista en "ciudades" con actualización en
cascada:
create table ciudades(
codigo tinyint identity,
nombre varchar(20),
primary key (codigo)
);

create table clientes (


codigo int identity,
nombre varchar(30),
domicilio varchar(30),
codigociudad tinyint not null,
primary key(codigo),
constraint FK_clientes_ciudad
foreign key (codigociudad)
references ciudades(codigo)
on update cascade,
);

3- Ingrese algunos registros para ambas tablas:


insert into ciudades (nombre) values('Cordoba');
insert into ciudades (nombre) values('Cruz del Eje');
insert into ciudades (nombre) values('Carlos Paz');
insert into ciudades (nombre) values('La Falda');
insert into ciudades (nombre) values('Villa Maria');

insert into clientes values ('Lopez Marcos','Colon 111',1);


insert into clientes values ('Lopez Hector','San Martin 222',1);
insert into clientes values ('Perez Ana','San Martin 333',2);
insert into clientes values ('Garcia Juan','Rivadavia 444',3);
insert into clientes values ('Perez Luis','Sarmiento 555',3);
insert into clientes values ('Gomez Ines','San Martin 666',4);
insert into clientes values ('Torres Fabiola','Alem 777',5);
insert into clientes values ('Garcia Luis','Sucre 888',5);
290
4- Necesitamos conocer los nombres de las ciudades de aquellos clientes cuyo domicilio
es en calle
"San Martin", empleando subconsulta.
3 registros.

5- Obtenga la misma salida anterior pero empleando join.

6- Obtenga los nombre de las ciudades de los clientes cuyo apellido no comienza con una
letra
específica, empleando subconsulta.
2 registros.

7- Pruebe la subconsulta del punto 6 separada de la consulta exterior para verificar


que retorna una
lista de valores de un solo campo.

4 - Subconsultas any - some - all


"any" y "some" son sinónimos. Chequean si alguna fila de la lista resultado de una subconsulta
se encuentra el valor especificado en la condición.
Compara un valor escalar con los valores de un campo y devuelven "true" si la comparación con
cada valor de la lista de la subconsulta es verdadera, sino "false".
El tipo de datos que se comparan deben ser compatibles.
La sintaxis básica es:
...VALORESCALAR OPERADORDECOMPARACION
ANY (SUBCONSULTA);

Queremos saber los títulos de los libros de "Borges" que pertenecen a editoriales que han
publicado también libros de "Richard Bach", es decir, si los libros de "Borges" coinciden con
ALGUNA de las editoriales que publicó libros de "Richard Bach":
select titulo
from libros
where autor='Borges' and
codigoeditorial = any
(select e.codigo
from editoriales 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 devolviendo los títulos de "Borges" que coinciden.
"all" también compara un valor escalar con una serie de valores. Chequea si TODOS los valores
de la lista de la consulta externa se encuentran en la lista de valores devuelta por la consulta

291
interna.
Sintaxis:
VALORESCALAR OPERADORDECOMPARACION all (SUBCONSULTA);

Queremos saber si TODAS las editoriales que publicaron libros de "Borges" coinciden con
TODAS las editoriales que publicaron libros de "Richard Bach":
select titulo
from libros
where autor='Borges' and
codigoeditorial = all
(select e.codigo
from editoriales 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 títulos.
Veamos otro ejemplo con un operador de comparación diferente:
Queremos saber si ALGUN precio de los libros de "Borges" es mayor a ALGUN precio de los
libros de "Richard Bach":
select titulo,precio
from libros
where autor='Borges' and
precio > any
(select precio
from libros
where autor='Bach');

El precio de cada libro de "Borges" es comparado con cada valor de la lista de valores retornada
por la subconsulta; si ALGUNO cumple la condición, es decir, es mayor a ALGUN precio de
"Richard Bach", se lista.
Veamos la diferencia si empleamos "all" en lugar de "any":
select titulo,precio
from libros
where autor='borges' and
precio > all
(select precio
from libros
where autor='bach');

El precio de cada libro de "Borges" es comparado con cada valor de la lista de valores retornada
por la subconsulta; si cumple la condición, es decir, si es mayor a TODOS los precios de "Richard
Bach" (o al mayor), se lista.
Emplear "= any" es lo mismo que emplear "in".
Emplear "<> all" es lo mismo que emplear "not in".
Recuerde que solamente las subconsultas luego de un operador de comparación al cual es
seguido por "any" o "all") pueden incluir cláusulas "group by".
292
Trabajamos con las tablas "libros" y "editoriales" de una librería.
Eliminamos las tablas si existen y las creamos:
if object_id('libros') is not null
drop table libros;
if object_id('editoriales') is not null
drop table editoriales;

create table editoriales(


codigo tinyint identity,
nombre varchar(30),
primary key (codigo)
);

create table libros (


codigo int identity,
titulo varchar(40),
autor varchar(30),
codigoeditorial tinyint,
precio decimal(5,2),
primary key(codigo),
constraint FK_libros_editorial
foreign key (codigoeditorial)
references editoriales(codigo)
on update cascade,
);

Ingresamos algunos registros:


insert into editoriales values('Planeta');
insert into editoriales values('Emece');
insert into editoriales values('Paidos');
insert into editoriales values('Siglo XXI');

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


insert into libros values('Ilusiones','Richard Bach',4,18);
insert into libros values('Puente al infinito','Richard Bach',2,20);
insert into libros values('Aprenda PHP','Mario Molina',4,40);
insert into libros values('El aleph','Borges',2,10);
insert into libros values('Antología','Borges',1,20);
insert into libros values('Cervantes y el quijote','Borges',3,25);

Mostramos los títulos de los libros de "Borges" de editoriales que han publicado también libros de "Richard
Bach":
select titulo
from libros
where autor like '%Borges%' and
codigoeditorial = any
(select e.codigo
from editoriales as e
join libros as l
on codigoeditorial=e.codigo
where l.autor like '%Bach%');

Realizamos la misma consulta pero empleando "all" en lugar de "any":


select titulo
from libros
where autor like '%Borges%' and
codigoeditorial = all
(select e.codigo
from editoriales as e
293
join libros as l
on codigoeditorial=e.codigo
where l.autor like '%Bach%');

Mostramos los títulos y precios de los libros "Borges" cuyo precio supera a ALGUN precio de los libros de
"Richard Bach":
select titulo,precio
from libros
where autor like '%Borges%' and
precio > any
(select precio
from libros
where autor like '%Bach%');

Veamos la diferencia si empleamos "all" en lugar de "any":


select titulo,precio
from libros
where autor like '%Borges%' and
precio > all
(select precio
from libros
where autor like '%Bach%');

Empleamos la misma subconsulta para eliminación:


delete from libros
where autor like '%Borges%' and
precio > all
(select precio
from libros
where autor like '%Bach%');

95 - Subconsultas correlacionadas
Un almacén almacena la información de sus ventas en una tabla llamada "facturas" en la cual
guarda el número de factura, la fecha y el nombre del cliente y una tabla denominada "detalles"
en la cual se almacenan los distintos items correspondientes a cada factura: el nombre del
artículo, el precio (unitario) y la cantidad.
Se necesita una lista de todas las facturas que incluya el número, la fecha, el cliente, la cantidad
de artículos comprados y el total:
select f.*,
(select count(d.numeroitem)
from Detalles 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 número de factura lo toma del "select" exterior); el tercer "select" retorna una lista de
294
valores de una sola columna con el total por factura (el número de factura lo toma del "select"
exterior); el primer "select" (externo) devuelve todos los datos de cada factura.
A este tipo de subconsulta se la denomina consulta correlacionada. La consulta interna se evalúa
tantas veces como registros tiene la consulta externa, se realiza la subconsulta para cada registro
de la consulta externa. El campo de la tabla dentro de la subconsulta (f.numero) se compara con
el campo de la tabla externa.
En este caso, específicamente, la consulta externa pasa un valor de "numero" a la consulta
interna. La consulta interna toma ese valor y determina si existe en "detalles", si existe, la
consulta interna devuelve la suma. El proceso se repite para el registro de la consulta externa, la
consulta externa pasa otro "numero" a la consulta interna y SQL Server repite la evaluación.
Un almacén almacena la información de sus ventas en una tabla llamada "facturas" en la cual
guarda el número de factura, la fecha y el nombre del cliente y una tabla denominada "detalles"
en la cual se almacenan los distintos items correspondientes a cada factura: el nombre del
artículo, el precio (unitario) y la cantidad.
Eliminamos las tablas si existen:
if object_id('detalles') is not null
drop table detalles;
if object_id('facturas') is not null
drop table facturas;

Las creamos con las siguientes estructuras:


create table facturas(
numero int not null,
fecha datetime,
cliente varchar(30),
primary key(numero)
);

create table detalles(


numerofactura int not null,
numeroitem int not null,
articulo varchar(30),
precio decimal(5,2),
cantidad int,
primary key(numerofactura,numeroitem),
constraint FK_detalles_numerofactura
foreign key (numerofactura)
references facturas(numero)
on update cascade
on delete cascade,
);

Ingresamos algunos registros:


insert into facturas values(1200,'2007-01-15','Juan Lopez');
insert into facturas values(1201,'2007-01-15','Luis Torres');
insert into facturas values(1202,'2007-01-15','Ana Garcia');
insert into facturas values(1300,'2007-01-20','Juan Lopez');

insert into detalles values(1200,1,'lapiz',1,100);


insert into detalles values(1200,2,'goma',0.5,150);
insert into detalles values(1201,1,'regla',1.5,80);
insert into detalles values(1201,2,'goma',0.5,200);
insert into detalles values(1201,3,'cuaderno',4,90);
295
insert into detalles values(1202,1,'lapiz',1,200);
insert into detalles values(1202,2,'escuadra',2,100);
insert into detalles values(1300,1,'lapiz',1,300);

Se necesita una lista de todas las facturas que incluya el número, la fecha, el cliente, la cantidad de artículos
comprados y el total:
select f.*,
(select count(d.numeroitem)
from detalles as d
where f.numero=d.numerofactura) as cantidad,
(select sum(d.precio*cantidad)
from detalles as d
where f.numero=d.numerofactura) as total
from facturas as f;

Primer problema:
Un club dicta clases de distintos deportes a sus socios. El club tiene una tabla
llamada
"inscriptos" en la cual almacena el número de "socio", el código del deporte en el cual
se inscribe
y la cantidad de cuotas pagas (desde 0 hasta 10 que es el total por todo el año), y una
tabla
denominada "socios" en la que guarda los datos personales de cada socio.
1- Elimine las tablas si existen:
if object_id('inscriptos') is not null
drop table inscriptos;
if object_id('socios') is not null
drop table socios;
2- Cree las tablas:
create table socios(
numero int identity,
documento char(8),
nombre varchar(30),
domicilio varchar(30),
primary key (numero)
);

create table inscriptos (


numerosocio int not null,
deporte varchar(20) not null,
cuotas tinyint
constraint CK_inscriptos_cuotas
check (cuotas>=0 and cuotas<=10)
constraint DF_inscriptos_cuotas default 0,
primary key(numerosocio,deporte),
constraint FK_inscriptos_socio
foreign key (numerosocio)
references socios(numero)
on update cascade
on delete cascade,
);

3- Ingrese algunos registros:


insert into socios values('23333333','Alberto Paredes','Colon 111');
insert into socios values('24444444','Carlos Conte','Sarmiento 755');
insert into socios values('25555555','Fabian Fuentes','Caseros 987');
insert into socios values('26666666','Hector Lopez','Sucre 344');

insert into inscriptos values(1,'tenis',1);


insert into inscriptos values(1,'basquet',2);
insert into inscriptos values(1,'natacion',1);
insert into inscriptos values(2,'tenis',9);

296
insert into inscriptos values(2,'natacion',1);
insert into inscriptos values(2,'basquet',default);
insert into inscriptos values(2,'futbol',2);
insert into inscriptos values(3,'tenis',8);
insert into inscriptos values(3,'basquet',9);
insert into inscriptos values(3,'natacion',0);
insert into inscriptos values(4,'basquet',10);

4- Se necesita un listado de todos los socios que incluya nombre y domicilio, la


cantidad de
deportes a los cuales se ha inscripto, empleando subconsulta.
4 registros.

5- Se necesita el nombre de todos los socios, el total de cuotas que debe pagar (10 por
cada
deporte) y el total de cuotas pagas, empleando subconsulta.
4 registros.

6- Obtenga la misma salida anterior empleando join.

96 - 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 ningún registro, es como un test de existencia; SQL
Server termina la recuperación de registros cuando por lo menos un registro cumple la condición
"where" de la subconsulta.
La sintaxis básica es la siguiente:
... where exists (SUBCONSULTA);

En este ejemplo se usa una subconsulta correlacionada con un operador "exists" en la cláusula
"where" para devolver una lista de clientes que compraron el artículo "lapiz":
select cliente,numero
from facturas 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 combinación.


Podemos buscar los clientes que no han adquirido el artículo "lapiz" empleando "if not exists":
select cliente,numero
297
from facturas as f
where not exists
(select *from Detalles as d
where f.numero=d.numerofactura
and d.articulo='lapiz');

Un comercio que vende artículos de librería y papelería almacena la información de sus ventas en una tabla
llamada "facturas" y otra "detalles".
Eliminamos las tablas si existen:

if object_id('detalles') is not null


drop table detalles;
if object_id('facturas') is not null
drop table facturas;

Las creamos con las siguientes estructuras:


create table facturas(
numero int not null,
fecha datetime,
cliente varchar(30),
primary key(numero)
);

create table detalles(


numerofactura int not null,
numeroitem int not null,
articulo varchar(30),
precio decimal(5,2),
cantidad int,
primary key(numerofactura,numeroitem),
constraint FK_detalles_numerofactura
foreign key (numerofactura)
references facturas(numero)
on update cascade
on delete cascade
);

Ingresamos algunos registros:


insert into facturas values(1200,'2007-01-15','Juan Lopez');
insert into facturas values(1201,'2007-01-15','Luis Torres');
insert into facturas values(1202,'2007-01-15','Ana Garcia');
insert into facturas values(1300,'2007-01-20','Juan Lopez');

insert into detalles values(1200,1,'lapiz',1,100);


insert into detalles values(1200,2,'goma',0.5,150);
insert into detalles values(1201,1,'regla',1.5,80);
insert into detalles values(1201,2,'goma',0.5,200);
insert into detalles values(1201,3,'cuaderno',4,90);
insert into detalles values(1202,1,'lapiz',1,200);
insert into detalles values(1202,2,'escuadra',2,100);
insert into detalles values(1300,1,'lapiz',1,300);

Empleamos una subconsulta correlacionada con un operador "exists" en la cláusula "where" para devolver la
lista de clientes que compraron el artículo "lapiz":
select cliente,numero
from facturas as f
where exists
(select *from detalles as d

298
where f.numero=d.numerofactura
and d.articulo='lapiz');

Buscamos los clientes que NO han comprado el artículo "lapiz":


select cliente,numero
from facturas as f
where not exists
(select *from detalles as d
where f.numero=d.numerofactura
and d.articulo='lapiz');

Primer problema:

Un club dicta clases de distintos deportes a sus socios. El club tiene una tabla
llamada
"inscriptos" en la cual almacena el número de "socio", el código del deporte en el cual
se inscribe
y la cantidad de cuotas pagas (desde 0 hasta 10 que es el total por todo el año), y una
tabla
denominada "socios" en la que guarda los datos personales de cada socio.
1- Elimine las tablas si existen:
if object_id('inscriptos') is not null
drop table inscriptos;
if object_id('socios') is not null
drop table socios;

2- Cree las tablas:


create table socios(
numero int identity,
documento char(8),
nombre varchar(30),
domicilio varchar(30),
primary key (numero)
);

create table inscriptos (


numerosocio int not null,
deporte varchar(20) not null,
cuotas tinyint
constraint CK_inscriptos_cuotas
check (cuotas>=0 and cuotas<=10)
constraint DF_inscriptos_cuotas default 0,
primary key(numerosocio,deporte),
constraint FK_inscriptos_socio
foreign key (numerosocio)
references socios(numero)
on update cascade
on delete cascade,
);

3- Ingrese algunos registros:


insert into socios values('23333333','Alberto Paredes','Colon 111');
insert into socios values('24444444','Carlos Conte','Sarmiento 755');
insert into socios values('25555555','Fabian Fuentes','Caseros 987');
insert into socios values('26666666','Hector Lopez','Sucre 344');

insert into inscriptos values(1,'tenis',1);


insert into inscriptos values(1,'basquet',2);
insert into inscriptos values(1,'natacion',1);
insert into inscriptos values(2,'tenis',9);
insert into inscriptos values(2,'natacion',1);
insert into inscriptos values(2,'basquet',default);

299
insert into inscriptos values(2,'futbol',2);
insert into inscriptos values(3,'tenis',8);
insert into inscriptos values(3,'basquet',9);
insert into inscriptos values(3,'natacion',0);
insert into inscriptos values(4,'basquet',10);

4- Emplee una subconsulta con el operador "exists" para devolver la lista de socios que
se
inscribieron en un determinado deporte.
3 registros.

5- Busque los socios que NO se han inscripto en un deporte determinado empleando "not
exists".
1 registro.

6- Muestre todos los datos de los socios que han pagado todas las cuotas.
1 registro.

97 - Subconsulta simil autocombinación


Algunas sentencias en las cuales la consulta interna y la externa emplean la misma tabla
pueden reemplazarse por una autocombinación.
Por ejemplo, queremos una lista de los libros que han sido publicados por distintas editoriales.
select distinct l1.titulo
from libros as l1
where l1.titulo in
(select l2.titulo
from libros as l2
where l1.editorial <> l2.editorial);

En el ejemplo anterior empleamos una subconsulta correlacionada y las consultas interna y


externa emplean la misma tabla. La subconsulta devuelve una lista de valores por ello se emplea
"in" y sustituye una expresión en una cláusula "where".
Con el siguiente "join" se obtiene el mismo resultado:
select distinct l1.titulo
from libros as l1
join libros as l2
on l1.titulo=l1.titulo and
l1.autor=l2.autor
where l1.editorial<>l2.editorial;

Otro ejemplo: Buscamos todos los libros que tienen el mismo precio que "El aleph" empleando
subconsulta:
select titulo
from libros
where titulo<>'El aleph' and
precio =
(select precio
from libros
where titulo='El aleph');

300
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 evalúa la subconsulta, si el precio es mayor que el promedio.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe y la creamos:

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)
);

Ingresamos los siguientes registros:


insert into libros values('Alicia en el pais de las maravillas','Lewis
Carroll','Emece',20.00);
insert into libros values('Alicia en el pais de las maravillas','Lewis
Carroll','Plaza',35.00);
insert into libros values('Aprenda PHP','Mario Molina','Siglo XXI',40.00);
insert into libros values('El aleph','Borges','Emece',10.00);
insert into libros values('Ilusiones','Richard Bach','Planeta',15.00);
insert into libros values('Java en 10 minutos','Mario Molina','Siglo XXI',50.00);
insert into libros values('Martin Fierro','Jose Hernandez','Planeta',20.00);
insert into libros values('Martin Fierro','Jose Hernandez','Emece',30.00);
insert into libros values('Uno','Richard Bach','Planeta',10.00);

Obtenemos la lista de los libros que han sido publicados por distintas editoriales empleando una consulta
correlacionada:
select distinct l1.titulo
from libros as l1
where l1.titulo in
(select l2.titulo
from libros as l2
where l1.editorial <> l2.editorial);

El siguiente "join" retorna el mismo resultado:


select distinct l1.titulo
from libros as l1
join libros as l2
on l1.titulo=l2.titulo
where l1.editorial<>l2.editorial;

Buscamos todos los libros que tienen el mismo precio que "El aleph" empleando subconsulta:

301
select titulo
from libros
where titulo<>'El aleph' and
precio =
(select precio
from libros
where titulo='El aleph');

Obtenemos la misma salida empleando "join":


select l1.titulo
from libros as l1
join libros as l2
on l1.precio=l2.precio
where l2.titulo='el aleph' and
l1.titulo<>l2.titulo;

Buscamos los libros cuyo precio supera el precio promedio de los libros por editorial:
select l1.titulo,l1.editorial,l1.precio
from libros as l1
where l1.precio >
(select avg(l2.precio)
from libros as l2
where l1.editorial= l2.editorial);

Obtenemos la misma salida pero empleando un "join" con "having":


select l1.editorial,l1.titulo,l1.precio
from libros as l1
join libros as l2
on l1.editorial=l2.editorial
group by l1.editorial, l1.titulo, l1.precio
having l1.precio > avg(l2.precio);

Primer problema:

Un club dicta clases de distintos deportes a sus socios. El club tiene una tabla
llamada "deportes"
en la cual almacena el nombre del deporte, el nombre del profesor que lo dicta, el día
de la semana
que se dicta y el costo de la cuota mensual.
1- Elimine la tabla si existe:
if object_id('deportes') is not null
drop table deportes;

2- Cree la tabla:
create table deportes(
nombre varchar(15),
profesor varchar(30),
dia varchar(10),
cuota decimal(5,2),
);

3- Ingrese algunos registros. Incluya profesores que dicten más de un curso:


insert into deportes values('tenis','Ana Lopez','lunes',20);
insert into deportes values('natacion','Ana Lopez','martes',15);
insert into deportes values('futbol','Carlos Fuentes','miercoles',10);
insert into deportes values('basquet','Gaston Garcia','jueves',15);
insert into deportes values('padle','Juan Huerta','lunes',15);
insert into deportes values('handball','Juan Huerta','martes',10);

302
4- Muestre los nombres de los profesores que dictan más de un deporte empleando
subconsulta.

5- Obtenga el mismo resultado empleando join.

6- Buscamos todos los deportes que se dictan el mismo día que un determinado deporte
(natacion)
empleando subconsulta.

7- Obtenga la misma salida empleando "join".

98 - Subconsulta en lugar de una tabla


Se pueden emplear subconsultas que retornen un conjunto de registros de varios campos en
lugar de una tabla.
Se la denomina tabla derivada y se coloca en la cláusula "from" para que la use un "select"
externo.
La tabla derivada debe ir entre paréntesis y tener un alias para poder referenciarla. La sintaxis
básica es la siguiente:
select ALIASdeTABLADERIVADA.CAMPO
from (TABLADERIVADA) as ALIAS;

La tabla derivada es una subsonsulta.


Podemos probar la consulta que retorna la tabla derivada y luego agregar el "select" externo:
select f.*,
(select sum(d.precio*cantidad)
from Detalles as d
where f.numero=d.numerofactura) as total
from facturas as f;

La consulta anterior contiene una subconsulta correlacionada; retorna todos los datos de
"facturas" y el monto total por factura de "detalles". Esta consulta retorna varios registros y varios
campos y será la tabla derivada que emplearemos en la siguiente consulta:
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 número 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.

303
Un comercio que vende artículos de librería y papelería almacena la información de sus ventas en una tabla
llamada "facturas", otra "detalles" y otra "clientes".
Eliminamos las tablas si existen:

if object_id('detalles') is not null


drop table detalles;
if object_id('facturas') is not null
drop table facturas;
if object_id('clientes') is not null
drop table clientes;

Las creamos con las siguientes estructuras:


create table clientes(
codigo int identity,
nombre varchar(30),
domicilio varchar(30),
primary key(codigo)
);

create table facturas(


numero int not null,
fecha datetime,
codigocliente int not null,
primary key(numero),
constraint FK_facturas_cliente
foreign key (codigocliente)
references clientes(codigo)
on update cascade
);

create table detalles(


numerofactura int not null,
numeroitem int not null,
articulo varchar(30),
precio decimal(5,2),
cantidad int,
primary key(numerofactura,numeroitem),
constraint FK_detalles_numerofactura
foreign key (numerofactura)
references facturas(numero)
on update cascade
on delete cascade,
);

Ingresamos algunos registros:


insert into clientes values('Juan Lopez','Colon 123');
insert into clientes values('Luis Torres','Sucre 987');
insert into clientes values('Ana Garcia','Sarmiento 576');

insert into facturas values(1200,'2007-01-15',1);


insert into facturas values(1201,'2007-01-15',2);
insert into facturas values(1202,'2007-01-15',3);
insert into facturas values(1300,'2007-01-20',1);

insert into detalles values(1200,1,'lapiz',1,100);


insert into detalles values(1200,2,'goma',0.5,150);
insert into detalles values(1201,1,'regla',1.5,80);
insert into detalles values(1201,2,'goma',0.5,200);
insert into detalles values(1201,3,'cuaderno',4,90);

304
insert into detalles values(1202,1,'lapiz',1,200);
insert into detalles values(1202,2,'escuadra',2,100);
insert into detalles values(1300,1,'lapiz',1,300);

Vamos a realizar un "select" para recuperar el número de factura, el código de cliente, la fecha y la suma
total de todas las facturas:
select f.*,
(select sum(d.precio*cantidad)
from detalles as d
where f.numero=d.numerofactura) as total
from facturas as f;

Esta consulta contiene una subconsulta correlacionada.


Ahora utilizaremos el resultado de la consulta anterior como una tabla derivada que emplearemos en lugar
de una tabla para realizar un "join" y recuperar el número de factura, el nombre del cliente y el monto total
por factura:
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;

Note que para referenciar la tabla derivada debimos colocar un alias a la consulta.
Primer problema:

Un club dicta clases de distintos deportes. En una tabla llamada "socios" guarda los
datos de los
socios, en una tabla llamada "deportes" la información referente a los diferentes
deportes que se
dictan y en una tabla denominada "inscriptos", las inscripciones de los socios a los
distintos
deportes.
Un socio puede inscribirse en varios deportes el mismo año. Un socio no puede
inscribirse en el
mismo deporte el mismo año. Distintos socios se inscriben en un mismo deporte en el
mismo año.

1- Elimine las tablas si existen:


if object_id('inscriptos') is not null
drop table inscriptos;
if object_id('socios') is not null
drop table socios;
if object_id('deportes') is not null
drop table deportes;

2- Cree las tablas con las siguientes estructuras:


create table socios(
documento char(8) not null,
nombre varchar(30),
domicilio varchar(30),
primary key(documento)
);
create table deportes(
codigo tinyint identity,
nombre varchar(20),
305
profesor varchar(15),
primary key(codigo)
);
create table inscriptos(
documento char(8) not null,
codigodeporte tinyint not null,
año char(4),
matricula char(1),--'s'=paga, 'n'=impaga
primary key(documento,codigodeporte,año),
constraint FK_inscriptos_socio
foreign key (documento)
references socios(documento)
on update cascade
on delete cascade
);

3- Ingrese algunos registros en las 3 tablas:


insert into socios values('22222222','Ana Acosta','Avellaneda 111');
insert into socios values('23333333','Betina Bustos','Bulnes 222');
insert into socios values('24444444','Carlos Castro','Caseros 333');
insert into socios values('25555555','Daniel Duarte','Dinamarca 44');

insert into deportes values('basquet','Juan Juarez');


insert into deportes values('futbol','Pedro Perez');
insert into deportes values('natacion','Marina Morales');
insert into deportes values('tenis','Marina Morales');

insert into inscriptos values ('22222222',3,'2006','s');


insert into inscriptos values ('23333333',3,'2006','s');
insert into inscriptos values ('24444444',3,'2006','n');
insert into inscriptos values ('22222222',3,'2005','s');
insert into inscriptos values ('22222222',3,'2007','n');
insert into inscriptos values ('24444444',1,'2006','s');
insert into inscriptos values ('24444444',2,'2006','s');

4- Realice una consulta en la cual muestre todos los datos de las inscripciones,
incluyendo el
nombre del deporte y del profesor.
Esta consulta es un join.

5- Utilice el resultado de la consulta anterior como una tabla derivada para emplear en
lugar de una
tabla para realizar un "join" y recuperar el nombre del socio, el deporte en el cual
está inscripto,
el año, el nombre del profesor y la matrícula.

99 - Subconsulta (update - delete)


Dijimos que podemos emplear subconsultas en sentencias "insert", "update", "delete", además
de "select".
La sintaxis básica para realizar actualizaciones con subconsulta es la siguiente:
update TABLA set CAMPO=NUEVOVALOR
where CAMPO= (SUBCONSULTA);

Actualizamos el precio de todos los libros de editorial "Emece":


306
update libros set precio=precio+(precio*0.1)
where codigoeditorial=
(select codigo
from editoriales
where nombre='Emece');

La subconsulta retorna un único valor. También podemos hacerlo con un join.


La sintaxis básica para realizar eliminaciones con subconsulta es la siguiente:
delete from TABLA
where CAMPO in (SUBCONSULTA);

Eliminamos todos los libros de las editoriales que tiene publicados libros de "Juan Perez":
delete from libros
where codigoeditorial in
(select e.codigo
from editoriales as e
join libros
on codigoeditorial=e.codigo
where autor='Juan Perez');

La subconsulta es una combinación que retorna una lista de valores que la consulta externa
emplea al seleccionar los registros para la eliminación.
Trabajamos con las tablas "libros" y "editoriales" de una librería.
Eliminamos las tablas si existen y las creamos:

if object_id('libros') is not null


drop table libros;
if object_id('editoriales') is not null
drop table editoriales;

create table editoriales(


codigo tinyint identity,
nombre varchar(30),
primary key (codigo)
);

create table libros (


codigo int identity,
titulo varchar(40),
autor varchar(30),
codigoeditorial tinyint,
precio decimal(5,2),
primary key(codigo)
);

Ingresamos algunos registros:


insert into editoriales values('Planeta');
insert into editoriales values('Emece');
insert into editoriales values('Paidos');
insert into editoriales values('Siglo XXI');

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


insert into libros values('Ilusiones','Richard Bach',2,20);
insert into libros values('El aleph','Borges',3,10);
insert into libros values('Aprenda PHP','Mario Molina',4,40);
insert into libros values('Poemas','Juan Perez',1,20);
insert into libros values('Cuentos','Juan Perez',3,25);
307
insert into libros values('Java en 10 minutos','Marcelo Perez',2,30);

Actualizamos el precio de todos los libros de editorial "Emece" incrementándolos en un 10%:


update libros set precio=precio+(precio*0.1)
where codigoeditorial=
(select codigo
from editoriales
where nombre='Emece');

Eliminamos todos los libros de las editoriales que tiene publicados libros de "Juan Perez":
delete from libros
where codigoeditorial in
(select e.codigo
from editoriales as e
join libros
on codigoeditorial=e.codigo
where autor='Juan Perez');

Primer problema:

Un club dicta clases de distintos deportes a sus socios. El club tiene una tabla
llamada
"inscriptos" en la cual almacena el número de "socio", el código del deporte en el cual
se inscribe
y si la matricula está o no paga, y una tabla denominada "socios" en la que guarda los
datos
personales de cada socio.
1- Elimine las tablas si existen:
if object_id('inscriptos') is not null
drop table inscriptos;
if object_id('socios') is not null
drop table socios;

2- Cree las tablas:


create table socios(
numero int identity,
documento char(8),
nombre varchar(30),
domicilio varchar(30),
primary key (numero)
);

create table inscriptos (


numerosocio int not null,
deporte varchar(20) not null,
matricula char(1),-- 'n' o 's'
primary key(numerosocio,deporte),
constraint FK_inscriptos_socio
foreign key (numerosocio)
references socios(numero)
);

3- Ingrese algunos registros:


insert into socios values('23333333','Alberto Paredes','Colon 111');
insert into socios values('24444444','Carlos Conte','Sarmiento 755');
insert into socios values('25555555','Fabian Fuentes','Caseros 987');
insert into socios values('26666666','Hector Lopez','Sucre 344');

insert into inscriptos values(1,'tenis','s');


insert into inscriptos values(1,'basquet','s');
insert into inscriptos values(1,'natacion','s');

308
insert into inscriptos values(2,'tenis','s');
insert into inscriptos values(2,'natacion','s');
insert into inscriptos values(2,'basquet','n');
insert into inscriptos values(2,'futbol','n');
insert into inscriptos values(3,'tenis','s');
insert into inscriptos values(3,'basquet','s');
insert into inscriptos values(3,'natacion','n');
insert into inscriptos values(4,'basquet','n');

4- Actualizamos la cuota ('s') de todas las inscripciones de un socio determinado (por


documento)
empleando subconsulta.

5- Elimine todas las inscripciones de los socios que deben alguna matrícula.
5 registros eliminados.

100 - Subconsulta (insert)


Aprendimos que una subconsulta puede estar dentro de un "select", "update" y "delete"; también
puede estar dentro de un "insert".
Podemos ingresar registros en una tabla empleando un "select".
La sintaxis básica es la siguiente:
insert into TABLAENQUESEINGRESA (CAMPOSTABLA1)
select (CAMPOSTABLACONSULTADA)
from TABLACONSULTADA;

Un profesor almacena las notas de sus alumnos en una tabla llamada "alumnos". Tiene otra tabla
llamada "aprobados", con algunos campos iguales a la tabla "alumnos" pero en ella solamente
almacenará los alumnos que han aprobado el ciclo.
Ingresamos registros en la tabla "aprobados" seleccionando registros de la tabla "alumnos":
insert into aprobados (documento,nota)
select (documento,nota)
from alumnos;

Entonces, se puede insertar registros en una tabla con la salida devuelta por una consulta a otra
tabla; para ello escribimos la consulta y le anteponemos "insert into" junto al nombre de la tabla
en la cual ingresaremos los registros y los campos que se cargarán (si se ingresan todos los
campos no es necesario listarlos).
La cantidad de columnas devueltas en la consulta debe ser la misma que la cantidad de campos
a cargar en el "insert".
Se pueden insertar valores en una tabla con el resultado de una consulta que incluya cualquier
tipo de "join".
Un profesor almacena las notas de sus alumnos en una tabla llamada "alumnos" (documento,
nombre, nota). Tiene otra tabla llamada "aprobados" (documento,nota) en la que guarda los

309
alumnos que han aprobado el ciclo.
Eliminamos las tablas si existen:
if object_id('alumnos') is not null
drop table alumnos;
if object_id('aprobados') is not null
drop table aprobados;

Creamos las tablas:


create table alumnos(
documento char(8) not null,
nombre varchar(30),
nota decimal(4,2)
constraint CK_alumnos_nota_valores check (nota>=0 and nota <=10),
primary key(documento),
);

create table aprobados(


documento char(8) not null,
nota decimal(4,2)
constraint CK_aprobados_nota_valores check (nota>=0 and nota <=10),
primary key(documento),
);

Ingresamos registros en "alumnos":


insert into alumnos values('30000000','Ana Acosta',8);
insert into alumnos values('30111111','Betina Bustos',9);
insert into alumnos values('30222222','Carlos Caseros',2.5);
insert into alumnos values('30333333','Daniel Duarte',7.7);
insert into alumnos values('30444444','Estela Esper',3.4);

Ingresamos registros en la tabla "aprobados" seleccionando registros de la tabla "alumnos":


insert into aprobados
select documento,nota
from alumnos
where nota>=4;

Note que no se listan los campos en los cuales se cargan los datos porque tienen el mismo nombre que los de
la tabla de la cual extraemos la información.
Veamos si los registros se han cargado:
select *from aprobados;

Primer problema:
Un comercio que vende artículos de librería y papelería almacena la información de sus
ventas en una
tabla llamada "facturas" y otra "clientes".
1- Elimine las tablas si existen:
if object_id('facturas') is not null
drop table facturas;
if object_id('clientes') is not null
drop table clientes;

2-Créelas:
create table clientes(
codigo int identity,
nombre varchar(30),
domicilio varchar(30),
310
primary key(codigo)
);

create table facturas(


numero int not null,
fecha datetime,
codigocliente int not null,
total decimal(6,2),
primary key(numero),
constraint FK_facturas_cliente
foreign key (codigocliente)
references clientes(codigo)
on update cascade
);

3-Ingrese algunos registros:


insert into clientes values('Juan Lopez','Colon 123');
insert into clientes values('Luis Torres','Sucre 987');
insert into clientes values('Ana Garcia','Sarmiento 576');
insert into clientes values('Susana Molina','San Martin 555');

insert into facturas values(1200,'2007-01-15',1,300);


insert into facturas values(1201,'2007-01-15',2,550);
insert into facturas values(1202,'2007-01-15',3,150);
insert into facturas values(1300,'2007-01-20',1,350);
insert into facturas values(1310,'2007-01-22',3,100);

4- El comercio necesita una tabla llamada "clientespref" en la cual quiere almacenar el


nombre y
domicilio de aquellos clientes que han comprado hasta el momento más de 500 pesos en
mercaderías.
Elimine la tabla si existe y créela con esos 2 campos:
if object_id ('clientespref') is not null
drop table clientespref;
create table clientespref(
nombre varchar(30),
domicilio varchar(30)
);

5- Ingrese los registros en la tabla "clientespref" seleccionando registros de la tabla


"clientes" y
"facturas".

6- Vea los registros de "clientespref":


select *from clientespref;

101 - Crear tabla a partir de otra (select - into)


Podemos crear una tabla e insertar datos en ella en una sola sentencia consultando otra tabla (o
varias) con esta sintaxis:
select CAMPOSNUEVATABLA
into NUEVATABLA
from TABLA
where CONDICION;

Es decir, se crea una nueva tabla y se inserta en ella el resultado de una consulta a otra tabla.
311
Tenemos la tabla "libros" de una librería y queremos crear una tabla llamada "editoriales" que
contenga los nombres de las editoriales.
La tabla "editoriales", que no existe, contendrá solamente un campo llamado "nombre". La tabla
libros contiene varios registros.
Podemos crear la tabla "editoriales" con el campo "nombre" consultando la tabla "libros" y en el
mismo momento insertar la información:
select distinct editorial as nombre
into editoriales
from libros;

La tabla "editoriales" se ha creado con el campo "nombre" seleccionado del campo "editorial" de
"libros".
Los campos de la nueva tabla tienen el mismo nombre, tipo de dato y valores almacenados que
los campos listados de la tabla consultada; si se quiere dar otro nombre a los campos de la nueva
tabla se deben especificar alias.
Entonces, luego de la lista de selección de campos de la tabla a consultar, se coloca "into"
seguido del nombre de la nueva tabla y se sigue con la consulta.
Podemos emplear "group by", funciones de agrupamiento y "order by" en las consultas. También
podemos emplear "select... into" con combinaciones, para crear una tabla que contenga datos de
2 o más tablas.
Tenemos la tabla "libros" de una librería y queremos crear una tabla llamada "editoriales" que contenga los
nombres de las editoriales.
Eliminamos las tablas "libros" y "editoriales" si existen:

if object_id('libros')is not null


drop table libros;
if object_id('editoriales')is not null
drop table editoriales;

Creamos la tabla "libros":


create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(30),
editorial varchar(20),
precio decimal(5,2),
primary key(codigo)
);

Ingresamos algunos registros;


insert into libros values('Uno','Richard Bach','Planeta',15);
insert into libros values('El aleph','Borges','Emece',25);
insert into libros values('Matematica estas ahi','Paenza','Nuevo siglo',18);
insert into libros values('Aprenda PHP','Mario Molina','Nuevo siglo',45);
insert into libros values('Ilusiones','Richard Bach','Planeta',14);
insert into libros values('Java en 10 minutos','Mario Molina','Nuevo siglo',50);

Creamos una tabla llamada "editoriales" que contenga los nombres de las editoriales:

312
select distinct editorial as nombre
into editoriales
from libros;

Veamos la nueva tabla:


select *from editoriales;

Necesitamos una nueva tabla llamada "librosporeditorial" que contenga la cantidad de libros de cada
editorial. Primero eliminamos la tabla, si existe:
if object_id('cantidadporeditorial') is not null
drop table cantidadporeditorial;

Creamos la nueva tabla:


select editorial as nombre,count(*) as cantidad
into cantidadporeditorial
from libros
group by editorial;

Veamos los registros de la nueva tabla:


select *from cantidadporeditorial;

La tabla "cantidadporeditorial" se ha creado con el campo llamado "nombre" seleccionado del campo
"editorial" de "libros" y con el campo "cantidad" con el valor calculado con count(*) de la tabla "libros".
Queremos una tabla llamada "ofertas4" que contenga los mismos campos que "libros" y guarde los 4 libros
más económicos. Primero eliminamos, si existe, la tabla "ofertas4":
if object_id('ofertas4') is not null
drop table ofertas4;

Creamos "ofertas4" e insertamos la consulta de "libros":


select top 4 *
into ofertas4
from libros
order by precio desc;

La consulta anterior retorna los 4 primeros libros de la tabla "libros", ordenados en forma ascendente por
precio y los almacena en la nueva tabla ("ofertas4"). Note que no se listan los campos a extraer, se coloca un
asterisco para indicar que se incluyen todos los campos.
Veamos los registros de la nueva tabla:
select *from ofertas4;

Agregamos una columna a la tabla "editoriales" que contiene la ciudad en la cual está la casa central de cada
editorial:
alter table editoriales add ciudad varchar(30);

Actualizamos dicho campo:


update editoriales set ciudad='Cordoba' where nombre='Planeta';
update editoriales set ciudad='Cordoba' where nombre='Emece';
update editoriales set ciudad='Buenos Aires' where nombre='Nuevo siglo';

313
Queremos una nueva tabla llamada "librosdecordoba" que contenga los títulos y autores de los libros de
editoriales de Cordoba. En primer lugar, la eliminamos, si existe:
if object_id('librosdecordoba') is not null
drop table librosdecordoba;

Consultamos las 2 tablas y guardamos el resultado en la nueva tabla que estamos creando:
select titulo,autor
into librosdecordoba
from libros
join editoriales
on editorial=nombre
where ciudad='Cordoba';

Consultamos la nueva tabla:


select *from librosdecordoba;

Primer problema:

Un supermercado almacena los datos de sus empleados en una tabla denominada "empleados"
y en una
tabla llamada "sucursales" los códigos y ciudades de las diferentes sucursales.
1- Elimine las tablas "empleados" y "sucursales" si existen:
if object_id('empleados')is not null
drop table empleados;
if object_id('sucursales')is not null
drop table sucursales;

2- Cree la tabla "sucursales":


create table sucursales(
codigo int identity,
ciudad varchar(30) not null,
primary key(codigo)
);

3- Cree la tabla "empleados":


create table empleados(
documento char(8) not null,
nombre varchar(30) not null,
domicilio varchar(30),
seccion varchar(20),
sueldo decimal(6,2),
codigosucursal int,
primary key(documento),
constraint FK_empleados_sucursal
foreign key (codigosucursal)
references sucursales(codigo)
on update cascade
);

4- Ingrese algunos registros para ambas tablas:


insert into sucursales values('Cordoba');
insert into sucursales values('Villa Maria');
insert into sucursales values('Carlos Paz');
insert into sucursales values('Cruz del Eje');

insert into empleados values('22222222','Ana Acosta','Avellaneda


111','Secretaria',500,1);
insert into empleados values('23333333','Carlos Caseros','Colon
222','Sistemas',800,1);

314
insert into empleados values('24444444','Diana Dominguez','Dinamarca
333','Secretaria',550,2);
insert into empleados values('25555555','Fabiola Fuentes','Francia
444','Sistemas',750,2);
insert into empleados values('26666666','Gabriela Gonzalez','Guemes
555','Secretaria',580,3);
insert into empleados values('27777777','Juan Juarez','Jujuy 777','Secretaria',500,4);
insert into empleados values('28888888','Luis Lopez','Lules 888','Sistemas',780,4);
insert into empleados values('29999999','Maria Morales','Marina
999','Contaduria',670,4);

5- Realice un join para mostrar todos los datos de "empleados" incluyendo la ciudad de
la sucursal:
select documento,nombre,domicilio,seccion,sueldo,ciudad
from empleados
join sucursales on codigosucursal=codigo;

6-Cree una tabla llamada "secciones" que contenga las secciones de la empresa (primero
elimínela, si
existe):
if object_id('secciones') is not null
drop table secciones;

select distinct seccion as nombre


into secciones
from empleados;

7- Recupere la información de "secciones":


select *from secciones;
3 registros.

8- Se necesita una nueva tabla llamada "sueldosxseccion" que contenga la suma de los
sueldos de los
empleados por sección. Primero elimine la tabla, si existe:
if object_id('sueldosxseccion') is not null
drop table sueldosxseccion;

select seccion, sum(sueldo) as total


into sueldosxseccion
from empleados
group by seccion;

9- Recupere los registros de la nueva tabla:


select *from sueldosxseccion;

10- Se necesita una tabla llamada "maximossueldos" que contenga los mismos campos que
"empleados" y
guarde los 3 empleados con sueldos más altos. Primero eliminamos, si existe, la tabla
"maximossueldos":
if object_id('maximossueldos') is not null
drop table maximossueldos;

select top 3 *
into maximossueldos
from empleados
order by sueldo;

11- Vea los registros de la nueva tabla:


select *from maximossueldos;

12- Se necesita una nueva tabla llamada "sucursalCordoba" que contenga los nombres y
sección de los
empleados de la ciudad de Córdoba. En primer lugar, eliminamos la tabla, si existe.
Luego, consulte

315
las tablas "empleados" y "sucursales" y guarde el resultado en la nueva tabla:
if object_id('sucursalCordoba') is not null
drop table sucursalCordoba;

select nombre,ciudad
into sucursalCordoba
from empleados
join sucursales
on codigosucursal=codigo
where ciudad='Cordoba';

13- Consulte la nueva tabla:


select *from sucursalCordoba;

102 - go
Esto solo se aplica cuando instale el SQL Server en su máquina.
"go" es un signo de finalización 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
separándolas 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 instrucción.
316
No está de más recordar que esto solo se aplica cuando instale el SQL Server en su máquina y
ejecute los comandos desde el Query Analyzer.

103 - 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 través de la vista no están
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 también tabla virtual porque los resultados que retorna y la manera de
referenciarlas es la misma que para una tabla.
Las vistas permiten:
- ocultar información: permitiendo el acceso a algunos datos y manteniendo oculto el resto de la
información 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 administración de los permisos de usuario: se pueden dar al usuario permisos para
que solamente pueda acceder a los datos a través de vistas, en lugar de concederle permisos
para acceder a ciertos campos, así se protegen las tablas base de cambios en su estructura.
- mejorar el rendimiento: se puede evitar tipear instrucciones repetidamente almacenando en una
vista el resultado de una consulta compleja que incluya información de varias tablas.
Podemos crear vistas con: un subconjunto de registros y campos de una tabla; una unión de
varias tablas; una combinación de varias tablas; un resumen estadístico de una tabla; un
subconjunto de otra vista, combinación de vistas y tablas.
Una vista se define usando un "select".
La sintaxis básica 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;

En el siguiente ejemplo creamos la vista "vista_empleados", que es resultado de una combinación


en la cual se muestran 4 campos:
create view vista_empleados as
select (apellido+' '+e.nombre) as nombre,sexo,
s.nombre as seccion, cantidadhijos

317
from empleados as e
join secciones as s
on codigo=seccion

Para ver la información contenida en la vista creada anteriormente tipeamos:


select *from vista_empleados;

Podemos realizar consultas a una vista como si se tratara de una tabla:


select seccion,count(*) as cantidad
from vista_empleados;

Los nombres para vistas deben seguir las mismas reglas que cualquier identificador. Para
distinguir una tabla de una vista podemos fijar una convención para darle nombres, por ejemplo,
colocar el sufijo ?vista? y luego el nombre de las tablas consultadas en ellas.
Los campos y expresiones de la consulta que define una vista DEBEN tener un nombre. Se debe
colocar nombre de campo cuando es un campo calculado o si hay 2 campos con el mismo
nombre. Note que en el ejemplo, al concatenar los campos "apellido" y "nombre" colocamos un
alias; si no lo hubiésemos hecho aparecería un mensaje de error porque dicha expresión DEBE
tener un encabezado, 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 había un encabezado (el alias
de la concatenación) llamado "nombre" y no pueden repetirse, si sucediera, aparecería un
mensaje de error.
Otra sintaxis es la siguiente:
create view NOMBREVISTA (NOMBRESDEENCABEZADOS)
as
SENTENCIASSELECT
from TABLA;

Creamos otra vista de "empleados" denominada "vista_empleados_ingreso" que almacena la


cantidad de empleados por año:
create view vista_empleados_ingreso (fecha,cantidad)
as
select datepart(year,fechaingreso),count(*)
from empleados
group by datepart(year,fechaingreso)

La diferencia es que se colocan entre paréntesis los encabezados de las columnas que
aparecerán en la vista. Si no los colocamos y empleamos la sintaxis vista anteriormente, se
emplean los nombres de los campos o alias (que en este caso habría que agregar) colocados en
el "select" que define la vista. Los nombres que se colocan entre paréntesis deben ser tantos
como los campos o expresiones que se definen en la vista.
Las vistas se crean en la base de datos activa.
Al crear una vista, SQL Server verifica que existan las tablas a las que se hacen referencia en
ella.

318
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 cláusulas "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.
Una empresa almacena la información de sus empleados en dos tablas llamadas "empleados" y "secciones".
Eliminamos las tablas, si existen:

if object_id('empleados') is not null


drop table empleados;
if object_id('secciones') is not null
drop table secciones;

Creamos las tablas:


create table secciones(
codigo tinyint identity,
nombre varchar(20),
sueldo decimal(5,2)
constraint CK_secciones_sueldo check (sueldo>=0),
constraint PK_secciones primary key (codigo)
);

create table empleados(


legajo int identity,
documento char(8)
constraint CK_empleados_documento check (documento like '[0-9][0-9][0-9][0-9][0-9]
[0-9][0-9][0-9]'),
sexo char(1)
constraint CK_empleados_sexo check (sexo in ('f','m')),
apellido varchar(20),
nombre varchar(20),
domicilio varchar(30),
seccion tinyint not null,
cantidadhijos tinyint
constraint CK_empleados_hijos check (cantidadhijos>=0),
estadocivil char(10)
constraint CK_empleados_estadocivil check (estadocivil in
('casado','divorciado','soltero','viudo')),
fechaingreso datetime,
constraint PK_empleados primary key (legajo),
constraint FK_empleados_seccion
foreign key (seccion)
references secciones(codigo)
on update cascade,
constraint UQ_empleados_documento
unique(documento)
);

Ingresamos algunos registros:


insert into secciones values('Administracion',300);

319
insert into secciones values('Contaduría',400);
insert into secciones values('Sistemas',500);

insert into empleados values('22222222','f','Lopez','Ana','Colon


123',1,2,'casado','1990-10-10');
insert into empleados values('23333333','m','Lopez','Luis','Sucre
235',1,0,'soltero','1990-02-10');
insert into empleados values('24444444','m','Garcia','Marcos','Sarmiento
1234',2,3,'divorciado','1998-07-12');
insert into empleados values('25555555','m','Gomez','Pablo','Bulnes
321',3,2,'casado','1998-10-09');
insert into empleados values('26666666','f','Perez','Laura','Peru
1254',3,3,'casado','2000-05-09');

Eliminamos la vista "vista_empleados" si existe. Aún no hemos aprendido a eliminar vistas, lo veremos
próximamente:
if object_id('vista_empleados') is not null
drop view vista_empleados;

Creamos la vista "vista_empleados", que es resultado de una combinación en la cual se muestran 5 campos:
create view vista_empleados as
select (apellido+' '+e.nombre) as nombre,sexo,
s.nombre as seccion, cantidadhijos
from empleados as e
join secciones as s
on codigo=seccion;

Vemos la información de la vista:


select *from vista_empleados;

Realizamos una consulta a la vista como si se tratara de una tabla:


select seccion,count(*) as cantidad
from vista_empleados
group by seccion;

Eliminamos la vista "vista_empleados_ingreso" si existe:


if object_id('vista_empleados_ingreso') is not null
drop view vista_empleados_ingreso;

Creamos otra vista de "empleados" denominada "vista_empleados_ingreso" que almacena la cantidad de


empleados por año:
create view vista_empleados_ingreso (fecha,cantidad)
as
select datepart(year,fechaingreso),count(*)
from empleados
group by datepart(year,fechaingreso);

Vemos la información:
select *from vista_empleados_ingreso;

Primer problema:

Un club dicta cursos de distintos deportes. Almacena la información en varias tablas.

320
El director no quiere que los empleados de administración conozcan la estructura de las
tablas ni
algunos datos de los profesores y socios, por ello se crean vistas a las cuales tendrán
acceso.
1- Elimine las tablas y créelas nuevamente:
if object_id('inscriptos') is not null
drop table inscriptos;
if object_id('socios') is not null
drop table socios;
if object_id('profesores') is not null
drop table profesores;
if object_id('cursos') is not null
drop table cursos;

create table socios(


documento char(8) not null,
nombre varchar(40),
domicilio varchar(30),
constraint PK_socios_documento
primary key (documento)
);

create table profesores(


documento char(8) not null,
nombre varchar(40),
domicilio varchar(30),
constraint PK_profesores_documento
primary key (documento)
);

create table cursos(


numero tinyint identity,
deporte varchar(20),
dia varchar(15),
constraint CK_inscriptos_dia check (dia
in('lunes','martes','miercoles','jueves','viernes','sabado')),
documentoprofesor char(8),
constraint PK_cursos_numero
primary key (numero),
);

create table inscriptos(


documentosocio char(8) not null,
numero tinyint not null,
matricula char(1),
constraint CK_inscriptos_matricula check (matricula in('s','n')),
constraint PK_inscriptos_documento_numero
primary key (documentosocio,numero)
);

2- Ingrese algunos registros para todas las tablas:


insert into socios values('30000000','Fabian Fuentes','Caseros 987');
insert into socios values('31111111','Gaston Garcia','Guemes 65');
insert into socios values('32222222','Hector Huerta','Sucre 534');
insert into socios values('33333333','Ines Irala','Bulnes 345');

insert into profesores values('22222222','Ana Acosta','Avellaneda 231');


insert into profesores values('23333333','Carlos Caseres','Colon 245');
insert into profesores values('24444444','Daniel Duarte','Sarmiento 987');
insert into profesores values('25555555','Esteban Lopez','Sucre 1204');

insert into cursos values('tenis','lunes','22222222');


insert into cursos values('tenis','martes','22222222');
insert into cursos values('natacion','miercoles','22222222');

321
insert into cursos values('natacion','jueves','23333333');
insert into cursos values('natacion','viernes','23333333');
insert into cursos values('futbol','sabado','24444444');
insert into cursos values('futbol','lunes','24444444');
insert into cursos values('basquet','martes','24444444');

insert into inscriptos values('30000000',1,'s');


insert into inscriptos values('30000000',3,'n');
insert into inscriptos values('30000000',6,null);
insert into inscriptos values('31111111',1,'s');
insert into inscriptos values('31111111',4,'s');
insert into inscriptos values('32222222',8,'s');

3- Elimine la vista "vista_club" si existe:


if object_id('vista_club') is not null drop view vista_club;

4- Cree una vista en la que aparezca el nombre y documento del socio, el deporte, el
día y el nombre
del profesor.

5- Muestre la información contenida en la vista.

6- Realice una consulta a la vista donde muestre la cantidad de socios inscriptos en


cada deporte
ordenados por cantidad.

7- Muestre (consultando la vista) los cursos (deporte y día) para los cuales no hay
inscriptos.

8- Muestre los nombres de los socios que no se han inscripto en ningún curso
(consultando la vista)

9- Muestre (consultando la vista) los profesores que no tienen asignado ningún deporte
aún.

10- Muestre (consultando la vista) el nombre y documento de los socios que deben
matrículas.

11- Consulte la vista y muestre los nombres de los profesores y los días en que asisten
al club para
dictar sus clases.

12- Muestre la misma información anterior pero ordenada por día.

13- Muestre todos los socios que son compañeros en tenis los lunes.

14- Elimine la vista "vista_inscriptos" si existe y créela para que muestre la cantidad
de
inscriptos por curso, incluyendo el número del curso, el nombre del deporte y el día.

15- Consulte la vista:


select *from vista_inscriptos;

104 - Vistas (información)


Las vistas son objetos, así que para obtener información de ellos pueden usarse los siguientes
procedimientos almacenados del sistema:
322
"sp_help" sin parámetros 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 creación, propietario, los campos
y demás información.
"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 demás 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.


También 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%';--búsqueda con comodín

Una empresa almacena la información de sus empleados en dos tablas llamadas "empleados" y "secciones".
Eliminamos las tablas, si existen:

if object_id('empleados') is not null


drop table empleados;
if object_id('secciones') is not null
drop table secciones;

Creamos las tablas:


create table secciones(
codigo tinyint identity,
nombre varchar(20),
sueldo decimal(5,2)
constraint CK_secciones_sueldo check (sueldo>=0),
constraint PK_secciones primary key (codigo)
);

create table empleados(

323
legajo int identity,
documento char(8)
constraint CK_empleados_documento check (documento like '[0-9][0-9][0-9][0-9][0-9]
[0-9][0-9][0-9]'),
sexo char(1)
constraint CK_empleados_sexo check (sexo in ('f','m')),
apellido varchar(20),
nombre varchar(20),
domicilio varchar(30),
seccion tinyint not null,
cantidadhijos tinyint
constraint CK_empleados_hijos check (cantidadhijos>=0),
estadocivil char(10)
constraint CK_empleados_estadocivil check (estadocivil in
('casado','divorciado','soltero','viudo')),
fechaingreso datetime,
constraint PK_empleados primary key (legajo),
constraint FK_empleados_seccion
foreign key (seccion)
references secciones(codigo)
on update cascade,
constraint UQ_empleados_documento
unique(documento)
);

Ingresamos algunos registros:


insert into secciones values('Administracion',300);
insert into secciones values('Contaduría',400);
insert into secciones values('Sistemas',500);

insert into empleados values('22222222','f','Lopez','Ana','Colon


123',1,2,'casado','1990-10-10');
insert into empleados values('23333333','m','Lopez','Luis','Sucre
235',1,0,'soltero','1990-02-10');
insert into empleados values('24444444','m','Garcia','Marcos','Sarmiento
1234',2,3,'divorciado','1998-07-12');
insert into empleados values('25555555','m','Gomez','Pablo','Bulnes
321',3,2,'casado','1998-10-09');
insert into empleados values('26666666','f','Perez','Laura','Peru
1254',3,3,'casado','2000-05-09');

Eliminamos la vista "vista_empleados" si existe:


if object_id('vista_empleados') is not null
drop view vista_empleados;

Creamos la vista "vista_empleados", que es resultado de una combinación en la cual se muestran 5 campos:
create view vista_empleados as
select (apellido+' '+e.nombre) as nombre,sexo,
s.nombre as seccion, cantidadhijos
from empleados as e
join secciones as s
on codigo=seccion;

Vemos la información de la vista:


select *from vista_empleados;

Ejecutamos "sp_help" enviándole como argumento el nombre de la vista:

324
sp_help vista_empleados;

Vemos el texto que define la vista:


sp_helptext vista_empleados;

Ejecutamos el procedimiento almacenado del sistema "sp_depends" seguido del nombre de la vista:
sp_depends vista_empleados;

aparecen las tablas y campos de las cuales depende la vista, es decir, las tablas referenciadas en la misma.
Ejecutamos el procedimiento "sp_depends" seguido del nombre de la tabla "empleados":
sp_depends empleados;

aparece la vista "vista_empleados" y las restricciones que dependen de ella.


Consultamos la tabla del sistema "sysobjects":
select *from sysobjects;

Si queremos ver todas las vistas creadas por nosotros, podemos tipear:
select *from sysobjects
where xtype='V' and-- tipo vista
name like 'vista%';--búsqueda con comodín

105 - 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 definición 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
325
Si ejecutamos el procedimiento almacenado del sistema "sp_helptext" seguido del nombre de una
vista encriptada, aparece un mensaje indicando tal situación y el texto no se muestra.
Una empresa almacena la información de sus empleados en dos tablas llamadas "empleados" y "secciones".
Eliminamos las tablas, si existen:

if object_id('empleados') is not null


drop table empleados;
if object_id('secciones') is not null
drop table secciones;

Creamos las tablas:


create table secciones(
codigo tinyint identity,
nombre varchar(20),
sueldo decimal(5,2)
constraint CK_secciones_sueldo check (sueldo>=0),
constraint PK_secciones primary key (codigo)
);

create table empleados(


legajo int identity,
documento char(8)
constraint CK_empleados_documento check (documento like '[0-9][0-9][0-9][0-9][0-9]
[0-9][0-9][0-9]'),
sexo char(1)
constraint CK_empleados_sexo check (sexo in ('f','m')),
apellido varchar(20),
nombre varchar(20),
domicilio varchar(30),
seccion tinyint not null,
cantidadhijos tinyint
constraint CK_empleados_hijos check (cantidadhijos>=0),
estadocivil char(10)
constraint CK_empleados_estadocivil check (estadocivil in
('casado','divorciado','soltero','viudo')),
fechaingreso datetime,
constraint PK_empleados primary key (legajo),
constraint FK_empleados_seccion
foreign key (seccion)
references secciones(codigo)
on update cascade,
constraint UQ_empleados_documento
unique(documento)
);

Eliminamos la vista "vista_empleados" si existe:


if object_id('vista_empleados') is not null
drop view vista_empleados;

Creamos una vista con su definición 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

326
on codigo=seccion;

Ejecutamos el procedimiento almacenado del sistema "sp_helptext" seguido del nombre de una vista
encriptada:
sp_helptext vista_empleados;

No se muestra.

106 - 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 explícitamente.
Solo el propietario puede eliminar una vista.
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;

Una empresa almacena la información de sus empleados en dos tablas llamadas "empleados" y "secciones".
Eliminamos las tablas, si existen:
if object_id('empleados') is not null
drop table empleados;
if object_id('secciones') is not null
drop table secciones;

Creamos las tablas:


create table secciones(
codigo tinyint identity,
nombre varchar(20),
sueldo decimal(5,2)
constraint CK_secciones_sueldo check (sueldo>=0),
constraint PK_secciones primary key (codigo)
);

create table empleados(


legajo int identity,
documento char(8)
constraint CK_empleados_documento check (documento like '[0-9][0-9][0-9][0-9][0-9]
[0-9][0-9][0-9]'),
sexo char(1)
constraint CK_empleados_sexo check (sexo in ('f','m')),
apellido varchar(20),
nombre varchar(20),
domicilio varchar(30),
327
seccion tinyint not null,
cantidadhijos tinyint
constraint CK_empleados_hijos check (cantidadhijos>=0),
estadocivil char(10)
constraint CK_empleados_estadocivil check (estadocivil in
('casado','divorciado','soltero','viudo')),
fechaingreso datetime,
constraint PK_empleados primary key (legajo),
constraint FK_empleados_seccion
foreign key (seccion)
references secciones(codigo)
on update cascade,
constraint UQ_empleados_documento
unique(documento)
);

Ingresamos algunos registros:


insert into secciones values('Administracion',300);
insert into secciones values('Contaduría',400);
insert into secciones values('Sistemas',500);

insert into empleados values('22222222','f','Lopez','Ana','Colon


123',1,2,'casado','1990-10-10');
insert into empleados values('23333333','m','Lopez','Luis','Sucre
235',1,0,'soltero','1990-02-10');
insert into empleados values('24444444','m','Garcia','Marcos','Sarmiento
1234',2,3,'divorciado','1998-07-12');
insert into empleados values('25555555','m','Gomez','Pablo','Bulnes
321',3,2,'casado','1998-10-09');
insert into empleados values('26666666','f','Perez','Laura','Peru
1254',3,3,'casado','2000-05-09');

Eliminamos la vista "vista_empleados" si existe:


if object_id('vista_empleados') is not null
drop view vista_empleados;

Creamos la vista "vista_empleados", que es resultado de una combinación en la cual se muestran 5 campos:
create view vista_empleados as
select (apellido+' '+e.nombre) as nombre,sexo,
s.nombre as seccion, cantidadhijos
from empleados as e
join secciones as s
on codigo=seccion;

Veamos la información de la vista:


select *from vista_empleados;

Eliminamos la tabla "empleados":


drop table empleados;

Verificamos que la vista aún existe:


sp_help;

Eliminamos la vista:

328
drop view vista_empleados;

Verificamos que la vista ya no existe:


sp_help vista_empleados;

107 - Vistas (with check option)


Es posible obligar a todas las instrucciones de modificación de datos que se ejecutan en una
vista a cumplir ciertos criterios.
Por ejemplo, creamos la siguiente vista:
create view vista_empleados
as
select apellido, e.nombre, sexo, s.nombre as seccion
from empleados as e
join secciones as s
on seccion=codigo
where s.nombre='Administracion'
with check option;

La vista definida anteriormente muestra solamente algunos de los datos de los empleados de la
sección "Administracion". Además, solamente se permiten modificaciones a los empleados de esa
sección.
Podemos actualizar el nombre, apellido y sexo a través de la vista, pero no el campo "seccion"
porque está restringuido.
Una empresa almacena la información de sus empleados en dos tablas llamadas "empleados" y "secciones".
Eliminamos las tablas, si existen:
if object_id('empleados') is not null
drop table empleados;
if object_id('secciones') is not null
drop table secciones;

Creamos las tablas:


create table secciones(
codigo tinyint identity,
nombre varchar(20),
sueldo decimal(5,2)
constraint CK_secciones_sueldo check (sueldo>=0),
constraint PK_secciones primary key (codigo)
);

create table empleados(


legajo int identity,
documento char(8)
constraint CK_empleados_documento check (documento like '[0-9][0-9][0-9][0-9][0-9]
[0-9][0-9][0-9]'),
sexo char(1)
constraint CK_empleados_sexo check (sexo in ('f','m')),
apellido varchar(20),
329
nombre varchar(20),
domicilio varchar(30),
seccion tinyint not null,
cantidadhijos tinyint
constraint CK_empleados_hijos check (cantidadhijos>=0),
estadocivil char(10)
constraint CK_empleados_estadocivil check (estadocivil in
('casado','divorciado','soltero','viudo')),
fechaingreso datetime,
constraint PK_empleados primary key (legajo),
constraint FK_empleados_seccion
foreign key (seccion)
references secciones(codigo)
on update cascade,
constraint UQ_empleados_documento
unique(documento)
);

Ingresamos algunos registros:


insert into secciones values('Administracion',300);
insert into secciones values('Contaduría',400);
insert into secciones values('Sistemas',500);

insert into empleados values('22222222','f','Lopez','Ana','Colon


123',1,2,'casado','1990-10-10');
insert into empleados values('23333333','m','Lopez','Luis','Sucre
235',1,0,'soltero','1990-02-10');
insert into empleados values('24444444','m','Garcia','Marcos','Sarmiento
1234',2,3,'divorciado','1998-07-12');
insert into empleados values('25555555','m','Gomez','Pablo','Bulnes
321',3,2,'casado','1998-10-09');
insert into empleados values('26666666','f','Perez','Laura','Peru
1254',3,3,'casado','2000-05-09');

Eliminamos la vista "vista_empleados" si existe:


if object_id('vista_empleados') is not null
drop view vista_empleados;

Creamos la vista "vista_empleados", que es resultado de una combinación:


Es posible obligar a todas las instrucciones de modificación de datos que se ejecutan en una vista a cumplir
ciertos criterios.
Por ejemplo, creamos la siguiente vista:
create view vista_empleados
as
select apellido, e.nombre, sexo, s.nombre as seccion
from empleados as e
join secciones as s
on seccion=codigo
where s.nombre='Administracion'
with check option;

Consultamos la vista:
select *from vista_empleados;

Actualizamos el nombre de un empleado a través de la vista:

330
update vista_empleados set nombre='Beatriz' where nombre='Ana';

Veamos si la modificación se realizó en la tabla:


select *from empleados;

Primer problema:
Una empresa almacena la información de sus clientes en dos tablas llamadas "clientes" y
"ciudades".
1- Elimine las tablas, si existen:
if object_id('clientes') is not null
drop table clientes;
if object_id('ciudades') is not null
drop table ciudades;

2- Cree las tablas:


create table ciudades(
codigo tinyint identity,
nombre varchar(20),
constraint PK_ciudades
primary key (codigo)
);

create table clientes(


nombre varchar(20),
apellido varchar(20),
documento char(8),
domicilio varchar(30),
codigociudad tinyint
constraint FK_clientes_ciudad
foreign key (codigociudad)
references ciudades(codigo)
on update cascade
);

3- Ingrese algunos registros:


insert into ciudades values('Cordoba');
insert into ciudades values('Carlos Paz');
insert into ciudades values('Cruz del Eje');
insert into ciudades values('La Falda');

insert into clientes values('Juan','Perez','22222222','Colon 1123',1);


insert into clientes values('Karina','Lopez','23333333','San Martin 254',2);
insert into clientes values('Luis','Garcia','24444444','Caseros 345',1);
insert into clientes values('Marcos','Gonzalez','25555555','Sucre 458',3);
insert into clientes values('Nora','Torres','26666666','Bulnes 567',1);
insert into clientes values('Oscar','Luque','27777777','San Martin 786',4);

4- Elimine la vista "vista_clientes" si existe:


if object_id('vista_clientes') is not null
drop view vista_clientes;

5- Cree la vista "vista_clientes" para que recupere el nombre, apellido, documento,


domicilio, el
código y nombre de la ciudad a la cual pertenece, de la ciudad de "Cordoba" empleando
"with check
option".

6- Consulte la vista:
select *from vista_clientes;

7- Actualice el apellido de un cliente a través de la vista.

8- Verifique que la modificación se realizó en la tabla:


331
select *from clientes;

9- Intente cambiar la ciudad de algún registro.


Mensaje de error.

108 - Vistas (modificar datos de una tabla a


través de vistas)
Si se modifican los datos de una vista, se modifica la tabla base.
Se puede insertar, actualizar o eliminar datos de una tabla a través de una vista, teniendo en
cuenta lo siguiente, las modificaciones que se realizan a las vistas:
- no pueden afectar a más de una tabla consultada. Pueden modificarse datos de una vista que
combina varias tablas pero la modificación solamente debe afectar a una sola tabla.
- no se pueden cambiar los campos resultado de un cálculo.
- pueden generar errores si afectan a campos a las que la vista no hace referencia. Por ejemplo,
si se ingresa un registro en una vista que consulta una tabla que tiene campos not null que no
están incluidos en la vista.
- la opción "with check option" obliga a todas las instrucciones de modificación 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.
Una empresa almacena la información de sus empleados en dos tablas llamadas "empleados" y
"secciones".
Eliminamos las tablas, si existen:
if object_id('empleados') is not null
drop table empleados;
if object_id('secciones') is not null
drop table secciones;

Creamos las tablas:


create table secciones(
codigo tinyint identity,
nombre varchar(20),
sueldo decimal(5,2)
constraint CK_secciones_sueldo check (sueldo>=0),
constraint PK_secciones primary key (codigo)
);

create table empleados(


legajo int identity,
documento char(8)
constraint CK_empleados_documento check (documento like '[0-9][0-9][0-9][0-9][0-9]
[0-9][0-9][0-9]'),
sexo char(1)
332
constraint CK_empleados_sexo check (sexo in ('f','m')),
apellido varchar(20),
nombre varchar(20),
domicilio varchar(30),
seccion tinyint not null,
cantidadhijos tinyint
constraint CK_empleados_hijos check (cantidadhijos>=0),
estadocivil char(10)
constraint CK_empleados_estadocivil check (estadocivil in
('casado','divorciado','soltero','viudo')),
fechaingreso datetime,
constraint PK_empleados primary key (legajo),
sueldo decimal(6,2),
constraint FK_empleados_seccion
foreign key (seccion)
references secciones(codigo)
on update cascade,
constraint UQ_empleados_documento
unique(documento)
);

Ingresamos algunos registros:


insert into secciones values('Administracion',300);
insert into secciones values('Contaduría',400);
insert into secciones values('Sistemas',500);

insert into empleados values('22222222','f','Lopez','Ana','Colon


123',1,2,'casado','1990-10-10',600);
insert into empleados values('23333333','m','Lopez','Luis','Sucre
235',1,0,'soltero','1990-02-10',650);
insert into empleados values('24444444', 'm', 'Garcia', 'Marcos', 'Sarmiento 1234', 2,
3, 'divorciado', '1998-07-12',800);
insert into empleados values('25555555','m','Gomez','Pablo','Bulnes
321',3,2,'casado','1998-10-09',900);
insert into empleados values('26666666','f','Perez','Laura','Peru
1254',3,3,'casado','2000-05-09',700);

Eliminamos la vista "vista_empleados" si existe:


if object_id('vista_empleados') is not null
drop view vista_empleados;

Creamos la vista "vista_empleados", que es resultado de una combinación en la cual se muestran 5 campos:
create view vista_empleados as
select (apellido+' '+e.nombre) as nombre,sexo,
s.nombre as seccion, cantidadhijos
from empleados as e
join secciones as s
on codigo=seccion;

Vemos la información contenida en la vista:


select *from vista_empleados;

Eliminamos la vista "vista_empleados2" si existe:


if object_id('vista_empleados2') is not null
drop view vista_empleados2;

333
Creamos otra vista de "empleados" denominada "vista_empleados2" que consulta solamente la tabla
"empleados" con "with check option":
create view vista_empleados2
as
select nombre, apellido,fechaingreso,seccion,estadocivil,sueldo
from empleados
where sueldo>=600
with check option;

Consultamos la vista:
select *from vista_empleados2;

Ingresamos un registro en la vista "vista_empleados2":


insert into vista_empleados2 values('Pedro','Perez','2000-10-10',1,'casado',800);

No es posible insertar un registro en la vista "vista_empleados" porque el campo de la vista "nombre" es un


campo calculado.
Actualizamos la sección de un registro de la vista "vista_empleados":
update vista_empleados set seccion='Sistemas' where nombre='Lopez Ana';

Si intentamos actualizar el nombre de un empleado no lo permite porque es una columna calculada.


Actualizamos el nombre de un registro de la vista "vista_empleados2":
update vista_empleados2 set nombre='Beatriz' where nombre='Ana';

Verifique que se actualizó la tabla:


select *from empleados;

Eliminamos un registro de la vista "vista_empleados2":


delete from vista_empleados2 where apellido='Lopez';

Si podemos eliminar registros de la vista "vista_empleados2" dicha vista solamente consulta una tabla. No
podemos eliminar registros de la vista "vista_empleados" porque hay varias tablas en su definición.
Primer problema:
Un club dicta cursos de distintos deportes. Almacena la información en varias tablas.
1- Elimine las tabla "inscriptos", "socios" y "cursos", si existen:
if object_id('inscriptos') is not null
drop table inscriptos;
if object_id('socios') is not null
drop table socios;
if object_id('cursos') is not null
drop table cursos;

2- Cree las tablas:


create table socios(
documento char(8) not null,
nombre varchar(40),
domicilio varchar(30),
constraint PK_socios_documento
primary key (documento)
);

334
create table cursos(
numero tinyint identity,
deporte varchar(20),
dia varchar(15),
constraint CK_inscriptos_dia check (dia
in('lunes','martes','miercoles','jueves','viernes','sabado')),
profesor varchar(20),
constraint PK_cursos_numero
primary key (numero),
);

create table inscriptos(


documentosocio char(8) not null,
numero tinyint not null,
matricula char(1),
constraint PK_inscriptos_documento_numero
primary key (documentosocio,numero),
constraint FK_inscriptos_documento
foreign key (documentosocio)
references socios(documento)
on update cascade,
constraint FK_inscriptos_numero
foreign key (numero)
references cursos(numero)
on update cascade
);

3- Ingrese algunos registros para todas las tablas:


insert into socios values('30000000','Fabian Fuentes','Caseros 987');
insert into socios values('31111111','Gaston Garcia','Guemes 65');
insert into socios values('32222222','Hector Huerta','Sucre 534');
insert into socios values('33333333','Ines Irala','Bulnes 345');

insert into cursos values('tenis','lunes','Ana Acosta');


insert into cursos values('tenis','martes','Ana Acosta');
insert into cursos values('natacion','miercoles','Ana Acosta');
insert into cursos values('natacion','jueves','Carlos Caseres');
insert into cursos values('futbol','sabado','Pedro Perez');
insert into cursos values('futbol','lunes','Pedro Perez');
insert into cursos values('basquet','viernes','Pedro Perez');

insert into inscriptos values('30000000',1,'s');


insert into inscriptos values('30000000',3,'n');
insert into inscriptos values('30000000',6,null);
insert into inscriptos values('31111111',1,'s');
insert into inscriptos values('31111111',4,'s');
insert into inscriptos values('32222222',1,'s');
insert into inscriptos values('32222222',7,'s');

4- Realice un join para mostrar todos los datos de todas las tablas, sin repetirlos:
select documento,nombre,domicilio,c.numero,deporte,dia, profesor,matricula
from socios as s
join inscriptos as i
on s.documento=documentosocio
join cursos as c
on c.numero=i.numero;

5- Elimine, si existe, la vista "vista_cursos":


if object_id('vista_cursos') is not null
drop view vista_cursos;

6- Cree la vista "vista_cursos" que muestre el número, deporte y día de todos los
cursos.

335
7- Consulte la vista ordenada por deporte.

8- Ingrese un registro en la vista "vista_cursos" y vea si afectó a "cursos".


Puede realizarse el ingreso porque solamente afecta a una tabla base.

9- Actualice un registro sobre la vista y vea si afectó a la tabla "cursos".


Puede realizarse la actualización porque solamente afecta a una tabla base.

10- Elimine un registro de la vista para el cual no haya inscriptos y vea si afectó a
"cursos".
Puede realizarse la eliminación porque solamente afecta a una tabla base.

11- Intente eliminar un registro de la vista para el cual haya inscriptos.


No lo permite por la restricción "foreign key".

12- Elimine la vista "vista_inscriptos" si existe y créela para que muestre el


documento y nombre
del socio, el numero de curso, el deporte y día de los cursos en los cuales está
inscripto.

13- Intente ingresar un registro en la vista.


No lo permite porque la modificación afecta a más de una tabla base.

14- Actualice un registro de la vista.


Lo permite porque la modificación afecta a una sola tabla base.

15- Vea si afectó a la tabla "socios":


select *from socios;

16- Intente actualizar el documento de un socio.


No lo permite por la restricción.

17- Intente eliminar un registro de la vista.


No lo permite porque la vista incluye varias tablas.

109 - Vistas modificar (alter view)


Para modificar una vista puede eliminarla y volver a crearla o emplear "alter view".
Con "alter view" se modifica la definición de una vista sin afectar los procedimientos almacenados
y los permisos. Si elimina una vista y vuelve a crearla, debe reasignar los permisos asociados a
ella.
Sintaxis básica para alterar una vista:
alter view NOMBREVISTA
with encryption--opcional
as SELECT

En el ejemplo siguiente se altera vista_empleados para agregar el campo "domicilio":


alter view vista_empleados
with encryption
as
select (apellido+' '+e.nombre) as nombre,sexo,
s.nombre as seccion, cantidadhijos,domicilio
from empleados as e
336
join secciones as s
on codigo=seccion

Si creó la vista con "with encryption" y quiere modificarla manteniendo la encriptación, debe
colocarla nuevamente, en caso de no hacerlo, desaparece.
Si crea una vista con "select *" y luego agrega campos a la estructura de las tablas involucradas,
los nuevos campos no aparecerán en la vista; esto es porque los campos se seleccionan al
ejecutar "create view"; debe alterar la vista.
Una empresa almacena la información de sus empleados en dos tablas llamadas "empleados" y
"secciones".
Eliminamos las tablas, si existen:
if object_id('empleados') is not null
drop table empleados;
if object_id('secciones') is not null
drop table secciones;

Creamos las tablas:


create table secciones(
codigo tinyint identity,
nombre varchar(20),
constraint PK_secciones primary key (codigo)
);

create table empleados(


legajo int identity,
documento char(8),
nombre varchar(30),
domicilio varchar(30),
seccion tinyint not null,
constraint FK_empleados_seccion
foreign key (seccion)
references secciones(codigo)
on update cascade,
constraint PK_empleados
primary key (documento)
);

Ingresamos algunos registros:


insert into secciones values('Administracion');
insert into secciones values('Contaduría');
insert into secciones values('Sistemas');

insert into empleados values('22222222','Lopez Ana','Colon 123',1);


insert into empleados values('23333333','Lopez Luis','Sucre 235',1);
insert into empleados values('24444444','Garcia Marcos','Sarmiento 1234',2);
insert into empleados values('25555555','Gomez Pablo','Bulnes 321',3);
insert into empleados values('26666666','Perez Laura','Peru 1254',3);

Eliminamos la vista "vista_empleados" si existe:


if object_id('vista_empleados') is not null
drop view vista_empleados;

Creamos la vista "vista_empleados" encriptada que muestre algunos campos de los empleados de la sección
1 y colocamos "with check option":
337
create view vista_empleados
with encryption
as
select documento,nombre,seccion
from empleados
where seccion=1
with check option;

Consultamos la vista:
select *from vista_empleados;

Veamos el texto de la vista:


sp_helptext vista_empleados;

No lo permite porque está encriptada.


Si intentamos modificar la sección de un empleado desde la vista la opción "with check option" no lo
permite.
Modificamos la vista para que muestre el domicilio y no colocamos la opción de encriptación ni "with check
option":
alter view vista_empleados
as
select documento,nombre,seccion, domicilio
from empleados
where seccion=1;

Consultamos la vista para ver si se modificó:


select *from vista_empleados;

Aparece el nuevo campo.


Veamos el texto de la vista:
sp_helptext vista_empleados;

Lo permite porque ya no está encriptada.


Actualizamos la sección de un empleado:
update vista_empleados set seccion=2 where documento='22222222';

Si se permite porque la opción "with check option" se quitó de la vista.


Consultamos la vista:
select *from vista_empleados;

Note que el registro modificado ya no aparece porque no pertenece a la sección 1.


Eliminamos la vista "vista_empleados2" si existe:
if object_id('vista_empleados2') is not null
drop view vista_empleados2;

Creamos la vista "vista_empleados2" que muestre todos los campos de la tabla "empleados":

338
create view vista_empleados2
as
select *from empleados;

Consultamos la vista:
select *from vista_empleados2;

Agregamos un campo a la tabla "empleados":


alter table empleados
add sueldo decimal(6,2);

Consultamos la vista "vista_empleados2":


select *from vista_empleados2;

Note que el nuevo campo agregado a "empleados" no aparece, a pesar que la vista indica que muestre todos
los campos de dicha tabla; esto sucede porque los campos se seleccionan al ejecutar "create view", para que
aparezcan debemos alterar la vista:
alter view vista_empleados2
as
select *from empleados;

Consultemos la vista:
select *from vista_empleados2;

Primer problema:
Un club dicta cursos de distintos deportes. Almacena la información en varias tablas.
1- Elimine las tabla "inscriptos", "socios" y "cursos", si existen:
if object_id('inscriptos') is not null
drop table inscriptos;
if object_id('socios') is not null
drop table socios;
if object_id('cursos') is not null
drop table cursos;

2- Cree las tablas:


create table socios(
documento char(8) not null,
nombre varchar(40),
domicilio varchar(30),
constraint PK_socios_documento
primary key (documento)
);

create table cursos(


numero tinyint identity,
deporte varchar(20),
dia varchar(15),
constraint CK_inscriptos_dia check (dia
in('lunes','martes','miercoles','jueves','viernes','sabado')),
profesor varchar(20),
constraint PK_cursos_numero
primary key (numero),
);

create table inscriptos(


documentosocio char(8) not null,
numero tinyint not null,
339
matricula char(1),
constraint PK_inscriptos_documento_numero
primary key (documentosocio,numero),
constraint FK_inscriptos_documento
foreign key (documentosocio)
references socios(documento)
on update cascade,
constraint FK_inscriptos_numero
foreign key (numero)
references cursos(numero)
on update cascade
);

3- Ingrese algunos registros para todas las tablas:


insert into socios values('30000000','Fabian Fuentes','Caseros 987');
insert into socios values('31111111','Gaston Garcia','Guemes 65');
insert into socios values('32222222','Hector Huerta','Sucre 534');
insert into socios values('33333333','Ines Irala','Bulnes 345');

insert into cursos values('tenis','lunes','Ana Acosta');


insert into cursos values('tenis','martes','Ana Acosta');
insert into cursos values('natacion','miercoles','Ana Acosta');
insert into cursos values('natacion','jueves','Carlos Caseres');
insert into cursos values('futbol','sabado','Pedro Perez');
insert into cursos values('futbol','lunes','Pedro Perez');
insert into cursos values('basquet','viernes','Pedro Perez');

insert into inscriptos values('30000000',1,'s');


insert into inscriptos values('30000000',3,'s');
insert into inscriptos values('30000000',6,null);
insert into inscriptos values('31111111',1,'n');
insert into inscriptos values('31111111',4,'s');
insert into inscriptos values('32222222',1,'n');
insert into inscriptos values('32222222',7,'n');

4- Elimine la vista "vista_deudores" si existe:


if object_id('vista_deudores') is not null
drop view vista_deudores;

5- Cree la vista "vista_deudores" que muestre el documento y nombre del socio, el


deporte, el día y
la matrícula, de todas las inscripciones no pagas colocando "with check option".

6- Consulte la vista:
select *from vista_deudores;

7- Veamos el texto de la vista.

8- Intente actualizar a "s" la matrícula de una inscripción desde la vista.


No lo permite por la opción "with check option".

9- Modifique el documento de un socio mediante la vista.

10- Vea si se alteraron las tablas referenciadas en la vista:


select *from socios;
select *from inscriptos;

11- Modifique la vista para que muestre el domicilio, coloque la opción de encriptación
y omita
"with check option".

12- Consulte la vista para ver si se modificó:


select *from vista_deudores;
Aparece el nuevo campo.

340
13- Vea el texto de la vista.
No lo permite porque está encriptada.

14- Actualice la matrícula de un inscripto.


Si se permite porque la opción "with check option" se quitó de la vista.

15- Consulte la vista:


select *from vista_empleados;
Note que el registro modificado ya no aparece porque la matrícula está paga.

16- Elimine la vista "vista_socios" si existe:


if object_id('vista_socios') is not null
drop view vista_socios;

17- Cree la vista "vista_socios" que muestre todos los campos de la tabla "socios".

18- Consulte la vista.

19- Agregue un campo a la tabla "socios".

20- Consulte la vista "vista_socios".


El nuevo campo agregado a "socios" no aparece, pese a que la vista indica que muestre
todos los
campos de dicha tabla.

21- Altere la vista para que aparezcan todos los campos.

22- Consulte la vista.

110 - Lenguaje de control de flujo (case)


La función "case" compara 2 o más valores y devuelve un resultado.
La sintaxis es la siguiente:
case VALORACOMPARAR
when VALOR1 then RESULTADO1
when VALOR2 then RESULTADO2
...
else RESULTADO3
end

Por cada valor hay un "when" y un "then"; si encuentra un valor coincidente en algún "where"
ejecuta el "then" correspondiente a ese "where", si no encuentra ninguna coincidencia, se ejecuta
el "else"; si no hay parte "else" retorna "null". Finalmente se coloca "end" para indicar que el
"case" ha finalizado.
Un profesor guarda las notas de sus alumnos de un curso en una tabla llamada "alumnos" que
consta de los siguientes campos:
- nombre (30 caracteres),
- nota (valor entero entre 0 y 10, puede ser nulo).

341
Queremos mostrar los nombres, notas de los alumnos y en una columna extra llamada
"resultado" empleamos un case que testee la nota y muestre un mensaje diferente si en dicho
campo hay un valor:
- 0, 1, 2 ó 3: 'libre';
- 4, 5 ó 6: 'regular';
- 7, 8, 9 ó 10: 'promocionado';

Esta es la sentencia:
select nombre,nota, resultado=
case nota
when 0 then 'libre'
when 1 then 'libre'
when 2 then 'libre'
when 3 then 'libre'
when 4 then 'regular'
when 5 then 'regular'
when 6 then 'regular'
when 7 then 'promocionado'
when 8 then 'promocionado'
when 9 then 'promocionado'
when 10 then 'promocionado'
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 expresión "case" en cualquier lugar en el que pueda utilizar una expresión.
También se puede emplear con "group by" y funciones de agrupamiento.
Un profesor guarda las notas de sus alumnos de un curso en una tabla llamada "alumnos" que
consta de los siguientes campos:
- nombre (30 caracteres),
- nota (valor entero entre 0 y 10, puede ser nulo),

342
Eliminamos la tabla si existe y la creamos nuevamente:
if object_id('alumnos') is not null
drop table alumnos;

create table alumnos(


nombre varchar(40),
nota tinyint,
constraint CK_alunos_nota check (nota>=0 and nota<=10)
);

Ingresamos algunos registros:


insert into alumnos values('Ana Acosta',8);
insert into alumnos values('Carlos Caseres',4);
insert into alumnos values('Federico Fuentes',2);
insert into alumnos values('Gaston Guzman',3);
insert into alumnos values('Hector Herrero',5);
insert into alumnos values('Luis Luna',10);
insert into alumnos values('Marcela Morales',7);
insert into alumnos values('Marcela Morales',null);

Queremos mostrar el nombre y nota de cada alumno y en una columna extra llamada "condicion"
empleamos un case que testee la nota y muestre un mensaje diferente si en dicho campo hay un valor:
- 0, 1, 2 ó 3: 'libre';
- 4, 5 ó 6: 'regular';
- 7, 8, 9 ó 10: 'promocionado';

Esta es la sentencia:
select nombre,nota, condicion=
case nota
when 0 then 'libre'
when 1 then 'libre'
when 2 then 'libre'
when 3 then 'libre'
when 4 then 'regular'
when 5 then 'regular'
when 6 then 'regular'
when 7 then 'promocionado'
when 8 then 'promocionado'
when 9 then 'promocionado'
when 10 then 'promocionado'
end
from alumnos;

Obtenemos la misma salida pero empleando el "case" con operadores de comparación:


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;

Vamos a agregar el campo "condicion" a la tabla:


alter table alumnos
add condicion varchar(20);

343
Veamos la tabla:
select *from alumnos;

Recordemos que se puede emplear una expresión "case" en cualquier lugar en el que pueda utilizar una
expresión. Queremos actualizar el campo "condicion" para guardar la condición de cada alumno según su
nota:
update alumnos set condicion=
case
when nota<4 then 'libre'
when nota between 4 and 7 then 'regular'
when nota >7 then 'promocionado'
end;

Veamos la tabla actualizada:


select *from alumnos;

Note que el alumno sin nota contiene "null" en "condición" porque no especificamos valor para "else".
Mostramos la cantidad de alumnos libres, regulares y aprobados y en una columna extra mostramos un
mensaje, ordenamos por cantidad:
select condicion, count(*) as cantidad,resultado=
case condicion
when 'libre' then 'repitentes'
when 'regular' then 'rinden final'
when 'promocionado' then 'no rinden final'
else 'sin datos'
end
from alumnos
group by condicion
order by cantidad;

Primer problema:
Una empresa registra los datos de sus empleados en una tabla llamada "empleados".
1- Elimine la tabla "empleados" si existe:
if object_id('empleados') is not null
drop table empleados;

2- Cree la tabla:
create table empleados(
documento char(8) not null,
nombre varchar(30) not null,
sexo char(1),
fechanacimiento datetime,
fechaingreso datetime,
cantidadhijos tinyint,
sueldo decimal(5,2),
primary key(documento)
);

3- Ingrese algunos registros:


insert into empleados values ('22333111','Juan Perez','m','1970-05-10','1987-04-
05',2,550);
insert into empleados values ('25444444','Susana Morales','f','1975-11-06','1990-04-
06',0,650);
insert into empleados values ('20111222','Hector Pereyra','m','1965-03-25','1997-04-
12',3,510);
insert into empleados values ('30000222','Luis LUque','m','1980-03-29','1999-11-
06',1,700);
344
insert into empleados values ('20555444','Laura Torres','f','1965-12-22','2003-11-
06',3,400);
insert into empleados values ('30000234','Alberto Soto','m','1989-10-10','1999-11-
06',2,420);
insert into empleados values ('20125478','Ana Gomez','f','1976-09-21','1998-11-
06',3,350);
insert into empleados values ('24154269','Ofelia Garcia','f','1974-05-12','1990-11-
06',0,390);
insert into empleados values ('30415426','Oscar Torres','m','1978-05-02','1997-11-
06',1,400);

4- Es política de la empresa festejar cada fin de mes, los cumpleaños de todos los
empleados que
cumplen ese mes. Si los empleados son de sexo femenino, se les regala un ramo de rosas,
si son de
sexo masculino, una corbata. La secretaria de la Gerencia necesita saber cuántos ramos
de rosas y
cuántas corbatas debe comprar para el mes de mayo.

5- Además, si el empleado cumple 10,20,30,40... años de servicio, se le regala una


placa
recordatoria. La secretaria de Gerencia necesita saber la cantidad de años de servicio
que cumplen
los empleados que ingresaron en el mes de abril para encargar dichas placas.

6- La empresa paga un sueldo adicional por hijos a cargos. Para un sueldo menor o igual
a $500 el
salario familiar por hijo es de $200, para un sueldo superior, el monto es de $100 por
hijo. Muestre
el nombre del empleado, el sueldo básico, la cantidad de hijos a cargo, el valor del
salario por
hijo, el valor total del salario familiar y el sueldo final con el salario familiar
incluido de
todos los empleados.

111 - Lenguaje de control de flujo (if)


Existen palabras especiales que pertenecen al lenguaje de control de flujo que controlan la
ejecución de las sentencias, los bloques de sentencias y procedimientos almacenados.
Tales palabras son: begin... end, goto, if... else, return, waitfor, while, break y continue.
- "begin... end" encierran un bloque de sentencias para que sean tratados como unidad.
- "if... else": testean una condición; se emplean cuando un bloque de sentencias debe ser
ejecutado si una condición se cumple y si no se cumple, se debe ejecutar otro bloque de
sentencias diferente.
- "while": ejecuta repetidamente una instrucción siempre que la condición sea verdadera.
- "break" y "continue": controlan la operación de las instrucciones incluidas en el bucle "while".
Veamos un ejemplo. Tenemos nuestra tabla "libros"; queremos mostrar todos los títulos de los
cuales no hay libros disponibles (cantidad=0), si no hay, mostrar un mensaje indicando tal
situación:

345
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 condición 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 condición 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;

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


Eliminamos la tabla si existe:
if object_id('libros') is not null
drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40) not null,
autor varchar(30),
editorial varchar(20),
precio decimal(5,2),
cantidad tinyint,
primary key (codigo)
);

Ingresamos los siguientes registros:


insert into libros values('Uno','Richard Bach','Planeta',15,100);
insert into libros values('El aleph','Borges','Emece',20,150);
insert into libros values('Aprenda PHP','Mario Molina','Nuevo siglo',50,200);
insert into libros values('Alicia en el pais de las maravillas','Lewis
Carroll','Emece',15,0);
insert into libros values('Java en 10 minutos','Mario Molina','Emece',40,200);

Mostramos los títulos de los cuales no hay libros disponibles (cantidad=0); en caso que no haya, mostramos
un mensaje:

346
if exists (select *from libros where cantidad=0)
(select titulo from libros where cantidad=0)
else
select 'No hay libros sin stock';

Hacemos un descuento del 10% a todos los libros de editorial "Emece"; si no hay, mostramos 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';

Veamos si se actualizaron:
select *from libros where editorial='Emece';

Eliminamos los libros de los cuales no hay stock (cantidad=0); 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';

Se eliminó un registro, se ejecutó la sentencia del "if" porque había registros que cumplían la condición.
Ejecutamos nuevamente la sentencia anterior:
if exists (select *from libros where cantidad=0)
delete from libros where cantidad=0
else
select 'No hay registros eliminados';

Ahora se ejecutó la sentencia del "else" porque no había registros que cumplieran la condición.
Primer problema:
Una empresa registra los datos de sus empleados en una tabla llamada "empleados".
1- Elimine la tabla "empleados" si existe:
if object_id('empleados') is not null
drop table empleados;

2- Cree la tabla:
create table empleados(
documento char(8) not null,
nombre varchar(30) not null,
sexo char(1),
fechanacimiento datetime,
sueldo decimal(5,2),
primary key(documento)
);

3- Ingrese algunos registros:


insert into empleados values ('22333111','Juan Perez','m','1970-05-10',550);
insert into empleados values ('25444444','Susana Morales','f','1975-11-06',650);
insert into empleados values ('20111222','Hector Pereyra','m','1965-03-25',510);
insert into empleados values ('30000222','Luis LUque','m','1980-03-29',700);
insert into empleados values ('20555444','Laura Torres','f','1965-12-22',400);
insert into empleados values ('30000234','Alberto Soto','m','1989-10-10',420);
insert into empleados values ('20125478','Ana Gomez','f','1976-09-21',350);
insert into empleados values ('24154269','Ofelia Garcia','f','1974-05-12',390);
insert into empleados values ('30415426','Oscar Torres','m','1978-05-02',400);
347
4- Es política de la empresa festejar cada fin de mes, los cumpleaños de todos los
empleados que
cumplen ese mes. Si los empleados son de sexo femenino, se les regala un ramo de rosas,
si son de
sexo masculino, una corbata. La secretaria de la Gerencia necesita saber cuántos ramos
de rosas y
cuántas corbatas debe comprar para el mes de mayo.

Segundo problema:
Un teatro con varias salas guarda la información de las entradas vendidas en una tabla
llamada
"entradas".
1- Elimine la tabla, si existe:
if object_id('entradas') is not null
drop table entradas;

2- Cree la tabla:
create table entradas(
sala tinyint,
fechahora datetime,
capacidad smallint,
entradasvendidas smallint,
primary key(sala,fechahora)
);

3- Ingrese algunos registros:


insert into entradas values(1,'2006-05-10 20:00',300,50);
insert into entradas values(1,'2006-05-10 23:00',300,250);
insert into entradas values(2,'2006-05-10 20:00',400,350);
insert into entradas values(2,'2006-05-11 20:00',400,380);
insert into entradas values(2,'2006-05-11 23:00',400,400);
insert into entradas values(3,'2006-05-12 20:00',350,350);
insert into entradas values(3,'2006-05-12 22:30',350,100);
insert into entradas values(4,'2006-05-12 20:00',250,0);

4- Muestre, si existen, todas las funciones para la cuales hay entradas disponibles,
sino un mensaje
que indique que están agotadas.

112 - Variables de usuario


Las variables nos permiten almacenar un valor y recuperarlo más adelante para emplearlos en
otras sentencias.
Las variables de usuario son específicas de cada conexión y son liberadas automáticamente al
abandonar la conexión.
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

348
colocando "declare" el nombre de la variable que comienza con el símbolo 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 instrucción del lote.


Una variable a la cual no se le ha asignado un valor contiene "null".
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
librería. Para ello podemos emplear una variable para almacenar el precio más alto:
declare @mayorprecio
select @mayorprecio:=max(precio) from libros

349
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 más 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.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe:
if object_id('libros') is not null
drop table libros;

Creamos la tabla:
create table libros(
titulo varchar(30),
autor varchar(25),
editorial varchar(20),
precio decimal(5,2)
);

Ingresamos algunos registros:


insert into libros values('Uno','Bach Richard','Planeta',15);
insert into libros values('El aleph','Borges J. L.','Emece',25);
insert into libros values('Matematica estas ahi','Paenza Adrian','Siglo XXI',15);
insert into libros values('Aprenda PHP','Mario Molina','Siglo XXI',35);
insert into libros values('Java en 10 minutos','Mario Molina','Siglo XXI',35);

Declare una variable llamada "@valor" de tipo "int" y vea su contenido:


declare @valor int
select @valor;

Declare una variable llamada "@nombre" de tipo "varchar(20)", asígnele un valor y vea su contenido:
declare @nombre varchar(20)
set @nombre='Juan'
select @nombre;

Queremos saber todos los datos de los libros con mayor precio de la tabla "libros". Declare una variable de
tipo decimal, busque el precio más alto de "libros" y almacénelo en una variable, luego utilice dicha variable
para mostrar todos los datos del libro:
declare @mayorprecio decimal(5,2)
select @mayorprecio=max(precio) from libros
select *from libros where precio=@mayorprecio;

Primer problema:
Un profesor almacena el documento y nombre de sus alumnos en una tabla llamada
"alumnos" y en otra
tabla llamada "notas" almacena las notas de los mismos.
1- Elimine las tablas, si existen:
if object_id('alumnos') is not null
drop table alumnos;

350
if object_id('notas') is not null
drop table notas;

2- Créelas con los campos necesarios. Agregue una restricción "primary key" para el
campo
"documento" y una restricción "foreign key" para que en la tabla "notas" el documento
del alumno
haga referencia al documento de la tabla "alumnos":
create table alumnos(
documento char(8) not null
constraint CK_alumnos_documento check (documento like '[0-9][0-9][0-9][0-9][0-9][0-
9][0-9][0-9]'),
nombre varchar(30),
constraint PK_alumnos
primary key(documento)
);

create table notas(


documento char(8) not null,
nota decimal(4,2)
constraint CK_notas_nota check (nota between 0 and 10),
constraint FK_notas_documento
foreign key(documento)
references alumnos(documento)
on update cascade
);

3- Ingrese algunos registros:


insert into alumnos values('30111111','Ana Algarbe');
insert into alumnos values('30222222','Bernardo Bustamante');
insert into alumnos values('30333333','Carolina Conte');
insert into alumnos values('30444444','Diana Dominguez');
insert into alumnos values('30555555','Fabian Fuentes');
insert into alumnos values('30666666','Gaston Gonzalez');

insert into notas values('30111111',5.1);


insert into notas values('30222222',7.8);
insert into notas values('30333333',4);
insert into notas values('30444444',2.5);
insert into notas values('30666666',9.9);
insert into notas values('30111111',7.3);
insert into notas values('30222222',8.9);
insert into notas values('30444444',6);
insert into notas values('30666666',8);

4- Declare una variable llamada "@documento" de tipo "char(8)" y vea su contenido:


declare @documento char(8)
select @documento;

5- Intente usar la variable "@documento" para almacenar el documento del alumno con la
nota más alta:
select @documento= documento from notas
where nota=(select max(nota) from notas);
No se puede porque la variable fue declarada en otro lote de sentencias y no es
reconocida.

6- Declare una variable llamada "@documento" de tipo "char(8)" y almacene en ella el


documento del
alumno con la nota más alta, luego recupere el nombre del alumno:
declare @documento char(8)
select @documento= documento from notas
where nota=(select max(nota) from notas)
select nombre from alumnos where documento=@documento;

351
113 - Tipos de datos text, ntext y image
Los tipos de datos "ntext", "text" e "image" representan tipos de datos de longitud fija y variable
en los que se pueden guardar gran cantidad de información, caracteres unicode y no unicode y
datos binarios.
"ntext" almacena datos unicode de longitud variable y el máximo es de aproximadamente
1000000000 caracteres, en bytes, el tamaño es el doble de los caracteres ingresados (2 GB).
"text" almacena datos binarios no unicode de longitud variable, el máximo es de 2000000000
caracteres aprox. (2 GB). No puede emplearse en parámetros de procedimientos almacenados.
"image" es un tipo de dato de longitud variable que puede contener de 0 a 2000000000 bytes (2
GB) aprox. de datos binarios. Se emplea para almacenar gran cantidad de información o gráficos.
Se emplean estos tipos de datos para almacenar valores superiores a 8000 caracteres.
Ninguno de estos tipos de datos admiten argumento para especificar su longitud, como en el caso
de los tipos "char", o "varchar".
Como estos tipos de datos tiene gran tamaño, SQL Server los almacena fuera de los registros, en
su lugar guarda un puntero (de 16 bytes) que apunta a otro sitio que contiene los datos.
Para declarar un campo de alguno de estos tipos de datos, colocamos el nombre del campo
seguido del tipo de dato:
...
NOMBRECAMPO text
....

Otras consideraciones importantes:


- No pueden definirse variables de estos tipos de datos.
- Los campos de estos tipos de datos no pueden emplearse para índices.
- La única restricción que puede aplicar a estos tipos de datos es "default".
- Se pueden asociar valores predeterminados pero no reglas a campos de estos tipos de datos.
- No pueden alterarse campos de estos tipos con "alter table".
Una librería almacena los datos de sus libros en una tabla llamada "libros".
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla con un campo de tipo "text" en el cual almacenaremos la sinopsis (comentario) del libro:
create table libros(
titulo varchar(40),
autor varchar(30),

352
editorial varchar(20),
precio decimal(6,2),
sinopsis text
);

Ingresamos algunos registros:


insert into libros values
('Ilusiones','Richard Bach','Planeta',15,null);
insert into libros values
('Aprenda PHP','Mario Molina','Nuevo Siglo',45,
'Trata todos los temas necesarios para el aprendizaje de PHP');
insert into libros (titulo,autor,editorial) values
('Uno','Richard Bach','Planeta');
insert into libros values
('El aleph','Borges','Emece',18,'Uno de los más célebres libros de este autor');

La siguiente consulta muestra la cantidad de libros que tienen sinopsis:


select count(*)
from libros
where sinopsis is not null;

Agregamos una restricción "default" al campo "sinopsis" (es la única restricción que puede aplicarse a
campos de tipo "text"):
alter table libros
add constraint DF_libros_sinopsis
default 'No hay datos'
for sinopsis;

Ingresamos un registro con valores por defecto:


insert into libros default values;

Recuperamos los registros y vemos que se almacenó el valor por defecto:


select *from libros;

Primer problema:

En una página web se guardan los siguientes datos de las visitas: número de visita,
nombre, mail,
pais, fecha.
1- Elimine la tabla "visitas", si existe:
if object_id('visitas') is not null
drop table visitas;

2- Créela con la siguiente estructura:


create table visitas (
numero int identity,
nombre varchar(30),
mail varchar(50),
pais varchar (20),
fecha datetime
constraint DF_visitas_fecha default getdate(),
comentarios text
constraint DF_visitas_comentarios default 'Ninguno',
constraint PK_visitas
primary key(numero)
);

353
3- Ingrese algunos registros:
insert into visitas values ('Ana Maria
Lopez','AnaMaria@hotmail.com','Argentina','2006-10-10 10:10',null);
insert into visitas values ('Gustavo
Gonzalez','GustavoGGonzalez@hotmail.com','Chile','2006-10-10 21:30',default);
insert into visitas values ('Fabiola
Martinez','MartinezFabiola@hotmail.com','Mexico',default,'Excelente página');
insert into visitas values ('Mariano Perez','PerezM@hotmail.com','Argentina','2006-11-
11 14:30','Muy buena y divertida');

4- Recupere todos los registros:


select *from visitas;

5- Cuente la cantidad de visitas que tienen comentarios.


Retorna 3.

6- Intente agregar una restricción "check" al campo de tipo "text":


alter table visitas
add constraint CK_comentarios
check (comentarios not like '[0-9]%');
No lo permite.

7- Intente alterar el campo de tipo "text" para que no acepte valores nulos:
alter table visitas
alter column comentarios text not null;
No lo permite.

8- Elimine la regla llamada "RG_texto" (si existe):


if object_id('RG_texto') is not null
drop rule RG_texto;

9- Cree la regla "RG_texto" que no permita que el primer caracter sea un dígito:
create rule RG_texto
as @valor not like '[0-9]%';

10- Asóciela al campo "nombre":


exec sp_bindrule RG_texto, 'visitas.nombre';

11- Intente asociarla al campo "comentarios":


exec sp_bindrule RG_texto, 'visitas.comentarios';
No lo permite.

12- Quite la restricción "default" del campo "comentarios":


alter table visitas
drop DF_visitas_comentarios;

13- Ingrese un registro con valores por defecto y recupere todos los registros:
insert into visitas default values;
select *from visitas;

14- Elimine el valor predeterminado llamado "VP_SinComentarios":


if object_id('VP_Sincomentarios') is not null
drop default VP_Sincomentarios;

15- Cree un valor por defecto que almacene el valor "Sin comentarios":
create default VP_Sincomentarios
as 'Sin comentarios';

16- Asócielo al campo "comentarios":


exec sp_bindefault VP_Sincomentarios, 'visitas.comentarios';

17- Ingrese un registro con valores por defecto y recupere todos los registros:
insert into visitas default values;
select *from visitas;

354
114 - Tipo de dato text - ntext e image (punteros)
Explicamos anteriormente que como estos tipos de datos tiene gran tamaño, SQL Server
almacena los datos fuera de los registros; en el registro guarda un puntero (de 16 bytes) que
apunta a otro sitio, que contiene la dirección en la cual se guardan los datos propiamente dichos.
La función "textptr" devuelve el valor del puntero a texto que corresponde al campo text, ntext o
image; tal valor puede emplearse para manipular los datos de este tipo, con las funciones para
leer, escribir y actualizar.
Sintaxis:
textptr(CAMPO);

El campo debe ser tipo text, ntext o image.


En el campo de tipo "text" no se almacenan los datos sino la dirección en la cual se encuentran
los datos. Podemos ver esa dirección tipeando la siguiente sentencia:
select titulo, textptr(sinopsis) from libros;

La función "textptr" retorna un puntero a texto (valor binario de 16). Si el campo no tiene texto,
retorna un puntero a null; por ello se debe usar la función "textvalid" para confirmar si el puntero a
texto existe.
Si la consulta retorna más de un registro, "textptr" retorna un puntero a texto del último registro
devuelto.
La funcion "textvalid" controla si un puntero a texto es válido. Sintaxis:
textvalid ('TABLA.CAMPO', PUNTEROATEXTO);

Los argumentos son: el nombre de la tabla y campo y el nombre del puntero a texto que se va a
controlar. Retorna 1 si el puntero es válido y 0 si no lo es. No se puede emplear "updatetext",
"writetext" y "readtext" si el puntero no es válido.
La siguiente consulta muestra si los punteros son válidos en cada registro del campo "sinopsis"
de la tabla "libros":
select titulo,
textvalid('libros.sinopsis', textptr(sinopsis)) as 'Puntero valido'
from libros;

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 válido, empleando la
variable:
declare @puntero varbinary(16)
select @puntero = textptr(sinopsis)
from libros
where titulo= 'Ilusiones'
select textvalid('libros.sinopsis', @puntero);
355
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 válido. Para crear un puntero a texto válido ejecute un
"insert" o "update" con datos que no sean nulos para el campo text, ntext o image.
Una librería almacena los datos de sus libros en una tabla llamada "libros".
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla con la siguiente estructura:


create table libros(
titulo varchar(40),
autor varchar(30),
editorial varchar(20),
precio decimal(6,2),
sinopsis text
);

Ingresamos algunos registros:


insert into libros values
('Ilusiones','Richard Bach','Planeta',15,null);
insert into libros values
('Aprenda PHP','Mario Molina','Nuevo Siglo',45,
'Trata todos los temas necesarios para el aprendizaje de PHP');
insert into libros (titulo,autor,editorial) values
('Uno','Richard Bach','Planeta');

Veamos la dirección que almacena cada registro en el campo de tipo "text" ("sinopsis") de la tabla "libros":
select titulo, textptr(sinopsis) as direccion from libros;

Note que un sólo libro, el segundo, tiene una dirección, los demás contienen "null".
La siguiente consulta muestra si los punteros son válidos en cada registro del campo "sinopsis" de la tabla
"libros":
select titulo,
textvalid('libros.sinopsis', textptr(sinopsis)) as 'Puntero valido'
from libros;

Note que el primer y tercer libro tienen punteros inválidos, en el primero porque se almacenó "null" y en el
segundo, porque el campo no fue inicializado.
Declaramos una variable a la cual le asignamos el valor del puntero a texto de un registro y luego vemos si
dicho puntero es válido:
declare @puntero varbinary(16)
select @puntero = textptr(sinopsis) from libros
where titulo= 'Ilusiones'
select textvalid('libros.sinopsis', @puntero);

Veamos el siguiente ejemplo:

356
declare @puntero varbinary(16)
select @puntero = textptr(sinopsis) from libros
select textvalid('libros.sinopsis', @puntero);

Declaramos una variable, almacenamos en ella el puntero a texto de "libros"; esta consulta retorna varios
registros, por lo tanto, el puntero devuelve el valor del último registro (cuyo título es 'Uno'). Finalmente
chequeamos si el valor del puntero es válido.

115 - Tipo de dato text - ntext e image (leer)


La función "readtext" lee valores de un campo text, ntext o image, comenzando desde una
posición y leyendo un específico número de bytes. Sintaxis:
readtext TABLA.CAMPO PUNTEROATEXTO
DESPLAZAMIENTO CANTIDAD;

Analicemos la sintaxis:
- PUNTEROATEXTO: puntero a texto válido, binary(16).
- DESPLAZAMIENTO: número de bytes (para text o image) o caracteres (ntext) que se mueve el
puntero antes de comenzar a leer.
- CANTIDAD: número de bytes o caracteres a leer desde la posición indicada por
DESPLAZAMIENTO. Si es 0, se leen 4KB bytes o hasta el final.
Leemos la información almacenada en el campo "sinopsis" de "libros" del registro con código 2,
desde la posición 9, 50 caracteres:
declare @puntero varbinary(16)
select @puntero=textptr(sinopsis)
from libros
where codigo=2
readtext libros.sinopsis @puntero 9 50;

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 válido y al intentar leer dicho campo ocurre un error, porque
la función "readtext" requiere un puntero válido. Para evitarlo podemos chequear el puntero antes
de pasárselo a la función 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';

Primer problema:

En una página web se guardan los siguientes datos de las visitas: número de visita,
nombre, mail,
pais, fecha.

357
1- Elimine la tabla "visitas", si existe:
if object_id('visitas') is not null
drop table visitas;

2- Créela con la siguiente estructura:


create table visitas (
numero int identity,
nombre varchar(30),
mail varchar(50),
pais varchar (20),
fecha datetime
constraint DF_visitas_fecha default getdate(),
comentarios text,
constraint PK_visitas
primary key(numero)
);

3- Ingrese algunos registros:


insert into visitas values ('Ana Maria
Lopez','AnaMaria@hotmail.com','Argentina','2006-10-10 10:10',null);
insert into visitas values ('Gustavo
Gonzalez','GustavoGGonzalez@hotmail.com','Chile','2006-10-10 21:30',default);
insert into visitas values ('Fabiola
Martinez','MartinezFabiola@hotmail.com','Mexico',default,'Excelente página');
insert into visitas values ('Mariano Perez','PerezM@hotmail.com','Argentina','2006-11-
11 14:30','Muy buena y divertida');

4- Leemos la información almacenada en el campo "comentarios" de "visitas" del registro


número 3,
desde la posición 0, 10 caracteres.

5- Intente leer el campo "comentarios" del registro número 1.


Error, porque el puntero es inválido.

6- Recupere el campo "comentarios" de la visita número 1 (desde el comienzo al final),


controlando
que el puntero sea válido.

116 - Tipo de dato text - ntext e image (escribir)


La función "writetext" sobreescribe (reemplaza) el texto de un campo "text", "ntext" o "image".
No puede emplearse en vistas.
Sintaxis:
writetext TABLA.CAMPO PUNTEROATEXTO
DATO;

Luego de "writetext" se coloca el nombre de la tabla y el campo (text, ntext o image) a actualizar.
"PUNTEROATEXTO" es el valor que almacena el puntero a texto del dato de tipo "text", "ntext" o
"image", tal puntero debe ser válido. "DATO" es el texto que almacena, puede ser una variable o
un literal.
Este ejemplo coloca el puntero a texto en una variable "@puntero" y luego "writetext" almacena el
nuevo texto en el registro apuntado por "@puntero":
358
declare @puntero binary(16)
select @puntero = textptr (sinopsis)
from libros
where codigo=2
writetext libros.sinopsis @puntero 'Este es un nuevo libro acerca de PHP escrito por el profesor
Molina que aborda todos los temas necesarios para el aprendizaje desde cero de este lenguaje.';

Recuerde que 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 válido y al intentar escribir dicho campo
ocurre un error, porque la función "writetext" requiere un puntero válido. Para evitarlo podemos
chequer el puntero antes de pasárselo a la función de escritura:
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 más alto que las demas.'
else select 'puntero invalido, no se actualizó el registro';

Una librería almacena los datos de sus libros en una tabla llamada "libros".
Eliminamos la tabla, si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla con la siguiente estructura:


create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(20),
sinopsis text
);

Ingresamos algunos registros:


insert into libros values ('Ilusiones','Richard Bach','Planeta',null);
insert into libros values ('Aprenda PHP','Mario Molina','Nuevo Siglo',
'Contiene todos los temas necesarios para el aprendizaje de PHP');
insert into libros (titulo,autor,editorial) values ('Uno','Richard Bach','Planeta');
insert into libros values ('El Aleph','Borges','Emece','Uno de los libros más célebres
de este autor.');

Colocamos el puntero a texto del campo "sinopsis" del registro con código 2 en una variable "@puntero" y
luego "writetext" almacena el nuevo texto en el registro apuntado por "@puntero":
declare @puntero binary(16)
select @puntero = textptr (sinopsis)
from libros
where codigo=2
writetext libros.sinopsis @puntero 'Este es un nuevo libro acerca de PHP escrito por
el profesor
Molina que aborda todos los temas necesarios para el aprendizaje desde cero de este
lenguaje.';

Leemos el campo "sinopsis" del libro cuyo código es 2, para ver si se actualizó:
declare @puntero binary(16)
select @puntero = textptr (sinopsis)
359
from libros
where codigo=2
readtext libros.sinopsis @puntero 0 0;

Si intentamos actualizar un campo "text", "ntext" o "image" que tiene valor nulo o un puntero inválido,
aparece un mensaje de error, por lo tanto, en el siguiente ejemplo, verificamos si el puntero es válido antes
de pasárselo a la función "writetext":
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 más alto que las
demás.'
else select 'puntero invalido, no se actualizó el registro';

Para crear un puntero válido debemos ingresar un valor con "insert" o "update":
update libros set sinopsis='xx' where codigo=1;

Ahora si podemos actualizar el campo "sinopsis" del registro con código 1 con "writetext" porque ya tiene
un puntero válido:
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 más alto que las
demás.'
else select 'puntero invalido, no se actualizó el registro';

Veamos si se actualizó:
declare @puntero binary(16)
select @puntero = textptr (sinopsis)
from libros
where codigo=1
readtext libros.sinopsis @puntero 0 0;

Creamos un puntero válido al ingresar un nuevo registro:


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

Note que ingresamos una cadena vacía para el campo "sinopsis", con ello creamos un puntero válido.
Actualizamos el campo "sinopsis" del último registro ingresado con "writetext":
declare @puntero varbinary(16)
select @puntero=textptr(sinopsis)
from libros where codigo=5
if (textvalid('libros.sinopsis', @puntero)=1)
writetext libros.sinopsis @puntero 'Trata de las aventuras de una niña en un país muy
extraño.'
else select 'puntero invalido, no se actualizó el registro';

Veamos si se actualizó:
declare @puntero binary(16)
select @puntero = textptr (sinopsis)
from libros

360
where codigo=5
readtext libros.sinopsis @puntero 0 0;

Primer problema:

En una página web se guardan los siguientes datos de las visitas: número de visita,
nombre, mail,
pais, fecha.
1- Elimine la tabla "visitas", si existe:
if object_id('visitas') is not null
drop table visitas;

2- Créela con la siguiente estructura:


create table visitas (
numero int identity,
nombre varchar(30),
mail varchar(50),
pais varchar (20),
fecha datetime
constraint DF_visitas_fecha default getdate(),
comentarios text,
constraint PK_visitas
primary key(numero)
);
3- Ingrese algunos registros:
insert into visitas values ('Ana Maria
Lopez','AnaMaria@hotmail.com','Argentina','2006-10-10 10:10',null);
insert into visitas values ('Gustavo
Gonzalez','GustavoGGonzalez@hotmail.com','Chile','2006-10-10 21:30',default);
insert into visitas values ('Fabiola
Martinez','MartinezFabiola@hotmail.com','Mexico',default,'Excelente página');
insert into visitas values ('Mariano Perez','PerezM@hotmail.com','Argentina','2006-11-
11 14:30','Muy buena y divertida');

4- Recupere todos los registros:


select *from visitas;

5- Reemplace el texto del campo "comentarios" del registro con número 3.

6- Lea el campo "comentarios" de la visita número 3 para ver si se actualizó.

7- Intente actualizar el campo "text" de la visita número 1.


Error, puntero inválido.

8- Vuelva a intentar la actualización del punto anterior pero controlando que el


puntero sea válido.

9- Ingrese un nuevo registro con cadena vacía para el campo "comentarios":


insert into visitas values ('Salvador
Quiroga','salvador@hotmail.com','Argentina','2006-09-09 18:25','');

10- Actualice el campo "comentarios" del registro ingresado anteriormente.

11- Verifique que se actualizó.

117 - Tipo de dato text - ntext e image

361
(actualizar)
Aprendimos que la función "writetext" sobreescribe, reemplaza el contenido completo de un
campo de tipo "text", "ntext" o "image".
Para actualizar campos de estos tipos también empleamos "updatetext", que permite cambiar una
porción del campo (o todo el campo). La sintaxis básica es la siguiente:
updatetext TABLA.CAMPO PUNTEROATEXTO
DESPLAZAMIENTODELPUNTERO
LONGITUDDEBORRADO
DATOAINSERTAR;

Analizamos la sintaxis:
- TABLA.CAMPO: campo y tabla que se va a actualizar.
- PUNTEROATEXTO: valor del puntero, retornado por la función "textptr", que apunta al dato text,
ntext o image que se quiere actualizar.
- DESPLAZAMIENTODELPUNTERO: indica la posición en que inserta el nuevo dato. Especifica
la cantidad de bytes (para campos text e image) o caracteres (para campos ntext) que debe
moverse el puntero para insertar el dato. Los valores pueden ser: 0 (el nuevo dato se inserta al
comienzo), "null" (coloca el puntero al final), un valor mayor a cero y menor o igual a la longitud
total del texto (inserta el nuevo dato en la posición indicada) y un valor mayor a la longitud total
del campo (genera un mensaje de error).
Es importante recordar que cada caracter ntext ocupa 2 bytes.
- LONGITUDDEBORRADO: indica la cantidad de bytes (para text e image) o caracteres (para
ntext) a borrar comenzando de la posición indicada por el parámetro
DESPLAZAMIENTODELPUNTERO. Si colocamos el valor 0 (no se borra ningún dato), "null"
(borra todos los datos desde la posición indicada por el parámetro
DESPLAZAMIENTODELPUNTERO hasta el final), un valor mayor que cero y menor o igual a la
longitud del texto (borra tal cantidad) y un valor inválido, es decir, mayor a la longitud del texto
(genera un mensaje de error).
Es importante recordar que cada caracter "ntext" ocupa 2 bytes.
- 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 función "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)
362
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 función "textptr(sinopsis)", tal puntero apunta al campo "sinopsis" del
libro "Aprenda PHP". Luego actualizamos el campo, colocando el puntero en la posición 18, no
borramos ningún 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 dirección del campo que queremos
actualizar y otro para obtener la dirección del campo del cual extraemos la información.
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 código 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;

Entonces, se emplea "updatetext" para modificar datos de campos de tipo text, ntext e image,
pudiendo cambiar una porción del texto.
Una librería almacena los datos de sus libros en una tabla llamada "libros" y en otra tabla denominada
"ofertas" almacena el título del libro y la sinopsis.
Eliminamos las tablas, si existen:

if object_id('libros') is not null


drop table libros;
if object_id('ofertas') is not null
drop table ofertas;

Creamos las tablas:


create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(20),
sinopsis text
);

create table ofertas(


titulo varchar(40),
sinopsis text
363
);

Ingresamos algunos registros:


insert into libros values ('Ilusiones','Richard Bach','Planeta',null);
insert into libros values ('Aprenda PHP','Mario Molina','Nuevo Siglo','Para aprender
PHP a paso');
insert into libros values ('Uno','Richard Bach','Planeta','');
insert into libros values ('El Aleph','Borges','Emece','Uno de los libros más célebres
de este autor.');

insert into ofertas values ('Aprenda PHP','');

Recuperamos todos los registros de ambas tablas:


select *from libros;
select *from ofertas;

Necesitamos que en la sinopsis del libro "Aprenda PHP" se guarde "Para aprender PHP paso a paso";
debemos insertar en la posición 18, el texto "paso ", sin eliminar ningún caracter. Verificamos que el puntero
sea válido, en caso de no serlo, mostramos un mensaje de error:
declare @puntero binary(16)
select @puntero = textptr(sinopsis)
from libros
where titulo='Aprenda PHP'
if (textvalid('libros.sinopsis',@puntero)=1)
updatetext libros.sinopsis @puntero 18 0 'paso '
else
select 'Puntero inválido';

Leemos el campo "sinopsis" actualizado anteriormente para verificar que se actualizó:


declare @puntero binary(16)
select @puntero = textptr (sinopsis)
from libros
where titulo='Aprenda PHP'

readtext libros.sinopsis @puntero 0 0;

Necesitamos actualizar la sinopsis del libro "Aprenda PHP" de la tabla "ofertas" con la sinopsis del mismo
libro de la tabla "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;

Leemos el campo "sinopsis" actualizado anteriormente para verificar que se actualizó:


declare @puntero binary(16)
select @puntero = textptr (sinopsis)
from ofertas
where titulo='Aprenda PHP'
364
readtext ofertas.sinopsis @puntero 0 0;

Primer problema:

Un maestro almacena los datos de sus alumnos en una tabla denominada "alumnos", incluye
el
documento, el nombre, la nota y un comentario acerca del comportamiento de cada uno de
ellos.
1- Elimine la tabla si existe:
if object_id('alumnos') is not null
drop table alumnos;

2- Créela con la siguiente estructura:


create table alumnos (
documento char(8),
nombre varchar(30),
nota decimal(4,2),
concepto text,
constraint PK_alumnos
primary key(documento)
);

3- Ingrese algunos registros:


insert into alumnos values ('22222222','Ana Acosta',3,'Participativo. Generoso');
insert into alumnos values ('23333333','Carlos Caseres',7,'Poco participativo');
insert into alumnos values ('24444444','Diego Duarte',8,'Buen compañero');
insert into alumnos values ('25555555','Fabiola Fuentes',2,null);

4- Recupere todos los registros:


select *from alumnos;

5- Inserte en el concepto del alumno con documento "23333333" el texto "comunicativo",


en la
posición 5, borrando todos los caracteres siguientes. Verifique que el puntero sea
válido, en caso
de no serlo, muestre un mensaje de error.

6- Lea el campo "concepto" actualizado anteriormente para verificar que se actualizó.

7- Intente actualizar el concepto del alumno con documento "25555555" el texto "Muy
comunicativo".
Verifique que el puntero sea válido, en caso de no serlo, muestre un mensaje de error.
Puntero inválido.

8- Intente agregar texto al campo "concepto" del alumno con documento "24444444" en la
posición 20.
Mensaje de error porque el texto tiene una longitud menor.

9- Inserte en el concepto del alumno con documento "24444444" el texto "alumno y", en
la posición 5,
sin borrar ningún caracter. Verifique que el puntero sea válido antes de pasar el
puntero a la
función "updatetext".

10- Lea el campo "concepto" actualizado anteriormente para verificar que se actualizó.

11- Elimine la tabla "reprobados" si existe:


if object_id('reprobados') is not null
drop table reprobados;

12- Cree la tabla "reprobados" que contenga 2 campos: documento y concepto:


create table reprobados(
documento char(8) not null,
365
concepto text
);

13- Ingrese los siguientes registros en "reprobados" (en el campo "concepto" ingresamos
cadenas
vacías para que se creen punteros válidos):
insert into reprobados values('22222222','');
insert into reprobados values('25555555','');

14- Actualice el "concepto" del alumno "22222222" de la tabla "reprobados" con el


concepto de dicho
alumno de la tabla "alumnos". Verifique que los punteros sean válidos.

15- Verifique la actualización.

16- Intente actualizar el "concepto" del alumno "25555555" de la tabla "reprobados" con
el concepto
de dicho alumno de la tabla "alumnos". Verifique que los punteros sean válidos.
Mensaje de error porque hay un puntero inválido, el de la tabla "alumnos", porque el
registro
consultado contiene "null" en "concepto".

17- Intente actualizar el "concepto" del alumno "23333333" de la tabla "reprobados" con
el concepto
de dicho alumno de la tabla "alumnos". Verifique que los punteros sean válidos.
Mensaje de error porque hay un puntero inválido, el de "reprobados", no existe el
registro
consultado.

118 - Tipo de dato text - ntext e image


(funciones)
Las siguientes son otras funciones que pueden emplearse con estos tipos de datos:
- datalenght(CAMPO): devuelve el número de bytes de un determinado campo. Retorna "null" si el
campo es nulo. Ejemplo:
select titulo, datalength(sinopsis) as longitud
from libros
order by titulo;

- patindex ('PATRON',CAMPO): retorna el comienzo de la primera ocurrencia de un patrón de la


expresión especificada, si el patrón no se encuentra, retorna cero. El patrón es una cadena que
puede incluir comodines.
Ejemplo:
select patindex('%PHP%', sinopsis)
from libros;

Con este tipo de datos también puede utilizarse "like", pero "like" solamente puede incluirse en la
cláusula "where".

366
- substring (TEXTO,INICIO,LONGITUD): devuelve una parte del texto especificado como primer
argumento, empezando desde la posición especificada por el segundo argumento y de tantos
caracteres de longitud como indica el tercer argumento.
Ejemplo:
select titulo,substring(sinopsis,1,20)
from libros;

Una librería almacena los datos de sus libros en una tabla llamada "libros".
Eliminamos la tabla si existe:

if object_id('libros') is not null


drop table libros;

Creamos la tabla:
create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(20),
sinopsis text
);

Ingresamos algunos registros:


insert into libros values ('Ilusiones','Richard Bach','Planeta',null);
insert into libros values ('Aprenda PHP','Mario Molina','Nuevo Siglo',
'Para aprender PHP paso a paso');
insert into libros values ('Programación elemental en PHP','Mario Molina','Planeta',
'Contiene conceptos básicos de PHP');

Veamos la longitud del campo "sinopsis" de todos los libros:


select titulo, datalength(sinopsis) as longitud
from libros
order by titulo;

Buscamos todos los libros en los cuales en su sinopsis se encuentre el texto "PHP":
select titulo
from libros
where patindex('%PHP%', sinopsis)>0;

Empleamos la función "patindex" para mostrar la posición en que se encuentra la cadena "PHP" en la
sinopsis de todos los libros:
select titulo, patindex('%PHP%', sinopsis) as posicion
from libros;

Note que en el libro en el cual no se encuentra, retorna "0".


Seleccionamos los títulos y los primeros 10 caracteres de la sinopsis de cada uno de ellos:
select titulo,substring(sinopsis,1,10) as sinopsis
from libros;

367
119 - 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 implementación de procedimientos almacenados y
desencadenadores (triggers).
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: están almacenados en la base de datos "master" y llevan el prefijo "sp_"; permiten
recuperar información de las tablas del sistema y pueden ejecutarse en cualquier base de datos.
2) locales: los crea el usuario (próximo 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 están disponibles en la sesión de un solo usuario y se eliminan
automáticamente al finalizar la sesión; los globales están disponibles en las sesiones de todos los
usuarios.
4) extendidos: se implementan como bibliotecas de vínculos dinámicos (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 sintácticamente. 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 algún 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 lógica de la aplicación con las otras aplicaciones, con lo cual el acceso y las
modificaciones de los datos se hacen en un solo sitio.
- permiten realizar todas las operaciones que los usuarios necesitan evitando que tengan acceso
directo a las tablas.
- reducen el tráfico de red; en vez de enviar muchas instrucciones, los usuarios realizan
operaciones enviando una única instrucción, lo cual disminuye el número de solicitudes entre el
cliente y el servidor.
368
120 - Procedimientos almacenados (crear -
ejecutar)
Los procedimientos almacenados se crean en la base de datos seleccionada, excepto los
procedimientos almacenados temporales, que se crean en la base de datos "tempdb".
En primer lugar se deben tipear y probar las instrucciones que se incluyen en el procedimiento
almacenado, luego, si se obtiene el resultado esperado, se crea el procedimiento.
Los procedimientos almacenados pueden hacer referencia a tablas, vistas, a funciones definidas
por el usuario, a otros procedimientos almacenados y a tablas temporales.
Un procedimiento almacenado pueden incluir cualquier cantidad y tipo de instrucciones, excepto:
create default, create procedure, create rule, create trigger y create view.
Se pueden crear otros objetos (por ejemplo índices, tablas), en tal caso deben especificar el
nombre del propietario; se pueden realizar inserciones, actualizaciones, eliminaciones, etc.
Si un procedimiento almacenado crea una tabla temporal, dicha tabla sólo existe dentro del
procedimiento y desaparece al finalizar el mismo. Lo mismo sucede con las variables.
Hemos empleado varias veces procedimientos almacenados del sistema ("sp_help",
"sp_helpconstraint", etc.), ahora aprenderemos a crear nuestros propios procedimientos
almacenados.
Para crear un procedimiento almacenado empleamos la instrucción "create procedure".
La sintaxis básica parcial es:
create procedure NOMBREPROCEDIMIENTO
as INSTRUCCIONES;

Para diferenciar los procedimientos almacenados del sistema de los procedimientos almacenados
locales use un prefijo diferente a "sp_" cuando les de el nombre.
Con las siguientes instrucciones creamos un procedimiento almacenado llamado
"pa_libros_limite_stock" que muestra todos los libros de los cuales hay menos de 10 disponibles:
create proc pa_libros_limite_stock
as
select *from libros
where cantidad <=10;

Entonces, creamos un procedimiento almacenado colocando "create procedure" (o "create proc",


que es la forma abreviada), luego el nombre del procedimiento y seguido de "as" las sentencias
que definen el procedimiento.
"create procedure" debe ser la primera sentencia de un lote.
Para ejecutar el procedimiento almacenado creado anteriormente tipeamos:

369
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;

Vamos a crear un procedimiento almacenado que contenga las siguientes instrucciones:

- eliminación de la tabla "libros" si existe;


- creación de la tabla "libros" con: codigo, titulo, autor, editorial, precio, cantidad;
- ingresode algunos registros.
En primer lugar, debemos eliminarlo, si existe (no hemos aprendido aún a eliminar procedimientos
almacenados, en próximos capítulos lo veremos):
if object_id('pa_crear_libros') is not null
drop procedure pa_crear_libros;

Creamos el procedimiento:
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),
cantidad smallint,
primary key(codigo)
370
)
insert into libros values('Uno','Richard Bach','Planeta',15,5)
insert into libros values('Ilusiones','Richard Bach','Planeta',18,50)
insert into libros values('El aleph','Borges','Emece',25,9)
insert into libros values('Aprenda PHP','Mario Molina','Nuevo siglo',45,100)
insert into libros values('Matematica estas ahi','Paenza','Nuevo siglo',12,50)
insert into libros values('Java en 10 minutos','Mario Molina','Paidos',35,300);

Disponemos un punto y coma (;) en la última instrucción del procedimiento almacenado.


Ejecutamos el procedimiento:
exec pa_crear_libros;

Veamos si ha creado la tabla:


select *from libros;

Ejecutamos el procedimiento almacenado del sistema "sp_help" y el nombre del procedimiento almacenado
para verificar que existe el procedimiento creado recientemente:
sp_help pa_crear_libros;

Aparece el nombre, propietario, tipo y fecha de creación.


Necesitamos un procedimiento almacenado que muestre los libros de los cuales hay menos de 10. En primer
lugar, lo eliminamos si existe:
if object_id('pa_libros_limite_stock') is not null
drop procedure pa_libros_limite_stock;

Creamos el procedimiento:
create proc pa_libros_limite_stock
as
select *from libros
where cantidad <=10;

Ejecutamos el procedimiento almacenado del sistema "sp_help" junto al nombre del procedimiento creado
recientemente para verificar que existe:
sp_help pa_libros_limite_stock;

Aparece el nombre, propietario, tipo y fecha de creación.


Lo ejecutamos:
exec pa_libros_limite_stock;

Modificamos algún registro y volvemos a ejecutar el procedimiento:


update libros set cantidad=2 where codigo=4;
exec pa_libros_limite_stock;

Note que el resultado del procedimiento ha cambiado porque los datos han cambiado.
Primer problema:

Una empresa almacena los datos de sus empleados en una tabla llamada "empleados".
1- Eliminamos la tabla, si existe y la creamos:

371
if object_id('empleados') is not null
drop table empleados;

create table empleados(


documento char(8),
nombre varchar(20),
apellido varchar(20),
sueldo decimal(6,2),
cantidadhijos tinyint,
seccion varchar(20),
primary key(documento)
);

2- Ingrese algunos registros:


insert into empleados values('22222222','Juan','Perez',300,2,'Contaduria');
insert into empleados values('22333333','Luis','Lopez',300,0,'Contaduria');
insert into empleados values ('22444444','Marta','Perez',500,1,'Sistemas');
insert into empleados values('22555555','Susana','Garcia',400,2,'Secretaria');
insert into empleados values('22666666','Jose Maria','Morales',400,3,'Secretaria');

3- Elimine el procedimiento llamado "pa_empleados_sueldo" si existe:


if object_id('pa_empleados_sueldo') is not null
drop procedure pa_empleados_sueldo;

4- Cree un procedimiento almacenado llamado "pa_empleados_sueldo" que seleccione los


nombres,
apellidos y sueldos de los empleados.

5- Ejecute el procedimiento creado anteriormente.

6- Elimine el procedimiento llamado "pa_empleados_hijos" si existe:


if object_id('pa_empleados_hijos') is not null
drop procedure pa_empleados_hijos;

7- Cree un procedimiento almacenado llamado "pa_empleados_hijos" que seleccione los


nombres,
apellidos y cantidad de hijos de los empleados con hijos.

8- Ejecute el procedimiento creado anteriormente.

9- Actualice la cantidad de hijos de algún empleado sin hijos y vuelva a ejecutar el


procedimiento
para verificar que ahora si aparece en la lista.

121 - Procedimientos almacenados (eliminar)


Los procedimientos almacenados se eliminan con "drop procedure". Sintaxis:
drop procedure NOMBREPROCEDIMIENTO;

Eliminamos el procedimiento almacenado llamado "pa_libros_autor":


drop procedure pa_libros_autor;

Si el procedimiento que queremos eliminar no existe, aparece un mensaje de error, para evitarlo,
podemos emplear esta sintaxis:
372
if object_id('NOMBREPROCEDIMIENTO') is not null
drop procedure NOMBREPROCEDIMIENTO;

Eliminamos, si existe, el procedimiento "pa_libros_autor", si no existe, mostramos un mensaje:


if object_id('pa_libros_autor') is not null
drop procedure pa_libros_autor
else
select 'No existe el procedimiento "pa_libros_autor"';

"drop procedure" puede abreviarse con "drop proc".


Se recomienda ejecutar el procedimiento almacenado del sistema "sp_depends" para ver si algún
objeto depende del procedimiento que deseamos eliminar.
Podemos eliminar una tabla de la cual dependa un procedimiento, SQL Server lo permite, pero
luego, al ejecutar el procedimiento, aparecerá un mensaje de error porque la tabla referenciada
no existe.
Eliminamos, si existe, el procedimiento almacenado "pa_crear_libros":

if object_id('pa_crear_libros') is not null


drop procedure pa_crear_libros;

Verificamos que no existe ejecutando "sp_help":


sp_help pa_crear_libros;

Aparece un mensaje de error indicando que no existe.


Creamos el procedimiento:
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),
cantidad smallint,
primary key(codigo)
)
insert into libros values('Uno','Richard Bach','Planeta',15,5)
insert into libros values('Ilusiones','Richard Bach','Planeta',18,50)
insert into libros values('El aleph','Borges','Emece',25,9)
insert into libros values('Aprenda PHP','Mario Molina','Nuevo siglo',45,100)
insert into libros values('Matematica estas ahi','Paenza','Nuevo siglo',12,50)
insert into libros values('Java en 10 minutos','Mario Molina','Paidos',35,300);

Verificamos que existe:


sp_help pa_crear_libros;

Aparece.
Lo eliminamos sin corroborar su existencia:
drop proc pa_crear_libros;
373
Vemos si aparece en la lista de objetos que muestra "sp_help":
sp_help pa_crear_libros;

Aparece un mensaje de error indicando que no existe.


Solicitamos su eliminación nuevamente:
drop proc pa_crear_libros;

No existe, aparece un mensaje de error.


Solicitamos su eliminación verificando si existe, si no existe, mostramos un mensaje:
if object_id('pa_crear_libros') is not null
drop proc pa_crear_libros
else
select 'No existe el procedimiento "pa_crear_libros"';

122 - Procedimientos almacenados (parámetros


de entrada)
Los procedimientos almacenados pueden recibir y devolver información; para ello se emplean
parámetros, de entrada y salida, respectivamente.
Veamos los primeros. Los parámetros de entrada posibilitan pasar información a un
procedimiento.
Para que un procedimiento almacenado admita parámetros de entrada se deben declarar
variables como parámetros al crearlo. La sintaxis es:
create proc NOMBREPROCEDIMIENTO
@NOMBREPARAMETRO TIPO =VALORPORDEFECTO
as SENTENCIAS;

Los parámetros se definen luego del nombre del procedimiento, comenzando el nombre con un
signo arroba (@). Los parámetros son locales al procedimiento, es decir, existen solamente
dentro del mismo. Pueden declararse varios parámetros por procedimiento, se separan por
comas.
Cuando el procedimiento es ejecutado, deben explicitarse valores para cada uno de los
parámetros (en el orden que fueron definidos), a menos que se haya definido un valor por
defecto, en tal caso, pueden omitirse. Pueden ser de cualquier tipo de dato (excepto cursor).
Luego de definir un parámetro y su tipo, opcionalmente, se puede especificar un valor por defecto;
tal valor es el que asume el procedimiento al ser ejecutado si no recibe parámetros. Si no se
coloca valor por defecto, un procedimiento definido con parámetros no puede ejecutarse sin
valores para ellos. El valor por defecto puede ser "null" o una constante, también puede incluir
comodines si el procedimiento emplea "like".

374
Creamos un procedimiento que recibe el nombre de un autor como parámetro 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 parámetro:
exec pa_libros_autor 'Borges';

Creamos un procedimiento que recibe 2 parámetros, 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;

El procedimiento se ejecuta colocando "execute" (o "exec") seguido del nombre del procedimiento
y los valores para los parámetros separados por comas:
exec pa_libros_autor_editorial 'Richard Bach','Planeta';

Los valores de un parámetro pueden pasarse al procedimiento mediante el nombre del parámetro
o por su posición. La sintaxis anterior ejecuta el procedimiento pasando valores a los parámetros
por posición. También podemos emplear la otra sintaxis en la cual pasamos valores a los
parámetros por su nombre:
exec pa_libros_autor_editorial @editorial='Planeta', @autor='Richard Bach';

Cuando pasamos valores con el nombre del parámetro, el orden en que se colocan puede
alterarse.
No podríamos ejecutar el procedimiento anterior sin valores para los parámetros. Si queremos
ejecutar un procedimiento que permita omitir los valores para los parámetros debemos, al crear el
procedimiento, definir valores por defecto para cada parámetro:
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 parámetro a un procedimiento que tiene definido más de un parámetro sin
especificar a qué parámetro corresponde (valor por posición), asume que es el primero. Es decir,
375
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 parámetro, debemos emplear la sintaxis de paso
de valores a parámetros por nombre:
exec pa_libros_autor_editorial2 @editorial='Paidos';

Podemos emplear patrones de búsqueda 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) = '%'
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 posición, 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 parámetro, para el primer parámetro toma el valor por defecto:
exec pa_libros_autor_editorial3 @editorial='P%';

También podríamos haber tipeado:


exec pa_libros_autor_editorial3 default, 'P%';

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


Eliminamos la tabla si existe y la creamos nuevamente:

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)
);

Ingresamos algunos registros:


insert into libros values ('Uno','Richard Bach','Planeta',15);
insert into libros values ('Ilusiones','Richard Bach','Planeta',12);
insert into libros values ('El aleph','Borges','Emece',25);
insert into libros values ('Aprenda PHP','Mario Molina','Nuevo siglo',50);
insert into libros values ('Matematica estas ahi','Paenza','Nuevo siglo',18);
insert into libros values ('Puente al infinito','Bach Richard','Sudamericana',14);
insert into libros values ('Antología','J. L. Borges','Paidos',24);
insert into libros values ('Java en 10 minutos','Mario Molina','Siglo XXI',45);
insert into libros values ('Cervantes y el quijote','Borges- Casares','Planeta',34);
376
Eliminamos el procedimiento almacenado "pa_libros_autor" si existe:
if object_id('pa_libros_autor') is not null
drop procedure pa_libros_autor;

Creamos el procedimiento para que reciba el nombre de un autor y muestre todos los libros del autor
solicitado:
create procedure pa_libros_autor
@autor varchar(30)
as
select titulo, editorial,precio
from libros
where autor= @autor;

Ejecutamos el procedimiento:
exec pa_libros_autor 'Richard Bach';

Empleamos la otra sintaxis (por nombre) y pasamos otro valor:


exec pa_libros_autor @autor='Borges';

Eliminamos, si existe, el procedimiento "pa_libros_autor_editorial":


if object_id('pa_libros_autor_editorial') is not null
drop procedure pa_libros_autor_editorial;

Creamos un procedimiento "pa_libros_autor_editorial" que recibe 2 parámetros, 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;

Ejecutamos el procedimiento enviando los parámetros por posición:


exec pa_libros_autor_editorial 'Richard Bach','Planeta';

Ejecutamos el procedimiento enviando otros valores y lo hacemos por nombre:


exec pa_libros_autor_editorial @autor='Borges',@editorial='Emece';

Si ejecutamos el procedimiento omitiendo los parámetros, aparecerá un mensaje de error.


Eliminamos, si existe, el procedimiento "pa_libros_autor_editorial2":
if object_id('pa_libros_autor_editorial2') is not null
drop procedure pa_libros_autor_editorial2;

Creamos el procedimiento almacenado "pa_libros_autor_editorial2" que recibe los mismos parámetros, esta
vez definimos valores por defecto para cada parámetro:
create procedure pa_libros_autor_editorial2
@autor varchar(30)='Richard Bach',
@editorial varchar(20)='Planeta'
377
as
select titulo,autor,editorial,precio
from libros
where autor= @autor and
editorial=@editorial;

Ejecutamos el procedimiento anterior sin enviarle valores para verificar que usa los valores por defecto:
exec pa_libros_autor_editorial2;

Muestra los libros de "Richard Bach" y editorial "Planeta" (valores por defecto).
Enviamos un solo parámetro al procedimiento:
exec pa_libros_autor_editorial2 'Planeta';

SQL Server asume que es el primero, y no hay registros cuyo autor sea "Planeta".
Especificamos el segundo parámetro, enviando parámetros por nombre:
exec pa_libros_autor_editorial2 @editorial='Planeta';

Muestra los libros de "Richard Bach" (valor por defecto para "autor") de la editorial enviada como
argumento ("Planeta").
Ejecutamos el procedimiento enviando parámetros por nombre en distinto orden:
exec pa_libros_autor_editorial2 @editorial='Nuevo siglo',@autor='Paenza';

Definimos un procedimiento empleando patrones de búsqueda (antes verificamos si existe para eliminarlo):
if object_id('pa_libros_autor_editorial3') is not null
drop procedure pa_libros_autor_editorial3;

create proc pa_libros_autor_editorial3


@autor varchar(30) = '%',
@editorial varchar(30) = '%'
as
select titulo,autor,editorial,precio
from libros
where autor like @autor and
editorial like @editorial;

Ejecutamos el procedimiento enviando parámetro por posición, asume que es el primero:


exec pa_libros_autor_editorial3 'P%';

La sentencia anterior ejecuta el procedimiento almacenado "pa_libros_autor_editorial3" enviando un valor


que es asumido como el primero y para el segundo parámetro toma el valor por defecto; muestra los libros
cuyo autor comience con "P", de cualquier editorial.
Ejecutamos el procedimiento especificando que el valor corresponde al segundo parámetro:
exec pa_libros_autor_editorial3 @editorial='P%';

La sentencia anterior ejecuta el procedimiento almacenado "pa_libros_autor_editorial3" enviando un valor


para el segundo parámetro, para el primer parámetro toma el valor por defecto; muestra los libros de
cualquier autor cuya editorial comience con "P".
La sentencia siguiente muestra lo mismo que la anterior:
378
exec pa_libros_autor_editorial3 default, 'P%';

Primer problema:

Una empresa almacena los datos de sus empleados en una tabla llamada "empleados".
1- Eliminamos la tabla, si existe y la creamos:
if object_id('empleados') is not null
drop table empleados;

create table empleados(


documento char(8),
nombre varchar(20),
apellido varchar(20),
sueldo decimal(6,2),
cantidadhijos tinyint,
seccion varchar(20),
primary key(documento)
);

2- Ingrese algunos registros:


insert into empleados values('22222222','Juan','Perez',300,2,'Contaduria');
insert into empleados values('22333333','Luis','Lopez',300,0,'Contaduria');
insert into empleados values ('22444444','Marta','Perez',500,1,'Sistemas');
insert into empleados values('22555555','Susana','Garcia',400,2,'Secretaria');
insert into empleados values('22666666','Jose Maria','Morales',400,3,'Secretaria');

3- Elimine el procedimiento llamado "pa_empleados_sueldo" si existe:


if object_id('pa_empleados_sueldo') is not null
drop procedure pa_empleados_sueldo;

4- Cree un procedimiento almacenado llamado "pa_empleados_sueldo" que seleccione los


nombres,
apellidos y sueldos de los empleados que tengan un sueldo superior o igual al enviado
como
parámetro.

5- Ejecute el procedimiento creado anteriormente con distintos valores:


exec pa_empleados_sueldo 400;
exec pa_empleados_sueldo 500;

6- Ejecute el procedimiento almacenado "pa_empleados_sueldo" sin parámetros.


Mensaje de error.

7- Elimine el procedimiento almacenado "pa_empleados_actualizar_sueldo" si existe:


if object_id('pa_empleados_actualizar_sueldo') is not null
drop procedure pa_empleados_actualizar_sueldo;

8- Cree un procedimiento almacenado llamado "pa_empleados_actualizar_sueldo" que


actualice los
sueldos iguales al enviado como primer parámetro con el valor enviado como segundo
parámetro.

9- Ejecute el procedimiento creado anteriormente y verifique si se ha ejecutado


correctamente:
exec pa_empleados_actualizar_sueldo 300,350;
select *from empleados;

10- Ejecute el procedimiento "pa_empleados_actualizar_sueldo" enviando un solo


parámetro.
Error.

379
11- Ejecute el procedimiento almacenado "pa_empleados_actualizar_sueldo" enviando en
primer lugar el
parámetro @sueldonuevo y en segundo lugar @sueldoanterior (parámetros por nombre).

12- Verifique el cambio:


select *from empleados;

13- Elimine el procedimiento almacenado "pa_sueldototal", si existe:


if object_id('pa_sueldototal') is not null
drop procedure pa_sueldototal;

14- Cree un procedimiento llamado "pa_sueldototal" que reciba el documento de un


empleado y muestre
su nombre, apellido y el sueldo total (resultado de la suma del sueldo y salario por
hijo, que es de
$200 si el sueldo es menor a $500 y $100, si el sueldo es mayor o igual a $500).
Coloque como valor
por defecto para el parámetro el patrón "%".

15- Ejecute el procedimiento anterior enviando diferentes valores:


exec pa_sueldototal '22333333';
exec pa_sueldototal '22444444';
exec pa_sueldototal '22666666';

16- Ejecute el procedimiento sin enviar parámetro para que tome el valor por defecto.
Muestra los 5 registros.

123 - Procedimientos almacenados (parámetros


de salida)
Dijimos que los procedimientos almacenados pueden devolver información; para ello se
emplean parámetros de salida. El valor se retorna a quien realizó la llamada con parámetros de
salida. Para que un procedimiento almacenado devuelva un valor se debe declarar una variable
con la palabra clave "output" al crear el procedimiento:
create procedure NOMBREPROCEDIMIENTO
@PARAMETROENTRADA TIPO =VALORPORDEFECTO,
@PARAMETROSALIDA TIPO=VALORPORDEFECTO output
as
SENTENCIAS
select @PARAMETROSALIDA=SENTENCIAS;

Los parámetros de salida pueden ser de cualquier tipo de datos, excepto text, ntext e image.
Creamos un procedimiento almacenado al cual le enviamos 2 números y retorna el promedio:
create procedure pa_promedio
@n1 decimal(4,2),
@n2 decimal(4,2),
@resultado decimal(4,2) output
as
select @resultado=(@n1+@n2)/2;

Al ejecutarlo también debe emplearse "output":


380
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 enviándole 2 valores y mostramos el resultado.
La instrucción que realiza la llamada al procedimiento debe contener un nombre de variable para
almacenar el valor retornado.
Creamos un procedimiento almacenado que muestre los títulos, editorial y precio de los libros de
un determinado autor (enviado como parámetro 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
parámetros 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;

Eliminamos el procedimiento almacenado "pa_promedio", si existe:

if object_id('pa_promedio') is not null


drop proc pa_promedio;

Creamos un procedimiento almacenado al cual le enviamos 2 números decimales y retorna el promedio:


create procedure pa_promedio
@n1 decimal(4,2),
@n2 decimal(4,2),
@resultado decimal(4,2) output
as
select @resultado=(@n1+@n2)/2;

Lo ejecutamos enviando diferentes valores:


declare @variable decimal(4,2)
execute pa_promedio 5,6, @variable output
select @variable

execute pa_promedio 5.3,4.7, @variable output


select @variable

execute pa_promedio 9,10, @variable output

381
select @variable;

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


Eliminamos la tabla si existe y la creamos nuevamente:
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)
);

Ingresamos algunos registros:


insert into libros values ('Uno','Richard Bach','Planeta',15);
insert into libros values ('Ilusiones','Richard Bach','Planeta',12);
insert into libros values ('El aleph','Borges','Emece',25);
insert into libros values ('Aprenda PHP','Mario Molina','Nuevo siglo',50);
insert into libros values ('Matematica estas ahi','Paenza','Nuevo siglo',18);
insert into libros values ('Puente al infinito','Richard Bach','Sudamericana',14);
insert into libros values ('Antología','J. L. Borges','Paidos',24);
insert into libros values ('Java en 10 minutos','Mario Molina','Siglo XXI',45);
insert into libros values ('Antología','Borges','Planeta',34);

Eliminamos el procedimiento almacenado "pa_autor_sumaypromedio", si existe:


if object_id('pa_autor_sumaypromedio') is not null
drop proc pa_autor_sumaypromedio;

Creamos un procedimiento almacenado que muestre los títulos, editorial y precio de los libros de un
determinado autor (enviado como parámetro 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 enviando distintos valores:


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

execute pa_autor_sumaypromedio 'Borges', @s output, @p output


select @s as total, @p as promedio

382
execute pa_autor_sumaypromedio 'Mario Molina', @s output, @p output
select @s as total, @p as promedio

Ejecutamos el procedimiento sin pasar el parámetro para autor. Recuerde que en estos casos debemos
colocar los nombres de las variables.
declare @s decimal(6,2), @p decimal(6,2)
execute pa_autor_sumaypromedio @suma=@s output,@promedio= @p output
select @s as total, @p as promedio;

Primer problema:

Una empresa almacena los datos de sus empleados en una tabla llamada "empleados".
1- Eliminamos la tabla, si existe y la creamos:
if object_id('empleados') is not null
drop table empleados;

create table empleados(


documento char(8),
nombre varchar(20),
apellido varchar(20),
sueldo decimal(6,2),
cantidadhijos tinyint,
seccion varchar(20),
primary key(documento)
);

2- Ingrese algunos registros:


insert into empleados values('22222222','Juan','Perez',300,2,'Contaduria');
insert into empleados values('22333333','Luis','Lopez',350,0,'Contaduria');
insert into empleados values ('22444444','Marta','Perez',500,1,'Sistemas');
insert into empleados values('22555555','Susana','Garcia',null,2,'Secretaria');
insert into empleados values('22666666','Jose Maria','Morales',460,3,'Secretaria');
insert into empleados values('22777777','Andres','Perez',580,3,'Sistemas');
insert into empleados values('22888888','Laura','Garcia',400,3,'Secretaria');

3- Elimine el procedimiento llamado "pa_seccion" si existe:


if object_id('pa_seccion') is not null
drop procedure pa_seccion;

4- Cree un procedimiento almacenado llamado "pa_seccion" al cual le enviamos el nombre


de una
sección y que nos retorne el promedio de sueldos de todos los empleados de esa sección
y el valor
mayor de sueldo (de esa sección)

5- Ejecute el procedimiento creado anteriormente con distintos valores.

6- Ejecute el procedimiento "pa_seccion" sin pasar valor para el parámetro "sección".


Luego muestre
los valores devueltos por el procedimiento.
Calcula sobre todos los registros porque toma el valor por defecto.

7- Elimine el procedimiento almacenado "pa_sueldototal", si existe y cree un


procedimiento con ese
nombre que reciba el documento de un empleado y retorne el sueldo total, resultado de
la suma del
sueldo y salario por hijo, que es $200 si el sueldo es menor a $500 y $100 si es mayor
o igual.

8- Ejecute el procedimiento anterior enviando un documento existente.

9- Ejecute el procedimiento anterior enviando un documento inexistente.


383
Retorna "null".

10- Ejecute el procedimiento anterior enviando el documento de un empleado en cuyo


campo "sueldo"
contenga "null".
Retorna "null".

11- Ejecute el procedimiento anterior sin enviar valor para el parámetro "documento".
Retorna el valor calculado del último registro.

124 - Procedimientos almacenados (return)


La instrucción "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 parámetro. 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 parámetros
correspondientes al título y autor DEBEN ingresarse con un valor distinto de "null", los demás son
opcionales. El procedimiento retorna "1" si la inserción se realiza, es decir, si se ingresan valores
para título y autor y "0", en caso que título 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

384
insert into libros values (@titulo,@autor,@editorial,@precio)
return 1
end;

Para ver el resultado, debemos declarar una variable en la cual se almacene el valor devuelto por
el procedimiento; luego, ejecutar el procedimiento asignándole 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;

También podríamos emplear un "if" para controlar el valor de la variable de retorno:


declare @retorno int;
exec @retorno=pa_libros_ingreso 'El gato con botas','Anónimo'
if @retorno=1 print 'Registro ingresado'
else select 'Registro no ingresado porque faltan datos';

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


Eliminamos la tabla si existe y la creamos nuevamente:

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)
);

Ingresamos algunos registros:


insert into libros values ('Uno','Richard Bach','Planeta',15);
insert into libros values ('Ilusiones','Richard Bach','Planeta',12);
insert into libros values ('El aleph','Borges','Emece',25);
insert into libros values ('Aprenda PHP','Mario Molina','Nuevo siglo',50);
insert into libros values ('Matematica estas ahi','Paenza','Nuevo siglo',18);
insert into libros values ('Puente al infinito','Richard Bach','Sudamericana',14);
insert into libros values ('Antología','J. L. Borges','Paidos',24);
insert into libros values ('Java en 10 minutos','Mario Molina','Siglo XXI',45);
insert into libros values ('Antología','Borges','Planeta',34);

Eliminamos el procedimiento llamado "pa_libros_autor", si existe:


if object_id('pa_libros_autor') is not null
drop procedure pa_libros_autor;

Creamos un procedimiento que muestre todos los libros de un autor determinado que se ingresa como
parámetro. 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

385
begin
select 'Debe indicar un autor'
return
end
select titulo from libros where autor = @autor;

Ejecutamos el procedimiento con parámetro:


exec pa_libros_autor 'Borges';

Ejecutamos el procedimiento sin parámetro:


exec pa_libros_autor;

Eliminamos el procedimiento "pa_libros_ingreso", si existe:


if object_id('pa_libros_ingreso') is not null
drop procedure pa_libros_ingreso;

Creamos un procedimiento almacenado que ingresa registros en la tabla "libros". Los parámetros
correspondientes al título y autor DEBEN ingresarse con un valor distinto de "null", los demás son
opcionales. El procedimiento retorna "1" si la inserción se realiza (si se ingresan valores para título y autor)
y "0", en caso que título 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;

Declaramos una variable en la cual almacenaremos el valor devuelto, ejecutamos el procedimiento enviando
los dos parámetros obligatorios y vemos el contenido de la variable:
declare @retorno int
exec @retorno=pa_libros_ingreso 'Alicia en el pais...','Lewis Carroll'
select 'Ingreso realizado=1' = @retorno;

El procedimiento retornó "1", lo cual indica que el registro fue ingresado.


Verifiquemos el ingreso consultando la tabla:
select *from libros;

Ejecutamos los mismos pasos, pero esta vez no enviamos valores al procedimiento:
declare @retorno int
exec @retorno=pa_libros_ingreso
select 'Ingreso realizado=1' = @retorno;

El procedimiento retornó "0", lo cual indica que el registro no fue ingresado.


Verifiquemos que el ingreso no se realizó consultando la tabla:

386
select *from libros;

Empleamos un "if" para controlar el valor de la variable de retorno. Enviando al procedimiento valores para
los parámetros obligatorios:
declare @retorno int
exec @retorno=pa_libros_ingreso 'El gato con botas','Anónimo'
if @retorno=1 select 'Registro ingresado'
else select 'Registro no ingresado porque faltan datos';

Verifiquemos el ingreso consultando la tabla:


select *from libros;

Empleamos nuevamente un "if" y no enviamos valores al procedimiento:


declare @retorno int
exec @retorno=pa_libros_ingreso
if @retorno=1 select 'Registro ingresado'
else select 'Registro no ingresado porque faltan datos';

Verifiquemos que el ingreso no se realizó:


select *from libros;

Primer problema:

Una empresa almacena los datos de sus empleados en una tabla llamada "empleados".
1- Eliminamos la tabla, si existe y la creamos:
if object_id('empleados') is not null
drop table empleados;

create table empleados(


documento char(8),
nombre varchar(20),
apellido varchar(20),
cantidadhijos tinyint,
seccion varchar(20),
primary key(documento)
);

2- Ingrese algunos registros:


insert into empleados values('22222222','Juan','Perez',2,'Contaduria');
insert into empleados values('22333333','Luis','Lopez',0,'Contaduria');
insert into empleados values ('22444444','Marta','Perez',NULL,'Sistemas');
insert into empleados values('22555555','Susana','Garcia',2,'Secretaria');
insert into empleados values('22666666','Jose Maria','Morales',1,'Secretaria');
insert into empleados values('22777777','Andres','Perez',3,'Sistemas');
insert into empleados values('22888888','Laura','Garcia',3,'Secretaria');

3- Elimine el procedimiento llamado "pa_empleados_seccion", si existe:


if object_id('pa_empleados_seccion') is not null
drop procedure pa_empleados_seccion;

4- Cree un procedimiento que muestre todos los empleados de una sección determinada que
se ingresa
como parámetro. Si no se ingresa un valor, o se ingresa "null", se muestra un mensaje y
se sale del
procedimiento.

5- Ejecute el procedimiento enviándole un valor para el parámetro.

387
6- Ejecute el procedimiento sin parámetro.

7- Elimine el procedimiento "pa_actualizarhijos", si existe:


if object_id('pa_actualizarhijos') is not null
drop procedure pa_actualizarhijos;

8- Cree un procedimiento almacenado que permita modificar la cantidad de hijos


ingresando el
documento de un empleado y la cantidad de hijos nueva. Ambos parámetros DEBEN
ingresarse con un
valor distinto de "null". El procedimiento retorna "1" si la actualización se realiza
(si se
ingresan valores para ambos parámetros) y "0", en caso que uno o ambos parámetros no se
ingresen o
sean nulos.

9- Declare una variable en la cual se almacenará el valor devuelto por el


procedimiento, ejecute el
procedimiento enviando los dos parámetros y vea el contenido de la variable.
El procedimiento retorna "1", con lo cual indica que fue actualizado.

10- Verifique la actualización consultando la tabla:


select *from empleados;

11- Ejecute los mismos pasos, pero esta vez envíe solamente un valor para el parámetro
"documento".
Retorna "0", lo que indica que el registro no fue actualizado.

12- Verifique que el registro no se actualizó consultando la tabla:


select *from empleados;

13- Emplee un "if" para controlar el valor de la variable de retorno. Enviando al


procedimiento valores para los parámetros.
Retorna 1.

14- Verifique la actualización consultando la tabla:


select *from empleados;

15- Emplee nuevamente un "if" y envíe solamente valor para el parámetro "hijos".
Retorna 0.

125 - Procedimientos almacenados (información)


Los procedimientos almacenados son objetos, así que para obtener información de ellos pueden
usarse los siguientes procedimientos almacenados del sistema y las siguientes tablas:
- "sp_help": sin parámetros nos muestra todos los objetos de la base de datos seleccionada,
incluidos los procedimientos. En la columna "Object_type" aparece "stored procedure" si es un
procedimiento almacenado. Si le enviamos como argumento el nombre de un procedimiento,
obtenemos la fecha de creación e información sobre sus parámetros.
- "sp_helptext": seguido del nombre de un procedimiento almacenado nos muestra el texto que
define el procedimiento, excepto si ha sido encriptado.

388
- "sp_stored_procedures": muestra todos los procedimientos almacenados, los propietarios, etc.
Este procedimiento almacenado puede recibir 3 parámetros: @sp_name (nombre, nvarchar,
admite comodines para búsqueda de patrones), @sp_owner (propietario, nvarchar, admite
comodines) y @qualifier (nombre de la base de datos). Por ejemplo, podemos ver todos los
procedimientos almacenados creados por nosotros con esta sentencia:
sp_stored_procedures @sp_name='pa_%';

- "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 y 2) nombre y tipo de los
objetos que dependen del objeto nombrado. Por ejemplo, ejecutamos "sp_depends" seguido del
nombre de un procedimiento:
sp_depends pa_autor_promedio;

aparecen las tablas (y demás 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 demás 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%';--búsqueda con comodín

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


Eliminamos la tabla si existe y la creamos nuevamente:

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)
);

Ingresamos algunos registros:


insert into libros values ('Uno','Richard Bach','Planeta',15);
insert into libros values ('Ilusiones','Richard Bach','Planeta',12);
insert into libros values ('El aleph','Borges','Emece',25);
insert into libros values ('Aprenda PHP','Mario Molina','Nuevo siglo',50);
insert into libros values ('Matematica estas ahi','Paenza','Nuevo siglo',18);
insert into libros values ('Puente al infinito','Richard Bach','Sudamericana',14);
insert into libros values ('Antología','J. L. Borges','Paidos',24);
389
insert into libros values ('Java en 10 minutos','Mario Molina','Siglo XXI',45);
insert into libros values ('Antología','Borges','Planeta',34);

Eliminamos el procedimiento llamado "pa_autor_promedio", si existe y luego lo creamos para que reciba el
nombre de un autor y nos retorne el promedio de los precios de todos los libros de tal autor:
if object_id('pa_autor_promedio') is not null
drop proc pa_autor_promedio;

create procedure pa_autor_promedio


@autor varchar(30)='%',
@promedio decimal(6,2) output
as
select @promedio=avg(precio)
from libros
where autor like @autor;

Ejecutamos el procedimiento "sp_help" seguido del nombre del procedimiento creado:


sp_help pa_autor_promedio;

Obtenemos la fecha de creación e información sobre sus parámetros (nombre, tipo, longitud, etc.).
Veamos el texto de nuestro procedimiento:
sp_helptext pa_autor_promedio;

Ejecutamos el procedimiento almacenado del sistema "sp_stored_procedures":


sp_stored_procedures;

muestra todos los procedimientos almacenados, los propietarios, etc., entre ellos se encuentra el
procedimiento "pa_autor_promedio".
Ejecutamos el procedimiento anterior pero enviándole un valor para el primer parámetro:
sp_stored_procedures 'pa_%';

Ejecutamos "sp_depends" seguido del nombre del procedimiento "pa_autor_promedio":


sp_depends pa_autor_promedio;

Obtenemos la siguiente información: los objetos de los cuales depende el procedimiento, en este caso, los
campos y tablas referenciadas en el mismo: el nombre del objeto (libros), el tipo de objeto (tabla), el campo
(autor y precio) y demás información que no analizaremos.
En caso que haya objetos que dependen del procedimiento, aparece otra tabla; aquí no aparece porque el
procedimiento no tiene objetos que dependan de él.
Ejecutamos el procedimiento "sp_depends" seguido del nombre de la tabla "libros":
sp_depends libros;

aparece todos los objetos que dependen de ella; en este caso, únicamente el procedimiento creado
anteriormente.
Consultamos la tabla del sistema "sysobjects":
select *from sysobjects;

390
Nos muestra nombre y varios datos de todos los objetos de la base de datos actual. La columna "xtype"
indica el tipo de objeto. "P" indica que es un procedimiento almacenado.
Recuperamos todos los procedimientos almacenados creados por nosotros de la tabla "sysobjects":
select *from sysobjects
where xtype='P' and-- tipo procedimiento
name like 'pa%';--búsqueda con comodín

deben aparecer el procedimiento creado anteriormente.


Eliminamos el procedimiento:
drop proc pa_autor_promedio;

Ejecutamos "sp_depends" seguido del nombre de la tabla "libros":


sp_depends libros;

Aparece un mensaje indicando que la tabla no tiene objetos que dependan de ella ni ella depende de ningún
objeto.

126 - Procedimientos almacenados (encriptado)


Dijimos que SQL Server guarda el nombre del procedimiento almacenado en la tabla del sistema
"sysobjects" y su contenido en la tabla "syscomments".
Si no quiere que los usuarios puedan leer el contenido del procedimiento podemos indicarle a
SQL Server que codifique la entrada a la tabla "syscomments" que contiene el texto. Para ello,
debemos colocar la opción "with encryption" al crear el procedimiento:
create procedure NOMBREPROCEDIMIENTO
PARAMETROS
with encryption
as INSTRUCCIONES;

Esta opción es opcional.


Creamos el procedimiento almacenado "pa_libros_autor" con la opción de encriptado:
create procedure pa_libros_autor
@autor varchar(30)=null
with encryption
as
select *from libros
where autor=@autor;

Si ejecutamos el procedimiento almacenado del sistema "sp_helptext" para ver su contenido, no


aparece.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe y la creamos nuevamente:

391
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)
);

Eliminamos el procedimiento llamado "pa_libros_autor", si existe:


if object_id('pa_libros_autor') is not null
drop procedure pa_libros_autor;

Creamos el procedimiento almacenado "pa_libros_autor" con la opción de encriptado:


create procedure pa_libros_autor
@autor varchar(30)=null
with encryption
as
select *from libros
where autor=@autor;

Ejecutamos el procedimiento almacenado del sistema "sp_helptext" para ver su contenido:


sp_help pa_libros_autor;

no aparece.

127 - Procedimientos almacenados (modificar)


Los procedimientos almacenados pueden modificarse, por necesidad de los usuarios o por
cambios en la estructura de las tablas que referencia.
Un procedimiento almacenado existente puede modificarse con "alter procedure". Sintaxis:
alter procedure NOMBREPROCEDIMIENTO
@PARAMETRO TIPO = VALORPREDETERMINADO
as SENTENCIAS;

Modificamos el procedimiento almacenado "pa_libros_autor" para que muestre, además del título,
la editorial y precio:
alter procedure pa_libros_autor
@autor varchar(30)=null
as
if @autor is null
begin
select 'Debe indicar un autor'
return
end
else

392
select titulo,editorial,precio
from libros
where autor = @autor;

Si quiere modificar un procedimiento que se creó con la opción "with encryption" y quiere
conservarla, debe incluirla al alterarlo.
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe y la creamos nuevamente:

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)
);

Ingresamos algunos registros:


insert into libros values ('Uno','Richard Bach','Planeta',15);
insert into libros values ('Ilusiones','Richard Bach','Planeta',12);
insert into libros values ('El aleph','Borges','Emece',25);
insert into libros values ('Aprenda PHP','Mario Molina','Nuevo siglo',50);
insert into libros values ('Matematica estas ahi','Paenza','Nuevo siglo',18);
insert into libros values ('Puente al infinito','Richard Bach','Sudamericana',14);
insert into libros values ('Antología','J. L. Borges','Paidos',24);
insert into libros values ('Java en 10 minutos','Mario Molina','Siglo XXI',45);
insert into libros values ('Antología','Borges','Planeta',34);

Eliminamos el procedimiento llamado "pa_libros_autor", si existe:


if object_id('pa_libros_autor') is not null
drop procedure pa_libros_autor;

Creamos el procedimiento almacenado "pa_libros_autor" con la opción de encriptado para que muestre
todos los títulos de los libros cuyo autor se envía como argumento:
create procedure pa_libros_autor
@autor varchar(30)=null
with encryption
as
select titulo from libros
where autor like @autor;

Ejecutamos el procedimiento:
exec pa_libros_autor 'Richard Bach';

Intentamos ver el contenido del procedimiento:


exec sp_helptext pa_libros_autor;

No se puede porque está encriptado.

393
Modificamos el procedimiento almacenado "pa_libros_autor" para que muestre, además del título, la
editorial y precio, quitándole la encriptación:
alter procedure pa_libros_autor
@autor varchar(30)=null
as
select titulo, editorial, precio from libros
where autor like @autor;

Ejecutamos el procedimiento:
exec pa_libros_autor 'Borges';

Veamos el contenido del procedimiento:


exec sp_helptext pa_libros_autor;

es posible porque ya no está encriptado.


Modificamos el procedimiento almacenado "pa_libros_autor" para que, en caso de no enviarle un valor,
muestre todos los registros:
alter procedure pa_libros_autor
@autor varchar(30)='%'
as
select titulo, editorial, precio from libros
where autor like @autor;

Ejecutamos el procedimiento:
exec pa_libros_autor;

128 - Procedimientos almacenados (insertar)


Podemos ingresar datos en una tabla con el resultado devuelto por un procedimiento
almacenado.
La instrucción siguiente crea el procedimiento "pa_ofertas", que ingresa libros en la tabla
"ofertas":
create proc pa_ofertas
as
select titulo,autor,editorial,precio
from libros
where precio<50;

La siguiente instrucción 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.


Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla si existe y la creamos nuevamente:
394
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)
);

Ingresamos algunos registros:


insert into libros values ('Uno','Richard Bach','Planeta',15);
insert into libros values ('Ilusiones','Richard Bach','Planeta',12);
insert into libros values ('El aleph','Borges','Emece',25);
insert into libros values ('Aprenda PHP','Mario Molina','Nuevo siglo',50);
insert into libros values ('Matematica estas ahi','Paenza','Nuevo siglo',18);
insert into libros values ('Puente al infinito','Richard Bach','Sudamericana',14);
insert into libros values ('Antología','J. L. Borges','Paidos',24);
insert into libros values ('Java en 10 minutos','Mario Molina','Siglo XXI',45);
insert into libros values ('Antología','Borges','Planeta',34);

Eliminamos la tabla "ofertas" si existe y la creamos con los mismos campos de la tabla "libros":
if object_id('ofertas') is not null
drop table ofertas;

create table ofertas(


titulo varchar(40),
autor varchar(30),
editorial varchar(20),
precio decimal(5,2)
);

Eliminamos el procedimiento llamado "pa_ofertas", si existe:


if object_id('pa_ofertas') is not null
drop procedure pa_ofertas;

Creamos el procedimiento para que seleccione los libros cuyo precio no supera los 30 pesos:
create proc pa_ofertas
as
select titulo,autor,editorial,precio
from libros
where precio<=30;

Vamos a ingresar en la tabla "ofertas" el resultado devuelto por el procedimiento almacenado "pa_ofertas":
insert into ofertas exec pa_ofertas;

Veamos el contenido de "ofertas":


select *from ofertas;

Eliminamos la tabla "libros_por_editorial" si existe y luego creamos la tabla con dos campos: nombre de
editorial y cantidad:
if object_id('libros_por_editorial') is not null
drop table libros_por_editorial;

395
create table libros_por_editorial(
editorial varchar(20),
cantidad int
);

Eliminamos el procedimiento llamado "pa_libros_por_editorial", si existe:


if object_id('pa_libros_por_editorial') is not null
drop procedure pa_libros_por_editorial;

Creamos el procedimiento para que cuente la cantidad de libros de cada editorial:


create proc pa_libros_por_editorial
as
select editorial,count(*)
from libros
group by editorial;

Vamos a ingresar en la tabla "libros_por_editorial" el resultado devuelto por el procedimiento almacenado


"pa_libros_por_editorial":
insert into libros_por_editorial exec pa_libros_por_editorial;

Veamos el contenido de la tabla "libros_por_editorial":


select *from libros_por_editorial;

Primer problema:

Un profesor guarda en una tabla llamada "alumnos" el nombre de los alumnos y su nota.
1- Elimine la tabla si existe y créela:
if object_id('alumnos') is not null
drop table alumnos;

create table alumnos(


documento char(8),
nombre varchar(40),
nota decimal(4,2),
primary key(documento)
);

2- Ingrese algunos registros:


insert into alumnos values ('22222222','Pedro Lopez',5);
insert into alumnos values ('23333333','Ana Lopez',4);
insert into alumnos values ('24444444','Maria Juarez',8);
insert into alumnos values ('25555555','Juan Garcia',5.6);
insert into alumnos values ('26666666','Karina Torres',2);
insert into alumnos values ('27777777','Nora Torres',7.5);
insert into alumnos values ('28888888','Mariano Herrero',3.5);

3- Elimine la tabla "aprobados" si existe y créela con los mismos campos de la tabla
"alumnos":
if object_id('aprobados') is not null
drop table aprobados;

create table aprobados(


documento char(8),
nombre varchar(40),
nota decimal(4,2)
);

396
4- Elimine la tabla "desaprobados" si existe y créela con los siguientes campos:
if object_id('desaprobados') is not null
drop table desaprobados;

create table desaprobados(


documento char(8),
nombre varchar(40)
);

5- Elimine el procedimiento llamado "pa_aprobados", si existe:


if object_id('pa_aprobados') is not null
drop procedure pa_aprobados;

6- Cree el procedimiento para que seleccione todos los datos de los alumnos cuya nota
es igual o
superior a 4.

7- Ingrese en la tabla "aprobados" el resultado devuelto por el procedimiento


almacenado "pa_aprobados".

8- Vea el contenido de "aprobados":


select *from aprobados;

9- Elimine el procedimiento llamado "pa_desaprobados", si existe:


if object_id('pa_desaprobados') is not null
drop procedure pa_desaprobados;

10- Cree el procedimiento para que seleccione el documento y nombre de los alumnos cuya
nota es
menor a 4.

11- Ingrese en la tabla "desaprobados" el resultado devuelto por el procedimiento


almacenado
"pa_desaprobados".

12- Vea el contenido de "desaprobados":


select *from desaprobados;

129 - Procedimientos almacenados (anidados)


Un procedimiento almacenado puede llamar a otro procedimiento almacenado. El procedimiento
que es invocado por otro debe existir cuando creamos el procedimiento que lo llama. Es decir, si
un procedimiento A llama a otro procedimiento B, B debe existir al crear A.
Los procedimientos almacenados pueden anidarse hasta 32 niveles.
Creamos un procedimiento almacenado que reciba 2 números enteros y nos retorne el producto
de los mismos:
create procedure pa_multiplicar
@numero1 int,
@numero2 int,
@producto int output
as
select @producto=@numero1*@numero2;

397
Creamos otro procedimiento que nos retorne el factorial de un número, tal procedimiento llamará
al procedimiento "pa_multiplicar":
create procedure pa_factorial
@numero int
as
declare @resultado int
declare @num int
set @resultado=1
set @num=@numero
while (@num>1)
begin
exec pa_multiplicar @resultado,@num, @resultado output
set @num=@num-1
end
select rtrim(convert(char,@numero))+'!='+convert(char,@resultado);

Cuando un procedimiento (A) llama a otro (B), el segundo (B) tiene acceso a todos los objetos que cree el primero
(A).

Eliminamos, si existen, los procedimientos almacenados siguientes:


if object_id('pa_multiplicar') is not null
drop proc pa_multiplicar;
if object_id('pa_factorial') is not null
drop proc pa_factorial;

Creamos un procedimiento almacenado que reciba 2 números enteros y nos retorne el producto de los
mismos:
create procedure pa_multiplicar
@numero1 int,
@numero2 int,
@producto int output
as
select @producto=@numero1*@numero2;

Probamos el procedimiento anterior:


declare @x int
exec pa_multiplicar 3,9, @x output
select @x as '3*9'
exec pa_multiplicar 50,8, @x output
select @x as '50*8';

Creamos un procedimiento que nos retorne el factorial de un número, tal procedimiento llamará al
procedimiento "pa_multiplicar":
create procedure pa_factorial
@numero int
as
if @numero>=0 and @numero<=12
begin
declare @resultado int
declare @num int
set @resultado=1
set @num=@numero
while (@num>1)
begin
exec pa_multiplicar @resultado,@num, @resultado output
set @num=@num-1
398
end
select rtrim(convert(char,@numero))+'!='+convert(char,@resultado)
end
else select 'Debe ingresar un número entre 0 y 12';

Note que en el procedimiento controlamos que el valor sea positivo, además, como los parámetros fueron
definidos de tipo "int" el valor no debe superar 12 porque el factorial excede el tipo.
Ejecutamos el procedimiento que nos retorna el factorial de un número:
exec pa_factorial 5;
exec pa_factorial 10;

Veamos las dependencias del procedimiento "pa_multiplicar":


exec sp_depends pa_multiplicar;

Recordemos que "sp_depends" nos informa sobre los objetos dependientes del objeto nombrado (en este
caso el procedimiento "pa_multiplicar"), mostrando el nombre y tipo de objeto (en este caso el
procedimiento "pa_factorial") y, si existieren, los objetos de los cuales el objeto depende (en este caso,
ninguno).
Veamos las dependencias del procedimiento "pa_factorial":
exec sp_depends pa_factorial;

Recordemos que "sp_depends" nos informa sobre los objetos de los cuales depende el objeto nombrado (en
este caso el procedimiento "pa_multiplicar"), mostrando el nombre, tipo y columna (en este caso el
procedimiento "pa_multiplicar") y, si existieren, los objetos dependientes de él (en este caso, ninguno).

130 - Procedimientos Almacenados (recompilar)


La compilación es un proceso que consiste en analizar el procedimiento almacenado y crear un
plan de ejecución. Se realiza la primera vez que se ejecuta un procedimiento almacenado o si el
procedimiento almacenado se debe volver a compilar (recompilación).
SQL Server recompila automáticamente un procedimiento almacenado si se realiza algún
cambio en la estructura de una tabla (o vista) referenciada en el procedimiento (alter table y alter
view) y cuando se modifican las claves (insert o delete) de una tabla referenciada.
Un procedimiento almacenado puede recompilarse explícitamente. En general se recomienda no
hacerlo excepto si se agrega un índice a una tabla referenciada por el procedimiento o si los
datos han variado mucho desde la última compilación.
SQL Server ofrece tres métodos para recompilar explícitamente un procedimiento almacenado:
1) Se puede indicar, al crear el procedimiento, que SQL Server no guarde en la caché un plan de
ejecución para el procedimiento sino que lo compile cada vez que se ejecute.
En este caso la sintaxis es la siguiente:
create procedure NOMBREPROCEDIMIENTO
PARAMETROS
with recompile
399
as
SENTENCIAS;

2) Podemos especificar "with recompile" al momento de ejecutarlo:


exec NOMBREPROCEDIMIENTO with recompile;

3) Podemos ejecutar el procedimiento almacenado del sistema "sp_recompile". Este


procedimiento vuelve a compilar el procedimiento almacenado (o desencadenador) que se
especifica. La sintaxis es:
exec sp_recompile NOMBREOBJETO;

El parámetro enviado debe ser el nombre de un procedimiento, de un desencadenador, de una


tabla o de una vista. Si es el nombre de una tabla o vista, todos los procedimientos almacenados
que usan tal tabla (o vista) se vuelven a compilar.

131 - Procedimientos Almacenados (con join)


Hasta ahora, hemos creado procedimientos que incluyen una sola tabla o pocas instrucciones
para aprender la sintaxis, pero la funcionalidad de un procedimiento consiste básicamente en
que contengan muchas instrucciones o instrucciones complejas y así evitar tipear repetidamente
dichas instrucciones; además si no queremos que el usuario conozca la estructura de las tablas
involucradas, los procedimientos permiten el acceso a ellas.
Podemos crear procedimientos que incluyan combinaciones (join), subconsultas, varias
instrucciones y llamadas a otros procedimientos.
Podemos crear todos los procedimientos que necesitemos para que realicen todas las
operaciones y consultas.

132 - Tablas temporales


Las tablas temporales son visibles solamente en la sesión actual.
Las tablas temporales se eliminan automáticamente al acabar la sesión o la función o
procedimiento almacenado en el cual fueron definidas. Se pueden eliminar con "drop table".
Pueden ser locales (son visibles sólo en la sesión 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,
...
);
400
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 restricción "foreign key" ni ser indexada, tampoco puede
ser referenciada por una vista.
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;

133 - 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
expresión 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 configuración, cursor, meta data,
seguridad y estadísticas del sistema son no deterministicas.
SQL Server provee muchas funciones y además permite que el usuario pueda definir sus propias
funciones.
Sabemos que una función es un conjunto de sentencias que operan como una unidad lógica, una
rutina que retorna un valor. Una función tiene un nombre, acepta parámetros de entrada y retorna
un valor escalar o una tabla.
Los parámetros de entrada pueden ser de cualquier tipo, excepto timestamp, cursor y table.
401
Las funciones definidas por el usuario no permiten parámetros de salida.
No todas las sentencias SQL son válidas dentro de una función. NO es posible emplear en ellas
funciones no determinadas (como getdate()) ni sentencias de modificación o actualización de
tablas o vistas. Si podemos emplear sentencias de asignación, de control de flujo (if), de
modificación y eliminación de variables locales.
SQL Server admite 3 tipos de funciones definidas por el usuario clasificadas según el valor
retornado:
1) escalares: retornan un valor escalar;
2) de tabla de varias instrucciones (retornan una tabla) y
3) de tabla en línea (retornan una tabla).
Las funciones definidas por el usuario se crean con la instrucción "create function" y se eliminan
con "drop function".

134 - Funciones (drop)


Las funciones definidas por el usuario se eliminan con la instrucción "drop function":
Sintaxis:
drop function NOMBREPPROPIETARIO.NOMBREFUNCION;

Se coloca el nombre del propietario seguido del nombre de la función.


Si la función que se intenta eliminar no existe, aparece un mensaje indicándolo, para evitarlo,
podemos verificar su existencia antes de solicitar su eliminación (como con cualquier otro objeto):
if object_id('NOMBREPROPIETARIO.NOMBREFUNCION') is not null
drop function NOMBREPROPIETARIO.NOMBREFUNCION;

Eliminamos, si existe, la función denominada "f_fechacadena":


if object_id('dbo.f_fechacadena') is not null
drop function dbo.f_fechacadena;

135 - Funciones escalares (crear y llamar)


Una función escalar retorna un único valor. Como todas las funciones, se crean con la
instrucción "create function". La sintaxis básica es:
create function NOMBRE
(@PARAMETRO TIPO=VALORPORDEFECTO)
returns TIPO
begin
INSTRUCCIONES

402
return VALOR
end;

Luego del nombre se colocan (opcionalmente) los parámetros de entrada con su tipo.
La cláusula "returns" indica el tipo de dato retornado.
El cuerpo de la función, se define en un bloque "begin...end" que contiene las instrucciones que
retornan el valor. El tipo del valor retornado puede ser de cualquier tipo, excepto text, ntext,
image, cursor o timestamp.
Creamos una simple función denominada "f_promedio" que recibe 2 valores y retorna el
promedio:
create function f_promedio
(@valor1 decimal(4,2),
@valor2 decimal(4,2)
)
returns decimal (6,2)
as
begin
declare @resultado decimal(6,2)
set @resultado=(@valor1+@valor2)/2
return @resultado
end;

Entonces, luego de "create function" y el nombre de la función, se deben especificar los


parámetros de entrada con sus tipos de datos (entre paréntesis), 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".
En el ejemplo anterior se declara una variable local a la función (desaparece al salir de la función)
que calcula el resultado que se retornará.
Al hacer referencia a una función escalar, se debe especificar el propietario y el nombre de la
función:
select dbo.f_promedio(5.5,8.5);

Cuando llamamos a funciones que tienen definidos parámetros de entrada DEBEMOS suministrar
SIEMPRE un valor para él.
Si llamamos a la función anterior sin enviarle los valores para los parámetros:
select dbo.f_promedio();

SQL Server muestra un mensaje de error indicando que necesita argumentos.


Creamos una función a la cual le enviamos una fecha y nos retorna el nombre del mes en
español:
create function f_nombreMes
(@fecha datetime='2007/01/01')
returns varchar(10)
as
begin
declare @nombre varchar(10)
set @nombre=
case datename(month,@fecha)
403
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 función, especificamos los parámetros


de entrada con sus tipos de datos (entre paréntesis). El parámetro de entrada tiene definido un
valor por defecto.
Luego de los parámetros 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 función escalar, se debe especificar el propietario y el nombre de la
función:
select nombre,
dbo.f_nombreMes(fechaingreso) as 'mes de ingreso'
from empleados;

No olvide que cuando invocamos funciones que tienen definidos parámetros de entrada
DEBEMOS suministrar SIEMPRE un valor para él.
Podemos colocar un valor por defecto al parámetro, pero al invocar la función, para que tome el
valor por defecto DEBEMOS especificar "default". Por ejemplo, si llamamos a la función 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 instrucción "create function" debe ser la primera sentencia de un lote.


Una empresa tiene almacenados los datos de sus empleados en una tabla denominada
"empleados".
Eliminamos la tabla, si existe y la creamos con la siguiente estructura:
if object_id('empleados') is not null
drop table empleados;

create table empleados(


404
documento char(8) not null,
nombre varchar(30),
fechaingreso datetime,
mail varchar(50),
telefono varchar(12)
);

Ingresamos algunos registros:


insert into empleados values('22222222', 'Ana
Acosta','1985/10/10','anaacosta@gmail.com','4556677');
insert into empleados values('23333333', 'Bernardo Bustos',
'1986/02/15',null,'4558877');
insert into empleados values('24444444', 'Carlos Caseros','1999/12/02',null,null);
insert into empleados values('25555555', 'Diana Dominguez',null,null,'4252525');

Eliminamos, si existe, la función "f_fechaCadena":


if object_id('dbo.f_fechaCadena') is not null
drop function dbo.f_fechaCadena;

Creamos una función a la cual le enviamos una fecha (de tipo varchar), en el cuerpo de la función se analiza
si el dato enviado corresponde a una fecha, si es así, se almacena en una variable el mes (en español) y se le
concatenan el día y el año y se retorna esa cadena; en caso que el valor enviado no corresponda a una fecha,
la función retorna la cadena 'Fecha inválida':
create function f_fechaCadena
(@fecha varchar(25))
returns varchar(25)
as
begin
declare @nombre varchar(25)
set @nombre='Fecha inválida'
if (isdate(@fecha)=1)
begin
set @fecha=cast(@fecha as datetime)
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
set @nombre=rtrim(cast(datepart(day,@fecha) as char(2)))+' de '+@nombre
set @nombre=@nombre+' de '+ rtrim(cast(datepart(year,@fecha)as char(4)))
end--si es una fecha válida
return @nombre
end;

Recuperamos los registros de "empleados", mostrando el nombre y la fecha de ingreso empleando la función
creada anteriormente:
select nombre, dbo.f_fechaCadena(fechaingreso) as ingreso from empleados;

405
Note que el registro con valor nulo en el campo "fechaingreso" muestra "fecha inválida".
Empleamos la función en otro contexto:
select dbo.f_fechaCadena(getdate());

Primer problema:

Una clínica almacena los turnos para los distintos médicos en una tabla llamada
"consultas" y en
otra tabla "medicos" los datos de los médicos.
1- Elimine las tablas si existen:
if object_id('consultas') is not null
drop table consultas;
if object_id('medicos') is not null
drop table medicos;

2- Cree las tablas con las siguientes estructuras:


create table medicos (
documento char(8) not null,
nombre varchar(30),
constraint PK_medicos
primary key clustered (documento)
);

create table consultas(


fecha datetime,
medico char(8) not null,
paciente varchar(30),
constraint PK_consultas
primary key (fecha,medico),
constraint FK_consultas_medico
foreign key (medico)
references medicos(documento)
on update cascade
on delete cascade
);

3- Ingrese algunos registros:


insert into medicos values('22222222','Alfredo Acosta');
insert into medicos values('23333333','Pedro Perez');
insert into medicos values('24444444','Marcela Morales');

insert into consultas values('2007/03/26 8:00','22222222','Juan Juarez');


insert into consultas values('2007/03/26 8:00','23333333','Gaston Gomez');
insert into consultas values('2007/03/26 8:30','22222222','Nora Norte');
insert into consultas values('2007/03/28 9:00','22222222','Juan Juarez');
insert into consultas values('2007/03/29 8:00','24444444','Nora Norte');
insert into consultas values('2007/03/24 8:30','22222222','Hector Huerta');
insert into consultas values('2007/03/24 9:30','23333333','Hector Huerta');

4- Elimine la función "f_nombreDia" si existe:


if object_id('f_nombreDia') is not null
drop function f_nombreDia;

5- Cree la función "f_nombreDia" que recibe una fecha (tipo string) y nos retorne el
nombre del día
en español.

6- Elimine la función "f_horario" si existe:


if object_id('f_horario') is not null
drop function f_horario;

406
7- Cree la función "f_horario" que recibe una fecha (tipo string) y nos retorne la hora
y minutos.

8- Elimine la función "f_fecha" si existe:


if object_id('f_fecha') is not null
drop function f_fecha;

9- Cree la función "f_fecha" que recibe una fecha (tipo string) y nos retorne la fecha
(sin hora ni
minutos)

10- Muestre todas las consultas del médico llamado 'Alfredo Acosta', incluyendo el día
(emplee la
función "f_nombreDia", la fecha (emplee la función "f_fecha"), el horario (emplee la
función
"f_horario") y el nombre del paciente.

11- Muestre todos los turnos para el día sábado, junto con la fecha, de todos los
médicos.

12- Envíe a la función "f_nombreDia" una fecha y muestre el valor retornado:


declare @valor char(30)
set @valor='2007/04/09'
select dbo.f_nombreDia(@valor);

Segundo problema:

Una empresa almacena datos de sus empleados en una tabla denominada "empleados".
1- Elimine la tabla si existe y créela con la siguiente estructura:
if object_id('empleados') is not null
drop table empleados;
create table empleados(
documento char(8) not null,
nombre varchar(30),
fechanacimiento datetime,
fechaingreso datetime,
telefono char(12),
mail varchar(50)
);

2- Ingrese algunos registros:


insert into empleados values('22222222', 'Ana Acosta', '1970/10/02', '1995/10/10',
'4556677', 'anitaacosta@hotmail.com');
insert into empleados values('25555555', 'Bernardo Bustos', '1973/01/15',
'1999/02/15', '4789012', null);
insert into empleados values('30000000', 'Carlos Caseros', '1980/5/25', '2001/05/05',
null, null);
insert into empleados values('32222222', 'Estela Esper', '1985/02/20', '2006/12/12',
null, 'estelaesper@gmail.com');

3- Elimine la función "f_edad" si existe:


if object_id('f_edad') is not null
drop function f_edad;

4- Cree la función "f_edad" que reciba 2 fechas (de tipo datetime) y nos retorne un
valor positivo
correspondiente a la diferencia entre ambas.
Recuerde que en las funciones definidas por el usuario no pueden incluir funciones no
determinísticas (como getdate), por ello, debemos enviar la fecha actual.
Note que la función retorna un valor positivo (tinyint), en ella se valida que la
primera fecha a la
cual se le resta la segunda fecha sea mayor. Si quisiéramos calcular la cantidad de
años entre dos

407
fechas podríamos emplear la función del sistema "datediff" que retorna un int, esta
función (ya
vista) retorna un valor negativo si la primera fecha es menor a la segunda fecha
enviada. Pero
nosotros queremos la edad de una persona, así que siempre enviaremos como primera fecha
una
posterior a la segunda.

5- Muestre los nombres de los empleados y la edad (calculada con la función


anteriormente creada)

6- Muestre el nombre de los empleados y la edad (calculada con la función "f_edad") que
tenían al
ingresar a la empresa y los años de servicio.

7- Llame a la función "f_edad" enviándole la fecha actual y su fecha de nacimiento y


muestre el
valor retornado.

8- Intente invocar la función sin enviarle valores.


Mensaje de error.

9- Llame a la función para que tome el valor por defecto del segundo argumento.

10- Elimine la función "f_valorNulo" si existe:


if object_id('f_valorNulo') is not null
drop function f_valorNulo;

11- Cree una función para reemplazar un valor "null" por el texto "No tiene".

12- Muestre todos los empleados, empleando la función "f_valorNulo" enviándole como
argumento los
campos "mail" y "telefono".

136 - Funciones de tabla de varias instrucciones


Hemos visto el primer tipo de funciones definidas por el usuario, que retornan un valor escalar.
Ahora veremos las funciones con varias instrucciones que retornan una tabla.
Las funciones que retornan una tabla pueden emplearse en lugar de un "from" de una consulta.
Este tipo de función es similar a un procedimiento almacenado; la diferencia es que la tabla
retornada por la función puede ser referenciada en el "from" de una consulta, pero el resultado de
un procedimiento almacenado no.
También 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

408
(CAMPO1 TIPO,
CAMPO2 TIPO,
CAMPO3 TIPO
)
as
begin
insert @NOMBRETABLARETORNO
select CAMPOS
from TABLA
where campo OPERADOR @PARAMETRO
RETURN
end

Como cualquier otra función, se crea con "create function" seguida del nombre de la función;
luego (opcionalmente) los parámetros de entrada con su tipo de dato.
La cláusula "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 función se define también 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 función denominada "f_ofertas" que recibe un parámetro. La
función retorna una tabla con el codigo, título, autor y precio de todos los libros cuyo precio sea
inferior al parámetro:
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)
)
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 función 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 función "f_ofertas":
select *from libros as l
join dbo.f_ofertas(25) as o
on l.codigo=o.codigo;

Se puede llamar a la función como si fuese una tabla o vista listando algunos campos:
select titulo,precio from dbo.f_ofertas(40);
409
Trabajamos con la tabla "libros" de una librería.
Eliminamos la tabla, si existe y la creamos con la siguiente estructura:

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(6,2)
);

Ingresamos algunos registros:


insert into libros values('Uno','Richard Bach','Planeta',15);
insert into libros values('Ilusiones','Richard Bach','Planeta',10);
insert into libros values('El aleph','Borges','Emece',25);
insert into libros values('Aprenda PHP','Mario Molina','Siglo XXI',55);
insert into libros values('Alicia en el pais','Lewis Carroll','Paidos',35);
insert into libros values('Matematica estas ahi','Paenza','Nuevo siglo',25);

Eliminamos la función "f_ofertas" si existe":


if object_id('f_ofertas') is not null
drop function f_ofertas;

Creamos la función "f_ofertas" que reciba un parámetro correspondiente a un precio y nos retorne una tabla
con código, título, autor y precio de todos los libros cuyo precio sea inferior al parámetro:
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)
)
as
begin
insert @ofertas
select codigo,titulo,autor,precio
from libros
where precio<@minimo
return
end;

Llamamos a la función como si fuera una tabla, recuerde que podemos omitir el nombre del propietario:
select *from f_ofertas(30);

Realizamos un join entre "libros" y la tabla retornada por la función "f_ofertas" y mostramos todos los
campos de "libros". Incluimos una condición para el autor:
select l.titulo,l.autor,l.editorial
from libros as l
join dbo.f_ofertas(25) as o
on l.codigo=o.codigo

410
where l.autor='Richard Bach';

La siguiente consulta nos retorna algunos campos de la tabla retornada por "f_ofertas" y algunos registros
que cumplen con la condición "where":
select titulo,precio from f_ofertas(40)
where autor like '%B%';

Eliminamos la función "f_listadolibros" si existe":


if object_id('f_listadolibros') is not null
drop function f_listadolibros;

Creamos otra función que retorna una tabla:


create function f_listadolibros
(@opcion varchar(10)
)
returns @listado table
(titulo varchar(40),
detalles varchar(60)
)
as
begin
if @opcion not in ('autor','editorial')
set @opcion='autor'
if @opcion='editorial'
insert @listado
select titulo,
(editorial+'-'+autor) from libros
order by 2
else
if @opcion='autor'
insert @listado
select titulo,
(autor+'-'+editorial) from libros
order by 2
return
end;

Note que si el valor enviado al parámetro no está incluido en la lista de valores especificada, se setea el
parámetro con el valor "autor".
Llamamos a la función enviando el valor "autor":
select *from dbo.f_listadolibros('autor');

Llamamos a la función enviando el valor "editorial":


select *from dbo.f_listadolibros('editorial');

Llamamos a la función enviando un valor inválido:


select *from dbo.f_listadolibros('precio');

Primer problema:

Una empresa almacena los datos de sus empleados en una tabla denominada "empleados".
1- Elimine la tabvla si existe:
if object_id('empleados') is not null
drop table empleados;

411
2- Cree la tabla con la siguiente estructura:
create table empleados(
documento char(8) not null,
apellido varchar(30) not null,
nombre varchar(30) not null,
domicilio varchar(30),
ciudad varchar(30),
fechanacimiento datetime,
constraint PK_empleados
primary key(documento)
);

3- Ingrese algunos registros:


insert into empleados values('22222222','Acosta','Ana','Avellaneda
123','Cordoba','1970/10/10');
insert into empleados values('23333333','Bustos','Bernardo','Bulnes
234','Cordoba','1972/05/15');
insert into empleados values('24444444','Caseros','Carlos','Colon 356','Carlos
Paz','1980/02/25');
insert into empleados values('25555555','Fuentes','Fabiola','Fragueiro 987','Jesus
Maria','1984/06/12');

4- Elimine la función "f_empleados" si existe:


if object_id('f_empleados') is not null
drop function f_empleados;

5- Cree una función que reciba como parámetro el texto "total" o "parcial" y muestre,
en el primer
caso, todos los datos de los empleados y en el segundo caso (si recibe el valor
"parcial"): el
documento, apellido, ciudad y año de nacimiento.

6- Llame a la función creada anteriormente enviándole "total".

7- Llame a la función anteriormente creada sin enviar argumento.


Mensaje de error.

8- Llame a la función enviándole una cadena vacía.

9- Ejecute la función "f_empleados" enviando "parcial" como argumento y recupere


solamente los
registros cuyo domicilio es "Cordoba".

137 - Funciones con valores de tabla en línea


Una función con valores de tabla en línea retorna una tabla que es el resultado de una única
instrucción "select".
Es similar a una vista, pero más flexible en el empleo de parámetros. En una vista no se puede
incluir un parámetro, lo que hacemos es agregar una cláusula "where" al ejecutar la vista. Las
funciones con valores de tabla en línea funcionan como una vista con parámetros.
Sintaxis:
create function NOMBREFUNCION

412
(@PARAMETRO TIPO=VALORPORDEFECTO)
returns table
as
return (
select CAMPOS
from TABLA
where CONDICION
);

Como todas las funciones definidas por el usuario, se crea con "create function" seguido del
nombre que le damos a la función; luego declaramos los parámetros de entrada con su tipo de
dato entre paréntesis. El valor por defecto es opcional.
"returns" especifica "table" como el tipo de datos a retornar. No se define el formato de la tabla a
retornar porque queda establecido en el "select".
El cuerpo de la función no contiene un bloque "begin...end" como las otras funciones.
La cláusula "return" contiene una sola instrucción "select" entre paréntesis. El resultado del
"select" es la tabla que se retorna. El "select" está sujeto a las mismas reglas que los "select" de
las vistas.
Creamos una función con valores de tabla en línea que recibe un valor de autor como parámetro:
create function f_libros
(@autor varchar(30)='Borges')
returns table
as
return (
select titulo,editorial
from libros
where autor like '%'+@autor+'%'
);

Estas funciones retornan una tabla y se hace referencia a ellas en la cláusula "from", como una
vista:
select *from f_libros('Bach');

Recuerde a que todas las funciones que tienen definidos parámetros se les DEBE suministrar
valores para ellos al invocarse.
Recuerde que para que el parámetro tome el valor por defecto (si lo tiene) DEBE enviarse
"default" al llamar a la función; si no le enviamos parámetros, SQL Server muestra un mensaje de
error.
--incorrecto: select *from f_libros();
select *from f_libros(default);--correcto

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


Eliminamos la tabla si existe y la creamos con la siguiente estructura:

if object_id('libros') is not null


drop table libros;

create table libros(


codigo int identity,
titulo varchar(40),

413
autor varchar(30),
editorial varchar(20)
);

Ingresamos algunos registros:


insert into libros values('Uno','Richard Bach','Planeta');
insert into libros values('El aleph','Borges','Emece');
insert into libros values('Ilusiones','Richard Bach','Planeta');
insert into libros values('Aprenda PHP','Mario Molina','Nuevo siglo');
insert into libros values('Matematica estas ahi','Paenza','Nuevo siglo');

Eliminamos, si existe la función "f_libros":


if object_id('f_libros') is not null
drop function f_libros;

Creamos una función con valores de tabla en línea que recibe un valor de autor como parámetro:
create function f_libros
(@autor varchar(30)='Borges')
returns table
as
return (
select titulo,editorial
from libros
where autor like '%'+@autor+'%'
);

Llamamos a la función creada anteriormente enviando un autor:


select *from f_libros('Bach');

Eliminamos, si existe la función "f_libros_autoreditorial":


if object_id('f_libros_autoreditorial') is not null
drop function f_libros_autoreditorial;

Creamos una función con valores de tabla en línea que recibe dos parámetros:
create function f_libros_autoreditorial
(@autor varchar(30)='Borges',
@editorial varchar(20)='Emece')
returns table
as
return (
select titulo,autor,editorial
from libros
where autor like '%'+@autor+'%' and
editorial like '%'+@editorial+'%'
);

Llamamos a la función creada anteriormente:


select *from f_libros_autoreditorial('','Nuevo siglo');

Llamamos a la función creada anteriormente enviando "default" para que tome los valores por defecto:
select *from f_libros_autoreditorial(default,default);

414
138 - 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 instrucción "alter function".
Sintaxis general:
alter function PROPIETARIO.NOMBREFUNCION
NUEVADEFINICION;

Sintaxis para modificar funciones escalares:


alter funtion PROPIETARIO.NOMBREFUNCION
(@PARAMETRO TIPO=VALORPORDEFECTO)
returns TIPO
as
begin
CUERPO
return EXPRESIONESCALAR
end

Sintaxis para modificar una función 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 función con valores de tabla en línea


alter function NOMBREFUNCION
(@PARAMETRO TIPO)
returns TABLE
as
return (SENTENCIAS SELECT)

Veamos un ejemplo. Creamos una función que retorna una tabla en línea:
create function f_libros
(@autor varchar(30)='Borges')
returns table
as
return (
select titulo,editorial
from libros
where autor like '%'+@autor+'%'
);

La modificamos agregando otro campo en el "select":


alter table f_libros
415
(@autor varchar(30)='Borges')
returns table
as
return (
select codigo,titulo,editorial
from libros
where autor like '%'+@autor+'%'
);

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

Eliminamos la tabla si existe y la creamos con la siguiente estructura:


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)
);

Ingresamos algunos registros:


insert into libros values('Uno','Richard Bach','Planeta');
insert into libros values('El aleph','Borges','Emece');
insert into libros values('Ilusiones','Richard Bach','Planeta');
insert into libros values('Aprenda PHP','Mario Molina','Nuevo siglo');
insert into libros values('Matematica estas ahi','Paenza','Nuevo siglo');

Eliminamos, si existe la función "f_libros":


if object_id('f_libros') is not null
drop function f_libros;

Creamos una función que retorna una tabla en línea:


create function f_libros
(@autor varchar(30)='Borges')
returns table
as
return (
select titulo,editorial
from libros
where autor like '%'+@autor+'%'
);

Llamamos a la función creada anteriormente enviando un autor:


select *from f_libros('Bach');

La modificamos agregando otro campo en el "select":


alter function f_libros
(@autor varchar(30)='Borges')
returns table
as
return (
select codigo,titulo,editorial
from libros
where autor like '%'+@autor+'%'
416
);

Probamos la función para ver si se ha modificado:


select *from f_libros('Bach');

139 - Funciones (encriptado)


Las funciones definidas por el usuario pueden encriptarse, para evitar que sean leídas con
"sp_helptext".
Para ello debemos agregar al crearlas la opción "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
select CAMPOS from TABLA
where campo OPERADOR @PARAMETRO
RETURN
end

En funciones con valores de tabla en línea:


create function NOMBREFUNCION
(@PARAMETRO TIPO=VALORPORDEFECTO)
returns table
with encryption
as
return (SELECT);

Veamos un ejemplo:
create function f_libros
417
(@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


función creada anteriormente, SQL Server mostrará un mensaje indicando que tal función está
encriptada.

140 - Funciones (información)


Las funciones son objetos, así que para obtener información de ellos pueden usarse los
siguientes procedimientos almacenados del sistema y las siguientes tablas:
- "sp_help": sin parámetros 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 función escalar, "table function" si es una función de tabla de varias sentencias
y "inline function" si es una función de tabla en línea.
Si le enviamos como argumento el nombre de una función definida por el usuario, obtenemos el
propietario, el tipo de función y la fecha de creación; si es una función de tabla, los campos de la
tabla retornada.
- "sp_helptext": seguido del nombre de una función definida por el usuario nos muestra el texto
que define la función, 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 función definida por el usuario:
sp_depends pa_libroslistado;

aparecen las tablas (y demás objetos) de las cuales depende el procedimiento, es decir, las tablas
(y campos) referenciadas en la misma. No aparecen objetos que dependan de la función porque
no existe ningún objeto que la referencie.
Podemos ejecutar el procedimiento seguido del nombre de una tabla:
sp_depends libros;

418
aparecen las funciones (y demás 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 función definida por el
usuario escalar, muestra "FN", si es una función de tabla de varias sentencias, muestra "TF" y si
es una función de tabla en linea muestra "IF".
Si queremos ver el nombre, tipo y fecha de creación 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');

141 - 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) específica.
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 (inserción, actualización y eliminación), el disparador se
ejecuta (se dispara) en forma automática.
Un trigger se asocia a un evento (inserción, actualización 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 automáticamente.
- no reciben y retornan parámetros.
- 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 inserción en la tabla "ventas" que
compruebe el campo "stock" de un artículo en la tabla "articulos"; el disparador controlaría que,
cuando el valor de "stock" sea menor a la cantidad que se intenta vender, la inserción del nuevo
registro en "ventas" no se realice.
Los disparadores se ejecutan DESPUES de la ejecución de una instrucción "insert", "update" o
"delete" en la tabla en la que fueron definidos. Las restricciones se comprueban ANTES de la

419
ejecución de una instrucción "insert", "update" o "delete". Por lo tanto, las restricciones se
comprueban primero, si se infringe alguna restricción, el desencadenador no llega a ejecutarse.
Los triggers se crean con la instrucción "create trigger". Esta instrucción especifica la tabla en la
que se define el disparador, los eventos para los que se ejecuta y las instrucciones que contiene.
Sintaxis básica:
create triggre NOMBREDISPARADOR
on NOMBRETABLA
for EVENTO- insert, update o delete
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 acción (evento, el tipo de modificación) sobre la tabla o vista que
activará el trigger. Puede ser "insert", "update" o "delete". Debe colocarse al menos UNA acción,
si se coloca más 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 inserción, actualización
o borrado provoca las acciones que el trigger realizará.
Consideraciones generales:
- "create trigger" debe ser la primera sentencia de un bloque y sólo 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 están permitidas en un desencadenador: create database, alter
database, drop database, load database, restore database, load log, reconfigure, restore log, disk
init, disk resize.
- Se pueden crear varios triggers para cada evento, es decir, para cada tipo de modificación
(inserción, actualización o borrado) para una misma tabla. Por ejemplo, se puede crear un "insert
trigger" para una tabla que ya tiene otro "insert trigger".
A continuación veremos la creación de un disparador para el suceso de inserción: "insert triger".

142 - Disparador de inserción (insert trigger)


Podemos crear un disparador para que se ejecute siempre que una instrucción "insert" ingrese
datos en una tabla.
Sintaxis básica:
create triggre NOMBREDISPARADOR
420
on NOMBRETABLA
for insert
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 activarán el trigger.
Luego de "as" se especifican las condiciones y acciones, es decir, las condiciones que determinan
cuando un intento de inserción provoca las acciones que el trigger realizará.
Creamos un trigger sobre la tabla "ventas" para el evento se inserción. 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") dándole un nombre ("DI_ventas_insertar")


sobre ("on") una tabla específica ("ventas") para ("for") el suceso de inserción ("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").
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 acción. 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".

421
"rollback transaction" es la sentencia que deshace la transacción, es decir, borra todas las
modificaciones que se produjeron en la última transacción restableciendo todo al estado anterior.
"raiserror" muestra un mensaje de error personalizado.
Para identificar fácilmente los disparadores de otros objetos se recomienda usar un prefijo y
darles el nombre de la tabla para la cual se crean junto al tipo de acción.
La instrucción "writetext" no activa un disparador.
Una librería almacena los datos de sus libros en una tabla denominada "libros" y en otra tabla
llamada "ventas", las ventas de los mismos.
Eliminamos las tablas si existen:
if object_id('ventas') is not null
drop table ventas;
if object_id('libros') is not null
drop table libros;

Creamos las tablas, con las siguientes estructuras:


create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
precio decimal(6,2),
stock int,
constraint PK_libros primary key(codigo)
);

create table ventas(


numero int identity,
fecha datetime,
codigolibro int not null,
precio decimal (6,2),
cantidad int,
constraint PK_ventas primary key(numero),
constraint FK_ventas_codigolibro
foreign key (codigolibro) references libros(codigo)
);

Ingresamos algunos registros en "libros":


insert into libros values('Uno','Richard Bach',15,100);
insert into libros values('Ilusiones','Richard Bach',18,50);
insert into libros values('El aleph','Borges',25,200);
insert into libros values('Aprenda PHP','Mario Molina',45,200);

Cuando ingresamos un registro en "ventas", debemos:


- controlar que el código del libro exista en "libros" (lo hacemos con la restricción "foreign key" establecida
en "ventas"),
- controlar que exista stock, lo cual no puede controlarse con una restricción "foreign key" porque el campo
"stock" no es clave primaria en la tabla "libros"; tampoco puede establecerse una restricción "check", porque
ese tipo de restricciones no pueden hacer referencia a campos de otras tablas.
- disminuir el stock en la tabla "libros".

422
Creamos un disparador para que se ejecute cada vez que una instrucción "insert" ingrese datos en "ventas";
el mismo controlará que haya stock en "libros" y actualizará el campo "stock":
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

Ingresamos un registro en "ventas":


insert into ventas values('2007/04/01',1,15,1);

Al ejecutar la sentencia de inserción anterior, se disparó el trigger, el registro se agregó a la tabla del
disparador ("ventas") y disminuyó el valor del campo "stock" de "libros". Verifiquemos que el disparador se
ejecutó consultando la tabla "ventas" y "libros":
select *from ventas;
select *from libros where codigo=1;

Ingresamos un registro en "ventas", solicitando una cantidad superior al stock:


insert into ventas values('2007/04/01',2,18,100);

El disparador se ejecuta y muestra un mensaje, la inserción no se realizó porque la cantidad solicitada supera
el stock.
Finalmente probaremos ingresar una venta con un código de libro inexistente:
insert into ventas values('2007/04/01',5,18,1);

El trigger no llegó a ejecutarse, porque la comprobación de restricciones (que se ejecuta antes que el
disparador) detectó que la infracción a la "foreign key".
Primer problema:
Una empresa almacena los datos de sus empleados en una tabla denominada "empleados" y
en otra tabla
llamada "secciones", el código de la sección y el sueldo máximo de cada una de ellas.
1- Elimine las tablas si existen:
if object_id('empleados') is not null
drop table empleados;
if object_id('secciones') is not null
drop table secciones;

2- Cree las tablas, con las siguientes estructuras:


create table secciones(
423
codigo int identity,
nombre varchar(30),
sueldomaximo decimal(8,2),
constraint PK_secciones primary key(codigo)
);

create table empleados(


documento char(8) not null,
nombre varchar(30) not null,
domicilio varchar(30),
codigoseccion int not null,
sueldo decimal(8,2),
constraint PK_empleados primary key(documento),
constraint FK_empelados_seccion
foreign key (codigoseccion) references secciones(codigo)
);

3- Ingrese algunos registros en ambas tablas:


insert into secciones values('Administracion',1500);
insert into secciones values('Sistemas',2000);
insert into secciones values('Secretaria',1000);

insert into empleados values('22222222','Ana Acosta','Avellaneda 88',1,1100);


insert into empleados values('23333333','Bernardo Bustos','Bulnes 345',1,1200);
insert into empleados values('24444444','Carlos Caseres','Colon 674',2,1800);
insert into empleados values('25555555','Diana Duarte','Colon 873',3,1000);

4- Cree un disparador para que se ejecute cada vez que una instrucción "insert" ingrese
datos en
"empleados"; el mismo debe verificar que el sueldo del empleado no sea mayor al sueldo
máximo
establecido para la sección, si lo es, debe mostrar un mensaje indicando tal situación
y deshacer la
transacción.

5- Ingrese un nuevo registro en "empleados" cuyo sueldo sea menor o igual al


establecido para la
sección.

6- Verifique que el disparador se ejecutó consultando la tabla "empleados":


select *from empleados;

7- Intente ingresar un nuevo registro en "empleados" cuyo sueldo sea mayor al


establecido para la
sección.
El disparador se ejecutó mostrando un mensaje y la transacción se deshizo.

8- Verifique que el registro no se agregó en "empleados":


select *from empleados;

9- Intente ingresar un empleado con código de sección inexistente.


Aparece un mensaje de error porque se viola la restricción "foreign key"; el trigger no
llegó a
ejecutarse.

Segundo problema:
Un club de barrio almacena los datos de sus socios en una tabla llamada "socios", los
datos de las
inscripciones en una tabla denominada "inscriptos" y en una tabla "morosos" almacena el
documento de
los socios inscriptos que deben matrícula.
1- Elimine las tablas si existen:
if object_id('inscriptos') is not null
drop table inscriptos;
424
if object_id('socios') is not null
drop table socios;
if object_id('morosos') is not null
drop table morosos;

2- Cree las tablas, con las siguientes estructuras:


create table socios(
documento char(8) not null,
nombre varchar(30),
domicilio varchar(30),
constraint PK_socios primary key (documento)
);

create table inscriptos(


documento char(8) not null,
deporte varchar(30) not null,
matricula char(1),
constraint CK_inscriptos check (matricula in ('s','n')),
constraint PK_inscriptos primary key (documento,deporte),
constraint FK_inscriptos_documento foreign key(documento)
references socios (documento)
);

create table morosos(


documento char(8) not null
);

3- Ingrese algunos registros en las tres tablas:


insert into socios values ('22222222','Ana Acosta','Avellaneda 800');
insert into socios values ('23333333','Bernardo Bustos','Bulnes 234');
insert into socios values ('24444444','Carlos Caseros','Colon 321');
insert into socios values ('25555555','Mariana Morales','Maipu 483');

insert into inscriptos values ('22222222','tenis','s');


insert into inscriptos values ('22222222','natacion','n');
insert into inscriptos values ('23333333','tenis','n');
insert into inscriptos values ('24444444','tenis','s');
insert into inscriptos values ('24444444','futbol','s');

insert into morosos values ('22222222');


insert into morosos values ('23333333');

4- Cree un disparador de inserción que no permita ingresar inscripciones si el socio es


moroso, es
decir, si está en la tabla "morosos".

5- Realice la inscripción de un socio que no deba matrículas.


El disparador se ejecutó.

6- Intente inscribir a un socio moroso.


El trigger se disparó, mostró un mensaje y no permitió la inserción.

7- Cree otro "insert trigger" para "inscriptos" que ingrese el socio en la tabla
"morosos" si no
paga la matrícula (si se ingresa 'n' para el campo "matricula"). Recuerde que podemos
crear varios
triggers para un mismo evento sobre una misma tabla.

8- Realice la inscripción de un socio que no deba matrículas con el valor 's' para
"matricula".
El disparador "dis_inscriptos_insertar" se ejecuta y permite la transacción; el
disparador
"dis_inscriptos_insertar2" se ejecuta y permite la transacción.

425
9- Realice la inscripción de un socio que no deba matrículas con el valor 'n' para
"matricula".
El disparador "dis_inscriptos_insertar" se ejecuta y permite la transacción; el
disparador
"dis_inscriptos_insertar2" se ejecuta y permite la transacción.

10- Verifique que el disparador "dis_inscriptos_insertar2" se ejecutó consultando la


tabla
"morosos":
select *from morosos;

11- Realice la inscripción de un socio que deba matrículas con el valor 's' para
"matricula".
El disparador "dis_inscriptos_insertar" se ejecuta y no permite la transacción; el
disparador
"dis_inscriptos_insertar2" no llega a ejecutarse.

12- Realice la inscripción de un socio que deba matrículas con el valor 'n' para
"matricula".
El disparador "dis_inscriptos_insertar" se ejecuta y no permite la transacción; el
disparador
"dis_inscriptos_insertar2" no llega a ejecutarse.

13- Creamos un disparador sobre la tabla "socios" para que no permita ingresar nuevos
socios. El
mismo debe mostrar un mensaje al dispararse y deshacer la transacción.

14- Intente ingresar un nuevo socio.


El trigger se dispara, muestra el mensaje y deshace la transacción.

15- Actualizar el domicilio de un socio existente.


El trigger no se dispara porque está definido para el evento "insert".

143 - Disparador de borrado (delete trigger)


Podemos crear un disparador para que se ejecute siempre que una instrucción "delete" elimine
datos en una tabla.
Sintaxis básica:
create triggre NOMBREDISPARADOR
on NOMBRETABLA
for delete
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 "delete"), lo que indica que las eliminaciones
sobre la tabla activarán el trigger.
Luego de "as" se especifican las condiciones que determinan cuando un intento de eliminación
causa las acciones que el trigger realizará.
426
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") dándole un nombre ("DI_ventas_borrar") sobre


("on") una tabla específica ("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 acción.
Dentro del trigger se puede acceder a esta tabla virtual "deleted".
El siguiente disparador se crea para controlar que no se elimine más 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 están eliminando; si se está eliminando más de un registro, el
disparador retorna un mensaje de error y deshace la transacción:
create trigger DIS_libros_borrar
on libros
for delete
as
if (select count(*) from deleted) > 1
begin
raiserror('No puede borrar más 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 transacción.
Si se ejecuta el siguiente "delete", que afecta a un solo registro, se activa el disparador y permite
la transacción:
delete from libros where codigo=5;

La sentencia "truncate table" no puede incluirse en un disparador de borrado (delete trigger).


Una librería almacena los datos de sus libros en una tabla denominada "libros" y en otra tabla
llamada "ventas", las ventas de los mismos.
Eliminamos las tablas si existen:
if object_id('ventas') is not null
drop table ventas;
if object_id('libros') is not null

427
drop table libros;

Creamos las tablas, con las siguientes estructuras:


create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(20),
precio decimal(6,2),
stock int,
constraint PK_libros primary key(codigo)
);

create table ventas(


numero int identity,
fecha datetime,
codigolibro int not null,
precio decimal (6,2),
cantidad int,
constraint PK_ventas primary key(numero),
constraint FK_ventas_codigolibro
foreign key (codigolibro) references libros(codigo)
on delete no action
);

Ingresamos algunos registros en "libros" y en "ventas":


insert into libros values('Uno','Richard Bach','Planeta',15,100);
insert into libros values('Ilusiones','Richard Bach','Planeta',18,50);
insert into libros values('El aleph','Borges','Emece',25,200);
insert into libros values('Aprenda PHP','Mario Molina','Emece',45,200);

insert into ventas values('2006/01/01',1,15,1);


insert into ventas values('2007/01/01',2,18,2);

Creamos un disparador para actualizar el campo "stock" de la tabla "libros" cuando se elimina un registro de
la tabla "ventas" (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;

Eliminamos un registro de "ventas":


delete from ventas where numero=2;

Al ejecutar la sentencia de eliminación anterior, se disparó el trigger, el registro se eliminó de la tabla del
disparador ("ventas") y se actualizó el stock en "libros". Verifiquemos que el disparador se ejecutó
consultando la tabla "libros" y vemos si el stock aumentó:
select *from libros where codigo=2;

Verificamos que el registro se eliminó de "ventas":


select *from ventas;

428
Creamos un disparador para controlar que no se elimine más de un registro de la tabla "libros". El disparador
se activa cada vez que se ejecuta un "delete" sobre "libros", controlando la cantidad de registros que se están
eliminando; si se está eliminando más de un registro, el disparador retorna un mensaje de error y deshace la
transacción:
create trigger DIS_libros_borrar
on libros
for delete
as
if (select count(*) from deleted) > 1
begin
raiserror('No puede eliminar más de un libro',16,1)
rollback transaction
end;

Solicitamos la eliminación de varios registros de "libros":


delete from libros where editorial='Emece';

Se activa el disparador y deshace la transacción.


Solicitamos la eliminación de un solo libro:
delete from libros where codigo=4;

Se activa el disparador y permite la transacción; consultamos la tabla y vemos que el libro fue eliminado:
select *from libros;

Note que eliminamos libros que no estaban en "ventas; si hubiésemos intentado eliminar un libro cuyo
código esté en "ventas", la restricción "foreign key" no lo hubiese permitido; la restricción evita que se
ejecute el "delete", el trigger no llega a dispararse.
Primer problema:
Un comercio que vende artículos de informática almacena los datos de sus artículos en
una tabla
denominada "articulos".
1- Elimine la tabla si existe:
if object_id('articulos') is not null
drop table articulos;

2- Cree la tabla, con la siguiente estructura:


create table articulos(
codigo int identity,
tipo varchar(30),
descripcion varchar(40),
precio decimal(8,2),
stock int,
constraint PK_articulos primary key (codigo)
);

3- Ingrese algunos registros:


insert into articulos values ('impresora','Epson Stylus C45',400,100);
insert into articulos values ('impresora','Epson Stylus C85',500,200);
insert into articulos values ('impresora','Epson Stylus Color 600',400,0);
insert into articulos values ('monitor','Samsung 14',900,0);
insert into articulos values ('monitor','Samsung 17',1200,0);
insert into articulos values ('monitor','xxx 15',1500,0);
insert into articulos values ('monitor','xxx 17',1600,0);
insert into articulos values ('monitor','zzz 15',1300,0);

429
4- Cree un disparador para controlar que no se elimine un artículo si hay stock. El
disparador se
activará cada vez que se ejecuta un "delete" sobre "articulos", controlando el stock,
si se está
eliminando un artículo cuyo stock sea mayor a 0, el disparador debe retornar un mensaje
de error y
deshacer la transacción.

5- Solicite la eliminación de un articulo que no tenga stock.


Se activa el disparador y permite la transacción.

6- Intente eliminar un artículo para el cual haya stock.


El trigger se dispara y deshace la transacción. Puede verificar que el artículo no fue
eliminado
consultando la tabla "articulos".

7- Solicite la eliminación de varios artículos que no tengan stock.


Se activa el disparador y permite la transacción. Puede verificar que se borraron 2
artículos
consultando la tabla "articulos".

8- Intente eliminar varios artículos, algunos con stock y otros sin stock.
El trigger se dispara y deshace la transacción, es decir, ningún artículo fue
eliminado, tampoco los
que tienen stock igual a 0.

9- Cree un trigger para evitar que se elimine más de 1 artículo.


Note que hay 2 disparadores para el mismo suceso (delete) sobre la misma tabla.

10- Solicite la eliminación de 1 artículo para el cual no haya stock.


Ambos disparadores "DIS_articulos_borrar" y "DIS_articulos_borrar2" se activan y
permiten la
transacción.

11- Solicite la eliminación de 1 artículo que tenga stock.


El disparadores "DIS_articulos_borrar" se activa y no permite la transacción. El
disparador
"DIS_articulos_borrar2" no llega a activarse.

12- Solicite la eliminación de 2 artículos para los cuales no haya stock.


El disparador "DIS_articulos_borrar" se activa y permite la transacción pero el
disparador
"DIS_articulos_borrar2" no permite la transacción.

13- Solicite la eliminación de 2 artículos para los que haya stock.


El disparador "DIS_articulos_borrar" se activa y no permite la transacción. El
disparador
"DIS_articulos_borrar2" no llega a activarse.

144 - Disparador de actualización (update


trigger)
Podemos crear un disparador para que se ejecute siempre que una instrucción "update"
actualice los datos de una tabla.
Sintaxis básica:
430
create triggre NOMBREDISPARADOR
on NOMBRETABLA
for update
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 "update"), lo que indica que las actualizaciones
sobre la tabla activarán el trigger.
Luego de "as" se especifican las condiciones y acciones, es decir, las condiciones que determinan
cuando un intento de modificación provoca las acciones que el trigger realizará.
El siguiente disparador de actualización se crea para evitar que se modifiquen los datos de la
tabla "libros":
create trigger DIS_libros_actualizar
on libros
for update
as
raiserror('Los datos de la tabla "libros" no pueden modificarse', 10, 1)
rollback transaction

Entonces, creamos el disparador ("create trigger") dándole un nombre ("DI_libros_actualizar")


sobre una tabla específica ("libros") para ("for") el suceso de actualización ("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 instrucción "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 función "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 actualización del campo "precio"; así,
cuando el disparador detecte una actualización en tal campo, realizará las acciones apropiadas
(mostrar un mensaje y deshacer la actualización); en caso que se actualice otro campo, el
disparador se activa, pero permite la transacción.
431
Creamos un disparador de actualización 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
on d.codigo=i.codigo
end
else
begin
raiserror('El precio y stock no pueden modificarse. La actualización no se realizó.', 10, 1)
rollback transaction
end;

Empleamos "if update" para que el trigger controle si la actualización 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 transacción.
Note que el disparador no controla los intentos de actualización 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.
Una librería almacena los datos de sus libros en una tabla denominada "libros".
Eliminamos la tabla si existe:
if object_id('libros') is not null
drop table libros;

Creamos la tablas, con la siguiente estructura:


create table libros(
codigo int identity,
titulo varchar(40),
autor varchar(30),
editorial varchar(20),
precio decimal(6,2),
stock int,
constraint PK_libros primary key(codigo)
);

Ingresamos algunos registros en "libros":


insert into libros values('Uno','Richard Bach','Planeta',15,100);
insert into libros values('Alicia en el pais...','Lewis Carroll','Planeta',18,50);
insert into libros values('El aleph','Borges','Emece',25,200);
insert into libros values('Aprenda PHP','Mario Molina','Nuevo siglo',45,200);

Creamos un disparador para evitar que se modifiquen los datos de la tabla "libros":

432
create trigger DIS_libros_actualizar
on libros
for update
as
raiserror('Los datos de la tabla "libros" no pueden modificarse', 10, 1)
rollback transaction;

Intentamos realizar alguna actualización en "libros":


update libros set titulo='Alicia en el pais de las maravillas' where codigo=2;

El disparador se activó, mostró un mensaje y deshizo la actualización. Podemos comprobarlo consultando la


tabla "libros".
Eliminamos el disparador creado anteriormente (tema que veremos más adelante):
drop trigger DIS_libros_actualizar;

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;

Veamos qué sucede si intentamos actualizar el precio de un libro:


update libros set precio=30 where codigo=2;

El disparador se activa, muestra un mensaje y deshace la transacción.


Veamos qué sucede al actualizar el campo "titulo":
update libros set titulo='Alicia en el pais de las maravillas' where codigo=2;

El disparador se activa y realiza la transacción. Lo verificamos consultando la tabla:


select *from libros;

Veamos qué sucede si intentamos actualizar el precio y la editorial de un libro:


update libros set precio=30,editorial='Emece' where codigo=1;

El disparador se activa, muestra un mensaje y deshace la transacción; el registro no fue actualizado,


verificamos:
select *from libros;

Eliminamos el disparador creado anteriormente:


drop trigger DIS_libros_actualizar_precio;

Creamos un disparador de actualización que muestra el valor anterior y nuevo valor de los registros
actualizados. El trigger debe controlar que la actualización se realice en los campos "titulo", "autor" y
"editorial" y no en los demás campos (precio y stock)); si se modifican los campos permitidos y ninguno de
433
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 transacción:
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.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
on d.codigo=i.codigo
end
else
begin
raiserror('El precio y stock no pueden modificarse. La actualización no se
realizó.', 10, 1)
rollback transaction
end;

Veamos qué sucede si modificamos campos permitidos:


update libros set editorial='Paidos', autor='Desconocido' where codigo>3;

El trigger se dispara y muestra los registros modificados, los valores antes y después de la transacción.
Veamos qué sucede si en la sentencia "update" intentamos modificar algún campo no permitido:
update libros set editorial='Paidos', precio=30 where codigo>3;

El trigger se dispara y muestra el mensaje de error, la transacción no se realizó.


Intentamos modificar el código de un libro:
update libros set codigo=9 where codigo>=3;

El disparador no llega a dispararse porque SQL Server muestra un mensaje de error ya que el campo
"codigo", por ser "identity", no puede modificarse.
Primer problema:
Un club almacena los datos de sus socios en una tabla denominada "socios", las
inscripciones en
"inscriptos" y en otra tabla "morosos" guarda los documentos de los socios que deben
matrículas.
1- Elimine las tablas si existen:
if object_id('inscriptos') is not null
drop table inscriptos;
if object_id('socios') is not null
drop table socios;
if object_id('morosos') is not null
drop table morosos;

2- Cree las tablas, con las siguientes estructuras:


create table socios(
documento char(8) not null,
nombre varchar(30),
domicilio varchar(30),
constraint PK_socios primary key(documento)
);

434
create table inscriptos(
numero int identity,
documento char(8) not null,
deporte varchar(20),
matricula char(1),
constraint FK_inscriptos_documento
foreign key (documento)
references socios(documento),
constraint CK_inscriptos_matricula check (matricula in ('s','n')),
constraint PK_inscriptos primary key(documento,deporte)
);

create table morosos(


documento char(8) not null
);

3- Ingrese algunos registros en las 3 tablas:


insert into socios values('22222222','Ana Acosta','Avellaneda 800');
insert into socios values('23333333','Bernardo Bustos','Bulnes 345');
insert into socios values('24444444','Carlos Caseros','Colon 382');
insert into socios values('25555555','Mariana Morales','Maipu 234');

insert into inscriptos values('22222222','tenis','s');


insert into inscriptos values('22222222','natacion','n');
insert into inscriptos values('23333333','tenis','n');
insert into inscriptos values('24444444','futbol','s');
insert into inscriptos values('24444444','natacion','s');

insert into morosos values('22222222');


insert into morosos values('23333333');

4- Cree un disparador para la tabla "inscriptos" que se active ante una sentencia
"update" y no
permita actualizar más de un registro.

5- Cree otro disparador para la tabla "inscriptos" que se active ante una sentencia
"update". Si se
actualiza el pago de la matrícula a 's', el socio debe eliminarse de la tabla
"morosos"; no debe
permitir modificar a 'n' una matrícula paga.

6- Actualice cualquier campo (diferente de "matricula") de un registro de la tabla


"inscriptos".
Ambos disparadores se activaron permitiendo la transacción.

7- Actualice cualquier campo (diferente de "matricula") de varios registros de la tabla


"inscriptos".
El disparador "dis_inscriptos_actualizar1" se activa y no permite la transacción. El
disparador
"dis_inscriptos_actualizar_matricula" no llega a activarse.

8- Actualice el campo "matricula" a 's' de un inscripto que deba la matrícula.


Ambos disparadores se activaron y permitieron la actualización.

9- Verifique que el campo se actualizó y que el socio ya no está en "morosos":


select *from inscriptos;
select *from morosos;

10-Actualice el campo "matricula" a 'n' de un inscripto que tenga la matrícula paga.


Ambos disparadores se activaron; "dis_inscriptos_actualizar_matricula" deshace la
transacción.

435
145 - Disparadores (varios eventos)
Hemos aprendido a crear disparadores para diferentes eventos (insert, update y delete).
Dijimos que un disparador puede definirse para más de una acción; en tal caso, deben
separarse con comas.
Creamos un trigger para evitar que se inscriban socios que deben matrículas y no permitir que se
eliminen las inscripciones de socios deudores. El trigger se define para ambos eventos en la
misma sentencia de creación.
create trigger dis_inscriptos_insert_delete
on inscriptos
for insert,delete
as
if exists (select *from inserted join morosos
on morosos.documento=inserted.documento)
begin
raiserror('El socio es moroso, no puede inscribirse en otro curso', 16, 1)
rollback transaction
end
else
if exists (select *from deleted join morosos
on morosos.documento=deleted.documento)
begin
raiserror('El socio debe matriculas, no puede borrarse su inscripcion', 16, 1)
rollback transaction
end
else
if (select matricula from inserted)='n'
insert into morosos select documento from inserted;

El trigger controla:
- si se intenta ingresar una inscripción de un socio moroso, se deshace la transacción;
- si se intenta eliminar una inscripción de un socio que está en "morosos", se deshace la
transacción;
- si se ingresa una nueva inscripción y no se paga la matrícula, dicho socio se ingresa a la tabla
"morosos".
Un club almacena los datos de sus socios en una tabla denominada "socios", las inscripciones en
"inscriptos" y en otra tabla "morosos" guarda los documentos de los socios que deben matrículas.
Eliminamos las tablas si existen:
if object_id('inscriptos') is not null
drop table inscriptos;
if object_id('socios') is not null
drop table socios;
if object_id('morosos') is not null
drop table morosos;

Creamos las tablas, con las siguientes estructuras:


create table socios(
documento char(8) not null,
436
nombre varchar(30),
domicilio varchar(30),
constraint PK_socios primary key(documento)
);

create table inscriptos(


numero int identity,
documento char(8) not null,
deporte varchar(20),
matricula char(1),
constraint FK_inscriptos_documento
foreign key (documento)
references socios(documento),
constraint CK_inscriptos_matricula check (matricula in ('s','n')),
constraint PK_inscriptos primary key(documento,deporte)
);

create table morosos(


documento char(8) not null
);

Ingresamos algunos registros en las 3 tablas:


insert into socios values('22222222','Ana Acosta','Avellaneda 800');
insert into socios values('23333333','Bernardo Bustos','Bulnes 345');
insert into socios values('24444444','Carlos Caseros','Colon 382');
insert into socios values('25555555','Mariana Morales','Maipu 234');

insert into inscriptos values('22222222','tenis','s');


insert into inscriptos values('22222222','natacion','n');
insert into inscriptos values('23333333','tenis','n');
insert into inscriptos values('24444444','futbol','s');
insert into inscriptos values('24444444','natacion','s');

insert into morosos values('22222222');


insert into morosos values('23333333');

Creamos un trigger para evitar que se inscriban socios que deben matrículas y no permitir que se eliminen
las inscripciones de socios deudores. El trigger se define para ambos eventos en la misma sentencia de
creación.
create trigger dis_inscriptos_insert_delete
on inscriptos
for insert,delete
as
if exists (select *from inserted join morosos
on morosos.documento=inserted.documento)
begin
raiserror('El socio es moroso, no puede inscribirse en otro curso', 16, 1)
rollback transaction
end
else
if exists (select *from deleted join morosos
on morosos.documento=deleted.documento)
begin
raiserror('El socio debe matriculas, no puede borrarse su inscripcion', 16, 1)
rollback transaction
end
else
if (select matricula from inserted)='n'
insert into morosos select documento from inserted;

El trigger controla:
437
- si se intenta ingresar una inscripción de un socio moroso, se deshace la transacción;
- si se intenta eliminar una inscripción de un socio que está en "morosos", se deshace la transacción;
- si se ingresa una nueva inscripción y no se paga la matrícula, dicho socio se ingresa a la tabla "morosos".
Ingresamos una inscripción de un socio no deudor con matrícula paga:
insert into inscriptos values('25555555','tenis','s');

El disparador se activa ante el "insert" y permite la transacción.


Ingresamos una inscripción de un socio no deudor con matrícula 'n':
insert into inscriptos values('25555555','natacion','n');

El disparador se activa ante el "insert", permite la transacción y agrega al socio en la tabla "morosos".
Verifiquémoslo consultando las tablas correspondientes:
select *from inscriptos;
select *from morosos;

Ingresamos una inscripción de un socio deudor:


insert into inscriptos values('25555555','basquet','s');

El disparador se activa ante el "insert" y deshace la transacción porque encuentra su documento en la tabla
"morosos".
Eliminamos una inscripción de un socio no deudor:
delete inscriptos where numero=4;

El disparador se activa ante la sentencia "delete" y permite la transacción. Verificamos que la inscripción nº
4 fue eliminada de "inscriptos":
select *from inscriptos;

Intentamos eliminar una inscripción de un socio deudor:


delete inscriptos where numero=6;

El disparador se activa ante el "delete" y deshace la transacción porque encuentra su documento en


"morosos".
Primer problema:
Una empresa almacena los datos de sus empleados en una tabla denominada "empleados" y
los datos de
las distintas sucursales en una tabla "sucursales".
1- Elimine las tablas si existen:
if object_id('empleados') is not null
drop table empleados;
if object_id('sucursales') is not null
drop table sucursales;
2- Cree las tablas, con las siguientes estructuras:
create table sucursales(
codigo int identity,
domicilio varchar(30),
constraint PK_sucursales primary key (codigo)
);

438
create table empleados(
documento char(8) not null,
nombre varchar(30),
domicilio varchar(30),
sucursal int not null,
constraint PK_empleados primary key (documento),
constraint FK_empleados_sucursal foreign key(sucursal)
references sucursales(codigo)
);

3- Ingrese algunos registros en las dos tablas:


insert into sucursales values ('Colon 123');
insert into sucursales values ('Sucre 234');
insert into sucursales values ('Rivadavia 345');

insert into empleados values ('22222222','Ana Acosta','Avellaneda 1258',1);


insert into empleados values ('23333333','Betina Bustos','Bulnes 345',2);
insert into empleados values ('24444444','Carlos Caseres','Caseros 948',3);
insert into empleados values ('25555555','Fabian Fuentes','Francia 845',1);
insert into empleados values ('26666666','Gustavo Garcia','Guemes 587',2);
insert into empleados values ('27777777','Maria Morales','Maipu 643',3);

4- Cree un disparador de inserción, eliminación y actualización que no permita


modificaciones en la
tabla "empleados" si tales modificaciones afectan a empleados de la sucursal de 1.

5- Ingrese un empleado en la sucursal 3.


El trigger se dispara permitiendo la transacción;

6- Intente ingresar un empleado en la sucursal 1.


El trigger se dispara y deshace la transacción.

7- Ejecute un "update" sobre "empleados" que permita la transacción.

8- Ejecute un "update" sobre "empleados" que el trigger deshaga.

9- Elimine un empleado (o varios) que no sean de la sucursal 1.


El trigger se ejecuta y la transacción se realiza.

10- Intente eliminar un empleado (o varios) de la sucursal 1.


El trigger deshace la transacción.

146 - Disparador (Instead Off y after)


Hasta el momento hemos aprendido que un trigger se crea sobre una tabla específica para un
evento (inserción, eliminación o actualización).
También podemos especificar el momento de disparo del trigger. El momento de disparo indica
que las acciones (sentencias) del trigger se ejecuten luego de la acción (insert, delete o update)
que dispara el trigger o en lugar de la acción.
La sintaxis para ello es:
create triggre NOMBREDISPARADOR
on NOMBRETABLA o VISTA
MOMENTODEDISPARO-- after o instead of

439
ACCION-- insert, update o delete
as
SENTENCIAS

Entonces, el momento de disparo especifica cuando deben ejecutarse las acciones (sentencias)
que realiza el trigger. Puede ser "después" (after) o "en lugar" (instead of) del evento que lo
dispara.
Si no especificamos el momento de disparo en la creación del trigger, por defecto se establece
como "after", es decir, las acciones que el disparador realiza se ejecutan luego del suceso
disparador. Hasta el momento, todos los disparadores que creamos han sido "after".
Los disparadores "instead of" se ejecutan en lugar de la acción desencadenante, es decir,
cancelan la acción desencadenante (suceso que disparó el trigger) reemplazándola por otras
acciones.
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 inserción, 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 acción (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 restricción "foreign key" y las comprobaciones
de restricciones "check" deben realizarse con éxito antes de ejecutarse el trigger. Es la opción por
defecto si solamente colocamos "for" (equivalente a "after").
La sintaxis es:
create triggre NOMBREDISPARADOR
on NOMBRETABLA

440
after | for-- son equivalentes
ACCION-- insert, update o delete
as
SENTENCIAS

b) "instead of": sobreescribe la acción desencadenadora del trigger. Se puede definir solamente
un disparador de este tipo para cada acción (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 acción "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 ejecución 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.
Una empresa almacena los datos de sus empleados en una tabla "empleados" y en otra tabla
"clientes" los datos de sus clientes.
Eliminamos las tablas si existen:
if object_id('empleados') is not null
drop table empleados;
if object_id('clientes') is not null
drop table clientes;

Creamos ambas tablas:


create table empleados(
documento char(8) not null,
nombre varchar(30),
domicilio varchar(30),
constraint PK_empleados primary key(documento)
);

create table clientes(


documento char(8) not null,
nombre varchar(30),
domicilio varchar(30),
constraint PK_clientes primary key(documento)
);

441
Eliminamos la siguiente vista:
if object_id('vista_empleados_clientes') is not null
drop view vista_empleados_clientes;

Creamos 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 inserción, que redirija las inserciones
a la tabla correspondiente:
create trigger DIS_empl_clie_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';

Ingresamos un empleado y un cliente en la vista:


insert into vista_empleados_clientes values('22222222','Ana Acosta', 'Avellaneda
345','empleado');
insert into vista_empleados_clientes values('23333333','Bernardo Bustos', 'Bulnes
587','cliente');

Veamos si se almacenaron en la tabla correspondiente:


select *from empleados;
select *from clientes;

Creamos un disparador sobre la vista "vista_empleados_clientes" para el evento "update", que redirija las
actualizaciones a la tabla correspondiente:
create trigger DIS_empl_clie_actualizar
on vista_empleados_clientes
instead of update
as
declare @condicion varchar(10)
set @condicion = (select condicion from inserted)
if update(documento)
begin
raiserror('Los documentos no pueden modificarse', 10, 1)
rollback transaction
end
else
begin
if @condicion ='empleado'
begin
update empleados set empleados.nombre=inserted.nombre,
empleados.domicilio=inserted.domicilio
from empleados
join inserted

442
on empleados.documento=inserted.documento
end
else
if @condicion ='cliente'
begin
update clientes set clientes.nombre=inserted.nombre,
clientes.domicilio=inserted.domicilio
from clientes
join inserted
on clientes.documento=inserted.documento
end
end;

Realizamos una actualización sobre la vista, de un empleado:


update vista_empleados_clientes set nombre= 'Ana Maria Acosta' where
documento='22222222';

Veamos si se actualizó la tabla correspondiente:


select *from empleados;

Realizamos una actualización sobre la vista, de un cliente:


update vista_empleados_clientes set domicilio='Bulnes 1234' where
documento='23333333';

Veamos si se actualizó la tabla correspondiente:


select *from clientes;

Primer problema:
Un club almacena los datos de sus socios en una tabla denominada "socios", los
distintos cursos que
dictan en "cursos" y las inscripciones de los distintos socios en los distintos cursos
en
"inscriptos".
1- Elimine las tablas si existen:
if object_id('inscriptos') is not null
drop table inscriptos;
if object_id('socios') is not null
drop table socios;
if object_id('cursos') is not null
drop table cursos;

2- Cree las tablas, con las siguientes estructuras:


create table socios(
documento char(8) not null,
nombre varchar(30),
domicilio varchar(30),
constraint PK_socios primary key(documento)
);
create table cursos(
numero tinyint identity,
deporte char(20),
cantidadmaxima tinyint,
constraint PK_cursos primary key(numero)
);

create table inscriptos(


documento char(8) not null,
numerocurso tinyint,
fecha datetime,
443
constraint PK_inscriptos primary key(documento,numerocurso),
constraint FK_inscriptos_documento
foreign key (documento)
references socios(documento),
constraint FK_inscriptos_curso
foreign key (numerocurso)
references cursos(numero)
);

Los cursos tiene un número que los identifica, y según el deporte, hay un límite de
inscriptos (por
ejemplo, en tenis, no pueden inscribirse más de 4 socios; en natación, solamente se
aceptan 6
alumnos como máximo). Cuando un curso está completo, es decir, hay tantos inscriptos
como valor
tiene el campo "cantidadmaxima"), el socio queda inscripto en forma condicional. El
club guarda esa
información en una tabla denominada "condicionales" que luego analiza, porque si se
inscriben muchos
para un deporte determinado, se abrirá otro curso.

2- Elimine la tabla "condicionales" si existe:


if object_id('condicionales') is not null
drop table condicionales;

3- Cree la tabla, con la siguiente estructura:


create table condicionales(
documento char(8) not null,
codigocurso tinyint not null,
fecha datetime
);

4- Ingrese algunos registros en las tablas "socios", "cursos" e "inscriptos":


insert into socios values('22222222','Ana Acosta','Avellaneda 800');
insert into socios values('23333333','Bernardo Bustos','Bulnes 345');
insert into socios values('24444444','Carlos Caseros','Colon 382');
insert into socios values('25555555','Mariana Morales','Maipu 234');
insert into socios values('26666666','Patricia Palacios','Paru 587');

insert into cursos values('tenis',4);


insert into cursos values('natacion',6);
insert into cursos values('basquet',20);
insert into cursos values('futbol',20);

insert into inscriptos values('22222222',1,getdate());


insert into inscriptos values('22222222',2,getdate());
insert into inscriptos values('23333333',1,getdate());
insert into inscriptos values('23333333',3,getdate());
insert into inscriptos values('24444444',1,getdate());
insert into inscriptos values('24444444',4,getdate());
insert into inscriptos values('25555555',1,getdate());

5- Cree un trigger "instead of" para el evento de inserción para que, al intentar
ingresar un
registro en "inscriptos" controle que el curso no esté completo (tantos inscriptos a
tal curso como
su "cantidadmaxima"); si lo estuviese, debe ingresarse la inscripción en la tabla
"condicionales" y
mostrar un mensaje indicando tal situación. Si la "cantidadmaxima" no se alcanzó, se
ingresa la
inscripción en "inscriptos".

6- Inscriba un socio en un curso que no esté completo.


Verifique que el trigger realizó la acción esperada consultando las tablas:

444
select *from inscriptos;
select *from condicionales;

7- Inscriba un socio en un curso que esté completo.


Verifique que el trigger realizó la acción esperada consultando las tablas:
select *from inscriptos;
select *from condicionales;

147 - Disparador (eliminar)


Los triggers se eliminan con la instrucción "drop trigger":
drop trigger NOMBREDISPARADOR;

Si el disparador que se intenta eliminar no existe, aparece un mensaje indicándolo, para evitarlo,
podemos verificar su existencia antes de solicitar su eliminación (como con cualquier otro objeto):
if object_id('NOMBREDISPARADOR') is not null
drop trigger NOMBREDISPARADOR;

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 automáticamente.

148 - Disparador (información)


Los triggers (disparadores) son objetos, así que para obtener información de ellos pueden
usarse los siguientes procedimientos almacenados del sistema y las siguientes tablas:
- "sp_help": sin parámetros 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 creación.
- "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).
445
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 demás 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 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 ningún objeto que lo referencie.
También 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 específica 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;

Nos muestra la siguiente información:


trigger_name trigger_owner isupdate isdelete isinsert isafter isinsteadof
------------------------------------------------------------------------------------------------------------
dis_inscriptos_insertar dbo 0 0 1 0 1

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 inserción (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 creación de todos los disparadores, podemos tipear:
select name,xtype as tipo,crdate as fecha
from sysobjects
446
where xtype = 'TR';

149 - Disparador (modificar)


Los triggers pueden modificarse y eliminarse.
Al modificar la definición de un disparador se reemplaza la definición existente del disparador por
la nueva definición.
La sintaxis general es la siguiente:
alter trigger NOMBREDISPARADOR
NUEVADEFINICION;

Asumiendo que hemos creado un disparador llamado "dis_empleados_borrar" que no permitía


eliminar más 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.
Una empresa almacena los datos de sus empleados en una tabla denominada "empleados".
Eliminamos la tabla si existe:
if object_id('empleados') is not null
drop table empleados;

Creamos la tabla, con la siguiente estructura:


create table empleados(
documento char(8) not null,
nombre varchar(30) not null,
domicilio varchar(30),
constraint PK_empleados primary key(documento),
);

Ingresamos algunos registros:


insert into empleados values('22000000','Ana Acosta','Avellaneda 56');
insert into empleados values('23000000','Bernardo Bustos','Bulnes 188');
insert into empleados values('24000000','Carlos Caseres','Caseros 364');
insert into empleados values('25555555','Diana Duarte','Colon 1234');
insert into empleados values('26666666','Diana Duarte','Colon 897');

447
insert into empleados values('27777777','Matilda Morales','Colon 542');

Creamos un disparador para que no permita eliminar más de un registro a la vez de la tabla empleados:
create trigger dis_empleados_borrar
on empleados
for delete
as
if (select count(*) from deleted)>1
begin
raiserror('No puede eliminar más de un 1 empleado', 16, 1)
rollback transaction
end;

Eliminamos 1 empleado:
delete from empleados where documento ='22000000';

El trigger se dispara y realiza la eliminación. Podemos verificarlo consultando "empleados".


Intentamos eliminar varios empleados:
delete from empleados where documento like '2%';

El trigger se dispara, muestra un mensaje y deshace la transacción.


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 más de 3 empleados',16, 1)
rollback transaction
end;

Intentamos eliminar 5 empleados:


delete from empleados where documento like '2%';

El trigger se dispara, muestra el nuevo mensaje y deshace la transacción.


Eliminamos 3 empleados:
delete from empleados where domicilio like 'Colon%';

El trigger se dispara y realiza las eliminaciones solicitadas. Puede verificarse consultando la tabla
"empleados".

150 - disparador (deshabilitar y habilitar)


Se puede deshabilitar o habilitar un disparador específico de una tabla o vista, o todos los
disparadores que tenga definidos.
448
Si se deshabilita un disparador, éste sigue existiendo, pero al ejecutar una instrucción "insert",
"update" o "delete" en la tabla, no se activa.
Sintaxis para deshabilitar o habilitar un disparador:
alter table NOMBRETABLA
ENABLE | DISABLE trigger NOMBREDISPARADOR;

El siguiente ejemplo deshabilita un trigger:


alter table empleados
disable trigger dis_empleados_borrar;

Se pueden deshabilitar (o habilitar) varios disparadores en una sola sentencia, separando sus
nombres con comas. El siguiente ejemplo deshabilitamos dos triggers definidos sobre la tabla
empleados:
alter table empleados
disable trigger dis_empleados_actualizar, dis_empleados_insertar;

Sintaxis para habilitar (o deshabilitar) todos los disparadores de una tabla específica:
alter table NOMBRETABLA
ENABLE | DISABLE TRIGGER all;

La siguiente sentencia habilita todos los triggers de la tabla "empleados":


alter table empleados
enable trigger all;

Una empresa almacena los datos de sus empleados en una tabla denominada "empleados".
Eliminamos la tabla si existe:

if object_id('empleados') is not null


drop table empleados;

Creamos la tabla, con la siguiente estructura:


create table empleados(
documento char(8) not null,
nombre varchar(30) not null,
domicilio varchar(30),
seccion varchar(20),
constraint PK_empleados primary key(documento),
);

Ingresamos algunos registros:


insert into empleados values('22222222','Ana Acosta','Bulnes 56','Secretaria');
insert into empleados values('23333333','Bernardo Bustos','Bulnes 188','Contaduria');
insert into empleados values('24444444','Carlos Caseres','Caseros 364','Sistemas');
insert into empleados values('25555555','Diana Duarte','Colon 1234','Sistemas');
insert into empleados values('26666666','Diana Duarte','Colon 897','Sistemas');
insert into empleados values('27777777','Matilda Morales','Colon 542','Gerencia');

Creamos un disparador para que no permita eliminar más de un registro a la vez de la tabla empleados:
create trigger dis_empleados_borrar
on empleados
for delete
as
449
if (select count(*) from deleted)>1
begin
raiserror('No puede eliminar más de un 1 empleado', 16, 1)
rollback transaction
end;

Creamos un disparador para que no permita actualizar el campo "documento" de la tabla "empleados":
create trigger dis_empleados_actualizar
on empleados
for update
as
if update(documento)
begin
raiserror('No puede modificar el documento de los empleados', 16, 1)
rollback transaction
end;

Creamos un disparador para que no permita ingresar empleados en la sección "Gerencia":


create trigger dis_empleados_insertar
on empleados
for insert
as
if (select seccion from inserted)='Gerencia'
begin
raiserror('No puede ingresar empleados en la sección "Gerencia".', 16, 1)
rollback transaction
end;

Intentamos borrar varios empleados:


delete from empleados where domicilio like 'Bulnes%';

El trigger se dispara, muestra el mensaje y deshace la transacción.


Deshabilitamos el trigger para el evento de eliminación:
alter table empleados
disable trigger dis_empleados_borrar;

Borramos varios empleados:


delete from empleados where domicilio like 'Bulnes%';

El trigger no se disparó porque está deshabilitado. Podemos verificar que los registros de eliminaron
recuperando los datos de la tabla:
select *from empleados;

Intentamos modificar un documento:


update empleados set documento='23030303' where documento='23333333';

El trigger se dispara, muestra el mensaje y deshace la transacción.


Intentamos ingresar un nuevo empleado en "Gerencia":
insert into empleados values('28888888','Juan Juarez','Jamaica 123','Gerencia');

El trigger se dispara, muestra el mensaje y deshace la transacción.


450
Deshabilitamos los disparadores de inserción y actualización definidos sobre "empleados":
alter table empleados
disable trigger dis_empleados_actualizar, dis_empleados_insertar;

Ejecutamos la sentencia de actualización del documento:


update empleados set documento='20000444' where documento='24444444';

El trigger no se dispara porque está deshabilitado, el documento se actualizó. verifiquémoslo:


select *from empleados;

Ingresar un nuevo empleado en "Gerencia":


insert into empleados values('28888888','Juan Juarez','Jamaica 123','Gerencia');

El trigger "dis_empleados_insertar" no se dispara porque está deshabilitado, el registro se agregó a la tabla.


verifiquémoslo:
select *from empleados;

Habilitamos todos los triggers de la tabla "empleados":


alter table empleados
enable trigger all;

Ya no podemos eliminar más de un registro, actualizar un documento ni ingresar un empleado en la sección


"Gerencia"; lo intentamos:
update empleados set documento='30000000' where documento='28888888';

El trigger se dispara (está habilitado), muestra el mensaje y deshace la transacción.

151 - Disparador (with encryption)


Hasta el momento hemos aprendido que un trigger se crea sobre una tabla (o vista),
especificando el momento de ejecución (after o instead of), para un evento (inserción,
eliminación o actualización).
Podemos encriptar los triggers para evitar que sean leídos con "sp_helptext". Para ello debemos
agregar al crearlos la opción "with encryption" luego del nombre de la tabla o vista:
create triggre NOMBREDISPARADOR
on NOMBRETABLAoVISTA
with encryption
MOMENTODEDISPARO--after o instead of
ACCION-- insert, update, delete
as
SENTENCIAS

El siguiente disparador se crea encriptado:


create trigger DIS_empleados_insertar
451
on empleados
with encryption
after insert
as
if (select seccion from inserted)='Gerencia'
begin
raiserror('No puede ingresar empleados en la sección "Gerencia".', 16, 1)
rollback transaction
end;

Si ejecutamos el procedimiento almacenado del sistema "sp_helptext" seguido del nombre del
trigger creado anteriormente, SQL Server mostrará un mensaje indicando que tal disparador ha
sido encriptado.
Una empresa almacena los datos de sus empleados en una tabla denominada "empleados".
Eliminamos la tabla si existe:
if object_id('empleados') is not null
drop table empleados;

Creamos la tabla, con la siguiente estructura:


create table empleados(
documento char(8) not null,
nombre varchar(30) not null,
domicilio varchar(30),
seccion varchar(20),
constraint PK_empleados primary key(documento),
);

Creamos el siguiente disparador encriptado:


create trigger DIS_empleados_insertar
on empleados
with encryption
after insert
as
if (select seccion from inserted)='Gerencia'
begin
raiserror('No puede ingresar empleados en la sección "Gerencia".', 16, 1)
rollback transaction
end;

Ejecutamos el procedimiento almacenado del sistema "sp_helptext" seguido del nombre del trigger creado
anteriormente:
sp_helptext dis_empleados_insertar;

SQL Server muestra un mensaje indicando que tal disparador ha sido encriptado.
Modificamos el disparador para quitar la encriptación:
alter trigger dis_empleados_insertar
on empleados
after insert
as
if (select seccion from inserted)='Gerencia'
begin
raiserror('No puede ingresar empleados en la sección "Gerencia".', 16, 1)
rollback transaction
end;

452
Ejecutamos el procedimiento almacenado del sistema "sp_helptext" seguido del nombre del trigger:
sp_helptext dis_empleados_insertar;

SQL Server nos permite ver la definición del trigger porque ya no está encriptado.

152 - Disparador (condicionales)


Una instrucción "insert", "update" o "delete" que invoque a un disparador puede afectar a varios
registros. En tales casos, un trigger rechaza o acepta cada transacción de modificación como
una totalidad. Podemos optar por:
1) procesar todos los registros: todos los registros afectados deberán cumplir los criterios del
disparador para que se produzca la acción, o
2) permitir acciones condicionales: puede definir un disparador que controle si cada registro
afectado cumple con la condición; si algún registro no la cumple, la acción no se produce para tal
registro pero si para los demás que si la cumplen.
Veamos un ejemplo. Tenemos la tabla "libros". Creamos un disparador de actualización sobre la
tabla "libros". Se permite actualizar el stock de varios libros a la vez; pero ningún "stock" debe
tener un valor negativo. Entonces, si algún "stock" queda con un valor negativo, no debe cambiar,
los demás 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 transacción con "rollback transaction" porque en ese caso TODOS los
registros modificados volverían 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 transacción.
Una librería almacena los datos de sus libros en una tabla denominada "libros".
Eliminamos la tabla si existe y la creamos con los siguientes campos:

453
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),
stock int,
constraint pk_libros primary key (codigo)
);

Ingresamnos algunos registros:


insert into libros values('Uno','R. Bach','Planeta',50);
insert into libros values('Ilusiones','R. Bach','Planeta',15);
insert into libros values('El aleph','Borges','Emece',10);
insert into libros values('Aprenda PHP','M. Molina','Nuevo siglo',5);

Creamos un disparador de actualización sobre la tabla "libros". Se permite actualizar el stock de varios libros
a la vez; pero ningún "stock" debe tener un valor negativo. Si algún "stock" queda con un valor negativo, no
debe cambiar, los demás 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;

Actualizamos el stock de todos los libros, restándoles 15:


update libros set stock=stock-15;

Veamos el resultado:
select *from libros;

Solamente se actualizaron los 2 primeros libros, cuyo valor de stock era igual o superior a 15; los otros
libros no se actualizaron.
Primer problema:

Una empresa almacena los datos de sus empleados en una tabla denominada "empleados" y
en otra tabla
denominada "secciones", información sobre las distintas secciones de la empresa.
1- Elimine las tablas si existen y créelas con los siguientes campos:
if object_id('empleados') is not null
drop table empleados;
if object_id('secciones') is not null
drop table secciones;

create table secciones(

454
codigo int identity,
nombre varchar(30),
constraint pk_secciones primary key (codigo)
);

create table empleados(


documento char(8) not null,
nombre varchar(30),
domicilio varchar(30),
seccion int not null,
constraint pk_empleados primary key (documento),
constraint fk_empleados_seccion foreign key(seccion)
references secciones (codigo)
);

2- Ingrese algunos registros:


insert into secciones values('Secretaria');
insert into secciones values('Sistemas');
insert into secciones values('Contaduría');
insert into secciones values('Gerencia');

insert into empleados values('22222222','Alejandro Acosta','Avellaneda 90',1);


insert into empleados values('22333333','Betina Bustos','Bulnes 345',2);
insert into empleados values('23444444','Camila Costa','Colon 234',1);
insert into empleados values('23555555','Daniel Duarte','Duarte Quiros 345',3);
insert into empleados values('23666666','Estela Esperanza','España 211',4);

3- Cree un disparador de eliminación sobre la tabla "empleados" que permita borrar


varios empleados
a la vez, pero ningún empleado de la sección "Gerencia".
Se eliminan todos los empleados solicitados en cualquier sentencia "delete", y luego se
vuelven a
insertar aquellos de la sección "Gerencia".

4- Elimine varios registros entre los cuales haya un empleado de "Gerencia".

5- Vea el resultado:
select *from empleados;
Solamente se eliminaron aquellos que no pertenecen a la sección "Gerencia".

455

455
Dirección web del curso
https://www.tutorialesprogramacionya.com/sqlserverya/index.php?
inicio=0

456

También podría gustarte