【问题标题】:What's wrong with this ASP recursive function?这个 ASP 递归函数有什么问题?
【发布时间】:2008-11-18 19:46:56
【问题描述】:

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

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 的主帐户,该主帐户将拥有自己的子帐户。考虑到这一点,我是否以正确的方式进行此操作?

感谢您的快速回复。

【问题讨论】:

  • 您是否收到错误消息?
  • 功能卡住还是返回错误?你检查过你的数据是否有循环吗?

标签: asp-classic vbscript ado


【解决方案1】:

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

一种选择是为每个呼叫使用新连接。如果递归次数过多,您将很快失去连接。

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

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

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

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

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

【讨论】:

  • 谢谢乔尔,这很有帮助。稍后我将研究 CTE 以将此函数移动到 SQL 中。现在我已经做了一个快速的脏修复,并将孩子存储到一个数组中,然后用数组中的那些孩子再次调用该函数。感谢您的意见,这非常有帮助。
  • w3schools.com/ado/met_rs_getrows.asp。我在下面发布了这个,但我想让你看到它。
  • +1 用于参数化查询;它们不仅更安全,而且 SQL Server 可以优化查询并在查询字符串不变的情况下重复使用相同的优化。
【解决方案2】:

SQL 连接用完?

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

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

【讨论】:

  • 他不会每次都创建一个新的连接——他试图将同一个连接与多个记录集一起使用,而经典 asp 不支持 IIRC。但它在正确的轨道上。
  • 我认为 ADO 支持连接池,所以他并没有用完连接。这是我的来源15seconds.com/issue/970531.htm
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    它是如何断裂的?

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

          【解决方案7】:

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

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

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

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

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

          【讨论】:

            【解决方案8】:

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

            【讨论】:

              【解决方案9】:

              当我找到关于如何做到这一点的好教程时,我将尝试将查询移动到 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)
              

              【讨论】:

              • 请使用获取行。 w3schools.com/ado/met_rs_getrows.asp 它将整个记录集转储到一个数组中,您不必执行连接到数据库的循环
              • 另一个注释。确保您不会陷入无限循环。 IE。确保孩子不是其父母之一的父母。如果这种情况在您的业务逻辑中是可能的,那么请确保在您的代码中考虑到它(跟踪已处理的节点并且不要重新处理它们)。
              【解决方案10】:

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

              我使用客户端光标

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

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

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2011-04-27
                • 1970-01-01
                • 2012-11-26
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多