2012-05-30 31 views
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 
.... 

在這種情況下訪問,我想SELECT/home/joe/foo/bar.txt因爲領導下到它的每一個父文件夾是可讀的,但我不希望至 SELECT/home/jim/foo/bar.txt,因爲其父文件夾之一不可讀。

編輯:或者,我可以重新修改這樣的問題:「我可以AND跨多行的一列的值嗎?」

+0

+1對於很好的格式。 – Sarfraz

+1

@Sarfraz我發現如果我花了額外的15分鐘格式,我的問題得到500%的更多關注。 – puk

回答

1

它可與非標準的SQL進行改進,但是這取決於您的數據庫供應商。例如,您可能希望使用Oracle中的CONNECT BY子句檢查分層查詢。 MySQL可能有類似的東西。不過,我建議不要這樣的解決方案有三個原因:

  1. 供應商鎖定。
  2. 目前還不清楚這些查詢的效率如何,或者如何優化它們。
  3. 如果您需要更多像這樣的規則(例如可繼承的用戶權限),複雜度會非常快地增加。

相反,我會建議如下的做法,我在一些媒體已經習慣了大型項目:

  1. 對於r現場使用三態布爾(TRUEFALSENULLNULL將代表「繼承」。

  2. 添加一個新的領域effective_r每個文件(也許每個文件夾)。這將包含應用所有繼承規則的結果,只能是TRUEFALSE。當然,每次更改層次結構時都必須計算此字段,但更新速度並不經常發生,當它們發生時,它們只會影響層次結構的一部分。

  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 
    

    對於用戶權限,規則可能會更加複雜和複雜得多。

+0

如果我問這樣的問題該怎麼辦:我可以通過多個行「AND」列嗎? – puk

+0

關於你的答案,我認爲你正在更新任何改變的所有文件權限。一個'chmod 0000/home/joe'命令儘管不太可能會停止整個系統。 – puk

+0

@puk,是的,你可以,但你必須訴諸供應商的具體擴展。出於我解釋的原因,你最好不要。 – linepogl

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) 
+0

其實我沒有嘗試,但我相信,在逐行基地,當我要的是隻適用'選擇F.id'如果所有的'D.r'列全部爲真。 – puk

+0

檢查編輯後的查詢 – Darshana

+0

這是做什麼的?不應該最後一個「TRUE」爲「FALSE」,那麼這可能會起作用。 – puk

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 
+0

這是有效的語法嗎?如果不存在「AS」聲明,那麼跟隨'a'和'b'字母的是什麼? – puk

+0

我沒有測試它,但[手冊](http://dev.mysql.com/doc/refman/5.5/en/join.html)說'AS'關鍵字是可選的。 –

+0

我現在看到。好的,我會測試它 – puk