2012-08-17 84 views
5

的時候可以說我有一個包含許多類似的許多行的表:TSQL選擇最小和最大的行分組

ID  Range   Range_begining  Profit 
---------------------------------------------------- 
1 (100-150)     100   -20 
2 (200-250)     200   40.2 
3 (100-150)     100   100 
4 (450-500)     450   -90 
... 

我做這樣一個簡單的查詢:

SELECT max([Range]) AS 'Range' 
    , count(ID) AS 'Count' 
    , round(avg([Profit]), 2) AS 'AVG Profit' 
    FROM 
     Orders 
    GROUP BY 
     Range_begining 

在此之後運行查詢時,我得到的結果是這樣的:

Range  Count  AVG Profit 
------------------------------------ 
(100-150)  2    40 
(200-250)  1    40.2 
(450-500)  1    -90 
... 

很簡單:)

我現在需要做的是用最小的和最大的利潤,其中數是選擇行大於10(這是一個參數)

我能得到最小值與此:

SELECT TOP 1 [Range], [AVG Profit] FROM (
    SELECT max([Range]) AS 'Range' 
     , count(ID) AS 'Count' 
     , round(avg([Profit]), 2) AS 'AVG Profit' 
     FROM 
      Orders 
     GROUP BY 
      Range_begining) X 
WHERE 
    [Count]>10 
ORDER BY 
    [AVG Profit] ASC --or DESC if I want max profit 

我正在考慮使用ORDER BY DESC爲上述查詢做一個UNION,但這不是最好的解決方案。

我需要做的事情:
選擇2行:一個最小值,一個最大AVG利用Range進行分組時獲利。

編輯: 如果我加2移動列到我的主數據表是這樣的:

ID  Range   Range_begining  Profit  OrderDate  Company 
--------------------------------------------------------------------------------- 
1 (100-150)     100   -20  2012-01-02   1 
2 (200-250)     200   40.2  2012-03-22   0 
3 (100-150)     100   100  2012-02-05   0 
4 (450-500)     450   -90  2012-05-12   1 
... 

,然後嘗試添加2個條件是這樣的:

; with ordering as (
    SELECT max([Range]) AS 'Range' 
    , count(ID) AS 'Count' 
    , round(avg([Profit]), 2) AS 'AVG Profit' 
    , row_number() over (order by avg([Profit])) rn_min 
    , row_number() over (order by avg([Profit]) desc) rn_max 
    FROM 
     Orders 
    GROUP BY 
     Range_begining 
    HAVING COUNT(ID) > 10 
    AND [Company][email protected] 
    AND (@from= '' OR [OrderDate]>[email protected]) 
    AND (@to= '' OR [OrderDate]<[email protected]) 
) 
select [range], [count], [avg profit] 
    from ordering 
where (rn_max = 1 or rn_min = 1) 

我得到一個錯誤,因爲[公司]和[OrderDate]

在HAVING子句中無效,因爲它不包含在 集合函數或GROUP BY子句中。

我該如何解決這個問題?

EDIT2 明白了!

; with ordering as (
    SELECT max([Range]) AS 'Range' 
    , count(ID) AS 'Count' 
    , round(avg([Profit]), 2) AS 'AVG Profit' 
    , row_number() over (order by avg([Profit])) rn_min 
    , row_number() over (order by avg([Profit]) desc) rn_max 
    FROM 
     Orders 
    WHERE 
    [Company][email protected] 
    AND (@from= '' OR [OrderDate]>[email protected]) 
    AND (@to= '' OR [OrderDate]<[email protected]) 
    GROUP BY 
     Range_begining 
    HAVING COUNT(ID) > 10 
) 
select [range], [count], [avg profit] 
    from ordering 
where (rn_max = 1 or rn_min = 1) 

編輯3 我可以用描述返回另一列是這樣的:

Range  AVG Profit    Description 
------------------------------------------------- 
(200-250)   40.2   Max profit here 
(450-500)   -90  Min profit, well done 

EDIT 4 快速的答案(基於@NikolaMarkovinović答案):

; with ordering as (
    SELECT max([Range]) AS 'Range' 
    , count(ID) AS 'Count' 
    , round(avg([Profit]), 2) AS 'AVG Profit' 
    , row_number() over (order by avg([Profit])) rn_min 
    , row_number() over (order by avg([Profit]) desc) rn_max 
    FROM 
     Orders 
    WHERE 
    [Company][email protected] 
    AND (@from= '' OR [OrderDate]>[email protected]) 
    AND (@to= '' OR [OrderDate]<[email protected]) 
    GROUP BY 
     Range_begining 
    HAVING COUNT(ID) > 10 
) 
    SELECT 
    CASE WHEN rn_max=1 THEN 'This is max' ELSE 'Min' END AS 'Description' 
    ,[range] 
    ,[count] 
    ,[avg profit] 
    FROM ordering 
    WHERE (rn_max = 1 or rn_min = 1) 
+2

請務必在您的問題中添加'SQL'標籤以獲得更多關注。 – 2012-08-17 08:20:12

回答

7

你可能會使用window functions一次做到這一點:

; with ordering as (
    SELECT max([Range]) AS 'Range' 
    , count(ID) AS 'Count' 
    , round(avg([Profit]), 2) AS 'AVG Profit' 
    , row_number() over (order by avg([Profit])) rn_min 
    , row_number() over (order by avg([Profit]) desc) rn_max 
    FROM 
     Orders 
    GROUP BY 
     Range_begining 
    HAVING COUNT(ID) > 10 
) 
select [range], [count], [avg profit], 
     case when rn_max = 1 
      then 'Max profit' 
      else 'Min profit' 
     end Description 
    from ordering 
where (rn_max = 1 or rn_min = 1) 

And here is Sql Fiddle example

+1

我再次承認你真棒! :) – Misiu 2012-08-17 08:22:37

+0

@Misiu我只做了一些(有限的)測試,在我的情況下(700000發票,按客戶分組,發票價值平均),兩種方法工作都很快,但row_number()幾乎是union all的兩倍。 – 2012-08-17 08:35:36

+0

我正在測試它。它看起來像你的解決方案要快得多,分組只完成一次。我想知道SQL和你一樣;) – Misiu 2012-08-17 08:40:12

2

這是SQLFiddle example。在你的最後一個問題查詢,您可以使用具有代替嵌套查詢:

select * from 
(SELECT TOP 1 
    max([Range]) AS 'Range' 
    , count(ID) AS 'Count' 
    , round(avg([Profit]), 2) AS 'AVG Profit' 
    FROM 
     Orders 
    GROUP BY 
     Range_begining 
    having count(id)>10 
    order by round(avg([Profit]), 2) ASC 
) a 
union all 
select * from 
(
SELECT TOP 1 
    max([Range]) AS 'Range' 
    , count(ID) AS 'Count' 
    , round(avg([Profit]), 2) AS 'AVG Profit' 
    FROM 
     Orders 
    GROUP BY 
     Range_begining 
    having count(id)>10 
    order by round(avg([Profit]), 2) desc 
)b 
+1

感謝這麼快的答案:)我想知道這將是快速的。正如你可以看到頂部和底部部分幾乎相同(asc/desc正在改變),所以這可以做到嗎?我想到的一個選擇是將分組數據選擇到臨時表中,然後選擇最小和最大行。也許這會更快,除了你(和我的)解決方案,我們必須將數據分組2次。如果我有100行,沒關係,但如果我有10KK(1000萬),這將會有點長。 – Misiu 2012-08-17 08:17:17