我正在使用闭包表来跟踪用户文件权限,并且在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)
可以使用非标准SQL来完成,但这取决于您的数据库供应商。例如,您可能希望使用Oracle中的CONNECT BY
子句检查分层查询。 MySQL可能有类似的东西。但是,出于三个原因,我建议反对这样的解决方案:
相反,我会建议我在几个大中型项目中使用以下方法:
对于r
字段,使用三态布尔值(TRUE
,FALSE
和NULL
),其中NULL
代表“继承” ”。
为每个文件添加一个新字段effective_r
(可能为每个文件夹添加)。这将包含应用所有继承规则的结果,并且只能是TRUE
或FALSE
。当然,您必须在层次结构的每次更改时计算此字段,但这更快,因为更新不经常发生,当它们发生时,它们只影响层次结构的一部分。
定义自上而下的传播规则。在这种情况下很容易:
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
对于用户权限,规则可能更加复杂和复杂。
答案 1 :(得分: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)
答案 2 :(得分: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