使用条件子查询的Postgresql WHERE子句

时间:2020-01-31 18:25:35

标签: postgresql

我遇到的情况是,每个客户端都有用户,并且每个用户都可以访问有关一个或多个分支的信息。 我们也有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

2 个答案:

答案 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个结果。