Mostrando entradas con la etiqueta Base de Datos. Mostrar todas las entradas
Mostrando entradas con la etiqueta Base de Datos. Mostrar todas las entradas

miércoles, 4 de junio de 2014

Quitar AUTO_CLOSE de Bases de Datos SQL Server

En SQL Server 2008 R2 existe una propiedad de las bases de datos llamada AUTO_CLOSE. Dicha propiedad hace, mientras esté activa (ON), que si una base de datos deja de ser accedida por alguna conexión se cierre, es decir entra en un estado de reposo por decirlo de alguna forma. Esto hace que cuando una conexión intente acceder a esta base de datos, SQL Server 2008 R2 deba realizar una serie de comprobaciones para "despertar" a la base de datos.

Todo esto se traduce en una sensación de lentitud al conectar con la base de datos. Para solucionarlo, podemos ejecutar el siguiente código:

01 USE [master]
02 GO
03 ALTER DATABASE [MiBaseDeDatos] SET AUTO_CLOSE OFF
04 GO

De esta forma, optimizamos el rendimiento de nuestras bases de datos, y lo más importante, es que no tiene ningún inconveniente. De hecho, es posible que en futuras versiones de SQL Server, esta configuración no exista y la base de datos este siempre "Despierta"  (AUTO_CLOSE = OFF).

miércoles, 26 de junio de 2013

SQLServer 2008. Importación masiva de datos de un fichero delimitado a una tabla.

Como todos sabréis, y sino os lo digo yo, SQL Server 2008 (en todas sus versiones) nos ofrece un abanico enorme de posibilidades para realizar cualquier cosa que podamos imaginar sobre una base de datos. En este artículo me centrare en insertar grandes volúmenes de datos en una tabla de una tacada. 

Como ya he comentando, tenemos muchísimas posibilidades, pero si los datos están en un fichero externo, es decir, no viene de otra base de datos ya sea en nuestro servidor u otro (enlace al artículo sobre vinculación de servidores), dichas posibilidades se van recortando, aun así, son muchas. Entre ellas y por mi propia experiencia, las que más me gustan son dos:  mediante XML y con ficheros delimitados.

En esta ocasión vamos a explicar como lo haríamos con ficheros delimitados, donde nuevamente, tenemos varias posibilidades, siendo la que explicaré a continuación no la mas rápida (de implementar), pero si la más segura y efectiva. Este proceso sería muy útil a la hora de hacer una importación/exportación de datos.
Teniendo la tabla:
01 CREATE TABLE poetas
02 (
03    CODIGO integer not null PRIMARY KEY,
04    NOMBRE VARCHAR(150) not null,
05    APELLIDOS VARCHAR(150) not null,
06    DIRECCION VARCHAR(150) null,
07    LOCALIDAD VARCHAR(150) null,
08    PROVINCIA VARCHAR(150) null
09 )
Y teniendo un fichero con el siguiente formato: 
01 1|Gustavo Adolfo|Bécquer| Conde de Barajas|Sevilla|Sevilla##
Donde el carácter "|" es el separador de campos, y los caracteres "##" el separador de linea. Además necesitamos un XML con la definición de campos del fichero a usar relacionándolos con los campos y tipos de las columnas de la tabla donde se van a insertar los datos. El formato sería: 

