这个ASP递归函数有什么问题?
当我调用这个函数时,一切正常,只要我不试图递归调用函数。换句话说,如果我取消注释行:这个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.
我不知道是什么导致的问题。我明白这可能是由于几个因素。
- 未关闭连接并尝试重新打开相同的连接。
- 对数据库的许多并发连接。
数据库内容如下:
AcctID | ParentID
1 Null
2 1
3 1
4 2
5 2
6 3
7 4
的想法是,这样我可以有子帐户主账户,而这些子帐户都可以拥有自己的子帐户。最终会有另一个MasterID账户,其ParentID为Null,将拥有自己的孩子。考虑到这一点,我是否以正确的方式开展这项工作?
感谢您的快速回复。
谢谢大家,
比一般的错误其它:
Error Type: (0x80020009) Exception occurred.
我不知道是什么导致的问题。我明白这可能是由于几个因素。
- 未关闭连接并尝试重新打开相同的连接。
- 对数据库的许多并发连接。
数据库内容如下:
AcctID | ParentID
1 Null
2 1
3 1
4 2
5 2
6 3
7 4
的想法是,这样我可以有子帐户主账户,而这些子帐户都可以拥有自己的子帐户。最终会有另一个MasterID账户,其ParentID为Null,将拥有自己的孩子。考虑到这一点,我是否以正确的方式开展这项工作?
感谢您的快速回复。
看起来像失败了,因为您的连接仍然忙于服务前一次调用的RecordSet。
一种选择是为每个呼叫使用全新连接。如果你递归太多次,那么你很快就会失去联系。
另一种选择是将每个RecordSet的内容读入一个断开的集合:(Dictionary,Array等),以便您可以立即关闭连接。然后遍历断开连接的集合。
如果您使用SQL Server 2005或更高版本,则有更好的选择。您可以使用CTE(公用表表达式)编写递归sql查询。然后,您可以将所有内容移动到数据库,并且只需执行一个查询。
其他注意事项:
ID字段通常是int
s,所以您不应将它们包含在sql字符串中的字符中。
最后,这段代码可能没问题,因为我怀疑用户是否被允许直接输入一个id号。但是,所使用的动态sql技术非常危险,通常应该避免。使用查询参数来防止sql注入。
我并不太担心不使用intLevel
做任何事情。看看代码,这显然是一个早期版本,intLevel稍后可以用来确定类似缩进或样式化元素时使用的类名称。
尝试使用函数定义中的DIM声明,宣布该变量为本地:
Function GetChilds(ParentID, intLevel)
Dim rsData, sSQL
Set ...
编辑:好吧,我尽量做到更加明确。
我的理解是,由于rsData没有被DIM声明,所以它不是局部变量,而是全局变量。因此,如果循环访问WHILE语句,则会到达最内层rsData记录集的.Eof。您从递归函数调用返回,并且下一步再次是失败的rsData.MoveNext。
如果rsData确实是本地的,请纠正我。
用完SQL连接?
你正在处理那么多的层(客户端的Response.Write,服务器的ASP和数据库),这并不奇怪有问题。
也许你可以发布一些关于错误的细节?
他不会每次都创建一个新的连接 - 他试图对多个记录集使用相同的连接,而传统的asp不支持IIRC。但它是在正确的轨道上。 – 2008-11-18 19:57:18
我认为ADO支持连接池,所以他没有用完连接。这是我的来源http://www.15seconds.com/issue/970531.htm – MrChrister 2008-11-18 20:07:33
它是如何突破的?
我的猜测是,经过一定数量的递归后,你可能会得到堆栈溢出(讽刺),因为你没有分配太多的RecordSet。
很难说,没有关于它如何破坏的更多描述,但是你没有使用intLevel来做任何事情。
在每次调用中,您都会打开一个到数据库的新连接,并且在打开一个新连接之前不要关闭它。
不是说这实际上是递归问题的解决方案,但是您可能更好地制定一个以分层格式返回所有信息的SQL语句,而不是对数据库进行递归调用。
想想看,虽然它可能是因为你有太多的并发数据库连接。你不断打开,但不会开始关闭,直到你退出你的递归循环。
如果你需要这样的递归,我会亲自把递归存储到存储过程中,并处理数据库端的处理,以避免打开多个连接。如果你使用mssql2005查看一些叫做Common Table Expressions(CTE)的东西,它们使递归变得容易。还有其他方法可以用其他RDBMS实现递归。
基于这些消耗,我会尝试将查询移动到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)
我有相同的情况下工作代码:现在作为一个快速和肮脏的修复,如下我已经改变了代码。
我使用的是客户方光标
...
rsData.CursorLocation = adUseClient
rsData.Open sSQL, conDB, adOpenKeyset, adLockOptimistic
rsData.ActiveConnectcion = Nothing
...
在其他答复中指出,这是不是很有效,我只在一个管理界面,代码很少被调用和速度不是关键使用它。
我不会在常规网页中使用这样的递归过程。 可以重写代码以从数据库获取一次调用中的所有数据,也可以调用一次并将其保存到本地数组,并将数组保存到应用程序变量中。
您是否收到错误消息? – 2008-11-18 19:48:28
函数卡住或返回错误?你有没有检查你的数据循环? – 2008-11-18 19:57:04