Introducción
Si bien el objeto DataSet es muy completo y brinda una funcionalidad muy amplia hay que reconocer que ciertas operación con la información pueden estar fuera del alcance del mismo, por lo tanto se debe recurrir a otras técnicas.
Es bien conocido que el DataSet posee un método de nombre Merge(), el cual resulta muy práctico para unir información proveniente de diversas consultas, siempre y cuando se respete la misma estructura en los datos, pero este solo une información, no permite obtener obtener diferencias entre las dos fuentes de datos.
Que sucede si es necesario cruzar registros para obtener diferencias entre ellos y trabajar con esta información, es aquí donde entra en juego Linq, proveyendo una herramienta muy amplia de posibilidades.
Join entre DataSet, devolviendo la lista de string
En el ejemplo se devolverá una lista de string con el resultado del join de ambos datatable. Una de las listas representan una relojes de registro de ingreso de personal, mientras que la otra devuelve cuales de estoy hay que controlar.
[C#]
private void btnListaString_Click(object sender, EventArgs e) { #region Defino Columnas DataGridView dataGridView1.Columns.Clear(); DataGridViewColumn col = new DataGridViewColumn(new DataGridViewTextBoxCell()); col.HeaderText = "Relojes ha Controlar"; col.Name = "Relojes"; col.DataPropertyName = "Value"; col.Width = 160; dataGridView1.Columns.Add(col); #endregion List<string> lista = (from dt1 in ObtenerListadoRelojes().AsEnumerable() join dt2 in ObtenerRelojesControlar().AsEnumerable() on dt1.Field<int>("Numero") equals dt2.Field<int>("NumControl") select dt1.Field<string>("Nombre")).ToList(); dataGridView1.AutoGenerateColumns = false; dataGridView1.DataSource = lista.Select(x => new { Value = x }).ToList(); }
[VB.NET]
Private Sub btnListaString_Click(ByVal sender As Object, ByVal e As EventArgs) 'Defino Columnas DataGridView dataGridView1.Columns.Clear() Dim col As New DataGridViewColumn(New DataGridViewTextBoxCell()) col.HeaderText = "Relojes ha Controlar" col.Name = "Relojes" col.DataPropertyName = "Value" col.Width = 160 dataGridView1.Columns.Add(col) Dim lista As List(Of String) = (From dt1 In ObtenerListadoRelojes().AsEnumerable() _ Join dt2 In ObtenerRelojesControlar().AsEnumerable() _ On dt1.Field(Of Integer)("Numero") Equals dt2.Field(Of Integer)("NumControl") _ Select dt1.Field(Of String)("Nombre")).ToList() dataGridView1.AutoGenerateColumns = False dataGridView1.DataSource = lista.[Select](Function(x) New With {.Value = x}).ToList() End Sub
Es interesante marcar como el “join” de linq, en la parte de “on” se asemeja a una consulta SQL, igualando que campo se comparan en la operación.
En la sección del “select” de la consulta, solo se define un campo el tipo string, el cual es utilizado para conformar cada ítem de la lista devuelta por la consulta linq.
Join entre DataSet - mapeando con una clase
El uso de clases para convertir datos en una estructura diferente puede resultar útil cuando se requiere obtener datos complejos con los cuales trabajar.
En este ejemplo era necesario obtener dos atributos provenientes de los datos originales, usar un string era imposible ya que este solo representa un dato, por lo tanto el uso de la clase permitió lograr el objetivo.
[C#]
private void btnlistaClase_Click(object sender, EventArgs e) { #region Defino Columnas DataGridView dataGridView1.Columns.Clear(); DataGridViewColumn col1 = new DataGridViewColumn(new DataGridViewTextBoxCell()); col1.HeaderText = "Relojes ha Controlar"; col1.Name = "Relojes"; col1.DataPropertyName = "Nombre"; col1.Width = 160; dataGridView1.Columns.Add(col1); DataGridViewColumn col2 = new DataGridViewColumn(new DataGridViewTextBoxCell()); col2.HeaderText = "Horario"; col2.Name = "Horario"; col2.DataPropertyName = "Hora"; col2.Width = 50; dataGridView1.Columns.Add(col2); #endregion List<Datos> lista = (from dt1 in ObtenerListadoRelojesConHorarios().AsEnumerable() join dt2 in ObtenerRelojesControlar().AsEnumerable() on dt1.Field<int>("Numero") equals dt2.Field<int>("NumControl") select new Datos { Nombre = dt1.Field<string>("Nombre"), Hora = dt1.Field<string>("Hora") }).ToList(); dataGridView1.AutoGenerateColumns = false; dataGridView1.DataSource = lista; }
[VB.NET]
Private Sub btnlistaClase_Click(sender As Object, e As EventArgs) 'Defino Columnas DataGridView dataGridView1.Columns.Clear() Dim col1 As New DataGridViewColumn(New DataGridViewTextBoxCell()) col1.HeaderText = "Relojes ha Controlar" col1.Name = "Relojes" col1.DataPropertyName = "Nombre" col1.Width = 160 dataGridView1.Columns.Add(col1) Dim col2 As New DataGridViewColumn(New DataGridViewTextBoxCell()) col2.HeaderText = "Horario" col2.Name = "Horario" col2.DataPropertyName = "Hora" col2.Width = 50 dataGridView1.Columns.Add(col2) Dim lista As List(Of Datos) = (From dt1 In ObtenerListadoRelojesConHorarios().AsEnumerable() _ Join dt2 In ObtenerRelojesControlar().AsEnumerable() _ On dt1.Field(Of Integer)("Numero") Equals dt2.Field(Of Integer)("NumControl") _ Select New Datos() With { _ .Nombre = dt1.Field(Of String)("Nombre"), _ .Hora = dt1.Field(Of String)("Hora") _ }).ToList() dataGridView1.AutoGenerateColumns = False dataGridView1.DataSource = lista End Sub
Hay que remarcar como el la sección del “select” se crea un nueva instancia de la clase por cada ítem resultante de la query, en esta es donde se especifica como mapean las propiedades con los campos originales.
Ejemplos de código
El código ha sido desarrollado usando Visual Studio 2008
[C#] |
[VB.NET] |
Leandro buenos dias, estuve viendo tu articulo y tengo una pregunta...
ResponderEliminartengo dos orígenes de datos los cuales me retornan columnas variables pues se genera por medio de un pivot de acuerdo al rango de fechas, si el rango es de 2 meses me genera dos columnas, y asi... la columna comun es el id, la cual usaria para hacer la union. los dos datatable tienen el mismo numero de columnas.
Como puedo hacer el union de los 2 datatable con todas sus columnas..
gracias
hola ricardo.cabra
ResponderEliminarlo unico que se me ocurre es normalizar, o sea definir un numero de columnas fijas para el resultado
si uno devuelve 2 columnas y otro 5 columnas, entonces el resultado seran 5 columnas, solo que del primer datatable se cargaran solo las 2 primeras dejando el resto con un valor por defecto o sino nulas, del segundo se cargan todas las columnas
saludos
hola, soy algo nueva en esto y quiero saber si puedo concatenar 2 grids ya sea con el metodo merge o con el join datatable los dos datatables obtinen su origen de datos de un datasource con una consulta a la base de datos en oracle.
ResponderEliminaresto lo requiero para emitir un reporte el cual me traiga 3 columnas, 2 de ellas de un grid maestro y la tercera de su respectivo grid detalle
hola magugo
ResponderEliminarno entendi muy bien el planteo que la pregunta, mas que nada porque no quedo claro de dodne surge la informacion
mencionas los DataSource de los grid pero esto que tiene que ver luego con los reportes
o sea si realzias consultas a Oracle y cargas datatable que tienen la misma estructura de campos, podrias luego usa el Merge() para unir los registros
saludos
LEANDRO TENGO UNA CONSULTA, DE PASAR UN QUERY SQL A LINQ-DATATABLE:
ResponderEliminarSELECT TH.cNombre, COUNT(TH.cNombre) cant, H.cNombre
FROM RESERVA.Habitacion H
INNER JOIN RESERVA.TipoHabitacion TH ON H.iIdTipoHabitacion = TH.IdTipoHabitacion
WHERE th.IdTipoHabitacion <> 1
GROUP BY TH.cNombre, H.cNombre
HAVING COUNT(TH.cNombre) > 1 OR TH.cNombre = TH.cNombre , Y LLEGUE A PASARLO DE ESA FORMA: Dim tblQuery = From r In tblHabitTarifa.AsEnumerable() _
Group r By idtipohabit = r.Field(Of String)("idtipohabitacion1"), _
tipohabit = r.Field(Of String)("tipohabitacion1"), _
habit = r.Field(Of String)("habitacion")
Into cont = Count() _
Where cont > 1 Or tipohabit = tipohabit And idtipohabit <> 1
Select idtipohabit, tipohabit, cont , Y NO LOGRO A HACER QUE SOLO CUENTE EL CAMPO: " r.Field(Of String)("tipohabitacion1") "... HELP PLEASE..
hola fer007
ResponderEliminarhas analizado ejemplos como estos
LINQ To SQL Samples - GROUP BY/HAVING
porque alli veras que para hacer un group by de multiples campos necesitas algo como esto
Group By Key = New With {p.CategoryID, p.SupplierID} Into Group
o sea armar un objeto anonimo, que no veo que alli estes definiendo
---
al igual que esto
Into cont = Count()
deberia ser
Where Group.Count() >= 10
o sea trabajas directo con el Group
saludos
HOLLE HERMNO ECHAME LA MANO
ResponderEliminarTengo un dataTable de dos origenes de datos (claves y propiedades), estoy juntando las dos en una sola
las propiedades están en rangos de filas,quiero repartir cada clave de la table claves (esta tiene solo una columna llamada claves), en cada uno de los rangos mi tabla propiedades le quiero asignar una sola clave y asi sucesivamente con los demás rangos de tal forma que en la DataTable nueva llegue cada rango de filas con una sola clave y así repartidlas, esto conyeva a ser adacentes las filas de dos tables
Hola, yo necesito comparar dos datatable y obtener los registros que solo estan en un una de las tablas, me explico, tengo TablaA y TablaB, en TablaA hay 30 registros y en TablaB hay 25 registros, porque 5 de ellos se han eliminado.
ResponderEliminarNecesitaría obtener una tabla con los datos que se han eliminado, ya que al inicio TablaA y TablaB tenían los mismos datos.
hola
Eliminarsi utilizas linq podrias realizar un left join
Realizar operaciones de combinación externa izquierda
de esta forma podrias comprar las tablas y ver cuales estan en una faltando en la otra
saludos