martes, 27 de diciembre de 2011

Separar cadena delimitada por comas en SQL Server

Vista previa
Vista Previa
Requisito
SQL Server 2008 0 Superior


Objectivo General
Crear una consulta que divide una cadena delimitada por comas en diferente valores

USE [Test]]
GO

IF OBJECT_ID('TempTable', N'U') IS NOT NULL
  DROP TABLE TempTable
GO

CREATE TABLE TempTable
(
 [id]   INT IDENTITY(1,1) NOT NULL,
 [index]   INT,
 [valor]   NVARCHAR(50) NULL,
 [modificado] NVARCHAR(50) NULL,
 [original]  NVARCHAR(50) NULL
)
GO

DECLARE @ORIGINAL NVARCHAR(50);
DECLARE @MODIFICADO NVARCHAR(50);
DECLARE @job  NVARCHAR(50);
DECLARE @INDEX  INT = 0;
DECLARE @VALOR  NVARCHAR(50) = 0;

SELECT @ORIGINAL = COLUMNB FROM dbo.Table_3 WHERE ID = 7; --19,2,3,14,15,16

INSERT INTO TempTable([valor], [modificado], [original]) VALUES (@VALOR, RTRIM(LTRIM(@ORIGINAL)), RTRIM(LTRIM(@ORIGINAL)))

WHILE ((SELECT LEN([modificado]) from TempTable where id = (select MAX(id) from TempTable)) > 0)
 BEGIN
  BEGIN TRY
   SET @job = (SELECT [modificado] from TempTable where id = (select MAX(id) from TempTable))
   SET @VALOR = SUBSTRING(RTRIM(LTRIM(@job)), 1 , CHARINDEX(N',', RTRIM(LTRIM(@job))) - 1);
   SET @INDEX = CHARINDEX(N',', @job);
   SET @MODIFICADO = SUBSTRING(RTRIM(LTRIM(@job)), @INDEX + 1, LEN(RTRIM(LTRIM(@job))));
 
    IF (((SELECT [index] from TempTable where id = (select MAX(id) from TempTable)) = 0))
     BEGIN
      INSERT INTO TempTable([index], [valor], [modificado], [original]) VALUES (@INDEX, RTRIM(LTRIM(@job)), RTRIM(LTRIM(@MODIFICADO)), RTRIM(LTRIM(@ORIGINAL)))
      BREAK
     END

    ELSE
     INSERT INTO TempTable([index], [valor], [modificado], [original]) VALUES (@INDEX, RTRIM(LTRIM(@VALOR)), RTRIM(LTRIM(@MODIFICADO)), RTRIM(LTRIM(@ORIGINAL)))
     CONTINUE
  END TRY
 
  BEGIN CATCH
   INSERT INTO TempTable([index], [valor], [modificado], [original]) VALUES (@INDEX, RTRIM(LTRIM(@job)), 0, RTRIM(LTRIM(@ORIGINAL)))
   BREAK
  END CATCH;
 END
GO

  

