Im trying to create a report in SSRS. The report calls a stored procedure for its data. I want to display the data in a table.
But the thing is, the result from the stored procedure differs from time to time, because every customer has its own 'template'. This means the result for customer A could be :
AccountNumber | CustomerID
1234567890 0987654321
1579086421 1234565465
...................... ....................
and for customer B could be:
CustomerName | Address
Customer B Teststreet 1
Customer Test Teststreet 2
...................... ....................
There are 50 different columns to choose from. The order of the columns is also editable. My stored procedure takes care of this. The only thing I want is to put the resultset of the storedprocedure 1:1 in my report (header+body). Do you guys know how to do this?
If thats not possible, is there a C# solution to this? I.e creating a report object in C#, adjust settings etc.
Thanks
You can create the SSRS report dynamically based on the data set returned by the stored procedure. The report format (RDL) is documented and its an XML format. So you can use System.XML namespace to generate RDL. Alternate (and unsupported) way is to refer RDL object model assembly (Microsoft.ReportingServices.RdlObjectModel) - you can locate the assembly on SSRS 2008 server machine and copy it on your local machine. It offers an object model to read/generate RDL.
I have adopted approach where RDL is generated (as XML) dynamically based on the data-table and then publish the RDL on SSRS server using web services API.
One solution might be to modify your SP so that the data returned looks something like:
ColNameA DataA ColNameB DataB
AccountNumber, 1234567890, CustomerID, 948477586
AccountNumber, 5466584426, CustomerID, 458852244
Then, in SSRS drag out a table. Create a group on ColNameA. In that Group Row, place the Field ColNameA in the first Column, place ColNameB in the second column.
In the Details Row, place DataA in the first column, and DataB in the second column, and should look like this:
The sample query i used was:
select 'AccountNumber' as ColNameA, 1234567890 as DataA, 'CustomerID' as ColNameB, 0987654321 as DataB UNION
select 'AccountNumber' as ColNameA, 5546488393 as DataA, 'CustomerID' as ColNameB, 4747599393 as DataB
Getting the names of the Columns (AccountNumber, CustomerID or CustomerName, CustomerAddress) will be the key. You can get them via:
select * from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'my_table_name'
You can use the Rdlobjectmodel provided by the Report designer to create and alter a report.
You can reference the Microsoft.ReportingServices.Designer.Controls in to your project but that includes all the dependencies as well which is 20+ assembly or you can copy the following set of DLLs in a separate folder at your project root level and use it to reference the DLLs
Microsoft.ReportingServices.QueryDesigners
Microsoft.ReportingServices.Designer.Controls
Microsoft.ReportingServices.RdlObjectModel
Microsoft.ReportingServices.ReportDesign.Common
Microsoft.ReportingServices.RichText
Microsoft.ReportingServices.RPLObjectModel
private Report LoadReportTemplate()
{
const string docPath = "template.rdl";//path for your template report
using (var fs = new FileStream(docPath, FileMode.Open))
{
var report = Report.Load(fs);
return report;
}
}
Follow the link for Documentation
Related
I ask for advice what would you suggest to display 2 unrelated tables in a single report (if needed with subreport), the first one with groupings and the second one "flat"?
Details:
I am developing a report with C# 4 and Crystal Reports for Visual studio.
Data comes from a Stored procedure in Sql Server 2005, that returns 2 tables.
I pass the data to the report with the .SetDataSource(Dataset) method and I checked that data in the dataset is correct.
I need to display the data from the first table in a quite complex form with different sections. This part works.
At the end of the report I need to print the data from the second table (no grouping, just a "flat" print). To do this I added a subreport.
The tables are not related. I added a Dummy field on them with the same value and used it to mimic a relation in the database expert; I configured the subreport link, based on the dummy field.
I have 2 problems.
The data in the main report is cross joined with the second table (I could expect that since they are related with a constant field)
The subreport is always empty (I checked several times that its table in datasource contains correct data).
I tried different configurations of link and tried to place the subreport in different sections (It should be in the report footer).
I set a border around the subreport: the border is printed but inside it is empty.
I also setup a formula to count the records of both tables and put the count in the report: the count is > 0 for both.
Your approach looks wrong.... At first if both are not releated I don't see any use in creating a dummy field and linking tables which will always provide cross join. Any way as per your descrition table 1 data is saperately displayed and table 2 data is saperately displayed
As per your description in main report you need data from table 1 and in footer you need data from table 2.
Try this approach:
Don't link tables
take table 1 in main report and show the required display.
Take sub report and in that take only table 2 and display in footer and make sure don't link main report and sub report nor link two tables.
Ideally speaking, without linked table not used in crystal report as reporting tools used to display the relevant data based on our criteria and display 2 different set of data without link does not mean as a report as well as tough to integrate in crystal report.
Tough to integrate means at any point you have to link the table either via union in sqlserver or at crystal report side, other wise it display as Cartesian.
As per my thoughts you can do it by some approach.
Approach 1 : You can get the data by union or unionAll with set of columns and filter the data where table's column does not have null value ie.
http://www.maximumimpactsolutions.co.uk/blog/comments.asp?bd=118
Table1col1 Table1col2 Table1col3 Table2Col1 Table2Col2 Table2Col3
value value value null null null --table1data
null null null value value value --table2data
Approach 2 : Second is sub-report, but still need the linked as you already done. But there is proper rule to be set (this is very very tedious job).
Approach 3 : Generate 2 report at c# side and merge into one PDF, then export it.
check this links.
Reporting on multiple tables independently in Crystal Reports 11
http://www.tek-tips.com/viewthread.cfm?qid=241195
http://www.crystalreportsbook.com/Forum/forum_posts.asp?TID=4756
http://www.codeproject.com/Questions/348959/Crystal-reports-for-multiple-tables
I have 2 tables in an Access Database. (SQL examples are fine).
The first table is a table called Reports.
The fields are :-
ID
ReportName
ReportType
The second table is a Template, where a user will store a host of reports. The user modifies this template table each week. Below are the fields :-
ID
UserID
ReportName
ReportType
ScheduledDate
Completed
AssignedBy
I have everything in my code working, but I overlooked something vital.
I need to reflect the changes a user may make to the Report Table, I.E, EDIT a reports name, into the Template Table, so that any instance of the edited report, now shows the new name.
I'm struggling to work out how I can do this?
Any help would be appreciated, it doesn't have to be exact code, more so the method.
Only works for .adp project, you should be able to do this using a trigger.
something like
create <trigger_name>
ON Report
....
I'm still new to C# and reports, and in order to take baby steps, I started with a Crystal Report using one table.
Eventually I figured it out and it worked brilliantly.
Then I added another table to the report. I haven't changed anything in my code. Adding a field from the second table to the report, results in a blank report.
Removing that field again (so no columns form the second table is on the report), the report produces data again.
So I get the impression that the problem is on the report side. But I have included the code anyway:
private void Load_Ord_Rep()
{
using (MySqlConnection conn = new MySqlConnection(OTW.Properties.Settings.Default.wcdbConnectionString))
{
conn.Open();
String sql = "SELECT * FROM wcdb.order_table, wcdb.mat_table WHERE order_no = '13661' and order_table.mat_code = mat_table.mat_code";
using (MySqlCommand cmdSel = new MySqlCommand(sql, conn))
{
DataSet ds = new DataSet();
MySqlDataAdapter da = new MySqlDataAdapter(cmdSel);
da.Fill(ds);
ReportDocument rpt = new ReportDocument();
rpt.Load("C:\\Visual Studio 2008\\Projects\\OTW\\OTW\\CrystalReport3.rpt");
dataView1.Table = ds.Tables[0];
rpt.SetDataSource(dataView1);
crystalReportViewer1.ReportSource = rpt;
crystalReportViewer1.Refresh();
}
conn.Close();
}
}
With further investigation I have come to the conclusion the problem is not the code or the link, but rather the loading of the second table. I did a outer join with the values being equal or greater. Only the first table's results are displayed on the report. So because the second table's values are not read, no join can be established between the two tables and thus no data on the report. Now the question: why is the second table not being read by Crystal Report!?
UPDATE
I removed the second table from the main report and added a sub-report with the data. Same result as before. The sup report shows blank. Running the sup report on its own (as the main report), it populated correctly. I'm using MySQL, could it then maybe be a database issue?
UPDATE
I created a new app, this time connected the report to the database using ODBC (instead of ADO.NET). And it worked perrrfect. Now to figure out why ADO.Net is not working....as my entire program is based on it.
You don't need to do any of the code you are doing with the dataset/dataview. I recommend you allow the report to "pull" data (you are "pushing" data to the report now).
To pull data requires only the following:
ReportDocument rpt = new ReportDocument();
rpt.Load("C:\\Visual Studio 2008\\Projects\\OTW\\OTW\\CrystalReport3.rpt");
rpt.SetDataBaseLogon("userName", "password", "servername", "database");
crystalReportViewer1.ReportSource = rpt;
This reduces the risk that you're confusing the report up by passing in a dataview that it has no idea how to map to the tables you added while designing the report. (Which I'm 99.999% sure is happening now.)
All the code above lacks is parameters if you had any that needed to be set. It looks like you may be trying to "push" data because you wanted to filter on that order number or something? If that is the case, add a 'record selection formula' inside your report, base it's 'where' aspect of that formula on a Crystal Report parameter and add a rpt.SetParameter(arguments) line to the code I provided.
No this is not a licensing constraint, I'm certain of that.
Successful troubleshooting path:
1) Used the simple code to load the report:
* No errors, but no data in the report
2) Played with join options in the report:
* Also resulted in a blank report, even with only one table
* Concerned the second table is not reading data
3) Checked for record filtering by a 'record selection formula' which might limit/prevent rows from being returned
* In this case, wasn't applicable
4) Analyzed how joins were being done:
["It's probably how you are joining. For example: if you do a right outer join to a second table and it has no data, then even the first table's rows won't come back."]
A join was done on the primary id field (mat_code) in the 'orders' table to the same field (mat_code) in the second table, 'material'. This seemed fine.
Attempted a Left Outer join with the link type being ">=". The report printed the first table (orders) with data. But still none of the second tables' (material) data.
Made a new report, this time first adding the "second" table (material) and then the first (orders). This time with an outer join, and only the material table shows data. In other words, it only appears to pull data for the first, or primary table, then stops.
5) Attempted loading the second table's data by putting it into a subreport, and linking the subreport on the same 'mat_code' field:
(Helpful tutorial: http://vb.net-informations.com/crystal-report/vb.net_crystal_report_subreport.htm)
Sub-report also shows blank. Only the main report works. Whatever table exists in the subreport is not being populated.
Still sees no data using the simple suggested code, his original code still shows the first table's data
6) Check for a mismatch between the table's connection and definition, and the actual data structure/contents being submitted to, or loaded by, the report. A specific test was suggested, go to:
i) 'Set Database Location' (where you manage tables for the report)
ii) Look to see if an XML/DataSet was used versus OLEDB was done
iii) Change the database table location to the same tables, but with an OLEDB connection type (repeat for all tables)
When attempting above, when the 'Update' button was hit, the screen just flickered for a split second, but showed no message. After testing the report again, still no change in behavior. He was using ADO.NET to connect to the data.
7) Still highly suspicious of the data table definition and connection type. Suggested the following test:
i) Make a brand new 1 page application with only simple test code.
ii) Make a new report with only the 'orders' and 'materials' tables, using OLEDB from the beginning
iii) Add only the mat_code field from the main table to the report
iv) Add a subreport for 'materials' linked on mat_code
v) Show only mat_code on the subreport
vi) Run the app
If data shows, the problem is either:
Answer 1: A mismatch between the database definition (as was read when the report was FIRST made, and connected via ADO.NET to the tables) and the actual data table/column definitions being found when loading the report later (i.e. Someone edited the 'materials' table to change a column definition, or the number of fields, etc.)
Answer 2: Possible defect in the particular combination of Crystal Reports and Windows drivers required to push ADO.NET data to the report. Using the pull-data model over OLEDB may be working around something issues. Those issues may be addressed with the latest Crystal Reports and/or Windows drivers (i.e. hotfixes or Windows updates, driver packs, etc.)
The test worked fine. Even without making a subreport. The data pulled in just like it was supposed to.
Check the link between the two tables in crystal reports. Are they as what you expect ?
Check it by
Field Explorer> DataBaseFields > (RightClick) DatabaseExpert > (See Tab) Links
I have a table Called EmployeeTypes which hold types of Employees, now i want to create a report using SSRS which will have something like this:
EmployeeType1 EmployeeType2 EmployeeType3
4 23 2
where the numbers are the count of employees.
problem is how can i generate the columns programmatically like that as the EmployeeTypes Table can have many Types and expand by time?
It sounds like you are looking for a cross tab report, using a data set like select count(*), employee_type from employee_table group by employee_type.
You can use the report wizard to create a 'Matrix' report type (as opposed to a 'Tabular' report type). The wizard will guide you through the steps to get what you need.
An SSRS report is defined by an XML file (the .RDL or .RDLC file). You could generate columns programmatically by directly modifying the XML. Not for the faint of heart, but people do it, and it can be done.
Here is a sample:
http://www.codeproject.com/Articles/11254/SQL-Reporting-Services-with-Dynamic-Column-Reports
I have a database table with the following structure:
Columns: DealID (int), CustomerID (int), DocumentArchiveID (int), DealXML (string of XML)
The issue is:
a value for the CustomerID field can appear multiple times in the table but for each time it appears the DealXML associated with it is unique. I need to connect to the table and create a file of the XML contained in the DealXML column - but only one file per CustomerID. So basically if a CustomerID appears 5 times I need to create one file with all the related DealXML data in that file... if a CustomerID appears only once then I create a single file with just that CustomerID's DealXML in it. So at the end of it all I need one file per each unique instance of CustomerID but each file should contain all the DealXML data associated with that particular CustomerID. I am at a complete loss as to how to accomplish this and I'm facing a deadline.
Summary: need to create a file for each unique instance of CustomerID (a CustomerID can appear many times in the table, each time with a distinct DealXML that needs to go into the file) - each file contains all the DealXML for that particular CustomerID.
I thought of creating a FileStream with FileMode.Append, creating a unique file for each unique CustomerID in the database but unfortunately I do not have control over what I name my files (they must adhere to a convention established outside my organization) and cannot use this method to create unique files (based on the CustomerID) which was my first idea.
I'm doing this in .Net platform using C# and a SQL Server 2008 database.
Any help? Anyone? Feedback deeply appreciated.
First, you need a query that brings you back everything in the correct order:
SELECT CustomerID, DealID, DealXML FROM TABLE ORDER BY CustomerID, DealID
Next loop through the results. Every time you encounter a new CustomerID, just create a new file (using whatever conventions you need to) and write the xml (Note that you will have to wrap all of the DealXML values with a root element to create well formed xml):
<customerData>
<DealXML />
<DealXML />
<DealXML />
</customerData>
EDIT: You can do something like this (this is some sketch code - but this gives you the basic idea)... The basic idea is you put the deal xmls into a collection until you move to another customer id.
int previousCustomerId = -1;
List<string> deals = new List<string>();
while(rs.ReadNext())
{
int customerId = rs["CustomerID"];
if (customerId != previousCustomerId)
{
//Don't do this on the first go
if (customerId != -1)
{
//Generate a filename
string filename = GenerateFileName(customerId);
//There are better ways to write multiple values to a file,
// but this should give you an idea of where to start.
File.AppendText(filename, "<customerData>");
//Dump all of the DealXML values to the file
foreach(string deal in deals)
File.AppendText(filename, deal);
File.AppendText(filename, "</customerData>");
}
//Reinitialize the list
deals = new List<string>();
//Save the new customer id
previousCustomerID = customerId;
}
deals.Add((string)rs["DealXML"]);
}
Well, a brute-force approach would be to query for distinct CustomerIDs. If DealXML is nullable, you should select distinct CustomerID from table X where DealXML is not null
Then, loop over the resulting dataset. In each loop, query for the DealXML for the current Customer ID. You'll get back a dataset with 1 or more rows; open a new file, loop thru the dataset, and append to the file.
You'll need to put a wrapper around each customer's XML to be well-formed...