2013-01-18 16 views
6

我有一個SQL挑戰,我需要一點幫助。SQL - 如何按id進行分組並識別具有最高值的列?

下面是一個簡單的例子,在我的真實情況下,我有一個緩慢的VIEW約500k行。所以如果你有一個有效的解決方案,我將不勝感激。我想我必須以某種方式使用GROUP BY,但我不確定。

比方說,我有這樣的

╔═════════╦══════════╦══════════╦═══════╗ 
║ ORDERID ║ NAME ║ TYPE ║ PRICE ║ 
╠═════════╬══════════╬══════════╬═══════╣ 
║  1 ║ Broccoli ║ Food  ║ 1  ║ 
║  1 ║ Beer  ║ Beverage ║ 5  ║ 
║  1 ║ Coke  ║ Beverage ║ 2  ║ 
║  2 ║ Beef  ║ Food  ║ 2.5 ║ 
║  2 ║ Juice ║ Beverage ║ 1.5 ║ 
║  3 ║ Beer  ║ Beverage ║ 5  ║ 
║  4 ║ Tomato ║ Food  ║ 1  ║ 
║  4 ║ Apple ║ Food  ║ 1  ║ 
║  4 ║ Broccoli ║ Food  ║ 1  ║ 
╚═════════╩══════════╩══════════╩═══════╝ 

一個表,所以我想要做的是:

在每一個訂單,那裏有食物和飲料定單行,我想最高的飲料價格

所以在這個例子我想有一個結果集的這一點:

╔═════════╦═══════╦═══════╗ 
║ ORDERID ║ NAME ║ PRICE ║ 
╠═════════╬═══════╬═══════╣ 
║  1 ║ Beer ║ 5  ║ 
║  2 ║ Juice ║ 1.5 ║ 
╚═════════╩═══════╩═══════╝ 

如何以有效的方式實現這一目標?

+0

。 。因爲你說這個觀點很貴,我認爲你應該選擇波格丹的解決方案。我以前從未做過這樣的評論,但是您確實強調視圖的緩慢性,並且該解決方案是唯一一次只能掃描一次視圖的解決方案。 –

+0

是的,這是一個好的或者更好的答案。在我經歷波格丹之前,我已經選擇並實施了這個。然而,我的最終標準是要解決這個問題,所以我選擇了第一個也是最好的答案。但我理解你的意見。 – Rupal

回答

2

由於您已標記爲SQL Server,請使用Common Table ExpressionWindow Functions

;WITH filteredList 
AS 
(
    SELECT OrderID 
    FROM tableName 
    WHERE Type IN ('Food','Beverage') 
    GROUP BY OrderID 
    HAVING COUNT(DISTINCT Type) = 2 
), 
greatestList 
AS 
(
    SELECT a.OrderID, a.Name, a.Type, a.Price, 
      DENSE_RANK() OVER (PARTITION BY a.OrderID 
           ORDER BY a.Price DESC) rn 
    FROM tableName a 
      INNER JOIN filteredList b 
       ON a.OrderID = b.OrderID 
    WHERE a.Type = 'Beverage' 
) 
SELECT OrderID, Name, Type, Price 
FROM greatestList 
WHERE rn = 1 
+0

,所以這一個會返回記錄OrderID = 3:) – WKordos

+0

哦,它已經改變:) – WKordos

+0

這工作正常! – Rupal

1

如果您使用的SQL Server 2005或更高,您可以使用一個CTEDENSE_RANK功能:

WITH CTE 
    AS (SELECT orderid, 
       name, 
       type, 
       price, 
       RN = Dense_rank() 
         OVER ( 
         PARTITION BY orderid 
         ORDER BY CASE WHEN type='Beverage' THEN 0 ELSE 1 END ASC 
         , price DESC) 
     FROM dbo.tablename t 
     WHERE EXISTS(SELECT 1 
         FROM dbo.tablename t2 
         WHERE t2.orderid = t.orderid 
           AND type = 'Food') 
     AND EXISTS(SELECT 1 
         FROM dbo.tablename t2 
         WHERE t2.orderid = t.orderid 
           AND type = 'Beverage')) 
SELECT orderid, 
     name, 
     price 
FROM CTE 
WHERE rn = 1 
如果你希望所有的訂單