Descripcion
  1. Primero verificamos que no exista una tabla que usaremos como tabla temporal Dentro de la base de datos, de existir procedemos a borrarla
     IF OBJECT_ID('TempTable', N'U') IS NOT NULL
    DROP TABLE TempTable
    GO
  2. Creamos nuestra tabla de datos el cual usaremos como tabla temporal.
    CREATE TABLE TempTable
    (
     [id]    INT IDENTITY(1,1) NOT NULL,
     [index de la coma anterior] INT,
     [valor]    NVARCHAR(50) NULL,
     [modificado]   NVARCHAR(50) NULL,
         [original]   NVARCHAR(50) NULL
    )
    GO
    
  3. Declaramos la variable que serviran de datos para la tabla asi como para poder ir procesando los valores.
     DECLARE @ORIGINAL NVARCHAR(50);
    DECLARE @MODIFICADO  NVARCHAR(50);
    DECLARE @job  NVARCHAR(50);
    DECLARE @INDEX  INT = 0;
    DECLARE @VALOR  NVARCHAR(50) = 0;
  4. Extraemos el valor original de la base de datos.
    SELECT @ORIGINAL = COLUMNB FROM dbo.Table_3 WHERE ID = 7;
    
  5. Insertamos el valor original a la tabla temporal. Note que la columna [original] como la columna [modificado] tendran el mismo valor y  la columna [valor] tendra un valor inicial de cero(0) para la primera fila.
     INSERT INTO TempTable([valor], [modificado], [original]) VALUES (@VALOR, RTRIM(LTRIM(@ORIGINAL)), RTRIM(LTRIM(@ORIGINAL))) 
  6. Utilizamos la palabra reservada de control de flujo WHILE, para establecer una condición para la ejecución repetida de una instrucción o bloque de instrucciones SQL. Las instrucciones se ejecutan repetidamente siempre que la condición especificada sea verdadera. Se puede controlar la ejecución de instrucciones en el bucle WHILE con las palabras clave BREAK y CONTINUE.
    WHILE ((SELECT LEN([modificado]) from TempTable where id = (select MAX(id) from TempTable)) > 0)
     BEGIN
      BEGIN TRY
       SET @job = (SELECT [modificado] from TempTable where id = (select MAX(id) from TempTable))
       SET @VALOR = SUBSTRING(RTRIM(LTRIM(@job)), 1 , CHARINDEX(N',', RTRIM(LTRIM(@job))) - 1);
       SET @INDEX = CHARINDEX(N',', @job);
       SET @MODIFICADO = SUBSTRING(RTRIM(LTRIM(@job)), @INDEX + 1, LEN(RTRIM(LTRIM(@job))));
     
        IF (((SELECT [index de la coma anterior] from TempTable where id = (select MAX(id) from TempTable)) = 0))
         BEGIN
          INSERT INTO TempTable([index de la coma anterior], [valor], [modificado], [original]) VALUES (@INDEX, RTRIM(LTRIM(@job)), RTRIM(LTRIM(@MODIFICADO)), RTRIM(LTRIM(@ORIGINAL)))
          BREAK
         END
    
        ELSE
         INSERT INTO TempTable([index de la coma anterior], [valor], [modificado], [original]) VALUES (@INDEX, RTRIM(LTRIM(@VALOR)), RTRIM(LTRIM(@MODIFICADO)), RTRIM(LTRIM(@ORIGINAL)))
         CONTINUE
      END TRY
     
      BEGIN CATCH
       INSERT INTO TempTable([index de la coma anterior], [valor], [modificado], [original]) VALUES (@INDEX, RTRIM(LTRIM(@job)), 0, RTRIM(LTRIM(@ORIGINAL)))
       BREAK
      END CATCH;
     END
    GO
    
  7. Encerramos el bloque de relevancia dentro de una instrucion TRY....CATCH para Implementa un mecanismo de control de errores para el procedimiento.
     BEGIN TRY
     SET @job = (SELECT [modificado] from TempTable where id = (select MAX(id) from TempTable))
     SET @VALOR = SUBSTRING(RTRIM(LTRIM(@job)), 1 , CHARINDEX(N',', RTRIM(LTRIM(@job))) - 1);
     SET @INDEX = CHARINDEX(N',', @job);
     SET @MODIFICADO = SUBSTRING(RTRIM(LTRIM(@job)), @INDEX + 1, LEN(RTRIM(LTRIM(@job))));
     
      IF (((SELECT [index de la coma anterior] from TempTable where id = (select MAX(id) from TempTable)) = 0))
       BEGIN
        INSERT INTO TempTable([index de la coma anterior], [valor], [modificado], [original]) VALUES (@INDEX, RTRIM(LTRIM(@job)), RTRIM(LTRIM(@MODIFICADO)), RTRIM(LTRIM(@ORIGINAL)))
        BREAK
       END
    
      ELSE
       INSERT INTO TempTable([index de la coma anterior], [valor], [modificado], [original]) VALUES (@INDEX, RTRIM(LTRIM(@VALOR)), RTRIM(LTRIM(@MODIFICADO)), RTRIM(LTRIM(@ORIGINAL)))
       CONTINUE
    END TRY
    
  8. Dentro del bloque TRY....CATCH seteamos los valores de las variables, para poder trabajarle, en caso de producirse algun error en el codigo la instrucion pasara al bloque CATCH donde trataremos los errores. Notese el uso de las funciones:
    para poder lograr nuestro cometido.
     SET @job = (SELECT [modificado] from TempTable where id = (select MAX(id) from TempTable))
    SET @VALOR = SUBSTRING(RTRIM(LTRIM(@job)), 1 , CHARINDEX(N',', RTRIM(LTRIM(@job))) - 1);
    SET @INDEX = CHARINDEX(N',', @job);
    SET @MODIFICADO = SUBSTRING(RTRIM(LTRIM(@job)), @INDEX + 1, LEN(RTRIM(LTRIM(@job))));
  9. Luego una instrucion IF...ELSE determina que el valor de la columna [index de la coma anterior] de la tabla temporar cumpla con la condicion , en caso contrario se procesa en el bloque ELSE.
     IF (((SELECT [index de la coma anterior] from TempTable where id = (select MAX(id) from TempTable)) = 0))
     BEGIN
      INSERT INTO TempTable([index de la coma anterior], [valor], [modificado], [original]) VALUES (@INDEX, RTRIM(LTRIM(@job)), RTRIM(LTRIM(@MODIFICADO)), RTRIM(LTRIM(@ORIGINAL)))
      BREAK
     END
    
     ELSE
      INSERT INTO TempTable([index de la coma anterior], [valor], [modificado], [original]) VALUES (@INDEX, RTRIM(LTRIM(@VALOR)), RTRIM(LTRIM(@MODIFICADO)), RTRIM(LTRIM(@ORIGINAL)))
      CONTINUE
    
  10. Por ultimo tenemos el bloque CATCH donde capturamos cualquier anomalia que se encuentre en el codigo.
     BEGIN CATCH
     INSERT INTO TempTable([index de la coma anterior], [valor], [modificado], [original]) VALUES (@INDEX, RTRIM(LTRIM(@job)), 0, RTRIM(LTRIM(@ORIGINAL)))
     BREAK
    END CATCH;
    

