ネストされたセットのパンくずリストを見つける
-
03-07-2019 - |
質問
グループのリストを格納するためにネストされたセット(別名、修正された事前順序ツリートラバーサル)を使用しており、すべてのグループに対してブレッドクラム(テーブルではなく文字列)を生成する簡単な方法を見つけようとしていますすぐに。私のデータも隣接リストモデルを使用して保存されます(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
- ノードB
テーブルを返すユーザー定義関数が必要になります:
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グループでは<!> gt; 10秒かかります)。 groupidとuseridをパラメーターとして受け取り、nvarcharを返します。指定されたグループの親(左/右の値を取得するための1つのクエリ、親を検索するための別のクエリ)を見つけ、ユーザーがアクセスできるグループにリストを制限します(上記と同じWHERE句を使用して、さらに別のクエリ)そして、カーソルを使用して各グループを通過し、文字列に追加してから、最終的にその値を返します。
これを実行するためのメソッドが必要です(たとえば、<!> lt; = 1s)、すぐに実行されます。
これはSQL Server 2005にあります。
解決 4
私がやったことは、すべてのレベルで何度も何度もこのテーブルをそれ自体に単純に結び付ける大きな結合を作成することです。
最初にテーブル@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クエリから直接利用できるようにする必要がある場合があります。
これを答えとして受け入れています。それは私がやっていることであり、他の人にとってはうまくいくかもしれないからです。 p>
他のヒント
ここで<!> quot; breadcrumb <!> quot;を取得するために機能した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
キャス
わかりました。これは、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
可能な場合は、次のようなパス(または系統と呼ばれると聞いたと思います)フィールドを使用します。
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以降のみを取得するには(psuedocode):
path = SELECT Path FROM Nodes WHERE ID = 3
SELECT * FROM Nodes WHERE Path LIKE = path + '%'
Kathyのステートメントを変更して、すべての要素のブレッドクラムを取得しました
SELECT
GROUP_CONCAT(
ancestor.name
ORDER BY ancestor.lft ASC
SEPARATOR ' > '
),
child.*
FROM `categories` child
JOIN `categories` ancestor
ON child.lft >= ancestor.lft
AND child.lft <= ancestor.rgt
GROUP BY child.lft
ORDER BY child.lft
自由にWHERE条件を追加できます。例:
WHERE ancestor.lft BETWEEN 6 AND 11
SQLサーバー固有のコードはありませんが、単に探していますか:
SELECT * FROMテーブルWHERE <!> lt; (currentid.left)AND右<!> gt; (currentid.right)