viernes, 30 de enero de 2015

Traer el último registro de un grupo de registros


Traer el último, primer, etc.  registro de un grupo de registros

En algún momento necesite consultar el último registro ingresado en una tabla detalle por cada registro de la tabla padre, No es traer los primer registro de una consulta “TOP 1”. Para hacerme entender, he creado el siguiente ejemplo:

Tabla maestra “Cliente”
IdCliente
Cliente
1
Homero
2
Barth
3
Lisa
4
Batman
5
Superman

Tabla detalle “Factura”
IdFactura
IdCliente
Valor
Fecha
1
1
50
01/01/2015
2
1
55
01/01/2015
3
1
45
03/01/2015
4
2
38
03/01/2015
5
2
45
03/02/2015
6
3
60
01/02/2015
7
4
54
15/01/2015
8
4
66
03/02/2015
9
5
70
02/02/2015
10
5
62
03/01/2015
11
5
33
02/01/2015

La consulta de todos los datos:
Select Cliente.IdCliente
       ,Cliente.Cliente
       ,Factura.IdFactura
       ,Factura.Fecha
        ,Factura.Valor
From Cliente
     Inner Join Factura On Cliente.IdCliente = Factura.IdCliente

El resultado:
IdCliente
Cliente
IdFactura
Fecha
Valor
1
Homero
1
01/01/2015
50
1
Homero
2
01/01/2015
55
1
Homero
3
03/01/2015
45
2
Barth
4
03/01/2015
38
2
Barth
5
03/02/2015
45
3
Lisa
6
01/02/2015
60
4
Batman
7
15/01/2015
54
4
Batman
8
03/02/2015
66
5
Superman
9
02/02/2015
70
5
Superman
10
03/01/2015
62
5
Superman
11
02/01/2015
33


Ahora necesito extraer la última factura registrada por cada cliente, para eso necesitamos traer el máximo IdFactura por cliente. Para lo anterior utilizo una sub consulta en el Inner Join que me permita traer el máximo IdFactura ingresado por cliente y posteriormente lo incluyo como parte de un segundo Inner Join para poder traer la data como se puede apreciar en la siguiente tabla.

IdCliente
Cliente
IdFactura
Fecha
Valor
1
Homero
3
03/01/2015
45
2
Barth
5
03/02/2015
45
3
Lisa
6
01/02/2015
60
4
Batman
8
03/02/2015
66
5
Superman
11
02/01/2015
33

A continuación la consulta.
Select Cliente.IdCliente
,Cliente.Cliente
,Factura.IdFactura
,Factura.Fecha
       ,Factura.Valor
From Cliente
Inner Join (Select Max(IdFactura) As Id, IdCliente From Factura Group By IdCliente) As Fact On Cliente.IdCliente = Fact.IdCliente
        Inner Join Factura On Cliente.IdCliente = Factura.IdCliente  And Fact.Id = Factura.IdFactura
Order By IdCliente

No es la más eficiente pero es una manera de consultar muy sencilla.

jueves, 29 de enero de 2015

Ordenamiento de columnas en un GridView




Ordenamiento de columnas en un GridView  en Visual Studio .NET

Cuando usamos listas de entidades para ser mostradas en un GridView, éstas no permiten utilizar la opción de ordenamiento automático de columnas AllowSorting=true del GridView porque generan el siguiente error: “El origen de datos 'odsPersonaList' no admite la ordenación con datos IEnumerable. Sólo se admite la ordenación automática con DataView, DataTable y DataSet”.

Durante algún tiempo, intenté utilizar los métodos de ordenamiento de listas en la capa de presentación usando métodos del GridView, sin resultados.

Evitando ampliar la funcionalidad de las listas, opté por usar otro componente que integrara  la ordenación;  para lo cual fue suficiente buscar en internet un método que convirtiera la lista en un DataTable. En el ejemplo publicado a continuación podrán encontrar el código  que permita hacer esto. No sé de quién es originalmente, lo encontré en muchos sitios web, por ello espero que la persona que lo creó, me disculpe al no poder darle el crédito que se merece.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;

/// <summary>
/// Utilidades para aplicar a la capa de entidades de negocio del proyecto
/// </summary>

public class UtilidadesLN
{
                  public UtilidadesLN(){}
    public DataTable ConvertToDataTable<T>(IList<T> data)
    {
        PropertyDescriptorCollection properties =
           TypeDescriptor.GetProperties(typeof(T));
        DataTable table = new DataTable();
        foreach (PropertyDescriptor prop in properties)
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
        foreach (T item in data)
        {
            DataRow row = table.NewRow();
            foreach (PropertyDescriptor prop in properties)
                row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
            table.Rows.Add(row);
        }
        return table;
    }
}

