2011-12-18 3 views
1

Итак, в базе данных есть таблица с именем role_users. Это содержит все роли, которые есть у пользователей. Здесь есть два столбца: user_id, role_id.SQL: где НЕ IN запрос

Обычный пользователь, без дополнительных ролей, имеет 1 строку в этой таблице. Эта строка имеет role_id 1.

Администратор, имеет 2 строки в этой таблице. Один с ROLE_ID 1, и один ряд с ROLE_ID 2

Как это:

user_id role_id 
88  1 
88  2 
99  1 // Only one row with that user_id, so he's a user 

Теперь им пытаются подсчитать, сколько пользователей/администратор/продавцы/партнеры, что существует.

Продавцы имеют 3 ряда, один с ROLE_ID 1, 2 и ROLE_ID ROLE_ID 3.

Партнер ROLE_ID 1, ROLE_ID 4

Так я попытался это:

SELECT user_id FROM roles_users WHERE role_id IN (1) // MEMBERS ONLY 
SELECT user_id FROM roles_users WHERE role_id IN (1,2) // ADMIN 
SELECT user_id FROM roles_users WHERE role_id IN (1,2,3) // SELLER 
SELECT user_id FROM roles_users WHERE role_id IN (1,4) // PARTNERS 

Но эти запросы Не работает должным образом. Они дают мне счет, который больше, чем предполагалось. И я считаю, что это потому, что это не исключает любые строки,

Я имею в виду, когда он должен искать role_id 1, для членов он включает в себя партнеров, администраторов, продавцов, потому что он проверяет только, любой с строкой role_id 1 и thats it.

Так как я могу это сделать правильно? Поэтому, когда он ухаживает член, он также должен убедиться, что user_id не имеет больше строк с другими role_ids как 2,3,4

+0

Попробуйте выбрать отдельный user_Id – Sparky

+0

