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

アイデアは、私が子アカウントを持つマスター アカウントを持つことができ、それらの子アカウントが独自の子アカウントを持つことができるようにすることです。最終的に、ParentID が Null で、独自の子を持つ別のマスター アカウントが存在します。それを念頭に置いて、私はこれを正しい方法で行っていますか?

迅速な対応に感謝します。


みんな、ありがとう、

通常のエラー以外:

エラーの種類: (0x80020009) 例外が発生しました。

何が問題を引き起こしているのかわかりませんでした。おそらくいくつかの要因によるものだと理解しています。

  1. 接続を閉じずに、同じ接続を再度開こうとしています。
  2. データベースへの多数の同時接続。

データベースの内容は次のとおりです。

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

アイデアは、私が子アカウントを持つマスター アカウントを持つことができ、それらの子アカウントが独自の子アカウントを持つことができるようにすることです。最終的に、ParentID が Null で、独自の子を持つ別のマスター アカウントが存在します。それを念頭に置いて、私はこれを正しい方法で行っていますか?

迅速な対応に感謝します。

4

10 に答える 10

2

接続が前回の呼び出しからの RecordSet を提供するためにまだビジーであるため、失敗したように見えます。

1 つのオプションは、通話ごとに新しい接続を使用することです。再帰を何度も繰り返すと、すぐに接続が不足する危険性があります。

もう 1 つのオプションは、各 RecordSet の内容を切断されたコレクション (Dictionary、Array など) に読み込むことで、すぐに接続を閉じることができます。次に、切断されたコレクションを反復処理します。

SQL Server 2005 以降を使用している場合は、さらに優れたオプションがあります。CTE (共通テーブル式) を使用して、再帰的な SQL クエリを作成できます。その後、すべてをデータベースに移動でき、1 つのクエリを実行するだけで済みます。

その他の注意事項:
ID フィールドは通常ints であるため、SQL 文字列内で ' 文字で囲むべきではありません。

最後に、ユーザーが ID 番号を直接入力できるとは思えないため、このコードはおそらく問題ありません。ただし、使用される動的 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

このような再帰が必要な場合は、複数の接続が開かないように、個人的に再帰をストアドプロシージャに入れ、データベース側でその処理を処理します。mssql2005を使用している場合は、Common Table Expressions(CTE)と呼ばれるものを調べれば、再帰が簡単になります。他のRDBMSで再帰を実装する方法は他にもあります。

于 2008-11-18T20:35:54.243 に答える
0

同じシナリオの作業コードがあります。

クライアント側のカーソルを使用します

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

他の回答で指摘されているように、これはあまり効率的ではありません。コードが頻繁に呼び出されず、速度がそれほど重要ではない管理インターフェイスでのみ使用します。

通常の Web ページでは、このような再帰的なプロセスは使用しません。データベースから 1 回の呼び出しですべてのデータを取得するようにコードを修正するか、呼び出しを 1 回行ってローカル配列に保存し、その配列をアプリケーション変数に保存します。

于 2013-02-27T17:04:13.200 に答える
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

提案に基づいて、その方法に関する優れたチュートリアルが見つかったら、クエリを 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 に答える