在两个正值之间添加所有负值

时间:2012-05-08 10:15:43

标签: sql sql-server-2008

这是我的表

    ID  account Value date
    0     Tom    10   6/7
    1     Tom    -9   6/8
    2     Tom    -5   6/9
    3     Tom    -4   6/10
    4     Tom    20   6/11
    5     Tom    30   6/12
    6     Tom    -4   6/13
    7     Tom    -5   6/14
    8     Tom     7   6/15
    9     Tom    -5   6/16
   10     Tom    -5   6/16
   11     Tom    10   6/16
   12     Tom    -2   6/17

我必须在正值之间添加所有负值,并使用之前的正值减去它们。以下是上面的示例表

的结果
    ID  account Value date
    0     Tom    10   6/7
    4     Tom     2   6/11
    5     Tom    30   6/12
    8     Tom    -2   6/15
   11     Tom     0   6/16
   12     Tom    -2   6/17

4 个答案:

答案 0 :(得分:2)

尝试此解决方案(警告:Sql Server 2012):http://www.sqlfiddle.com/#!6/f5ce0/7

with summary as
(
  select 
      *,
      -- if negative make it zero
      -- if positive make it 1


     grp = 
       sum((sign(value)+1) / 2)
       over(order by id desc) 


  from tbl
)
select 
  id = max(id),
  account = max(account),
  value = sum(value),
  date = max(date)
from summary
group by grp
order by grp desc

输出:

ID  ACCOUNT VALUE   DATE
0   Tom     10      2001-06-07
4   Tom     2       2001-06-11
5   Tom     30      2001-06-12
8   Tom     -2      2001-06-15
11  Tom     0       2001-06-16
12  Tom     -2      2001-06-17

第一步,为正数和负数创建一个标记,稍后我们将使用它来分隔组:

select 
    *,
    -- if negative make it zero
    -- if positive make it 1
    (sign(value)+1) / 2 as rn

from tbl;

输出:

ID  ACCOUNT VALUE   DATE            RN
0   Tom     10      2001-06-07      1
1   Tom     -9      2001-06-08      0
2   Tom     -5      2001-06-09      0
3   Tom     -4      2001-06-10      0
4   Tom     20      2001-06-11      1
5   Tom     30      2001-06-12      1
6   Tom     -4      2001-06-13      0
7   Tom     -5      2001-06-14      0
8   Tom     7       2001-06-15      1
9   Tom     -5      2001-06-16      0
10  Tom     -5      2001-06-16      0
11  Tom     10      2001-06-16      1
12  Tom     -2      2001-06-17      0

第二步,SUM超过那些1s 0来创建分组,根据OP的问题这是向后的:http://www.sqlfiddle.com/#!6/f5ce0/3

select 
    *,
    -- if negative make it zero
    -- if positive make it 1

   grp = 
     sum((sign(value)+1) / 2)
     over(order by id desc) 

from tbl;

输出:

ID  ACCOUNT VALUE   DATE            GRP
12  Tom     -2      2001-06-17      0
11  Tom     10      2001-06-16      1
10  Tom     -5      2001-06-16      1
9   Tom     -5      2001-06-16      1
8   Tom     7       2001-06-15      2
7   Tom     -5      2001-06-14      2
6   Tom     -4      2001-06-13      2
5   Tom     30      2001-06-12      3
4   Tom     20      2001-06-11      4
3   Tom     -4      2001-06-10      4
2   Tom     -5      2001-06-09      4
1   Tom     -9      2001-06-08      4
0   Tom     10      2001-06-07      5

最后一步,将这些相关数字分组:http://www.sqlfiddle.com/#!6/f5ce0/7

with summary as
(
  select 
      *,
      -- if negative make it zero
      -- if positive make it 1


     grp = 
       sum((sign(value)+1) / 2)
       over(order by id desc) 


  from tbl
)
select 
  id = max(id),
  account = max(account),
  value = sum(value),
  date = max(date)
from summary
group by grp
order by grp desc

输出:

ID  ACCOUNT VALUE   DATE
0   Tom     10      2001-06-07
4   Tom     2       2001-06-11
5   Tom     30      2001-06-12
8   Tom     -2      2001-06-15
11  Tom     0       2001-06-16
12  Tom     -2      2001-06-17

答案 1 :(得分:2)

适用于Sql Server 2008:http://www.sqlfiddle.com/#!3/4b7cb/2

with a as 
(
  select z.id,z.account,z.date, z.value, coalesce(x.id,-1) as grp
  from tbl z
  outer apply
  (
    select top 1 y.id
    from tbl y where y.id < z.id and y.value > 0
    order by y.id desc
  ) as x  
)
select 
  id = max(id), 
  account = max(account),
  value = sum(value),
  date = max(date)
from a 
group by grp
order by id

数据样本:

CREATE TABLE [tbl]
    ([ID] int, [account] varchar(3), [Value] int, [date] date);

INSERT INTO [tbl]
    ([ID], [account], [Value], [date])