使用DENSE_RANK與最高價格相同,如果你想要的話ROW_NUMBER

DEMO

3

您可以使用一個獲取max(price)用於食品和飲料,然後每個訂單的子查詢加入該回你的表得到的結果:

select t1.orderid, 
    t1.name, 
    t1.price 
from yourtable t1 
inner join 
(
    select max(price) MaxPrice, orderid 
    from yourtable t 
    where type = 'Beverage' 
    and exists (select orderid 
       from yourtable o 
       where type in ('Food', 'Beverage') 
        and t.orderid = o.orderid 
       group by orderid 
       having count(distinct type) = 2) 
    group by orderid 
) t2 
    on t1.orderid = t2.orderid 
    and t1.price = t2.MaxPrice 

SQL Fiddle with Demo

結果是:

| ORDERID | NAME | PRICE | 
--------------------------- 
|  1 | Beer |  5 | 
|  2 | Juice | 1.5 | 
2

這是關係部門:link 1,link 2

如果除數表(只食品飲料)是靜態的,那麼你可以使用這些解決方案之一:

DECLARE @OrderDetail TABLE 
    ([OrderID] int, [Name] varchar(8), [Type] varchar(8), [Price] decimal(10,2)) 
; 

INSERT INTO @OrderDetail 
    ([OrderID], [Name], [Type], [Price]) 
SELECT 1, 'Broccoli', 'Food', 1.0 
UNION ALL SELECT 1, 'Beer', 'Beverage', 5.0 
UNION ALL SELECT 1, 'Coke', 'Beverage', 2.0 
UNION ALL SELECT 2, 'Beef', 'Food', 2.5 
UNION ALL SELECT 2, 'Juice', 'Beverage', 1.5 
UNION ALL SELECT 3, 'Beer', 'Beverage', 5.0 
UNION ALL SELECT 4, 'Tomato', 'Food', 1.0 
UNION ALL SELECT 4, 'Apple', 'Food', 1.0 
UNION ALL SELECT 4, 'Broccoli', 'Food', 1.0 

-- Solution 1 
SELECT od.OrderID, 
     COUNT(DISTINCT od.Type) AS DistinctTypeCount, 
     MAX(CASE WHEN od.Type='beverage' THEn od.Price END) AS MaxBeveragePrice 
FROM @OrderDetail od 
WHERE od.Type IN ('food', 'beverage') 
GROUP BY od.OrderID 
HAVING COUNT(DISTINCT od.Type) = 2 -- 'food' & 'beverage' 

-- Solution 2: better performance 
SELECT pvt.OrderID, 
     pvt.food AS MaxFoodPrice, 
     pvt.beverage AS MaxBeveragePrice 
FROM (
    SELECT od.OrderID, od.Type, od.Price 
    FROM @OrderDetail od 
    WHERE od.Type IN ('food', 'beverage') 
) src 
PIVOT (MAX(src.Price) FOR src.Type IN ([food], [beverage])) pvt 
WHERE pvt.food IS NOT NULL 
AND  pvt.beverage IS NOT NULL 

結果(方案1 & 2):

OrderID  DistinctTypeCount MaxBeveragePrice 
----------- ----------------- --------------------------------------- 
1   2     5.00 
2   2     1.50 

Table 'Worktable'. Scan count 2, logical reads 23, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table '#09DE7BCC'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

OrderID  MaxFoodPrice       MaxBeveragePrice 
----------- --------------------------------------- --------------------------------------- 
1   1.00         5.00 
2   2.50         1.50 

Table '#09DE7BCC'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
+0

+1。 。 .Yours是唯一一次掃描原始數據的解決方案。鑑於這個問題說源頭是一個緩慢的觀點,我不知道爲什麼這不是被接受的答案。 –

+0

@GordonLinoff:第二個解決方案(PIVOT,1次掃描)由[Razvan Socol](http://ro.linkedin.com/in/razvansocol)(以前的SQL Server MVP)提出。我將此解決方案用於生產存儲過程。 –

+0

Razvan提供了這個問題的答案嗎?第二種解決方案的一個變體是我如何回答這個問題。我在這裏看到的所有其他答案都會導致視圖的多次掃描。 –

相關問題