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 );

Results:

|               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

Demo Fiddle