lunes, 22 de agosto de 2011

Metodo Generico

Objectivo
Crear un metodo que no este sujeto a un query especifico y que retorne un control poblado de datos.
Requisito:
  • Visual Estudio 2008 o Superior
  • Agregar una conexion a la base de datos con la que quiere interactual, para este ejemplo se utilizo la base de datos AdventureWorks descargada del site codeplex
  • Agregar un label al formulario para reflejar el valor selecciona del control
Nota:
  • Para realizar esta practica agregue u projecto tipo web y pegue este codigo en el codebehind de la pagina Default.aspx
  • Este codigo trabaja bien con controles de lista de aplicaciones web y aplicaciones winform, tales como:
  1. CheckBoxList
  2. DropDownList
  3. ListBox y
  4. RadioButtonList


Codigo C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Reflection;
using System.Globalization;
using System.ComponentModel;

public partial class _Default : System.Web.UI.Page
{
    #region Evento Load de la pagina
    protected void Page_Load(object sender, EventArgs e)
    {
        AdventureWorks_DataModel.AdventureWorks_DataEntities db = new AdventureWorks_DataModel.AdventureWorks_DataEntities();

        form1.Controls.Add(ControlBuilder<AdventureWorks_DataModel.Product, DropDownList>(db.Products.Select(dd => dd).ToList(), "ProductID", "Name", "dd1", "hola"));
    }
    #endregion Evento Load de la pagina

