MySQL组和连续几天聚合

时间:2017-04-14 07:43:19

标签: mysql

我的桌子旨在包含宠物及其活动数据,例如睡眠时间和锻炼时间。表定义如下。

CREATE TABLE IF NOT EXISTS TEST_ACTIVITY(
   pet_id INT(11) UNSIGNED NOT NULL,
   simple_sleep_time INT(11),
   deep_sleep_time INT(11),
   mild_movement_time INT(11),
   moderate_movement_time INT(11),
   severe_movement_time INT(11),
   start_time INT(11),
   date DATE,
   PRIMARY KEY (pet_id, start_time, date)
) ENGINE=INNODB

将以下数据插入表中。

INSERT INTO TEST_ACTIVITY (pet_id, simple_sleep_time, deep_sleep_time,mild_movement_time, moderate_movement_time, severe_movement_time, start_time, date) 
VALUES 
(100,  1, 1, 1, 1, 1, 0, '2015-03-10'), 
(100,  2, 1, 1, 1, 1, 30, '2015-03-10'),  
(100,  3, 1, 1, 1, 1, 0, '2015-03-11'), 
(100,  4, 1, 1, 1, 1, 30, '2015-03-11'), 
(100,  5, 1, 1, 1, 1, 0, '2015-03-12'), 
(100,  6, 1, 1, 1, 1, 30, '2015-03-12'), 
(100,  7, 1, 1, 1, 1, 0, '2015-03-13'), 
(100,  8, 1, 1, 1, 1, 30, '2015-03-13'),  
(101,  9, 1, 1, 1, 1, 0, '2015-03-10'), 
(101,  10, 1, 1, 1, 1, 30, '2015-03-10'), 
(101,  11, 1, 1, 1, 1, 0, '2015-03-11'), 
(101,  12, 1, 1, 1, 1, 30, '2015-03-11'),
(101,  13, 1, 1, 1, 1, 0, '2015-03-12'),
(101,  14, 1, 1, 1, 1, 30, '2015-03-12'),
(101,  15, 1, 1, 1, 1, 0, '2015-03-13'),
(101,  16, 1, 1, 1, 1, 30, '2015-03-13'),
(102,  17, 1, 1, 1, 1, 0, '2015-03-10'),
(102,  18, 1, 1, 1, 1, 30, '2015-03-10'),
(102,  19, 1, 1, 1, 1, 0, '2015-03-11'),
(102,  20, 1, 1, 1, 1, 30, '2015-03-11'),
(102,  21, 1, 1, 1, 1, 0, '2015-03-12'),
(102,  22, 1, 1, 1, 1, 30, '2015-03-12'),
(102,  23, 1, 1, 1, 1, 0, '2015-03-13'),
(102,  24, 1, 1, 1, 1, 30, '2015-03-13');

select * from TEST_ACTIVITY ORDER BY pet_id, date, start_time;

