如何正确构造此SQL查询?

时间:2012-02-02 20:26:46

标签: sql postgresql relational-database

我有五张桌子:

用户

 user_id | name
--------------------
    0    | Mark  
    1    | Jen
    2    | Mbali
    3    | Mbabani
    4    | Fang Zhao

角色

 role_id | name
--------------------
    3    | Employee
    4    | Customer Asia
    5    | Customer Africa

User_Role_Assoc

 role_id | user_id
--------------------
    3    |   0
    3    |   1
    3    |   2
    5    |   3
    4    |   4

Role_Reps

 role_id | user_id
--------------------
    4    |   0
    4    |   1

请求

 req_id  | user_id
--------------------
    8    |   3
    9    |   3
   10    |   4
   11    |   4

Mark,Jen和Mbali都是虚构公司的员工(role_id = 3)。另外两个用户Mbabani和Fang Zhao是创建请求的客户。

查询应该能够看到req_id 8和9是由属于Customer Africa角色的用户(Mbabani [3])请求的(通过User_Role_Assoc),没有分配代表(Role_Reps)。

查询应该能够看到req_id 10和11是由属于客户亚洲角色(通过User_Role_Assoc)的用户(方昭[4])请求的,该用户有代表。

所有员工都应该能够看到所有请求。除非在Role_Reps表中有一个角色代表负责该角色。如果有任何代表,在这种情况下马克和仁,他们是唯一可以看到请求的人。如果Role_Reps表中没有定义代表,那么每个人都应该能够看到请求。

所以我需要一个查询:

如果我传入userid=2(Mbali),我应该得到以下结果:

 req_id  | user_id
--------------------
   10    |   4
   11    |   4

如果我传入userid=0userid=1(Mark或Jen),我会得到以下结果:

 req_id  | user_id
--------------------
    8    |   3
    9    |   3
   10    |   4
   11    |   4

我希望自己清楚明白。


更新

以下是使用数据生成表格的DDL:

DROP TABLE IF EXISTS t_user;


CREATE TABLE t_user (
    userid          integer PRIMARY KEY,
    name            varchar(20)
);

GRANT ALL PRIVILEGES ON t_user TO PUBLIC;

INSERT INTO t_user (userid, name) VALUES (0,'Mark');
INSERT INTO t_user (userid, name) VALUES (1,'Jen');
INSERT INTO t_user (userid, name) VALUES (2,'Mbali');
INSERT INTO t_user (userid, name) VALUES (3,'Mbabani');
INSERT INTO t_user (userid, name) VALUES (4,'Fang Zhao');



DROP TABLE IF EXISTS t_role;


CREATE TABLE t_role (
    roleid          integer PRIMARY KEY,
    name            varchar(20)
);

GRANT ALL PRIVILEGES ON t_role TO PUBLIC;

INSERT INTO t_role (roleid, name) VALUES (3,'Employee');
INSERT INTO t_role (roleid, name) VALUES (4,'Customer Asia');
INSERT INTO t_role (roleid, name) VALUES (5,'Customer Africa');


DROP TABLE IF EXISTS t_user_role_assoc;


CREATE TABLE t_user_role_assoc (
    roleid          integer,
    userid          integer,
    primary key(roleid, userid)
);

GRANT ALL PRIVILEGES ON t_user_role_assoc TO PUBLIC;

INSERT INTO t_user_role_assoc (roleid, userid) VALUES (3,0);
INSERT INTO t_user_role_assoc (roleid, userid) VALUES (3,1);
INSERT INTO t_user_role_assoc (roleid, userid) VALUES (3,2);
INSERT INTO t_user_role_assoc (roleid, userid) VALUES (5,3);
INSERT INTO t_user_role_assoc (roleid, userid) VALUES (4,4);




DROP TABLE IF EXISTS t_role_reps;


CREATE TABLE t_role_reps (
    roleid          integer,
    userid          integer,
    primary key(roleid, userid)
);

GRANT ALL PRIVILEGES ON t_role_reps TO PUBLIC;

INSERT INTO t_role_reps (roleid, userid) VALUES (4,0);
INSERT INTO t_role_reps (roleid, userid) VALUES (4,1);


DROP TABLE IF EXISTS t_request;


CREATE TABLE t_request (
    req_id          integer PRIMARY KEY,
    userid          integer
);

GRANT ALL PRIVILEGES ON t_request TO PUBLIC;

INSERT INTO t_request (req_id, userid) VALUES (8,3);
INSERT INTO t_request (req_id, userid) VALUES (9,3);
INSERT INTO t_request (req_id, userid) VALUES (10,4);
INSERT INTO t_request (req_id, userid) VALUES (11,4);

4 个答案:

答案 0 :(得分:2)

正如一般建议一样,我发现通过使用自然键而不是代理键创建一组新关系,通常更容易看到发生了什么。所以在这种情况下,我可能会这样做:

User
-------------------------
 username    | name