    #region Generic Method
    private static U ControlBuilder<T, U>(List<T> row, string datavaluefieldname, string datatextfieldname, string id, string Etiqueta)
        where T : class
        where U : class
    {
        Type ControlToReturnType = typeof(U);

        DropDownList ControlToReturn = (DropDownList)ControlToReturnType.InvokeMember
            (
            null,
            BindingFlags.Public |
            BindingFlags.Instance |
            BindingFlags.CreateInstance,
            null,
            null,
            null
            );
        
        ControlToReturn.ID = id;
        ControlToReturn.AutoPostBack = true;
        ControlToReturn.CausesValidation = false;
        ControlToReturn.Width = System.Web.UI.WebControls.Unit.Percentage(100);
        ControlToReturn.SelectedIndexChanged += new EventHandler(ControlToReturn_SelectedIndexChanged);

        foreach (T item in row)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(item);

            System.ComponentModel.PropertyDescriptor MyProperty1 = properties.Find(datatextfieldname, false);
            System.ComponentModel.PropertyDescriptor MyProperty2 = properties.Find(datavaluefieldname, false);

            ListItem li = new ListItem();

            li.Text = MyProperty1.GetValue(item).ToString();
            li.Value = MyProperty2.GetValue(item).ToString();
            ControlToReturn.Items.Add(li);
        }

        ListItem EtiquetaApertura = new ListItem();
        EtiquetaApertura.Enabled = true;
        EtiquetaApertura.Selected = true;
        EtiquetaApertura.Value = null;
        EtiquetaApertura.Text = Etiqueta;
        ControlToReturn.Items.Insert(0, EtiquetaApertura);

        return ControlToReturn as U;
    }
    #endregion GenericMethod

    #region Manejador de evento para el control
    static void ControlToReturn_SelectedIndexChanged(object sender, EventArgs e)
    {
        DropDownList dl = (DropDownList)sender;
        Page page = (Page)dl.NamingContainer;

        Label lb = (Label)page.FindControl("lb1");

        lb.Text = dl.SelectedValue;

    }
    #endregion Manejador de evento para el control
}
            
