Transact SQL
Transact SQL
SQL es un lenguaje de consulta para los sistemas de bases de datos relacinales, pero que no posee la potencia de los lenguajes de programacin. Transact SQL es el lenguaje procedimental de programacin que proporciona SQL Server para ampliar SQL con los elementos caractersticos de los lenguajes de programacin: variables, sentencias de control de flujo, buclesTransact SQL es el lenguaje de programacin que proporciona SQL Server para extender el SQL estndar con otro tipo de instrucciones. Con Transact SQL vamos a poder programar las unidades de programa de la base de datos SQL Server, estn son: Procedimientos almacenados Funciones Triggers Scripts Pero adems Transact SQL nos permite realizar programas sobre las siguientes herramientas de SQL Server: Service Broker Fundamentos de Transact SQL Para programar en Transact SQL es necesario conocer sus fundamentos. Como introduccin vamos a ver algunos elementos y conceptos bsicos del lenguaje. Transact SQL no diferencia maysculas de minsculas Un comentario es una aclaracin que el programador incluye en el cdigo, dos tipos: o -- Para un comentario de lnea simple o /* ... */ Para un comentario de varias lneas Un literal es un valor fijo de tipo numrico, carcter, cadena o lgico no representado por un identificador (es un valor explcito). Una variable es un valor identificado por un nombre (identificador) sobre el que podemos realizar modificaciones. En Transact SQL los identificadores de variables deben comenzar por el carcter @, es decir, el nombre de una variable debe comenzar por @.Para declarar variables en Transact SQL debemos utilizar la palabra clave declare, seguido del identificador y tipo de datos de la variable. Veamos algunos ejemplos:
declare @nombre varchar(50) -- declare declara una variable -- @nombre es el identificador de la -- variable de tipo varchar set @nombre = 'www.devjoker.com' -- El signo = es un operador -- www.devjoker.com es un literal print @Nombre -- Imprime por pantalla el valor de @nombre. -- No diferencia maysculas ni minsculas
Scripts y lotes.
Un script de Transact SQL es un conjunto de sentencias de Transact SQL en formato de texto plano que se ejecutan en un servidor de SQL Server. 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 la instruccin GO.
-- Este es el primer lote del script SELECT * FROM COMENTARIOS
GO -- GO es el separador de lotes
-- Este es el segundo lote del script SELECT getdate() -- getdate() es una funcin integrada que devuelve -- la fecha
En ocasiones es necesario separar las sentencias en varios lotes, porque Transact SQL no permite la ejecucin de ciertos comandos en el mismo lote, si bien normalmente tambin se utilizan los lotes para realizar separaciones lgicas dentro del script.
Cuando definimos una tabla, variable o constante debemos asignar un tipo de dato que indica los posibles valores. El tipo de datos define el formato de almacenamiento, espacio que de disco-memoria que va a ocupar un campo o variable, restricciones y rango de valores validos. Transact SQL proporciona una variedad predefinida de tipos de datos. Casi todos los tipos de datos manejados por Transact SQL son similares a los soportados por SQL. Tipos de datos numricos. SQL Server dispone de varios tipos de datos numricos. Cuanto mayor sea el nmero que puedan almacenar mayor ser en consecuencia el espacio utilizado para almacenarlo. Como regla general se recomienda usar el tipo de dato mnimo posible. Todos los datos numricos admiten el valor NULL. Bit. Puede almacenar el rango de valores de 1 a 0. Tinyint. Puede almacenar el rango de valores de 0 a 255. SmallInt. Puede almacenar el rango de valores -32768 a 32767. Int. Puede almacenar el rango de valores -231 a 231-1. BigInt. Puede almacenar el rango de valores -263 a 263-1. Decimal(p,s). Una columna de tipo decimal puede almacenar datos numricos decimales sin redondear. Donde p es la precisin (nmero total del dgitos) y s la escala (nmero de valores decimales) Flota[(n)]. Una columna de datos float puede almacenar el rango de valores -1,79x-10308 a 1,79x-10308, si la definimos con el valor mximo de precisin.
Donde n es el nmero de bits que se utilizan para almacenar la mantisa del nmero float en notacin cientfica y, por tanto, dicta su precisin y el tamao de almacenamiento. Si se especifica n, debe ser un valor entre 1 y 53. El valor predeterminado de n es 53. n value Precisin Tamao de almacenamiento 1-24 7 dgitos 4 bytes 25-53 15 dgitos 8 bytes SQL Server 2005 trata n como uno de dos valores posibles. Si 1<=n<=24, n se trata como 24. Si 25<=n<=53, n se trata como 53.
Real. Sinnimo de float(24). Puede almacenar el rango de valores -3,4x-1038 a 3,4x-1038, Money - SmallMoney: Almacena valores numricos monetarios, con una precisin de hasta diez milsimas de la unidad monetaria.
Tipo de datos
Money
smallmoney
Notas Los tipos de datos Money y smallmoney tienen una precisin de una diezmilsima de las unidades monetarias que representan.
Todos los tipos de datos enteros pueden marcarse con la propiedad identity para hacerlos autonumricos.
DECLARE @bit bit, @tinyint tinyint, @smallint smallint, @int int, @bigint bigint, @decimal decimal(10,3), -- 10 digitos, 7 enteros y -- 3 decimales @real real, @double float(53), @money money
4
set @bit = 1 print @bit set @tinyint = 255 print @tinyint set @smallint = 32767 print @smallint set @int = 642325 print @int set @decimal = 56565.234 -- Punto como separador decimal print @decimal set @money = 12.34 print @money
Tipos de datos de carcter. Char(n). Almacena n caracteres en formato ASCII, un byte por cada letra. Cuando almacenamos datos en el tipo char, siempre se utilizan los n caracteres indicados, incluso si la entrada de datos es inferior. Por ejemplo, si en un char(5), guardamos el valor 'A', se almacena 'A ', ocupando los cinco bytes. Varchar(n).Almacena n caracteres en formato ASCII, un byte por cada letra. Cuando almacenamos datos en el tipo varchar, nicamente se utilizan los caracteres necesarios. Por ejemplo, si en un varchar(255), guardamos el valor 'A', se almacena 'A', ocupando solo un byte bytes. Varchar(max). Igual que varchar, pero al declararse como max puede almacenar 231-1 bytes. Nchar(n).Almacena n caracteres en formato UNICODE, dos bytes por cada letra. Es recomendable utilizar este tipo de datos cuando los valores que vayamos a almacenar puedan pertenecer a diferentes idiomas. Nvarchar(n).Almacena n caracteres en formato UNICODE, dos bytes por cada letra. Es recomendable utilizar este tipo de datos cuando los valores que vayamos a almacenar puedan pertenecer a diferentes idiomas. Nvarchar(max).Igual que varchar, pero al declararse como max puede almacenar 231-1 bytes.
Tipos de datos de fecha. Datetime. Almacena fechas con una precisin de milisegundo. Debe usarse para fechas muy especificas. SmallDatetime. Almacena fechas con una precisin de minuto, por lo que ocupa la mitad de espacio de que el tipo datetime, para tablas que puedan llegar a tener muchos datos es un factor a tener muy en cuenta. TimeStamp.Se utiliza para marcar un registro con la fecha de insercin actualizacin. El tipo timestamp se actualiza automticamente cada vez que insertamos o modificamos los datos. Tipos de datos binarios. Binary. Se utiliza para almacenar datos binarios de longitud fija, con una longitud mxima de 8000 bytes. Varbinary. Se utiliza para almacenar datos binarios de longitud variable, con una longitud mxima de 8000 bytes... Es muy similar a binary, salvo que varbinary utiliza menos espacio en disco. Varbinary(max).Igual que varbinary, pero puede almacenar 231-1 bytes Tipos de datos personalizados. Transact SQL permite la creacin de tipos de datos personalizados, a travs de la instruccin CREATE TYPE. Personalmente, desaconsejo el uso de tipos de datos personalizados
CREATE TYPE MD5 FROM CHAR(32) NULL GO DECLARE @miMD5 MD5 set @miMD5 = '0000000000000000000000000000000A' print @miMD5
Declarar variables es Transact SQL Una variable es un valor identificado por un nombre (identificador) sobre el que podemos realizar modificaciones. En Transact SQL los identificadores de variables deben comenzar por el carcter @, es decir, el nombre de una variable debe comenzar por @. Para declarar variables en Transact SQL debemos utilizar la palabra clave declare, seguido del identificador y tipo de datos de la variable. declare @nombre varchar(50)-- declare declara una variable -- @nombre es el identificador de la -- variable de tipo varchar set @nombre = 'www.devjoker.com' -- El signo = es un operador -- www.devjoker.com es un literal print @Nombre -- Imprime por pantalla el valor de @nombre. -- No diferencia maysculas ni minsculas
PRINT @nombre El siguiente ejemplo muestra como asignar variables utilizando una sentencia SELECT.
DECLARE @nombre VARCHAR(100), @apellido VARCHAR(100), @sueldo numeric(7,2) SELECT @nombre=nombre , @apellido=Apellido, @sueldo=sueldo FROM empleados WHERE nemp = 1 PRINT @nombre PRINT @apellido PRINT cast(@sueldo as varchar(20)) + '' select * from empleados where nemp = 1
Un punto a tener en cuenta cuando asignamos variables de este modo, es que si la consulta SELECT devuelve ms de un registro, las variables quedarn asignadas con los valores de la ltima fila devuelta. Por ltimo veamos como asignar variables a travs de un cursor.
DECLARE @nombre VARCHAR(100), @apellido VARCHAR(100), @sueldo numeric(7,2) DECLARE CDATOS CURSOR FOR SELECT nombre , Apellido, sueldo FROM empleados OPEN CDATOS FETCH CDATOS INTO @nombre, @apellido, @sueldo
7
WHILE (@@FETCH_STATUS = 0) BEGIN PRINT @nombre PRINT @apellido PRINT @sueldo FETCH CDATOS INTO @nombre, @apellido, @sueldo END CLOSE CDATOS DEALLOCATE CDATOS
Operadores lgicos
Operador de concatenacin
Otros
Estructura condicional IF
La estructura condicional IF permite evaluar una expresin booleana (resultado SI - NO), y ejecutar las operaciones contenidas en el bloque formado por BEGIN END.
IF (<expresion>) BEGIN ... END ELSE IF (<expresion>) BEGIN ... END ELSE BEGIN ... END
10
ELSE <valor_devuelto> -- Valor por defecto END
Ejemplo de CASE.
DECLARE @Web varchar(100), @diminutivo varchar(3) SET @diminutivo = 'DJK' SET @Web = (CASE @diminutivo WHEN 'DJK' THEN 'www.devjoker.com' WHEN 'ALM' THEN 'www.aleamedia.com' ELSE 'www.defecto.com' END) PRINT @Web
Bucle WHILE
El bucle WHILE se repite mientras expresion se evalue como verdadero, nico tipo de bucle de Transact.
WHILE <expresion> BEGIN ... END
11
12
WHERE nRecibo = @nRecibo END
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, recomendndose el uso de TRY - CATCH para la gestin de errores.
DECLARE @divisor int, @dividendo int, @resultado int SET @dividendo = 100 SET @divisor = 0 SET @resultado = @dividendo/@divisor IF @@ERROR > 0 GOTO error PRINT 'No hay error' RETURN error: PRINT 'Se ha producido una division por cero'
13
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.
14
En versiones anteriores a SQL Server 2005, no estaban disponibles las instrucciones TRY CATCH. En estas versiones se controlaban los errores utilizando la variable global de sistema @@ERROR, que almacena el nmero de error producido por la ltima sentencia Transact SQL ejecutada.
DECLARE @divisor int , @dividendo int , @resultado int SET @dividendo = 100 SET @divisor = 0 -- Esta lnea 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:
DECLARE @divisor int , @dividendo int , @resultado int SET @dividendo = 100 SET @divisor = 0 -- Esta lnea provoca un error de division por 0 SET @resultado = @dividendo/@divisor PRINT 'Controlando el error ...' -- Esta lnea estable @@ERROR a cero IF @@ERROR = 0 BEGIN -- Se ejecuta esta parte! PRINT 'No hay error' END ELSE BEGIN PRINT 'Hay error' END
15
En ocasiones es necesario provocar voluntariamente un error, por ejemplo nos puede interesas que se genere un error cuando los datos incumplen una regla de negocio. Podemos provocar un error en tiempo de ejecucin a travs de la funcin RAISERROR.
DECLARE @tipo int, @clasificacion int SET @tipo = 1 SET @clasificacion = 3 IF (@tipo = 1 AND @clasificacion = 3) BEGIN RAISERROR ('El tipo no puede valer uno y la clasificacion 3', 16, -- Severidad 1 -- Estado ) END
La funcin RAISERROR recibe tres parmetros, el mensaje del error (o cdigo de error predefinido), la severidad y el estado. La severidad indica el grado de criticidad del error. Admite valores de 0 al 25, pero solo podemos asignar valores del 0 al 18. Los errores el 20 al 25 son considerados fatales por el sistema, y cerraran la conexin que ejecuta el comando RAISERROR. Para asignar valores del 19 al 25 se necesitaria ser miembros de la funcin de SQL Server sysadmin.
16
--Activamos el modo de transacciones implicitas SET IMPLICIT_TRANSACTIONS ON --Desactivamos el modo de transacciones implicitas SET IMPLICIT_TRANSACTIONS OFF
Cuando la opcin ANSI_DEFAULTS est establecida en ON, IMPLICIT_TRANSACTIONS tambin se establece en ON. El siguiente ejemplo muestra el script anterior haciendo uso de transacciones explicitas.
SET IMPLICIT_TRANSACTIONS ON DECLARE @importe DECIMAL(18,2), @CuentaOrigen VARCHAR(12), @CuentaDestino VARCHAR(12) /* Asignamos el importe de la transferencia * y las cuentas de origen y destino */ SET @importe = 50 SET @CuentaOrigen = '200700000002' SET @CuentaDestino = '200700000001' BEGIN TRY /* Descontamos el importe de la cuenta origen */ 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 /* Incrementamos el importe de la cuenta destino */ UPDATE CUENTAS SET SALDO = SALDO + @importe WHERE NUMCUENTA = @CuentaDestino /* 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 /* 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
El siguiente ejemplo muestra el mismo script con transacciones implcitas. La transaccin sigue activa hasta que emita una instruccin COMMIT o ROLLBACK. Una vez que la primera transaccin se ha confirmado o revertido, se inicia automticamente una nueva transaccin la siguiente vez que la conexin ejecuta una instruccin para modificar datos. La conexin contina generando transacciones implcitas hasta que se desactiva el modo de transacciones implcitas.
17
Cuando es ON, SET IMPLICIT_TRANSACTIONS establece la conexin al modo de transaccin implcita. Cuando es OFF, restablece la conexin al modo de transaccin con confirmacin automtica. Cuando una conexin est en modo de transaccin implcita y actualmente no est realizando una transaccin, la ejecucin de cualquiera de las instrucciones siguientes inicia una transaccin: ALTER TABLE FETCH REVOKE CREATE GRANT SELECT DELETE INSERT TRUNCATE TABLE DROP OPEN UPDATE Si la conexin tiene ya una transaccin abierta, estas instrucciones no inician una nueva transaccin. Al trmino de las transacciones que se abran automticamente por ser esta opcin ON, el usuario debe confirmarlas o revertirlas explcitamente. En caso contrario, las transacciones y todos los cambios que se realicen en los datos se revertirn cuando el usuario se desconecte. Una vez confirmada una transaccin, la ejecucin de alguna de las instrucciones anteriores iniciar una transaccin nueva. El modo de transaccin implcita permanecer activo hasta que la conexin ejecute una instruccin SET IMPLICIT_TRANSACTIONS OFF, que restablece el modo de confirmacin automtica en la conexin. En el modo de confirmacin automtica, todas las instrucciones individuales se confirman cuando finalizan correctamente. El proveedor OLE DB de SQL Native Client para SQL Server y el controlador ODBC de SQL Native Client establecen automticamente IMPLICIT_TRANSACTIONS en OFF al conectarse. SET IMPLICIT_TRANSACTIONS tiene el valor predeterminado OFF para conexiones con el proveedor administrado SQLClient y para solicitudes SOAP recibidas a travs de extremos HTTP. Cuando SET ANSI_DEFAULTS est establecido en ON, SET IMPLICIT_TRANSACTIONS tambin se establece en ON. La opcin SET IMPLICIT_TRANSACTIONS se establece en tiempo de ejecucin, no en tiempo de anlisis. Ejemplo Transacciones implicitas
SET NOCOUNT ON; --GO SET IMPLICIT_TRANSACTIONS ON; --GO PRINT N'Tran count at start = ' + CAST(@@TRANCOUNT AS NVARCHAR(10)); --Tran count at start = 0 GO IF OBJECT_ID(N'dbo.t1',N'U') IS NOT NULL DROP TABLE dbo.t1; GO CREATE table dbo.t1 (a int); GO INSERT INTO dbo.t1 VALUES (1); GO PRINT 'Tran count despues de create e insert = ' + CAST(@@TRANCOUNT AS NVARCHAR(10)) COMMIT PRINT 'Tran count despues de COMMIT = ' + CAST(@@TRANCOUNT AS NVARCHAR(10)) SELECT * FROM T1 INSERT INTO dbo.t1 VALUES (1); GO PRINT 'Tran count despues de 2 insert = ' + CAST(@@TRANCOUNT AS NVARCHAR(10)) ROLLBACK PRINT 'Tran count despues de ROLLBACK = ' SELECT * FROM T1
18
+ CAST(@@TRANCOUNT AS NVARCHAR(10))
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 desharn 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 -- Este COMMIT solo afecta a la segunda transaccion. COMMIT -- Este ROLLBACK afecta a las dos transacciones. ROLLBACK
Una consideracin a tener en cuanta cuando trabajamos con transacciones anidadas es la posibilidad de utilizar puntos de guardado o SAVEPOINTs.
19
20
Los puntos de recuperacin (SavePoints) permiten manejar las transacciones por pasos, pudiendo hacer rollbacks hasta un punto marcado por el savepoint y no por toda la transaccin. El siguiente ejemplo muestra como trabajar con puntos de recuperacin.
BEGIN TRAN UPDATE EMPLEADOS SET NOMBRE = 'Devjoker' WHERE ID=101 UPDATE EMPLEADOS SET APELLIDO1 = 'Devjoker.COM' WHERE ID=101 SAVE TRANSACTION P1 -- Guardamos la transaccion (Savepoint) UPDATE EMPLEADOS SET APELLIDO1 = 'Otra cosa!' WHERE ID=101 -- Este ROLLBACK afecta solo a las instrucciones posteriores al savepoint P1. ROLLBACK TRANSACTION P1 -- Confirmamos la transaccion COMMIT select * from recibos begin transaction insert into recibos values( 60, 's') --insecion 1 SAVE TRANSACTION P1 insert into recibos values( 70, 's')--insercion 2 rollback transaction p1 -- deshace la insercion 2 rollback --deshace la insecin 1 y 2
Un usuario puede establecer un punto de almacenamiento, o marcador, dentro de una transaccin. El punto de almacenamiento define una ubicacin a la que la transaccin puede volver si se cancela parte de la transaccin de forma condicional. Si se revierte una transaccin hasta un punto de almacenamiento, se debe continuar hasta su finalizacin con ms instrucciones Transact-SQL si es necesario y una instruccin COMMIT TRANSACTION o se debe cancelar completamente al revertir la transaccin hasta su inicio. Para cancelar una transaccin completa, utilice el formato ROLLBACK TRANSACTION transaction_name. Se deshacen todas las instrucciones o procedimientos de la transaccin. En una transaccin se permiten nombres de puntos de almacenamiento duplicados, pero una instruccin ROLLBACK TRANSACTION que especifique el nombre de un punto de
almacenamiento slo revertir la transaccin hasta la instruccin SAVE TRANSACTION ms reciente que tambin utilice ese nombre.
21
22
No se admite SAVE TRANSACTION en transacciones distribuidas iniciadas explcitamente con BEGIN DISTRIBUTED TRANSACTION u originadas a partir de una transaccin local. Importante: Cuando una transaccin comienza, los recursos utilizados durante la transaccin se mantienen hasta la finalizacin de la misma (es decir, los bloqueos). Cuando se revierte parte de una transaccin hasta un punto de almacenamiento, se mantienen los recursos hasta la finalizacin de la transaccin o hasta revertir la transaccin completa. savepoint_name Es el nombre asignado al punto de almacenamiento. Los nombres de los puntos de almacenamiento deben ajustarse a las reglas para los identificadores, aunque estn limitados a 32 caracteres. @savepoint_variable Es el nombre de una variable definida por el usuario que contiene un nombre de punto de almacenamiento vlido. La variable debe declararse con un tipo de datos char, varchar, nchar o nvarchar. Es posible pasar ms de 32 caracteres a la variable, aunque slo se utilizarn los primeros 32.
Argumentos
23
Un procedimiento es un programa dentro de la base de datos que ejecuta una accin o conjunto de acciones especficas. Un procedimiento tiene un nombre, un conjunto de parmetros (opcional) y un bloque de cdigo. En Transact SQL los procedimientos almacenados pueden devolver valores (numrico entero) o conjuntos de resultados. Para crear un procedimiento almacenado debemos emplear la sentencia CREATE PROCEDURE.
CREATE PROCEDURE <nombre_procedure> [@param1 <tipo>, ...] AS -- Sentencias del procedure
insertaEmpleado
El siguiente ejemplo muestra un procedimiento almacenado, denominado que inserta un registro en la tabla "empleados".
CREATE PROCEDURE insertaEmpleado @nemp int, @nombre varchar(10), @apellido varchar(10), @sueldo numeric(6,2), @ventas numeric(6,2)= 0, @fechaAlta datetime, @nDep int AS BEGIN INSERT INTO empleados (nEmp,nombre, apellido, fe_alta, sueldo, ventas, ndep) VALUES (@nemp,@nombre, @apellido, @fechaAlta,@sueldo, @ventas, @nDep) END
Para la ejecutar un procedimiento almacenado debemos utilizar la sentencia EXEC. Cuando la ejecucin del procedimiento almacenado es la primera instruccin del lote, podemos omitir el uso de EXEC. El siguiente ejemplo muestra la ejecucin del procedimiento almacenado anterior.
DECLARE @fechaAlta datetime set @fechaAlta = convert(datetime, '04/05/2010', 103) EXEC insertaEmpleado 40,'Pedro', 'Herrarte', 1200.00, 2340, @fechaAlta, 1
Siempre es deseable que las instrucciones del procedimiento estn dentro de un bloque TRY CATCH y controlados por una transaccin.
ALTER PROCEDURE insertaEmpleado @nemp int, @nombre varchar(10), @apellido varchar(10), @sueldo numeric(6,2), @ventas numeric(6,2)= 0, @fechaAlta datetime, @nDep int AS BEGIN
24
BEGIN TRY BEGIN TRAN INSERT INTO empleados (nEmp,nombre, apellido, fe_alta, sueldo, ventas, ndep) VALUES (@nemp,@nombre, @apellido, @fechaAlta,@sueldo, @ventas, @nDep) COMMIT END TRY BEGIN CATCH ROLLBACK PRINT ERROR_MESSAGE() END CATCH END
DECLARE @fechaAlta datetime set @fechaAlta = convert(datetime, '04/05/2010', 103) EXEC insertaEmpleado 40,'Pedro', 'Herrarte', 1200.00, 2340, @fechaAlta, 1
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 procedimiento como en la ejecucin. El siguiente ejemplo muestra la definicin de un procedimiento con parmetros de salida.
CREATE PROCEDURE spu_ObtenerSaldoCuenta @numCuenta varchar(20), @saldo decimal(10,2) output AS BEGIN SELECT @saldo = SALDO FROM CUENTAS WHERE NUMCUENTA = @numCuenta END
Un procedimiento almacenado puede devolver valores numricos enteros a travs de la instruccin RETURN. Normalmente debemos utilizar los valores de retorno para determinar si la ejecucin del procedimiento ha sido correcta o no. Si queremos obtener valores se recomienda utilizar parmetros de salida o funciones escalares (se vern mas adelante en este tutorial). El siguiente ejemplo muestra un procedimiento almacenado que devuelve valores.
CREATE PROCEDURE spu_EstaEnNumerosRojos @numCuenta varchar(20) AS BEGIN IF (SELECT SALDO FROM CUENTAS WHERE NUMCUENTA = @numCuenta) < 0 BEGIN RETURN 1 END ELSE RETURN 0 END
25
Otra caracterstica muy interesante de los procedimientos almacenados en Transact SQL es que pueden devolver uno o varios conjuntos de resultados. El siguiente ejemplo muestra un procedimiento almacenado que devuelve un conjunto de resultados.
CREATE PROCEDURE spu_MovimientosCuenta @numCuenta varchar(20) AS BEGIN SELECT @numCuenta, SALDO_ANTERIOR, SALDO_POSTERIOR, IMPORTE, FXMOVIMIENTO FROM MOVIMIENTOS INNER JOIN CUENTAS ON MOVIMIENTOS.IDCUENTA = CUENTAS.IDCUENTA WHERE NUMCUENTA = @numCuenta ORDER BY FXMOVIMIENTO DESC END
Funciones escalares
26
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: CREATE FUNCTION <FunctionName> ( -- Lista de parmetros <@Param1, @p1> <Data_Type_For_Param1, , int>, ... ) -- Tipo de datos que devuelve la funcin. RETURNS <Function_Data_Type, ,int> AS BEGIN ... END
Pueden ser utilizadas en cualquier sentencia Transact SQL. Un aspecto a tener en cuenta, es que para utilizar una funcin escalar debemos identificar el nombre de la funcin con el propietario de la misma. El siguiente ejemplo muestra como utilizar la funcin anteriormente creada en una sentencia Transact SQL. Un aspecto muy a tener en cuenta es que la funcin ejecutar sus sentencias SELECT una vez por cada fila del conjunto de resultados devuelto por la consulta SELECT principal.
SELECT IDCUENTA, NUMCUENTA, SALDO, FXALTA, -- Ejecucin de la funcin: dbo.fn_MultiplicaSaldo( NUMCUENTA, IDCUENTA) AS RESULTADO FROM CUENTAS
El siguiente ejemplo muestra como utilizar una funcin escalar en un script Transact SQL.
DECLARE @NumCuenta VARCHAR(20), @Resultado DECIMAL(10,2) SET @NumCuenta = '200700000001' SET @Resultado = dbo.fn_MultiplicaSaldo(@NumCuenta, 30.5)
27
PRINT @Resultado
Las funciones escalares son muy similares a procedimientos almacenados con parmetros de salida, pero estas pueden ser utilizadas en consultas de seleccin y en la clusula where de las mismas. Las funciones no pueden ejecutar sentencias INSERT o UPDATE.
Funciones en lnea
Las funciones en lnea son las funciones que devuelven un conjunto de resultados correspondientes a la ejecucin de una sentencia SELECT. La sintaxis para una funcin de tabla en lnea es la siguiente:
CREATE FUNCTION <Inline_Function_Name, sysname, FunctionName> ( -- Lista de parmetros <@param1, sysname, @p1> <Data_Type_For_Param1, , int>,... ) RETURNS TABLE AS RETURN ( -- Sentencia Transact SQL )
28
No podemos utilizar la clusula ORDER BY en la sentencia de una funcin el lnea. Las funciones en lnea pueden utilizarse dentro de joins o querys como si fueran una tabla normal.
SELECT * FROM fn_MovimientosCuenta('200700000001') SELECT * FROM CUENTAS INNER JOIN CUENTAS_CLIENTE ON CUENTAS_CLIENTE.IDCUENTA = CUENTAS.IDCUENTA INNER JOIN CLIENTES ON CLIENTES.id = CUENTAS_CLIENTE.IDCLIENTE INNER JOIN fn_MovimientosCuenta('200700000001') A ON A.IDCUENTA= CUENTAS.IDCUENTA
29
RETURNS -- variable de tipo tabla y su estructura <@Table_Variable_Name, sysname, @Table_Var> TABLE ( <Column_1, sysname, c1> <Data_Type_For_Column1, , int>, <Column_2, sysname, c2> <Data_Type_For_Column2, , int> ) AS BEGIN -- Sentencias que cargan de datos la tabla declarada RETURN END
30
ORDER BY FXMOVIMIENTO DESC -- Vamos a la siguiente cuenta FETCH CDATOS INTO @idcuenta, @numcuenta, @saldo END CLOSE CDATOS; DEALLOCATE CDATOS; RETURN END
Cast y Convert
Convierten una expresin de un tipo de datos en otro de forma explcita. CAST y CONVERT proporcionan funciones similares. CONVERT ( data_type [ ( length ) ] , expression [ , style ] ) Donde: data_type, es el tipo de destino al que queremos convertir la expresin expresin, la expresin que queremos convertir style, parmetro opcional que especifica el formato que tiene expresin. Por ejemplo, si queremos convertir un varchar a datetime, aqu debemos especificar el formato de la fecha (el tipo varchar).
DECLARE @fecha varchar(20) -- Convertimos un valor varchar a datetime -- El 103 indica el formato en el que esta escrita la fecha -- 103 => dd/mm/aa SET @fecha = CONVERT(datetime, '19/03/2008',103) SELECT @fecha DECLARE @fecha datetime, @fechaFormateada varchar(20) -- Convertimos ahora una fecha a varchar y la formateamos -- 3 => dd/mm/aa SET @fecha = GETDATE() SET @fechaFormateada = CONVERT(varchar(20), @fecha, 3) SELECT @fechaFormateada -- Un ejemplo utilizando CAST DECLARE @dato varchar(2),
31
@dato2 int SET @dato = '27' SET @dato2 = cast(@dato AS int) SELECT @dato2
A continuacin mostramos la tabla de cdigos MicroSoft). Sin el Con el siglo (aa) siglo ? Estndar 1 ( ) (aaaa) 0 o 100 (1, Valor predeterminado 2 ) 1 101 EE.UU. 2 102 ANSI 3 103 Britnico/Francs 4 104 Alemn 5 105 Italiano (1) 6 106 (1) 7 107 8 108 10 11 12 14 -
mes dd aaaa hh:mia.m. (o p. m.) mm/dd/aaaa aa.mm.dd dd/mm/aa dd.mm.aa dd-mm-aa dd mes aa Mes dd, aa hh:mi:ss mes dd aaaa 9 o 109 (1, Valor predeterminado + hh:mi:ss:mmma.m. (o p. 2 ) milisegundos m.) 110 EE.UU. mm-dd-aa 111 JAPN aa/mm/dd 112 ISO aammdd Europeo 13 o 113 (1, dd mes aaaa predeterminado + 2 ) hh:mi:ss:mmm(24h) milisegundos 114 hh:mi:ss:mmm(24h) aaaa-mm-dd 20 o 120 (2) ODBC cannico hh:mi:ss(24h) ODBC cannico (con aaaa-mm-dd 21 o 121 (2) milisegundos) hh:mi:ss.mmm(24h) aaaa-mm126 (4) ISO8601 ddThh:mi:ss.mmm (sin espacios) aaaa-mmISO8601 con zona 127(6, 7) ddThh:mi:ss.mmmZ horaria Z. (sin espacios) dd mes aaaa 130 (1, 2) Hijri (5) hh:mi:ss:mmma.m. dd/mm/aa 131 (2) Hijri (5) hh:mi:ss:mmma.m.
Isnull
Evala una expresin 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 expresin a evaluar. ISNULL ( expression , replacement_value )
DECLARE @datoInt int,
32
@datoVarchar varchar(100) SET @datoInt = NULL SET @datoVarchar = NULL 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 ] )
DECLARE @dato1 int, @dato2 int, @dato3 int, @dato4 int, @dato5 int SET SET SET SET SET @dato1 @dato2 @dato3 @dato4 @dato5 = = = = = null NULL NULL 100 125
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.
DECLARE @fechaLocal datetime, @fechaUTC datetime SET @fechaLocal = getdate() SET @fechaUTC = GETUTCDATE() SELECT @fechaLocal, @fechaUTC
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.
33
DECLARE @sql nvarchar(1000) SET @sql = 'SELECT COD_PAIS, NOMBRE_PAIS, ACTIVO, FX_ALTA FROM PAISES' EXEC (@sql)
Tambin con SQL dinmico podemos ejecutar sentencias de tipo DDL (Data Definition Languaje), como CREATE TABLE.
DECLARE @sql nvarchar(1000) SET @sql='CREATE TABLE TEMPORAL ( ID int IDENTITY, DATO varchar(100))' EXEC (@sql) SET @sql = 'SELECT * FROM TEMPORAL' EXEC (@sql)
El principal inconveniente de trabajar con la instruccin EXEC es que no permite el uso de parmetros abriendo la puerta a potenciales ataques de Sql Injections - http://www.devjoker.com/contenidos/Articulos/45/Seguridaden-Internet--SQL-Injections.aspx Adems el uso de la instruccin EXEC es menos eficiente, en trminos de rendimiento, que sp_executesql. Para solventar el problema debemos trabajar siempre con sq_executesql, que permite el uso de parmetros y con el que obtendremos un mejor rendimiento de nuestras consultas.
sp_executesql admite la sustitucin de valores de parmetros para cualquier parmetro especificado en la cadena Transact-SQL a ejecutar. El siguiente ejemplo muestra el uso de sp_executesql con parmetros:
DECLARE @sql nvarchar(1000), @paramDefinition nvarchar(255), @paramValue char(3) SET @paramDefinition = '@codPais char(3)'
34
SET @paramValue = 'ESP' SET @sql = 'SELECT COD_PAIS, NOMBRE_PAIS, ACTIVO, FX_ALTA FROM PAISES WHERE COD_PAIS = @codPais' EXEC sp_executesql @sql, @paramDefinition, @paramValue DECLARE @sql nvarchar(1000), @paramDefinition nvarchar(255), @paramValue int SET @paramDefinition = '@ndep int' SET @paramValue = 1 SET @sql = 'SELECT * FROM empleados PAISES WHERE ndep = @ndep' EXEC sp_executesql @sql, @paramDefinition, @paramValue
Para ejecutar una cadena, se recomienda utilizar el procedimiento almacenado sp_executesql, en lugar de una instruccin EXECUTE. Puesto que este procedimiento almacenado admite la sustitucin de parmetros, sp_executesql es ms verstil que EXECUTE; y como sp_executesql genera planes de ejecucin con ms probabilidades de que SQL Server 2005 los vuelva a utilizar, es ms eficaz que EXECUTE. Lotes independientes Cuando sp_executesql o la instruccin EXECUTE ejecutan una cadena, sta se ejecuta como su propio lote independiente. SQL Server compila la instruccin o instrucciones Transact-SQL de la cadena en un plan de ejecucin independiente del plan de ejecucin del lote que contena sp_executesql o la instruccin EXECUTE. Para los lotes independientes se aplican las siguientes reglas: Las instrucciones Transact-SQL de la cadena EXECUTE o sp_executesql no se compilan en un plan de ejecucin hasta que la instruccin EXECUTE o sp_executesql se han ejecutado. Las cadenas no se analizan ni se comprueba que tengan errores hasta que se han ejecutado. Los nombres a los que se hace referencia en las cadenas no se resuelven hasta que se han ejecutado. Las instrucciones Transact-SQL de la cadena ejecutada no tienen acceso a ninguna de las variables declaradas en el lote que contiene la instruccin EXECUTE o sp_executesql. El lote que contiene la instruccin EXECUTE o sp_executesql no tiene acceso a las variables o cursores locales definidos en la cadena ejecutada. Si la cadena ejecutada contiene una instruccin USE que cambia el contexto de la base de datos, este cambio slo dura hasta que finaliza la ejecucin de sp_executesql o la instruccin EXECUTE. La ejecucin de los dos lotes siguientes muestra estos puntos:
Copiar
/*Show not having access to variables from the calling batch. */ DECLARE @CharVariable CHAR(3); SET @CharVariable = 'abc'; /* sp_executesql fails because @CharVariable has gone out of scope. */ EXECUTE sp_executesql N'PRINT @CharVariable'; GO /* Show database context resetting after sp_executesql finishes. */ USE master; GO EXECUTE sp_executesql N'USE AdventureWorks;' GO /* This statement fails because the database context has now returned to master. */ SELECT * FROM Sales.Store; GO
35
Sustituir valores de parmetros sp_executesql admite la sustitucin de valores de parmetros para cualquier parmetro especificado en la cadena Transact-SQL, pero la instruccin EXECUTE no lo hace. Por tanto, las cadenas Transact-SQL generadas por sp_executesql son ms similares que las generadas por la instruccin EXECUTE. El optimizador de consultas de SQL Server probablemente compare las instrucciones Transact-SQL de sp_executesql con los planes de ejecucin de las instrucciones ejecutadas previamente, lo que ahorra la sobrecarga de trabajo que supone compilar un nuevo plan de ejecucin. En la instruccin EXECUTE, todos los valores de los parmetros se deben convertir a caracteres o datos Unicode y formar parte de la cadena TransactSQL. Si la instruccin se ejecuta varias veces, debe generarse una cadena Transact-SQL completamente nueva para cada ejecucin, incluso cuando las nicas diferencias se encuentren en los valores suministrados para los parmetros. Esto genera una sobrecarga adicional de las formas siguientes: La capacidad del optimizador de consultas de SQL Server para comparar la nueva cadena Transact-SQL con un plan de ejecucin existente est obstaculizada por el cambio constante de los valores de los parmetros del texto de la cadena, especialmente en las instrucciones Transact-SQL complejas. La cadena completa se debe volver a generar en cada ejecucin. Los valores de los parmetros (que no sean de caracteres ni Unicode) se deben convertir a un formato de caracteres o Unicode en cada ejecucin. sp_executesql admite que se establezcan valores en los parmetros, independientemente de la cadena Transact-SQL: Copiar
DECLARE @IntVariable INT; DECLARE @SQLString NVARCHAR(500); DECLARE @ParmDefinition NVARCHAR(500); /* Build the SQL string one time. */ SET @SQLString = N'SELECT * FROM AdventureWorks.Sales.Store WHERE SalesPersonID = @SalesID'; /* Specify the parameter format one time. */ SET @ParmDefinition = N'@SalesID int'; /* Execute the string with the first parameter value. */ SET @IntVariable = 275;
EXECUTE sp_executesql @SQLString, @ParmDefinition, @SalesID = @IntVariable; /* Execute the same string with the second parameter value. */ SET @IntVariable = 276; EXECUTE sp_executesql @SQLString, @ParmDefinition, @SalesID = @IntVariable;
36
sp_executesql ofrece las siguientes ventajas adicionales: Como el texto real de la instruccin Transact-SQL no cambia entre una ejecucin y otra, el optimizador de consultas debe comparar la instruccin Transact-SQL de la segunda ejecucin con el plan de ejecucin generado para la primera instruccin. De este modo, SQL Server no tiene que compilar la segunda instruccin. La cadena Transact-SQL slo se genera una vez. El archivo de datos tiene formato nativo. No es necesaria la conversin a Unicode. Nota: Los nombres de objeto de la cadena de instrucciones deben aparecer completos para que SQL Server pueda volver a utilizar el plan de ejecucin. Volver a utilizar planes de ejecucin En las versiones anteriores de SQL Server, la nica forma de volver a utilizar los planes de ejecucin era definir las instrucciones Transact-SQL como un procedimiento almacenado y hacer que la aplicacin lo ejecutara. Esto genera una sobrecarga administrativa adicional para las aplicaciones. La utilizacin de sp_executesql puede contribuir a reducir esta sobrecarga, al tiempo que permite que SQL Server vuelva a utilizar planes de ejecucin. Se puede utilizar sp_executesql en lugar de los procedimientos almacenados cuando se ejecute una instruccin Transact-SQL varias veces y la nica variacin la constituyan los valores de los parmetros suministrados a la instruccin Transact-SQL. Al permanecer constantes las instrucciones Transact-SQL y variar slo los valores de los parmetros, es probable que el optimizador de consultas de SQL Server vuelva a utilizar el plan de ejecucin que genera para la primera ejecucin. En el siguiente ejemplo se genera y ejecuta una instruccin DBCC CHECKDB para cada base de datos de un servidor, excepto para las cuatro bases de datos del sistema. Copiar
USE master; GO SET NOCOUNT ON; GO DECLARE AllDatabases CURSOR FOR SELECT name FROM sys.databases WHERE database_id > 4 OPEN AllDatabases DECLARE @DBNameVar NVARCHAR(128) DECLARE @Statement NVARCHAR(300) FETCH NEXT FROM AllDatabases INTO @DBNameVar WHILE (@@FETCH_STATUS = 0) BEGIN PRINT N'CHECKING DATABASE ' + @DBNameVar SET @Statement = N'USE ' + @DBNameVar + CHAR(13) + N'DBCC CHECKDB (' + @DBNameVar + N')' + N'WITH PHYSICAL_ONLY' EXEC sp_executesql @Statement PRINT CHAR(13) + CHAR(13) FETCH NEXT FROM AllDatabases INTO @DBNameVar
37
El controlador ODBC de SQL Server utiliza sp_executesql para implementar SQLExecDirect cuando la instruccin Transact-SQL que se est ejecutando contiene marcadores de parmetros enlazados. Esto permite ampliar las ventajas proporcionadas por sp_executesql a todas las aplicaciones que utilicen ODBC o API definidas sobre ODBC, como RDO. Las aplicaciones existentes de ODBC que se transfieren a SQL Server 2005 aumentan automticamente su rendimiento sin que tengan que ser escritas de nuevo. La nica excepcin es que sp_executesql no se usa con parmetros de datos de la ejecucin. Para obtener ms informacin, vea Using Statement Parameters. El proveedor ODBC de SQL Native Client tambin utiliza sp_executesql para implementar la ejecucin directa de instrucciones con parmetros enlazados. Las aplicaciones que usan OLE DB o ADO aprovechan las ventajas proporcionadas por sp_executesql sin que se tengan que escribir de nuevo.
Apndices.
Equivalencia de datos de SQL Server y .NET
La siguiente lista muestra los tipos de datos de SQL Server 2005 y sus equivalentes con CRL, para el namespace System.Data.SqlTypes y los tipos nativos de CRL .NET FrameWork CLR data type SQL Server CLR data type (SQL Server) (.NET Framework) varbinary SqlBytes, SqlBinary Byte[] binary SqlBytes, SqlBinary Byte[] varbinary(1), SqlBytes, SqlBinary byte, Byte[] binary(1) image ninguno ninguno varchar ninguno ninguno char ninguno ninguno nvarchar(1), Char, String, SqlChars, SqlString nchar(1) Char[] SqlChars, SqlString SQLChars es mejor para la nvarchar transferencia de datos y SQLString String, Char[] obtiene mejor rendimiento para operaciones con Strings.
38 nchar SqlChars, SqlString text ninguno ntext ninguno uniqueidenti SqlGuid fier rowversion ninguno bit SqlBoolean tinyint SqlByte smallint SqlInt16 int SqlInt32 bigint SqlInt64 smallmoney SqlMoney money SqlMoney numeric SqlDecimal decimal SqlDecimal real SqlSingle float SqlDouble smalldatetim SqlDateTime e datetime SqlDateTime sql_variant ninguno User-defined ninguno type(UDT) table cursor timestamp xml ninguno ninguno ninguno SqlXml String, Char[] ninguno ninguno Guid Byte[] Boolean Byte Int16 Int32 Int64 Decimal Decimal Decimal Decimal Single Double DateTime DateTime Object Misma clase que la definida en el assemblie. ninguno ninguno ninguno ninguno