Mediante la funcionalidad anterior convierto una lista en un DataTable para ser expuesta como un objeto de datos en la capa de entidades de negocio.

public class PersonaLN:UtilidadesLN
{
    PersonaAD AD = new PersonaAD();
                  public PersonaLN()     {}

    //Consulta Normal usando una lista generica
    [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, true)]
    public List<PersonaEN> ConsultarPersona(String Buscar)
    {
        return AD.ConsultarPersonas(Buscar);
    }
    [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, false)]
    public DataTable ConsultarPersonaDT(String Buscar)
    {
        return ConvertToDataTable(AD.ConsultarPersonas(Buscar));
    }


}

Ilustración 1
De esta manera puedo utilizar ObjectDataSource en la capa de presentación, para acceder a los datos y poder utilizarlos desde un GridView. En la ilustración 1, podrán ver dos GridView enlazados, el primero con una lista y el segundo a un DataTable.

















El primer GridView enlazado a una Lista, en el momento de pulsar clic sobre el nombre de la columna va a generar el  error 'odsPersonaList' no admite la ordenación con datos IEnumerable. Sólo se admite la ordenación automática con DataView, DataTable y DataSet” (Ver ilustración 2):


Ilustración 2
Mientras que al usar un objeto conectado al método que devuelve un DataTable, el sistema ordenará las columnas de manera automática. A continuación se ve en la ilustración 3, el GridView al consultar los datos:

Ilustración 3
 Después de pulsar clic sobre la columna del nombre, el GridView ordena los datos sin generar error. a continuación en la ilustración 4, se pueden apreciar los datos ordenados descendentemente :

Ilustración 4
Este ejemplo lo realicé con Visual Studio Community  2013,  lenguaje C# y  clases en la carpeta App_code que emulan las capas del proyecto. Lo pueden descargar aquí. El código se entrega tal como está, o sea bajo su propia responsabilidad de uso.

Por favor, no olvides darle un “me gusta” al articulo si este ejemplo te es de utilidad. 

lunes, 5 de enero de 2015

Usando reflection, un ejemplo practico con C Sharp "C#"



Reflection es una herramienta sencilla en su concepto, pero un poco difícil en su manejo. Permite el acceso a objetos (ensamblados, módulos, propiedades, tipos de datos, etc) de manera dinámica.
Para poder comprender un poco más del tema, voy a mostrar el siguiente ejemplo:

Tengo una cantidad “n” de clases que necesito mostrar en formularios web, para evitar escribir por cada atributo de cada clase la sentencia de asignación de la propiedad al control web “txtUsuario.text = objeto.usuario”. Cree una clase con un método en la carpeta App_code de nombre “ManejoControles”, a esta clase le paso como parámetros el objeto y un panel que contiene los controles. Esta clase se encarga de recorrer los controles contenidos en el panel, en caso de ser  alguno de tres predeterminados (Texto, DropDownList y RadioButtonList), se busca por el nombre del control en el objeto el valor y se le asigna al control. (Espero haberme hecho entender).

Como estándar a cada control le asigno una nomenclatura en su nombre, las tres primeras letras corresponden al tipo de control y el resto del nombre corresponde al nombre de la propiedad del objeto. Adicionalmente dejo todos los controles que quiero llenar con los valores del atributo de un objeto dentro de un control panel de asp.net.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Logica;
using Entidades;
using System.Reflection;

        public void ObjetoPanel(Panel Panel, object Objeto)
        {
            String TipoControl;
            String Nombre = String.Empty;

            foreach (Control oControl in Panel.Controls)
            {
                TipoControl = oControl.GetType().ToString();
                switch (TipoControl)
                {
                    case "System.Web.UI.WebControls.TextBox":
                        TextBox oTextBox = (TextBox)oControl;
                        Nombre = oTextBox.ID.Substring(3, oTextBox.ID.Length - 3);
                        oTextBox.Text = Convert.ToString(Objeto.GetType().GetProperty(Nombre).GetValue(Objeto, null));
                        break;
                    case "System.Web.UI.WebControls.DropDownList":
                        DropDownList oLista = (DropDownList)oControl;
                        Nombre = oLista.ID.Substring(3, oLista.ID.Length - 3);
                        oLista.SelectedValue = Convert.ToString(Objeto.GetType().GetProperty(Nombre).GetValue(Objeto, null));
                        break;
                    case "System.Web.UI.WebControls.RadioButtonList":
                        RadioButtonList oRadio = (RadioButtonList)oControl;
                        Nombre = oRadio.ID.Substring(3, oRadio.ID.Length - 3);
                        oRadio.SelectedValue = Convert.ToString(Objeto.GetType().GetProperty(Nombre).GetValue(Objeto, null));
                        break;
                }

            }

        }