01 <?xml version="1.0"?> 
02 
03 <BCPFORMAT xmlns="http://schemas.microsoft.
04 com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.
05 org/2001/XMLSchema-instance"> 
06 
07 <RECORD> 
08 
09 <FIELD ID="1" xsi:type="CharTerm" TERMINATOR="|"/> 
10 <FIELD ID="2" xsi:type="CharTerm" TERMINATOR="|"/> 
11 <FIELD ID="3" xsi:type="CharTerm" TERMINATOR="|"/> 
12 <FIELD ID="4" xsi:type="CharTerm" TERMINATOR="|"/> 
13 <FIELD ID="5" xsi:type="CharTerm" TERMINATOR="|"/> 
14 <FIELD ID="6" xsi:type="CharTerm" TERMINATOR="##"/> 
15 
16 </RECORD> <ROW> 
17 
18 <COLUMN SOURCE="1" NAME="CODIGO" xsi:type="SQLSMALLINT"/> 
19 <COLUMN SOURCE="2" NAME="NOMBRE" xsi:type="SQLNVARCHAR"/> 
20 <COLUMN SOURCE="3" NAME="APELLIDOS" xsi:type="SQLNVARCHAR"/> 
21 <COLUMN SOURCE="4" NAME="DIRECCION" xsi:type="SQLNVARCHAR"/> 
22 <COLUMN SOURCE="5" NAME="LOCALIDAD" xsi:type="SQLNVARCHAR"/> 
23 <COLUMN SOURCE="6" NAME="PROVINCIA" xsi:type="SQLNVARCHAR"/> 
24 
25 </ROW> </BCPFORMAT> 


Solo nos faltaría ejecutar una instrucción como esta:


01 INSERT INTO poetas(CODIGO, NOMBRE, APELLIDOS, DIRECCION, LOCALIDAD, PROVINCIA) 
02 SELECT  CODIGO, NOMBRE, APELLIDOS, DIRECCION, LOCALIDAD, PROVINCIA
03 FROM OPENROWSET(BULK 'C:\exportacion\poetas.txt', 
04 FORMATFILE='C:\exportacion\poetas.xml' 
05 ) as t1 ;
Aclarar que igual que lo hacemos con una sentencia INSERT, podríamos hacerlo con un UPDATE o un DELETE sin más problemas. También deciros que esta instrucción disparará una sola vez los triggers que tengamos  en la tabla, por lo que deberemos programarlos para que traten el conjunto de datos y no solo un registro.

miércoles, 19 de junio de 2013

Ejemplo de encriptación de campos de una base de datos de SQL Server 2008 con Clave

Todos las empresas quieren proteger sus datos de terceros, e incluso de sus propios trabajadores. Lo que muchos "jefes" no tienen tan claro es que siempre hay dos niveles de protección... uno físico y uno lógico. De nada sirve poner un sistema de seguridad por software de ultima generación y "super seguro" si cualquiera tiene acceso al disco físico donde se almacenan los datos. Como respuesta a la pregunta, si alguien se lleva mi base de datos, ¿puede acceder a los datos?. La respuesta siempre es SI. Lo único que podemos hacer es ponerlo más difícil.

Por tanto, desde mi punto de vista, lo más importante es tener un sitio seguro donde almacenar los datos y que físicamente  tan sólo personal autorizado puede acceder a ellos. Además hay que complementar esto con una buena seguridad por software que nos evite el máximo número de ataques posible. ¿y si todo esto falla? la solución pasa por encriptar parte o la totalidad de nuestra base de datos, que si bien no es infalible, nuevamente complicamos un poco mas las cosas a "los malos".

Hay varias formas de encriptar una base de datos de SQL Server 2008, una de las más sencillas es la siguiente. Encriptar los campos de las tablas que no queramos que nadie conozca con una clave. Está claro que si perdemos la clave... nos resultará un poco complicado obtener nosotros mismos esos datos, pero como siempre, por "fuerza bruta" se pueden obtener. Por esto decimos, que lo único que conseguiremos es complicar un poco mas las cosas a "los malos". En próximas entradas hablaré de otros métodos de encriptación de las bases de datos de SQL Server 2008. Es importante saber también  que este método de encriptación esta disponible en la versión Express.

Un ejemplo: 

