我遇到的情况是,每个客户端都有用户,并且每个用户都可以访问有关一个或多个分支的信息。 我们也有sys管理员,他们可以看到所有内容,并且在数据库中没有分配任何站点。只是说用户是sys admin,所以我们的系统不限制访问。
我需要进行数据库查询,以提取用户有权访问的分支列表,但是如果用户是sys admin,则要提取系统中所有分支的列表。
我正在尝试类似的操作,但这不起作用:
Select sites.name, sites.id
FROM sites
WHERE
sites.id IN (
CASE
WHEN (select u.level FROM users "u" WHERE u.username = 'JohnBrown') ='ROLE_SYSTEM_ADMIN'
THEN
(select id FROM sites)
ELSE
(select s2.id FROM users_have_sites uhs2
left join users u2 ON u2.id = uhs2.user_id
left join sites s2 ON s2.id = uhs2.site_id
where u2.username = 'JonhBrown')
END
)
我收到此错误:
ERROR: more than one row returned by a subquery used as an expression
答案 0 :(得分:0)
我认为这可以满足您的需求
select s.name, s.id
from sites s
inner join users u on u.username = 'JohnBrown'
where
u.level = 'ROLE_SYSTEM_ADMIN'
or exists (
select 1
from users_have_sites uhs
where uhs.site_id = s.id and uhs.user_id = u.id
)
这是查询的另一个版本,您可能会发现它更容易理解(我愿意):
select s.name, s.id
from users u
inner join sites s
on u.level = 'ROLE_SYSTEM_ADMIN'
or exists (
select 1
from users_have_sites uhs
where uhs.site_id = s.id and uhs.user_id = u.id
)
where u.username = 'JohnBrown'
答案 1 :(得分:0)
我认为类似这样的方法对您有用:
SELECT s.name, s.id
FROM sites s
LEFT JOIN users_have_sites uhs ON uhs.site_id = s.id
LEFT JOIN users u ON u.id = uhs.user_id AND u.username = 'JohnBrown'
WHERE (CASE WHEN (SELECT u.level FROM users WHERE u.username = 'JohnBrown') = 'ROLE_SYSTEM_ADMIN'
THEN TRUE ELSE FALSE END
OR u.id IS NOT NULL);
LEFT JOIN
不会像sites
那样过滤INNER JOIN
表中的记录,因此,符合WHERE
子句中任一条件的站点将在结果中。这意味着,如果您的子查询显示该用户是sys admind,或者在users_have_sites
表中找到该用户的记录并找到了站点,则这些站点将位于结果集中。
编辑:另一个相当容易阅读的解决方案是这样的:
SELECT s.name, s.id
FROM sites s,
users_have_sites uhs,
users u
WHERE u.username = 'JohnBrown'
AND (u.level = 'ROLE_SYSTEM_ADMIN'
OR (s.id = uhs.site_id AND u.id = uhs.user_id))
GROUP BY s.name, s.id;
此查询的缺点是它使用隐式联接,这些隐式联接已不再使用。通常,它们被视为较旧的处理方式,效率可能较低。这会将表上的所有行连接到另一个表的所有行,然后所有过滤(以及通常认为的连接条件)都在WHERE
子句中。这些类型的联接效率可能较低,但这种联接不应如此,因为WHERE子句可确保每个站点只有1个结果。