MySQL分组显示零记录

时间:2012-05-07 11:37:55

标签: mysql sql group-by

我有一个Intranet网站,只有注册用户才能观看视频。用户可以在iPhone,iPad,Android或Web等多个平台上观看视频,并跟踪所有这些信息(为了简化问题,请不要考虑其他平台)。我想在管理员面板上显示一些视频观看报告。

表结构如下(我有很多其他列,但它们对于这个问题并不重要);

CREATE TABLE IF NOT EXISTS `history` (
  `history_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `video_id` int(11) unsigned NOT NULL,
  `user_id` int(11) unsigned NOT NULL,
  `platform_id` int(11) unsigned NOT NULL,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`history_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=9 ;

INSERT INTO `history` (`history_id`, `video_id`, `user_id`, `platform_id`, `created`) VALUES
(1, 1, 1, 1, '2012-05-06 08:13:57'),
(2, 2, 1, 1, '2012-05-06 13:23:57'),
(3, 1, 1, 4, '2012-05-06 18:16:39'),
(4, 4, 2, 3, '2012-05-07 08:14:19'),
(5, 1, 2, 3, '2012-05-07 08:14:55'),
(6, 2, 1, 1, '2012-05-07 15:14:55'),
(7, 3, 2, 1, '2012-05-07 18:05:14'),
(8, 3, 1, 4, '2012-05-07 18:15:24');

CREATE TABLE IF NOT EXISTS `sys_list_of_values` (
  `value_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `display_text` text COLLATE utf8_unicode_ci NOT NULL,
  `list_value` text COLLATE utf8_unicode_ci NOT NULL,
  `list_group` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`value_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=10 ;

INSERT INTO `sys_list_of_values` (`value_id`, `display_text`, `list_value`, `list_group`) VALUES
(1, 'iPhone', 'iphone', 'platform'),
(2, 'Android', 'android', 'platform'),
(3, 'iPad', 'ipad', 'platform'),
(4, 'Website', 'web', 'platform'),
(5, 'Active', 'active', 'status'),
(6, 'Passive', 'passive', 'status'),
(7, 'Waiting for approvement', 'waiting', 'status'),
(8, 'Spam', 'spam', 'status'),
(9, 'Deleted', 'deleted', 'status');

CREATE TABLE IF NOT EXISTS `users` (
  `user_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(80) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=3 ;

INSERT INTO `users` (`user_id`, `username`) VALUES
(1, 'test_user_1'),
(2, 'test_user_2');

CREATE TABLE IF NOT EXISTS `videos` (
  `video_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) unsigned NOT NULL,
  `title` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `status` int(11) unsigned NOT NULL,
  PRIMARY KEY (`video_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=5 ;

INSERT INTO `videos` (`video_id`, `user_id`, `title`, `created`, `status`) VALUES
(1, 1, 'Test Video 1', '2012-05-07 08:12:52', 7),
(2, 1, 'Test Video 2', '2012-05-07 08:12:52', 5),
(3, 1, 'Test Video 3', '2012-05-07 08:13:17', 5),
(4, 1, 'Test Video 4', '2012-05-07 08:13:17', 6);

我正在尝试显示如下的报告;

Platform      |     Watch_Count
===============================
iPhone                  20
iPad                     0
Android                  2
Website                120
Total                  142

我有一些过滤器选项,例如video_idcreatedplatform等。例如;我想显示所有视频,特定视频(video_id)或日期(已创建)等的总观看报告。我还想显示所有平台,无论它们是0还是其他任何平台。

以下查询仅显示iPhone和网站,但我想显示所有平台。如果没有手表计数,则必须显示为0(零)。我也无法显示汇总所有watch_count的最后一行并显示总数。

SELECT sys_list_of_values.display_text, COUNT(history.history_id) AS 'total_watch' FROM history
JOIN sys_list_of_values ON sys_list_of_values.value_id = history.platform_id
WHERE history.video_id = 1
AND history.created <  '2012-05-07'
GROUP BY history.platform_id

4 个答案:

答案 0 :(得分:3)

保留FROM HISTORY,将JOIN更改为RIGHT JOIN

SELECT sys_list_of_values.display_text, COUNT(history.history_id) AS 'total_watch' 
FROM history
RIGHT JOIN sys_list_of_values 
   ON sys_list_of_values.value_id = history.platform_id
   AND history.created <  '2012-05-07'
   AND history.video_id = 1

GROUP BY sys_list_of_values.value_id 
order by sys_list_of_values.display_text;

如果您想使用LEFT JOIN,请切换历史记录和sys_list_of_values:

SELECT sys_list_of_values.display_text, COUNT(history.history_id) AS 'total_watch' 
FROM sys_list_of_values 
LEFT JOIN history
   ON sys_list_of_values.value_id = history.platform_id
   AND history.created <  '2012-05-07'
   AND history.video_id = 1

GROUP BY sys_list_of_values.value_id 
order by sys_list_of_values.display_text;

实时测试:http://www.sqlfiddle.com/#!2/495d1/15


GROUP BY与ROLLUP与ORDER不兼容,无论如何ROLLUP会自动对列表进行排序,只需删除ORDER:

SELECT sys_list_of_values.display_text, 
  -- sys_list_of_values.value_id,
  COUNT(history.platform_id) AS 'total_watch' 
FROM sys_list_of_values 
LEFT JOIN history
   ON sys_list_of_values.value_id = history.platform_id
   AND history.created <  '2012-05-07'
   AND history.video_id = 1

where sys_list_of_values.list_group = 'platform'
GROUP BY 
 sys_list_of_values.display_text with rollup

实时测试:http://www.sqlfiddle.com/#!2/495d1/68

答案 1 :(得分:0)

使用LEFT JOIN并将涉及“正确”表格的条件从WHERE移至ON

SELECT s.display_text
     , COUNT(h.history_id) AS total_watch 
FROM 
    sys_list_of_values AS s 
  LEFT JOIN 
    history AS h
      ON  h.platform_id = s.value_id 
      AND h.video_id = 1
      AND h.created <  '2012-05-07'
GROUP BY s.value_id
  WITH ROLLUP                     --- for an additional final row with total sum

如果您需要ORDER BY display_textROLLUP,请使用此选项:

GROUP BY s.display_text
  WITH ROLLUP             

答案 2 :(得分:0)

您可以使用子查询将计数加入平台列表:

SELECT display_text, IFNULL(total_watch, 0)
FROM sys_list_of_values
LEFT JOIN (
    SELECT platform_id AS value_id, COUNT(history_id) AS total_watch
    FROM history
    WHERE video_id = 1
    AND history.created < '2012-05-07'
    GROUP BY platform_id
) AS watch_counts USING (value_id)
WHERE list_group = 'platform'

顺便说一下,在应用程序代码中完成平台列表而不是在数据库中执行它通常更有效率,特别是如果假设应用程序代码具有所有现有平台的长期缓存。 / p>

答案 3 :(得分:0)

SELECT
  sys_list_of_values.value_id     as VID,
  sys_list_of_values.display_text,
  COUNT(history.history_id)       AS 'total_watch'
FROM sys_list_of_values
   JOIN history
    ON sys_list_of_values.value_id = history.platform_id
where sys_list_of_values.list_group = 'platform'
    and history.created < '2012-05-07'
GROUP BY history.platform_id
order by VID