SQL: где НЕ IN запрос - PullRequest
       5

SQL: где НЕ IN запрос

1 голос
/ 19 декабря 2011

Итак, в базе данных есть таблица с именем role_users.Это содержит все роли, которые имеют пользователи.Здесь есть два столбца: user_id, role_id.

Обычный пользователь без дополнительных ролей имеет 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, role_id 2 и role_id 3.

Partner имеет 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 и вот оно.

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

Ответы [ 6 ]

1 голос
/ 19 декабря 2011

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 голосов
/ 19 декабря 2011

Вот решение с использованием 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)
0 голосов
/ 19 декабря 2011

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

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

РЕДАКТИРОВАТЬ: я думаю, что это ответ на неправильный вопрос, он показывает тип каждого пользователя.

0 голосов
/ 19 декабря 2011

Вот решение на основе объединения, которое, надеюсь, работает на любом 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» в предложении where, чтобы изменить роли, которые вы хотите использовать для пользователяиметь или не иметь.

0 голосов
/ 19 декабря 2011

Относительно продавцов и партнеров:
Насколько я понимаю, продавцы - единственные, у кого может быть 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 голосов
/ 19 декабря 2011

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

SELECT 
    COUNT(*) AS num
    GROUP_CONCAT(role_id ORDER BY role_id) AS privilegeset
FROM roles_users
GROUP BY user_id
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...