计算具有在给定日期首先出现的相关记录的记录

时间:2016-02-16 08:51:31

标签: mysql datetime join

我有两个表playersgames,创建如下:

CREATE TABLE IF NOT EXISTS `players` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `created_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

CREATE TABLE IF NOT EXISTS `games` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `player` int(11) NOT NULL,
  `played_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

我希望每天提取3个值:

  1. 当天创建的玩家数量
  2. 当天玩的球员数量
  3. 当天第一次参加比赛的球员数量
  4. 因此,假设玩家表格如下所示:

    +----+--------+---------------------+
    | id | name   | created_at          |
    +----+--------+---------------------+
    |  1 | Alan   | 2016-02-01 00:00:00 |
    |  2 | Benny  | 2016-02-01 06:00:00 |
    |  3 | Calvin | 2016-02-02 00:00:00 |
    |  4 | Dan    | 2016-02-03 00:00:00 |
    +----+--------+---------------------+
    

    游戏表如下:

    +----+--------+---------------------+
    | id | player | played_at           |
    +----+--------+---------------------+
    |  1 |      1 | 2016-02-01 01:00:00 |
    |  2 |      3 | 2016-02-02 02:00:00 |
    |  3 |      2 | 2016-02-03 14:00:00 |
    |  4 |      3 | 2016-02-03 17:00:00 |
    |  5 |      3 | 2016-02-03 18:00:00 |
    +----+--------+---------------------+
    

    然后查询应返回类似

    的内容
    +------------+-----+--------+-------+
    | day        | new | played | first |
    +------------+-----+--------+-------+
    | 2016-02-01 | 2   | 1      | 1     |
    | 2016-02-02 | 1   | 1      | 1     |
    | 2016-02-03 | 1   | 2      | 1     |
    +------------+-----+--------+-------+
    

    我有一个解决方案1( new ):

    SELECT Date(created_at) AS day,
           Count(*)         AS new
    FROM   players
    GROUP  BY day;  
    

    这很容易。我想我也有2个解决方案(播放),感谢MySQL COUNT DISTINCT

    select Date(played_at) AS day,
           Count(Distinct player) AS played
    FROM   games
    GROUP  BY day;
    

    但我不知道如何获得3( first )所需的结果。我也不知道如何将所有内容放在一个查询中,以节省执行时间(games表可能包含数百万条记录)。

    如果您需要,这是一个插入示例数据的查询:

    INSERT INTO `players` (`id`, `name`, `created_at`) VALUES
    (1, 'Alan', '2016-02-01 00:00:00'),
    (2, 'Benny', '2016-02-01 06:00:00'),
    (3, 'Calvin', '2016-02-02 00:00:00'),
    (4, 'Dan', '2016-02-03 00:00:00');
    
    INSERT INTO `games` (`id`, `player`, `played_at`) VALUES
    (1, 1, '2016-02-01 01:00:00'),
    (2, 3, '2016-02-02 02:00:00'),
    (3, 2, '2016-02-03 14:00:00'),
    (4, 3, '2016-02-03 17:00:00'),
    (5, 3, '2016-02-03 18:00:00');
    

4 个答案:

答案 0 :(得分:4)

一个版本是将所有相关数据导入联合并从那里进行分析;

SELECT SUM(type='P') new, 
       COUNT(DISTINCT CASE WHEN type='G' THEN pid END) played, 
       SUM(type='F') first 
FROM (
  SELECT id pid, DATE(created_at) date, 'P' type FROM players 
  UNION ALL 
  SELECT player, DATE(played_at) date,  'G' FROM games 
  UNION ALL 
  SELECT player, MIN(DATE(played_at)),  'F' FROM games GROUP BY player
) z 
GROUP BY date;

在工会中;

类型为P的记录是玩家创建统计信息 类型为G的记录是与玩家相关的游戏统计数据 类型为F的记录是玩家第一次玩游戏时的统计数据。

答案 1 :(得分:2)

您可以根据min(playing_at)计算临时表的结果,并通过

进行过滤
select count(player) from 
   (  select player, min(played_at)  
      from games 
      group by player 
      having min(played_at) = YOUR_GIVEN_DATE ) as t;

答案 2 :(得分:1)

此查询将为您提供结果:

select day,( select count(distinct(id)) from players where Date(created_at) = temp.day ) as no_created_at ,
( select count(distinct(player)) from games where Date(played_at) = temp.day) as no_played_at,
( select count(distinct(player)) from games  where Date(played_at) = 
(select min(Date(played_at)) from games internal_games 
where internal_games.player =games.player and Date(games.played_at) = temp.day )) as no_first_played_at
 from (
SELECT Date(created_at) AS day     
FROM   players
GROUP  BY day 
union 
select Date(played_at) AS day
FROM   games
GROUP  BY day) temp 

和输出:

enter image description here

答案 3 :(得分:1)

这是一个带有一堆子查询的解决方案,它解释了玩家可能在没有游戏的情况下创建的可能性,反之亦然:

select
    all_dates.date as day,
    ifnull(new.num, 0) as new,
    ifnull(players.num, 0) as players,
    ifnull(first.num, 0) as first
from (
    select date(created_at) as date from players
    union
    select date(played_at) from games
) as all_dates
left join (
    select date(created_at) as created_at_date, count(*) as num
    from players
    group by created_at_date
) as new on all_dates.date = new.created_at_date
left join (
    select date(played_at) as played_at_date, count(distinct player) as num
    from games
    group by played_at_date
) as players on all_dates.date = players.played_at_date
left join (
    select min_date, count(*) num
    from (
        select player, date(min(played_at)) as min_date
        from games
        group by player
    ) as players_first
    group by min_date
) as first on all_dates.date = first.min_date
order by day;