这个ASP递归函数有什么问题?

时间:2008-11-18 19:46:56

标签: asp-classic vbscript ado

当我调用此函数时,一切正常,只要我不尝试再次递归调用该函数。换句话说,如果我取消注释该行:

GetChilds rsData("AcctID"), intLevel + 1 

然后功能中断。

<%
    Function GetChilds(ParentID, intLevel)
        Set rsData= Server.CreateObject("ADODB.Recordset")
        sSQL = "SELECT AcctID, ParentID FROM Accounts WHERE ParentID='" & ParentID &"'"
        rsData.Open sSQL, conDB, adOpenKeyset, adLockOptimistic
        If IsRSEmpty(rsData) Then
            Response.Write("Empty")
        Else
            Do Until rsData.EOF
                Response.Write rsData("AcctID") & "<br />"
                'GetChilds rsData("AcctID"), intLevel + 1 
                rsData.MoveNext
            Loop
        End If
        rsData.close: set rsData = nothing
    End Function

    Call GetChilds(1,0)
%>

*在反馈后编辑

谢谢大家,

除了通常的错误:

Error Type: (0x80020009) Exception occurred.

我不确定是什么导致了这些问题。我知道这可能是由于几个因素造成的。

  1. 未关闭连接并尝试重新打开相同的连接。
  2. 与数据库的多个并发连接。
  3. 数据库内容如下:

    AcctID | ParentID
    1        Null
    2        1
    3        1
    4        2
    5        2
    6        3
    7        4
    

    我的想法是,我可以拥有子帐户的主帐户,这些子帐户可以拥有自己的子帐户。最终会有一个父母ID为Null的另一个主帐户,它将拥有自己的子女。考虑到这一点,我是否正确地采用了这种方式?

    感谢您的快速回复。


    谢谢大家,

    除了通常的错误:

      

    错误类型:(0x80020009)异常   发生。

    我不确定是什么导致了这些问题。我知道这可能是由于几个因素造成的。

    1. 未关闭连接并尝试重新打开相同的连接。
    2. 与数据库的多个并发连接。
    3. 数据库内容如下:

      AcctID | ParentID
      1        Null
      2        1
      3        1
      4        2
      5        2
      6        3
      7        4
      

      我的想法是,我可以拥有子帐户的主帐户,这些子帐户可以拥有自己的子帐户。最终会有一个父母ID为Null的另一个主帐户,它将拥有自己的子女。考虑到这一点,我是否正确地采用了这种方式?

      感谢您的快速回复。

10 个答案:

答案 0 :(得分:2)

看起来它失败了,因为你的连接仍在忙于从前一次调用中提供RecordSet。

一种选择是为每个呼叫使用新连接。存在的危险是,如果你进行多次递归,你很快就会耗尽连接。

另一种选择是将每个RecordSet的内容读入断开的集合:(字典,数组等),以便您可以立即关闭连接。然后迭代断开连接的集合。

如果您使用的是SQL Server 2005或更高版本,则可以选择更好的选项。您可以使用CTE(公用表表达式)来编写递归SQL查询。然后,您可以将所有内容移动到数据库,只需执行一个查询。

其他一些说明:
ID字段通常为int s,因此您不应将它们包含在sql字符串中的'字符中。

最后,这段代码可能还可以,因为我怀疑用户是否可以直接输入id号。但是,使用的动态sql技术非常危险,通常应该避免使用。请改用查询参数以防止sql注入。

我并不担心不使用intLevel来做任何事情。查看代码这显然是一个早期版本,并且以后可以使用intLevel来确定缩进或在为元素设置样式时使用的类名。

答案 1 :(得分:1)

用尽SQL连接?

你正在处理那么多层(客户端的Response.Write,服务器的ASP和数据库),有问题就不足为奇了。

也许您可以发布一些有关错误的详细信息?

答案 2 :(得分:1)

很难说没有更多描述它如何中断,但你没有使用intLevel来做任何事情。

答案 3 :(得分:0)

尝试使用函数定义中的DIM语句将变量声明为local:

Function GetChilds(ParentID, intLevel)
Dim rsData, sSQL
Set ...

编辑:好的,我试着更明确。

我的理解是,由于DIM未声明rsData,因此它不是局部变量,而是全局变量。因此,如果循环遍历WHILE语句,则会到达最内层rsData记录集的.Eof。您从递归函数调用返回,下一步再次是rsData.MoveNext,它失败。

如果rsData确实是本地的,请纠正我。

答案 4 :(得分:0)

它是如何打破的?

我的猜测是,经过一定数量的递归后,你可能会得到Stack Overflow(具有讽刺意味),因为你没有分配过多的RecordSet。

答案 5 :(得分:0)

在每次通话中,您都会打开一个与数据库的新连接,并且在打开新数据库之前不要将其关闭。

答案 6 :(得分:0)

并不是说这实际上是递归问题的解决方案,但是你可能更好地编制一个以分层格式返回所有信息的SQL语句,而不是对数据库进行递归调用。

考虑一下,可能是因为你有太多的并发数据库连接。你不断打开,但在你退出递归循环之前不会开始关闭。

答案 7 :(得分:0)

如果您需要这样的递归,我会亲自将递归放入存储过程并在数据库端处理该处理,以避免打开多个连接。如果您正在使用mssql2005查看称为公用表表达式(CTE)的东西,它们会使递归变得容易。还有其他方法可以实现与其他RDBMS的递归。

答案 8 :(得分:0)

基于sugestions,当我找到一个关于如何做到这一点的好教程时,我将尝试将查询移动到CTE(公用表表达式)中。就目前而言,作为快速而肮脏的修复,我更改了代码如下:

Function GetChilds(ParentID, intLevel)
        'Open my Database Connection and Query the current Parent ID
        Set rsData= Server.CreateObject("ADODB.Recordset")
        sSQL = "SELECT AcctID, ParentID FROM Accounts WHERE ParentID='" & ParentID &"'"
        rsData.Open sSQL, conDB, adOpenKeyset, adLockOptimistic
        'If the Record Set is not empty continue
        If Not IsRSEmpty(rsData) Then
            Dim myAccts()
            ReDim myAccts(rsData.RecordCount)
            Dim i
            i = 0
            Do Until rsData.EOF
                Response.Write "Account ID: " & rsData("AcctID") & " ParentID: " & rsData("ParentID") & "<br />"
                'Add the Childs of the current Parent ID to an array.
                myAccts(i) = rsData("AcctID")
                i = i + 1
                rsData.MoveNext
            Loop
            'Close the SQL connection and get it ready for reopen. (I know not the best way but hey I am just learning this stuff)
            rsData.close: set rsData = nothing
            'For each Child found in the previous query, now lets get their childs.
            For i = 0 To UBound(myAccts)
                Call GetChilds(myAccts(i), intLevel + 1)
            Next
        End If
    End Function

    Call GetChilds(1,0)

答案 9 :(得分:0)

我有相同方案的工作代码。

我使用了一个客户端游标

...
rsData.CursorLocation = adUseClient
rsData.Open sSQL, conDB, adOpenKeyset, adLockOptimistic
rsData.ActiveConnectcion = Nothing
...

正如其他回复中所指出的那样,这不是很有效,我只在管理界面中使用它,在这种情况下,代码很少被调用,而速度并不那么重要。

我不会在常规网页中使用这样的递归过程。 重做代码以从数据库中获取一次调用中的所有数据,或者进行一次调用并将其保存到本地数组并将数组保存在应用程序变量中。