Santry Technology Solutions, Content Management, DotNetNuke, SharePoint Consulting
Register | Login
Thursday, March 11, 2010

Sections
  
About Us
  
Partners
Downloads
  
 WWWCoder.com Resource Directory

Descendant Count Using ASP.NET Code
6/3/2004 2:31:56 PM

In this article we are going to provide a method of using dynamic SQL to generate a descendant count of records under nested categories. This is similar to what you see with most directories including WWWCoder.com where you have a directory structure and each category contains a count of all children and descendants.

Other Options

Before I get comments saying there are other ways of doing this, I know that. In a previous method I provided a way of doing this using recursive SQL user defined functions. There are some cases where you may not be able to use functions. I prefer functions since all the recursion is done on the SQL server and this reduces the amount of calls to the SQL database over the network. Given this however, you may be running Access or an older version of SQL that doesn't support functions. Another reason is the ability to generate automated installation scripts may be an issue when using functions.

Building SQL

This method basically creates a SQL string and then adds to the count of the descendents. Your table structure basically consists of a category name, an id which is your primary key, a parent id field which contains the id of the parent category, and then a count field. In this method we are going to populate the value of the count field anytime a site is added or removed. If you've read the SQL function article, there is no need for a count field since this is calculated each time a request is made for the records.

The Method

Below we have a method that is called whenever a site is deleted or added to the category. We need to be able to update the descendent count since things have changed.


Public Sub UpdateSiteCount(ByVal ParentID As Integer, ByVal intLevel As Integer)
  Dim SQLQ, SQLQ1, SQLQ2 As String
  Dim tmpLevel As Integer = intLevel
  SQLQ1 = "Select CategoryID From Categories where  (ParentID = " & ParentID & ")"
  Dim dr As IDataReader = ExecuteSQL(SQLQ1)
  While dr.Read
    'build our category ids for count updating.
    If tmpLevel = 0 Then
      _ParentPaths = dr.GetInt32(0).ToString & ","
    Else
      _ParentPaths += dr.GetInt32(0).ToString & ","
    End If
    SQLQ = "Select Count(*) From Sites Where SiteActive = 1 AND SiteCatID = " & _
          dr.GetInt32(0).ToString
    'execute our SQL string using a helper method.
    Dim dr2 As IDataReader = ExecuteSQL(SQLQ)
    While dr2.Read
      If (dr2.GetInt32(0) <> 0) Then
        SQLQ2 = "Update Categories Set SiteCount = (SiteCount + " & _
            dr2.GetInt32(0).ToString & ") WHERE CategoryID IN (" & _
            Left(_ParentPaths, Len(_ParentPaths) - 1) & ")"
        ExecuteSQL(SQLQ2)
      End If
    End While
    dr2.Close()
    dr2.Dispose()
    'now call the method unto itself in order to do the recursion.
    UpdateSiteCount(dr.GetInt32(0), tmpLevel + 1)
  End While
  _ParentPaths = Trim(Replace(_ParentPaths, ("," & ParentID.ToString & ","), ","))
  dr.Close()
  dr.Dispose()
End Sub

Looking at the method above you can see it accepts a parent id, and the current level. Current level is for letting us know at what point in the recursion we are at, meaning is the first call to the method or is it higher than that. Parent id provides us with the current category id, each time we loop though and get a category, we pass the id of the current category to the method in order to get any direct child categories of it.

You can see the first thing we do is build a SQL string to get any category ids of children of the parent id passed to the method. We then loop through these records, as we're looping through the records we build another SQL string, this time it is an Update which is going to update the cumulative count of descendents for our categories. If you look at the string being generated here you can see how this count is going to be cumulative, basically we created a variable that is global to the class and for each recursive call we append the category id to it. This variable is the _parentPaths variable that is being concatenated at the very beginning of the loop. Since we have this string of ids we then update the count of all records that are in that pool of ids contained in the _parentPaths variable. Each time a new category id is being added for the child, sine the previous category ids are already in the string they get their count updated as well for each iteration of the method.

We then use a helper method to execute our SQL string in order to perform our update. This method we won't discuss here, but it basically executes a SQL command. The main point of this article is building the SQL string required in order to update our count.

Finally the we run out of records to update and the method will eventually end.

There are some drawbacks to this method as described earlier and you may want to consider using SQL functions or whatever your infrastructure permits. Performance can seriously be degraded for a method like this, you have to make several calls to the database over the network in order to perform this operation. You may want to consider running a method like this as a batch job rather than on every update.

Related Articles
   Related Document Recursive SQL User Defined Functions


