2012-03-08 5 views
26

Spero seguente codice di esempio è auto-esplicativo:Come combinare GROUP BY e ROW_NUMBER?

declare @t1 table (ID int,Price money, Name varchar(10)) 
declare @t2 table (ID int,Orders int, Name varchar(10)) 
declare @relation table (t1ID int,t2ID int) 
insert into @t1 values(1, 200, 'AAA'); 
insert into @t1 values(2, 150, 'BBB'); 
insert into @t1 values(3, 100, 'CCC'); 
insert into @t2 values(1,25,'aaa'); 
insert into @t2 values(2,35,'bbb'); 
insert into @relation values(1,1); 
insert into @relation values(2,1); 
insert into @relation values(3,2); 

select T2.ID AS T2ID 
,T2.Name as T2Name 
,T2.Orders 
,T1.ID AS T1ID 
,T1.Name As T1Name 
,T1Sum.Price 
FROM @t2 T2 
INNER JOIN (
    SELECT Rel.t2ID 
     ,MAX(Rel.t1ID)AS t1ID 
-- the MAX returns an arbitrary ID, what i need is: 
--  ,ROW_NUMBER()OVER(Partition By Rel.t2ID Order By Price DESC)As PriceList 
     ,SUM(Price)AS Price 
     FROM @t1 T1 
     INNER JOIN @relation Rel ON Rel.t1ID=T1.ID 
     GROUP BY Rel.t2ID 
)AS T1Sum ON T1Sum.t2ID = T2.ID 
INNER JOIN @t1 T1 ON T1Sum.t1ID=T1.ID 

Risultato:

T2ID T2Name Orders T1ID T1Name Price  
1  aaa  25  2  BBB 350,00  
2  bbb  35  3  CCC 100,00 

cosa ho bisogno è commentata in precedenza, un modo per ottenere il ROW_NUMBER ma anche per Group By in primo luogo. Quindi ho bisogno dello sum di tutti i prezzi T1 raggruppati per T2.ID nella tabella delle relazioni e nella query esterna dello t1ID con il prezzo più alto.

In altre parole: come cambiare MAX(Rel.t1ID)AS t1ID in un po 'restituendo l'ID con il prezzo più alto?

Così il risultato desiderato è (si noti che prima T1ID cambiato 2-1 dal momento che ha il prezzo più elevato):

T2ID T2Name Orders T1ID T1Name Price  
1  aaa  25  1  AAA 350,00  
2  bbb  35  3  CCC 100,00 

Nota: nel caso in cui vi state chiedendo il motivo per cui non si moltiplicano Orders con Prezzo: non sono realizzati (quindi avrei dovuto lasciare questa colonna poiché è un po 'ambigua, per favore ignorala, l'ho appena aggiunta per rendere tutto meno astratto). In realtà, Orders deve rimanere invariato, questo è il motivo per cui l'approccio di sub-query è associato a entrambi e il motivo per cui ho bisogno di raggruppare in primo luogo.