Реляционный оператор, который вам нужен, это [раздел] (http://en.wikipedia.org/wiki/Relational_algebra#Division_.28.C3.B7.29), широко известный как ["поставщик который поставляет все детали »] (http://www.dbdebunk.com/page/page/772076.htm). Предлагаемое чтение [здесь] (http://www.simple-talk.com/sql/t-sql-programming/divided-we-stand-the-sql-of-relational-division/), [здесь] (http: //www.dbdebunk.com/page/page/772076.htm) и [здесь] (http://www.cs.arizona.edu/~mccann/research/divpresentation.pdf). – onedaywhen

ответ

0

Это даст вам все комбинации привилегий в таблице с их подсчетом

SELECT 
    COUNT(*) AS num 
    GROUP_CONCAT(role_id ORDER BY role_id) AS privilegeset 
FROM roles_users 
GROUP BY user_id 
+0

Ух! Это действительно работает? Постарайтесь объяснить логику этого? – fge

0

что касается продавцов и партнеров:
Как я понимаю, продавцы являются единственными, которые могут иметь role_id = 3 и партнеры являются единственными, которые могут иметь role_id = 4, правильно?

Если да, то найти продавцов и партнеров довольно легко:

SELECT user_id FROM roles_users WHERE role_id = 3 // SELLER 
SELECT user_id FROM roles_users WHERE role_id = 4 // PARTNERS 
0

Вот нарисуй решение, которое, мы надеемся, работает на любой SQL.

SELECT user_id FROM roles_users RU0 
LEFT JOIN roles_users RU1 ON RU0.user_id = RU1.user_id AND RU1.role_id = 1 
LEFT JOIN roles_users RU2 ON RU0.user_id = RU2.user_id AND RU2.role_id = 2 
LEFT JOIN roles_users RU3 ON RU0.user_id = RU3.user_id AND RU3.role_id = 3 
LEFT JOIN roles_users RU4 ON RU0.user_id = RU4.user_id AND RU4.role_id = 4 
WHERE RU1.user_id IS NOT NULL -- should have role 1 
    AND RU2.user_id IS NULL  -- should NOT have role 2 
    AND RU3.user_id IS NULL  -- should NOT have role 3 
    AND RU4.user_id IS NOT NULL -- should have role 4 

Просто изменения «IS NULL» и «IS NOT NULL» в ИНЕКЕ изменить какие роли вы хотите, чтобы пользователь иметь или не иметь.

1

group_concat является то, что вы хотите:

select 
    case roles 
     when '1' then 'members' 
     when '1,2' then 'admins' 
     when '1,2,3' then 'sellers' 
     when '1,4' then 'partners' 
     else 'uh??' 
    end role, 
    count(user_id) nr_users from (
     select user_id, group_concat(role_id order by role_id separator ',') roles 
     from roles_users 
     group by user_id 
    ) 
group by role 
order by role; 

И, кстати, вы могли бы более эффективно хранить роли, используя битовую маску. Преимущество: только один столбец на идентификатор пользователя. Недостаток: сложнее строить запросы ...

+0

Мне это нравится. Но нужен ли вам запрос role_id в вашем внутреннем запросе, чтобы гарантировать, что возвращаемая строка всегда одинакова (т.е. «1,2,3», а не «3,1,2»)? – Glenn

+0

Именно в самом выражении group_concat он упорядочен, прежде чем он сгруппирован. – fge

+0

Правильно, извините, не поймал этого. Это хорошее решение. Я бы хотел, чтобы у Oracle была такая функция. – Glenn

0

Не самый элегантный, но начало

SELECT user_id 
     ,CASE WHEN (role1 > 0 AND 0 = role2 AND 0 = role3 AND 0 = role4) THEN 'MEMBER' 
     WHEN (role1 > 0 AND role2 > 0 AND 0 = role3 AND 0 = role4) THEN 'ADMIN' 
     WHEN (role1 > 0 AND role2 > 0 AND role3 > 0 AND 0 = role4) THEN 'SELLER' 
     WHEN (role1 > 0 AND 0 = role2 AND 0 = role3 AND role4 > 0) THEN 'PARTNER' 
     ELSE 'Something else' 
     END AS type 
    FROM (
    SELECT user_id 
      ,SUM(CASE role_id WHEN 1 THEN 1 ELSE 0 END) AS role1 
      ,SUM(CASE role_id WHEN 2 THEN 1 ELSE 0 END) AS role2 
      ,SUM(CASE role_id WHEN 3 THEN 1 ELSE 0 END) AS role3 
      ,SUM(CASE role_id WHEN 4 THEN 1 ELSE 0 END) AS role4 
     FROM roles_users 
     GROUP BY user_id 
    ) x 

EDIT: Я думаю, это ответить на неправильный вопрос, она показывает тип каждого пользователя.

0

Вот решение, использующее INTERSECT и MINUS. К сожалению, они еще не поддерживаются MySQL, но, возможно, кто-то найдет это полезным, тем не менее.

- Найти пользователей с ролью 1

SELECT user_id FROM 
(SELECT user_id, role_id FROM roles_users WHERE role_id = 1) 
MINUS SELECT user_id FROM roles_users WHERE role_id NOT IN (1) 

- Найти пользователей с ролями 1 и 2

SELECT user_id FROM 
(SELECT user_id, role_id FROM roles_users WHERE role_id = 1 
    INTERSECT SELECT user_id, role_id FROM roles_users WHERE role_id = 2) 
MINUS SELECT user_id FROM roles_users WHERE role_id NOT IN (1,2) 

- Найти пользователей с ролями 1, 2, 3

SELECT user_id FROM 
(SELECT user_id, role_id FROM roles_users WHERE role_id = 1 
    INTERSECT SELECT user_id, role_id FROM roles_users WHERE role_id = 2 
    INTERSECT SELECT user_id, role_id FROM roles_users WHERE role_id = 3) 
MINUS SELECT user_id FROM roles_users WHERE role_id NOT IN (1,2,3) 

- Найти пользователей с Ролями 1, 4

SELECT user_id FROM 
(SELECT user_id, role_id FROM roles_users WHERE role_id = 1 
    INTERSECT SELECT user_id, role_id FROM roles_users WHERE role_id = 4) 
MINUS SELECT user_id FROM roles_users WHERE role_id NOT IN (1,4) 
Смежные вопросы