Descripcion:
  1. En el evento load de la pagina agregamos el contexto de la base de datos que utilizaremos para realizar el query de los campos que queremos que se vizualicen en el control
    protected void Page_Load(object sender, EventArgs e)
        {
            AdventureWorks_DataModel.AdventureWorks_DataEntities db = new AdventureWorks_DataModel.AdventureWorks_DataEntities();
        }
  2. Agregamos el control a la coleccion de controles del formulario de la pagina invocando el metodo y pasandoles los argumentos requerido. Al metodo les estamo diciendo:
    • Que como parametro de entrada utilizaremos el tipo Product.
    • Que como parametro de salida nos retorne un DropDownList para visualizacion los datos.
    Y como argumento les estamos pasando:
    • Query que retorna una lista con todas las filas de la base de datos.
    • "ProductID", como el valor del control ListItems.
    • "Name", como el valor a mostrar en el control ListItems.
    • "dd1", como el ID del control.
    • "Seleccione una Opcion", como Etiqueta del control.
    protected void Page_Load(object sender, EventArgs e)
        {
            form1.Controls.Add(ControlBuilder<AdventureWorks_DataModel.Product, DropDownList>(db.Products.Select(dd => dd).ToList(), "ProductID", "Name", "dd1", "Seleccione una Opcion"));
        }
  3. Procedemos a la declaracion del metodo que contiene la siguiente firma
    • Un parametro Generico de entrada de Tipo T.
    • Un parametro Generico de salida de Tipo U.
    Y como Parametros Formales:
    • Una lista de tipo T.
    • Un valor para el control ListItems.
    • Un valor para mostrar en el control ListItems.
    • El ID del control.
    • Una etiqueta de apertura.
    • Y por ultimo restringimos el argumento de tipo a tipo de referencia
    private static U ControlBuilder<T, U>(List<T> row, string datavaluefieldname, string datatextfieldname, string id, string Encabezado)
            where T : class
            where U : class
        {
        }
                        
  4. Pasamos a la implementacion del metodo en donde obtenemos el tipo de valor pasado para la creacion de nuestra instancia
  5. Type ControlToReturnType = typeof(U);
  6. Creamos una instancia del tipo suministrado, para crear la instancia invocamos el metodo InvokeMember() del tipo.
  7. DropDownList ControlToReturn = (DropDownList)ControlToReturnType.InvokeMember
                (
                null,
                BindingFlags.Public |
                BindingFlags.Instance |
                BindingFlags.CreateInstance,
                null,
                null,
                null
                );
  8. Seteamos las propiedades e instanciamos nuestro manejador de evento
  9. ControlToReturn.ID = id;
    ControlToReturn.AutoPostBack = true;
    ControlToReturn.CausesValidation = false;
    ControlToReturn.Width = System.Web.UI.WebControls.Unit.Percentage(100);
    ControlToReturn.SelectedIndexChanged += new EventHandler(ControlToReturn_SelectedIndexChanged);
    
  10. Procesamos las filas pasada al metodo mediante el parametro formal "List<T> Rows" para poder almar nuestro control ListItems
  11. foreach (T item in row)
            {
                // Creo una nueva colleccion y le asigno a esta las prepiedades de item.
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(item);
    
                // Seteo el PropertyDescriptor a la propiedad especificada.
                System.ComponentModel.PropertyDescriptor MyProperty1 = properties.Find(datatextfieldname, false);
                System.ComponentModel.PropertyDescriptor MyProperty2 = properties.Find(datavaluefieldname, false);
    
                ListItem li = new ListItem();
    
                li.Text = MyProperty1.GetValue(item).ToString();
                li.Value = MyProperty2.GetValue(item).ToString();
                ControlToReturn.Items.Add(li);
            }
    
            ListItem EtiquetaApertura = new ListItem();
            EtiquetaApertura.Enabled = true;
            EtiquetaApertura.Selected = true;
            EtiquetaApertura.Value = null;
            EtiquetaApertura.Text = Encabezado;
            ControlToReturn.Items.Insert(0, EtiquetaApertura);
    
            return ControlToReturn as U;
        }
    • Descompongo la fila en cada uno de sus columnas o campos mediante TypeDescriptor.GetProperties (Método) y se los agregos a mi coleccion
      PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(item);;;;
      
    • Localizo los campos DataTextField y DataValueField dentro de la coleccion para asignarselo a la propiedad Text y Value respectivamente del control ListItem
    • System.ComponentModel.PropertyDescriptor MyProperty1 = properties.Find(datatextfieldname, falseSystem.ComponentModel.PropertyDescriptor MyProperty2 = properties.Find(datavaluefieldname, false);
      
    •  Creo mi instancia del control ListItem, pasandole el valor de los campos: DataTextField y DataValueField mediante PropertyDescriptor.GetValue (Método), a sus propiedades: Text y Value respectivamente y lo agrego a la coleccion de controles ListItem del control a retornar en el parametro generico del metodo
    • ListItem li = new ListItem();m();
      
      li.Text = MyProperty1.GetValue(item).ToString();
      li.Value = MyProperty2.GetValue(item).ToString();
      
      ControlToReturn.Items.Add(li);
    • Creamos una instancia del control ListItem que servira como etiqueta de apertura del control a retornar seteando sus propiedades y agregando a la posicion cero dentro de la coleccion de controles ListItem del control a retornar en el parametro generico del metodo
    • ListItem EtiquetaApertura = new ListItem();
      EtiquetaApertura.Enabled = true;
      EtiquetaApertura.Selected = true;
      EtiquetaApertura.Value = null;
      EtiquetaApertura.Text = Encabezado;
      ControlToReturn.Items.Insert(0, EtiquetaApertura);
  12. Retornamos el control
  13. return ControlToReturn as U;
  14. Por ultimo implementamos el metodo manejador para el evento OnSelectedIndexChanged del control retornado
  15. static void ControlToReturn_SelectedIndexChanged(object sender, EventArgs e)
        {
            DropDownList dl = (DropDownList)sender;
            Page page = (Page)dl.NamingContainer;
    
            Label lb = (Label)page.FindControl("lb1");
    
            lb.Text = dl.SelectedValue;
    
        }