Как подсчитать все записи в обеих таблицах в соотношении 1: n в одном запросе? - PullRequest
2 голосов
/ 02 апреля 2009

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

У меня есть следующая структура таблицы и данные:

CREATE TABLE `table_a` (
  `id` int(11) NOT NULL auto_increment,
  `table_b_id` int(11) NOT NULL,
  `field` varchar(10) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `table_b_id` (`table_b_id`)
);

INSERT INTO `table_a` VALUES(1, 1, 'test 1');
INSERT INTO `table_a` VALUES(2, 1, 'test 2');
INSERT INTO `table_a` VALUES(3, 0, 'test 3');

CREATE TABLE `table_b` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(10) NOT NULL,
  PRIMARY KEY  (`id`)
);

INSERT INTO `table_b` VALUES(1, 'value 1');
INSERT INTO `table_b` VALUES(2, 'value 2');

Как видите, идентификатор 2 в table_b не используется в table_a, а идентификатор 3 в table_a имеет значение 0 для table_b_id. То, что я хочу сделать, это получить количество раз, которое каждое значение в b используется в table_a, включая 2 из table_b и количество всех значений, не входящих в table_b.

Я задал следующий запрос:

SELECT b.name, COUNT(a.id) AS number
FROM table_a AS a
LEFT JOIN table_b AS b ON (b.id = a.table_b_id)
GROUP BY a.table_b_id

Но это, очевидно, только возвращает следующее:

name       number
-----------------
null       1
value 1    2

Как получить следующее, используя только SQL:

name       number
-----------------
null       1
value 1    2
value 2    0

Я использую MySQL. Я предполагаю, что ответ прост.

Редактировать: Нет ли другого способа, кроме запроса на объединение?

Ответы [ 4 ]

1 голос
/ 02 апреля 2009

То, что вы хотите здесь сделать, называется FULL OUTER JOIN.

На данный момент, MySQL не имеет этой функции, это означает, что вам нужно эмулировать с UNION (что не так уж плохо, поскольку вы все еще делаете это в одном запросе).

SELECT  name, SUM(IF(a.id IS NULL, 0, 1))
FROM    table_b b
LEFT JOIN
        table_a a
ON      a.table_b_id = b.id
GROUP BY
        b.id
UNION ALL
SELECT  name, COUNT(*)
FROM    table_a a
LEFT JOIN
        table_b b
ON      b.id = a.table_b_id
WHERE   b.id IS NULL
GROUP BY
        b.id
1 голос
/ 02 апреля 2009
SELECT b.name, SUM(case when a.id is null then 0 else 1 end) AS number
FROM table_a AS a
RIGHT JOIN table_b AS b ON (b.id = a.table_b_id)
GROUP BY b.id

Если вы хотите получить нулевую строку, это особенное. Я верю, что вам придется СОЮЗИТЬ это.

SELECT b.name, SUM(case when a.id is null then 0 else 1 end) AS number 
FROM table_a AS a
RIGHT JOIN table_b AS b ON (b.id = a.table_b_id)
GROUP BY b.id
UNION
select null, count(1) from table_a where table_b_id not in ( select id from table_b )
0 голосов
/ 05 апреля 2009

Существует долгое обсуждение различных способов решения этой проблемы на в этом посте Xaprb . Он показывает, как избежать использования UNION, хотя альтернативному механизму требуется дополнительная таблица «мьютекса», и он предлагает, что это медленнее, чем использование UNION.

0 голосов
/ 02 апреля 2009

Этот вложенный запрос будет работать с MySQL 5

SELECT b.name, (SELECT COUNT( * ) FROM table_a AS a WHERE a.table_b_id = b.id) AS amount FROM table_b AS b
...