4

グループのリストを保存するために、ネストされたセット (別名、変更されたプレオーダー ツリー トラバーサル) を使用しています。すべてのグループのブレッドクラムを (テーブルではなく文字列として) 一度に生成する簡単な方法を見つけようとしています。私のデータも、隣接リスト モデルを使用して保存されます (2 つの同期を維持するためのトリガーがあります)。

たとえば、次のようになります。

ID   Name    ParentId  Left   Right
0    Node A  0         1      12
1    Node B  0         2      5
2    Node C  1         3      4
3    Node D  0         6      11
4    Node E  3         7      8
5    Node F  4         9      9

これはツリーを表します:

  • ノード A
    • ノード B
      • ノード C
    • ノード D
      • ノード E
      • ノード F

テーブルを返すユーザー定義関数を使用できるようにしたいと思います。

ID  Breadcrumb
0   Node A
1   Node A > Node B
2   Node A > Node B > Node C
3   Node A > Node D
4   Node A > Node D > Node E
5   Node A > Node D > Node F

これをもう少し複雑にするために (質問の範囲外ですが)、尊重する必要があるユーザー制限もあります。たとえば、id=3 にしかアクセスできない場合、クエリを実行すると次のようになります。

ID  Breadcrumb
3   Node D
4   Node D > Node E
5   Node D > Node F

ユーザーIDをパラメーターとして受け取り、有効なすべてのグループのIDを含むテーブルを返すユーザー定義関数があります。クエリのどこかに限り

WHERE group.id IN (SELECT id FROM dbo.getUserGroups(@userid))

それが動作します。


これを実行できる既存のスカラー関数がありますが、妥当な数のグループでは機能しません (2000 グループで 10 秒以上かかります)。グループ ID とユーザー ID をパラメーターとして取り、nvarchar を返します。指定されたグループの親を検索し (左/右の値を取得するための 1 つのクエリ、親を検索するための別のクエリ)、ユーザーがアクセスできるグループにリストを制限します (上記と同じ WHERE 句を使用するため、さらに別のクエリを使用します)。次に、カーソルを使用して各グループを調べ、それを文字列に追加してから、最終的にその値を返します。

これを行うには、その場ですばやく (たとえば、<= 1 秒) 実行されるメソッドが必要です。

これは SQL Server 2005 上にあります。

4

6 に答える 6

5

これは、ツリー内の任意のポイントから「ブレッドクラム」パスを取得するために機能した SQL です。それが役に立てば幸い。

SELECT ancestor.id, ancestor.title, ancestor.alias 
FROM `categories` child, `categories` ancestor 
WHERE child.lft >= ancestor.lft AND child.lft <= ancestor.rgt 
AND child.id = MY_CURRENT_ID 
ORDER BY ancestor.lft

キャス

于 2011-08-04T16:13:29.367 に答える
3

Ok。これは、SQL Server 2005 ではなく、MySQL 用です。サブクエリで GROUP_CONCAT を使用します。

これにより、ブレッドクラム全体が単一の列として返されます。

SELECT 
 (SELECT GROUP_CONCAT(parent.name SEPARATOR ' > ')
 FROM category parent
 WHERE node.Left >= parent.Left
 AND node.Right <= parent.Right
 ORDER BY Left
 ) as breadcrumb
FROM category node
ORDER BY Left
于 2014-03-04T03:55:53.353 に答える
2

可能であれば、次のようなパス(または系統と呼ばれると聞いたことがあります)フィールドを使用します。

ID   Name    ParentId  Left   Right   Path
0    Node A  0         1      12      0,
1    Node B  0         2      5       0,1,
2    Node C  1         3      4       0,1,2,
3    Node D  0         6      11      0,3,
4    Node E  3         7      8       0,3,4,
5    Node F  4         9      9       0,3,4,

ノードD以降を取得するには(擬似コード):

path = SELECT Path FROM Nodes WHERE ID = 3
SELECT * FROM Nodes WHERE Path LIKE = path + '%'
于 2009-04-30T22:27:52.033 に答える
1

私が最終的にやったのは、このテーブルをそれ自体に単純に結び付ける大きな結合を、すべてのレベルで何度も何度も作成することです。

まず、テーブル @topLevelGroups に第 1 レベルのグループのみを入力し (ルートが 1 つしかない場合は、この手順を省略できます)、次に @userGroups にユーザーが表示できるグループを入力します。

SELECT groupid,
   (level1 
    + CASE WHEN level2 IS NOT NULL THEN ' > ' + level2 ELSE '' END
    + CASE WHEN level3 IS NOT NULL THEN ' > ' + level3 ELSE '' END
   )as [breadcrumb]
FROM (
  SELECT g3.*
    ,g1.name as level1
    ,g2.name as level2
    ,g3.name as level3
  FROM @topLevelGroups g1
  INNER JOIN @userGroups g2 ON g2.parentid = g1.groupid and g2.groupid <> g1.groupid
  INNER JOIN @userGroups g3 ON g3.parentid = g2.groupid 

  UNION

  SELECT g2.*
    ,g1.name as level1
    ,g2.name as level2
    ,NULL as level3
  FROM @topLevelGroups g1 
  INNER JOIN @userGroups g2 ON g2.parentid = g1.groupid and g2.groupid <> g1.groupid

  UNION

  SELECT g1.*
    ,g1.name as level1
    ,NULL as level2
    ,NULL as level3 
  FROM @topLevelGroups g1

) a
ORDER BY [breadcrumb]

これはかなり大きなハックであり、明らかに特定のレベル数に制限されています (私のアプリでは、選択できる合理的な制限があります)。サポートされるレベルが増えるほど、結合数が指数関数的に増加するという問題があります。したがって、はるかに遅くなります。

コードで実行する方が確実に簡単ですが、私にとっては常にオプションであるとは限りません.SQLクエリから直接利用できるようにする必要がある場合があります.


これは私がやったことであり、他の人にとってはうまくいくかもしれないので、これを答えとして受け入れています。

于 2009-08-14T20:49:14.200 に答える
0

SQL Server固有のコードはありませんが、単に探しているのは:

SELECT * FROM table WHERE left <(currentid.left)AND right>(currentid.right)

于 2009-04-30T22:28:54.653 に答える