基于某些逻辑对行进行分组?

时间:2016-09-06 23:57:35

标签: mysql sql group-by

这是我的表结构:

// reputations
+----+-------+---------------+
| id | value |   date_time   |
+----+-------+---------------+
| 1  | 5     | 1472105459    | -- in last month
| 2  | -2    | 1472105460    | -- in last month
| 3  | 15    | 1472746410    | -- in last week
| 4  | 5     | 1472746421    | -- in last week
| 5  | 10    | 1472746432    | -- in last week
| 6  | -6    | 1473115566    | -- yesterday
| 7  | 2     | 1473205567    | -- today
| 8  | 10    | 1473205590    | -- today
+----+-------+---------------+

我需要求和 value列并根据这些逻辑对它们进行分组:

(CASE WHEN FROM_UNIXTIME(MAX(date_time)) >= CURDATE() THEN 'today'
      WHEN FROM_UNIXTIME(MAX(date_time)) >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) THEN 'yesterday'
      WHEN FROM_UNIXTIME(MAX(date_time)) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) THEN 'in last week'
      ELSE 'in last month'
END) as range_day

所以这是预期的结果:

+----+-------+---------------+
| id | value |   date_time   |
+----+-------+---------------+
| 2  | 3     | 1472105460    | -- in last month
| 5  | 30    | 1472746432    | -- in last week
| 6  | -6    | 1473115566    | -- yesterday
| 8  | 12    | 1473205590    | -- today
+----+-------+---------------+

好吧,我怎么能这样做?

编辑:

我想我没有正确地提出我的问题。让我问另一种方式: 这是我的表结构:

// votes
+----+-------+-----------+---------------+
| id | value |  post_id  |   date_time   |
+----+-------+-----------+---------------+
| 1  | 5     | 1         | 1472105459    | -- in last month
| 2  | -2    | 2         | 1472105460    | -- in last month
| 3  | 10    | 2         | 1472746410    | -- in last week
| 4  | 5     | 1         | 1472746421    | -- in last week
| 5  | 10    | 3         | 1472746432    | -- in last week
| 6  | 5     | 1         | 1473115566    | -- in last week
| 7  | -2    | 1         | 1473205567    | -- yesterday
| 8  | 10    | 1         | 1473205590    | -- yesterday
+----+-------+-----------+---------------+

我想要这个输出:

// votes
+----+-------+-----------+---------------+---------------+
| id | value |  post_id  |   date_time   |   range_day   |
+----+-------+-----------+---------------+---------------+
| 1  | 5     | 1         | 1472105459    | in last month |
| 2  | -2    | 2         | 1472105460    | in last month |
| 3  | 10    | 2         | 1472746410    | in last week  |
| 5  | 10    | 3         | 1472746432    | in last week  |
| 6  | 10    | 1         | 1473115566    | in last week  |
| 8  | 8     | 1         | 1473205590    | yesterday     |
+----+-------+-----------+---------------+---------------+

如您所见,我根据post_idrange_day对它们进行了分组。注意到当某些行合并时我需要保留最大的date_time

我该怎么做?

2 个答案:

答案 0 :(得分:3)

我认为你过于复杂了。只需在CASE语句中删除MAX()即可。然后你可以GROUP BY range_day

SELECT MAX(id) as id,
  SUM(value) as value,
  (CASE WHEN FROM_UNIXTIME(date_time) >= CURDATE() THEN 'today'
        WHEN FROM_UNIXTIME(date_time) >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) THEN 'yesterday'
        WHEN FROM_UNIXTIME(date_time) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) THEN 'in last week'
        ELSE 'in last month'
  END) as range_day
FROM reputations
GROUP BY range_day

http://sqlfiddle.com/#!9/39e58c/1

现在SQLFidde返回:

| id | value |     range_day |
|----|-------|---------------|
|  2 |     3 | in last month |
|  6 |    24 |  in last week |
|  8 |    12 |     yesterday |

这不等于您的预期结果。但那是因为时间不是常数。 Tomorow你会得到另一个结果。

<强>更新

