2

質問:

ユーザー ファイルのアクセス許可を追跡するためにクロージャ テーブルを使用していJOINますread。結合されたすべての行が読み取り可能な場合にのみ、行を選択したい。を使用UNIONしても、明らかにこれは達成されません。

詳細:

フォルダ テーブル:

CREATE TABLE IF NOT EXISTS folders
(
   id                    INT NOT NULL AUTO_INCREMENT,
   path                  VARCHAR(500) NOT NULL,
   r                     BOOL NOT NULL DEFAULT FALSE,
   PRIMARY KEY ( id )
)engine=innodb;

ファイル テーブル:

CREATE TABLE IF NOT EXISTS files
(
   id                    INT NOT NULL AUTO_INCREMENT,
   parent_folder_id      INT NOT NULL ,
   path                  VARCHAR(500) NOT NULL,
   r                     BOOL NOT NULL DEFAULT FALSE,
   FOREIGN KEY ( parent_folder_id ) REFERENCES folders ( id ),
   PRIMARY KEY ( id )
)engine=innodb;

フォルダ - 親フォルダ クロージャ テーブル:

CREATE TABLE IF NOT EXISTS parent_folders
(
   id                INT NOT NULL AUTO_INCREMENT,
   folder_id         INT NOT NULL,
   parent_folder_id  INT NOT NULL,
   FOREIGN KEY ( folder_id ) REFERENCES folders ( id ),
   FOREIGN KEY ( parent_folder_id ) REFERENCES folders ( id ),
   PRIMARY KEY ( id )
)engine=innodb;

ここで、すべての読み取り可能なファイルを取得したい場合 (ユーザーを完全に省略したことは今のところ無視します)、次のように開始します。

SELECT 
    F.id, F.path, F.r, P.parent_folder_id, D.path, D.r
FROM
    files AS F 
    LEFT JOIN parent_folders AS P 
        ON F.parent_folder_id = P.folder_id 
    LEFT JOIN folders AS D  
        ON P.parent_folder_id = D.id;

これにより、すべてのファイル ID、パス、および読み取りアクセス許可のテーブルが表示され、その親フォルダーのそれぞれからアクセスできるようになります。

id   path                  r     id  path          r
......
0   /home/joe/foo/bar.txt  True  1   /home/joe/foo True
1   /home/joe/foo/bar.txt  True  2   /home/joe     True
1   /home/joe/foo/bar.txt  True  3   /home         True
1   /home/joe/foo/bar.txt  True  4   /             True
2   /home/jim/foo/bar.txt  True  5   /home/jim/foo True
2   /home/jim/foo/bar.txt  True  6   /home/jim     False
2   /home/jim/foo/bar.txt  True  7   /home         True
2   /home/jim/foo/bar.txt  True  8   /             True
....

この場合、それにつながるすべての親フォルダーが読み取り可能であるため、そうしたいと思いますが、その親フォルダーの 1 つが読み取り可能でないため、SELECT /home/joe/foo/bar.txtそうしたくありません。SELECT /home/jim/foo/bar.txt

編集:別の方法として、質問を次のように言い換えることもできANDます。

4

3 に答える 3

1

これは非標準 SQL で実行できますが、これはデータベース ベンダーによって異なります。たとえばCONNECT BY、Oracle の句を使用して階層クエリをチェックしたい場合があります。MySQL にも似たようなものがあるかもしれません。ただし、次の 3 つの理由から、このようなソリューションには反対することをお勧めします。

  1. ベンダー ロックイン。
  2. これらのクエリがどれほど効率的であるか、またはそれらを最適化する方法は明らかではありません。
  3. このようなルールがさらに必要な場合 (たとえば、継承可能なユーザー権限など) は、複雑さが急速に増します。

代わりに、いくつかの中規模から大規模のプロジェクトで使用した次のアプローチをお勧めします。

  1. フィールドには、「継承」を表すr3 状態のブール値 ( TRUEFALSEおよびNULL) を使用します。NULL

  2. effective_r各ファイル (および場合によっては各フォルダー) に新しいフィールドを追加します。これには、すべての継承ルールを適用した結果が含まれ、TRUEまたはのみになりますFALSE。もちろん、階層が変更されるたびにこのフィールドを計算する必要がありますが、更新はそれほど頻繁ではなく、更新が発生したときに階層の一部のみに影響するため、この方が高速です。

  3. トップダウンの伝播ルールを定義します。この場合、それは簡単です:

    parent effective_r       child r        child effective_r
    ---------------------    ------------   ---------------------
    <ROOT>                   NULL           TRUE
    <ROOT>                   TRUE           TRUE
    <ROOT>                   FALSE          FALSE
    TRUE                     NULL           TRUE
    FALSE                    NULL           FALSE
    TRUE|FALSE               TRUE           TRUE
    TRUE|FALSE               FALSE          FALSE
    

    ユーザー権限のルールは、はるかに複雑で洗練されたものになる可能性があります。

于 2012-05-30T06:19:36.763 に答える
0

WHERE 句を追加する

WHERE D.r = TRUE

編集

必要に応じて改善を行います

SELECT F.id, F.path, F.r, P.parent_folder_id, D.path, D.r
FROM files AS F
LEFT JOIN parent_folders AS P ON F.parent_folder_id = P.folder_id
LEFT JOIN folders AS D ON P.parent_folder_id = D.id
WHERE F.path NOT IN
    (SELECT A.path
     FROM files AS A
     LEFT JOIN parent_folders AS B ON A.parent_folder_id = B.folder_id
     LEFT JOIN folders AS C ON B.parent_folder_id = C.id
     WHERE C.r = FALSE)
于 2012-05-30T05:50:35.357 に答える
0
select a.path
from (
    SELECT 
        F.path, D.r
    FROM
        files AS F 
        LEFT JOIN parent_folders AS P 
            ON F.parent_folder_id = P.folder_id 
        LEFT JOIN folders AS D  
            ON P.parent_folder_id = D.id
) a
left join (
    SELECT 
        F.path, D.r
    FROM
        files AS F 
        LEFT JOIN parent_folders AS P 
            ON F.parent_folder_id = P.folder_id 
        LEFT JOIN folders AS D  
            ON P.parent_folder_id = D.id
) b on a.path = b.path and b.r = false
where b.r is null
group by path
于 2012-05-30T12:29:48.257 に答える