我想根据condition
返回基于price asc
排序的唯一项目。我的查询失败,因为Postgres希望items.id
子句中存在group by
。如果包含该查询,则返回与where
子句匹配的所有内容,这不是我想要的。为什么我需要包含该列?
select items.*
from items
where product_id = 1 and items.status = 'in_stock'
group by condition /* , items.id returns everything */
order by items.price asc
| id | condition | price |
--------------------------
| 1 | new | 9 |
| 2 | good | 5 |
| 3 | good | 3 |
我只想要带有ID 1和3的项目。
更新:使用下面的答案,这是一个小提琴,仍然会产生错误:
答案 0 :(得分:3)
问题在于PostgreSQL无法知道您想从哪个items
记录中获取值;也就是说,它无法说明你想要这个:
| id | condition | price |
--------------------------
| 1 | new | 9 |
| 3 | good | 3 |
而不是这个:
| id | condition | price |
--------------------------
| 1 | new | 9 |
| 2 | good | 5 |
要解决此问题,您需要使用某种聚合函数,例如MAX
:
SELECT MAX(id) AS id,
condition,
MAX(price) AS price
FROM items
WHERE product_id = 1
AND status = 'in_stock'
GROUP BY condition
ORDER BY price ASC
给出:
| id | condition | price |
--------------------------
| 1 | new | 9 |
| 3 | good | 5 |
(这个限制是SQL标准的一部分,大多数DBMS都强制执行它。一个例外是MySQL,它允许你的查询,但有一点需要注意“服务器可以自由选择每个组的任何值,所以除非他们如果是相同的,所选择的值是不确定的“[link]。”
答案 1 :(得分:3)
select *
from (
select distinct on (cond)
id, cond, price
from items
where product_id = 1 and items.status = 'in_stock'
order by cond, price
) s
order by price
答案 2 :(得分:2)
SQL标准需要这种行为,尽管像MySQL这样的某些数据库会忽略它,而是返回不可预测的结果。
如果“cond = good”有多行,你要求“cond = good”行的“id”,数据库应该给你哪一行? id = 3或id = 2的行?怎么知道选哪个? MySQL picks an arbitrary row if there are multiple candidates,但标准不允许这样做。
在您的情况下,您似乎想为每种情况选择价格最低的行。
PostgreSQL提供了一个扩展名DISTINCT ON ...
来帮助解决这个问题。 Clodaldo在他的回答中证明了这一点,所以我在此不再重复。使用DISTINCT ON
将比下面的示例更有效。
SQL标准方法是使用窗口对结果进行排名,然后对排名数据进行过滤。不幸的是,这是非常低效的,因为它需要收集和排序匹配内部where子句的所有行。
SELECT *
FROM (
SELECT *, dense_rank() OVER w AS itemrank
FROM items
WHERE product_id = 1 AND items.status = 'in_stock'
WINDOW w AS (PARTITION BY cond ORDER BY price ASC)
) ranked_items
WHERE itemrank = 1;
(http://sqlfiddle.com/#!1/33786/19)
另一种SQL标准方法是使用聚合子查询来查找每个类别的最小价格,然后以最小价格显示所有行:
SELECT *
FROM items INNER JOIN (
SELECT cond, min(price) AS minprice
FROM items
WHERE product_id = 1 AND items.status = 'in_stock'
GROUP BY cond
) minprices(cond, price)
ON (items.price = minprices.price AND items.cond = minprices.cond)
ORDER BY items.price;
与DISTINCT ON
版本不同,如果最低价格的商品具有多个具有相同条件和价格的条目,则会显示多个条目。
所以..你应该真的使用DISTINCT ON方法,但你需要理解它。开始with the PostgreSQL documentation here。
另外,较新的PostgreSQL版本允许您引用您在GROUP BY
中列出主键的表的任何列;它们识别主键上其他列的功能依赖性。因此,如果您在较新版本中提到过PK,则不必聚合其他cols。这就是标准所要求的,但旧版本不够聪明,无法明确地列出所有列。
这就是提出此问题的人通常想知道的内容,但并不严格适用于您的问题,因为事实证明您正在尝试使用GROUP BY
来过滤行。