如何按DATE_FORMAT(date,'%Y-%m-%d')求和,限制20个求和(每天),如果大于20求和剩余值?

how to sum value by DATE_FORMAT(date,'%Y-%m-%d'),the limit 20 sum(day by day), and sum remaining value if greater than 20?

如何通过DATE_FORMAT(date,'%Y-%m-%d')对值求和并识别前20行数据(按数据desc排序),如果大于20则对剩余值求和,否则为0?假设我的数据低于 SQL,怎么办?非常感谢您的任何建议。

SELECT SUM(value), id, DATE_FORMAT(date,'%Y-%m-%d') 
FROM test_table
GROUP BY id, DATE_FORMAT(date,'%Y-%m-%d')
sum(value)  id  DATE_FORMAT(date,'%Y-%m-%d')
-------------------------------------------- 
64.98   123456  2021-01-01
64.98   123456  2021-01-02
64.98   123456  2021-01-03
64.98   123456  2021-01-04
64.98   123456  2021-01-05
64.98   123456  2021-01-06
72.34   123456  2021-01-07
64.98   123456  2021-01-08
64.98   123456  2021-01-09
103.80  123456  2021-01-10
64.98   123456  2021-01-11
64.98   123456  2021-01-12
64.98   123456  2021-01-13
64.98   123456  2021-01-14
64.98   123456  2021-01-15
64.98   123456  2021-01-16
64.98   123456  2021-01-17
64.98   123456  2021-01-18
64.98   123456  2021-01-19
64.98   123456  2021-01-20
64.98   123456  2021-01-21
64.98   123456  2021-01-22
64.98   123456  2021-01-23
64.98   123456  2021-01-24
144.98  123456  2021-01-25
64.98   123456  2021-01-26
64.98   123456  2021-01-27
64.98   123456  2021-01-28
64.98   123456  2021-01-29
64.98   123456  2021-01-30
64.98   123456  2021-01-31
64.98   123456  2021-02-01
64.98   123456  2021-02-02
64.98   123456  2021-02-03
64.98   123456  2021-02-04
720.92  123456  2021-02-05
66.98   123456  2021-02-06
66.98   123456  2021-02-07
66.98   123456  2021-02-08
64.98   123456  2021-02-09
64.98   123456  2021-02-10
64.98   223456  2021-01-20
54.98   223456  2021-01-21

......................................

我需要如下输出:

  id      day_1    day_2   .....  day_20   day_other 
    -------------------------------------------- 
   123456  64.98   64.98  .....  234.67      2342.12  
   223456  64.98   64.98  .....  234.67      2342.12   

终于通过下面SQL解决了这个问题,非常感谢FanoFN的

SELECT id,
       SUM(CASE WHEN rn = 1 THEN vals ELSE 0 END) AS 'day_1',
       SUM(CASE WHEN rn = 2 THEN vals ELSE 0 END) AS 'day_2',
       SUM(CASE WHEN rn = 3 THEN vals ELSE 0 END) AS 'day_3',
       SUM(CASE WHEN rn = 4 THEN vals ELSE 0 END) AS 'day_4',
       SUM(CASE WHEN rn = 5 THEN vals ELSE 0 END) AS 'day_5',
       SUM(CASE WHEN rn = 6 THEN vals ELSE 0 END) AS 'day_6',
       SUM(CASE WHEN rn = 7 THEN vals ELSE 0 END) AS 'day_7',
       SUM(CASE WHEN rn = 8 THEN vals ELSE 0 END) AS 'day_8',
       SUM(CASE WHEN rn = 9 THEN vals ELSE 0 END) AS 'day_9',
       SUM(CASE WHEN rn = 10 THEN vals ELSE 0 END) AS 'day_10',
       SUM(CASE WHEN rn = 11 THEN vals ELSE 0 END) AS 'day_11',
       SUM(CASE WHEN rn = 12 THEN vals ELSE 0 END) AS 'day_12',
       SUM(CASE WHEN rn = 13 THEN vals ELSE 0 END) AS 'day_13',
       SUM(CASE WHEN rn = 14 THEN vals ELSE 0 END) AS 'day_14',
       SUM(CASE WHEN rn = 15 THEN vals ELSE 0 END) AS 'day_15',
       SUM(CASE WHEN rn = 16 THEN vals ELSE 0 END) AS 'day_16',
       SUM(CASE WHEN rn = 17 THEN vals ELSE 0 END) AS 'day_17',
       SUM(CASE WHEN rn = 18 THEN vals ELSE 0 END) AS 'day_18',
       SUM(CASE WHEN rn = 19 THEN vals ELSE 0 END) AS 'day_19',
       SUM(CASE WHEN rn = 20 THEN vals ELSE 0 END) AS 'day_20',
       SUM(CASE WHEN rn > 20 THEN vals ELSE 0 END) AS 'day_others'
