mi mail: maxi.accotto@gmail.com BLOG DE MAXI ACCOTTO

BLOG DE MAXI ACCOTTO

Blog de SQLServer

Mudanza

Hola gente, a partir de marzo de 2009 mi nuevo blog es: blog.maxiaccotto.com , los espero ahi Smile

Material del curso básico de SQL Server 2008

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

Generando Script de estructuras y datos con el Management Studio

Link

Posted: Feb 26 2009, 09:20 AM by Maxi Accotto | with 1 comment(s) |
Filed under:
Error al querer eliminar un job de SQL Server 2005

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

Se libero el Service Pack 3 del SQL Server 2005

 

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

Como evitar que se acceda a nuestro SQL vía Excel

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

Error inesperado en Sharepoint consumiendo reportes de Reporting Service

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

  1. Abrir el archivo web.config de la aplicación sharepoint (por ejemplo \inetpub\wwwroot\wss\VirtualDirectories\80)
  2. Buscar dentro del archivo httpRuntime
  3. 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.

Service pack 3 de SQL Server 2005

Ya se puede empezar a descargar y probar el beta del service pack3 de SQL Server 2005.

Aqui les dejo el link

Migración de WSS a otra máquina y hasta cambiando el servidor de base de datos

 

Me encontré con este excelente articulo el cual les recomiendo si necesitan pasar un sharepoint de un servidor a otro. Excelente :)

¿Donde esta el SAC (Surface Area Configuration) en SQL 2008?

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

  1. Entramos a nuestro Management Studio de 2008
  2. 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

image

Aquí simplemente debemos seleccionar en Facet la opción de "Surface Area Configuration" y ahora si podemos configurar las opciones de forma gráfica :)

Primera actualización de SQL Server 2008

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.

Posted: Oct 18 2008, 02:51 PM by Maxi Accotto | with no comments
Filed under:
Reporting Service es gratuito?

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:
Material sobre SQL Server 2008

La gente de JumpStart ha publicado ya hace un tiempo bastante material sobre SQL 2008 (Vídeos, HOL, Script , PPT, etc). Imperdible este link

Reporting Service 2008 desde 0

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.

Como generar el numero de nuestros comprobantes sin morir en el intento

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 Sad 

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 Smile

 

Bueno con esto termino, espero que les haya sido de utilidad y espero sus comentarios 

 

 

Se libero el CTP Febrero de SQL server 2008

Ya esta disponible el nuevo CTP de SQL2008

http://www.microsoft.com/downloads/details.aspx?FamilyId=749BD760-F404-4D45-9AC0-D7F1B3ED1053&displaylang=en

Posted: Feb 22 2008, 10:53 AM by Maxi Accotto | with no comments
Filed under:
Virtual Earth y SQL Server 2008

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

 

Posted: Nov 27 2007, 10:02 AM by Maxi Accotto | with 1 comment(s)
Filed under:
Sql Dinamico y seguridad en SQL2005

 

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 AS

SELECT * 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 FEDERICO

GRANT 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!

Posted: Nov 08 2007, 08:56 PM by Maxi Accotto | with 1 comment(s)
Filed under:
Material de la charla en el MUG sobre programacion en SQLServer2005

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

Como crear el tipo de datos Hora en SQLServer 2005

 

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.

 

Posted: Aug 12 2007, 10:34 PM by Maxi Accotto | with no comments
Filed under:
More Posts Next page »