仅汇总顺序值

时间:2019-10-21 10:51:00

标签: sql sql-server sql-server-2017 gaps-and-islands

我有一个包含3列的表格

  Create table test 
(
        Created  Datetime
    ,   Flag     Bit 
    ,   Amount   Money
)

看起来像这样

      Created              Flag    Amount
2019-12-01 00:00:00.000    1        50,40
2019-11-21 00:00:00.000    1        50,40
2019-11-06 00:00:00.000    0        50,40
2019-10-04 00:00:00.000    1        50,40
2019-09-08 00:00:00.000    1        50,40
2019-09-01 00:00:00.000    0        50,40
2019-08-04 00:00:00.000    1        50,40
2019-07-24 00:00:00.000    1        50,40
2019-07-23 00:00:00.000    1        50,40
2019-06-01 00:00:00.000    0        50,40
2019-05-05 00:00:00.000    0        50,40
2019-04-25 00:00:00.000    1        50,40
2019-03-11 00:00:00.000    0        50,40
2019-02-03 00:00:00.000    0        50,40
2019-02-02 00:00:00.000    0        50,40
2019-02-01 00:00:00.000    0        50,40
2019-01-31 00:00:00.000    1        50,40
2019-01-26 00:00:00.000    0        50,40
2019-01-26 00:00:00.000    0        50,40
2019-01-01 00:00:00.000    1        50,40

如您所见,它由Created降序排列。

想象一下,所有这些行都是事务。当标志为1时,我们有一个检查点。因此,例如从第20行到第17行是一个期间(始终从旧到新计数)。从第17行到第12行是另一个句点,依此类推。

请注意,在第9、8和7行中,我们有3个连续的标志,其值为1。发生这种情况时,连续的1不带0,我想将所有连续的1视为一个组。我希望它们以总金额显示为一行并保留其中MIN(Created)

例如,对于第9-7行,我要将其分组为amount的值为151.2flag的值为1和{ {1}}的值为Created(三行中的2019-07-23 00:00:00.000

此表的示例输出如下。

min(date)

2 个答案:

答案 0 :(得分:1)

如果只想折叠相邻的“ 1”,则一种方法是根据前0的计数和聚合来分配分组。因此,为了汇总“ 1”:

select min(created), 1 as flag, sum(amount)
from (select t.*,
             sum(1 - flag) over (order by created) as grouping
      from t
     ) t
where flag = 1
group by grouping;

当我们包含0时,这并不完全有效,因为0将与1结合在一起。所以我认为最简单的方法是union all

select min(created), 1 as flag, sum(amount)
from (select t.*,
             sum(1 - flag) over (order by created) as grouping
      from t
     ) t
where flag = 1
group by grouping
union all
select created, flag, amount
from t
where flag = 0;

我最初误认为该问题是所有期间的摘要,而不仅仅是相邻的“ 1”。您可以通过累积总和来确定组:

select t.*,
       sum(flag) over (order by created) as grouping
from t;

然后使用子查询将其汇总:

select min(created), max(created), count(*) as num_transactions,
       sum(amount) as total_amount
from (select t.*,
             sum(flag) over (order by created) as grouping
      from t
     ) t
group by grouping;

答案 1 :(得分:0)

您希望聚合所有标记为1的连续行。可以通过标记为0的行的连续计数来实现此目的。您可以在下表中看到该标记+零的运行计数标识组。

Created    | Amount | Flag | COUNT_0
-----------+--------+------+--------
2019-12-01 | 50,40  | 1    |       0   \   both rows flag=1, count_0=0 => one group
2019-11-21 | 50,40  | 1    |       0   /
2019-11-06 | 50,40  | 0    |       1   >   the only row with flag=0, count_0=1 => one group
2019-10-04 | 50,40  | 1    |       1   \   both rows flag=1, count_0=1 => one group
2019-09-08 | 50,40  | 1    |       1   /
2019-09-01 | 50,40  | 0    |       2   >   the only row with flag=0, count_0=2 => one group
2019-08-04 | 50,40  | 1    |       2   \
2019-07-24 | 50,40  | 1    |       2    |  all three rows flag=1, count_0=2 => one group
2019-07-23 | 50,40  | 1    |       2   /
2019-06-01 | 50,40  | 0    |       3   >   the only row with flag=0, count_0=3 => one group
2019-05-05 | 50,40  | 0    |       4   >   the only row with flag=0, count_0=4 => one group
2019-04-25 | 50,40  | 1    |       4   >   the only row with flag=1, count_0=4 => one group
2019-03-11 | 50,40  | 0    |       5   >   the only row with flag=0, count_0=5 => one group
2019-02-03 | 50,40  | 0    |       6   >   the only row with flag=0, count_0=6 => one group
2019-02-02 | 50,40  | 0    |       7   >   the only row with flag=0, count_0=7 => one group
2019-02-01 | 50,40  | 0    |       8   >   the only row with flag=0, count_0=8 => one group
2019-01-31 | 50,40  | 1    |       8   >   the only row with flag=1, count_0=8 => one group
2019-01-26 | 50,40  | 0    |       9   >   the only row with flag=0, count_0=9 => one group
2019-01-26 | 50,40  | 0    |      10   >   the only row with flag=0, count_0=10 => one group
2019-01-01 | 50,40  | 1    |      10   >   the only row with flag=1, count_0=10 => one group

相关查询:

select min(created), min(flag), sum(amount)
from
(
  select
    m.*,
    count(case when flag = 0 then 1 end) over (order by created) as count_0
  from mytable m
)
group by flag, count_0
order by min(created);