+--------+-------------------+-----------------+--------------------+------------------------+----------------------+------------+------------+
| pet_id | simple_sleep_time | deep_sleep_time | mild_movement_time | moderate_movement_time | severe_movement_time | start_time | date       |
+--------+-------------------+-----------------+--------------------+------------------------+----------------------+------------+------------+
|    100 |                 1 |               1 |                  1 |                      1 |                    1 |          0 | 2015-03-10 |
|    100 |                 2 |               1 |                  1 |                      1 |                    1 |         30 | 2015-03-10 |
|    100 |                 3 |               1 |                  1 |                      1 |                    1 |          0 | 2015-03-11 |
|    100 |                 4 |               1 |                  1 |                      1 |                    1 |         30 | 2015-03-11 |
|    100 |                 5 |               1 |                  1 |                      1 |                    1 |          0 | 2015-03-12 |
|    100 |                 6 |               1 |                  1 |                      1 |                    1 |         30 | 2015-03-12 |
|    100 |                 7 |               1 |                  1 |                      1 |                    1 |          0 | 2015-03-13 |
|    100 |                 8 |               1 |                  1 |                      1 |                    1 |         30 | 2015-03-13 |
|    101 |                 9 |               1 |                  1 |                      1 |                    1 |          0 | 2015-03-10 |
|    101 |                10 |               1 |                  1 |                      1 |                    1 |         30 | 2015-03-10 |
|    101 |                11 |               1 |                  1 |                      1 |                    1 |          0 | 2015-03-11 |
|    101 |                12 |               1 |                  1 |                      1 |                    1 |         30 | 2015-03-11 |
|    101 |                13 |               1 |                  1 |                      1 |                    1 |          0 | 2015-03-12 |
|    101 |                14 |               1 |                  1 |                      1 |                    1 |         30 | 2015-03-12 |
|    101 |                15 |               1 |                  1 |                      1 |                    1 |          0 | 2015-03-13 |
|    101 |                16 |               1 |                  1 |                      1 |                    1 |         30 | 2015-03-13 |
|    102 |                17 |               1 |                  1 |                      1 |                    1 |          0 | 2015-03-10 |
|    102 |                18 |               1 |                  1 |                      1 |                    1 |         30 | 2015-03-10 |
|    102 |                19 |               1 |                  1 |                      1 |                    1 |          0 | 2015-03-11 |
|    102 |                20 |               1 |                  1 |                      1 |                    1 |         30 | 2015-03-11 |
|    102 |                21 |               1 |                  1 |                      1 |                    1 |          0 | 2015-03-12 |
|    102 |                22 |               1 |                  1 |                      1 |                    1 |         30 | 2015-03-12 |
|    102 |                23 |               1 |                  1 |                      1 |                    1 |          0 | 2015-03-13 |
|    102 |                24 |               1 |                  1 |                      1 |                    1 |         30 | 2015-03-13 |
+--------+-------------------+-----------------+--------------------+------------------------+----------------------+------------+------------+    

我想首先使用以下公式计算每只宠物的每日得分:score = SUM(severe_movement_time) + SUM(moderate_movement_time) + SUM(mild_movement_time) + SUM(simple_sleep_time),然后根据得分确定每只宠物的等级。通过使用以下查询,我可以在2015-03-10一天完成。

SELECT pet_id, date, score, rank 
FROM 
(
SELECT t.pet_id, t.date, t.score, @prev := @curr, @curr := score, @rank := IF(@prev = @curr, @rank, @rank+1) AS rank 
FROM (
    SELECT pet_id, date, COALESCE(SUM(simple_sleep_time), 0) shallow, 
           COALESCE(SUM(deep_sleep_time), 0) deep, COALESCE(SUM(mild_movement_time), 0) light, 
           COALESCE(SUM(moderate_movement_time), 0) moderate, COALESCE(SUM(severe_movement_time), 0) heavy, 
          (COALESCE(SUM(severe_movement_time), 0) + COALESCE(SUM(moderate_movement_time), 0) + COALESCE(SUM(mild_movement_time), 0) + COALESCE(SUM(simple_sleep_time), 0)) score 
    FROM TEST_ACTIVITY  
    WHERE date = DATE('2015-03-10')
    GROUP BY pet_id 
    ORDER BY score DESC
) t, (SELECT @curr := null, @prev := null, @rank := 0) r ORDER BY score DESC
) x 

我的问题是如何编写单个查询来连续几天计算得分和排名,例如4天(start_date = 2015-03-10,end_date = 2015-03-13)。预期结果如下所列。