01 create database DBConClave 
02 
03 GO 
04 
05 use DBConClave 
06 
07 GO 
08 
09 create table Clientes 
10 ( 
11 id integer identity primary key, 
12 nombre varchar(100), 
13 apellidos varchar(200), 
14 cif varchar(20), 
15 ccc VARBINARY(8000) -- este es el campo que vamos a cifrar. 
16 ) 
17 
18 GO 
19 
20 -- insertamos un registro con clave 
21 -- es la menor protección pero también la que requiere menos recursos 
22 INSERT INTO Clientes (nombre, apellidos, cif, ccc) 
23 VALUES ('Sandra', 'Matos', '1231231',ENCRYPTBYPASSPHRASE('mipassword','123132131321')) --mispassword es la clave de cifrado 
24 
25 GO 
26 
27 --si hacemos un select normal no podemos obtener el ccc 
28 SELECT * FROM CLientes 
29 
30 
31 --para poder obtener el ccc del cliente deberíamos pasarle también la clave. 
32 SELECT nombre, apellidos, cif, CONVERT(VARCHAR(300), 
33 DECRYPTBYPASSPHRASE('mispassword',ccc)) as ccc 
34 FROM Clientes



miércoles, 12 de junio de 2013

Vinculación de servidores con SQL Server 2008

Hay veces que necesitamos obtener en una sola consulta datos de tablas de diferentes bases de datos. Si estas bases de datos, están en el mismo servidor, no tenemos ningún problema, haciéndolo de la manera siguiente, lo tendríamos solucionado. Por ejemplo:

01 SELECT * FROM BD1.esquema.Tabla
02 UNION ALL
03 SELECT * FROM bd2.esquema.Tabla

En este caso estamos haciendo una simple unión (recordar que las campos que se devuelvan en cada subconsulta deben ser del mismo tipo y en el mismo orden). Pero que pasa si las bases de datos están en servidores diferentes, SQL Server 2008 nos proporciona las características necesarias para solventar este problema.

Lo primero que tendremos que hacer es agregar el servidor remoto al nuestro (al que estamos conectado) de la siguiente forma:

01 EXEC sp_addlinkedserver 'SERVIDORREMOTO\INSTANCIA', N'SQL Server';

Ahora,  de una forma similar a como lo haciamos con diferentes bases de datos pero en el mismo servidor, lo haremos pero anteponiendo  el servidor remoto.


01 SELECT * FROM [SERVIDORREMOTO\INSTANCIA].[BD1].[esquema].[Tabla]
02 UNION ALL
03 SELECT * FROM [SERVIDORLOCAL\INSTANCIA].[bd2].[esquema].[Tabla]


Para definir el Inicio de Sesión y la clave con la que queremos acceder al servidor, usaremos el procedimiento :sp_addlinkedsrvlogin, por ejemplo: exec sp_addlinkedsrvlogin 'h5sefsomzv.database.windows.net,1433', 'FALSE', NULL, 'iniciodesesion', 'claveiniciodesesion';

martes, 21 de mayo de 2013

Quitar estado "Suspect" de Bases de Datos de SQL Server 2008

Hay veces que nuestras bases de datos pueden aparecer "sin previo aviso" en estado Suspect... normalmente esto ocurre porque el dispositivo físico donde están los ficheros de la base de datos no se ha inicializado antes que el propio servicio de SQL Server 2008. Por tanto la base de datos se marca de esta forma para impedir su utilización. Si este es el motivo, normalmente basta con reiniciar el servicio de SQL Server y la base de datos aparecerá disponible de nuevo. 

Muchas veces esto no es suficiente, o realmente lo que provoca el estado Suspect es una corrupción de alguno de los ficheros de la base de datos, para estos casos podemos seguir el siguiente proceso.


Ponemos la base de datos en estado de emergencia
01 ALTER DATABASE miBaseDeDatos SET EMERGENCY; 
Ponemos la base de datos en modo de usuario único para asegurarnos que solo nosotros trabajamos en ella
01 ALTER DATABASE miBaseDeDatos SET SINGLE_USER; 
Realizamos la reparación de la base de datos permitiendo la pérdida de datos (el asumir pérdida de datos puede no ser necesario, para no permitirlo usaremos el parametro REPAIR_REBUILD)
01 DBCC checkdb ('miBaseDeDatos', REPAIR_ALLOW_DATA_LOSS);
Volvemos a poner a poner la base de datos disponible.

