2016-06-06 2 views
1

У меня есть следующие таблицы в моей базе данных SQL Server;Как я могу выбрать всех пользователей, у которых есть набор разрешений, а не одно конкретное разрешение SQL?

учетная_запись_пользователя

Username | UserId 
Joe  |  1 
Hannah |  2 
Jack  |  3 
Jill  |  4 

Разрешение

Permission | PermissionId 
p1   |  1 
p2   |  2 
p3   |  3 
p4   |  4 

UserPermission

UserId | PermissionId 
1  | 1 
1  | 2 
1  | 3 
1  | 4 
2  | 1 
2  | 2 
2  | 4 
3  | 1 
3  | 2 
3  | 3 
4  | 2 
4  | 3 
4  | 4 

Я хочу вернуть набор имен пользователей, где пользователь имеет разрешения p1 и p2, но не разрешение p3.

Username 
Hannah 

Ближайший я получил это;

SELECT ua.Username 
FROM UserPermission up 
JOIN UserAccount ua ON ua.UserID = up.UserId 
JOIN Permission p ON p.PermissionId = up.PermissionId 
WHERE p.Name = 'p1' OR p.Name = 'p2' 
GROUP BY up.UserId,ua.Username 
HAVING COUNT(up.UserId) = 2 

Что возвращает только пользователи, имеющие разрешения p1 и p2 (и он чувствует, как я буду в совершенно неправильном направлении так или иначе)

Что бы лучший способ, чтобы получить набор результатов, показанный (и что, если бы я хотел пользователей, у которых были p1 и p2, но не p3 или p4 для +1!)?

Спасибо.

+0

Вы идете в правильном направлении. Единственное, что я сделал бы, это 'p.Name IN ('p1', 'p2')' – MatBailie

+0

Это не исключает пользователей, у которых также есть p3 –

+0

. Я не знаю, что именно вы храните как «имя» для разрешения, но лучше было бы ссылаться на них по своим Идентификаторам в предложении WHERE, если это возможно. – marsze

ответ

2

Вы можете продолжить идею, что используете. Единственное отличие является удаление пункта where и повышение having положения:

SELECT u.Username 
FROM UserPermission up JOIN 
    UserAccount ua 
    ON ua.UserID = up.UserId JOIN 
    Permission p 
    ON p.PermissionId = up.PermissionId 
GROUP BY up.UserId,u.Username 
HAVING SUM(CASE WHEN p.name = 'p1' THEN 1 ELSE 0 END) > 0 AND 
     SUM(CASE WHEN p.name = 'p2' THEN 1 ELSE 0 END) > 0 AND 
     SUM(CASE WHEN p.name IN ('p3', 'p4') THEN 1 ELSE 0 END) = 0; 
0

я использую Entity Framework, и это дает мне следующее сгенерированный SQL для такого запроса:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name] 
    FROM [dbo].[Users] AS [Extent1] 
    WHERE (EXISTS (SELECT 
     1 AS [C1] 
     FROM [dbo].[UserPermissions] AS [Extent2] 
     WHERE ([Extent1].[Id] = [Extent2].[User_Id]) AND (1 = [Extent2].[Permission_Id]) 
    )) AND (EXISTS (SELECT 
     1 AS [C1] 
     FROM [dbo].[UserPermissions] AS [Extent3] 
     WHERE ([Extent1].[Id] = [Extent3].[User_Id]) AND (2 = [Extent3].[Permission_Id]) 
    )) AND (NOT EXISTS (SELECT 
     1 AS [C1] 
     FROM [dbo].[UserPermissions] AS [Extent4] 
     WHERE ([Extent1].[Id] = [Extent4].[User_Id]) AND (3 = [Extent4].[Permission_Id]) 
    )) 

Это может быть более эффективным , Мне нравится использовать EXISTS для лучшей производительности. Возможно, вам захочется переписать его, чтобы улучшить читаемость и сопоставить имена столбцов и таблиц с вашей моделью базы данных.

1
SELECT ua.UserName 
FROM UserPermission up 
LEFT JOIN UserAccount ua 
    ON ua.UserId = up.UserId 
LEFT JOIN Permission p 
    ON p.PermissionId = up.PermissionId 
GROUP BY ua.UserName 
HAVING SUM (CASE WHEN p.Permission IN ('p1','p2') THEN 2 WHEN p.Permission = 'p3' THEN 1 ELSE 0 END) = 4 

Выход:

UserName 
Hannah 
Смежные вопросы