Page Options:
format for printing  Format for Printer
email article  Email Page
add to your favorites   Add to Favorites
How would you rate the quality of this content?
Poor - - Excellent
Comments?
Overall Rating:
Comments Left:
Left on 4/27/2005 3:21:41 AM by Anonymous
Comments: not really the most efficient way of doing this. poor example
Left on 3/21/2005 4:44:33 PM by Anonymous
Comments: great article
Left on 2/9/2005 5:47:34 AM by Anonymous
Comments: If you call recursively a user defined function above 32 times in a SQL server. It will throw an error then we need to go for this they of method only.
Left on 7/25/2004 5:03:41 PM by Anonymous
Comments: Here's an article that could get you started without even using the DataRelation object if necessary.

http://www.eggheadcafe.com/articles/20031014.asp


No ratings available.
Left on 7/25/2004 4:59:56 PM by Anonymous
Comments: Plus, there are other alternatives with ADO.NET that utilize the DataRelation object but not User-Defined Functions.  No matter how you slice, SQL and recursion can almost always be avoided by using a better technique.
Left on 7/25/2004 4:56:22 PM by Anonymous
Comments: I was just trying to add a little humor.  Relax.  A User-Defined Function could have been integrated to grab some of the counts as the result set was being formulated rather than repeat trips back to the database.  Granted this code probably works but it would be much slower than better coding techniques.  I just mentioned one of them...  If the author had any experience with hierarchical data and the need to deliver these types of results in a high performance environment, he'd already know his methodology is exactly what one should avoid.
No ratings available.
Left on 7/24/2004 6:06:44 PM by Anonymous
Comments: No wonder you don't mix with liberals.  To mix with someone you need to actually listen to them before you condemn their ideas :).

I suppose this does have to do with politics, because it shows how so many people, even highly educated programmers, are willing to cast a vote without even bothering to look closely at what they're voting about.  Chris
Left on 7/23/2004 3:03:04 PM by Anonymous
Comments: Again another person saying a User Defined Function, did you read the article?! And WTF does this have to do with politics??!!
No ratings available.
Left on 7/23/2004 2:59:57 PM by Anonymous
Comments: This could be handled with the DataRelation object in ADO.NET, a single stored procedure, and a single User-Defined Function.  Its performance would stomp this method into the ground.

SQL and recursion are like myself and Liberals.  they just don't mix.
Left on 7/23/2004 12:07:26 PM by Anonymous
Comments: The above code is perfectly good. 

Why?  Because if one puts it in a site, it will work.  If the site starts getting a lot of traffic, it may start slowing down.  In which case, the developer will know that it is time to put effort effort into optimization.  And the best optimization wouldn't be a stored procedure, which would also ultimately break down under stress, but (as the author states!) a batch process, or a better way of permanently storing the results.  And both concepts are beyond the scope of the article, which is to get a "Descendant Count using ASP.NET Code".

Where do you people get off, dissing an article without reading it?  The first paragraph even links to a previous article that explains how to do the same thing in a stored procedure!

Chris
Left on 7/23/2004 9:19:54 AM by Anonymous
Comments: For all you people who think your way is the best, it may be, but the bottom line is the client is happy that the application works.  Stop your complaining and thank the person for providing the example in the first place, they had to take time away to write the article didn't they?  If you have a better way, write your own article.  There are usually reasons why somone has done something a certain way, either because of restrictions or a special requirement.  We are all in this together to make a living in IT, lets support each other and make the profession better together!
No ratings available.
Left on 7/23/2004 8:46:55 AM by Anonymous
Comments: Yes, it appears that several people only read the title and the sample code and skipped the first paragraph which explained the usage of this code.  Or the last paragraph, which again reminds the user that SQL is the better way. Some people just like to bash the work of others ...
Left on 7/23/2004 7:21:30 AM by Anonymous
Comments: Obviously all the comments below didn't read the first paragraph where it was stated there wasn't access to SQL. Nice.
No ratings available.
Left on 7/23/2004 6:11:03 AM by Anonymous
Comments: This would be fine if nobody ever looks at your site and youre happy with poor scalability. Operations like this are much better accomplished with recursive stored procedures. There is no need to use code like the above if you have access to stored procedures.
Left on 7/23/2004 4:54:44 AM by Anonymous
Comments: Hmm...not great, this problem is better solved using Nested Intervals / Nested Sets in SQL
Left on 7/23/2004 4:39:30 AM by Anonymous
Comments: Stupid way of solving this problem. A bad example to readers.
Left on 7/23/2004 3:47:13 AM by Anonymous
Comments: This sort of task would surely be better situated within a trigger/stored proc?
  

 Latest Articles
  

 Latest News
  

 

Spotlight
Syndication

 


 


Digg This
 


DotNetNuke Platinum Benefactor

  
 

 Terms Of Use | Privacy Statement
 Copyright 2008 - Santry Technology Solutions, Box 172, Girard, PA 16417, (814) 774-0970