7

我有一个 SQL 挑战,我需要一些帮助。

下面是一个简化的例子,在我的真实情况下,我在慢速视图中有大约 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   ║
╚═════════╩═══════╩═══════╝

我怎样才能以有效的方式实现这一目标?

4

4 回答 4

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

结果是:

| ORDERID |  NAME | PRICE |
---------------------------
|       1 |  Beer |     5 |
|       2 | Juice |   1.5 |
于 2013-01-18T11:34:07.843 回答
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
于 2013-01-18T11:22:22.637 回答
2

这是关系划分:链接 1链接 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.
于 2013-01-18T12:30:35.303 回答
1

如果您使用的是 Sql-Server 2005 或更高版本,您可以使用CTEwithDENSE_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

于 2013-01-18T11:24:25.307 回答