【问题标题】:Group rows by condition按条件分组行
【发布时间】:2017-06-04 09:50:49
【问题描述】:

我有这些数据:

Start   End     Quantity
425     449     24
450     474     24
475     499     24
500     524     24
2300    2324    24
2400    2499    99
2500    2599    99
2800    2899    99
2900    2999    99
3200    3249    49
3250    3299    49
3300    3349    49
3350    3399    49
3400    3449    49
3500    3549    49
3600    3624    24
3650    3674    24
3700    3724    24
3950    3964    14
4000    4000    0
4150    4399    249
4400    4499    99
5034    5075    41

数量是End - Start的结果。

我想获取以下数据,Generated 行:

Start   End     Quantity
425     449     24
450     474     24
475     499     24
500     524     24
425     524     96
2300    2324    24
2300    2324    24
2400    2499    99
2500    2599    99
-----GENERATED----
425     2599    438
------------------

2800    2899    99
2900    2999    99
3200    3249    49
3250    3299    49
3300    3349    49
3350    3399    49
3400    3449    49
3500    3549    49
-----GENERATED-----
2800    3549    492
------------------

3600    3624    24
3650    3674    24
3700    3724    24
3950    3964    14
4000    4000    0
4150    4399    249
4400    4499    99
5034    5075    41
-----GENERATED-----
3600    5075    475
------------------

条件是它必须将所有数量相加,直到 500。如果超过 500,则重新计数。

我已经尝试过 Rollup,但我找不到合适的条件让它发挥作用。

当然,通过编程代码而不是 SQL 更容易做到这一点,但我们必须在数据库环境中进行。获取生成行的工具可以是任何东西,循环函数、新表等。

错误解决

我在运行@Prdp 的查询时遇到了错误:

Msg 530, Level 16, State 1, Line 1
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.

我在这里找到了解决方案: http://sqlhints.com/tag/the-statement-terminated-the-maximum-recursion-100-has-been-exhausted-before-statement-completion/

更新 1

使用@Prdp 的查询我们得到以下结果:

Start   End     rn  st
(400)   424     1   24
425     449     2   48
450     474     3   72
475     499     4   96
500     524     5   120
2300    2324    6   144
2400    2499    7   243
2500    2599    8   342
2800    (2899)  9   (441)  
(2900)  2999    10  99
3200    3249    11  148
3250    3299    12  197
3300    3349    13  246
3350    3399    14  295
3400    3449    15  344
3500    3549    16  393
3600    3624    17  417
3650    3674    18  441
3700    3724    19  465
3950    3964    20  479
4000    (4000)  21  (479) 
(4150)  4399    22  249
4400    4499    23  348
5034    (5075)  24  (389) 

它越来越接近我们的需要。是否可以只提取() 之间的数据而丢弃其他数据? 我们也可以使用游标。

【问题讨论】:

  • 请标记您正在使用的数据库管理系统。
  • @vkp 我们使用的数据库是sql-server-2005
  • 你可以试试递归查询。

标签: sql sql-server sql-server-2005 rollup


【解决方案1】:

您可以使用Recursive CTE。我想不出更好的办法。

;WITH cte
     AS (SELECT *,
                Row_number()OVER(ORDER BY start) rn
         FROM   Yourtable),
     rec_cte
     AS (SELECT *,
                ( [End] - Start ) AS st,
                1                 AS grp
         FROM   cte
         WHERE  rn = 1
         UNION ALL
         SELECT a.*,
                CASE
                  WHEN st + ( a.[End] - a.Start ) >= 500 THEN a.[End] - a.Start
                  ELSE st + ( a.[End] - a.Start )
                END,
                CASE
                  WHEN st + ( a.[End] - a.Start ) >= 500 THEN b.grp + 1
                  ELSE grp
                END
         FROM   cte a
                JOIN rec_cte b
                  ON a.rn = b.rn + 1)
SELECT Min(Start) as Start,
       Max([End]) as [End],
       Max(st) as Quantity
FROM   rec_cte
GROUP  BY grp
OPTION (maxrecursion 0) 

【讨论】:

  • 这行不通,因为它不会生成总行数。例如,应该有一行 425 2899 417。如果您可以为每个组创建一个组标记,那么您可以使用汇总。我可以在最近的版本中想办法做到这一点,但我不知道 2005 年允许什么和不允许什么。当然,如果你不告诉任何人,你可以使用光标
  • @AdamJacobson - 你只想要这三个摘要行作为结果吗?
  • @Prdp 是的,只有 3 行,其他数据“无用”。
  • @AdamJacobson 我们也可以使用游标,我们不会告诉任何人:P
  • @user7441876 - 可以完成会更新我的答案等待
【解决方案2】:

这是 MySQL 中提出的解决方案。类似的策略应该适用于 SQL Server。

drop table if exists TestData;
create table TestData(Start int, End int, Quantity int);
insert TestData values (425,449,24);
insert TestData values (450,474,24);
insert TestData values (475,499,24);
insert TestData values (500,524,24);
insert TestData values (2300,2324,24);
insert TestData values (2400,2499,99);
insert TestData values (2500,2599,99);
insert TestData values (2800,2899,99);
insert TestData values (2900,2999,99);
insert TestData values (3200,3249,49);
insert TestData values (3250,3299,49);
insert TestData values (3300,3349,49);
insert TestData values (3350,3399,49);
insert TestData values (3400,3449,49);
insert TestData values (3500,3549,49);
insert TestData values (3600,3624,24);
insert TestData values (3650,3674,24);
insert TestData values (3700,3724,24);
insert TestData values (3950,3964,14);
insert TestData values (4000,4000,0);
insert TestData values (4150,4399,249);
insert TestData values (4400,4499,99);
insert TestData values (5034,5075,41);

drop table if exists DataRange;
create table DataRange (StartRange int, EndRange int);
insert DataRange values (425, 2599);
insert DataRange values (2800,3549);
insert DataRange values (3600,5075);

select
DataRange.StartRange,DataRange.EndRange
,sum(TestData.quantity) as Quantity 
from TestData 
inner join DataRange on 
    (TestData.start between DataRange.StartRange and DataRange.EndRange )
    or
    (TestData.End between DataRange.StartRange and DataRange.EndRange )
group by DataRange.StartRange,DataRange.EndRange

【讨论】:

  • 你假设他想要即时计算的值
  • @AdamJacobson:这些值不需要在某个地方进行硬编码吗?为什么不把它们放在一张桌子上呢?
  • @AdamJacobson:我现在明白了。感谢您的澄清。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-16
  • 2014-12-18
  • 2021-10-09
  • 2021-09-16
  • 2018-12-03
  • 2021-02-03
相关资源
最近更新 更多