Buscar este blog

lunes, 18 de julio de 2011

Control de errores en Transact SQL

Control de errores en Transact SQL

Uso de TRY CATCH

A partIr de la versión 2005, SQL Server proporciona el control de errores a través de las instrucciónes TRY y CATCH.

Estas nuevas instrucciones suponen un gran paso adelante en el control de errores en SQL Server, un tanto precario en las versiones anteriores.

La sintaxis de TRY CATCH es la siguiente:

    
BEGIN TRY
...
END TRY
BEGIN CATCH
...
END CATCH

El siguiente ejemplo ilustra el uso de TRY - CATCH.


BEGIN TRY

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 'No hay error'
END TRY
BEGIN CATCH
PRINT 'Se ha producido un error'
END CATCH
 

Funciones especiales de Error

Las funciones especiales de error,están disponibles únicamente en el bloque CATCH para la obtención de información detallada del error.

Son:

  • ERROR_NUMBER(), devuelve el número de error.
  • ERROR_SEVERITY(), devuelve la severidad del error.
  • ERROR_STATE(), devuelve el estado del error.
  • ERROR_PROCEDURE(), devuelve el nombre del procedimiento almacenado que ha provocado el error.
  • ERROR_LINE(), devuelve el número de línea en el que se ha producido el error.
  • ERROR_MESSAGE(), devuelve el mensaje de error.

Son extremadamente útiles para realizar una auditoría de errores.


BEGIN TRY

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 '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

Lógicamente, podemos utilizar estas funciones para almacenar esta información en una tabla de la base de datos y registrar todos los errores que se produzcan.

La variable de sistema @@ERROR

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 número de error producido por la última sentencia Transact SQL ejecutada.


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 más habituales es sin duda, incluir una nueva sentencia Transact SQL entre la línea que provoco el error y la que lo controla. Esa nueva instrucción restaura el valor de @@ERROR y no controlaremos el error.

El siguiente ejemplo ilustra esta situación:


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

