Brendan Tompkins [MVP]

Sponsors

The Lounge

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Multi-Table DataSet with DataRelations from a Single-Table DataSet

Recently, I was tasked with creating a reporting application that did the following:

  1. Allow the user to choose Columns in one DataTable in a DataSet to group by.
  2. Render a grouped report from the flat table data.

So, for example, if the DataTable looked like this:

Department EmployeeID Product Count
Purchasing 3322       Apples  1
Purchasing 3322       Oranges 1

Purchasing 3311       Oranges 2
HR         1222       Apples  1
HR         1111       Apples  3

I had to generate a rollup report that looked like this:

Department: Purchasing
  Employee: 3322
    Product: Apples  Count: 1
    Product: Oranges Count: 2
                     Total  3
  Employee: 3311
    Product: Oranges Count: 2
                     Total: 2
          Purchasing Total: 5

Department: HR
  Employee: 1222
    Product: Apples  Count: 1
                     Total: 1
  Employee: 1111
    Product: Apples  Count: 3
                     Total: 3
                  HR Total: 4
               Grand Total: 9

You can see that the solution was going to involve recursion of some sort.  The trouble was, recursion on a flat DataTable is a nightmare!  If you've ever tried it, you know what I mean. Forget using foreach, and bone up on arrays if you're going down this route.

Well, I wrote the code to do this recursion, but I wasn't happy with what I had come up with.  It was ugly code, and hard to follow.  I was complaining to a friend of mine about the complexity, and drawing on a white board.  I drew a picture of an ADO DataSet with DataTables related with DataRelations, and said “If I only had this, this would be easy”...  Then it hit me.  I needed to generate a multi-table DataSet from a flat, one-table DataSet.  I could then use the GetChildRows() method of each data row to find out if I needed to recurse. I could also use a foreach to iterate through the DataRows. 

What I came up with is the following method, that I thought I'd share.  What it does is take a flat DataSet with one DataTable and a string array of ColumnNames that are to be used to generate a DataSet with multiple, related dataTables.  It returns the root table name, which you'll need to start your recursion:

public static string GetNestedDataSet(DataSet flatDs, ref DataSet nestedDs, string [] columnNames)
{
   
string rootTableName = null;
    nestedDs = flatDs.Copy();
    DataTable dataTable = nestedDs.Tables[0];

   
// Store the previous iteration's previous columns
   
DataColumn [] prevUniqueColumns = null;

   
foreach(string columnName in columnNames) 
    {
       
// Add the table
       
DataTable tbl = nestedDs.Tables.Add(columnName + "Table");
       
if(rootTableName == null) rootTableName = tbl.TableName;

        // Store the current iteration's columns
       
ArrayList currentColumns = new ArrayList();
       
if(prevUniqueColumns != null)
        {
           
// Add all the previous columns
           
foreach(DataColumn col in prevUniqueColumns)
            {
                currentColumns.Add(
new DataColumn(col.ColumnName));
            }

           
// Add the current Columns
           
DataColumn [] foriegnKeyCols = (DataColumn []) currentColumns.ToArray(typeof(DataColumn));
            tbl.Columns.AddRange(foriegnKeyCols);

            // Add the parent relations
            tbl.ParentRelations.Add(prevUniqueColumns, foriegnKeyCols);
        }

        // Now add the new column
       
currentColumns.Add(tbl.Columns.Add(columnName)); 

       
// Get the key columns for the current table
       
DataColumn [] primaryKeyCols = (DataColumn []) currentColumns.ToArray(typeof(DataColumn));

       
// Make everything unique
       
tbl.Constraints.Add(tbl.TableName + "_unique", primaryKeyCols, false);

       
// Store columns for next time...
       
prevUniqueColumns = primaryKeyCols;

       
// Now add the data...
       
foreach(DataRow row in nestedDs.Tables[0].Rows)
        {
               
try
               
{
                       
ArrayList al = new ArrayList();
                       
foreach(DataColumn c in primaryKeyCols) al.Add(row[c.ColumnName]);
                        tbl.Rows.Add((
object[]) al.ToArray()); 
                }
               
catch(System.Data.ConstraintException){}
        }
    }
    
    
// Finally, add the table relationship to the base table
    
if(prevUniqueColumns != null)
    {
       
// Add the current Columns
       
DataColumn [] foriegnKeyCols = new DataColumn [prevUniqueColumns.Length];
       
for(int i = 0; i<prevUniqueColumns.Length; i++)
        {
                foriegnKeyCols[i] = dataTable.Columns[prevUniqueColumns[i].ColumnName];
        }
        dataTable.ParentRelations.Add("BaseRelation", prevUniqueColumns, foriegnKeyCols);
    }

    // This happens when we have no rollup columns
    
if(rootTableName == null) rootTableName = dataTable.TableName;
    
return rootTableName;
}

