SQL:使用GROUP BY子句选择第二低的值(通过子查询)

时间:2014-01-28 01:12:08

标签: mysql sql group-by correlated-subquery

给定十进制值数据库,分组到位置......

CREATE TABLE ItemPrices
    (`Company` varchar(11), `LocationID` char(5), `ProductLevel` varchar(11), `LowPrice` decimal(5,2))
;

INSERT INTO ItemPrices
    (`Company`, `LocationID`, `LowPrice`)
VALUES
    ('Company 1', 10001, 100.00),
    ('Company 2', 10001, 200.00),
    ('Company 3', 10001, 300.00),
    ('Company 4', 10001, 400.00),
    ('Company 1', 10002, 10.00),
    ('Company 2', 10002, NULL),
    ('Company 3', 10002, 30.00),
    ('Company 4', 10002, 40.00)
;

可以轻松选择某个位置的最低值:

SELECT LocationID,min(LowPrice) FROM ItemPrices GROUP BY LocationID;

LOCATIONID  MIN(LOWPRICE)
10001       100
10002       10

但是使用子查询过滤掉最低值并返回下一个最低值并不能按预期工作:

SELECT LocationID,min(LowPrice) FROM ItemPrices
WHERE LowPrice >
(SELECT min(LowPrice)
    FROM ItemPrices)
GROUP BY LocationID;

LOCATIONID  MIN(LOWPRICE)
10001       100
10002       30

结果应该显示LocationID 10001的第二低价格为200,而不是100。

似乎子查询在所有情况下都返回最低LowPrice(10)。正确的逻辑是找到特定LocationID(100)的最低价格,然后为LocationID(200)找到更大的值并返回更大的值。

如何让子查询知道它应该只评估价格的一部分?

只需将GROUP BY放在那里(LowPrice > (SELECT min(LowPrice) FROM ItemPrices GROUP BY LocationID))告诉我Subquery returns more than 1 row - 当然,它会为每个LocationID返回一行。

2 个答案:

答案 0 :(得分:2)

在输入这个问题时,我发现我需要一个相关的子查询。第一步是对表ItemPrices进行别名。我使用i作为外部查询,s作为子查询。

SELECT i.LocationID,min(LowPrice) FROM ItemPrices i
WHERE
LowPrice >
(SELECT min(LowPrice)
 FROM ItemPrices s
WHERE i.LocationID = s.LocationID)
GROUP BY LocationID;

让我们来吧!

LOCATIONID  MIN(LOWPRICE)
10001       200
10002       30

看起来不错!你怎么想?

SQLFiddle:http://sqlfiddle.com/#!2/341ce/5

答案 1 :(得分:0)

使用>的问题将是最便宜的价格与两个或更多地点相关联的情况。所以也许所希望的行为是显示第二个最便宜的,即使技术上与第一个相同,但是这将显示第三个最便宜。

另一个解决方案是创建一个临时表并用最便宜的位置填充它,然后离开它们并删除所有匹配的记录。

#create a temp table to hold the cheapest
create temporary table t (ID int, MINPRICE int, LOCATIONID int);

#fill it with the very cheapest prices
insert into t select ID, min(LOWPRICE) as MINPRICE, LOCATIONID from `ItemPrices` group by LOCATIONID;

#select the second cheapest records by EXCLUDING the very cheapest
select ItemPrices.ID, min(ItemPrices.LOWPRICE) as MINPRICE, ItemPrices.LOCATIONID 
from ItemPrices
left join t 
       on t.ID = ItemPrices.ID
where t.ID is null
group by LOCATIONID;

这种方法的另一个优点是,通过将结果迭代到临时表中n-1次然后写出最后一次迭代作为结果,可以更容易地扩展以提取最便宜的Nth。

免责声明:我没有检查示例代码的语法,但理论是合理的。我在一张包含> 50M记录的桌子上遇到了这个记录,它快速而准确。