Simular TRUNC en SQL Server.pdf

download Simular TRUNC en SQL Server.pdf

of 26

Transcript of Simular TRUNC en SQL Server.pdf

  • Simular TRUNC en SQL Server

    TRUNC es una funcion de PL/SQL muy popular en el mundo ORACLE, recibe una fecha con hora y la devuelve truncada a las 00:00:00.

    Es decir si se pasa como parametro 25/08/2009 19:45:23 devuelve 25/08/2009 00:00:00.

    Personalmente me parece una funcin extremadamente util - por ejemplo para obtener los registros correspondientes a un dia concreto o rango de fechas- , pero que inexplicablemente SQL Server no incorpora de forma nativa esta funcin, aunque el problema se mitiga gracias a los nuevos tipos de datos de SQL Server 2008 para fechas.

    Por supuesto no es dificil obtener realizar este tipo de consultas. Normalmente los programadores recurren a las funciones CAST y CONVERT para realizar este tipo de consultas. Esta forma de trabajar es propensa a errores, sobre todo de localizacion, por no hablar de aquellos que confian en la coversion implicita ... Yo siempre recomiendo trabajar de forma tipada - es decir con variables datetime.

    Para conseguir el mismo resultado que nos da la funcion TRUNC podemos utilizar este simple script, donde convertimos la fecha - en este caso getdate() - al tipo varchar(10) para truncar el resto de caracteres (el tiempo), posteriormente volvemos a convertir a fecha para obtener una nueva fecha.

    DECLARE @fecha datetime,

    @fechastring varchar(10)

    SET @fechastring = CONVERT(varchar(10),getdate(), 103)

    SET @fecha = CONVERT(datetime, @fechastring, 103)

    SELECT @fecha

    SELECT []

    FROM []

    WHERE [] > @fecha

    Es importante que el formato sea el mismo es la dos conversiones, a varchar y a datetime.

    Pero como hemos dicho la recomendacion es trabajar siempre de forma tipada, por lo que podemes crear una funcion UDF.

  • CREATE FUNCTION dbo.trunc (@input datetime)

    RETURNS datetime

    AS

    BEGIN

    DECLARE @fecha datetime,

    @fechastring varchar(10)

    SET @fechastring = CONVERT(varchar(10),@input, 103)

    SET @fecha = CONVERT(datetime, @fechastring, 103)

    RETURN @fecha

    END

    Este sencillo ejemplo muestra como podramos utilizar la funcion.

    CREATE TABLE FechaSample

    (

    id int IDENTITY PRIMARY KEY,

    fecha_alta datetime

    )

    go

    DECLARE @i int,

    @j int ,

    @f datetime

    SET @j = 0

  • WHILE (@j < 10)

    BEGIN

    SET @f = getdate() + @j

    SET @i = 0

    WHILE (@i < 10)

    BEGIN

    INSERT INTO Fechasample

    ( fecha_alta)

    VALUES

    (@f)

    SET @i = @i + 1

    END

    SET @j = @j + 1

    END

    go

    SELECT * FROM fechaSample

    WHERE

    fecha_alta between dbo.trunc(getdate())

    AND dbo.trunc(getdate() + 1)

    Obtendriamos el siguiente resultado:

    id fecha_alta

    --- -----------------------

    1 2009-10-06 12:21:45.587

    2 2009-10-06 12:21:45.587

  • 3 2009-10-06 12:21:45.587

    4 2009-10-06 12:21:45.587

    5 2009-10-06 12:21:45.587

    6 2009-10-06 12:21:45.587

    7 2009-10-06 12:21:45.587

    8 2009-10-06 12:21:45.587

    9 2009-10-06 12:21:45.587

    10 2009-10-06 12:21:45.587

    (10 row(s) affected)

    Como siempre, cuando realizamos conversiones en una base de datos hay que tener en cuenta que si convertimos una columna, no solo tendremos un coste adicional de procesamiento sino que ademas dejaremos de utilizar los indices que pudieran estar definidos para el campo. Tenedlo en cuenta.

    Cmo insertar un fichero .xml en un campo de tipo XML de SQL server 2005 o 2008

    A peticin de unos alumnos aqui teneis el cdido de cmo insertar directamente un fichero

    XML en un campo de tipo XML con .net aunque sea con un poco de retraso:

    En C#

    using (SqlConnection Conn = new SqlConnection(ConnectionString))

    {

    Conn.Open();

    using (SqlCommand Cmd = Conn.CreateCommand())

    {

    Cmd.CommandText = @"INSERT INTO TablaConXML

    VALUES

    (4,'CampoNoXML',@punteroaCampoXML)";

  • SqlXml ParametroSQLXML

    = new SqlXml(new XmlTextReader("Fichero.Xml"));

    Cmd.Parameters.AddWithValue("@punteroaCampoXML"

    , ParametroSQLXML);

    Cmd.ExecuteNonQuery();

    }

    }

    En VB.Net

    Using Conn As SqlConnection = New SqlConnection(ConnectionString)

    Conn.Open()

    Using Cmd As SqlCommand = Conn.CreateCommand()

    Cmd.CommandText = "INSERT INTO TablaConXML

    VALUES

    (4,'CampoNoXML',@punteroaCampoXML)"

    Dim ParametroSQLXML As SqlXml

    = New SqlXml(New XmlTextReader("Fichero.Xml"))

    Cmd.Parameters.AddWithValue("@punteroaCampoXML", ParametroSQLXML)

    Cmd.ExecuteNonQuery()

    End Using

    End Using

    SET IDENTITY_INSERT ON y LinqToSQL

    Recientemente he tenido que realizar un pequeo programa para migrar datos. Leia de una base de datos y grababa en otra, ambas SQL Server 2000.

    Me decid a usar VS 2008 y LinqToSQL por la rapidez en el desarrollo. No poda usar Integration Services ni DTS (o al menos no me parecio practico), ya que entre otras cosas tenia que crear y tratar documentos.

  • El caso es que a la hora de realizar inserciones en la tabla de destino, la clave primaria estaba definida como identity - pero necesitaba insertar los mismos valores en ambas bases de datos - y me encontraba con el siguiente error.

    Explicit value must be specified for identity column in table 'DatosIdentity' when IDENTITY_INSERT is set to ON.

    La solucion es sencilla, pero seguro que a mas de uno le ahorro un dolor de cabeza. Sencillamente debemos habilitar la insercin en los campos de identidad a travs de la instruccion SET IDENTITY_INSERT. Pero al estar utilizando Linq To SQL la cosa cambia entre SQL Server 2005 y SQL 2000. Mientras que SQL Server 2005 todo funciona bien a la primera, en SQL Server 2000 se produce el error anterior.

    Vamos a crear un tabla con un campo identity y un proyecto con Visual Studio 2008 en el que grabamos datos en dicha tabla.

    Primero creamos la tabla sobre SQL Server 2005.

    CREATE TABLE DatosIdentity

    (

    Id int IDENTITY NOT NULL,

    Dato varchar(50) NULL,

    Fecha datetime NULL

    CONSTRAINT PK_DatosIdentity PRIMARY KEY (Id)

    )

    En nuestro proyecto de VS 2008 aadimos un formulario y modelo de datos de LinqToSQLClasses (ver como) conectado a SQL Server 2005.

    En el formulario programos un botn de la siguiente manera:

    using (DataClasses1DataContext ctx = new DataClasses1DataContext())

    {

    ctx.ExecuteCommand("SET IDENTITY_INSERT DatosIdentity ON");

    DatosIdentity datos = new DatosIdentity();

  • datos.Id = 1;

    datos.Dato = "Devjoker.com";

    datos.Fecha = DateTime.Now;

    ctx.DatosIdentities.InsertOnSubmit(datos);

    ctx.SubmitChanges();

    ctx.ExecuteCommand("SET IDENTITY_INSERT DatosIdentity OFF");

    }

    En SQL Server 2005 funcionara bien, pero si cambiamos la cadena de conexion y empezamos a trabajar con con SQL Server 2000 obtendremos el sguiente error:

    Explicit value must be specified for identity column in table 'DatosIdentity' when IDENTITY_INSERT is set to ON.

    La solucion es cambiar los decoradores de la propieda identity y cambiar el atributo IsDbGenerated=false.

    [Column(Storage="_Id", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true,

    IsDbGenerated=false)]

    public int Id

    {

    get

    {

    return this._Id;

    }

    set

    {

    if ((this._Id != value))

    {

  • this.OnIdChanging(value);

    this.SendPropertyChanging();

    this._Id = value;

    this.SendPropertyChanged("Id");

    this.OnIdChanged();

    }

    }

    }

    Con esto funcionar.

    El porque no lo s, cosas que tienen las nuevas tecnologas.

    LinQ To SQL - Un ejemplo sencillo

    En este articulo vamos a intentar explicar como funciona Linq To SQL. Para empezar diremos que Linq To Sql es un ORM ligero para bases de datos, con soporte para Linq integrado, disponible en la nueva versin de .NET framework.

    Para quienes no sepan que es un ORM diremos que es un mapeo entre objetos de bases de datos y objetos de un lengunaje de programacin, en nuestro caso sern clases de C#. Es decir, a cada objeto de la base de datos le corresponde un objeto (Clase) de C#.

    Lo primero que vamos a necesitar para desarrollar nuestro ejemplo es una pequea base de datos de SQL Server 2005, que crearemos ejecutando el siguiente script.

    CREATE DATABASE LINQDB

    GO

    USE LINQDB

    GO

    if exists (

    select 1

    from sysobjects

  • where id = object_id('Perfiles')

    and type = 'U'

    )

    drop table Perfiles

    go

    if exists (

    select 1

    from sysobjects

    where id = object_id('Usuarios')

    and type = 'U'

    )

    drop table Usuarios

    go

    if exists (

    select 1

    from sysobjects

    where id = object_id('UsuariosPerfiles')

    and type = 'U'

    )

    drop table UsuariosPerfiles

    go

    /*==============================================================*/

    /* Table: Perfiles */

    /*==============================================================*/

  • create table Perfiles

    (

    CodPerfil int identity,

    Perfil varchar(100) not null,

    Activo char(1) not null

    constraint CKC_ACTIVO_PERFILES

    check (Activo in ('S','N')),

    constraint PK_PERFILES

    primary key (CodPerfil)

    )

    go

    /*==============================================================*/

    /* Table: Usuarios */

    /*==============================================================*/

    create table Usuarios

    (

    CodUsuario int identity,

    Usuario varchar(255) not null,

    Clave varchar(255) not null,

    FxAlta datetime not null,

    Activo char(1) not null

    constraint CKC_ACTIVO_USUARIOS

    check (Activo in ('S','N')),

    constraint PK_USUARIOS

    primary key (CodUsuario)

    )

  • go

    /*==============================================================*/

    /* Table: UsuariosPerfiles */

    /*==============================================================*/

    create table UsuariosPerfiles

    (

    CodUsuario int not null,

    CodPerfil int not null,

    constraint PK_USUARIOSPERFILES

    primary key (CodUsuario, CodPerfil)

    )

    go

    alter table UsuariosPerfiles

    add constraint FK_USUARIOS_REFERENCE_USUARIOS

    foreign key (CodUsuario)

    references Usuarios (CodUsuario)

    go

    alter table UsuariosPerfiles

    add constraint FK_USUARIOS_REFERENCE_PERFILES

    foreign key (CodPerfil)

    references Perfiles (CodPerfil)

    go

    La base de datos es muy sencilla y contiene solo tres tablas:

    Usuarios - Que contiene la lista de usuarios. Perfiles - Que contiene la lista de perfiles.

    UsuariosPerfiles - Que define los perfiles asignados a cada usuario.

  • En realidad en el articulo solo utilizaremos la tabla de usuarios, pero incluimos tres para demostrar como el asistente de Visual Studio genera multiples clases con Linq To Sql.

    Abrimos nuestro Visual Studio 2008 y creamos un nuevo proyecto de tipo Win Forms - es solo la eleccin que yo he hecho, pero podemos usar Linq To Sql en cualquier proyecto!.

    En primer lugar, seleccionamos aadir nuevo elemento y seleccionamos Linq To Sql Classes, llamaremos al archivo LinqDb.dbml.

    [ Ampliar Imagen]

    Con esta accin debemos tener tres nuevos archivos en el proyecto.

    LinqDb.dbml - es archivo xml que define el mapeo. LinqDb.dbml.layout - es un archivo xml que utiliza el diseador.

    LinqDb.designer.cs. - archivo de C# donde se definen las clases y el contexto (la conexin).

    [ Ampliar Imagen]

  • Lo siguiente que debemos hacer es definir una conexin a nuestra base de datos. Abrimos el explorador de servidores y aadimos una nueva conexin.

    [ Ampliar Imagen]

    Probamos la conexin y guardamos.

    Lo siguiente que vamos a hacer es generar las clases de mapeo con la base de datos. En el explorador del proyecto hacemos doble click en el archivo Linqdb.dbml - con lo que Visual Studio abrir el diseador grfico.

    En la ventana del explorador de servidores, seleccionamos la base de datos que hemos creado al inicio del articulo y desplegamos las tablas.

  • [ Ampliar Imagen]

    Seleccionamos las tablas que queramos mapear a C# y arrastramos sobre el diseador. Al arrastrar generamos la clases de mapeo - clases que representan los objetos de base de datos.

    [ Ampliar Imagen]

    Visula Studio nos preguntar si queremos almacenar la informacin de la conexin a la base de datos en el archivo de configuracin de la aplicacin.

    Una vez hecho esto, podemos examinar el visor de clases, y veremos que se han aadido las siguientes clases a nuestro proyecto:

    Una clase DataContext - Que representa el contexto de la conexin.

  • Una clase por cada tabla que hayamos seleccionado. Cada clase tendr una propiedad por cada campo de la tabla a la que representa.

    [ Ampliar Imagen]

    En nuestro proyecto se han creado las siguientes clases:

    LinqDbDataContext Usuarios Perfiles

    UsuariosPerfiles

    La explicacin de estas clases la abordaremos es futuros articulos, pero es importante destacar que si queremos hacer algn cambio en las clase generadas debemos hacerlo en un archivo aparte, aprobechando que son clases parciales. De esta forma podremos regenerar el ORM en cualquier momento sin afectar a nuestro cdigo.

    De momento, Visual Studio ha realizado todo el trabajo por nosostros.

    Con todo esto aadimos un formulario a nuestro proyecto, en el colocamos tres campos para grabar datos en la tabla de usuarios - el nombre de usuario, la clave de acceso y la fecha de alta.

  • [ Ampliar Imagen]

    Escribimos la siguiente clase LinqTest, que dar de alta y buscar usuarios. Tiene los siguientes mtodos:

    AddUsuario - que dar de alta un usuario en la base de datos

    QueryUsuarioByCodigo - que utilizar Linq para recuperar los datos de un usuario.

    partial class LinqTest

    {

    LinqDbDataContext dc = new LinqDbDataContext();

    const char SI = 'S';

    internal void AddUsuario(string nombre, string clave,

    DateTime fecha)

    {

    Usuarios usuario = new Usuarios();

    usuario.Usuario1 = nombre;

    usuario.Clave = clave;

    usuario.Activo = SI;

    usuario.FxAlta = fecha;

    dc.Usuarios.InsertOnSubmit(usuario);

    dc.SubmitChanges();

    //Probamos si recupera bien los identitys

  • MessageBox.Show(

    String.Format("Se ha dado de alta el usuario {0} con el cdigo {1}",

    nombre,

    usuario.CodUsuario.ToString())

    );

    }

    internal Usuarios QueryUsuarioByCodigo(int codigo)

    {

    var usu = from u in dc.Usuarios

    where u.CodUsuario == codigo

    select u;

    if (usu.Count() > 0)

    return usu.First();

    else

    return null;

    }

    }

    Y ahora el cdigo del formulario para consumir la clase, tambin muy sencillo:

    public partial class Form1 : Form

    {

    public Form1()

    {

  • InitializeComponent();

    }

    private void cmdAlta_Click(object sender, EventArgs e)

    {

    // Damos de alta el registro e inicializamos

    LinqTest obj = new LinqTest();

    obj.AddUsuario( txtUsuario.Text ,

    txtClave.Text,

    dtpFxAlta.Value);

    PonerDatosEnPantalla(null);

    }

    private void cmdBuscar_Click(object sender, EventArgs e)

    {

    // Buscamos y ponemos los datos en pantalla.

    LinqTest obj = new LinqTest();

    frmBuscar f = new frmBuscar();

    f.ShowDialog();

    int codUsuario = f.ReturnValue;

    Usuarios u = obj.QueryUsuarioByCodigo(codUsuario);

    PonerDatosEnPantalla(u);

    }

    void PonerDatosEnPantalla(Usuarios u)

    {

  • if (u != null)

    { // Ponemos los datos

    txtUsuario.Text = u.Usuario1;

    txtClave.Text = u.Clave;

    dtpFxAlta.Value = u.FxAlta;

    }

    else

    { // Inicializamos el formulario

    txtUsuario.Text = String.Empty ;

    txtClave.Text = String.Empty;

    dtpFxAlta.Value = DateTime.Now;

    }

    }

    }

    El cdigo es muy sencillo, pero lo explicamos brevemente:

    Boton de alta (cmdAlta_Click) - crea una instancia de LinqTest y le pasa los valores del formulario para que d de alta un registro.

    Botn de bsqueda (cmdBuscar_Click) - pide al usuario un cdigo y utilizando una instancia de LinqTest consulta los datos para mostrarlos en pantalla.

    Como podemos ver el cdigo se ayuda un formulario de busqueda, tambien muy sencillo.

    [ Ampliar Imagen]

    Por si alguien quiere el cdigo del formulario de busqueda ...

  • public partial class frmBuscar : Form

    {

    public frmBuscar()

    {

    InitializeComponent();

    }

    private void cmdBuscar_Click(object sender, EventArgs e)

    {

    this.Close();

    }

    public int ReturnValue

    {

    get { int i = 0;

    int.TryParse(txtCodigo.Text,out i);

    return i;

    }

    }

    }

    Pues bien, con esto terminamos. Saludos y hasta la proxima. DJK.

    Formatear nmeros en SQL Server

    Una duda que suele tener la gente es como formatear los datos numrico es SQL Server - Es bastante comn tener que formatear un nmero completando con ceros hasta un nmero determinado de posiciones.

    Podemos hacer un bucle para concatenar la expresion resultante, pero no parece ni mucho menos la opcin mas adecuada.

  • El siguiente script ilustra una forma ms eficiente de conseguir el mismo resultado.

    DECLARE @num int,

    @formato varchar(10),

    @expresion varchar(10)

    SET @formato = '0000000000'

    SET @num = 2002

    SET @expresion = RIGHT( @formato + cast(@num AS varchar), 10)

    SELECT @expresion

    La idea es concatenar una expresion de formato fijo - los ceros - con el nmero a formatear. Posteriormente utilizamos la funcin RIGHT para recortar los n elementos de la derecha.

    La salida de la ejecucion es la siguiente:

    DATO_FORMATEADO

    ---------------

    0000002002

    Como podemos ver, hemos conseguido nuestro objetivo. Por supuesto, este truco es vlido tambin para completar con espacios en blanco - muy util para obtener ficheros de ancho fijo - o cualquier otro tipo de caracter.

    Podemos hacer las cosas un poco mejor, y creamos una funcion a la que le pasaremos el nmero a formatear y el formato de salida.

    CREATE FUNCTION dbo.FormatNum

    (

    @number int,

  • @format varchar(50)

    )

    RETURNS varchar(50)

    AS

    BEGIN

    DECLARE @len int

    SET @len = len(@format)

    RETURN RIGHT( @format + cast(@number AS varchar), @len)

    END

    Sobre una supuesta tabla Pais, que hemos cargado previamente con cinco registros - con el cdigo autonumrico - ejecutamos la siguiente consulta:

    SELECT

    dbo.FormatNum(CodPais,'000000000000') AS CodPais,

    NombrePais,

    Activo

    FROM

    Pais

    El resultado de la consulta es el siguiente:

    CodPais NombrePais Activo

    ----------------- -------------- ------

    000000000001 Pais 0 0

    000000000002 Pais 1 0

    000000000003 Pais 2 0

    000000000004 Pais 3 0

    000000000005 Pais 4 0

    Como leer un campo XML de SQL Server 2005 con C#

    El soporte nativo a XML de SQL Server 2005 es una de las caracteristicas ms interesantes

    de la verisn. Cuando empezamos a trabajar con Xml nos encontramos con el problema de

  • como leer los datos guardados en formato Xml de la base de datos, es decir, como obtener una

    instancia de XmlDocument a partir de una consulta a la base de datos.

    Para este ejemplo, lo primero que vamos a necesitar es una tabla con un campo de

    tipo Xml, para ello ejecutamos el siguiente script en nuestra base de datos.

    CREATE TABLE TABLA_CON_XML

    (

    Id int identity not null,

    CampoXml Xml,

    CONSTRAINT PK_TABLA_CON_XML PRIMARY KEY (Id)

    )

    Para grabar datos en la tabla anterior necesitamos una segunda tabla con el objetivo de realizar una consulta posterior con la clausula FOR XML de SQL Server 2005. La creamos con el siguiente script:

    CREATE TABLE INFO

    (

    Id int identity not null,

    DbName varchar(100),

    UserName varchar(100),

    FxAlta datetime,

    CONSTRAINT PK_INFO PRIMARY KEY (Id)

    )

    GO

    INSERT INTO INFO

    SELECT DB_NAME(), USER_NAME(), GETDATE()

    Con esto, grabamos un registro en nuestra tabla utilizando para ello una consulta con la clausula FOR XML.

  • DECLARE @Xml Xml

    set @Xml = (SELECT * FROM INFO

    FOR XML AUTO, elements)

    INSERT INTO TABLA_CON_XML

    (CampoXml) VALUES (@Xml)

    GO

    SELECT * FROM TABLA_CON_XML

    La consulta debe haber generado un Xml como el que se muestra a continuacin.

    1

    PRUEBAS

    dbo

    2007-08-13T11:07:11.077

    Ahora ya podemos escribir el cdigo C# necesario para acceder a la base de datos y recuperar el Xml.

    Lo primero que debemos hacer es importar los namespaces necesarios para trabajar con SQL Server y Xml.

    using System.Data.SqlClient;

    using System.Data.SqlTypes;

    using System.Xml;

    Para el ejemplo, hemos realizado una sencilla aplicacin Windows en la que a travs de una botn leemos el Xml de la base de datos y lo guardamos en disco.

  • private void button1_Click(object sender, EventArgs e)

    {

    XmlDocument doc = new XmlDocument();

    string qs = @"Data Source=10.74.4.100;

    Initial Catalog=PRUEBAS;

    Persist Security Info=True;

    User ID=sa;Password=xxxxxxxx";

    SqlConnection conexion = new SqlConnection(qs);

    conexion.Open();

    LoadXml(conexion, doc);

    conexion.Close();

    doc.Save("C:\\OutputXml.xml");

    }

    El cdigo anterior, nicamente crea una instancia de XmlDocument y una conexion a nuestra base de datos. Es el mtodo LoadXml el que realmente accede a la base de datos y obtiene el Xml.

    A continuacin se muestra el mtodo LoadXml:

    protected bool LoadXml(SqlConnection cn, XmlDocument doc)

    {

    //Leemos el Xml de la base de datos.

    string sql = @"SELECT Id, CampoXml

    FROM TABLA_CON_XML

    WHERE Id = @Id";

    SqlCommand cm = new SqlCommand(sql,cn);

    cm.Parameters.Add(new SqlParameter("@Id",1));

  • using (SqlDataReader dr = cm.ExecuteReader())

    {

    if (dr.Read())

    {

    SqlXml mixml = dr.GetSqlXml(dr.GetOrdinal("CampoXml"));

    doc.LoadXml( mixml.Value);

    return true;

    }

    else

    {

    return false;

    }

    }

    }

    Este mtodo consulta la base de datos con un objeto SqlDataReader y almacena el resultado en una variable del tipo SqlXml(perteneciente al namespace System.Data.SqlTypes). Con este tan solo queda cargar nuestra instancia de XmlDocument con el valor del Xml leido de la base de datos a travs de LoadXml.

    Como podemos ver, trabajar con C#, Xml y SQL Server 2005 es bastante sencillo.