Si este ejemplo les es de utilidad, por favor no olvidar dejar un comentario o like.

Para el ejemplo use Web developer express 2013 que se consigue aquí.

jueves, 1 de enero de 2015

SQL server error 102 incorrect syntax near ' '

SQL server error 102 incorrect syntax near ' '

Este es uno de esos errores curiosos, lo vi hace poco cuando intente ejecutar un procedimiento almacenado en el servidor de producción, sin embargo cuando ejecutaba el procedimiento almacenado en mi maquina local, el error no aparecía, después de recordar códigos ASCII, encontré el problema.

Creo que en algún momento alguno de “Notostros”, los desarrolladores copiamos algún ejemplo o código de internet y junto con el ejemplo se ingresó en el procedimiento almacenado un espacio duro en vez de un espacio normal.

Para localizar el error utilice Word la opción de mostrar todo, que muestra los retornos de carro (Tecla enter) y los espacios los muestra como una especie de  punto localizado a la mitad de la altura de las letras “.” Y los espacios duros como un símbolo de grados “°”.

Para solucionar el error basta con copiar el “espacio duro”, llevarlo a la opción de buscar y reemplazar del Word (Ctrl + L: en español) y remplazarlo por un espacio normal.




Visual Studio 2013 No muestra las clases de la carpeta App_Code

Visual Studio 2013 No muestra las clases de la carpeta App_Code.

Hace poco decidí comenzar a hacer mis nuevos desarrollos con Visual Studio 2013, así que al crear mi primer aplicativo web comencé a utilizar algunas utilidades para manejo de controles y me encontré que en el nuevo Visual Studio 2013 para web cuando creas una clase dentro de la carpeta App_Code, a diferencia de con Visual Studio 2010, este no las compila de manera predeterminada, para esto tienes que entrar a las propiedades de la clase mediante clic derecho sobre el nombre de la clase y cambiar la opción de  “Acción de compilación” a compilación.

Con lo anterior podrás invocar las clases realizadas en tu carpeta App_code dentro de tus páginas web.




 Para descargar Visual Studio Web Developer express 2013, lo puedes bajar del siguiente enlace: Visual Studio 2013

lunes, 10 de noviembre de 2014

Optimización de Índices – Desfragmentación


Los índices en una base de datos con el tiempo tienden a fragmentarse, esto contribuye a disminuir el rendimiento de la base de datos. En SQL server tiene una opción para conocer la fragmentación de los índices de la base de datos
A continuación muestro una consulta  que lista las tablas del sistema, la cantidad de registros por tabla, sus índices y porcentaje de fragmentación. La consulta se ordena por orden de registros descendente.
--Consulta para extraer tablas y registros
Select Tablas.Tabla
       ,Tablas.Registros
       ,Indices.Indice
       ,Indices.Fragmentacion
       ,Round((Registros / 1000000),2) * 100 As Orden
From
(select Distinct SCS.name + '.' + SO.Name as Tabla
      ,si.rows as 'Registros'
from sysobjects as SO
    inner join sysindexes as SI on SO.Id = SI.id
    inner join sysfilegroups as SFG on SI.GroupId = SFG.GroupId
    inner join sys.objects SOS On SO.id=SOS.object_id
    inner join sys.schemas SCS On SOS.schema_id = SCS.schema_id) As Tablas
    Inner Join (SELECT d.name + '.' + c.name as Tabla
                      ,b.name As Indice
                      ,a.avg_fragmentation_in_percent 'Fragmentacion'
                FROM sys.dm_db_index_physical_stats (DB_ID(N'Cuentame'), NULL, NULL, NULL, NULL) AS a
                     JOIN sys.indexes AS b ON a.object_id = b.object_id AND a.index_id = b.index_id
                     Join sys.objects AS c on b.object_id = C.object_id
                     Join sys.schemas AS d ON c.schema_id = d.schema_id
WHERE b.name is Not NUll) As indices
      On Tablas.Tabla=indices.Tabla
Order By Registros Desc

Una vez ejecutado el listado, puedes seleccionar la tabla e índice que veas más fragmentado y reorganizar o reconstruir mediante la siguiente sentencia.
ALTER INDEX [NombreIndice] on [Esquema].[Tabla] REORGANIZE
O
ALTER INDEX [NombreIndice] on [Esquema].[Tabla] REBUILD

SQL Server es marca registrada de Microsoft.

Entrada destacada

Arquitectura de N-Capas con Visual Studio .NET y AngularJS

Este artículo es el primero de una serie de artículos en que quiero explicar mi visión de la programación por capas. La Arquitectura ...