Hola gente, a partir de marzo de 2009 mi nuevo blog es: blog.maxiaccotto.com , los espero ahi 
En el mes de febrero he dictado un curso de 3 días en el MUG (Club de usuarios Microsoft) de Argentina para aquellos que recién se inician sobre SQL Server. El curso fue dado todo sobre la edición Express (gratuita) y se han mostrado desde las cosas básicas hasta como migrar una base de datos Fox a SQL Server.
Aquí les dejo el link del material de los ejemplos de dicho curso
Al intentar eliminar un Job del SQL Server 2005 puede aparecer el siguiente mensaje de error
The DELETE statement conflicted with the REFERENCE constraint “FK_subplan_job_id”. The conflict occurred in database “msdb”, table “dbo.sysmaintplan_subplans”, column ‘job_id’.
The statement has been terminated. (Microsoft SQL Server, Error: 547)
nos indica que no se puede eliminar un job por problemas con los planes de mantenimiento.
Para poder resolver este problema y poder eliminar el Job les dejo el siguiente Script
declare @job_name varchar(500)
set @job_name = N'tujob' ;
USE [msdb]
delete sysmaintplan_log
FROM sysmaintplan_subplans AS subplan
INNER JOIN
sysjobs_view AS jobs ON subplan.job_id = jobs.job_id INNER JOIN
sysmaintplan_log ON subplan.subplan_id = sysmaintplan_log.subplan_id
WHERE (jobs.name = @job_name)
delete sysmaintplan_subplans
FROM sysmaintplan_subplans AS subplan INNER JOIN
sysjobs_view AS jobs ON subplan.job_id = jobs.job_id
WHERE (jobs.name = @job_name)
delete
from msdb.dbo.sysjobs_view where name = @job_name
Luego de unos meses en CTP ya esta disponible para descargar el SP3 del SQL server 2005. Lo instale en un cliente y por ahora todo bien :)
Aparte de incluir los hotfix también hay algunos cambios funcionales , aquí les dejo el enlace
En nuestras empresas o en clientes hay muchas veces que tenemos la necesidad de restringir el acceso a nuestro SQL desde ciertas aplicaciones como podría ser el paquete Office. A partir de SQL Server 2005 y con la incorporación de los triggers DDL podemos hacer esto de forma bastante simple.
Aquí les dejo un ejemplo de trigger DDL a nivel servidor de base de datos que no permitirá login desde aplicaciones Office.
CREATE TRIGGER [connection_limit_trigger]
ON ALL SERVER
FOR LOGON
AS
set nocount on
-- con este trigger controlamos quienes no pueden hacer login a SQL
BEGIN
IF app_name() like '%Office%'
ROLLBACK;
END;
GO
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
ENABLE TRIGGER [connection_limit_trigger] ON ALL SERVER
GO
Si tienen integrado a Sherepoint (ya sea el portal o bien el services) son Reporting services y ejecutan un reporte que dura mas de dos minutos les aparecerá del lado de Sharepoint el siguiente error: Error inesperado o bien “An unexpected error has occurred” en ingles.
Por defecto existe un timeout a nivel ASP que es de 2 minutos, para cambiar este valor habrá que hacer los siguientes pasos desde el servidor de Sharepoint
- Abrir el archivo web.config de la aplicación sharepoint (por ejemplo \inetpub\wwwroot\wss\VirtualDirectories\80)
- Buscar dentro del archivo httpRuntime
- Agregar executionTimeout con un valor en segundos deseado, por ejemplo 1800
<httpRuntime maxRequestLength="51200" executionTimeout = "1800" />
Esto nos permitirá extender el timeout del sharepoint haciendo que si tenemos reportes con una duración mayor a dos minutos los podamos ejecutar sin errores. Les recomiendo poner 1800 segundos ya que también ese valor es el predeterminados a nivel Reporting Service para un timeout de Reporte.
Aquí les dejo el link original de donde he sacado la solución y me ha funcionado perfecto.
Ya se puede empezar a descargar y probar el beta del service pack3 de SQL Server 2005.
Aqui les dejo el link
Me encontré con este excelente articulo el cual les recomiendo si necesitan pasar un sharepoint de un servidor a otro. Excelente :)
Al instalar muy contentos nuestro SQL Server 2008 nos vamos a encontrar con varias novedades muy buenas para los administradores y desarrolladores. Pero también nos vamos a encontrar con algunas cosas que antes teníamos y ahora no , como por ejemplo la herramienta SAC (Surface Area Configuration) de 2005 la cual nos permitía entre otras cosas habilitar las conexiones entrantes, el CLR, DAC, etc.
Entonces como hacemos en 2008 si no disponemos de esta herramienta? Bueno siempre tenemos la posibilidad de usar sp_configure.
Pero si no queremos usar desde código TSQL y si hacerlo de forma gráfica entonces ahora debemos hacerlo así.
- Entramos a nuestro Management Studio de 2008
- Luego de entrar e iniciar sesión en un servidor, hacemos click alterno del mouse sobre el nombre del servidor y luego vamos a la opción del menú "Facet"
Al hacer esto entraremos a la siguiente pantalla
Aquí simplemente debemos seleccionar en Facet la opción de "Surface Area Configuration" y ahora si podemos configurar las opciones de forma gráfica :)
El 23 de Septiembre se libero el primer hotfix del producto. Yo lo he bajado y probado en una migración que estoy llevando adelante y no he tenido problemas. De hecho me ha solucionado algunos problemas que estaba teniendo con Reporting Service cuando hacia reportes bien grandes.
Muchas veces se me hace esta pregunta o bien veo en foros la misma. La respuesta es SI. En la edición Express Advance Services es totalmente gratuita e incluye Reporting Service (ya sea 2005 o 2008).
Claro esta edición tiene algunas limitaciones pero lo que si es cierto que pueden desarrollar reportes tan complejos como en las ediciones Standard o Enterprise y luego si desean migrar a cualquier de estas ediciones no es necesario modificar nada de sus reportes.
La confusión por lo general viene ya que hay dos versiones de la edición Express de SQL (la común y la Advance Service) siendo solo esta ultima la que dispone de Reporting Service.
Aquí les dejo los links para poder descargar la Express 2005 o 2008
Etiquetas de Technorati:
Reporting Service
La gente de JumpStart ha publicado ya hace un tiempo bastante material sobre SQL 2008 (Vídeos, HOL, Script , PPT, etc). Imperdible este link
Los días 10 y 11 de Octubre he estado en la hermosa ciudad de Córdoba haciendo una conferencia para el Club de usuarios Microsoft (MUG) sobre Reporting Service 2008.
Primero quiero agradecer a los participantes de las jornadas ya que considerando que en argentina era un fin de semana largo han estado tanto el día viernes como el sábado por la mañana.
Aquí dejo el link al material utilizado en dichas jornadas, espero lo disfruten.
En la mayoria de nuestras aplicaciones tenemos la necesidad de poder generar el numero de nuestros comprobantes (Ordenes de compra, pedidos de ventas, etc)
Existen diferentes tecnicas para poder realizar esta operacion. En los ejemplos de este post mostrate las dos formas mas tradicionales de hacerlo y cuales son sus pros y contras de cada una.
Para estos ejemplos crearemos una base de datos nueva con la siguiente sentencia
CREATE
DATABASE EJEMPLO_NUMERACION
Usando autonumericos
Dentro de SQLserver podemos definir en un tipo de dato entero la posibilidad de que sea autoincremental, esto hara que de forma automatica el motor de base de datos calcule el valor que debe ir en ese campo. Usando esta posibilidad podriamos definir que en nuestros comprobantes el campo de numero sea autonumerico.
El siguiente ejemplo ilustra como hacer esta operacion
USE
EJEMPLO_NUMERACION
GO
CREATE
TABLE OC
(ID
INT IDENTITY, FECHA
DATETIME NOT NULL,
CLIENTE VARCHAR(50) NOT NULL)
GO
Como se podra observer el campo ID es de tipo int y a su vez tiene la propiedad de ser autoincremental, el resto de los campos corresponden a datos necesarios de la Orden de compra de este ejemplo.
Si queremos insertar un nuevo registro (o sea una nueva orden de compra) simplemente hacemos lo siguiente
INSERT
INTO OC
(FECHA
,CLIENTE
)
VALUES
(GETDATE(),'IBM')
go
Si luego de esto hacemos un select a la tabla OC y vemos los registros podremos observar que se ha generado de forma automatica el ID 1, si hacemos otro insert valido veremos que se generara el 2 y asi sucesivamente.
Ahora bien, que sucede si por ejemplo hacemos un insert y no se pudo realizar la transaccion, por ejemplo queremos insertar una OC donde el campo Cliente sea nulo lo cual no esta permitido por la integridad de la base de datos.
INSERT
INTO OC
(FECHA
,CLIENTE
)
VALUES
(GETDATE(),NULL)
go
Al hacer esta instruccion recibiremos el siguiente error:
Msg 515, Level 16, State 2, Line 2
Cannot insert the value NULL into column 'CLIENTE', table 'EJEMPLO_NUMERACION.dbo.OC'; column does not allow nulls. INSERT fails.
Bien, el registro si hacemos un select a la tabla no se ha generado y eso es correcto ya que ha existido un problema.
Ahora bien, hasta el momento solo tenemos generada la OC con ID = 1 y la 2 ha fallado. Pues arreglamos el tema del null en el insert para que se genere la OC = 2 ya que es la que continua luego de la 1.
INSERT
INTO OC (FECHA,CLIENTE)
VALUES
(GETDATE(),'MICROSOFT')
go
El registro se genero ahora "correctamente", pero si hacemos un select a la tabla nos encontraremos que ahora tenemos el ID = 1 y el ID = 3, con lo cual estamos teniendo huecos en nuestra numeracion.
Como se podra observar es un grave problema tener estos huecos en los numeros de los comprobantes (imagine que fueran facturas por ejemplo), pues esto se debe a como funciona el autonumero de SQLServer, es simple de usar e implementar pero hay que saber lo de las transacciones, ante una falla el numero lo reservo igual y considero que se genero con lo cual lo salta
Usando tabla numeradora
Otra alternativa a la que vimos es la de generar una tabla donde tendremos los numeros de los comprobantes y usarla para numerar uno nuevo, si la transaccion fallo simplemente hay que tener el recaudo de no cambiar el valor en la tabla numeradora.
Entonces, lo primero que generamos es esta tabla numeradora
CREATE
TABLE NUMERADOR (TIPO_COMPROBANTE VARCHAR(100),
ULTIMO_NUMERO INT)
GO
INSERT
INTO NUMERADOR
VALUES ('OC',1
)
INSERT
INTO NUMERADOR
VALUES ('FACTURAS',10
)
GO
La tabla numeradora podra ser utilizada por distintos comprobantes, de ahi es que tiene un campo denominado tipo_comprobante y otro ultimo_numero.
Como tambien se puede observar se le han insertado 2 registros, uno para los comprobantes de Ordendes de Compra y el otro para facturas.
Ahora bien, lo que debemos hacer como siguiente paso es utilizar esta tabla numeradora para obtener el ultimo_numero sumarle 1 y asignarlo a la OC que estamos generando. Aqui entran a jugar una serie mas de detalles a conocer, si hacemos un Select (sin cambiar el nivel de bloqueo) y otro usuario en otro sitio hace lo mismo podran obtener el mismo ultimo_numero lo cual al sumarle 1 los dos intentaran hacer la misma OC.
Para evitar este tipo de situaciones, ya que no solo necesito numerar sino tambien asegurar que no exista la posibilidad de obtener numeros duplicados (la misma OC por ejemplo) es que vamos a hacer uso de algunos trucos de bloqueos y variables.
DROP
TABLE OC
GO
CREATE
TABLE OC (NUMERO INT, FECHA DATETIME,CLIENTE VARCHAR(50) NOT NULL)
GO
declare @proximo_numero
int begin try
begin
tran
update numerador set @proximo_numero = ultimo_numero = ultimo_numero + 1
where tipo_comprobante = 'OC'
INSERT INTO OC VALUES (@proximo_numero,getdate(),'SQLTOTAL')
commit tran
end try
begin
catch
rollback tran -- primero hacemos un rollback
print @@error -- mostramos el error generado
end
catch
GO
La sentencia UPDATE no solo modifica la tabla sino que ademas actualiza la variable que luego utilizamos para generar el comprobante. De esta manera logramos que si hay 2 procesos haciendo esto por el uso de los bloqueos internos de SQL al hacer uso de la instruccion UPDATE, el segundo proceso no podra hacer update hasta que el primero no haya terminado la transaccion (ya sea por commit o rollback tran)
Si hacemos un select sobre OC vamos a observar que se ha generado el id numero 2, ahora bien si intentamos hacer un nuevo insert pero que falla y luego volvemos a insertar uno que no falla no tendremos los huecos en la numeracion como nos ha sucedido con los identity.
Para probar que no se van a generar 2 numeros iguales en tiempos iguales hay que hacer la siguiente prueba con el Script.
1) Habra dos ventanas de management studio o Query Analizer si usa 2000
2) En ambas copie por ejemplo este codigo
declare @proximo_numero int begin try
begin
tran
update numerador set @proximo_numero = ultimo_numero = ultimo_numero + 1
where tipo_comprobante = 'OC'
INSERT INTO OC VALUES (@proximo_numero,getdate(),'SQLTOTAL')
end try
begin
catch
rollback tran -- primero hacemos un rollback
print @@error -- mostramos el error generado
end
catch
3) Ejecute desde la primer ventana el script, luego hagalo desde la segunda. Como podra observar en la segunda ventana se queda esperando y no procesa , esto se debe que hay bloqueos sobre la tabla numeradora y que el segundo update no ha podido ser realizado.
4) En la primer ventana escriba commit tran y ejecute solo eso
Al hacer esto podra observar que la segunda ventana ha terminado.
5) Haga un select sobre OC y vea los resultados 
Bueno con esto termino, espero que les haya sido de utilidad y espero sus comentarios
SQL Server 2008 incorpora nuevos tipos de datos, uno de ellos es el tipo de datos Spatial.
En este link les dejo un interesante articulo con todo su codigo de ejemplo donde se integra SQL 2008 al famoso Virtual Earth.
http://www.viawindowslive.com:80/Articles/VirtualEarth/SQL2008Spatialafirstimpression.aspx
SQL-Dinámico y seguridad dentro de SQLServer 2005
El SQL-dinámico es una técnica la cual nos permite ejecutar sentencias TSQL de forma dinámica, para poder hacer uso de esta técnica es necesario utilizar o bien el store procedure sp_executesql o sino EXEC(@str).
Este articulo no tiene como objetivo entrar en detalle del SQL-dinámico ya que hay otros escritos al respecto como por ejemplo el siguiente link el cual recomiendo leer
http://www.hayes.ch/sql/sql_dinamico.html
Aquí nos concentraremos en la seguridad y los cambios que hay en SQL2005 al respecto.
Una de las mayores contras que tiene el uso de SQL-Dinámico es que si lo ponemos dentro de un Store Procedure el usuario que ejecute tal Store no solo deberá tener permisos de execute sino que también deberá tener permisos sobre los objetos que haga referencia el SQL-Dinámico.
Para poder ser más específicos veamos un ejemplo al respecto
USE
MASTER
GO
CREATE DATABASE TEST1
GO
USE TEST1
GO
-- CREAMOS UN NUEVO LOGIN
CREATE
LOGIN FEDERICO WITH PASSWORD ='PASSW@RD'
GO
USE TEST1
GO
-- CREAMOS EL USUARIO EN LA BASE DE DATOS
CREATE USER FEDERICO
GO
-- CREAMOS UNA TABLA
CREATE
TABLE DBO.EMPLEADOS (ID INT IDENTITY, NAME VARCHAR(50))
GO
-- CREAMOS UN STORE QUE CONSUME LA TABLA EMPLEADOS
CREATE
PROC DBO.USP_EMPLEADOS_GET_ALL ASSELECT * FROM DBO.EMPLEADOS
GO
-- CREAMOS OTRO STORE QUE CONSUME LA TABLA PERO CON SQLDINAMICO
CREATE
PROC DBO.USP_EMPLEADOS_GET_ALL2 AS
DECLARE @N NVARCHAR(50)
SET @N = N'SELECT * FROM DBO.EMPLEADOS'EXECUTE SP_EXECUTESQL @N
GO
-- FEDERICO SOLO TIENE PERMISOS A LOS STORES
GRANT
EXECUTE ON DBO.USP_EMPLEADOS_GET_ALL TO FEDERICOGRANT EXECUTE ON DBO.USP_EMPLEADOS_GET_ALL2 TO FEDERICO
GO
-- ENTRAMOS COMO FEDERICO
EXECUTE
AS LOGIN = 'FEDERICO'
SELECT
SUSER_SNAME()
SELECT
* FROM DBO.EMPLEADOS -- FALLA PORQUE NO TENEMOS PERMISO
EXEC
DBO.USP_EMPLEADOS_GET_ALL -- FUNCIONA
EXEC
DBO.USP_EMPLEADOS_GET_ALL2 -- FALLA POR EL SQL-DINAMICO
REVERT
-- REVERTIMOS EL LOGIN
Como se ha podido observar en el ejemplo el segundo Store ha fallado cuando el usuario Federico lo invoco ya que no tenia permisos sobre los objetos internos del mismo, en este caso la tabla "Empleados".
Esto atenta contra el buen uso de un Store procedure ya que una de sus virtudes es aislar a los usuarios del acceso directo a los objetos, con lo cual si deseamos en este caso usar SQL-Dinámico deberíamos darle permisos de Select a la tabla correspondiente.
Esto funciona así en 2000 y no hay solución al respecto, por lo cual yo no recomiendo mucho el uso de SQL-Dinámico y de usarlo tener en claro lo que está sucediendo.
¿Que cambios hay en 2005?
SqlServer 2005 permite dentro de un objeto como el Store Procedure indicarle el contexto de ejecución, esto es totalmente nuevo y se indica con la sentencia WITH EXECUTE AS (Caller, Owner o Username)
- Caller es utilizado por defecto y representa a quien llama (como vimos en el ejemplo 1)
- Owner indica que se impersonificara como el usuario propietario del objeto, en este caso como entre con la cuenta de SA entonces estamos impersonificando con ella.
- User_name nos permite indicar con que login deseamos que se impersonifique
El siguiente ejemplo modifica el Store con SQL Dinámico pero utilizando Execute AS
-- CAMBIOS EL STORE DEL SQL DINAMO PARA QUE SE EJECUTE EN
-- OTRO CONTEXTO DE SEGURIDAD
ALTER PROC DBO.USP_EMPLEADOS_GET_ALL2
WITH EXECUTE AS OWNER
AS
DECLARE @N NVARCHAR(50)
SET @N = N'SELECT * FROM DBO.EMPLEADOS'EXECUTE SP_EXECUTESQL @N
GO
-- VOLVEMOS A PROBAR
EXECUTE
AS LOGIN = 'FEDERICO'
SELECT
SUSER_SNAME()
SELECT
* FROM DBO.EMPLEADOS -- FALLA PORQUE NO TENEMOS PERMISO
EXEC
DBO.USP_EMPLEADOS_GET_ALL
EXEC
DBO.USP_EMPLEADOS_GET_ALL2
REVERT
Aquí se ve claramente que ahora Federico no necesito permisos sobre la tabla "Empleados" para poder hacer uso del SQL-Dinámico.
Con esto lo que logramos es seguir manteniendo una de las enormes virtudes que tienen los Stores Procedures que es el aislamiento con respecto a los objetos base.
No confundir el Execute AS del Store con el luego utilizado en el query para dentro del management Studio impersonificarnos como Federico sin la necesidad entrar a otra ventana.
Estos ejemplos deben ser probados desde SQL2005, si desea probar el primer ejemplo desde SQL2000 deberá sacar la sentencia Execute AS y probar la seguridad entrando con el correspondiente login y password.
Bueno espero les sea de utilidad y espero sus comentarios.
Nos vemos!
Este es el material que se ha utilizado en la jornada del MUG sobre programacion en SQLServer 2005
http://msmvps.com/files/folders/maxiaccotto/entry1133223.aspx
Creando el tipo de datos hora en SQL2005 usando CLR
Introducción
SQLServer dispone de dos tipos de datos que son útiles para el almacenamiento de fechas y horas (Datetime & SmallDatetime). En muchas ocasiones necesitamos tener un tipo de datos exclusivo para almacenar únicamente las horas como así también poder hacer su tratamiento como podrían ser sumas, restas, promedios, etc.
En este articulo veremos paso a paso como crear nuestro propio tipo de datos hora y poderlo utilizar como si fuere cualquier otro tipo de dato del motor nativo.
CLR en SQL2005
Una de las mayores incorporaciones que se le ha hecho a SQLServer 2005 es la posibilidad de crear objetos SQL (Triggers - Stores Procedures - User Function - User defined Type - Aggregate Function) utilizando .NET. La siguiente tabla muestra los tipos de objetos que se pueden crear con T-SQL y/o CLR.
|
Tipo de Objeto |
TSQL |
CLR |
|
Store Procedures |
X |
X |
|
Triggers |
X |
X |
|
User Function |
X |
X |
|
User Defined Type |
|
X |
|
Aggregate Function |
|
X |
Como se podrá observar si se desean crear "User defined Type" o bien "Aggregate Function" (como por ej. Sum, Avg, count,etc) es necesario hacerlos con CLR exclusivamente.
En este articulo no entraremos en detalle de cómo funciona el CLR dentro de SQL2005 sino que nos concentraremos en como crear un "User Defined Types" (UDT)
Debemos aclarar que no es necesario utilizar Visual Studio para poder programar en CLR dentro de SQL2005 pero si altamente recomendado por todas las facilidades que esta herramienta nos ofrece (Deploy - Debug , etc).
Al instalar SQL2005 por default no se activa el uso del CLR, esto es debido a que SQL2005 tiene como concepto "Seguro por defecto" con lo cual cierra todo tipo de funciones que pueden ser de alto riesgo.
Para poder habilitar el uso del CLR se puede o bien utilizar el SAC (Sourface Area Configuration) o sino la siguiente sentencia TSQL.
EXEC sp_configure 'show advanced options', 1
GO
RECONFIGURE
GO
sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO
Fig 1 Sourface Area Configuration
Descripción de los User Defined Types
En versiones anteriores a SQL2005 los UDT eran solamente Alias de otro tipo de dato como se muestra en el siguiente código.
EXEC sp_addtype N'age', N'tinyint', N'not null'
GO
CREATE RULE age_range
AS
@age >= 0 AND @age <=140
GO
EXEC sp_bindrule N'age_range', N'age'
GO
Ahora en SQL2005 los UDT son muchos mas ricos donde todo el comportamiento esta embebido en el código de este.
Los UDT deben respetar los siguientes requerimientos
- Soportar el concepto de Null (INullable)
- Soportar la conversión hacia y desde String
- Soportar la serialización
- Soportar un constructor por default
- Respetar las reglas de nombres (128 caracteres máximo)
Creando el UDT desde Visual Studio 2005
Una de las maneras de poder crear este tipo de objeto es mediante Visual Studio 2005, para ello debemos generar un nuevo proyecto del tipo "Database" ya sea en C# o VB.NET.
Al realizar esta operación nuestro segundo paso en el asistente es indicar a que servidor de SQL2005 nos vamos a conectar
Si nuestro servidor no se encuentra en la lista, debemos agregarlo desde el botón "Add New Reference.."
Nuestro siguiente paso es agregar un componente a nuestra solución desde el "Solution Explorer"
Como podemos observar se pueden agregar Stores Procedures, User Defined Function, User Defined type, Trigger, Aggregate y Class. En nuestro caso usaremos "User Defined Type". Cabe mencionar que dentro de una solución usted podría tener mas un ítem del tipo que fuere.
Al realizar esta operación el Visual Studio nos genera la estructura necesaria para este objeto, en la siguiente figura se muestra un fragmento del código.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native)]
public struct udttime : INullable
{
public override string ToString()
{
// Replace the following code with your code
return "";
}
............
Ahora empezaremos modificando el código base para poder llegar a nuestro objetivo final.
En principio le indicaremos una nueva propiedad "IsByteOrdered" a valor True para que el servidor utilice comparaciones ordenadas por Bytes.
[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native,IsByteOrdered = true)]
Nuestro siguiente paso es crear el código de estructura para nuestro tipo de datos UDTTIME.
public struct UDTTIME : INullable
{
private int milliseconds;
private bool m_Null;
public static UDTTIME Parse(SqlString s)
{
if (s.IsNull)
return Null;
UDTTIME x = new UDTTIME();
TimeSpan ts = TimeSpan.Parse((string)s);
x.milliseconds = (int)ts.TotalMilliseconds;
return x;
}
public override string ToString()............
Aquí estamos usando el tipo de datos TimeSpan y trabajando los tiempos en milisegundos
Ahora lo que haremos es implementar el método ToString con el siguiente código:
public override string ToString()
{
return
TimeSpan.FromMilliseconds(this.milliseconds).ToString();
}
Este método es de suma importancia ya que nos convertirá el valor del tipo timeSpan a algo legible por nuestros usuarios J
Una de las cosas que se pueden hacer también dentro de un tipo de datos es definir funciones, imaginemos que ahora queremos tener dentro de este tipo de datos una función que nos permita a un valor Time X sumarle tantos milisegundos. Para poder realizar esto agregaremos el siguiente código
public UDTTIME Addtime(UDTTIME o)
{
if (o.IsNull)
return Null;
else
{
this.milliseconds += o.milliseconds;
return this;
}
}
Ahora debemos implementar el tratado de los tipos Null para ello implementaremos lo siguiente.
public bool IsNull
{
get
{
// Put your code here
return m_Null;
}
}
public static UDTTIME Null
{
get
{
UDTTIME h = new UDTTIME();
h.m_Null = true;
return h;
}
}
Ahora ya estamos en condiciones de poder hacer el deploy de este componente. El código completo ha quedado de la siguiente manera:
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native,IsByteOrdered = true)]
public struct UDTTIME : INullable
{
// milliseconds
private int milliseconds;
// Private member
private bool m_Null;
public static UDTTIME Parse(SqlString s)
{
if (s.IsNull)
return Null;
UDTTIME x = new UDTTIME();
TimeSpan ts = TimeSpan.Parse((string)s);
x.milliseconds = (int)ts.TotalMilliseconds;
return x;
}
public override string ToString()
{
return TimeSpan.FromMilliseconds(this.milliseconds).ToString();
}
public UDTTIME Addtime(UDTTIME o)
{
if (o.IsNull)
return Null;
else
{
this.milliseconds += o.milliseconds;
return this;
}
}
public bool IsNull
{
get
{
// Put your code here
return m_Null;
}
}
public static UDTTIME Null
{
get
{
UDTTIME h = new UDTTIME();
h.m_Null = true;
return h;
}
}
}
Ahora desde Visual Studio haremos el deploy hacia el servidor que nos habíamos conectado, para ello debemos ir al solution Explorer y hacer el Deploy pulsando el botón alterno del Mouse sobre el nombre de la solución.
Luego de hacer el deploy (solo demorara unos segundos) entraremos al Management Studio en el cual realizaremos las pruebas con nuestro nuevo tipo de dato.
Primero crearemos una tabla con nuestro nuevo tipo de datos
USE ADVENTUREWORKS
GO
CREATE TABLE TIMES
(
id int,
[htime] UDTTIME NULL
)
GO
Luego insertaremos algunos valores
INSERT TIMES VALUES (1, '00:01:20')
INSERT TIMES VALUES (3, '16:30')
GO
Y por ultimo veremos como han quedado en nuestra tabla, aquí usaremos el ToString para que el resultado sea legible para un usuario
SELECT Id, HTIME.ToString() AS TIME
FROM TIMES
GO
Ahora en nuestra siguiente prueba trataremos de usar una suma de milisegundos a una hora determinada
DECLARE @Hours1 UDTTIME
SET @Hours1 = '10:00'
DECLARE @Seconds UDTTIME
SET @Seconds = '00:00:10'
SET @Hours1 =
@Hours1.Addtime(@Seconds)
SELECT @Hours1.ToString()
Ya disponemos de nuestro tipo de datos Hora que SQLServer por default no lo trae J.
Se podrían seguir agregando métodos al mismo como por ej.: Restar horas, tener un método para indicar el total de horas que representa un time, etc.
Cabe mencionar que cuando hemos hecho el Deploy de nuestro objeto SQLServer lo introduce dentro de la base de datos con lo cual si se hace un Backup / Restore se mantendrán todos los Assemblies.
Hacer el deploy sin disponer de Visual Studio 2005
No es una condición disponer de Visual Studio para poder hacer objetos CLR dentro de SQL2005, de hecho con un editor de textos simples y desde líneas de comando se puede resolver. En esta sección veremos los pasos necesarios para hacer este deploy sin el uso de visual Studio, tomaremos el código completo que hemos generado anteriormente y lo transformaremos en una dll con el SDK de .NET, para ello necesitaremos ejecutar el siguiente comando desde la línea de comandos
csc /t:library udttime.cs
Ahora debemos acceder a nuestro Management Studio.
Para no generar confusión con lo anteriormente realizado crearemos una nueva base de datos.
USE MASTER
GO
CREATE DATABASE TIME1
GO
Nuestro siguiente paso será crear el Assembly en nuestra nueva base de datos, para ello usaremos nuevas sentencias de TSQL
USE TIME1
GO
CREATE ASSEMBLY UDTTIME
FROM 'C:\UDTTIME.dll'
GO
Continuaremos creando el tipo de datos indicándole que el origen es un assembly
CREATE TYPE [dbo].[UDTTIME]
EXTERNAL NAME [UDTTIME].[UDTTIME]
GO
Nota: No confundir la palabra EXTERNAL con que la .dll este dentro o fuera del motor, siempre estará dentro y ese paso se realizo cuando hemos ejecutado el "CREATE ASSEMBLY", el External en este caso (al igual que cuando creamos un Store Procedure , User Define Function, Aggregate Function o Trigger) indica que el código de ese objeto es un assembly y no código TSQL
Por ultimo paso nos quedaría probar el tipo de datos UDTTIME, para lo cual ejecutaremos el siguiente código:
DECLARE @TIME1 UDTTIME
SET @TIME1 = '20:50:00'
SELECT @TIME1.ToString()
Conclusiones finales:
La inclusión del CLR dentro del motor de base de datos ha generado muchas discusiones entre los distintos especialistas, sin ninguna duda que es una feature muy importante pero hay que ser cuidadoso en como se la utiliza. Siempre es recomendado realizar con TSQL y solo dejar para CLR aquellos tipos de objetos que no se pueden crear con TSQL (como por ej. el que vimos en este articulo) o para aquellas operaciones donde el procesamiento de calculo pueda ser un problema resolver con este ultimo.
More Posts
Next page »