Отображать сводку отзывов пользователей сегодня, вчера и других условий в одном запросе ОПТИМИЗИРОВАННЫЙ - PullRequest
1 голос
/ 23 апреля 2020

Я видел много вопросов, связанных с подсчетом данных с разными диапазонами дат, но в отдельных запросах. Мне необходимо отобразить, сколько раз каждый пользователь заходил сегодня, вчера и сколько раз пользователь запрашивал «Раздел файлов». Поэтому мне нужно отобразить много столбцов, каждый с разным количеством для каждого пользователя в разных диапазонах дат или с указанием c деталей.

Вот сценарии для создания и вставки исходных данных:

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(45) DEFAULT NULL,
  `realname` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username_UNIQUE` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE `history` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(45) DEFAULT NULL,
  `access_date` datetime DEFAULT NULL,
  `detail` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

INSERT INTO users (username,realname) VALUES
("mwazowski","Mike Wazowski"),("jsullivan","James P. Sullivan"),
("rboggs","Randall Boggs"),("hwaternoose","Henry J. Waternoose");

INSERT INTO history (username,access_date,detail) VALUES
("","2020-04-22 12:00:00","publicData"),
("mwazowski","2020-04-22 12:01:00","login"),
("mwazowski","2020-04-22 12:02:00","practice"),
("mwazowski","2020-04-22 12:04:00","files"),
("mwazowski","2020-04-22 12:10:00","logout"),
("","2020-04-23 12:25:00","publicData"),
("","2020-04-23 12:27:00","publicData"),
("jsullivan","2020-04-23 12:30:00","login"),
("jsullivan","2020-04-23 12:35:00","files"),
("jsullivan","2020-04-23 12:40:00","logout"),
("","2020-04-23 12:52:00","publicData"),
("rboggs","2020-04-23 13:00:00","login"),
("rboggs","2020-04-23 13:01:00","files"),
("rboggs","2020-04-23 13:40:00","logout"),
("","2020-04-23 13:43:00","publicData");

Это набор результатов для таблицы Пользователи :

+----+-------------+---------------------+
| id |  username   |      realname       |
+----+-------------+---------------------+
|  1 | mwazowski   | Mike Wazowski       |
|  2 | jsullivan   | James P. Sullivan   |
|  3 | rboggs      | Randall Boggs       |
|  4 | hwaternoose | Henry J. Waternoose |
|    |             |                     |
+----+-------------+---------------------+

И это набор результатов для таблицы История :

+----+-----------+---------------------+------------+
| id | username  |     access_date     |   detail   |
+----+-----------+---------------------+------------+
|  1 |           | 2020-04-22 12:00:00 | publicData |
|  2 | mwazowski | 2020-04-22 12:01:00 | login      |
|  3 | mwazowski | 2020-04-22 12:02:00 | practice   |
|  4 | mwazowski | 2020-04-22 12:04:00 | files      |
|  5 | mwazowski | 2020-04-22 12:10:00 | logout     |
|  6 |           | 2020-04-23 12:25:00 | publicData |
|  7 |           | 2020-04-23 12:27:00 | publicData |
|  8 | jsullivan | 2020-04-23 12:30:00 | login      |
|  9 | jsullivan | 2020-04-23 12:35:00 | files      |
| 10 | jsullivan | 2020-04-23 12:40:00 | logout     |
| 11 |           | 2020-04-23 12:52:00 | publicData |
| 12 | rboggs    | 2020-04-23 13:00:00 | login      |
| 13 | rboggs    | 2020-04-23 13:01:00 | files      |
| 14 | rboggs    | 2020-04-23 13:40:00 | logout     |
| 15 |           | 2020-04-23 13:43:00 | publicData |
+----+-----------+---------------------+------------+

Вот запрос, который я разработал, который отображает подсчеты по группам записей доступа по пользователям, диапазонам дат на сегодня и вчера и задает c подробные данные (записи добавляются с полем detail = 'files'):

select coalesce(u.realname,"PUBLIC") username,
       coalesce(h2.todayCount,0) nToday,
       coalesce(h3.yesterdayCount,0) nYesterday,
       coalesce(h4.fileCount,0) nFiles
from (select distinct username from history) h1
left join users u on h1.username=u.username
left join (select username,count(1) todayCount from history
    where access_date>=current_date() group by username) h2
    on h1.username=h2.username
left join (select username,count(1) yesterdayCount from history
    where access_date between (current_date()-1) and current_date() group by username) h3
    on h1.username=h3.username
left join (select username,count(1) fileCount from history
    where detail="files" group by username) h4
    on h1.username=h4.username order by h1.username;

Этот запрос работает и отображает данные по мере необходимости, но он становится медленнее, поскольку существует больше данных, а также мне может понадобиться добавить больше столбцов позже, что потребует дополнительного чтения всей истории. Таблица истории читается несколько раз, и если потребуется больше диапазонов дат, она станет хуже. Это результат запроса с начальными данными:

+-------------------+--------+------------+--------+
|     username      | nToday | nYesterday | nFiles |
+-------------------+--------+------------+--------+
| PUBLIC            |      4 |          1 |      0 |
| James P. Sullivan |      3 |          0 |      1 |
| Mike Wazowski     |      0 |          4 |      1 |
| Randall Boggs     |      3 |          0 |      1 |
+-------------------+--------+------------+--------+

Даты, включенные в пример данных, дают ожидаемый результат только тогда, когда сегодня 23 апреля. Чтобы эти тестовые данные работали в любой будущий день, запустите следующие сценарии:

UPDATE history SET access_date=concat((curdate() - INTERVAL 1 DAY)," ",time(access_date)) WHERE id<=5;
UPDATE history SET access_date=concat(curdate()," ",time(access_date)) WHERE id>5;

1 Ответ

0 голосов
/ 24 апреля 2020

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

DROP TABLE IF EXISTS users;
CREATE TABLE `users` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(45) DEFAULT NULL,
  `realname` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`user_id`),
  UNIQUE KEY `username_UNIQUE` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS history;
CREATE TABLE `history` (
  `history_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` INT NULL,
  `access_date` datetime DEFAULT NULL,
  `detail` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`history_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

