Escolar Documentos
Profissional Documentos
Cultura Documentos
Transact SQL existe desde las primeras versiones de SQL Server, si bien a lo
largo de este tutorial nos centraremos en la versin SQL Server 2005.
Que vamos a necesitar?
Para poder seguir este tutorial correctamente necesitaremos tener los siguientes
elementos:
Procedimientos almacenados
Funciones
Triggers
Scripts
Pero adems Transact SQL nos permite realizar programas sobre las siguientes
herramientas de SQL Server:
Service Broker
Scripts y lotes.
Un script de Transact SQL es un conjunto de sentencias de Transact
en formato de texto plano que se ejecutan en un servidor de SQL Server.
SQL
Un script est compuesto por uno o varios lotes. Un lote delimita el alcance de las
variables y sentencias del script. Dentro de un mismo script se diferencian los
diferentes lotes a travs de las instruccin GO.
<row nombre="SVR01"/>
(SELECT nombre
FROM CLIENTES
WHERE ID = 1)
PRINT @nombre
El siguiente ejemplo muestra como asignar variables utilizando una sentencia
SELECT.
SELECT
@nombre=nombre ,
@apellido1=Apellido1,
@apellido2=Apellido2
FROM CLIENTES
WHERE ID = 1
PRINT @nombre
PRINT @apellido1
PRINT @apellido2
OPEN CDATOS
FETCH CDATOS INTO @nombre, @apellido1, @apellido2
WHILE (@@FETCH_STATUS = 0)
BEGIN
PRINT @nombre
PRINT @apellido1
PRINT @apellido2
FETCH CDATOS INTO @nombre, @apellido1, @apellido2
END
CLOSE CDATOS
DEALLOCATE CDATOS
SQL Server
varbinary
SqlBytes, SqlBinary
Byte[]
binary
SqlBytes, SqlBinary
Byte[]
varbinary(1),
binary(1)
SqlBytes, SqlBinary
byte, Byte[]
image
ninguno
ninguno
varchar
ninguno
ninguno
char
ninguno
ninguno
nvarchar(1),
nchar(1)
SqlChars, SqlString
SqlChars, SqlString
nvarchar
nchar
SqlChars, SqlString
String, Char[]
text
ninguno
ninguno
ntext
ninguno
ninguno
uniqueidentifier SqlGuid
Guid
rowversion
ninguno
Byte[]
bit
SqlBoolean
Boolean
tinyint
SqlByte
Byte
smallint
SqlInt16
Int16
int
SqlInt32
Int32
bigint
SqlInt64
Int64
smallmoney
SqlMoney
Decimal
money
SqlMoney
Decimal
numeric
SqlDecimal
Decimal
decimal
SqlDecimal
Decimal
real
SqlSingle
Single
float
SqlDouble
Double
smalldatetime
SqlDateTime
DateTime
datetime
SqlDateTime
DateTime
sql_variant
ninguno
Object
User-defined
type(UDT)
ninguno
table
ninguno
ninguno
cursor
ninguno
ninguno
timestamp
ninguno
ninguno
xml
SqlXml
ninguno
Operadores
Operador de
asignacin
Operadores
aritmticos
+ (suma)
- (resta)
* (multiplicacin)
/ (divisin)
10
** (exponente)
% (modulo)
Operadores
relacionales o de
comparacin
Operadores lgicos
Operador de
concatenacin
= (igual a)
<> (distinto de)
!= (distinto de)
< (menor que)
> (mayor que)
>= (mayor o igual a)
<= (menor o igual a)
!> (no mayor a)
!< (no menor a)
AND (y lgico)
NOT (negacion)
OR (o lgico)
& (AND a nivel de bit)
|
(OR a nivel de bit)
^
(OR exclusivo a nivel de bit)
+
ALL (Devuelve TRUE si el conjunto completo de comparaciones es TRUE)
ANY(Devuelve TRUE si cualquier elemento del conjunto de comparaciones
es TRUE)
Otros
11
BEGIN
...
END
ELSE IF (<expresion>)
BEGIN
...
END
ELSE
BEGIN
...
END
Ejemplo de la estructura condicional IF.
DECLARE @Web varchar(100),
@diminutivo varchar(3)
SET @diminutivo = 'DJK'
IF
@diminutivo = 'DJK'
BEGIN
PRINT 'www.devjoker.com'
END
ELSE
BEGIN
PRINT 'Otra Web (peor!)'
END
La estructura IF admite el uso de subconsultas:
DECLARE @coPais int,
@descripcion varchar(255)
set @coPais = 5
set @descripcion = 'Espaa'
IF EXISTS(SELECT * FROM PAISES
WHERE CO_PAIS = @coPais)
BEGIN
UPDATE PAISES
SET DESCRIPCION = @descripcion
WHERE CO_PAIS = @coPais
END
ELSE
BEGIN
END
CASE <expresion>
12
CASE
WHEN <expresion> = <valor_expresion> THEN <valor_devuelto>
WHEN <expresion> = <valor_expresion> THEN <valor_devuelto>
ELSE <valor_devuelto> -- Valor por defecto
END
El mismo ejemplo aplicando esta sintaxis:
END)
PRINT @Web
web
13
FROM WEBS
WHERE id=1)
WHEN @diminutivo = 'ALM' THEN (SELECT web
FROM WEBS
WHERE id=2)
ELSE 'www.devjoker.com'
END)
PRINT @Web
Bucle WHILE
El bucle WHILE se repite mientras expresion se evalue como verdadero.
Es el nico tipo de bucle del que dispone Transact SQL.
WHILE <expresion>
BEGIN
...
END
Un ejemplo del bucle WHILE.
14
END
BREAK
PRINT 'Iteracion del bucle ' + cast(@contador AS varchar)
Estructura GOTO
La sentencia goto nos permite desviar el flujo de ejecucin hacia una
etiqueta. Fu muy utilizada en versiones anteriores de SQL Server
conjuntamente con la variable de sistema @@ERROR para el control de
errores.
Actualmente, se desaconseja el uso GOTO, recomendandose el uso de
TRY - CATCH para la gestion de errores.
15
16
@dividendo int,
@resultado int
SET @dividendo = 100
SET @divisor = 0
-- Esta linea provoca un error de division por 0
SET @resultado = @dividendo/@divisor
PRINT 'No hay error'
END TRY
BEGIN CATCH
PRINT ERROR_NUMBER()
PRINT ERROR_SEVERITY()
PRINT ERROR_STATE()
PRINT ERROR_PROCEDURE()
PRINT ERROR_LINE()
PRINT ERROR_MESSAGE()
END CATCH
Lgicamente, podemos utilizar estas funciones para almacenar esta informacin
en una tabla de la base de datos y registrar todos los errores que se produzcan.
DECLARE @divisor
int ,
@dividendo int ,
@resultado int
SET @dividendo = 100
SET @divisor = 0
-- Esta linea provoca un error de division por 0
SET @resultado = @dividendo/@divisor
IF @@ERROR = 0
BEGIN
PRINT 'No hay error'
END
ELSE
BEGIN
PRINT 'Hay error'
END
El uso de @@ERROR para controlar errores puede provocar multitud de
problemas. Uno de los ms habituales es sin duda, incluir una nueva sentencia
Transact SQL entre la lnea que provoco el error y la que lo controla. Esa nueva
instruccin restaura el valor de @@ERROR y no controlaremos el error.
El siguiente ejemplo ilustra esta situacin:
17
DECLARE @divisor
int ,
@dividendo int ,
@resultado int
SET @dividendo = 100
SET @divisor = 0
-- Esta linea provoca un error de division por 0
SET @resultado = @dividendo/@divisor
PRINT 'Controlando el error ...' -- Esta linea estable @@ERROR a cero
IF @@ERROR = 0
BEGIN
-- Se ejecuta esta parte!
PRINT 'No hay error'
END
ELSE
BEGIN
PRINT 'Hay error'
END
18
El uso del asterisco indica que queremos que la consulta devuelva todos los campos que
existen en la tabla.
SELECT *
FROM FAMILIAS
Ahora vamos a realizar una consulta obteniendo adems de los datos de familias, los datos
de las categorias y los productos.
SELECT *
FROM FAMILIAS
INNER JOIN CATEGORIAS
ON CATEGORIAS.CO_FAMILIA = FAMILIAS.CO_FAMILIA
INNER JOIN PRODUCTOS
ON PRODUCTOS.CO_CATEGORIA = CATEGORIAS.CO_CATEGORIA
La combinacin se realiza a travs de la clausula INNER JOIN, que es una clasula
exclusiva, es decir las familias que no tengan categorias y productos asociados no se
devolveran.
Si queremos realizar la consulta para que no sea exclusiva, tenemos que utilizar LEFT
JOIN. El uso de la palabra reservada OUTER es opcional.
19
SELECT *
FROM FAMILIAS
LEFT OUTER JOIN CATEGORIAS
ON CATEGORIAS.CO_FAMILIA = FAMILIAS.CO_FAMILIA
LEFT OUTER JOIN PRODUCTOS
ON PRODUCTOS.CO_CATEGORIA = CATEGORIAS.CO_CATEGORIA
Los registros que no tengan datos relacionados en una consulta LEFT JOIN devolveran en
valor null en los campos que correspondan a las tablas en las que no tienen dato.
Tambin podemos forzar un producto cartesiano (todos con todos) a travs de CROSS
JOIN.
La clusula WHERE
La clusula WHERE es la instruccin que nos permite filtrar el resultado de una sentencia
SELECT.
SELECT *
FROM FAMILIAS
WHERE CO_FAMILIA = 1
OR CO_FAMILIA = 2
Podemos agrupar varias valores para una condicion en la clausula IN:
SELECT *
FROM FAMILIAS
WHERE CO_FAMILIA IN ( 1 , 2)
20
La clausula WHERE se puede utilizar conjuntamente con INNER JOIN, LEFT JOIN ...
SELECT
FAMILIAS.CO_FAMILIA,
FAMILIAS.FAMILIA
FROM FAMILIAS
INNER JOIN CATEGORIAS
ON CATEGORIAS.CO_FAMILIA = FAMILIAS.CO_FAMILIA
WHERE FAMILIAS.CO_FAMILIA > 1
Siempre que incluyamos un valor alfanumerico para un campo en la condicin WHERE
este debe ir entre comillas simples:
SELECT *
FROM FAMILIAS
WHERE FAMILIA = 'FAMILIA 1'
Para consultar campos alfanumericos, es decir, campos de texto podemos utilizar el
operador LIKE conjuntamente con comodines.
SELECT *
FROM FAMILIAS
WHERE FAMILIA LIKE 'FAM%'
Los comodines que podemos utilizar en son los siguientes:
21
La clusula ORDER BY
Podemos especificar el orden en el que sern devueltos los datos a travs de la clusula
ORDER BY.
22
Consultas agregadas
La clusula GROUP BY
La clausula GROUP BY combina los registros devueltos por una consulta SELECT
obteniendo uno o varios valores agregados(suma, valor mnimo y mximo ...).
Para cada registro se puede crear un valor agregado si se incluye una funcin SQL
agregada, como por ejemplo Sum o Count, en la instruccin SELECT. Su sintaxis es:
SELECT COUNT(*)
FROM PRODUCTOS
Este otro ejemplo, muestra la suma del PRECIO de cada uno de los productos que
componen un pedido, para calcular el total del pedido agrupados por los datos del cliente.
SELECT
CLIENTES.NOMBRE,
CLIENTES.APELLIDO1,
CLIENTES.APELLIDO2,
SUM(PRECIO) -- Total del pedido
FROM DETALLE_PEDIDO
INNER JOIN PEDIDOS
ON DETALLE_PEDIDO.CO_PEDIDO = PEDIDOS.CO_PEDIDO
23
SELECT
CLIENTES.NOMBRE,
CLIENTES.APELLIDO1,
CLIENTES.APELLIDO2,
SUM(PRECIO) -- Total del pedido
FROM DETALLE_PEDIDO
INNER JOIN PEDIDOS
ON DETALLE_PEDIDO.CO_PEDIDO = PEDIDOS.CO_PEDIDO
INNER JOIN CLIENTES
ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE
-- La clausula WHERE se aplica antes de realizar el calculo
WHERE CLIENTES.NOMBRE != 'UN NOMBRE'
GROUP BY CLIENTES.NOMBRE,
CLIENTES.APELLIDO1,
CLIENTES.APELLIDO2
La clusula HAVING
Es posible que necesitemos calcular un agregado, pero que no necesitemos obtener todos
los datos, solo los que cumplan una condicin del agregado. Por ejemplo, podemos calcular el
valor de las ventas por producto, pero que solo queramos ver los datos de los producto que
hayan vendido ms o menos de una determinada cantidad. En estos casos debemos utilizar la
clausula HAVING.
Una vez que GROUP BY ha combinado los registros, HAVING muestra cualquier registro
agrupado por la clusula GROUP BY que satisfaga las condiciones de la clusula HAVING. Se
utiliza la clusula WHERE para excluir aquellas filas que no desea agrupar, y la clusula
24
CLIENTES.NOMBRE,
CLIENTES.APELLIDO1,
CLIENTES.APELLIDO2,
SUM(PRECIO) -- Total del pedido
FROM DETALLE_PEDIDO
INNER JOIN PEDIDOS
ON DETALLE_PEDIDO.CO_PEDIDO = PEDIDOS.CO_PEDIDO
ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE
Funciones agregadas.
Transact SQL pone a nuestra disposicin multiples funciones agregadas, las ms comunes
son:
MAX
MIN
COUNT
SUM
AVG
AVG
Calcula la media aritmtica de un conjunto de valores contenidos en un campo
especificado de una consulta. Su sintaxis es la siguiente
AVG(<expr>)
25
En donde expr representa el campo que contiene los datos numricos para los que se desea
calcular la media o una expresin que realiza un clculo utilizando los datos de dicho campo.
La media calculada por Avg es la media aritmtica (la suma de los valores dividido por el
nmero de valores). La funcin Avg no incluye ningn campo Null en el clculo.
SELECT
CLIENTES.NOMBRE,
CLIENTES.APELLIDO1,
CLIENTES.APELLIDO2,
AVG(PRECIO) -- Promedio del pedido
FROM DETALLE_PEDIDO
INNER JOIN PEDIDOS
ON DETALLE_PEDIDO.CO_PEDIDO = PEDIDOS.CO_PEDIDO
INNER JOIN CLIENTES
ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE
GROUP BY CLIENTES.NOMBRE,
CLIENTES.APELLIDO1,
CLIENTES.APELLIDO2
Count
Calcula el nmero de registros devueltos por una consulta. Su sintaxis es la siguiente:
COUNT(<expr>)
En donde expr contiene el nombre del campo que desea contar. Los operandos de expr
pueden incluir el nombre de un campo de una tabla, una constante o una funcin (la cual
puede ser intrnseca o definida por el usuario pero no otras de las funciones agregadas de
SQL). Puede contar cualquier tipo de datos incluso texto.
Aunque expr puede realizar un clculo sobre un campo, Count simplemente cuenta el
nmero de registros sin tener en cuenta qu valores se almacenan en los registros. La funcin
Count no cuenta los registros que tienen campos null a menos que expr sea el carcter
comodn asterisco (*). Si utiliza un asterisco, Count calcula el nmero total de registros,
incluyendo aquellos que contienen campos null. Count(*) es considerablemente ms rpida
que Count(Campo).
26
SELECT COUNT(*)
FROM PEDIDOS
SELECT CLIENTES.NOMBRE, COUNT(*)
FROM PEDIDOS
INNER JOIN CLIENTES
ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE
GROUP BY CLIENTES.NOMBRE
Max, Min
Devuelven el mnimo o el mximo de un conjunto de valores contenidos en un campo
especifico de una consulta. Su sintaxis es:
MIN(<expr>)
MAX(<expr>)
En donde expr es el campo sobre el que se desea realizar el clculo. Expr pueden incluir el
nombre de un campo de una tabla, una constante o una funcin (la cual puede ser intrnseca
o definida por el usuario pero no otras de las funciones agregadas de SQL).
SELECT
CLIENTES.NOMBRE,
MIN(PEDIDOS.FX_ALTA),
MAX(PEDIDOS.FX_ALTA)
FROM PEDIDOS
INNER JOIN CLIENTES
ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE
GROUP BY CLIENTES.NOMBRE
Sum
Devuelve la suma del conjunto de valores contenido en un campo especifico de una
consulta. Su sintaxis es:
SUM(<expr>)
27
En donde expr respresenta el nombre del campo que contiene los datos que desean
sumarse o una expresin que realiza un clculo utilizando los datos de dichos campos. Los
operandos de expr pueden incluir el nombre de un campo de una tabla, una constante o una
funcin (la cual puede ser intrnseca o definida por el usuario pero no otras de las funciones
agregadas de SQL).
SELECT
CLIENTES.NOMBRE,
SUM(PEDIDOS.TOTAL_PEDIDO)
FROM PEDIDOS
INNER JOIN CLIENTES
ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE
GROUP BY CLIENTES.NOMBRE
SELECT TOP 3
CLIENTES.NOMBRE,
SUM(DETALLE_PEDIDO.PRECIO)
FROM DETALLE_PEDIDO
INNER JOIN PEDIDOS
ON DETALLE_PEDIDO.CO_PEDIDO = PEDIDOS.CO_PEDIDO
INNER JOIN CLIENTES
ON PEDIDOS.CO_CLIENTE = CLIENTES.CO_CLIENTE
GROUP BY CLIENTES.NOMBRE
ORDER BY 2 -- SUM(DETALLE_PEDIDO.PRECIO_UNIDAD)
Sin embargo, puede darse el caso, de que el cuarto cliente devuelto por la consulta tenga
un valor agragado identico al tercero, (es decir, estan empatados). El uso de TOP 3
discriminara el cuarto registro. Para evitar este comportamiento, y que la consulta devuelva
28
XML AUTO, el modo AUTO emplea los campos en la declaracin SELECT para formar
una jerarqua simple XML.
XML RAW, el modo RAW genera elementos nicos, los cuales se denominan row, por
cada fila retornada.
EXPLICIT, el modo EXPLICIT requiere un formato especfico que puede ser mapeado
en casi cualquier forma XML, y al mismo tiempo ser formulado por una sola consulta
SQL.
29
ORDER BY FAMILIA
FOR XML AUTO, TYPE
Obtendremos el siguiente resultado:
<FAMILIAS CO_FAMILIA="1" FAMILIA="FAMILIA 1" />
<FAMILIAS CO_FAMILIA="2" FAMILIA="FAMILIA 2" />
<FAMILIAS CO_FAMILIA="3" FAMILIA="FAMILIA 3" />
<FAMILIAS CO_FAMILIA="4" FAMILIA="FAMILIA 4" />
Podemos obtener el resultado como elementos de la siguiente forma:
SELECT CO_FAMILIA, FAMILIA
FROM FAMILIAS
FOR XML AUTO, ELEMENTS
Obtendremos el siguiente resultado:
<FAMILIAS>
<CO_FAMILIA>1</CO_FAMILIA>
<FAMILIA>FAMILIA 1</FAMILIA>
</FAMILIAS>
<FAMILIAS>
<CO_FAMILIA>2</CO_FAMILIA>
<FAMILIA>FAMILIA 2</FAMILIA>
</FAMILIAS>
<FAMILIAS>
<CO_FAMILIA>3</CO_FAMILIA>
<FAMILIA>FAMILIA 3</FAMILIA>
</FAMILIAS>
<FAMILIAS>
<CO_FAMILIA>4</CO_FAMILIA>
30
<FAMILIA>FAMILIA 4</FAMILIA>
</FAMILIAS>
Ahora un ejemplo de XML RAW:
SELECT CO_FAMILIA, FAMILIA
FROM FAMILIAS
ORDER BY FAMILIA
31
<FAMILIA>FAMILIA 4</FAMILIA>
</row>
Tambin es posible especificar el nodo que queremos que muestre:
<FamiliasDeProductos>
<CO_FAMILIA>1</CO_FAMILIA>
<FAMILIA>FAMILIA 1</FAMILIA>
</FamiliasDeProductos>
<FamiliasDeProductos>
<CO_FAMILIA>2</CO_FAMILIA>
32
<FAMILIA>FAMILIA 2</FAMILIA>
</FamiliasDeProductos>
<FamiliasDeProductos>
<CO_FAMILIA>3</CO_FAMILIA>
<FAMILIA>FAMILIA 3</FAMILIA>
</FamiliasDeProductos>
<FamiliasDeProductos>
<CO_FAMILIA>4</CO_FAMILIA>
<FAMILIA>FAMILIA 4</FAMILIA>
</FamiliasDeProductos>
Ahora un ejemplo con el formato XML EXPLICIT.
SELECT
1 AS TAG,
as "FamiliaDeProductos!1!DESCRIPCION"
FROM FAMILIAS
ORDER BY FAMILIA
FOR XML EXPLICIT
Obtenemos el siguiente resultado:
33
34
Las consultas a unir deben tener el mismo nmero campos, y adems los
campos deben ser del mismo tipo.
Slo puede haber una nica clausula ORDER BY al final de la sentencia
SELECT.
UNION
UNION devuelve la suma de dos o ms conjuntos de resultados. El conjunto
obtenido como resultado de UNION tiene la misma estructura que los conjuntos
originales.
El siguiente ejemplo muestra el uso de UNION
35
UNION
SELECT Nombre, Apellido1 , Apellido2, NifCif, FxNacimiento
FROM CLIENTES
Cuando realizamos una consulta con UNION internamente se realiza una
operacion DISTINCT sobre el conjunto de resultados final. Si queremos obtener
todos los valores debemos utiliza UNION ALL.
EXCEPT
EXCEPT devuelve la diferencia (resta) de dos o ms conjuntos de resultados. El
conjunto obtenido como resultado de EXCEPT tiene la misma estructura que los
conjuntos originales.
El siguiente ejemplo muestra el uso de EXCEPT
INTERSECT
Devuelve la interseccin entre dos o ms conjuntos de resultados en uno. El
conjunto obtenido como resultado de INTERSECT tiene la misma estructura que los
conjuntos originales.
El siguiente ejemplo muestra el uso de INTERSECT
36
FROM EMPLEADOS
INTERSECT
SELECT Nombre, Apellido1 , Apellido2, NifCif, FxNacimiento
FROM CLIENTES
PRECIO_UNIDAD,
FX_INICIO,
FX_FIN,
CO_PRODUCTO)
CO_PRODUCTO
FROM DETALLE_PEDIDO
37
En SQL Sever podemos marcar un campo de una tabla como autonumrico (identity), cuando
insertamos un registro en dicha tabla el valor del campo se genera automaticamente. Para
recuperar el valor generado disponemos de varios mtodos:
Utilizar la funcion @@identity, que devuelve el ltimo valor identidad insertado por la
transaccion:
Clausula OUTPUT
A partir de la version de SQL Server 2005 disponemos de la clausula OUTPUT para
recuperar los valores que hemos insertado. Al igual que en un trigger disponemos de las
tablas lgicas INSERTED y DELETED.
Las columnas con prefijo DELETED reflejan el valor antes de que se complete la
instruccin UPDATE o DELETE. Es decir, son una copia de los datos "antes" del cambio.
DELETED no se puede utilizar con la clusula OUTPUT en la instruccin INSERT.
Las columnas con prefijo INSERTED reflejan el valor despus de que se complete la
38
UPDATE <nombre_tabla>
SET <campo1> = <valor1>
39
WHERE <condicion>];
El siguiente ejemplo muestra el uso de UPDATE.
UPDATE CLIENTES
SET
NOMBRE = 'Devjoker',
APELLIDO1 = 'Herrarte',
APELLIDO2 = 'Snchez'
WHERE CO_CLIENTE = 10
Un aspecto a tener en cuenta, sobre todo si has trabajado con ORACLE, es que
SQL graba los cambios inmediatamente sin necesidad de hacer COMMIT. Por
supuesto podemos gestionar nosostros las transacciones pero es algo que hay que
hacer de forma explicita con la instruccion BEGIN TRAN y que se ver en capitulos
posteriores de este tutorial.
UPDATE CLIENTES
SET
NOMBRE = FICHERO_CLIENTES.NOMBRE,
APELLIDO1 = FICHERO_CLIENTES.APELLIDO1,
APELLIDO2 = FICHERO_CLIENTES.APELLIDO2
FROM CLIENTES
INNER JOIN FICHERO_CLIENTES
ON FICHERO_CLIENTES.CO_CLIENTE = CLIENTES.CO_CLIENTE
Clausula OUTPUT
A partir de la version de SQL Server 2005 disponemos de la clausula OUTPUT
para recuperar los valores que hemos insertado. Al igual que en un trigger
disponemos de las tablas lgicas INSERTED y DELETED.
40
Las columnas con prefijo DELETED reflejan el valor antes de que se complete la
instruccin UPDATE o DELETE. Es decir, son una copia de los datos "antes" del
cambio.
DELETED no se puede utilizar con la clusula OUTPUT en la instruccin INSERT.
UPDATE CLIENTES
SET
NOMBRE = 'Devjoker',
APELLIDO1 = 'Herrarte',
APELLIDO2 = 'Snchez'
OUTPUT
41
UPDATE CLIENTES
SET
NOMBRE = 'Devjoker',
APELLIDO1 = 'Herrarte',
APELLIDO2 = 'Snchez'
OUTPUT
GO
42
DECLARE @i int,
@dato varchar(100)
set @i = 0
WHILE (@i <100)
BEGIN
SET @i = @i +1
set @dato = 'Dato:' + cast(@i as varchar)
INSERT INTO DATOS (dato, fx_alta)
VALUES (@dato, getdate())
END
GO
43
WHERE Id=17
SELECT @@ROWCOUNT
Clausula OUTPUT
A partir de la version de SQL Server 2005 disponemos de la clausula OUTPUT
para recuperar los valores que hemos insertado. Al igual que en un trigger
disponemos de las tablas lgicas INSERTED y DELETED.
Las columnas con prefijo DELETED reflejan el valor antes de que se complete la
instruccin UPDATE o DELETE. Es decir, son una copia de los datos "antes" del
cambio.
DELETED no se puede utilizar con la clusula OUTPUT en la instruccin INSERT.
DELETE
FROM DATOS
OUTPUT DELETED.* INTO @FILAS_BORRADAS
WHERE Id=17
Truncate Table
Para borrar datos de forma masiva disponemos de la instruccin TRUNCATE
TABLE, que borra todos los datos de una tabla.
44
= '200700000001'
45
UPDATE CUENTAS
SET SALDO = SALDO - @importe
WHERE NUMCUENTA = @CuentaOrigen
/* Registramos el movimiento */
INSERT INTO MOVIMIENTOS
(IDCUENTA, SALDO_ANTERIOR, SALDO_POSTERIOR, IMPORTE, FXMOVIMIENTO)
SELECT
IDCUENTA, SALDO + @importe, SALDO, @importe, getdate()
FROM CUENTAS
WHERE NUMCUENTA = @CuentaOrigen
/* Registramos el movimiento */
INSERT INTO MOVIMIENTOS
(IDCUENTA, SALDO_ANTERIOR, SALDO_POSTERIOR, IMPORTE, FXMOVIMIENTO)
SELECT
IDCUENTA, SALDO - @importe, SALDO, @importe, getdate()
FROM CUENTAS
WHERE NUMCUENTA = @CuentaDestino
46
Transacciones explcitas
Cada transaccin se inicia explcitamente con la instruccin BEGIN TRANSACTION y
se termina explcitamente con una instruccin COMMIT o ROLLBACK.
Transacciones implcitas
Se inicia automtivamente una nueva transaccin cuando se ejecuta una
instruccin que realiza modificaciones en los datos, pero cada transaccin se
completa explcitamente con una instruccin COMMIT o ROLLBACK.
47
/* Registramos el movimiento */
INSERT INTO MOVIMIENTOS
(IDCUENTA, SALDO_ANTERIOR, SALDO_POSTERIOR,
IMPORTE, FXMOVIMIENTO)
SELECT
IDCUENTA, SALDO + @importe, SALDO, @importe, getdate()
FROM CUENTAS
WHERE NUMCUENTA = @CuentaOrigen
/* Registramos el movimiento */
INSERT INTO MOVIMIENTOS
(IDCUENTA, SALDO_ANTERIOR, SALDO_POSTERIOR,
IMPORTE, FXMOVIMIENTO)
SELECT
IDCUENTA, SALDO - @importe, SALDO, @importe, getdate()
FROM CUENTAS
WHERE NUMCUENTA = @CuentaDestino
48
/* Confirmamos la transaccion*/
COMMIT TRANSACTION -- O solo COMMIT
END TRY
BEGIN CATCH
/* Hay un error, deshacemos los cambios*/
ROLLBACK TRANSACTION -- O solo ROLLBACK
PRINT 'Se ha producido un error!'
END CATCH
SET IMPLICIT_TRANSACTIONS ON
BEGIN TRY
49
/* Registramos el movimiento */
INSERT INTO MOVIMIENTOS
(IDCUENTA, SALDO_ANTERIOR, SALDO_POSTERIOR,
IMPORTE, FXMOVIMIENTO)
SELECT
IDCUENTA, SALDO + @importe, SALDO, @importe, getdate()
FROM CUENTAS
WHERE NUMCUENTA = @CuentaOrigen
/* Registramos el movimiento */
INSERT INTO MOVIMIENTOS
(IDCUENTA, SALDO_ANTERIOR, SALDO_POSTERIOR,
IMPORTE, FXMOVIMIENTO)
SELECT
IDCUENTA, SALDO - @importe, SALDO, @importe, getdate()
FROM CUENTAS
WHERE NUMCUENTA = @CuentaDestino
50
/* Confirmamos la transaccion*/
COMMIT TRANSACTION -- O solo COMMIT
END TRY
BEGIN CATCH
/* Hay un error, deshacemos los cambios*/
ROLLBACK TRANSACTION -- O solo ROLLBACK
PRINT 'Se ha producido un error!'
END CATCH
SET IMPLICIT_TRANSACTIONS ON
BEGIN TRY
UPDATE CUENTAS SET FXALTA = FXALTA - 1
PRINT @@TRANCOUNT
COMMIT
END TRY
BEGIN CATCH
ROLLBACK
PRINT 'Error'
END CATCH
Otro punto a tener en cuenta cuando trabajamos con transacciones son los
bloqueos y el nivel de aislamiento.
51
Transacciones anidadas.
Podemos anidar varias transacciones. Cuando anidamos varias transacciones la
instruccin COMMIT afectar a la ltima transaccin abierta, pero ROLLBACK
afectar a todas las transacciones abiertas.
Un hecho a tener en cuenta, es que, si hacemos ROLLBACK de la transaccin
superior se desharan tambin los cambios de todas las transacciones internas,
aunque hayamos realizado COMMIT de ellas.
BEGIN TRAN
UPDATE EMPLEADOS
SET NOMBRE = 'Devjoker'
WHERE ID=101
BEGIN TRAN
UPDATE EMPLEADOS
SET APELLIDO1 = 'Devjoker.COM'
WHERE ID=101
52
BEGIN TRAN
UPDATE EMPLEADOS
SET NOMBRE = 'Devjoker'
WHERE ID=101
UPDATE EMPLEADOS
SET APELLIDO1 = 'Devjoker.COM'
WHERE ID=101
UPDATE EMPLEADOS
SET APELLIDO1 = 'Otra cosa!'
WHERE ID=101
-- Confirmamos la transaccion
COMMIT
53
54
AS
INSERT INTO CLIENTES (nombre, apellido1, apellido2, nifcif, fxnacimiento)
VALUES (@nombre, @apellido1, @apellido2, @nifCif, @fxNaciento)
@nombre varchar(100),
@apellido1 varchar(100),
@apellido2 varchar(100),
@nifCif varchar(20),
@fxNaciento datetime
AS
BEGIN TRY
BEGIN TRAN
INSERT INTO CLIENTES
(nombre, apellido1, apellido2, nifcif, fxnacimiento) VALUES
(@nombre, @apellido1, @apellido2, @nifCif, @fxNaciento)
COMMIT
END TRY
BEGIN CATCH
55
ROLLBACK
PRINT ERROR_MESSAGE()
END CATCH
Si queremos que los parmetros de un procedimiento almacenado sean de
entrada-salida debemos especificarlo a travs de la palabra clave OUTPUT , tanto
en la definicin del procedure como en la ejecucin.
El siguiente ejemplo muestra la definicin de un procedure con parmetros de
salida.
56
AS
BEGIN
IF (SELECT SALDO FROM CUENTAS
WHERE NUMCUENTA = @numCuenta) < 0
BEGIN
RETURN 1
END
ELSE
RETURN 0
END
El siguiente ejemplo muestra como ejecutar el procedure y obtener el valor
devuelto.
@numCuenta,
SALDO_ANTERIOR,
SALDO_POSTERIOR,
IMPORTE,
FXMOVIMIENTO
57
FROM MOVIMIENTOS
INNER JOIN CUENTAS ON MOVIMIENTOS.IDCUENTA = CUENTAS.IDCUENTA
WHERE NUMCUENTA = @numCuenta
ORDER BY FXMOVIMIENTO DESC
END
La ejecucin del procedimiento se realiza normalmente.
20070000000150.99100.9950.002007082516:18:36.490
2007000000010.9950.9950.002007082316:20:41.183
20070000000150.990.9950.002007082316:16:29.840
2007000000010.9950.9950.002007082316:14:05.900
Funciones escalares.
Funciones en lnea.
Funciones en lnea de multiples sentencias
Funciones escalares
Las funciones escalares devuelven un nico valor de cualquier tipo de los datos
tal como int, money, varchar, real, etc.
La sintaxis para una funcin escalar es la siguiente:
58
RETURN @Return
END
59
SELECT
IDCUENTA,
NUMCUENTA,
SALDO,
FXALTA,
-- Ejecucion de la funcion:
PRINT @Resultado
Las funciones escalares son muy similares a procedimientos almacenados con
parmetros de salida, pero estas pueden ser utilizadas en consultas de seleccion y
en la clausula where de las mismas.
Las funciones no pueden ejecutar sentencias INSERT o UPDATE.
Funciones en linea
Las funciones en linea son las funciones que devuelven un conjunto de resultados
correspondientes a la eecucin de una sentencia SELECT.
La sintaxis para una funcin de tabla en linea es la siguiente:
60
61
Las funciones en linea pueden utilizarse dentro de joins o querys como si fueran
una tabla normal.
62
AS
BEGIN
-- Sentencias que cargan de datos la tabla declarada
RETURN
END
El siguiente ejemplo muestra el uso de una funcion de tabla de multi sentencias.
63
@saldo decimal(10,2)
OPEN CDATOS
FETCH CDATOS INTO @idcuenta, @numcuenta, @saldo
-- Recorremos el cursor
WHILE (@@FETCH_STATUS = 0)
BEGIN
-- Insertamos la cuenta en la variable de salida
INSERT INTO @datos
(NumCuenta, Saldo)
VALUES
(@numcuenta, @saldo)
-- Insertamos los tres ltimos movimientos de la cuenta
INSERT INTO @datos
(Saldo_anterior, Saldo_posterior,
Importe_Movimiento, FxMovimiento )
SELECT TOP 3
SALDO_ANTERIOR, SALDO_POSTERIOR,
IMPORTE, FXMOVIMIENTO
FROM MOVIMIENTOS
WHERE IDCUENTA = @idcuenta
64
CLOSE CDATOS;
DEALLOCATE CDATOS;
RETURN
END
Para ejecutar la funcin:
Saldo_posterior
Importe_Movimiento
---------------- ------------------NULL
NULL
500.00
50.00
2007-08-25
550.00
50.00
2007-08-23
550.00
50.00
2007-08-23
NULL
NULL
100.99
50.00
2007-08-25
50.99
50.00
2007-08-23
0.99
50.00
2007-08-23
Cast y Convert
65
SELECT @fecha
SELECT @fechaFormateada
66
SELECT @dato2
A continuacin mostramos la tabla de cdigos de estilo (obtenida de MicroSoft).
Sin el siglo
(aa) (1)
Con el siglo ?
(aaaa)
Estndar
Entrada/salida (3)
0 o 100 (1, 2)
Valor predeterminado
101
EE.UU.
mm/dd/aaaa
102
ANSI
aa.mm.dd
103
Britnico/Francs
dd/mm/aa
104
Alemn
dd.mm.aa
105
Italiano
dd-mm-aa
106
(1)
dd mes aa
107
(1)
Mes dd, aa
108
hh:mi:ss
9 o 109 (1, 2)
Valor predeterminado +
milisegundos
mes dd aaaa
hh:mi:ss:mmma.m. (o p. m.)
10
110
EE.UU.
mm-dd-aa
11
111
JAPN
aa/mm/dd
12
112
ISO
aammdd
13 o 113 (1, 2)
14
114
hh:mi:ss:mmm(24h)
20 o 120 (2)
ODBC cannico
aaaa-mm-dd hh:mi:ss(24h)
21 o 121 (2)
aaaa-mm-dd
hh:mi:ss.mmm(24h)
67
aaaa-mm-ddThh:mi:ss.mmm
(sin espacios)
aaaa-mm-ddThh:mi:ss.mmmZ
126 (4)
ISO8601
127(6, 7)
130 (1, 2)
Hijri (5)
dd mes aaaa
hh:mi:ss:mmma.m.
131 (2)
Hijri (5)
dd/mm/aa hh:mi:ss:mmma.m.
(sin espacios)
Isnull
Evalua una expresion de entrado y si esta es NULL, reemplaza NULL con el valor
de reemplazo especificado. El valor de reemplazo debe ser del mismo tipo de datos
que la expresion a evaluar.
ISNULL ( expression , replacement_value )
SELECT
ISNULL(@dato, -1),
ISNULL(@datoVarchar, 'No hay dato')
COALESCE
Devuelve la primera expresin distinta de NULL entre sus argumentos. Un
aspecto a tener en cuenta es que todos los argumentos deben ser del mismo
tipo.
COALESCE ( expression [ ,...n ] )
68
@dato4 int,
@dato5 int
-- Devuelve 100
SELECT COALESCE(@dato1,@dato2,@dato3,@dato4,@dato5)
GetDate y GetUTCDate
GetDate devuelve la fecha y hora actuales del sistema en el formato interno
estndar de SQL Server 2005 para los valores datetime.
GetUTCDate devuelve el valor datetime que representa la hora UTC (hora
universal coordinada u hora del meridiano de Greenwich) actual.
69
Trigger DML.
Los trigger DML se ejecutan cuando un usuario intenta modificar datos mediante
un evento de lenguaje de manipulacin de datos (DML). Los eventos DML son
instrucciones INSERT, UPDATE o DELETE de una tabla o vista.
La sintaxis general de un trigger es la siguiente.
70
No existe una tabla UPDATED? No, hacer una actualizacin es lo mismo que
borrar (deleted) e insertar los nuevos (inserted). La sentencia UPDATE es la nica
en la que inserted y deleted tienen datos simultaneamente.
No puede se modificar directamente los datos de estas tablas.
El siguiente ejemplo, graba un historico de saldos cada vez que se modifica un
saldo de la tabla cuentas.
UPDATE CUENTAS
SET SALDO = SALDO + 10
WHERE IDCUENTA = 1
Una consideracin a tener en cuenta es que el trigger se ejecutar aunque la
instruccion DML (UPDATE, INSERT o DELETE ) no haya afectado a ninguna fila. En
este caso inserted y deleted devolveran un conjunto de datos vacio.
Podemos especificar a que columnas de la tabla debe afectar el trigger.
71
ON CUENTAS
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON impide que se generen mensajes de texto
-- con cada instruccin
SET NOCOUNT ON;
72
ROLLBACK
END
En este caso obtendremos el siguiente mensaje de error:
La transaccin termin en el desencadenador. Se anul el lote.
Podemos activar y desactivar Triggers a tarvs de las siguientes instrucciones.
Trigger DDL
Los trigger DDL se ejecutan en respuesta a una variedad de eventos de
lenguaje de definicin de datos (DDL). Estos eventos corresponden
principalmente a instrucciones CREATE, ALTER y DROP de Transact-SQL, y
73
74
WHILE (@@FETCH_STATUS = 0)
BEGIN
-- Lectura de la siguiente fila de un cursor
FETCH <nombre_cursor> INTO <lista_variables>
...
END -- Fin del bucle WHILE
-- Cierra el cursor
CLOSE <nombre_cursor>
-- Libera los recursos del cursor
DEALLOCATE <nombre_cursor>
75
@Nombre varchar(255),
@Apellido1 varchar(255),
@Apellido2 varchar(255),
@NifCif varchar(20),
@FxNacimiento datetime
FROM CLIENTES
-- Apertura del cursor
OPEN cClientes
-- Lectura de la primera fila del cursor
FETCH cClientes INTO
WHILE (@@FETCH_STATUS = 0 )
BEGIN
PRINT @Nombre + ' ' + @Apellido1 + ' ' + @Apellido2
-- Lectura de la siguiente fila del cursor
FETCH cClientes INTO
END
76
Descripcin
-1
-2
LOCAL
Especifica que el mbito del cursor es local para el proceso por lotes, procedimiento
almacenado o desencadenador en que se cre el cursor.
FROM CLIENTES
GLOBAL
Especifica que el mbito del cursor es global para la conexin. Puede hacerse
referencia al nombre del cursor en cualquier procedimiento almacenado o proceso
por lotes que se ejecute en la conexin.
77
SELECT
FROM CLIENTES
Si no se especifica GLOBAL ni LOCAL, el valor predeterminado se controla
mediante la configuracin de la opcin de base de datos default to local cursor.
El siguiente conjunto de parmetros que podemos especificar es [
FORWARD_ONLY | SCROLL ]. A continuacin mostramos el significado de cada una
de estas opciones.
FORWARD_ONLY
Especifica que el cursor slo se puede desplazar de la primera a la ltima fila. FETCH
NEXT es la nica opcin de recuperacin admitida.
FROM CLIENTES
SCROLL
Especifica que estn disponibles todas las opciones de recuperacin (FIRST, LAST,
PRIOR, NEXT, RELATIVE, ABSOLUTE). Si no se especifica SCROLL en una instruccin
DECLARE CURSOR la nica opcin de recuperacin que se admite es NEXT. No es
posible especificar SCROLL si se incluye tambin FAST_FORWARD.
Si se incluye la opcin SCROLL, la forma en la realizamos la lectura del cursor varia,
debiendo utilizar la siguiente sintaxis: FETCH [ NEXT | PRIOR | FIRST | LAST | RELATIVE
| ABSOLUTE ] FROM < INTO
78
FROM CLIENTES
-- Apertura del cursor
OPEN cClientes
-- Lectura de la primera fila del cursor
FETCH NEXT FROM cClientes
INTO @id, @Nombre, @Apellido1, @Apellido2, @NifCif, @FxNacimiento
WHILE (@@FETCH_STATUS = 0 )
BEGIN
PRINT @Nombre + ' ' + @Apellido1 + ' ' + @Apellido2
-- Lectura de la siguiente fila del cursor
FETCH NEXT FROM cClientes
INTO @id,@Nombre,@Apellido1,@Apellido2,@NifCif,@FxNacimiento
END
-- Lectura de la fila anterior
FETCH PRIOR FROM cClientes
INTO @id, @Nombre, @Apellido1, @Apellido2, @NifCif, @FxNacimiento
PRINT @Nombre + ' ' + @Apellido1 + ' ' + @Apellido2
-- Cierre del cursor
CLOSE cClientes
-- Liberar los recursos
DEALLOCATE cClientes
El siguiente conjunto de parmetros que podemos especificar es [ STATIC |
KEYSET | DYNAMIC | FAST_FORWARD ]. A continuacin mostramos el significado de
cada una de estas opciones.
79
STATIC
Define un cursor que hace una copia temporal de los datos que va a utilizar. Todas las
solicitudes que se realizan al cursor se responden desde esta tabla temporal de
tempdb; por tanto, las modificaciones realizadas en las tablas base no se reflejan en
los datos devueltos por las operaciones de recuperacin realizadas en el cursor y
adems este cursor no admite modificaciones.
FROM CLIENTES
KEYSET
Especifica que la pertenencia y el orden de las filas del cursor se fijan cuando se abre
el cursor. El conjunto de claves que identifica las filas de forma nica est integrado
en la tabla denominada keyset de tempdb.
FROM CLIENTES
DYNAMIC
Define un cursor que, al desplazarse por l, refleja en su conjunto de resultados todos
los cambios realizados en los datos de las filas. Los valores de los datos, el orden y la
pertenencia de las filas pueden cambiar en cada operacin de recuperacin. La
opcin de recuperacin ABSOLUTE no se puede utilizar en los cursores dinmicos.
FROM CLIENTES
FAST_FORWARD
Especifica un cursor FORWARD_ONLY, READ_ONLY con las optimizaciones de
rendimiento habilitadas. No se puede especificar FAST_FORWARD si se especifica
tambin SCROLL o FOR_UPDATE.
80
READ_ONLY
Evita que se efecten actualizaciones a travs de este cursor. No es posible hacer
referencia al cursor en una clusula WHERE CURRENT OF de una instruccin UPDATE
o DELETE. Esta opcin reemplaza la capacidad de actualizar el cursor.
FROM CLIENTES
SCROLL_LOCKS
Especifica que se garantiza que las actualizaciones o eliminaciones posicionadas
realizadas a travs del cursor sern correctas. Microsoft SQL Server bloquea las filas
cuando se leen en el cursor para garantizar que estarn disponibles para futuras
modificaciones. No es posible especificar SCROLL_LOCKS si se especifica tambin
FAST_FORWARD o STATIC.
FROM CLIENTES
OPTIMISTIC
Especifica que las actualizaciones o eliminaciones posicionadas realizadas a travs
del cursor no se realizarn correctamente si la fila se ha actualizado despus de ser
leda en el cursor. SQL Server no bloquea las filas al leerlas en el cursor. En su lugar,
utiliza comparaciones de valores de columna timestamp o un valor de suma de
comprobacin si la tabla no tiene columnas timestamp, para determinar si la fila se
ha modificado despus de leerla en el cursor. Si la fila se ha modificado, el intento de
actualizacin o eliminacin posicionada genera un error. No es posible especificar
OPTIMISTIC si se especifica tambin FAST_FORWARD.
81
FROM CLIENTES
Por ltimo, queda la opcin TYPE_WARNING
TYPE_WARNING
Especifica que se enva un mensaje de advertencia al cliente si el cursor se convierte
implcitamente del tipo solicitado a otro.
FROM CLIENTES
Podemos especificar multiples parmetros en la apertura de cursor,
pero unicamente un parmetro de cada grupo. Por ejemplo:
FROM CLIENTES
Para actualizar los datos de un cursor debemos especificar FOR UPDATE despues
de la sentencia SELECT en la declaracin del cursor, y WHERE CURRENT OF
<nombre_cursor> en la sentencia UPDATE tal y como muestra el siguiente ejemplo.
-- Declaracion de variables para el cursor
DECLARE @Id int,
@Nombre varchar(255),
@Apellido1 varchar(255),
@Apellido2 varchar(255),
@NifCif varchar(20),
82
@FxNacimiento datetime
-- Declaracin del cursor
DECLARE cClientes CURSOR FOR
SELECT
FROM CLIENTES
FOR UPDATE
WHILE (@@FETCH_STATUS = 0 )
BEGIN
UPDATE Clientes
SET APELLIDO2 = isnull(@Apellido2,'') + ' - Modificado'
WHERE CURRENT OF cClientes
-- Lectura de la siguiente fila del cursor
FETCH cClientes
INTO @id, @Nombre, @Apellido1, @Apellido2,
@NifCif, @FxNacimiento
END
-- Cierre del cursor
CLOSE cClientes
-- Liberar los recursos
DEALLOCATE cClientes
83
La instruccin EXECUTE
La instruccin EXECUTE - o simplemente EXEC - permite ejecutar una cadena de
caracteres que representa una sentencia SQL. La cadena de caracteres debe ser de
tipo nvarchar .
El siguiente ejemplo muestra como ejecutar una cadena de caracteres con la
instruccin EXEC.
84
EXEC (@sql)
85
**FIN**
86