Wednesday, August 13, 2008

Recursive SQL User Defined Functions

In this article we will cover the new feature of SQL Server 2000 and the ability to create user defined functions. We will focus on creating recursive queries in this article to extend upon previous articles.
Being the developer for this directory, I had a need to perform several recursive methods. Any time you are creating a tree like data structure like directories, org charts, etc. You will need to perform some kind of recursion. In this example, I wanted to be able to provide a count of all descendents sites under a particular category as you see on WWWCoder.com when navigating the site. Each category contains a count of descendent sites of all categories under that specific category.
In the past there were several ways to perform this, one simple method was to create a field in the categories table that would get incremented each time a site was added. This was accomplished using a recursive method in the ASP.Net code that would create a new connection to the database each time a new record was added. It did eliminate the need to call a recursive function for each time a person requested a page to display the category navigation. Basically the method performed would accept a category id and a string value to increment or decrement the count contained in the site count field:

       
Public Sub UpdateParentCount(ByVal ParentID As Integer, ByVal AddSubtract As String)
 Dim SQLQ As String = "UPDATE Categories Set SiteCount = SiteCount " & AddSubtract & " 1"
 If AddSubtract = "+" Then 'add a new icon.
  SQLQ = SQLQ & ", DateSiteAdded = '" & Date.Today & "'"
 End If
 SQLQ = SQLQ & " WHERE CategoryID=" & ParentID
 Dim secondConnection As New SqlConnection(GetDBConnectionString)
 Dim secondCommand As New SqlCommand(SQLQ, secondConnection)
 secondCommand.CommandType = CommandType.Text
 secondConnection.Open()
 secondCommand.ExecuteNonQuery()
 secondConnection.Close()
 secondConnection = Nothing
 Dim SQLQ2 As String = "SELECT ParentID FROM Categories Where CategoryID = " & ParentID
 Dim myConnection As New SqlConnection(GetDBConnectionString)
 Dim myCommand As New SqlCommand(SQLQ2, myConnection)
 myConnection.Open()
 Dim result As SqlDataReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection)
 If result.Read Then
  UpdateParentCount(result("ParentID"), AddSubtract)
 End If
 myConnection.Close()
 myConnection = Nothing
End Sub 
 
 

This wasn't too bad of solution, since I was able to eliminate the hits on the database for the count. The problem here is a count can become incorrect if any changes are performed outside of the application.
The solution for me had to meet the following requirements: reduce traffic between the Web server and the database server, and make sure an accurate count is always available in the database regardless of what modifies the records that it contains. In order to accomplish this, the count method was moved out of the ASP.Net code and into the database. SQL Server 2000 supports User Defined Functions that can be called from a stored procedure, in addition, the function can be recursive up to 32 levels. Since I can't see a need to go beyond 32 levels deep of categories, I opted to use the functions for creating the count. Here is an example of using a function from within a stored procedure:

ALTER procedure GetCategories
 
@ParentID   int
 
as
 
BEGIN
SELECT 
    CategoryID, CategoryName, Path, SiteCount, DateSiteAdded, 
    ParentID, SortColumn, dbo.CountChildren(CategoryID, 0) 
    As CulCount
FROM 
    Categories  
WHERE 
    ParentID = @ParentID
ORDER BY 
    SortColumn, CategoryName
END 
 

You'll notice in the SQL stored procedure's select statement there is a call to a function called CountChildren, in this function we pass the category id of the current category and the current cumulative count of the sites within the category.

ALTER FUNCTION dbo.CountChildren
(@id int, @cChildren int) 
RETURNS bigint 
AS
BEGIN
 
IF EXISTS (SELECT     
    Sites.SiteCatID
FROM         
    dbo.Categories 
INNER JOIN
    dbo.Sites 
ON 
    dbo.Categories.CategoryID = dbo.Sites.SiteCatID
WHERE 
    dbo.Categories.ParentID = @id OR dbo.Sites.SiteCatID = @id)