Now, to use this, do the following:

DataSet nestedDataSet = null;
string rootTableName = GetNestedDataSet(MyFlatDataSet, ref nestedDataSet, string [] {“Department“, “EmployeeID“, “Product“});

Here's an example (warning: Air Code) of the recursive routine to do the rendering:

private int RenderGroup(DataRow parentRow)
{
      // Render out something here, like parentRow[0]

     // Now get the children rows, if any and recurse
      
DataRelation childRelation = (parentRow.Table.ChildRelations.Count > 0) ? parentRow.Table.ChildRelations[0] : null;
     
if(childRelation != null)
         foreach(DataRow row in parentRow.GetChildRows(childRelation))
            RenderGroup(row, showDetail, container);
}

That's it!  I put this method in my Utils class for ease of use.   I hope that posting this will save someone else the agony of trying to write recursive routines against a flat DataTable.

-Brendan


Posted 04-15-2004 7:23 AM by Brendan Tompkins
Filed under:

[Advertisement]

Comments

Darrell wrote re: Multi-Table DataSet with DataRelations from a Single-Table DataSet
on 04-15-2004 4:21 AM
Very nice.
Dave Burke wrote re: foreach (table in tables)
on 04-19-2004 1:58 AM
vvn wrote re: Multi-Table DataSet with DataRelations from a Single-Table DataSet
on 03-30-2005 1:57 AM
good
Srivaths wrote re: Multi-Table DataSet with DataRelations from a Single-Table DataSet
on 03-06-2006 10:40 PM
hi. its really good. but i thinkthe naming conventions can be better. and i have a problem. i have written an aored procedure which returns 3 tables. how do i populate thosethree tables in a dataset and how do i generate a report using that dataset? it would be really great if you could answer my qurstion.bye.
tom wrote re: Multi-Table DataSet with DataRelations from a Single-Table DataSet
on 08-06-2007 5:03 PM

Hi Brendan,

fantastic post. exactly what i needed (i think) for my 3 level data report (from a flat dataset)

could you point me in the direction for implementing your dataset into nested repeaters?

thanks in advance

tom wrote re: Multi-Table DataSet with DataRelations from a Single-Table DataSet
on 08-06-2007 9:32 PM

never mind, worked it out!

fantastic code again thanks.

the following may help future people:

i bound my first repeater to:

nestedDataSet.Tables["myTopGroupingColumn"];

in my first nested repeater (2nd repeater)

DataSource='<%# ((System.Data.DataRowView)Container.DataItem).CreateChildView("Relation1") %>'

in my final repeater (3rd)

DataSource='<%# ((System.Data.DataRowView)Container.DataItem).CreateChildView("BaseRelation") %>'

in each item template its as easy as <%# Eval("eventId")%>

Anees wrote re: Multi-Table DataSet with DataRelations from a Single-Table DataSet
on 10-04-2008 6:11 AM

Hi,

I m new to .net

i m not getting how i should call this function into my pgm

plz help

thanks

nakliyat wrote re: Multi-Table DataSet with DataRelations from a Single-Table DataSet
on 01-01-2009 6:22 AM

Very good article thank you...

win wan yan wrote re: Multi-Table DataSet with DataRelations from a Single-Table DataSet
on 01-21-2009 11:43 AM

amazing thank you sooooooooooo much

Add a Comment

(required)  
(optional)
(required)  
Remember Me?