2008-11-18 75 views
2

当我调用这个函数时,一切正常,只要我不试图递归调用函数。换句话说,如果我取消注释行:这个ASP递归函数有什么问题?

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 

的想法是,这样我可以有子帐户主账户,而这些子帐户都可以拥有自己的子帐户。最终会有另一个MasterID账户,其ParentID为Null,将拥有自己的孩子。考虑到这一点,我是否以正确的方式开展这项工作?

感谢您的快速回复。


谢谢大家,

比一般的错误其它:

Error Type: (0x80020009) Exception occurred.

我不知道是什么导致的问题。我明白这可能是由于几个因素。

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

数据库内容如下:

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

的想法是,这样我可以有子帐户主账户,而这些子帐户都可以拥有自己的子帐户。最终会有另一个MasterID账户,其ParentID为Null,将拥有自己的孩子。考虑到这一点,我是否以正确的方式开展这项工作?

感谢您的快速回复。

+0

您是否收到错误消息? – 2008-11-18 19:48:28

+0

函数卡住或返回错误?你有没有检查你的数据循环? – 2008-11-18 19:57:04

回答

2

看起来像失败了,因为您的连接仍然忙于服务前一次调用的RecordSet。

一种选择是为每个呼叫使用全新连接。如果你递归太多次,那么你很快就会失去联系。

另一种选择是将每个RecordSet的内容读入一个断开的集合:(Dictionary,Array等),以便您可以立即关闭连接。然后遍历断开连接的集合。

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

其他注意事项:
ID字段通常是int s,所以您不应将它们包含在sql字符串中的字符中。

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

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

0

尝试使用函数定义中的DIM声明,宣布该变量为本地:

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

编辑:好吧,我尽量做到更加明确。

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

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

1

用完SQL连接?

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

也许你可以发布一些关于错误的细节?

+0

他不会每次都创建一个新的连接 - 他试图对多个记录集使用相同的连接,而传统的asp不支持IIRC。但它是在正确的轨道上。 – 2008-11-18 19:57:18

+0

我认为ADO支持连接池,所以他没有用完连接。这是我的来源http://www.15seconds.com/issue/970531.htm – MrChrister 2008-11-18 20:07:33

0

它是如何突破的?

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

1

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

0

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

0

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

想想看,虽然它可能是因为你有太多的并发数据库连接。你不断打开,但不会开始关闭,直到你退出你的递归循环。

0

如果你需要这样的递归,我会亲自把递归存储到存储过程中,并处理数据库端的处理,以避免打开多个连接。如果你使用mssql2005查看一些叫做Common Table Expressions(CTE)的东西,它们使递归变得容易。还有其他方法可以用其他RDBMS实现递归。

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) 
0

我有相同的情况下工作代码:现在作为一个快速和肮脏的修复,如下我已经改变了代码。

我使用的是客户方光标

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

在其他答复中指出,这是不是很有效,我只在一个管理界面,代码很少被调用和速度不是关键使用它。

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