1

我想返回基于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 的项目。

更新:这是使用下面答案的小提琴,它仍然会产生错误:

http://sqlfiddle.com/#!1/33786/2

4

3 回答 3

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,它允许您进行查询,但需要注意的是“服务器可以从每个组中自由选择任何值,因此除非它们相同,选择的值是不确定的”[链接]。)

于 2013-07-16T00:07:28.340 回答
3

SQL小提琴

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
于 2013-07-16T00:29:37.823 回答
2

SQL 标准需要这种行为,尽管像 MySQL 这样的一些数据库会忽略它,而是返回不可预测的结果

如果“cond = good”有不止一行,并且您询问“cond = good”所在行的“id”,数据库应该给您哪一行?id = 3 或 id = 2 的行?它应该如何知道选择哪个?如果有多个候选者,MySQL 会选择任意行,但标准不允许这样做。

在您的情况下,您似乎想为每个条件选择最低价格行。

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 方法,但你需要理解它。从这里的 PostgreSQL 文档开始。

附带说明一下,较新的 PostgreSQL 版本允许您引用表的任何列,其主键已在GROUP BY; 它们确定其他列对主键的功能依赖性。因此,如果您在较新版本中提到了 PK,则不必汇总其他列。这就是标准所要求的,但旧版本不够聪明,无法弄清楚并要求明确列出所有列。

这就是提出这个问题的人通常想知道的,但并不严格适用于您的问题,因为事实证明您正在尝试使用它GROUP BY来过滤行。

于 2013-07-16T01:33:06.060 回答