如何按条件计算聚合值?
How to calculate agregate values by condition?
这是我的基本示例
+----+-------+---------------------+
| id | value | Get avg data here ↓ |
+----+-------+---------------------+
| 1 | 22 | |
| 2 | 23 | |
| 3 | 4 | |
| 3 | 33 | |
| 4 | 222 | |
| 5 | 75 | |
| 6 | 92 | |
| 7 | 202 | |
+----+-------+---------------------+
像这样尝试smth,但是什么都没有
AVG (value) OVER (ORDER BY id
(CASE WHEN id 5 THEN ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING):: decimal(8,2) AS 'hello'),
(CASE WHEN id 1 THEN ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING):: decimal(8,2) AS 'hello'),
(ELSE ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING):: decimal(8,2) AS 'hello' END)
FROM initial_table)
你能帮帮我吗?
更新:对于这些,我添加了一个新列以允许对行进行排序。由于 OP 在代码中将 day_name 称为数字,我假设它们指的是天 - 因此将 'id' 列更改为 'daynum',并相应地添加了一列 'weeknum'.
先按周数再按天数排序等同于按日期排序。如果 OP 改为将日期字段作为要排序的键列,请将我对 ORDER BY weeknum, daynum
的引用更改为 ORDER BY datefield
.
这是我使用的数据设置(仅限前两周):
CREATE TABLE initial_table
(weeknum int, daynum int, value int, PRIMARY KEY (weeknum, daynum));
INSERT INTO initial_table (weeknum, daynum, value) VALUES
(1, 1, 22),
(1, 2, 23),
(1, 3, 4),
(1, 4, 23),
(1, 5, 14),
(1, 6, 132),
(1, 7, 211),
(2, 1, 155),
(2, 2, 190),
(2, 3, 33),
(2, 4, 222),
(2, 5, 75),
(2, 6, 92),
(2, 7, 107);
一个相对清晰的版本是使用 sub-query 或 CTE 来查找所有类型的平均值,然后使用外部查询选择适当的一个,例如
WITH AvgList AS
(SELECT weeknum,
daynum,
value,
SUM (value) OVER (ORDER BY weeknum, daynum) AS CumulativeSum,
AVG (value) OVER (ORDER BY weeknum, daynum ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING) :: decimal(8,2) AS Avg5,
AVG (value) OVER (ORDER BY weeknum, daynum ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) :: decimal(8,2) AS Avg1,
AVG (value) OVER (ORDER BY weeknum, daynum ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) :: decimal(8,2) AS AvgOther
FROM initial_table
)
SELECT weeknum,
daynum,
value,
CASE WHEN daynum = 5 THEN Avg5
WHEN daynum = 1 THEN Avg1
ELSE AvgOther END AS hello
FROM AvgList;
更简单的方法是将它们包含在单个 CASE 表达式中
SELECT weeknum,
daynum,
value,
CASE WHEN daynum = 5 THEN AVG (value) OVER (ORDER BY weeknum, daynum ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING) :: decimal(8,2)
WHEN daynum = 1 THEN AVG (value) OVER (ORDER BY weeknum, daynum ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) :: decimal(8,2)
ELSE AVG(value) OVER (ORDER BY weeknum, daynum ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) :: decimal(8,2) END AS hello
FROM initial_table;
这是一个 db<>fiddle 数据和两个选项。
仅供参考,这是一个 db<>fiddle in SQL Server,因为这是我最初写的。
这是我的基本示例
+----+-------+---------------------+
| id | value | Get avg data here ↓ |
+----+-------+---------------------+
| 1 | 22 | |
| 2 | 23 | |
| 3 | 4 | |
| 3 | 33 | |
| 4 | 222 | |
| 5 | 75 | |
| 6 | 92 | |
| 7 | 202 | |
+----+-------+---------------------+
像这样尝试smth,但是什么都没有
AVG (value) OVER (ORDER BY id
(CASE WHEN id 5 THEN ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING):: decimal(8,2) AS 'hello'),
(CASE WHEN id 1 THEN ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING):: decimal(8,2) AS 'hello'),
(ELSE ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING):: decimal(8,2) AS 'hello' END)
FROM initial_table)
你能帮帮我吗?
更新:对于这些,我添加了一个新列以允许对行进行排序。由于 OP 在代码中将 day_name 称为数字,我假设它们指的是天 - 因此将 'id' 列更改为 'daynum',并相应地添加了一列 'weeknum'.
先按周数再按天数排序等同于按日期排序。如果 OP 改为将日期字段作为要排序的键列,请将我对 ORDER BY weeknum, daynum
的引用更改为 ORDER BY datefield
.
这是我使用的数据设置(仅限前两周):
CREATE TABLE initial_table
(weeknum int, daynum int, value int, PRIMARY KEY (weeknum, daynum));
INSERT INTO initial_table (weeknum, daynum, value) VALUES
(1, 1, 22),
(1, 2, 23),
(1, 3, 4),
(1, 4, 23),
(1, 5, 14),
(1, 6, 132),
(1, 7, 211),
(2, 1, 155),
(2, 2, 190),
(2, 3, 33),
(2, 4, 222),
(2, 5, 75),
(2, 6, 92),
(2, 7, 107);
一个相对清晰的版本是使用 sub-query 或 CTE 来查找所有类型的平均值,然后使用外部查询选择适当的一个,例如
WITH AvgList AS
(SELECT weeknum,
daynum,
value,
SUM (value) OVER (ORDER BY weeknum, daynum) AS CumulativeSum,
AVG (value) OVER (ORDER BY weeknum, daynum ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING) :: decimal(8,2) AS Avg5,
AVG (value) OVER (ORDER BY weeknum, daynum ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) :: decimal(8,2) AS Avg1,
AVG (value) OVER (ORDER BY weeknum, daynum ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) :: decimal(8,2) AS AvgOther
FROM initial_table
)
SELECT weeknum,
daynum,
value,
CASE WHEN daynum = 5 THEN Avg5
WHEN daynum = 1 THEN Avg1
ELSE AvgOther END AS hello
FROM AvgList;
更简单的方法是将它们包含在单个 CASE 表达式中
SELECT weeknum,
daynum,
value,
CASE WHEN daynum = 5 THEN AVG (value) OVER (ORDER BY weeknum, daynum ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING) :: decimal(8,2)
WHEN daynum = 1 THEN AVG (value) OVER (ORDER BY weeknum, daynum ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) :: decimal(8,2)
ELSE AVG(value) OVER (ORDER BY weeknum, daynum ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) :: decimal(8,2) END AS hello
FROM initial_table;
这是一个 db<>fiddle 数据和两个选项。
仅供参考,这是一个 db<>fiddle in SQL Server,因为这是我最初写的。