FROM
(SELECT SUM(value) AS vals, id, DATE_FORMAT(date,'%Y-%m-%d') AS dt,
       ROW_NUMBER() OVER (PARTITION BY id ORDER BY dt DESC) AS rn
FROM test_table
GROUP BY id, dt) v
GROUP BY id;

注意: 不幸的是,此解决方案中的部分组件不适用于旧的 MySQL 版本;正如 OP 提到的 ,她正在使用 MySQL 5.7.

我明白了,那样的话你可以稍微升级一下查询。让我们考虑一下,条件可能会以某种方式更改为前 30 行数据,那么您可能希望使用准备好的语句,这样您就不需要继续更改查询。

  1. 我将在这个例子中使用 3 个变量:
    一个。 @columns :用于生成列 SUM(CASE ..
    b。 @sql :用于最终查询。
    c。 @maxrows : 定义“第 xx 行”。

  2. 所有变量将设置为:

SET @columns := NULL;
SET @sql := NULL;
SET @maxrows := XX; /*how many first rows*/

设置@columns@sqlNULL只是一种预防措施,@maxrows只是你需要旋转的行,在你现在的情况下应该是SET @maxrows := 20;

  1. 正在设置 @columns 个变量。
WITH RECURSIVE cte AS (
SELECT 1 seq UNION ALL
SELECT seq+1 FROM cte WHERE seq+1 <= @maxrows)
SELECT GROUP_CONCAT(cols SEPARATOR ', \r\n') 
             INTO @columns
FROM (
SELECT CONCAT('SUM(CASE WHEN rn =',seq,' THEN vals ELSE 0 END) AS "day_',seq,'"') 
         AS cols
FROM cte UNION
SELECT CONCAT('SUM(CASE WHEN rn >',seq,' THEN vals ELSE 0 END) AS "day_others"')
FROM cte WHERE seq=@maxrows) c;

这里有两个组成部分,第一个是使用通用 table 表达式生成基于 @maxrows 的行序列,然后第二个是连接 SUM(CASE .. 你的 SELECT查询。 WHEN rn > 20 的最后一个条件单独生成,并使用 UNION 与前 20 个 SUM(CASE .. 组合。最后,GEOUP_CONCAT() 是使生成的语法成为一行所必需的,以便它可以分配给 @columns 变量。注意:设置变量只有在单行时才有效。否则,您将收到 Subquery returns more than 1 row 错误。
运行 此查询随后检查 @columns 变量:SELECT @columns;

  1. 正在设置 @sql 变量:
SELECT CONCAT('SELECT id,',@columns,'
FROM
(SELECT SUM(value) AS vals, id, DATE_FORMAT(date,"%Y-%m-%d") AS dt,
       ROW_NUMBER() OVER (PARTITION BY id ORDER BY date DESC) AS rn
FROM test_table
GROUP BY id, date) v
GROUP BY id;') INTO @sql;

这基本上只是将您已有的子查询与 @columns 变量连接起来,并将其分配给 @sql 变量。检查变量:SELECT @sql;

  1. 最后一部分只是准备基于@sql的语句,执行然后释放它:
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Demo fiddle with separated components and checkings..

Demo fiddle with final forms.

P/S:您应该能够复制此查询并直接 运行 它但是如果出现错误(除了可能的 table 或列名),那么它可能是由 group_concat_max_len 设置引起。如果是这样,您可以通过 运行ning:

检查设置
SHOW VARIABLES like 'group_concat_max_len';

如果它显示 1024(以字节为单位),这意味着 it's the default setting 并且我们在这里使用 GROUP_CONCAT() 进行的操作,它不会成功。因此,您可能想要永久增加该值,或者您可以在完整查询之前添加此语法:

SET SESSION group_concat_max_len = 1000000;