I came accross a problem creating an (MY)sql statement for a changed feature in my application. It is about implementing a permission model.
Right now the tables look like this
So basically photos belong to albums and permissions are album-based. An sql statement for getting all photos user stefan has access to would look like this:
SELECT photo.* FROM photo
JOIN album2photo ON album2photo.photo=photo.id
JOIN album2permission ON album2permission.album=album2photo.album
WHERE album2permission.who IN ('stefan')
Now I want to achieve an additional kind of restriction. I want to block users from seeing specific photos in an album. For doing so I thought about creating another table
How it works now is that a user has access to all photos of an album except those for which there are specific photo permissions and he does not belong to those users permitted for the photo. Let me give you an example:
There is an album (1) with photos (10, 11, 12), the permissions look like this:
So stefan and katrin should be able to see the following photos:
Both are allowed to see the photos of album 1 because for both there is an entry in album2permission, but stefan is blocked from seeing photo 11 because for that photo there are specific permissions and stefan does not belong to the list of users permitted for that photo.
My problem now is the corresponding sql statement, especially the condition "there are no restrictions for the photo OR person does belong to permitted users". Something that would work obviously is:
SELECT photo.* FROM photo
JOIN album2photo ON album2photo.photo=photo.id
JOIN album2permission ON album2permission.album=album2photo.album
WHERE album2permission.who IN ('stefan')
AND (
NOT EXISTS (SELECT 1 FROM photo2permission WHERE photo2permission.photo=photo.id)
OR
EXISTS (SELECT 1 FROM photo2permissionWHERE photo2permission.photo=photo.id AND photo2permission.who='stefan')
)
But this strikes me as very inelegant and inefficient. Any ideas are highly appreciated.
Thanks, Stefan
答案 0 :(得分:2)
问题在于您的数据设计,这本身就需要进行复杂的查询检查。此设计也存在其他问题:
我会通过更改数据设计来解决这个问题。
有多种选择,但也许您已经完成的最简单的选择是让photo2permission
否定。换句话说,此处的条目意味着用户阻止看到照片。
然后你的查询将是这个(为了清楚起见,我已将其重命名为 blockedphotos ):
SELECT photo.* FROM photo
JOIN album2photo ON album2photo.photo=photo.id
JOIN album2permission ON album2permission.album=album2photo.album
WHERE album2permission.who IN ('stefan')
AND NOT EXISTS (
SELECT 1 FROM blockedphotos WHERE
blockedphotos.photo=photo.id and
blockedphotos.who='stefan'
)
左连接将是另一种选择,在许多情况下更有效:
SELECT photo.* FROM photo
JOIN album2photo ON album2photo.photo=photo.id
JOIN album2permission ON album2permission.album=album2photo.album
LEFT JOIN blockedphotos ON
blockedphotos.photo=photo.id and
blockedphotos.who='stefan'
WHERE album2permission.who IN ('stefan')
AND blockedphotos.photo is null
答案 1 :(得分:1)
这样做:
SELECT photo.* FROM photo
JOIN album2photo ON album2photo.photo=photo.id
JOIN album2permission ON album2permission.album=album2photo.album
LEFT JOIN photo2permission ON photo.id = photo2permission.photo
WHERE album2permission.who IN ('stefan') AND
(photo2permission.who = 'stefan' or photo2permission.who is null)
LEFT联接使得当照片在photo2permissions中具有相应的记录时,实际上您在该表的字段中获取值,但如果照片没有权限,则您获得空值。因此,您可以根据任一条件进行过滤 - 具有权限,包含“stefan”,或者该表中根本没有值。
但是,正如Dan1111在他的回答中指出的那样,您的数据模型将导致删除用户可能授予其他用户访问权限。您需要小心处理该案例,或更改您的模型。一种选择是将photoisprivate布尔值添加到照片表中,只要用户必须具有查看该照片的特定权限,就会将其标记为真。然后条件将变为......
WHERE album2permission.who IN ('stefan') AND
(photo.photoisprivate = false OR photo2permission.who = 'stefan')
你是否这样做的决定性因素,或者我认为DAN建议的阻止照片模型是你是否更有可能在某种情况下向一个用户(或少数用户)授予权限,而忽略其余用户,或者是否要阻止一个或几个用户,同时允许其余用户访问。您是否让用户授予访问权限或阻止访问权限?你的模型应该反映这个问题的答案。