01 ALTER DATABASE miBaseDeDatos SET ONLINE; 
Por último permitimos multiples conexiones.
01 ALTER DATABASE miBaseDeDatos SET MULTI_USER;

viernes, 17 de mayo de 2013

Ejemplo de uso de exepciones personalizadas en SQL Server

A veces es interesante crear excepciones personalizadas en una base de datos para que se disparen cuando realicemos una acción que no queramos permitir. Para explicarlo, propongo el siguiente ejemplo:
01 create database BDExcepciones 
02 GO
03 use BDExcepciones 
04 GO
05 create table Coches
06 ( 
07  id int identity primary key, 
08  marca varchar(20),   
09  descripcion varchar(100), 
10  matricula varchar(20) 
11 ) 
12 GO
Introducimos unos datos en la tabla
01 insert into Coches (marca, descripcion, matricula)  
02  VALUES ('Mercedes', 'El Coche del Roman Azul Cielo', 'NOTEPEGA'); 
03 insert into Coches (marca, descripcion, matricula)  
04  VALUES ('Opel Corsa', 'El corsita', 'PALABODA');  
05 insert into Coches (marca, descripcion, matricula)  
06  VALUES ('Rover 25', 'A ver lo que dura', 'ROTO');  
07 insert into Coches (marca, descripcion, matricula)         
08  VALUES ('Ford Focus', 'El coche nuevo', 'COCHENUEVO');  
Creamos excepciones.Los identificadores deben ser a partir del 50001 y la severidad debe ser 16 para que se trate como una excepción. Siempre hay que definir el mensaje en ingles y luego en español.
01 use master go
02 sp_addmessage 50002, 11, 'Ya existe un coche con esa matricula', 'us_english'; 
03 go
04 sp_addmessage 50002, 11, 'Ya existe un coche con esa matricula', 'spanish'; 
05 go
06 sp_addmessage 50003, 16, 'Ya existe un coche con esa matricula', 'us_english'; 
07 go
08 sp_addmessage 50003, 16, 'Ya existe un coche con esa matricula', 'spanish'; 
09 go
10 use BDExcepciones go 
Para lanzarla usaremos: RAISERROR (50002, 11, 1). Creamos un procedimiento que inserta un coche y si su matricula existe nos devuelve una excepcion. 
01 ALTER PROCEDURE InsertarCoche
02  @marca varchar(20),
03  @descripcion varchar(100),
04  @matricula varchar(20) AS
05 BEGIN
06  SELECT * FROM Coches WHERE matricula = @matricula 
07  if @@ROWCOUNT = 0 
08  BEGIN
09   INSERT INTO Coches (marca, descripcion, matricula) VALUES (@marca, @descripcion, @matricula);
10  END 
11  ELSE 
12  BEGIN
13   RAISERROR (50002, 11, 1)
14  END
15 END; 
16 -- lo probamos 
17 execute InsertarCoche 'cochenuevo', 'para cuando', 'ROTO'; 
Ahora vamos a hacer el mismo ejemplo pero en el trigger antes de insertar.
01 ALTER PROCEDURE InsertarCoche
02  @marca varchar(20),
03  @descripcion varchar(100),
04  @matricula varchar(20) AS
05 BEGIN
06  SELECT * FROM Coches WHERE matricula = @matricula 
07  if @@ROWCOUNT = 0 
08  BEGIN
09   INSERT INTO Coches (marca, descripcion, matricula) VALUES (@marca, @descripcion, @matricula);
10  END 
11  ELSE 
12  BEGIN
13   RAISERROR (50002, 11, 1)
14  END
15 END; 
16 -- lo probamos 
17 execute InsertarCoche 'cochenuevo', 'para cuando', 'ROTO'; 
Aunque personalmente, a estas alturas prefiero usar la base de datos tan solo como almacén de datos, y delegar a la capa de negocio toda la programación que de otra forma iría en ella, puede que en algún momento nos sea de utilidad.