Как получить исходную строку в соединении SQL от исчезновения? - PullRequest
0 голосов
/ 16 ноября 2008

В этом запросе:

SELECT COUNT(*) AS UserCount, Company.* FROM Company
LEFT JOIN User
ON User.CompanyId = Company.Id
WHERE Company.CanAccessSystem= true
AND(User.CanAccessSystem IS null OR User.CanAccessSystem = true)
GROUP BY Company.Id

Я хочу запросить список компаний, которые могут получить доступ к конкретной системе, а также количество пользователей, которые могут получить доступ к системе внутри компании.

Этот запрос работает для всех случаев, кроме одного очень важного. Если компания может получить доступ к системе, но никто из пользователей не может, компания полностью исчезнет из запроса (т. Е. Users.CanAccessSystem = false). В этом случае я просто хочу UserCount = 0.

Пример от компаний, которые могут получить доступ к системе:

Users   Company Name
1       WidgetWorks
3       WidgetCompany
0       WidgesRUs

Эта система работает на MySQL

Редактирование запроса: исправлена ​​опечатка "ON User.CompanyId = Company.Id"

Ответы [ 5 ]

3 голосов
/ 16 ноября 2008

Причина, по которой ваш результат не работает, заключается в том, что у вас нет предложения объединения.

SELECT IFNULL(COUNT(User.Id), 0) AS UserCount, Company.* 
FROM Company
LEFT JOIN User ON User.CompanyId = Company.Id AND User.CanAccessSystem = true
WHERE Company.CanAccessSystem = true
GROUP BY Company.Id

Это должно работать. Смысл с левым объединением состоит в том, что записи в основной таблице всегда должны появляться, однако оставленные присоединенные записи не должны.

IFNULL () предназначен только для возврата 0, поскольку ни один из соответствующих пользователей не будет отображать значение NULL в этом случае. Я не совсем уверен, как вы обрабатываете логические значения в MySQL, поскольку он не поддерживает его изначально.

2 голосов
/ 16 ноября 2008

Я думаю, я бы начал с подзапроса, который генерирует (непересекающееся) объединение:

  • Идентификатор компании и (ненулевое) количество пользователей, которые могут получить доступ к системе, где хотя бы один пользователь может получить доступ к системе
  • Идентификатор компании и нулевой счет, когда ни один пользователь не может получить доступ к системе

Предполагая, что User.CanAccessSystem IS NULL был артефактом, необходимым для учета ЛЕВОГО СОЕДИНЕНИЯ, что приводит к:

SELECT Company.ID, COUNT(*) AS UserCount
    FROM Company, User
    WHERE Company.ID = User.CompanyID
      AND User.CanAccessSystem = true
UNION
SELECT Company.ID, 0 AS UserCount
    FROM Company
    WHERE NOT EXISTS (SELECT * FROM User
                         WHERE Company.ID = User.CompanyID
                           AND User.CanAccessSystem = true)

Вы можете отфильтровать обе части с помощью 'AND Company.CanAccessSystem = true', и будет полезно, если большинство компаний не смогут получить доступ к системе - или вы можете отложить ее до финальной стадии обработки.

Затем необходимо выполнить прямое объединение этого результата с Компанией, обеспечив, чтобы условие фильтра для компании, имеющей доступ к системе, было применено где-то вдоль линии.

Номинально это приводит к следующему (непроверенному) коду:

SELECT UserCount, Company.*
    FROM Company JOIN
        (SELECT Company.ID AS ID, COUNT(*) AS UserCount
             FROM Company, User
             WHERE Company.ID = User.CompanyID
               AND User.CanAccessSystem = true
         UNION
         SELECT Company.ID AS ID, 0 AS UserCount
             FROM Company
             WHERE NOT EXISTS (SELECT * FROM User
                                  WHERE Company.ID = User.CompanyID
                                    AND User.CanAccessSystem = true)
        ) AS NumUsers
         ON Company.ID = NumUsers.ID
    WHERE Company.CanAccessSystem = true
0 голосов
/ 16 ноября 2008

У меня не установлен MySQL ... Попробовал это на MSSQL, и я верю, что он справится с вашим сценарием, с небольшой модификацией, чтобы быть действительным MySQL ...

SELECT c.CompanyID, c.CompanyName, SUM(CASE u.CanAccessSystem WHEN 1 then 1 else 0 end) 
FROM Company c LEFT OUTER JOIN
    [User] u
ON u.CompanyID = c.CompanyID
WHERE c.CanAccessSystem = 1
GROUP BY c.CompanyID, c.CompanyName
0 голосов
/ 16 ноября 2008

Вот небольшая хитрость, чтобы сделать то, что вы хотите, используя bools - это единицы или нули, которые вы можете суммировать (canaccess), чтобы получить количество пользователей, которые могут получить доступ к системе. Чтобы избежать проблем с NULL, есть COALESCE, который преобразует NULL в 1 (т. Е. Может иметь доступ к системе)

mysql> select * from companies;
+------+------------+-----------+
| name | company_id | canaccess |
+------+------------+-----------+
| foo  |          1 |         1 |
| bar  |          2 |         1 |
| baz  |          3 |         1 |
| quux |          4 |         1 |
+------+------------+-----------+
4 rows in set (0.00 sec)

mysql> select * from users;
+---------+------------+-----------+
| user_id | company_id | canaccess |
+---------+------------+-----------+
|       1 |          1 |         1 |
|       2 |          1 |         0 |
|       3 |          1 |         1 |
|       4 |          2 |      NULL |
|       5 |          2 |         1 |
|       6 |          2 |         0 |
|       7 |          3 |         1 |
|       8 |          3 |         0 |
|       9 |          4 |         0 |
+---------+------------+-----------+
9 rows in set (0.00 sec)

mysql> select company_id, sum(canaccess) from 
(select users.user_id, coalesce(users.canaccess,1) as canaccess, 
users.company_id, companies.name from companies join users 
on (users.company_id = companies.company_id) where companies.canaccess = 1 ) foo 
group by company_id;
+------------+----------------+
| company_id | sum(canaccess) |
+------------+----------------+
|          1 |              2 |
|          2 |              2 |
|          3 |              1 |
|          4 |              0 |
+------------+----------------+
4 rows in set (0.00 sec)
0 голосов
/ 16 ноября 2008

Вы должны посчитать количество пользователей, для которых User.CanAccessSystem имеет значение true. Подумайте о чем-то вроде

 count(case when User.CanAccessSystem then true end)

Выражение case вернет NULL в случае, если User.CanAccessSystem имеет значение false (по умолчанию), а count(expr) подсчитывает количество элементов, для которых выражение не равно нулю.

Итак ... Это?

SELECT COUNT(case when User.CanAccessSystem then true end) AS UserCount, Company.*
FROM Company LEFT JOIN User
ON Company.id = user.companyId   -- I had to guess, this seems to be missing in your query
WHERE Company.CanAccessSystem= true
GROUP BY Company.Id

p.s. Я использовал LEFT JOIN вместо INNER JOIN, чтобы не исключать компании без пользователей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...