2

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

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. 到数据库的许多并发连接。

数据库内容如下:

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

我的想法是,我可以拥有一个带有子帐户的主帐户,而这些子帐户可以拥有自己的子帐户。最终将有另一个父 ID 为 Null 的主帐户,该主帐户将拥有自己的子帐户。考虑到这一点,我是否以正确的方式进行?

感谢您的快速回复。


谢谢大家,

除了通常的错误:

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

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

  1. 不关闭连接并尝试重新打开相同的连接。
  2. 到数据库的许多并发连接。

数据库内容如下:

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

我的想法是,我可以拥有一个带有子帐户的主帐户,而这些子帐户可以拥有自己的子帐户。最终将有另一个父 ID 为 Null 的主帐户,该主帐户将拥有自己的子帐户。考虑到这一点,我是否以正确的方式进行?

感谢您的快速回复。

4

10 回答 10

2

看起来它失败了,因为您的连接仍在忙于为上一次调用的 RecordSet 提供服务。

一种选择是为每个呼叫使用新连接。存在的危险是,如果您递归太多次,您将很快失去连接。

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

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

其他一些注意事项:
ID 字段通常int是 ​​s,因此您不应将它们包含在 ' sql 字符串中的字符中。

最后,这段代码可能没问题,因为我怀疑是否允许用户直接输入身份证号码。但是,使用的动态 sql 技术非常危险,一般应避免使用。改为使用查询参数来防止 sql 注入。

我不太担心不使用intLevel任何东西。查看代码,这显然是一个早期版本,并且 intLevel 可以在以后用于确定诸如缩进或样式元素时使用的类名之类的东西。

于 2008-11-18T19:52:50.537 回答
1

SQL 连接用完了?

您在那里处理了很多层(用于客户端的 Response.Write、用于服务器的 ASP 和数据库),因此出现问题也就不足为奇了。

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

于 2008-11-18T19:51:20.353 回答
1

如果没有更多关于它如何破坏的描述,很难说,但你没有使用 intLevel 做任何事情。

于 2008-11-18T19:52:29.133 回答
0

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

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

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

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

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

于 2008-11-18T19:51:05.377 回答
0

怎么破?

我的猜测是,经过一定次数的递归后,您可能会遇到堆栈溢出(讽刺),因为您没有分配太多的 RecordSet。

于 2008-11-18T19:51:50.663 回答
0

在每次调用中,您都会打开与数据库的新连接,并且在打开新连接之前不要关闭它。

于 2008-11-18T19:54:04.840 回答
0

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

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

于 2008-11-18T19:55:05.853 回答
0

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

于 2008-11-18T20:35:54.243 回答
0

基于这些建议,当我找到有关如何执行此操作的好教程时,我将尝试将查询移动到 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)
于 2008-11-18T21:04:23.000 回答
0

我有相同场景的工作代码。

我使用客户端光标

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

正如在其他回复中指出的那样,这不是很有效,我仅在管理界面中使用它,在该界面中不经常调用代码并且速度并不那么关键。

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

于 2013-02-27T17:04:13.200 回答