SQL 服务器组行
SQL Server group rows
我 table 有这样的数据
OrdersDate
OrdersQuantity
2021-03-10
40
2021-03-11
80
2021-03-12
63
2021-03-13
20
2021-03-14
80
2021-03-15
80
2021-03-16
70
2021-03-17
20
2021-03-18
80
2021-03-19
80
2021-03-20
80
2021-03-21
80
2021-03-22
80
我需要将这些数据按 10 行分组,每组从头开始,但组的形式是 1-10 行,然后是 2-11,然后是 3-12。所以群体相交。我尝试添加 ROW_NUMBER 并基于它以某种方式对行进行分组,但由于组相交,我不知道如何指定组条件。
我希望结果像
OrdersDate
OrdersQuantity
1st group (from 2021-03-10 to 2021-03-19)
AVG()
2nd group (from 2021-03-11 to 2021-03-20)
AVG()
3rd group (from 2021-03-12 to 2021-03-21)
AVG()
4th group (from 2021-03-13 to 2021-03-22)
AVG()
我用游标解决了这个问题,但它只适用于小样本数据,当我在 >2kk 行上尝试它时,它花了很多时间。
有没有办法对行进行分组并计算每组的 AVG?
您可以尝试使用 cte 递归通过 DATEADD
函数为您的逻辑范围日期制作日历 table,然后按日期范围使用 self-join。
查询 1:
;WITH CTE AS (
SELECT MIN(OrdersDate) s_date,
MAX(OrdersDate) e_date
FROM T
UNION ALL
SELECT DATEADD(day , 1 , s_date) ,e_date
FROM CTE
WHERE DATEADD(day , 10 , s_date) <= e_date
)
SELECT CONCAT(t1.s_date,' to ' , t1.final_date) OrdersDate,
AVG(t2.OrdersQuantity) OrdersQuantity
FROM (
SELECT s_date,DATEADD(day , 9 , s_date) final_date
FROM CTE
) t1 INNER JOIN T t2
ON t2.OrdersDate BETWEEN t1.s_date AND t1.final_date
GROUP BY CONCAT(t1.s_date,' to ' , t1.final_date)
option ( MaxRecursion 0 );
| OrdersDate | OrdersQuantity |
|--------------------------|----------------|
| 2021-03-10 to 2021-03-19 | 61 |
| 2021-03-11 to 2021-03-20 | 65 |
| 2021-03-12 to 2021-03-21 | 65 |
| 2021-03-13 to 2021-03-22 | 67 |
您可以使用对每一行和接下来的 9 行进行操作的 window 函数来创建 10 组:
WITH cte AS (
SELECT OrdersDate from_Date,
MAX(OrdersDate) OVER (ORDER BY OrdersDate ROWS BETWEEN CURRENT ROW AND 9 FOLLOWING) to_Date,
AVG(1.0 * OrdersQuantity) OVER (ORDER BY OrdersDate ROWS BETWEEN CURRENT ROW AND 9 FOLLOWING) average_qty,
ROW_NUMBER() OVER (ORDER BY OrdersDate) rn,
COUNT(*) OVER () total_rows
FROM tablename
)
SELECT from_Date, to_Date, average_qty
FROM cte
WHERE rn <= total_rows - 9 OR rn = 1;
参见demo。
使用 CTE 首先使用 mod 获取符合条件的日期范围开始日期,然后按串联的开始和结束日期分组:
with r as (
select ordersdate StartDate, DateAdd(day,9,ordersdate) EndDate,
DateAdd(day, Count(*) over() % 10, Min(ordersdate) over()) MaxStart
from t
)
select Daterange, Avg(t.OrdersQuantity) OrdersQuantity
from r
cross apply (values( Concat(r.StartDate, ' to ', EndDate)))v(Daterange)
join t on t.ordersdate between StartDate and EndDate
where r.StartDate < = r.MaxStart
group by Daterange
我 table 有这样的数据
OrdersDate | OrdersQuantity |
---|---|
2021-03-10 | 40 |
2021-03-11 | 80 |
2021-03-12 | 63 |
2021-03-13 | 20 |
2021-03-14 | 80 |
2021-03-15 | 80 |
2021-03-16 | 70 |
2021-03-17 | 20 |
2021-03-18 | 80 |
2021-03-19 | 80 |
2021-03-20 | 80 |
2021-03-21 | 80 |
2021-03-22 | 80 |
我需要将这些数据按 10 行分组,每组从头开始,但组的形式是 1-10 行,然后是 2-11,然后是 3-12。所以群体相交。我尝试添加 ROW_NUMBER 并基于它以某种方式对行进行分组,但由于组相交,我不知道如何指定组条件。
我希望结果像
OrdersDate | OrdersQuantity |
---|---|
1st group (from 2021-03-10 to 2021-03-19) | AVG() |
2nd group (from 2021-03-11 to 2021-03-20) | AVG() |
3rd group (from 2021-03-12 to 2021-03-21) | AVG() |
4th group (from 2021-03-13 to 2021-03-22) | AVG() |
我用游标解决了这个问题,但它只适用于小样本数据,当我在 >2kk 行上尝试它时,它花了很多时间。
有没有办法对行进行分组并计算每组的 AVG?
您可以尝试使用 cte 递归通过 DATEADD
函数为您的逻辑范围日期制作日历 table,然后按日期范围使用 self-join。
查询 1:
;WITH CTE AS (
SELECT MIN(OrdersDate) s_date,
MAX(OrdersDate) e_date
FROM T
UNION ALL
SELECT DATEADD(day , 1 , s_date) ,e_date
FROM CTE
WHERE DATEADD(day , 10 , s_date) <= e_date
)
SELECT CONCAT(t1.s_date,' to ' , t1.final_date) OrdersDate,
AVG(t2.OrdersQuantity) OrdersQuantity
FROM (
SELECT s_date,DATEADD(day , 9 , s_date) final_date
FROM CTE
) t1 INNER JOIN T t2
ON t2.OrdersDate BETWEEN t1.s_date AND t1.final_date
GROUP BY CONCAT(t1.s_date,' to ' , t1.final_date)
option ( MaxRecursion 0 );
| OrdersDate | OrdersQuantity |
|--------------------------|----------------|
| 2021-03-10 to 2021-03-19 | 61 |
| 2021-03-11 to 2021-03-20 | 65 |
| 2021-03-12 to 2021-03-21 | 65 |
| 2021-03-13 to 2021-03-22 | 67 |
您可以使用对每一行和接下来的 9 行进行操作的 window 函数来创建 10 组:
WITH cte AS (
SELECT OrdersDate from_Date,
MAX(OrdersDate) OVER (ORDER BY OrdersDate ROWS BETWEEN CURRENT ROW AND 9 FOLLOWING) to_Date,
AVG(1.0 * OrdersQuantity) OVER (ORDER BY OrdersDate ROWS BETWEEN CURRENT ROW AND 9 FOLLOWING) average_qty,
ROW_NUMBER() OVER (ORDER BY OrdersDate) rn,
COUNT(*) OVER () total_rows
FROM tablename
)
SELECT from_Date, to_Date, average_qty
FROM cte
WHERE rn <= total_rows - 9 OR rn = 1;
参见demo。
使用 CTE 首先使用 mod 获取符合条件的日期范围开始日期,然后按串联的开始和结束日期分组:
with r as (
select ordersdate StartDate, DateAdd(day,9,ordersdate) EndDate,
DateAdd(day, Count(*) over() % 10, Min(ordersdate) over()) MaxStart
from t
)
select Daterange, Avg(t.OrdersQuantity) OrdersQuantity
from r
cross apply (values( Concat(r.StartDate, ' to ', EndDate)))v(Daterange)
join t on t.ordersdate between StartDate and EndDate
where r.StartDate < = r.MaxStart
group by Daterange