BEGIN 
   SET @cChildren = @cChildren + (
     SELECT 
        Count(SiteCatID) 
        FROM 
            Sites 
        WHERE 
            SiteCatID = @id AND SiteActive = 1)
  SELECT 
              @cChildren = dbo.CountChildren(CategoryID, @cChildren) 
            FROM 
              Categories 
            WHERE 
              ParentID = @id 
END 
  RETURN @cChildren 
END  
 
 

As you can see the function calls itself just as a recursive function in VB.Net would do, each time incrementing the cumulative count of all descendents of a particular category. In the end we have all the information generated on the SQL machine, and then it returns what we need without having to call a recursive method in the ASP.Net page and generate all the additional database calls over the network.
By: Patrick Santry, Microsoft MVP (ASP/ASP.NET), developer of this site, author of books on Web technologies, and member of the DotNetNuke core development team. If you're interested in the services provided by Patrick, visit his company Website at Santry.com.

Source: http://www.wwwcoder.com/

Recursive Sub-Procedures in ASP

This tutorial explains how to do recursive subroutines in ASP (VBScript). Use this algorithm to create threaded discussions, directories, or whatever use you have for it.

One of the best algorithms to know as a Web developer is how to code recursive sub procedures. What's recursion, and how does it help you as an ASP developer? Recursion allows you to code parent-child relationships. Parent-child relationships are the essence of directory tree-like structures. Trees allow you to develop threaded discussions, directory search engines (like santry.com), and perform lookups through your directories.

This process is done by calling a subroutine unto itself. What happens is the subroutine is initially called and then the subroutine calls itself until an end or condition is reached. Say for instance your doing a directory listing to list the contents of a directory, then each time the procedure encounters a new directory it calls itself to list the contents of the subdirectory, then the subroutine calls itself again to list the contents of directories in this sub directory and so on.


You can create this parent-child relationship in a database table. For example, the following table structure in a database:

 

RecordID
(autonumber)

ParentID

DisplayName

1

0

Topic 1

2

0

Topic 2

3

1

RE: Topic 1

4

1

RE: Topic 1

5

2

RE: RE: Topic 1

6

2

RE: RE: Topic 1



You can see from the above table that we have several records, some of the records have a 0 as a ParentID, meaning this is a top level parent record, then other records do have a value other than 0 in the ParentID, meaning they are children of the record that has a matching ID in the table. This structure is very versatile, in that you can have unlimited child records using this structure, thus allowing you to create as many nests or branches you wish. You should now see why this algorithm is very useful when it comes to the Web. This structure drives those threaded forums, directories and all the cool apps that you want to be able to create.


The next piece of code shows how to make the SQL call and then call the subroutine in order to display this structure to the user.
Set DBConn = Server.CreateObject("ADODB.Connection")
DBConn.Open "DSN=MyDSN"
'here we initially call the sub routine, we pass 0 as the parent ID
'this will pull all top level parent (meaning they don't have an 'ancestor).
'we also pass 0 for the level, this is used for spacing, or
'making the results appear threaded.
DoTree(0,0)
'----------------------------------------------------------
Sub DoTree(ParentID, intLevel)
Dim SQLQ, DBConn, rs, i
SQLQ = "SELECT RecordID, DisplayName FROM RECORDS " & _
      "WHERE ParentID = " & ParentID
       Set rs = DBConn.Execute(SQLQ)
       If Not rs.EOF Then
           Do Until rs.EOF
                 Response.Write "<img src=Spacer.gif Width= " & _
                 15 * intLevel & ">"
                 Response.Write rs("DisplayName") & "<br>"
'now call the subroutine we're in to see if this value has
'any children and increase the indent, and so on...    
                DoTree rs("RecordID"), intLevel + 1 
               rs.MoveNext
           Loop
       End If
       rs.Close
       Set rs = Nothing 
End Sub
'------------------------------------------------------------
DBConn.Close
Set DBConn= Nothing
'Once this routine is execute you should see results similiar to this:
Topic 1
    RE: Topic 1
        RE: RE: Topic 1
        RE: RE: Topic 1
    RE: Topic 1
Topic 2

So you can see from this example how you can use this to create a threaded type forum. You'll need to play around with the preceeding code a bit and find out how you can put it to use in your application.