VALUES
    (0, 'Tom', 10, '2001-06-07 00:00:00'),
    (1, 'Tom', -9, '2001-06-08 00:00:00'),
    (2, 'Tom', -5, '2001-06-09 00:00:00'),
    (3, 'Tom', -4, '2001-06-10 00:00:00'),
    (4, 'Tom', 20, '2001-06-11 00:00:00'),
    (5, 'Tom', 30, '2001-06-12 00:00:00'),
    (6, 'Tom', -4, '2001-06-13 00:00:00'),
    (7, 'Tom', -5, '2001-06-14 00:00:00'),
    (8, 'Tom', 7, '2001-06-15 00:00:00'),
    (9, 'Tom', -5, '2001-06-16 00:00:00'),
    (10, 'Tom', -5, '2001-06-16 00:00:00'),
    (11, 'Tom', 10, '2001-06-16 00:00:00'),
    (12, 'Tom', -2, '2001-06-17 00:00:00');

输出:

ID      ACCOUNT VALUE   DATE
0       Tom     10      2001-06-07
4       Tom     2       2001-06-11
5       Tom     30      2001-06-12
8       Tom     -2      2001-06-15
11      Tom     0       2001-06-16
12      Tom     -2      2001-06-17

工作原理:

select *, coalesce(x.id,-1) as z_id
from tbl z
outer apply
(
  select top 1 y.id 
  from tbl y where y.id < z.id and y.value > 0
  order by y.id desc
) as x
order by z.id desc;

输出:

ID      ACCOUNT VALUE   DATE            Z_ID
12      Tom     -2      2001-06-17      11
11      Tom     10      2001-06-16      8
10      Tom     -5      2001-06-16      8
9       Tom     -5      2001-06-16      8
8       Tom     7       2001-06-15      5
7       Tom     -5      2001-06-14      5
6       Tom     -4      2001-06-13      5
5       Tom     30      2001-06-12      4
4       Tom     20      2001-06-11      0
3       Tom     -4      2001-06-10      0
2       Tom     -5      2001-06-09      0
1       Tom     -9      2001-06-08      0
0       Tom     10      2001-06-07      -1

答案 2 :(得分:1)

使用MySQL:http://www.sqlfiddle.com/#!2/61773/18

这里的Mysqlism太多,所选字段不需要在GROUP BY上:

select 
  id, account, sum(value), date
from
(  
  select *, @gn := @gn +  ( (sign(value)+1) / 2 ) as gn
  from (tbl, (select @gn := 0)) vars
  order by id desc
) as x
group by gn
order by id

最好像这样写,意图是明确的:

select 
  max(id) as id, max(account) as account, sum(value) as value, max(date) as date
from
(  
  select *, @gn := @gn +  ( (sign(value)+1) / 2 ) as gn
  from (tbl, (select @gn := 0) vars)
  order by id desc
) as x
group by gn
order by gn desc

sign(value)的逻辑解释,请参阅:https://stackoverflow.com/a/10514961

答案 3 :(得分:0)

在复制我的答案时,我意识到你需要它用于sql-server或plsql。我的解决方案适用于MySQL。对于那个很抱歉。也许它无论如何都有帮助。

不使用cursors我认为没办法。

所以来了。

测试数据:

drop table if exists test;
create table test(id int, value int, c_date date);
insert into test values 
(    0     ,    10   ,'2012/6/7'),
(    1     ,    -9   ,'2012/6/8'),
(    2     ,    -5   ,'2012/6/9'),
(    3     ,    -4   ,'2012/6/10'),
(    4     ,    20   ,'2012/6/11'),
(    5     ,    30   ,'2012/6/12'),
(    6     ,    -4   ,'2012/6/13'),
(    7     ,    -5   ,'2012/6/14'),
(    8     ,     7   ,'2012/6/15'),
(    9     ,    -5   ,'2012/6/16'),
(   10     ,    -5   ,'2012/6/16'),
(   11     ,    10   ,'2012/6/16'),
(   12     ,    -2   ,'2012/6/17');

您需要的程序:

drop procedure if exists read_test;

create procedure read_test()
begin
declare done int default false;

declare v_id, v_value int;
declare v_date date;
declare adding_result int;

declare cur cursor for select id, value, c_date from test order by id;

declare continue handler for not found set done = true;

drop table if exists tmp_result;
create temporary table tmp_result as select * from test where 1 = 0;
set adding_result = 0;

open cur;

read_loop: LOOP
fetch cur into v_id, v_value, v_date;
if done then
leave read_loop;
end if;

if v_value >= 0 then

set adding_result = adding_result + v_value;
insert into tmp_result (id, value, c_date) values (v_id, adding_result, v_date);
set adding_result = 0;

else
set adding_result = adding_result + v_value;
end if;

end loop;
insert into tmp_result (id, value, c_date) values (v_id, adding_result, v_date);
close cur;

select * from tmp_result;

end;

最后

call read_test();

就是这样。输出:

id  value   c_date
0   10  2012-06-07
4   2   2012-06-11
5   30  2012-06-12
8   -2  2012-06-15
11  0   2012-06-16
12  -2  2012-06-17