2011-02-23 11 views
13

Tengo una tabla como la siguiente:de consultas SQL para agrupar los resultados basados ​​en la secuencia

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 

que desea agrupar los números de secuencia continua en una sola fila de la siguiente manera:

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

Por favor, ayuda para lograr este resultado

+1

la versión de SQL ¿servidor? es (id + seq) único? – RichardTheKiwi

Respuesta

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 Me tomó un tiempo descubrir el truco '- Seq' y verificar (para mí) que no puede haber colisiones, pero OP no puede pedir nada mejor que esto. –

+0

+1 Tenía la sospecha de que ROW_NUMBER (o RANK, ...) ayudaría pero lo abandoné por mi solución (más complicada y probablemente más lenta). –

+0

@Lieven: Por favor, créanme cuando digo que con un elogio como este no se necesita ninguna votacion formal. :) ¡Gracias eres muy amable! Por cierto, no deberías haber eliminado tu respuesta. Una demostración de CTE recursiva más nunca es demasiado, en mi opinión, ya que es una técnica bastante complicada, que se aprende mejor con ejemplos. Además, no noté la presencia de Martin o Quassnoi aquí. :) –

0

Pruebe la siguiente consulta.

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

Vaya, lo siento, es equivocada consulta que necesite secuencia

1

Esto parece funcionar muy bien. @breakingRows contendrá todas las filas que rompan la secuencia de id y seq (es decir, si id cambia o si no es 1 más que el seq anterior). Con esa tabla puede seleccionar todas las filas de dicha secuencia dentro de @temp. Debo agregar, sin embargo, que el rendimiento probablemente no sea tan bueno debido a todas las subconsultas, pero tendrá que probar para estar seguro.

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

Gracias por el esfuerzo. ¡La solución está funcionando perfectamente! Pero puedo marcar solo una respuesta como Mejor respuesta. – Nagesh

+0

No hay problema. La solución de Andriy es claramente la mejor. –

1

Bueno, hay tal vez una forma más elegante de hacer esto (algo que alude a mí que hay), pero aquí es un enfoque que va a funcionar si está utilizando una versión de SQL Server que acepta expresiones de tabla comunes :

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

Muchas gracias por el esfuerzo. Tu solución está funcionando bien. – Nagesh

1

desde Andriy ya se ha publicado la solución de oro, aquí está mi opinión el uso de una instrucción UPDATE para obtener el resultado de una tabla temporal, sólo por diversión.

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 

Notas:

  • btable: el nombre de la tabla
  • int id, int siguientes, dinero AMT: se espera columnas en la tabla
+0

Funciona, pero eso es algo nuevo de SQL Server para mí. Has logrado publicar algo valioso con solo divertirte, gracias. :) –

+0

¿Hay alguna razón específica para la'opción (maxdop 1) '? EDIT: Creo que lo descubrí. Actualizas '@ tmp' con datos de' @ tmp' en sí. El multihilo interferiría con eso, ¿correcto? –

+0

@Sem Sí, porque esta es una consulta frágil. No hay forma de usar ORDER BY en una consulta UPDATE, por lo que AFAIK debería funcionar, pero no hay garantías, si agrupamos en el orden requerido y forzamos maxdop 1. ** Insisto nuevamente ** (y voté también), Andriy's la respuesta es la de oro – RichardTheKiwi

Cuestiones relacionadas