1

バックグラウンド

グループテーブル、ユーザーテーブル、およびgroup_membersテーブルがあります。

groups { group_ varchar(50) not null, etc... }
users  { user_id varchar(50) not null, etc... }
group_members { group_ varchar(50 not null, member varchar(50) not null }

私の要件では、グループは他のグループをメンバーとして持つことができると述べています。ユーザーは、メンバーであるすべてのグループのメンバーと見なされる必要があります。
たとえば、これらのテーブルの次のデータについて考えてみましょう。

group_members              | groups         | users    |
========================== | ============== | ======== |
group_          member     | group_         | user_id  |
-------------------------- | -------------- | -------- |
'SYSTEM_ADMIN'  'OE_ADMIN' | 'SYSTEM_ADMIN' | 'USER    |
'SYSTEM_ADMIN'  'AR_ADMIN' | 'OE_ADMIN'     |          |
'SYSTEM_ADMIN'  'USER'     | 'AR_ADMIN'     |          |

「ユーザー」はどのグループのメンバーですか? する必要があります

member
==============
'SYSTEM_ADMIN'
'OE_ADMIN'
'AR_ADMIN'

質問

次のクエリを作成し、必要な結果を提供しましたが、少し複雑に見えます。

WITH GM
AS (
    SELECT GROUP_, MEMBER FROM group_members 
       WHERE member IN (SELECT group_ FROM groups)
)
SELECT group_ FROM group_members WHERE member = 'USER'
UNION
SELECT MEMBER AS GROUP_ FROM GM 
   WHERE group_ in (SELECT group_ FROM group_members WHERE member = 'USER')

このクエリをよりシンプルにする方法、または煩雑さを軽減する方法について何か提案はありますか?

4

2 に答える 2

0

あなたの再帰CTEは問題ないようです。

それはどのように機能しますか?

もっと簡単にする重要な方法はないと思います - 再帰的な CTE はいつもちょっと面倒に見えます。

WHERE IN は JOIN と同等ですが、読みやすいとは思えず、実行計画もかなり同等になるはずです。

ルート行は、再帰的な行の前に CTE として表すことができますが、読みやすさはあまり節約されません。

WITH root AS ()
     ,CTE AS ()
SELECT FROM ROOT 
    UNION
SELECT FROM CTE

いずれにせよ、これをビューまたはインライン テーブル値関数 (パラメーター付き) に配置して、システムの他の場所から簡単に使用できるようにすることで、あまり見る必要がなくなります。

私が階層のために行ったことは、より高性能なフラット化/非正規化バージョンを構築し、それをトリガーまたはタイマーで更新し、それを読み取り専用のパフォーマンス エンハンサーとして使用することです。たとえば、階層内で質問にタグを付けることができるテクニカル サポート システムでは、子が選択されたときにすべての親 (printers->hp->laserjet) を入力したため、問題を簡単に照会できました。 Windows の任意のバージョンでの hp プリンター、または Windows XP でのレーザージェットの問題。

于 2012-06-15T21:02:36.687 に答える
0

@Cade Rouxの回答に基づいて、さらに熟考すると、次のストアドプロシージャに導かれました

ALTER PROCEDURE GetUserGroups
@user_id varchar(50)
AS
BEGIN
SET NOCOUNT ON;

SELECT group_ FROM group_members WHERE member = @user_id
UNION
SELECT MEMBER AS GROUP_ FROM group_members
WHERE group_ IN (SELECT group_ FROM group_members WHERE member = @user_id) 
      AND member != @user_id
END

これにより、元のクエリが簡素化され、@user_id がメンバーであるグループ名のリストと、最上位グループのメンバーであるグループの名前を取得するという私の要件が適切に満たされます。

このコードは、グループ内のグループを 1 つのレベルにのみ許可します。

于 2012-06-18T16:45:13.410 に答える