2011-02-23 142 views
13

分組結果我有這樣的一個表:SQL查詢基於序列

ID Seq Amt 
1 1 500 
1 2 500 
1 3 500 
1 5 500 
2 10 600 
2 11 600 
3 1 700 
3 3 700 

我想組連續的序列號爲一行是這樣的:

ID Start End TotalAmt 
1 1  3 1500 
1 5  5 500 
2 10  11 1200 
3 1  1 700 
3 3  3 700 

請幫助達到這個結果。

+1

的SQL版本服務器? (id + seq)是唯一的嗎? – RichardTheKiwi 2011-02-23 08:45:04

回答

21
WITH numbered AS (
    SELECT 
    ID, Seq, Amt, 
    SeqGroup = ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Seq) - Seq 
    FROM atable 
) 
SELECT 
    ID, 
    Start = MIN(Seq), 
    [End] = MAX(Seq), 
    TotalAmt = SUM(Amt) 
FROM numbered 
GROUP BY ID, SeqGroup 
ORDER BY ID, Start 
; 
+1

+1花了我一會兒才弄清楚' - Seq'技巧,並驗證(對於我自己)不會有任何碰撞,但OP不能要求比這更好的。 – 2011-02-23 08:39:44

+0

+1我懷疑ROW_NUMBER(或RANK,...)會幫助我,但放棄了我的(更復雜,可能更慢)的解決方案。 – 2011-02-23 08:50:52

+0

@Lieven:當我說這樣的讚美時,請相信我,不需要任何正式的upvoting。 :)謝謝你,你很親切!順便說一句,你不應該刪除你的答案。在我看來,另一個CTE演示從來沒有太多,因爲這是一個相當棘手的技術,這是最好的例子。我也沒有注意到Martin或Quassnoi在這裏的存在。 :) – 2011-02-23 08:51:37

0

請嘗試以下查詢。

select id, min(seq), max(seq), sum(amt) from table group by id 

哎呀,對不起,這是錯誤的查詢,因爲你需要序列

1

這似乎很好地工作。 @breakingRows將包含破壞idseq序列的所有行(即如果id改變或者如果seq不比以前的seq多1)。使用該表格,您可以在@temp之內選擇這樣的序列的所有行。但是我必須補充說,由於所有的子查詢,性能可能並不是那麼好,但是你需要測試以確定。

declare @temp table (id int, seq int, amt int) 
insert into @temp select 1, 1, 500 
insert into @temp select 1, 2, 500 
insert into @temp select 1, 3, 500 
insert into @temp select 1, 5, 500 
insert into @temp select 2, 10, 600 
insert into @temp select 2, 11, 600 
insert into @temp select 3, 1, 700 
insert into @temp select 3, 3, 700 

declare @breakingRows table (ctr int identity(1,1), id int, seq int) 

insert into @breakingRows(id, seq) 
select id, seq 
from @temp t1 
where not exists 
    (select 1 from @temp t2 where t1.id = t2.id and t1.seq - 1 = t2.seq) 
order by id, seq 

select br.id, br.seq as start, 
     isnull ((select top 1 seq from @temp t2 
       where id < (select id from @breakingRows br2 where br.ctr = br2.ctr - 1) or 
        (id = (select id from @breakingRows br2 where br.ctr = br2.ctr - 1) and 
         seq < (select seq from @breakingRows br2 where br.ctr = br2.ctr - 1))   
       order by id desc, seq desc), 
       br.seq) 
     as [end], 
     (select SUM(amt) from @temp t1 where t1.id = br.id and 
     t1.seq < 
      isnull((select seq from @breakingRows br2 where br.ctr = br2.ctr - 1 and br.id = br2.id), 
        (select max(seq) + 1 from @temp)) and 
     t1.seq >= br.seq) 
from @breakingRows br 
order by id, seq 
+0

感謝您的努力。解決方案非常完美!但我可以只標記一個答案作爲最佳答案。 – Nagesh 2011-02-23 10:43:09

+0

沒問題。 Andriy的解決方案顯然是最好的解決方案。 – 2011-02-23 12:11:37

1

嗯,有可能是一個更優雅的方式來做到這一點(的東西我有提示),但這裏的一種方法,將如果你使用一個版本的SQL Server接受公共表表達式工作:

use Tempdb 
go 

create table [Test] 
(
    [id] int not null, 
    [Seq] int not null, 
    [Amt] int not null 
) 

insert into [Test] values 
(1, 1, 500), 
(1, 2, 500), 
(1, 3, 500), 
(1, 5, 500), 
(2, 10, 600), 
(2, 11, 600), 
(3, 1, 700), 
(3, 3, 700) 

;with 
lower_bound as (
    select * 
     from Test 
    where not exists (
     select * 
      from Test as t1 
     where t1.id = Test.id and t1.Seq = Test.Seq - 1 
    ) 
), 
upper_bound as (
    select * 
     from Test 
    where not exists (
     select * 
      from Test as t1 
     where t1.id = Test.id and t1.Seq = Test.Seq + 1 
    ) 
), 
bounds as (
    select id, (select MAX(seq) from lower_bound where lower_bound.id = upper_bound.id and lower_bound.Seq <= upper_bound.Seq) as LBound, Seq as Ubound 
     from upper_bound 
) 
select Test.id, LBound As [Start], UBound As [End], SUM(Amt) As TotalAmt 
    from Test 
    join bounds 
    on Test.id = bounds.id 
    and Test.Seq between bounds.LBound and bounds.Ubound 
group by Test.id, LBound, UBound 

drop table [Test] 
+0

非常感謝您的努力。你的解決方案工作正常。 – Nagesh 2011-02-23 11:01:33

1

由於舍甫琴科已經貼金溶液,這裏是使用UPDATE語句來得到一個臨時表的結果我取,只是爲了好玩。

declare @tmp table (
    id int, seq int, amt money, start int, this int, total money, 
    primary key clustered(id, seq)) 
; 
insert @tmp 
select *, start=seq, this=seq, total=convert(money,amt) 
from btable 
; 
declare @id int, @seq int, @start int, @amt money 
update @tmp 
set 
    @amt = total = case when id = @id and seq = @seq+1 then @amt+total else amt end, 
    @start = start = case when id = @id and seq = @seq+1 then @start else seq end, 
    @seq = this = seq, 
    @id = id = id 
from @tmp 
option (maxdop 1) 
; 
select id, start, max(this) [end], max(total) total 
from @tmp 
group by id, start 
order by id, start 

注:

  • BTABLE:你的表名
  • ID INT,SEQ INT,AMT錢:在你的表所需的列
+0

它的工作原理,但這是一些新的SQL Server的東西給我。你已經設法發佈一些有價值的東西,只要玩得開心,謝謝。 :) – 2011-02-23 09:39:02

+0

'選擇(maxdop 1)'有特定的原因嗎?編輯:我想我只是想通了。你用'@ tmp'本身的數據更新'@ tmp'。多線程會干擾,對嗎? – 2011-02-23 10:15:39

+0

@Sem是的,因爲這是一個脆弱的查詢。在UPDATE查詢中沒有辦法使用ORDER BY,所以AFAIK應該能夠工作 - 但不能保證 - 如果我們按照需要的順序進行聚類,並且強制maxdop 1。**我再次強調**(並且也投了票),Andriy的答案是黃金之一。 – RichardTheKiwi 2011-02-23 10:18:24