-------------------------
  mark       | Mark  
  jen        | Jen
  mbali      | Mbali
  mbabani    | Mbabani
  fangzhao   | Fang Zhao

Role
------------------------------------
 role_id          | name
------------------------------------
  employee        | Employee
  customer_asia   | Customer Asia
  customer_africa | Customer Africa

User_Role_Assoc
------------------------------
 role_id          | user_id
------------------------------
  employee        | mark
  employee        | jen
  employee        | mbali
  customer_africa | mbabani
  customer_asia   | fangzhao

Role_Reps
----------------------------
 role_id          | user_id
----------------------------
  customer_asia   | mark
  customer_africa | jen

Request
---------------------
 req_id  | user_id
---------------------
  8      | mbabani
  9      | mbabani
 10      | fangzhao
 11      | fangzhao

现在,当你构建你的查询时,你不必一次完成所有的连接,你可以交互式地添加另一个连接和另一个连接,当你接近你需要的时候,它会更加清晰。完成后,您可以将该查询带回到您的实际数据库,并使用代理ID进行所有操作,它将“正常工作。”

关于你的实际问题,你指望没有触发其他东西存在的东西。我经常发现在SQL中很难表达这种东西。如果您将其他数据添加到其中一个关系中以表示某些内容是公开的或全局的,您可能会发现它更容易,并使用单独的查询执行受限查询UNION以获取所有全局内容。

答案 1 :(得分:1)

希望我能理解,但这应该让你开始和结束(如果我理解的话,那就是正确的)

select r.req_id,
u1.name as requestingUser,
rl.name as BelongsToRole,
coalesce(r2.name, '') as RoleRep

from request R 
join user u1 on r.user_id=u1.user_id
join user_role_assoc ura on ura.user_id=u1.user_id
join role rl on rl.role_id=ura.role_id
left join role_Reps r1 on r1.role_id=rl.role_id
left join users R2 on r2.user_id=rl.user_id

答案 2 :(得分:0)

我认为这应该得到你想要的东西

SELECT R.req_id, R.user_id
FROM Request R
    INNER JOIN User_Role_Assoc A ON A.user_id = R.user_id
    LEFT JOIN Role_Reps L ON L.role_id = A.role_id
WHERE L.user_id IS NULL OR L.user_id = @user_id

答案 3 :(得分:0)

您好,我试了一下MSSQL。

First Select-Statement为您提供分配的代表的请求,第二个Select Statement为用户的请求提供,其中没有分配代表。它看起来没有优化我知道,但至少它的工作原理。我试了一下。

SELECT EMPLOYEE.USER_ID
   ,EMPLOYEE.NAME AS EMPLOYEE_NAME
   ,CUSTOMER.NAME AS ASSIGNED_CUSTOMER_NAME
   ,ASSIGNED_REQUESTS.REQ_ID AS REQ_ID
    FROM USR EMPLOYEE
INNER JOIN USER_ROLE_ASSOC URA ON EMPLOYEE.USER_ID = URA.USER_ID AND URA.ROLE_ID = 3
LEFT JOIN ROLE_REPS RR ON EMPLOYEE.USER_ID = RR.USER_ID
LEFT JOIN USER_ROLE_ASSOC CUSTOMER_ROLE ON RR.ROLE_ID = CUSTOMER_ROLE.ROLE_ID
LEFT JOIN USR CUSTOMER ON CUSTOMER_ROLE.USER_ID = CUSTOMER.USER_ID
LEFT JOIN REQUEST ASSIGNED_REQUESTS ON CUSTOMER.USER_ID = ASSIGNED_REQUESTS.USER_ID
WHERE ASSIGNED_REQUESTS.REQ_ID IS NOT NULL
UNION 
SELECT EMPLOYEE.USER_ID
   ,EMPLOYEE.NAME AS EMPLOYEE_NAME
   ,CUSTOMER.NAME AS ASSIGNED_CUSTOMER_NAME
   ,ASSIGNED_REQUESTS.REQ_ID AS REQ_ID
    FROM USR EMPLOYEE
INNER JOIN USER_ROLE_ASSOC URA ON EMPLOYEE.USER_ID = URA.USER_ID AND URA.ROLE_ID = 3
LEFT JOIN ROLE R ON R.ROLE_ID NOT IN (SELECT ROLE_ID FROM ROLE_REPS) AND R.ROLE_ID <> 3
LEFT JOIN USER_ROLE_ASSOC CUSTOMER_ROLE ON R.ROLE_ID = CUSTOMER_ROLE.ROLE_ID
LEFT JOIN USR CUSTOMER ON CUSTOMER_ROLE.USER_ID = CUSTOMER.USER_ID
LEFT JOIN REQUEST ASSIGNED_REQUESTS ON CUSTOMER.USER_ID = ASSIGNED_REQUESTS.USER_ID
WHERE ASSIGNED_REQUESTS.REQ_ID IS NOT NULL

如果您只想要一个Employee的请求,只需添加到select子句中的where子句:

And Employee.User_Id = ?