Source: http://www.wwwcoder.com/

Recursive Functions in ASP

A function that calls itself repeatedly, satisfying some condition is called a Recursive Function. Using recursion, we split a complex problem into its single simplest case. The recursive function only knows how to solve that simplest case. You'll see the difference between solving a problem iteratively and recursively later.

Warning!

Beware! Use the recursion technique carefully, if you fail to handle it appropriately you can end up with a never-ending ASP application.

Same Old Factorial problem

Almost for every language, the beginners learning the concept of recursion are presented with the same old Factorial problem. I'll follow the traditional path, because I feel that it's the easiest way to understand the basic concept of recursion.

Understanding the problem

In mathematics, the factorial of a positive integer n, written as n! is represented by the following formula:

n! = n . (n-1) . (n-2) . (n-3) . Š. . 1

In plain english, factorial of a number is the product of that number with a number 1 less than that number, this multiplication process continues until the number which is being multiplied, eventually becomes equal to 1.

Iterative Solution

In the following function the value of 5! is calculated iteratively:

<%  
Response.Write iterativeFactorial (5)

Function iterativeFactorial ( intNumber )

Dim intCount, intFactorial

intFactorial = 1
For intCount = intNumber To 1 Step -1

intFactorial = intFactorial * intCount
Next
iterativeFactorial = intFactorial
End Function
%>

Recursive Solution

The following function represents the recursive solution for the factorial problem.

<%  
Response.Write recursiveFactorial (5)
Function recursiveFactorial ( intNumber )
If intNumber <= 1 Then
recursiveFactorial = 1 Else
recursiveFactorial = intNumber * recursiveFactorial ( intNumber - 1 )
End If
End Function
%>

The working of the above function recursiveFactorial is simple. If the integer argument "intNumber" is less than or equal to one, then the function returns 1, else the return value of the function is the product of intNumber with the value returned by the same function for integer argument "intNumber-1".

Answer of the above problem:

5! = 5 . (5-1) . (5-2) . (5-3) . (5-4)

Simplifying:

5! = 5 . 4 . 3 . 2. 1 = 120

Example: Folder Tree

The following example will display the tree structure for a specified folder.

<%
' Displays the Tree structure, replaces vbTab character with 3 HTML spaces

Response.Write Replace (DisplayFolderTree ("c:\windows", 0), vbTab, "   ")

Function DisplayFolderTree ( strFolder, intTreeLevel )

Dim objFileSystem, objFolder, objSubFolders, objTempFolder
Set objFileSystem = CreateObject("Scripting.FileSystemObject")

' Get the name of current folder
Set objFolder = objFileSystem.GetFolder ( strFolder )

' Get the list of subfolders
Set objSubFolders = objFolder.SubFolders

' Adds the name of current folder with proper indentation
' intTreeLevel is used only for indenting folder name according to its pos ition
DisplayFolderTree = DisplayFolderTree & String ( intTreeLevel, vbTab) & "•"
DisplayFolderTree = DisplayFolderTree & vbTab & objFolder.Name & "<br>"

' Recursion takes place here
' If any "subfolders exist", the function is repeated for them too!
If objSubFolders.Count > 0 Then
For Each objTempFolder in objSubFolders
strArgument = strFolder & "\" & objTempFolder.Name
DisplayFolderTree = DisplayFolderTree & DisplayFolderTree ( strArgument, intTreeLevel+1 )
Next
End If

' When the control reaches HERE! This means your function has ended
' Now no more recursive calls to the function will take place

End Function
%>


Conclusion

The recursive functions solve the problem in a method much identical to their real-life solutions. In the example above, we simply display the name of current folder. Then we obtain a list of subfolders and repeat the function for each of them.

While using iterative functions we know about the number of iterations, ie. how many times a loop will be executed, in advance.

In the factorial problem, recursion is not necessary because we know that a loop will be executed until the value of the number becomes 1. But, in Folder Tree example, we never know how many subfolders may be present in a folder and how many iterations will be required. For this purpose, the most suitable solution is to call the function recursively to display information about a folder and its subfolders.

Source: http://www.15seconds.com/