Conclusione: ovviamente il nucleo della mia questione può essere risolta dalla OVER clause che può essere applicato a qualsiasi funzione di aggregazione come SUM (vedi Damien's answer) ciò che era nuovo per me. Grazie a tutti per i vostri approcci lavorativi.

+1

Non dovrebbe essere 'AAA' nel risultato finale invece di' BBB'? –

risposta

50

Wow, le altre risposte guardo complesso - quindi spero che non ho perso qualcosa di ovvio.

È possibile utilizzare OVER/PARTITION BY contro aggregati e quindi eseguire il raggruppamento/aggregazione senza una clausola GROUP BY.Così si può modificare la query a:

select T2.ID AS T2ID 
,T2.Name as T2Name 
,T2.Orders 
,T1.ID AS T1ID 
,T1.Name As T1Name 
,T1Sum.Price 
FROM @t2 T2 
INNER JOIN (
    SELECT Rel.t2ID 
     ,Rel.t1ID 
--  ,MAX(Rel.t1ID)AS t1ID 
-- the MAX returns an arbitrary ID, what i need is: 
     ,ROW_NUMBER()OVER(Partition By Rel.t2ID Order By Price DESC)As PriceList 
     ,SUM(Price)OVER(PARTITION BY Rel.t2ID) AS Price 
     FROM @t1 T1 
     INNER JOIN @relation Rel ON Rel.t1ID=T1.ID 
--  GROUP BY Rel.t2ID 
)AS T1Sum ON T1Sum.t2ID = T2.ID 
INNER JOIN @t1 T1 ON T1Sum.t1ID=T1.ID 
where t1Sum.PriceList = 1 

che dà il set di risultati richiesto.

+1

Wow, questo è quello che speravo e l'altro risponde a ciò che temevo. Grazie (tutto), devo dare un'occhiata più da vicino per vedere come posso installarlo nella mia query reale (il mio campione è _molto_ ridotto). –

+1

+1 decisamente più semplice –

+0

Grazie! Ho cercato di capire come farlo funzionare per alcune diverse funzioni di windowing. Ho continuato a mettere la somma nella partizione per clausola invece che all'inizio! – BilliD

2

Indubbiamente questo può essere semplificato ma i risultati corrispondono alle vostre aspettative.

La sostanza di questo è di

  • Calcolare il prezzo massimo in una separata CTE per ogni t2ID
  • Calcolare il prezzo totale in un separato CTE per ogni t2ID
  • Combinare i risultati di entrambi CTE 's

Istruzione SQL

;WITH MaxPrice AS ( 
    SELECT t2ID 
      , t1ID 
    FROM (  
       SELECT t2.ID AS t2ID 
         , t1.ID AS t1ID 
         , rn = ROW_NUMBER() OVER (PARTITION BY t2.ID ORDER BY t1.Price DESC) 
       FROM @t1 t1 
         INNER JOIN @relation r ON r.t1ID = t1.ID   
         INNER JOIN @t2 t2 ON t2.ID = r.t2ID 
      ) maxt1 
    WHERE maxt1.rn = 1        
) 
, SumPrice AS (
    SELECT t2ID = t2.ID 
      , Price = SUM(Price) 
    FROM @t1 t1 
      INNER JOIN @relation r ON r.t1ID = t1.ID 
      INNER JOIN @t2 t2 ON t2.ID = r.t2ID 
    GROUP BY 
      t2.ID   
)   
SELECT t2.ID 
     , t2.Name 
     , t2.Orders 
     , mp.t1ID 
     , t1.ID 
     , t1.Name 
     , sp.Price 
FROM @t2 t2 
     INNER JOIN MaxPrice mp ON mp.t2ID = t2.ID 
     INNER JOIN SumPrice sp ON sp.t2ID = t2.ID 
     INNER JOIN @t1 t1 ON t1.ID = mp.t1ID 
2

La deduplicazione (per selezionare il T1 max) e l'aggregazione deve essere effettuata fasi distinte. Ho usato un CTE poiché credo che questo lo rende più chiaro:

;WITH sumCTE 
AS 
(
    SELECT Rel.t2ID, SUM(Price) price 
    FROM @t1   AS T1 
    JOIN @relation AS Rel 
    ON  Rel.t1ID=T1.ID 
    GROUP 
    BY  Rel.t2ID 
) 
,maxCTE 
AS 
(
    SELECT Rel.t2ID, Rel.t1ID, 
      ROW_NUMBER()OVER(Partition By Rel.t2ID Order By Price DESC)As PriceList 
    FROM @t1   AS T1 
    JOIN @relation AS Rel 
    ON  Rel.t1ID=T1.ID 
) 
SELECT T2.ID AS T2ID 
,T2.Name as T2Name 
,T2.Orders 
,T1.ID AS T1ID 
,T1.Name As T1Name 
,sumT1.Price 
FROM @t2 AS T2 
JOIN sumCTE AS sumT1 
ON  sumT1.t2ID = t2.ID 
JOIN maxCTE AS maxT1 
ON  maxT1.t2ID = t2.ID 
JOIN @t1 AS T1 
ON  T1.ID = maxT1.t1ID 
WHERE maxT1.PriceList = 1 
2
;with C as 
(
    select Rel.t2ID, 
     Rel.t1ID, 
     t1.Price, 
     row_number() over(partition by Rel.t2ID order by t1.Price desc) as rn 
    from @t1 as T1 
    inner join @relation as Rel 
     on T1.ID = Rel.t1ID 
) 
select T2.ID as T2ID, 
     T2.Name as T2Name, 
     T2.Orders, 
     T1.ID as T1ID, 
     T1.Name as T1Name, 
     T1Sum.Price 
from @t2 as T2 
    inner join (
       select C1.t2ID, 
        sum(C1.Price) as Price, 
        C2.t1ID 
       from C as C1 
       inner join C as C2 
        on C1.t2ID = C2.t2ID and 
        C2.rn = 1 
       group by C1.t2ID, C2.t1ID 
      ) as T1Sum 
    on T2.ID = T1Sum.t2ID 
    inner join @t1 as T1 
    on T1.ID = T1Sum.t1ID