0

我有两个表(仅列出对问题重要的字段):

t_groups

  • INT groupId PRIMARY
  • VARCHAR(255) 语法

t_goods

  • INT goodId PRIMARY
  • INT 组 ID
  • INT价格
  • VARCHAR(255) 名称

现在我需要一个查询,它选择组名和每个组中最便宜的商品的名称。尝试这样做:

SELECT gr.groupId, grname, g.name
FROM t_groups AS gr
LEFT JOIN (SELECT * FROM t_goods ORDER BY PRICE ASC LIMIT 1) AS g
ON g.groupId = gr.groupId

但它不起作用——在 g.name 字段中返回 NULL。可以很容易地解释:

JOIN 语句中的 SELECT 首先选择最便宜的商品,然后尝试按 groupId 对其进行“过滤”。显然,它只适用于最便宜的商品所属的组。

我该如何解决任务?

4

2 回答 2

6

为什么您的查询不起作用

SELECT gr.groupId, grname, g.name
FROM t_groups AS gr
LEFT JOIN (SELECT * FROM t_goods ORDER BY PRICE ASC LIMIT 1) AS g
ON g.groupId = gr.groupId

内部查询选择数据库中绝对最便宜的商品(不考虑组)。因此,当您LEFT JOIN将此结果集分组时,只有实际包含普遍最便宜商品的组才有匹配的行(该组应该g.name正确填充列)。但是,由于LEFT JOIN工作方式的原因,所有其他组都将NULL作为g.

正确的解决方案

首先,您需要在每个组中选择最便宜的价格。这很简单:

SELECT groupId, MIN(price) AS minPrice FROM t_goods GROUP BY (groupId)

然而最便宜的价格是没有关联的goodId。问题是这样写是没有意义的:

/* does not make sense, although MySql has historically allowed it */
SELECT goodId, groupId, MIN(price) AS minPrice FROM t_goods GROUP BY (groupId)

原因是您不能选择未分组的列(即goodId),除非您将其包装在聚合函数(例如MIN)中:我们不知道您想要从共享相同的列中选择 一个。goodIdgroupId

goodId获取每组中最便宜商品的正确、便携方式是

SELECT goodId, temp.groupId, temp.minPrice
FROM (SELECT groupId, MIN(price) AS minPrice FROM t_goods GROUP BY groupId) temp
      JOIN t_goods ON temp.groupId = t_goods.groupId AND temp.minPrice = t_goods.price)

上面的查询首先找出每个组的最便宜的价格,然后再次加入到货物表中以查找该goodId组内具有该价格的商品的 s。

重要提示:如果多个商品在一组中具有相同的最便宜价格,则此查询将返回所有商品。如果您只希望每个组有一个结果,则必须指定决胜局,例如:

SELECT MIN(goodId), temp.groupId, MIN(temp.minPrice)
FROM (SELECT groupId, MIN(price) AS minPrice FROM t_goods GROUP BY groupId) temp
      JOIN t_goods ON temp.groupId = t_goods.groupId AND temp.minPrice = t_goods.price)
GROUP BY temp.groupId

有了这个查询,您就可以找到每组中最便宜的商品的名称和价格(最低的goodId将用作决胜局):

SELECT groupId, grname, gd.name, t3.minPrice
FROM t_groups AS gr
LEFT JOIN (SELECT MIN(goodId) AS goodId, t1.groupId, MIN(t1.minPrice) AS minPrice
           FROM (SELECT groupId, MIN(price) AS minPrice FROM t_goods GROUP BY groupId) t1
                 JOIN t_goods ON t1.groupId = t_goods.groupId AND t1.minPrice = t_goods.price
           ) t2
) t3 ON gr.groupId = t3.groupId
LEFT JOIN t_goods gd ON t3.goodId = gd.goodId

这个最终查询在其“外部”级别执行两个连接:

  • 使用“goodId每个组的最便宜的价格”表加入组以获得goodId最便宜的价格
  • 然后与商品表连接以获取商品的名称goodId

即使多种商品都以最便宜的价格捆绑在一起,它也将只生产一种商品。

于 2012-04-12T11:15:33.123 回答
1

你可以这样做:

select 
  t_groups.grname as `name of group`,
  t_goods.name as `name of good`
from (
  select 
    groupId, 
    min(price) as min_price
  from t_goods 
  group by groupId
) as mins
inner join t_goods
  on mins.groupId = t_goods.groupId and mins.min_price = t_goods.price
inner join t_groups
  on mins.groupId = t_groups.groupId

这是如何工作的:

  • mins子查询获取每个 groupId 的最低价格
  • 加入minst_goods拉出所有在他们的组中具有最低价格的商品。请注意,如果有多个最低价格的商品,这可能会返回一个组中的多个商品
  • 然后加入以t_groups获取组名

您的查询可能返回 NULL,因为它正在left join处理一个只有一行的子查询。

于 2012-04-12T11:15:54.540 回答