Как правильно построить этот SQL-запрос? - PullRequest
0 голосов
/ 03 февраля 2012

У меня есть пять таблиц:

Пользователь

 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

Марк, Джен и Мбали являютсявсе сотрудники (role_id = 3) вымышленной компании.Два других пользователя, Mbabani и Fang Zhao, являются клиентами, которые создают запросы.

В запросе должно быть указано, что req_id 8 и 9 был запрошен пользователем (Mbabani [3]), который принадлежит роли Customer Africa (посредством User_Role_Assoc), у которой нет назначенных представителей (Role_Reps).

В запросе должна быть возможность увидеть, что req_id 10 и 11 были запрошены пользователем (Fang Zhao [4]), который принадлежит роли Customer Asia (через User_Role_Assoc), которая имеет представителей.

Все сотрудники должны видеть все запросы.Если в таблице Role_Reps нет представителя роли, назначенного этой роли.Если есть какие-либо представители, в данном случае Марк и Джен, они ЕДИНСТВЕННЫЕ, которым разрешено видеть запрос.Если в таблице Role_Reps не определено ни одного представителя, тогда все должны видеть запросы.

Поэтому мне нужен запрос, который:

Если я передам userid=2 (Mbali)Я должен получить следующие результаты:

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

Если я передам userid=0 или userid=1 (Марк или Джен), я должен получить следующие результаты:

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

IНадеюсь, я ясно дал понять.


ОБНОВЛЕНИЕ

Вот 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 ]

2 голосов
/ 03 февраля 2012

Точно так же, как общий совет, я часто обнаруживаю, что проще понять, создавая новый набор отношений, используя естественные ключи вместо суррогатных. Так что в этом случае я мог бы сделать это:

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

Теперь, когда вы приступаете к построению запроса, вам не нужно выполнять все объединения сразу, вы можете в интерактивном режиме добавить еще одно объединение и другое, и вам будет намного понятнее, когда вы приблизитесь к тому, что вам нужно. Когда вы закончите, вы можете вернуть этот запрос обратно в вашу фактическую базу данных с суррогатными идентификаторами для всего, и он будет «просто работать».

Что касается вашего фактического вопроса, вы рассчитываете на отсутствие чего-то, что вызывает присутствие чего-то еще. Я обычно нахожу такие вещи трудно выразить в SQL. Возможно, вам будет проще, если вы добавите дополнительные данные в одно из отношений, чтобы указать, что что-то является общедоступным или глобальным, и выполните ограниченный запрос UNION с отдельным запросом, чтобы получить весь глобальный материал.

1 голос
/ 03 февраля 2012

Надеюсь, я понимаю, но это должно помочь вам начать и закрыть (если я правильно понял)

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
0 голосов
/ 03 февраля 2012

Привет, я попробовал MSSQL.

Первый оператор выбора дает вам запрос назначенных представителей, второй оператор выбора - запрос пользователей, для которых ни один представитель не назначен.Это выглядит не оптимизированным, я знаю, но, по крайней мере, это работает.Я попробовал.

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

Если вы просто хотите, чтобы запросы одного сотрудника, просто добавьте к предложению where в обоих случаях:

And Employee.User_Id = ?
0 голосов
/ 03 февраля 2012

Я думаю, что это должно получить то, что вы хотите

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
...