Generar un error con RAISERROR

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 ejecución a través de la función 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 función RAISERROR recibe tres parámetros, el mensaje del error (o código 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 conexion que ejecuta el comando RAISERROR. Para asignar valores del 19 al 25 necesitares ser miembros de la función de SQL Server sysadmin.

El estado es un valor para permitir que el programador identifique el mismo error desde diferentes partes del código. Admite valores entre 1 y 127, permite tratar .

Tipos de datos en Transact SQL

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 numéricos.

SQL Server dispone de varios tipos de datos numéricos. Cuanto mayor sea el número que puedan almacenar mayor será en consecuencia el espacio utilizado para almacenarlo. Como regla general se recomienda usar el tipo de dato mínimo posible. Todos los datos numéricos admiten el valor NULL.

Bit. Una columna o variable de tipo bit puede almacenar el rango de valores de 1 a 0.

Tinyint. Una columna o variable de tipo tinyint puede almacenar el rango de valores de 0 a 255.

SmallInt. Una columna o variable de tipo smallint puede almacenar el rango de valores -32768 a 32767.

Int. Una columna o variable de tipo int puede almacenar el rango de valores -231 a 231-1 .

BigInt. Una columna o variable de tipo bigint puede almacenar el rango de valores -263 a 263-1 .

Decimal(p,s). Una columna de tipo decimal puede almacenar datos numéricos decimales sin redondear. Donde p es la precisión (número total del dígitos) y s la escala (número de valores decimales)

Float. Una columna de datos float puede almacenar el rango de valores -1,79x-10308 a 1,79x-10308, , si la definimos con el valor máxmo de precisión. La precisión puede variar entre 1 y 53.

Real. Sinónimo de float(24). Puede almacenar el rango de valores -3,4x-1038 a 3,4x-1038,

Money. Almacena valores númericos monetarios de -263 a 263-1, con una precisión de hasta diez milésimas de la unidad monetaria.

SmallMoney. Almacena valores numéricos monetarios de -214.748,3647 a 214.748,3647, con una precisión de hasta diez milésimas de la unidad monetaria.

Todos los tipos de datos enteros pueden marcarse con la propiedad identity para hacerlos autonuméricos.


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
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 caracter.

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, unicamente 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 diferente idomas.

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 diferente idomas.

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 precision de milisegundo. Debe usarse para fechas muy especificas.

SmallDatetime. Almacena fechas con una precision 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 inserción - actualización. El tipo timestamp se actualiza automáticamente 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 máxima de 8000 bytes.

Varbinary. Se utiliza para almacenar datos binarios de longitud variable, con una longitud máxima 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

Tipo de datos XML.

XML.Una de las grandes mejoras que incorpora SQL Server 2005 es el soporte nativo para XML. Como podemos deducir, este tipo de datos se utiliza para almacenar XML.


DECLARE @myxml XML

set @myxml = (SELECT @@SERVERNAME NOMBRE FOR XML RAW, TYPE)

print cast(@myxml as varchar(max))

Obtendremos la siguiente salida:

Otros tipos de datos.

UniqueIdentifier. Se utiliza para identificadores únicos. Para generar identificadores únicos debemos utilizar la función NEWID().


DECLARE @myuniqueid UNIQUEIDENTIFIER

set @myuniqueid = NEWID()

print cast(@myuniqueid as varchar(36))

Obtendremos la siguiente salida: 46141D79-102C-4C29-A620-792EA0208637

Sql_Variant.Permite almacenar valores de diferentes tipos de datos. No puede almacena varchar(max), xml, timestamp y tipos de datos definidos por el usuario.

Tipos de datos personalizados.

Transact SQL permite la creación de tipos de datos personalizados, a trevés de la instrucción 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

Fundamentos de Transact SQL



Primeros pasos con Transact SQL

Para programar en Transact SQL es necesario conocer sus fundamentos.

Como introducción vamos a ver algunos elementos y conceptos básicos del lenguaje.

  • Transact SQL no es CASE-SENSITIVE, es decir, no diferencia mayúsculas de minúsculas como otros lenguajes de programación como C o Java.
  • Un comentario es una aclaración que el programador incluye en el código. Son soportados 2 estilos de comentarios, el de línea simple y de multilínea, para lo cual son empleados ciertos caracters especiales como son:
    • -- Para un comentario de linea simple
    • /* ... */ Para un comentario de varias lineas
  • Un literal es un valor fijo de tipo numérico, caracter, cadena o lógico no representado por un identificador (es un valor explícito).
  • 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 caracter @, es decir, el nombre de una variable debe comenzar por @.Para dec
  • larar variables en Transact SQL debemos utilizar la palabra clave declare, seguido del identificador y tipo de datos de la variable.
Veamos algunos ejemplos:


Esto es un comentario de línea simple


/*
Este es un comentario con varias líneas.
Conjunto de Líneas.
*/
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 valo
r

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 través de la instrucción 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 función integrada que devuelve
-- la fecha

En ocasiones es necesario separar las sentencias en varios lotes, porque Transact SQL no permite la ejecución de ciertos comandos en el mismo lote, si bien normalmente también se utilizan los lotes para realizar separaciones lógicas dentro del script.



videos de ayuda para oracle









TIPOS DE ÍNDICES EN ORACLE

Lectura/Escritura

B-tree (árboles binarios)

Function Based

Reserve key

Sólo lectura (read only)

Bitmap

Bitmap join

Index-organized table (algunas veces usados en lectura/escritura)

Cluster y hash cluster

Domain (muy específicos en aplicaciones Oracle)

Índices en Oracle

ÍNDICES CREADOS POR ORACLE DE MANERA AUTOMÁTICA

Al crearse la tabla se crea:

Un índice UNIQUE basado en B*-tree para mantener las columnas que se hayan definido como clave primaria de una tabla utilizando el constraint PRIMARY KEY de una tabla no organizada por índice.

Un índice UNIQUE basado en B*-tree para mantener la restricción de unicidad de cada grupo de columnas que se haya declarado como único utilizando el constraint UNIQUE.

Un índice basado en B*-tree para mantener las columnas que se hayan definido como clave primaria y todas las filas de una tabla organizada por índice.

Un índice basado en hashing para mantener las filas de un grupo de tablas (“cluster”) organizado por hash.

Índices en Oracle

CONSIDERACIONES DE LOS ÍNDICES

Un índice sólo es efectivo cuando es utilizado.

El mantenimiento de un índice tiene efecto sobre el rendimiento de las operaciones de eliminación, inserción y actualización.

ORACLE impone dos restricciones:

El número máximo de columnas: Btree 32 col. Bitmap 30 col.

El espacio requerido para almacenar una clave no puede exceder la mitad del espacio disponible para almacenar datos en un bloque ORACLE

Índices en Oracle Restricciones en Oracle 8

REGLAS EN EL DISEÑO DE ÍNDICES

Indexe solamente las tablas cuando las consultas no accedan a una gran cantidad de filas de la tabla.

No indexe tablas que son actualizadas con mucha frecuencia.

Indexe aquellas tablas que no tengan muchos valores repetidos en las columnas escogidas.

Las consultas muy complejas (en la cláusula WHERE) por lo general no toman mucha ventaja de los índices.

Índices en Oracle

SINTAXIS: CREACIÓN

Básica

CREATE INDEX nombre_indice ON [esquema.] nombre_tabla (columna1 [, columna2, ...])

UNIQUE garantizan que en una tabla (o “cluster”) no puedan existir dos filas con el mismo valor.

Índices en Oracle

SINTAXIS: MODIFICACIÓN

Básica

ALTER INDEX [schema.]index options

Índices en Oracle

SINTAXIS: ELIMINACIÓN

Básica

DROP INDEX [schema.]index [FORCE]

Índices en Oracle

ESTRUCTURA: B*-TREE

Se estructura como un árbol cuya raíz contiene múltiples entradas y valores de claves que apuntan al siguiente nivel del árbol.

Nivel 0.

tablas pequeñas de datos estáticos.

Nivel 1.

Indexa tablas dinámicas con el valor único de los identificadores de columna.

Nivel 2.

Indexa largas tablas o con poca cardinalidad.

Índices en Oracle

ESTRUCTURA: B*-TREE (GRÁFICO) Índices en Oracle

ESTRUCTURA: B*-TREE (CONVENIOS)

Los siguientes convenios son utilizados para el almacenamiento en un índice basado en B*- Tree:

En caso de que un índice no sea UNIQUE, si múltiples filas poseen el mismo valor de la clave en la estructura del índice se repetirán los valores de dichas claves.

Si una fila posee para todas las columnas de la clave el valor NULL, en el índice no existirá una entrada correspondiente a dicha fila.

Si la tabla no está particionada, se utilizan ROWIDs restringidos para indicar la dirección de la fila. Esto se debe a que todas las filas de la tabla no se encuentran en un mismo segmento y con esta política el índice requiere menos espacio en disco.

Índices en Oracle

ESTRUCTURA: BITMAP

Son efectivos para columnas simples con poca cardinalidad, esto es muchos valores distintos.

Más rápidos que los B*-Tree en entornos de read-only.

Almacenan valores de 0 ó 1 en el ROWID.

Ejemplo:

create bitmap index person_region on person (region);

Índices en Oracle Row Region North East West South 1 North 1 0 0 0 2 East 0 1 0 0 3 East 0 1 0 0 4 West 0 0 1 0 5 South 0 0 0 1 6 North 1 0 0 0

Los indices se usan para mejorar el rendimiento de las operaciones sobre una tabla.

En general mejoran el rendimiento las SELECT y empeoran (minimamente) el rendimiento de los INSERTy los DELETE.

Una vez creados no es necesario nada más, oracle los usa cuando es posible (ver EXPLAIN PLAN).

Índices

Un índice en una base datos Oracle 11g es un objeto opcional normalmente asociado a una tabla pero que su uso es casi imprescindible. Una de las misiones de los índices es permitir que las consultas de datos sean más rápidas devolviendo su resultado, sobre todo en tablas con miles o millones de líneas. Una tabla pude tener más de un índice y estos pueden estar compuestos por una o varias columnas.

El estamento básico para crear índices en una base de datos Oracle 11g es:

1.CREATE INDEX nombre_indice ON nombre_tabla (columna, columna1,….);

En el artículo - Introducción a la creación de tablas en Oracle 11g - creamos la tabla clientes, vamos a utilizarla como base para practicar la creación de índices.

En primer lugar vamos a crear un índice sobre la columna CIF para que nuestras búsquedas por esta columna sean llo más rapidas posible:

1.CREATE INDEX factura.clientes_idx1 ON factura.clientes (CIF)

2.TABLESPACE FACTURA_IDX01;

Vamos a crear otro índice compuesto por las columnas NombreCli y DireccionCli:

1.CREATE INDEX factura.clientes_idx2 ON factura.clientes (NombreCli, DireccionCli)

2.TABLESPACE FACTURA_IDX01;

Como podéis ver he añadido la cláusula TABLESPACE para indicar que el índice se cree en el tablespace FACTURA_IDX01, si omitimos esta cláusula el índice se creará en el DEFAULT TABLESPACE que tenga definido el usuario FACTURA.

El usuario que vaya a crear el índice tiene que tener privilegio de CREATE INDEX y UNLIMITED TABLESPACE o CUOTA sobre el tablespace FACTURA_IDX01.