| pet_id | date       | score | rank |
+--------+------------+-------+------+
|    102 | 2015-03-10 | 41    |    1 |
|    101 | 2015-03-10 | 25    |    2 |
|    100 | 2015-03-10 | 9     |    3 |
|    102 | 2015-03-11 | 45    |    1 |
|    101 | 2015-03-11 | 29    |    2 |
|    100 | 2015-03-11 | 13    |    3 |
|    102 | 2015-03-12 | 49    |    1 |
|    101 | 2015-03-12 | 33    |    2 |
|    100 | 2015-03-12 | 17    |    3 |
|    102 | 2015-03-13 | 53    |    1 |
|    101 | 2015-03-13 | 37    |    2 |
|    100 | 2015-03-13 | 21    |    3 |

1 个答案:

答案 0 :(得分:1)

请尝试以下方法......

SELECT pet_id,
       date,
       score,
       rank 
FROM 
(
    SELECT pet_id,
           date,
           score,
           @prevDate := COALESCE( @currDate,
                                  CURDATE() ) as prevDate,
           @currDate := date,
           @prevScore := COALESCE( @currScore,
                                   -1 ) AS prevScore,
           @currScore := score,
           @rank := IF( @prevDate <> @currDate,
                        1,
                        IF( @prevScore = @currScore,
                            @rank,
                            @rank + 1 ) ) AS rank
    FROM
    (
        SELECT pet_id,
               date,
               ( COALESCE( SUM( severe_movement_time ),
                           0 ) +
                     COALESCE( SUM( moderate_movement_time ),
                               0 ) +
                     COALESCE( SUM( mild_movement_time ),
                               0 ) +
                     COALESCE( SUM( simple_sleep_time ),
                               0 ) ) AS score
        FROM TEST_ACTIVITY  
        WHERE date BETWEEN DATE( '2015-03-10' ) AND DATE( '2015-03-13' )
        GROUP BY pet_id,
                 date
    ) AS scoreFinder, ( SELECT @currDate := NULL,
                               @prevDate := NULL,
                               @currScore := NULL,
                               @prevScore := NULL,
                               @rank := 0 ) AS r
    ORDER BY date,
             score DESC
) AS rankFinder;

我的答案基于您提供给我们的代码。我的第一个修改是删除shallowdeep等的计算,因为这些计算在计算后从未再次引用。

我的下一个修改是更改最内层查询的WHERE子句以使用BETWEEN运算符来定义要考虑的记录必须来自的日期范围。我已经使用了您的问题中的显式值,但您当然可以使用其他值或包含这些值的变量。

由于我们对每个pet_id的每个score date感兴趣,因此我将此列表分组条款扩展为GROUP BY pet_id和{{1配对。

由于此时不需要对列表进行排序,因此我删除了date子句。

我不喜欢超短别名,因为更复杂的声明越容易丢失它们所代表的内容并犯错误,无论是在编码还是调试时,这就是我将ORDER BY更改为t的原因(我尝试使用简短但描述性的别名)。无论你喜欢什么,你都可以自由地称呼它。

scoreFinder结合的此列表构成了最中间查询的基础,该查询首先按valuesInitialiser对此基数进行排序,然后按分数进行分类。

现在可以使用排序列表开始计算date。由于记录rankrank及其date相比,与前一记录的记录相比,必须跟踪每个记录的当前值和先前值。作为排序中更主要的字段,需要首先比较score的值。如果它们不同,则正在检查新date的第一条记录,这意味着date的值需要(重新)初始化为rank。如果两个日期相同,则需要测试1中的更改。如果分数未更改,则需要使用score的当前值。否则,rank的当前值将需要递增并使用新值。

生成rank的值后,剩下的就是只选择所需的字段。

要测试此语句,我会根据您提供的数据运行它,从而产生所需的结果。但是,我注意到示例数据中的rank都不同,因此我将其中一条记录从score更改为( 101, 12, 1, 1, 1, 1, 30, '2015-03-11' ),以便( 101, 12, 1, 1, 1, 17, 30, '2015-03-11' )为{ {1}} scorepet_id的{​​{1}}匹配date。我重复测试了我的陈述,希望对pet_id = 102的两个等级产生相同的等级。

如果您有任何问题或意见,请随时发表评论。