SQL - результаты из 3 таблиц, агрегатные функции

Я пытаюсь создать SQL-запрос для следующих результатов:

id, name,   Users in group, Messages in groups, Message type 1 in groups, Message type 2 in groups
1   Group1  4               3                   2                         1
2   Group2  3               2                   1                         1
3   Group1  1               1                   1                         0

Моя база данных:

create database user_test;

use user_test;


CREATE TABLE `user` (
  `id`             BIGINT      NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) NOT NULL,
  `group_id`       BIGINT      NOT NULL,
  PRIMARY KEY (`id`)
);


CREATE TABLE `user_group` (
  `id`      BIGINT           NOT NULL AUTO_INCREMENT,
  `name`    VARCHAR(100)
            CHARACTER SET utf8
            COLLATE utf8_bin NOT NULL,
  UNIQUE KEY `unique` (`name`) USING BTREE,
  PRIMARY KEY (`id`)
);


CREATE TABLE `message` (
  `id`               BIGINT   NOT NULL AUTO_INCREMENT,
  `user_id`           BIGINT   NOT NULL,
  `type`             INT      NOT NULL,
  `date`             DATETIME NOT NULL,
  UNIQUE KEY `unique` (`user_id`, `type`, `date`) USING BTREE,
  PRIMARY KEY (`id`)
);


ALTER TABLE user
    ADD CONSTRAINT user_group_c_fk FOREIGN KEY (group_id) REFERENCES user_group(id);


ALTER TABLE message
    ADD CONSTRAINT user_a_fk FOREIGN KEY (user_id) REFERENCES user(id);

И начальное значение:

INSERT INTO `user_group`
VALUES (1, "Group 1"),
       (2, "Group 2"),
       (3, "Group 3");

INSERT INTO `user`
VALUES (1, "User 1", 1),
       (2, "User 2", 1),
       (3, "User 3", 1),
       (4, "User 4", 1),
       (5, "User 5", 2),
       (6, "User 6", 2),
       (7, "User 7", 2),
       (8, "User 8", 3);

INSERT INTO `message`
VALUES (1, 1, 1, "2019-07-25 00:00:00"),
       (2, 1, 2, "2019-07-25 00:00:01"),
       (3, 1, 2, "2019-07-25 00:00:02"),
       (4, 5, 1, "2019-07-25 00:00:03"),
       (5, 6, 2, "2019-07-25 00:00:04"),
       (6, 8, 1, "2019-07-25 00:00:05");

Мне интересно, возможны ли результаты. Я пытаюсь с чем-то вроде этого:

use user_test;

select user_group.id, 
       user_group.name,
       count(user.id) As "Users in group",
       count(message.id) AS "Messages in groups",
       count(message.type = 1) AS "Message type1 in groups",
       count(message.type = 2) AS "Message type2 in groups"
FROM user_group
LEFT JOIN user ON user.group_id = user_group.id
LEFT JOIN message ON message.user_id = user.id
WHERE user_group.id = message.user_id
GROUP BY user_group.id;

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

Этот пункт WHERE выглядит очень странно. У вас уже есть такое же условие в предложении ON, но WHERE заставит LEFT JOIN возвращать обычный результат INNER JOIN.

jarlh 25.07.2019 10:31

О, правда. Я не могу удалить это предложение WHERE... Но проблема все та же.

profiler 25.07.2019 10:33

Серьезно рассмотрите возможность решения проблем с отображением данных в коде приложения, если таковые имеются.

Strawberry 25.07.2019 10:36

Но первый шаг — проверить, могу ли я сгенерировать этот результат в базе данных.

profiler 25.07.2019 10:38

@RaymondNijland, возможно, немного неясно, но обратите внимание, что я сказал предложение в WHERE, а не предложение а WHERE.

jarlh 25.07.2019 10:42

да, я заметил это после того, как написал свой комментарий и удалил свой комментарий до того, как вы разместили свой комментарий @jarlh

Raymond Nijland 25.07.2019 10:43

Я думаю, в таблице сообщений есть только 2 типа: один и два. Тогда 4-й столбец в результате будет суммой 5-го и 6-го. Я прав?

Mohammedshafeek C S 25.07.2019 11:01

Сообщения в группах = сообщение тип1 + сообщение тип2, да, там всего два типа сообщения.

profiler 25.07.2019 11:04
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
2
8
141
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

use user_test;

 select user_group.id, 
       user_group.name,
        count(user.id) As "Users in group",
        count(message.id) AS "Messages in groups",
        count(CASE message.type WHEN 1 THEN 1 ELSE null end) AS "Message type1 in groups",
        count(CASE message.type WHEN 2 THEN 1 ELSE null end) AS "Message type2 in groups" FROM user_group LEFT JOIN user ON
 user.group_id = user_group.id LEFT JOIN message ON message.user_id =
 user.id WHERE user_group.id = user.group_id GROUP BY user_group.id,
 message.type

Надеюсь это поможет. Я делаю Inner Join вместо левого соединения. Вы можете измениться, если это необходимо.

select 
ug.id, 
ug.name,
count(u.id) as users_in_group,
count(m.id) as messages_in_group,
count(decode(m.type,'1',count(m.id))) as message_type1_in_group, 
count(decode(m.type,'2',count(m.id))) as message_type1_in_group
from user_group ug
join user u on ug.id=u.group_id
join message m on m.user_id = u.user_id
group by ug.id,ug.name ug.id,ug.name;
Ответ принят как подходящий

Вы можете сделать это быстро и грязно, используя count(distinct):

SELECT ug.id, ug.name,
       count(distinct u.id) As "Users in group",
       count(distinct m.id) AS "Messages in groups",
       count(distinct case when m.type = 1 then m.id end) AS "Message type1 in groups",
       count(distinct case when m.type = 2 then m.id end) AS "Message type2 in groups"
FROM user_group ug  LEFT JOIN
     user u
     ON u.group_id = ug.id LEFT JOIN
     message m
     ON m.user_id = u.id
WHERE ug.id = m.user_id
GROUP BY ug.id;

Вы также можете получить точные подсчеты, агрегируя до, выполняя соединения. Также обратите внимание, что я добавил псевдонимы таблиц, чтобы запрос было легче писать и читать.

Немного отредактируйте ответ Гордона Линоффа.

SELECT a.id, a.name,
       COUNT(DISTINCT b.id) AS "Users in group" ,
       COUNT(c.id) AS "Messages in groups",
       COUNT(DISTINCT CASE WHEN c.type = 1 THEN c.id END) AS "Message type 1 in groups",
       COUNT(DISTINCT CASE WHEN c.type = 2 THEN c.id END) AS "Message type 2 in groups"
FROM user_group a LEFT JOIN
     USER b
     ON b.group_id = a.id LEFT JOIN
     message c
     ON c.user_id = b.id GROUP BY a.id;

Здесь совокупные результаты извлекаются из сгруппированной таблицы.

Спасибо за отличное дополнение. информация о запросе!

profiler 25.07.2019 14:37

Другие вопросы по теме