基于子行的行权限

时间:2014-04-11 17:16:48

标签: mysql sql group-by

我们假设您有以下表格:

CREATE TABLE users 
(
    id INT(11) NOT NULL,
    username VARCHAR(45) NOT NULL
)

CREATE TABLE accounts
(
    id INT(11) NOT NULL,
    accountname VARCHAR(45) NOT NULL
)

CREATE TABLE users_accounts
(
    user_id INT(11) NOT NULL,
    account_id INT(11) NOT NULL
)

CREATE TABLE events
(
    id INT(11) NOT NULL,
    eventdata VARCHAR(45) NOT NULL,
    eventtype VARCHAR(45) NOT NULL,
    date DATE NOT NULL
)

create table events_accounts
(
   event_id INT(11),
   account_id INT(11)
)

现在我希望用户能够查询事件表,但我想限制他们仅在他们也可以访问与事件关联的所有帐户时查看事件。

我已经提出了几种方法来进行查询,但它们看起来效率都很低。例如:

' View shows number of accounts associated with each event
CREATE VIEW event_account_count AS
SELECT e.id AS event_id, count(1) AS account_count
FROM events e JOIN event_accounts ea ON e.id = ea.event_id
GROUP BY e.id

其次

` View shows the number of accounts each user can see of each event
CREATE VIEW event_account_user_count AS
SELECT e.id AS event_id, ua.user_id, count(1) AS account_count
FROM events e 
    JOIN event_accounts ea ON e.id = ea.event_id 
    JOIN users_accounts ua ON ea.account_id = ua.account_id
GROUP BY e.id, ua.user_id

然后最后:

' Select all the events that this user can see where the number 
' of accounts of the event they can see is equal to the number of accounts in the event
SELECT e.*
FROM events e 
    JOIN event_account_count eac ON e.id = eac.event_id
    JOIN event_account_user_count eaus ON e.id = eaus.event_id
WHERE eac.account_count = eaus.account_count AND
eaus.user_id = @user_id

我知道随着事件数量的增加,这将变得非常低效。我已经考虑过实现第一个视图,即在事件表中添加一个account_count,这会切断其中一个查询,但我不知道如何绕过第二个视图。

我倾向于删除一项允许按帐户权限访问事件的功能,因为我知道这可能会成为针对事件表的任何查询的瓶颈,特别是如果查询过滤了事件表中的字段。一旦您在事件表中进行过滤,我就不会认为我可以使用视图,因为谓词不会被推送。

有关其他实现方法的想法吗?

3 个答案:

答案 0 :(得分:1)

尝试双重否定 - 找不到任何你不想找到的东西;)

首先,选择所有不是用户的帐户,然后选择不在这些帐户上的所有活动。

SELECT * FROM events where id NOT IN(
  SELECT event_id FROM events_accounts WHERE account_id NOT IN(
    SELECT account_id FROM users_accounts WHERE user_id = @user_id
))

我认为您不需要更多时间来附加表格并检查条件,但请自行检查。 (未经过测试的代码)

答案 1 :(得分:1)

@ bato3的解决方案非常好,但以下可重用的视图也可能有用:

/* View shows events inaccessible to users */
CREATE VIEW users_inaccessible_events AS
SELECT DISTINCT u.id AS user_id, event_id
FROM users u
    JOIN events_accounts ea ON 1
    LEFT JOIN users_accounts ua
        ON u.id = ua.user_id AND ea.account_id = ua.account_id
WHERE ua.account_id IS NULL

/* View shows events accessible to users */
CREATE VIEW users_accessible_events AS
SELECT u.id AS user_id, e.id AS event_id
FROM users u
    JOIN events e ON 1
    LEFT JOIN users_inaccessible_events AS uie
        ON u.id = uie.user_id AND e.id = uie.event_id
WHERE uie.event_id IS NULL

因此,提供所需结果的查询可以简单如下:

SELECT e.*
FROM users_accessible_events, events e
WHERE user_id = @user_id AND event_id = e.id

如果您需要,例如,可以访问特定事件的用户列表,或特定用户无法访问的事件列表等,则适用的查询也将非常简单,简单。

答案 2 :(得分:0)

  

现在我希望用户能够查询事件表,但我想   限制他们只有在他们也有权访问所有事件时才能查看事件   与活动相关的帐户。

因此,您希望获取与所有此帐户都与用户关联的帐户相关联的所有事件。因此,如果此帐户中至少有一个与用户无关,则不会返回。

所以这是解决方案:

SELECT * 
FROM events e 
WHERE NOT EXISTS (
      SELECT * 
      FROM events_accounts ea 
      WHERE ea.event_id = e.id 
      AND ea.account_id NOT IN (
           SELECT ua.account_id
           FROM users_accounts 
           WHERE ua.user_id = @user_id)

)
;

PS使用视图这不是一个好的解决方案。