mysql - SQL 按相对日期范围分组

标签 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 周内注册的用户。

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

最佳答案

我们将不得不创建一个结构化查询来获取这些内容。

首先,我们需要一个子查询来生成用户注册每周开始日期的列表。我们需要星期一的日期,因为您正在使用 %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

接下来,我们需要将其嵌套在另一个查询中,以便为每个(重叠的)四个星期的时间段获取一行,我们希望对其进行汇总。每行将包含三列:期间的第一个日期、期间的最后+1 日期和期间的标识符,例如“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

这看起来像一个毛球,但请注意,我们把它做成了一个三明治,由相当简单的部分组成。

将用户分配到适当的 4 周时间段的技巧已嵌入此加入的 ON 条件中。

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

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

这里的另一个技巧是使用实际日期而不是周 ID“2014-45”进行计算,因为不可能将周 ID 转换回日期,尤其是在年末,我们希望使用计算像 date - INTERVAL 3 WEEK 来计算开始和结束日期。

关于mysql - SQL 按相对日期范围分组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27660525/

相关文章:

sql - PostgreSQL 错误 : function expression in FROM cannot refer to other relations of same query level

mysql - 使用多个唯一标识符进行不同 SQL 计数

php - 不同类型用户的单一登录页面

mysql - sql查询以从5个表中获取数据

php - 每次刷新 jquery 移动页面时,行都会插入数据库

sql - 有没有办法重写这个语句而不需要子查询?

mysql - 如何将 MySQL 和 SQLite 连接到 Play Framework 2.2?

php - Mysql_query 不会发送到数据库

sql - Oracle - 如何在分层查询中使用连接并避免笛卡尔积

php - 将逗号放在 PHP 数字变量中