I will start with a question here. How many of you have had a chance to interact with the Employee table from the sample database Northwind? There you go… I can imagine countless hands in air, and why not, it is one of the standard databases that comes with both Access and SQL server. All right, are we going to discuses the Northwind database here? No. Is the Employee table something special? I would say yes. Why special? Well, if you pay close attention, it is just like any other standard table; however, two fields from the table, “EmployeeID” and “ReportsTo” are related to each other in an interesting way! Yes, you got it right; I am talking about hierarchical relationships, which we also call commonly as recursive data. I am trying to shed some light on reporting of data which is recursive in nature.
What is Recursive Data?
I am sure you must have come face to face with this challenge called Recursive Data if you had to deal with databases. Hierarchical data which defines the level of association with a defined relationship can be called recursive in nature. A typical example would be to take an accounting application database which has a table called ChartOfAccounts. The Primary Key “Account_Id” will have a foreign key relationship with another column called “Reporting_Account_Id”. Another example is the one which I am using in this article, where each Employee has a Manager assigned.
Do you recall “Self-Join”? As you can see in the above image, this is one way we display the recursive nature of data, just putting it here for some better understanding.
Reporting Challenge for Recursive Data
I am ready with my second question here; before I ask you, I would like your kind attention to the image with the Employee level output. Now the question: Do you think generating report like that without any custom code is a piece of cake? I am pretty sure, this time I will see many less hands in air compared to my first question! Or you can say yes, it is piece of cake, if you have already tried your hands on MS Reporting Services.
It is very common that when we have to deal with a situation like this, we do end up writing some sort of custom code in order to find out the level of hierarchy etc. A typical developer mindset will always have an endless list of wishes for software vendors, one of my wish was if something was done to address this issue of handling recursive data built right into the reporting engine. Some how, I felt my telepathy worked, and guys at Microsoft put this feature into Reporting Services, and here I am acknowledging them by writing a few lines here. Though, I would like to clarify one point here, I have worked with several other reporting engines and did enjoy them, however, since I started to work with Reporting Services, I personally felt much at ease.
Let’s wear our Report Writing hat now…
When I look at different reporting engines out there in market, the underlying concept remains very much the same; I am talking about the header, footer, data region, data grouping, summary etc. So, even if you are not yet exposed to Reporting Services, don’t worry if you have some working knowledge of any reporting engine, you will not have much difficulty to grasp the concepts laid down in this article.
With this article, I would also like to show the reader how Reporting Services can be used with Smart Client Windows Forms applications in a client environment.
I assume the reader of this article is comfortable using Visual Studio 2005, C#, SQL Server 2000, and Windows Forms. This article is not at all a “Reporting Services 101”, hence I will assume that you will try to play with the attached code and figure out the secrets hidden with it.
Implementing Reporting Services into a Smart Client is like 1.2.3…
- Create a
- Create Report
- Use the
Preview control to generate a report with the ADO.NET code interface
1. DataSet at your service
In order to create a dataset, just click Add New Item from Solution Explorer. Select
DataSet from the Visual Studio installed templates and give it a proper name. After the
DataSet is created, open it in a designer window and add a
DataTable to it. After you have added a
DataTable, add the required columns to it. In this example, I have three columns added, namely, EmployeeName, EmployeeID and Reports_to. Please make sure to set the
DataType property of each column to
DataSet should typically look like the above image. Now that we have our
DataSet ready, you will shortly see a fun way to fill it using a new technique introduced in ADO.NET of using a
SqlDataReader to fill a
Dataset (I guess my telepathy worked here too).
2. Report Design
As we did with
DataSet, just click Add New Item from Solution Explorer. Select Report from the Visual Studio installed templates and give it a proper name. As I clarified earlier, I am not going into the details for each and every control/elements of the Report Designer; instead, I will only point you to the important location which needs attention in order to create a report which uses recursive data.
As you can see in the above image, this is how my reports look like in designer. Typical of a report writing tool, Reporting Services also have an interface where you can define the header and footer to begin with, and move on to report body etc. In the header section, I have the Report Title (Magenta color) and Run Date (Blue color).
The most interesting part which I felt is the Body section, also called data region. Data region allows you to put several new exciting controls which basically decide how the data will be outputted. I have used the “
Table” control here which comes with a ready header and footer for it when placed on the designer surface for the first time.
TextBox control is used heavily to display information. If you look at the image, you can see that I just placed a textbox control and simply typed the report title inside. When it comes to specifying expressions, all you have to do is start with a “=” sign in front. You can check the Run Date example in which I am concatenating the string “Run Date” and the VB.NET function “
Today” to return the current date.
After putting all the required controls on the designer surface and making sure the layout meets our taste, it is time to spell the magic beans which will automatically handle the recursive nature of data and manage hierarchy level etc.
The trick is to put the grouping on the Detail section (make sure to select the Detail band and right click to access the group menu choice), by specifying the group on “EmployeeID” and the parent group “ReportsTo” as per the image below:
The report writer has a useful in-built function called “
Level”, which returns the current level of depth in a recursive hierarchy.
For the next output column in the report level, we will specify the following expression:
=Level("tableEmployee_Details_Group") + 1
Level function returns an integer starting with 0 for the first level; hence, I have added a 1 to the end result here. So, in our example, employee “Andrew Fuller” is topmost level. You can easily use a function like
IIF() to take this level number and substitute with something like “CEO”, “General Manager” etc.
The third and the last column in the report displays the count of all the employees who are reporting to a given employee record. The following expression does the trick for us:
"tableEmployee_Details_Group", Recursive) - 1
For both the expressions, "
tableEmployee_Details_Group" is used as reference name to group the definition which we applied on the Detail band of the data.
Did you also notice an interesting thing about the hierarchical formatting of EmployeeName in the report output? This is also done fairly easily with the following expression which you need to specify in the Padding->
=Level("tableEmployee_Details_Group") * 20 & "pt"
Based on each incremental level, it will add 20 pt to the left side of EmployeeName and the output will look like a try structure.
3. Show me the report!
I know after going through all that preparation, we are eager to see the output for report, aren’t we? The following code will just do that!
You can start by putting ToolBox->Data->ReportViewer control on a standard Windows Form. I am using C# here within the Windows Forms application framework. The same can be manipulated easily for an ASP.NET application framework, and further, could can be easily converted to VB.NET if that is what you use as your primary scripting language.
Make sure you have the code-behind Form
Load method as follows:
private void Form1_Load(object sender, EventArgs e)
string cnString = @"Data Source=(local);Initial Catalog=northwind;" +
SqlConnection conReport = new SqlConnection(cnString);
SqlCommand cmdReport = new SqlCommand();
DataSet dsReport = new dsEmployee();
cmdReport.CommandType = CommandType.Text;
cmdReport.Connection = conReport;
cmdReport.CommandText = "Select FirstName + ' ' + Lastname" +
" AS EmployeeName, EmployeeID, " +
"ReportsTo From Employees";
drReport = cmdReport.ExecuteReader();
ReportDataSource rds = new ReportDataSource();
rds.Name = "dsEmployee_dtEmployee";
rds.Value = dsReport.Tables;
catch (Exception ex)
if (conReport.State == ConnectionState.Open)
My favorite section - About...
As, we all know, there is always more then one way to address an issue. I am certainly not suggesting this is the only way we can handle data which is recursive in nature. I would love to hear from you if you like to share some of your tricks and am looking forward to having any constructive criticism you have got for me.
Disclaimer: Please feel free to use the content of this article as you may please; however, I won’t be held liable for any adverse effects, if at all it produces.
Thanks for reading… till my next attempt. Chao.