Double GroupBy с подсчетом и датами возвращает неправильные даты - PullRequest
2 голосов
/ 25 мая 2019

У меня есть три таблицы для отслеживания писем и их назначенных категорий: Email хранит содержимое письма, Category перечисляет категории и Classification связывает Email идентификатор записи с Category идентификатором записи. Схема с примерами данных и запросом доступна в SQLFiddle: http://sqlfiddle.com/#!9/a410a6/26/0

CREATE TABLE `Category` (
  `id` int(6) unsigned NOT NULL,
  `name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

CREATE TABLE `Mail` (
  `id` int(6) unsigned NOT NULL,
  `content` varchar(500) NOT NULL,
  `date` datetime NOT NULL,  
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;


CREATE TABLE `Classification` (
  `id` int(6) unsigned NOT NULL,
  `mail_id` int(6) unsigned NOT NULL,
  `category_id` int(6) unsigned NOT NULL,
  FOREIGN KEY (mail_id) REFERENCES Mail(id),
  FOREIGN KEY (category_id) REFERENCES Category(id),
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

INSERT INTO `Category` (`id`, `name`) VALUES
  ('1', 'Important'),
  ('2', 'Urgent'),
  ('3', 'Normal');

INSERT INTO `Mail` (`id`, `content`, `date`) VALUES
  ('1', 'Important Email', '2019-01-04T13:53:52'),
  ('2', 'Urgent Email', '2019-01-19T13:53:52'),
  ('3', 'Very Urgent Email', '2019-01-24T13:53:52'),
  ('4', 'Quite Urgent Email', '2019-01-24T13:53:52'),
  ('5', 'Normal Email', '2019-01-21T13:53:52'),
  ('6', 'Regular Email', '2019-01-14T13:53:52'),
  ('7', 'Regular Email', '2019-01-23T13:53:52'),
  ('8', 'Regular Email', '2019-01-23T13:53:52'),
  ('9', 'Regular Email', '2019-01-20T13:53:52'),
  ('10', 'Very Urgent Email', '2019-01-25T13:53:52'),
  ('11', 'Very Urgent Email', '2019-01-25T13:53:52');


INSERT INTO `Classification` (`id`, `mail_id`, `category_id`) VALUES
  ('1', '1', '1'),
  ('2', '2', '2'),
  ('3', '3', '2'),
  ('4', '4', '2'),
  ('5', '5', '3'),
  ('6', '6', '3'),
  ('7', '6', '3'),
  ('8', '6', '3'),
  ('9', '6', '3'),
  ('10', '6', '2'),
  ('11', '6', '2');

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

+----------------------+-----------+----------+
|         date         |   name    | count(*) |
+----------------------+-----------+----------+
| 2019-01-04T13:53:52Z | Important |        1 |
| 2019-01-14T13:53:52Z | Normal    |        1 |
| 2019-01-19T13:53:52Z | Urgent    |        1 |
| 2019-01-20T13:53:52Z | Normal    |        1 |
| 2019-01-21T13:53:52Z | Normal    |        1 |
| 2019-01-23T13:53:52Z | Normal    |        2 |
| 2019-01-24T13:53:52Z | Urgent    |        1 |
| 2019-01-25T13:53:52Z | Urgent    |        2 |
+----------------------+-----------+----------+

Для этого я запускаю следующий запрос с двойной групповой фильтрацией, фильтруя таблицу Classification:

SELECT Mail.date, Category.name, count(*) FROM Mail, Classification, Category WHERE Category.id = Classification.category_id AND Classification.mail_id = Mail.id GROUP BY Mail.date, Category.name 

Что дает мне следующие результаты:

+----------------------+-----------+----------+
|         date         |   name    | count(*) |
+----------------------+-----------+----------+
| 2019-01-04T13:53:52Z | Important |        1 |
| 2019-01-14T13:53:52Z | Normal    |        4 |
| 2019-01-14T13:53:52Z | Urgent    |        2 |
| 2019-01-19T13:53:52Z | Urgent    |        1 |
| 2019-01-21T13:53:52Z | Normal    |        1 |
| 2019-01-24T13:53:52Z | Urgent    |        2 |
+----------------------+-----------+----------+

Что совершенно неправильно.

Я пытался подставить оператор WHERE для JOIN:

SELECT Mail.date, Category.name, count(*) FROM (Mail, Category) RIGHT JOIN Classification ON Category.id = Classification.category_id AND Classification.mail_id = Mail.id GROUP BY Mail.date, Category.name `

Но я получаю те же результаты, что и выше.

Почему эти запросы возвращают эти ошибочные результаты и что я должен сделать, чтобы их исправить?

1 Ответ

1 голос
/ 25 мая 2019

Во-первых, ваш запрос должен выглядеть следующим образом:

SELECT m.date, c.name, count(*)
FROM Mail m JOIN
     Classification cl
     ON cl.mail_id = m.id JOIN
     Category c
     ON c.id = cl.category_id 
GROUP BY m.date, c.name ;

Теперь, когда мы ушли от этого, ваша проблема в том, что электронные письма имеют несколько категорий. Таким образом, они многократно подсчитаны. Следовательно, результаты, которые вы получаете, верны.

У вас есть точные дубликаты в таблице классификации, поэтому простое решение:

SELECT m.date, c.name, count(distinct m.id)
FROM Mail m JOIN
     Classification cl
     ON cl.mail_id = m.id JOIN
     Category c
     ON c.id = cl.category_id 
GROUP BY m.date, c.name ;

При этом настоящее решение - исправить ваши данные, чтобы в них не было дубликатов.

Здесь - это скрипта SQL, использующая ваши данные. У вас есть "2" для писем на 2019-01-23. Однако на эту дату нет секретных писем, поэтому они не находятся в результатах.

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