2012-03-08 41 views
26

Tôi hy vọng sau mẫu mã là tự giải thích:Làm cách nào để kết hợp GROUP BY và 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 

Kết quả:

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

gì tôi cần là nhận xét ở trên, một cách để có được những ROW_NUMBER mà còn để Group By ở nơi đầu tiên. Vì vậy, tôi cần sum của tất cả giá T1 được nhóm theo T2.ID trong bảng quan hệ và trong truy vấn bên ngoài là t1ID với mức giá cao nhất.

Nói cách khác: Cách thay đổi MAX(Rel.t1ID)AS t1ID để phần nào trả về ID có giá cao nhất?

Vì vậy, các kết quả mong muốn là (lưu ý rằng T1ID đầu tiên thay đổi 2-1 vì nó có giá cao hơn):

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

Note: trong trường hợp bạn đang tự hỏi tại sao tôi không nhân Orders với Giá: chúng không được thực hiện (vì vậy tôi nên rời khỏi cột này vì nó hơi mơ hồ một chút, xin vui lòng bỏ qua nó, tôi vừa thêm nó để làm cho tất cả ít trừu tượng hơn). Trên thực tế, Orders phải không thay đổi, đó là lý do cho cách tiếp cận phụ truy vấn để tham gia cả hai và lý do tại sao tôi cần nhóm theo địa điểm đầu tiên.

Kết luận: rõ ràng là cốt lõi của câu hỏi của tôi có thể được trả lời bằng những OVER clause có thể được áp dụng cho bất kỳ chức năng tổng hợp như SUM (xem Damien's answer) những gì là mới mẻ với tôi. Cảm ơn tất cả vì cách tiếp cận làm việc của bạn.

+1

Không nên là 'AAA' trong kết quả cuối cùng thay vì' BBB'? –

Trả lời

50

Wow, câu trả lời khác trông phức tạp - vì vậy tôi hy vọng tôi đã không bỏ lỡ một cái gì đó rõ ràng.

Bạn có thể sử dụng OVER/PARTITION BY đối với tập hợp và sau đó họ sẽ nhóm/tổng hợp mà không cần khoản GROUP BY.Vì vậy, tôi vừa sửa đổi truy vấn của bạn thành:

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 

Cho kết quả được yêu cầu.

+1

Wow, đây là những gì tôi đã hy vọng và câu trả lời khác những gì tôi đã lo sợ. Cảm ơn (tất cả), tôi phải có một cái nhìn gần hơn để xem làm thế nào tôi có thể cài đặt nó vào truy vấn thực tế của tôi (mẫu của tôi là _very_ giảm). –

+1

+1 chắc chắn đơn giản hơn –

+0

Cảm ơn bạn! Tôi đã cố gắng tìm ra cách để thực hiện công việc này cho một vài chức năng cửa sổ khác nhau. Tôi tiếp tục đặt tổng trong phân vùng theo mệnh đề thay vì ở đầu! – BilliD

2

Chắc chắn điều này có thể được đơn giản hóa nhưng kết quả phù hợp với mong đợi của bạn.

Các ý chính của việc này là để

  • Tính giá tối đa trong một riêng biệt CTE cho mỗi t2ID
  • Tính tổng giá trong một riêng biệt CTE cho mỗi t2ID
  • Kết hợp các kết quả của cả hai CTE 's

Câu lệnh 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

Chống trùng lặp (để chọn tối đa T1) và tập hợp cần được thực hiện như các bước riêng biệt. Tôi đã sử dụng một CTE kể từ khi tôi nghĩ rằng điều này làm cho nó rõ ràng hơn:

;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