您正在尝试使用无效且不合理的代码来解释您的问题。但是看看你的预期结果,我想你想按特定的日期范围进行分组。所以让我们先转换你的表:

SELECT id,
  value,
  DATE(FROM_UNIXTIME(date_time)) as dt,
  CURDATE() as today,
  (CASE WHEN FROM_UNIXTIME(date_time) >= CURDATE() THEN 'today'
        WHEN FROM_UNIXTIME(date_time) >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) THEN 'yesterday'
        WHEN FROM_UNIXTIME(date_time) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) THEN 'in last week'
        ELSE 'in last month'
  END) as range_day
FROM reputations

| id | value |                 dt |              today |     range_day |
|----|-------|--------------------|--------------------|---------------|
|  1 |     5 |    August, 25 2016 | September, 07 2016 | in last month |
|  2 |    -2 |    August, 25 2016 | September, 07 2016 | in last month |
|  3 |    15 | September, 01 2016 | September, 07 2016 |  in last week |
|  4 |     5 | September, 01 2016 | September, 07 2016 |  in last week |
|  5 |    10 | September, 01 2016 | September, 07 2016 |  in last week |
|  6 |    -6 | September, 05 2016 | September, 07 2016 |  in last week |
|  7 |     2 | September, 06 2016 | September, 07 2016 |     yesterday |
|  8 |    10 | September, 06 2016 | September, 07 2016 |     yesterday |

现在让我们用“手”分组结果:

  • 群组“昨天”有ids 8,7和valaues 2,10 =&gt; max(id)= max(7,8)= 8 和sum(value)= sum(2,10)= 12
  • 上周“ ”小组有3,4,5,6和15,5,10,-6 =&gt; max(id)= max(3,4,5,6)= 6 和sum(value)= sum(15,5,10,-6)= 24
  • 上个月“ ”组有ID 1,2和valaues 5,-2 =&gt; max(id)= max(1,2)= 2 和sum(value)= sum(5,-2)= 3

如果您想要每个组的“最大date_time”,只需将FROM_UNIXTIME((MAX(date_time))添加到SELECT子句中。

更新2:

刚刚意识到预期结果中的date_time列。所以这就是你得到它的方式:

SELECT MAX(id) as id,
  SUM(value) as value,
  MAX(date_time) as date_time
FROM reputations
GROUP BY 
  CASE WHEN FROM_UNIXTIME(date_time) >= CURDATE() THEN 'today'
        WHEN FROM_UNIXTIME(date_time) >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) THEN 'yesterday'
        WHEN FROM_UNIXTIME(date_time) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) THEN 'in last week'
        ELSE 'in last month'
  END
ORDER BY id

更新3

这应该可以解决您的更新问题:

SELECT
  MAX(id) as id,
  SUM(value) as value,
  post_id,
  MAX(date_time) as date_time,
  (CASE WHEN FROM_UNIXTIME(date_time) >= CURDATE() THEN 'today'
        WHEN FROM_UNIXTIME(date_time) >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) THEN 'yesterday'
        WHEN FROM_UNIXTIME(date_time) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) THEN 'in last week'
        ELSE 'in last month'
  END) as range_day
FROM votes
GROUP BY post_id, range_day
ORDER BY id

http://sqlfiddle.com/#!9/060b5/11

答案 1 :(得分:1)

您不能在CASE语句中使用MAX()。它将返回该领域的全局最大值。基本上是单行。相反,您需要按范围分组,然后选择local max:

SELECT a.`value`, a.date_time FROM
    (SELECT sum(`value`) as `value`,
    (CASE WHEN FROM_UNIXTIME(date_time) >= CURDATE() THEN 'today'
          WHEN FROM_UNIXTIME(date_time) >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) THEN 'yesterday'
          WHEN FROM_UNIXTIME(date_time) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) THEN 'in last week'
          ELSE 'in last month'
    END) as range_day, MAX(date_time) as date_time
    FROM reputations
    GROUP BY 2) a
ORDER BY 2 ASC

http://sqlfiddle.com/#!9/6ea19d/1