Informe con origen en tablas variables

Estoy haciendo un software para una organización sin ánimo de lucro (ONG) y me he encontrado con un problema.
Es una aplicación en vb.net que conecta con una base de datos MS SQL. En principio parce funcionar todo de maravilla, y el único problema que tengo es el siguiente:
La base de datos tiene tablas que se van creando con el tiempo y tienen el nombre con el formato "XXX_miembros", donde XXX es un número (por ej. 203_miembros).
Ahora, tengo un formulario con un visor de informes que me saca los datos de una tabla que está declarada en un dataset, pero necesito que me coja los datos de una que no aparece en el diseñador de datasets del Visual Studio 2010.
Por ejemplo, pongamos que el usuario que se loguea en el programa es del grupo "203". Al hacer click sobre el botón que muestra el informe de los miembros (DatosContactoGeneral. Rdlc), ese informe debería usar como tabla de origen de datos la tabla 203_miembros.
Creo que debe haber alguna forma sencilla como creando un dataset, datatable o alguna cosa así, pero desconozco la forma de hacerlo.
2

2 respuestas

Respuesta
1
Un poco complicada tu forma de generar el dato.
Primero, explicame como conectas a la BD, ¿usas asistente para esto?
Segundo, por que hablas de Diseñador de DS, ¿usas asistente para esto?
Tercero, ¿usas clase para conectarte a BD?.
Hola Eduardo,
Es la única forma que se me ha ocurrido. Si conoces otra mejor, te agradecería que me lo comentaras.
El DataSet lo tengo enlazado con la base de datos mediante el asistente. El problema aquí es que al aparecer una nueva tabla en la base de datos, el dataset no la muestra a menos que la añada manualmente. El Diseñador de DataSet únicamente muestra las tablas que he añadido originalmente, y no las que realmente hay en la BD.
No uso ninguna clase para conectarme a la BD, simplemente el informe tiene enlazado el DataSet que muestra los datos de la tabla "miembros" existente en el DataSet. El problema viene cuando quiero mostrar los datos de una tabla que no he añadido al diseñador del DataSet, por ejemplo "203_miembros".
El objetivo final es cambiar la datatable que contiene los datos del Seguro que hay una forma muy sencilla de hacerlo. ¿Podría servir lo siguiente?
Hacer un SELECT sobre la tabla nueva (203_miembros) y cargarlo en el datatable existente (miembros). Como el informe carga los datos del DataTable "miembros", funcionaría mostrando los nuevos datos ¿no? ¿Cómo se podría hacer?
Espero haberme explicado correctamente.
Muchas gracias por tu interés. Saludos.
Primero tienes una mala praxis tremenda usando Asistentes, no los he usado nunca y no puedo ayudarte con eso.
Te dejo un enlace de un MVP que te muestra, te enseña y te demuestra el por que usar Asistentes es una pésima práctica para un programador.
http://msmvps.com/blogs/cwalzer/archive/2007/09/24/anti-pr-225-cticas-i-acceso-a-datos-con-ado-net.aspx
En ese artículo también podrás ver y aprender a usar un DataSet y un DataTable para que puedas solucionar tu problema.
Muchas gracias por la ayuda. Al final he encontrado la solución a través de google y en parte con el artículo que me comentaste.
Por si alguien se encuentra con el mismo problema, lo he solucionado así:
'Creamos los elementos necesarios para cargar los datos y rellenamos el DataTable con los datos procedentes del dataAdapter
Dim connectionString As String = My.Settings.SMConnectionString
Dim sql As String = "SELECT * FROM " & tablaOrigenDatos
Dim connection As New SqlConnection(connectionString)
Dim dataadapter As New SqlDataAdapter(sql, connection)
Dim Table As New DataTable()
connection.Open()
dataadapter.Fill(Table)
connection.Close()
'Aquí le indicamos qué informe usar
ReportViewer1.ProcessingMode = ProcessingMode.Local
'ReportViewer1.LocalReport.ReportEmbeddedResource = "miNameSpace.DatosContacto.rdlc"
ReportViewer1.LocalReport.ReportEmbeddedResource = "minameSpace." & informeParaAbrir
'Quitamos cualquier datasource que tuviese el informe para cargar el nuestro
Me.ReportViewer1.LocalReport.DataSources.Clear()
'Creamos un nuevo ReportDataSource con los datos conseguidos de la BD
Dim rds As New ReportDataSource("GrupoDatosGenerales", Table)
ReportViewer1.LocalReport.DataSources.Add(rds)
'Finalmente mostramos el informe en modo de imprsión (pantalla completa)
Me.ReportViewer1.SetDisplayMode(Microsoft.Reporting.WinForms.DisplayMode.PrintLayout)
Respuesta
Si las tablas tienen la misma estructura no hay problema con eso, con solo cambair el nombre en la consulta te debe fucnionar.
Aquí te dejo un enlace en el cual explico como se deben crear los reportes con CR y VB.NET
http://vbcodigopocketpc.blogspot.com/search?q=crystal+report
Hola de nuevo.
Mil gracias por tu respuesta. En efecto, todas las tablas de miembros son iguales, aunque tienen bastantes más campos que tu ejemplo (alrededor de 50) por lo que el código va a ser largo. No es problema.
Entiendo que en la línea de código que pones abajo, donde dice
"sbQuery.Append(" FROM Persona ")" debería indicar la tabla desde la que coger los datos. ¿no?
Algo así como: sbQuery.Append(" FROM " & numeroGrupo & "_miembros")
Recuerda que la tabla XXX_miembros no está declarada en el dataset. Solamente existe en la base de datos SQL. ¿Funcionaría?
¿Sería posible hacer esto sin CrystalReports, utilizando los informes de visual studio? Uno de los campos de los informes se calcula usando una función y me ha costado bastante hacer que eso funcione. A demás, usar Crystal Reports significaría instalar software adicional en los equipos cliente, ¿no?
Uno de mis campos del formulario es algo así: "=code.funcionCalcularUnidad(edad)". Lo que hace es coger la edad de cada línea de la tabla y usa la función incrustada para calcular su "unidad". No se si esto se puede hacer con CrystalReports.
De nuevo, muchas gracias y enhorabuena por el excelente tutorial de la web.
Saludos.
Estas en lo correcto con respecto al nombre de la tabla, y si funciona el dataset independiente que no se llame igual, realmente eld atset lo utilizas paar caragr los datos y pasárselos al reporte, no más, y al principio para dibujar el reporte.
En crystal report tienes campos calculados en el cual puedes incluir tu calculo.
Esto es lo que tengo pero no me funciona. He probado con decenas de ejemplos que he encontrado por internet incluido el tuyo, pero no consigo que funcione con el reportviewer ya que no debo hacerlo a través de crystalReports dentro de lo posible.
Intuyo que es algo muy sencillo para quien sepa lo que hace, pero no encuentro el problema. ¿Sabrías decirme cómo?
De nuevo, mil gracias.
Dim oCnn As New SqlConnection ' Objeto de conexion a la base de datos
Dim daDatos As New SqlDataAdapter ' Objeto Adaptador para leer datos de la Base de datos
Dim cmdExec As New SqlCommand ' objeto comando para ejecutar sentencias sql
Dim dtDatos As New DataTable ' datatable para recibir los datos de la base de datos
Dim sbQuery As String ' StringBuilder para armar cadenas
Try
oCnn.ConnectionString = My.Settings.SMConnectionString
oCnn.Open()
cmdExec = oCnn.CreateCommand
cmdExec.Connection = oCnn
sbQuery = "SELECT Id, Apellidos, Nombre, Email, FROM " & XXX_miembros
daDatos = New SqlDataAdapter(cmdExec)
daDatos.Fill(dtDatos)
Me.ReportViewer1.LocalReport.DataSources.Add(daDatos)
Me.ReportViewer1.LocalReport.ReportEmbeddedResource = "DatosContactoGeneral.rdlc"
Me.ReportViewer1.SetDisplayMode(Microsoft.Reporting.WinForms.DisplayMode.PrintLayout)
Catch ex As Exception
MessageBox.Show("excepcion: " & ex.Message, "Mostrando Reporte")
End Try
Si te complicas, envía el resultado como parte del conjunto de datos que le envías al reporte.
Gracias por tu pronta respuesta, pero no entiendo muy bien a lo que te refieres.
No obstante, he conseguido algo más claro que CASI funciona. El único problema es que al mostrar el formulario me pone: "Se ha producido un error durante el procesamiento local de informes. No se ha especificado la definición del informe "DatosContactoGeneral.rdlc"
He visto en un foro que lo solucionan fácilmente, pero no dicen cómo! ¿Sabrías ayudarme?
Mil gracias.
Try
Dim sbQuery As String = "SELECT Id, Apellidos, Nombre, Email, FROM " & miTabla
Dim oCnn As New SqlConnection(My.Settings.SMConnectionString)
Dim cmd As New SqlCommand(sbQuery, oCnn)
Dim daVendor = New SqlDataAdapter
daVendor.SelectCommand = cmd
Dim dsVendors As New DataSet
daVendor.Fill(dsVendors)
Me.ReportViewer1.LocalReport.ReportEmbeddedResource = "DatosContactoGeneral.rdlc"
Me.ReportViewer1.LocalReport.DataSources.Add(New Microsoft.Reporting.WinForms.ReportDataSource("VendorList_Vendors", dsVendors.Tables(0)))
Me.ReportViewer1.RefreshReport()
Catch ex As Exception
MessageBox.Show("excepcion: " & ex.Message, "Mostrando Reporte")
End Try
Tiens que darle al ruta en donde esta el .rdlc
Creía que eso ya lo hacía mediante la linea:
Me.ReportViewer1.LocalReport.ReportEmbeddedResource = "DatosContactoGeneral.rdlc"
He probado a cambiarla por:
Me.ReportViewer1.LocalReport.ReportPath = "C:\...\Informes\DatosContactoGeneral.rdlc"
Y esta vez el informe se muestra, pero en blanco.
Cuando hago mi select, ¿tiene qué coincidir exactamente el mismo numero de campos con los nombres que aparecen en la tabla del informe? Tengo un campo que se calcula usando la siguiente expresión:
=code.ponerunidad(code.EdadAFinDeAno(Fields!FechaNacimiento.Value),Fields!UnidadManual.Value)
Me parece que se está complicando la cosa y en definitiva lo que necesito hacer es que el informe en vez de usar la tabla MIEMBROS, use la que yo le diga en ese momento (XXX_MIEMBROS).
Sería algo así:
-Cambiar tabla que usa el informe
-Mostrar informe
Igual que crystalreport el reporting services tanbein tiene la propiedad Reportsource allí le pasas el resultado del select que hagas, nuevamente te lo repito, este select debe coincir con los campos del dataset con el que pintaste el reporte inicialmente.
Sí, eso lo tengo claro, pero a lo que me refería es que en el informe hay columnas de la base de datos SQL y otras columnas que se calculan usando unas funciones y otros datos de la BD.
Por ejemplo:
Columnas de mi informe: UNIDAD - NOMBRE - APELLIDOS - FECHA NACIMIENTO - EDAD
Columnas existentes en la BD: NOMBRE - APELLIDOS - FECHA NACIMIENTO
Los campos UNIDAD y EDAD se calculan usando unas funciones y no se pueden obtener de la base de datos mediante el SELECT.
Por ejemplo, en el informe, la columna UNIDAD se obtiene mediante la expresión:
=code.ponerunidad(code.EdadAFinDeAno(Fields!FechaNacimiento.Value),Fields!UnidadManual.Value)
¿Cómo puedo calcular esas columnas y añadirlas al dataset para que aparezcan en el informe?
Gracias.
Llama dentro el select la función especifica para el valor que necesitas calcular
He estado buscando por decenas de páginas cómo usar las funciones dentro de comandos SELECT pero no he encontrado nada que siquiera se acerque.
El SELECT es: "SELECT Apellidos, Nombre, FechaNacimiento, TelfParticular, Direccion, Ciudad, CP FROM mytable"
Las funciones no están en la base de datos sino en una clase dentro del programa.
Como te comentaba, la expresión para el cálculo de unidad es:
=code.ponerunidad(code.EdadAFinDeAno(Fields!FechaNacimiento.Value),Fields!UnidadManual.Value)
El problema es que no sé como meter la función dentro del SELECT. Siento ser tan pesado, pero llevo días atascado con este problema y estoy desesperado. Muchas gracias.
Es hasta ahora dices que las funciones no son funciones de sql, sino del programa
Llena el dataset con el select, en el select incluye las columans calculadas aunque vayan vacías de la base de datos,
Cuando recibes el dataset lo recorres con un for y acad aregistro le aplicas tu función, luego que termines pasas el dataset al reporte
También había pensado eso, pero no me deja hacer un select de campos que no existen.
Por ejemplo: "SELECT Apellidos, Nombre, FechaNacimiento, TelfParticular, Direccion, Ciudad, CP, Unidad FROM miTabla"
"Unidad" no es un campo que exista en la base de datos por lo que me dice "El nombre de columna Unidad no es válido"
Por otro lado, no sé como se podría recorrer el dataset con un for para calcular el campo que necesitamos. Gracias.
select apellidos,...., '' as unidad from mitabla
for i=1 to ds.rows.count
ds.rows(i).item("micampo") = algo
next
Gracias por el ejemplo, pero como te comentaba, no me deja hacer un SELECT de algo que no existe en la base de datos.
En el ejemplo de SELECT que pones, ¿Exactamente qué hay que poner AS UNIDAD? No puedo añadir una función al SELECT, o por lo menos no sé cómo.
Por otro lado, ¿cómo podría usar mi función en el FOR? ¿Sería algo así?
ds.rows(i).item("Unidad") = mifunción(FechaNacimiento)
Mi duda sobre el FOR sería cómo puedo pasarle el valor FechaNacimiento que hemos cogido de la base de datos.
Creo que nos estamos complicando demasiado. ¿No serviría actualizar el datatable (miembros) del dataset existente con un SELECT que coja los datos de una tabla nueva (XXX_miembros)?
El informe coge los datos del DataTable miembros, hace los cálculos y los muestra. Por lo tanto, entiendo que si rellenamos los datos del DataTable con un SELECT que apunte a otra tabla debería de funcionar.
Si crees que funcionaría, ¿cómo sería el código para modificar el DataTable con el SELECT?
Gracias.
No te entiendo
¿Qué es lo que no funciona? Para armar un reporte DEBES crear un DATATSET, ese dataset lo rellenas con un select y se lo pasas despeus al reporte en datasource. Los cálculos que haces con tus clases debes tomar el dataset y el datatable y si no existen las columansa adicionarlas tables(indice). Columns. Add("nombrecolumna")
Gracias por la ayuda. Al final he encontrado la solución en google. Si alguien la necesita en el futuro, lo he solucionado así:
Try
'Creamos los elementos necesarios para cargar los datos y rellenamos el DataTable con los datos procedentes del dataAdapter
Dim connectionString As String = My.Settings.SMConnectionString
Dim sql As String = "SELECT * FROM " & tablaOrigenDatos
Dim connection As New SqlConnection(connectionString)
Dim dataadapter As New SqlDataAdapter(sql, connection)
Dim Table As New DataTable()
connection.Open()
dataadapter.Fill(Table)
connection.Close()
'Aquí le indicamos qué informe usar
ReportViewer1.ProcessingMode = ProcessingMode.Local
'ReportViewer1.LocalReport.ReportEmbeddedResource = "miNameSpace.DatosContacto.rdlc"
ReportViewer1.LocalReport.ReportEmbeddedResource = "miNameSpace." & informeParaAbrir
'Quitamos cualquier datasource que tuviese el informe para cargar el nuestro
Me.ReportViewer1.LocalReport.DataSources.Clear()
'Creamos un nuevo ReportDataSource con los datos conseguidos de la BD
Dim rds As New ReportDataSource("GrupoDatosGenerales", Table)
ReportViewer1.LocalReport.DataSources.Add(rds)
'Finalmente mostramos el informe en modo de imprsión (pantalla completa)
Me.ReportViewer1.SetDisplayMode(Microsoft.Reporting.WinForms.DisplayMode.PrintLayout)

Añade tu respuesta

Haz clic para o

Más respuestas relacionadas