SQL组按相对日期范围

时间:2014-12-26 18:22:13

标签: mysql sql

我有一个user表,其中包含标准列idregistered_date

对于一年中的每个星期(例如DATE_FORMAT'%x-%v'),我想要计算在该周的过去4周内注册的用户数(包括该周本身)。

例如,对于2014-50这一周,我想要在第50周以及第49,48和47周注册的用户数量。

通常,为了获得每周注册的用户数,我会使用:

SELECT DATE_FORMAT(registered_date, '%x-%v'), count(*)
FROM user
GROUP BY DATE_FORMAT(registered_date, '%x-%v')

但是,当然,这并不包括在过去3周内注册的用户。

知道如何相应地修改查询吗?

1 个答案:

答案 0 :(得分:2)

我们必须创建一个结构化查询才能获得这些东西。

首先,我们需要一个子查询,它将生成用户注册的每周开始日期的列表。我们需要星期一的日期,因为您使用%x-%v来获取周数。

要获取紧接任何DATETIME值之前的星期一的日期,此表达式会执行此操作。

DATE(registered_date) - INTERVAL WEEKDAY(registered_date) DAY

所以这个小子查询让我们得到星期一的列表。

     SELECT DISTINCT DATE(registered_date) - 
                INTERVAL WEEKDAY(registered_date) DAY as monday
       FROM user

接下来,我们需要将此嵌套在另一个查询中,以便为我们希望总结的每个(重叠)四周时段获取一行。每行中都有三列:句点的第一个日期,句点的最后一个日期和期间的标识符,例如' 2013-52'。

    SELECT monday - INTERVAL 3 WEEK AS start,
           monday + INTERVAL 1 WEEK AS finish,
           DATE_FORMAT(monday, '%x-%v') AS week
      FROM (
            SELECT DISTINCT DATE(registered_date) - 
                       INTERVAL WEEKDAY(registered_date) DAY as monday
              FROM user
           ) AS wks

冷却。现在我们有一个表,我们可以与user表一起提取哪些用户在哪个时期注册。我们可以这样做

 SELECT user.id, periods.week
   FROM user
   JOIN (  /* the subquery */
        ) AS periods ON user.registered_date >= periods.start
                    AND user.registered_date <  periods.finish

但是我们不想要那个细节,而是我们想要计数,所以我们将其重写为聚合查询。

 SELECT periods.week, COUNT(*) 
   FROM user
   JOIN (  /* the subquery */
        ) AS periods ON user.registered_date >= periods.start
                    AND user.registered_date <  periods.finish
  GROUP BY periods.week
  ORDER BY periods.week

将所有内容放在一起,这是查询。

 SELECT periods.week, COUNT(*) 
   FROM user
   JOIN ( 
          SELECT monday - INTERVAL 3 WEEK AS start,
                 monday + INTERVAL 1 WEEK AS finish,
                 DATE_FORMAT(monday, '%x-%v') AS week
            FROM (
                    SELECT DISTINCT DATE(registered_date) - 
                               INTERVAL WEEKDAY(registered_date) DAY as monday
                      FROM user
                 ) AS wks
        ) AS periods ON user.registered_date >= periods.start
                    AND user.registered_date <  periods.finish
  GROUP BY periods.week
  ORDER BY periods.week

这看起来像毛球,但请注意我们已经把它建成了三明治,相当简单的部分。

将用户分配到适当的四周时段的技巧嵌入在ON的连接条件中。

                     ON user.registered_date >= periods.start
                    AND user.registered_date <  periods.finish

由于开始日期和结束日期重叠,因此每个用户都被分配到多个四周的时间段。

这里的另一个技巧是使用实际日期而不是周ids&#39; 2014-45&#39;对于计算,因为它不可能,特别是在年末,从周ID转换回日期,我们希望使用像date - INTERVAL 3 WEEK这样的计算来计算开始和结束日期。