INSERT INTO users (username,realname) VALUES
("mwazowski","Mike Wazowski"),("jsullivan","James P. Sullivan"),
("rboggs","Randall Boggs"),("hwaternoose","Henry J. Waternoose");

INSERT INTO history (user_id,access_date,detail) VALUES
(NULL,"2020-04-22 12:00:00","publicData"),
(1,"2020-04-22 12:01:00","login"),
(1,"2020-04-22 12:02:00","practice"),
(1,"2020-04-22 12:04:00","files"),
(1,"2020-04-22 12:10:00","logout"),
(NULL,"2020-04-23 12:25:00","publicData"),
(NULL,"2020-04-23 12:27:00","publicData"),
(2,"2020-04-23 12:30:00","login"),
(2,"2020-04-23 12:35:00","files"),
(2,"2020-04-23 12:40:00","logout"),
(NULL,"2020-04-23 12:52:00","publicData"),
(3,"2020-04-23 13:00:00","login"),
(3,"2020-04-23 13:01:00","files"),
(3,"2020-04-23 13:40:00","logout"),
(NULL,"2020-04-23 13:43:00","publicData");

SELECT u.user_id
     , u.username  
     , u.realname          
     , DATE(h.access_date) access_date
     , COALESCE(COUNT(*),0) total
     , COALESCE(SUM(h.detail = 'files'),0) total_files
  FROM users u
  LEFT
  JOIN history h
    ON h.user_id = u.user_id
   AND h.access_date >= '2020-04-22 00:00:00' 
   AND h.access_date < '2020-04-24 00:00:00'
 GROUP
    BY u.user_id
     , DATE(h.access_date);

+---------+-------------+---------------------+-------------+-------+-------------+
| user_id | username    | realname            | access_date | total | total_files |
+---------+-------------+---------------------+-------------+-------+-------------+
|       1 | mwazowski   | Mike Wazowski       | 2020-04-22  |     4 |           1 |
|       2 | jsullivan   | James P. Sullivan   | 2020-04-23  |     3 |           1 |
|       3 | rboggs      | Randall Boggs       | 2020-04-23  |     3 |           1 |
|       4 | hwaternoose | Henry J. Waternoose | NULL        |     1 |           0 |
+---------+-------------+---------------------+-------------+-------+-------------+   

Обратите внимание, я немного изменил вашу схему; при составном индексе (user_id, access_date) это должно быть быстро.

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