获取具有最高价值的顶行,有联系

Get top row(s) with highest value, with ties

我在 PostgreSQL 中有一个名为 product 的关系,它包含 2 个字段:idquantity

我想找到 id 最高 quantity 的产品。据我所知,有两种方法可以做到:

SELECT id FROM product WHERE quantity >= ALL(SELECT quantity FROM product)

SELECT id FROM product WHERE quantity = (SELECT MAX(quantity) FROM product)

他们的执行速度有什么不同吗?

我在 postgres 中尝试了您的方法(测试 table 由 id 分发)。第一种方法 运行 对我来说要慢得多。这是我的比较结果:

上述方法一:3.1秒

上面方法2:0.13秒

方法一在反复的努力中至少慢了10倍。我认为您的方法 2 是更好的选择,因为子查询的运行速度可能比其他选项中的子查询快得多。

您的查询不等同。如果 quantity 值中的 anyNULL,则第一个 returns 根本没有行。第二个忽略 NULL 值。

Here 是一个 db<>fiddle 说明这一点。

有第三种变体

SELECT id FROM product 
WHERE quantity = (SELECT quantity FROM product ORDER BY quantity DESC NULLS LAST LIMIT 1)

如果 table 的 btree 索引为(数量 DESC NULLS LAST),此变体将非常快

如果任何行有 quantity IS NULL(如 ),则第一个查询失败。
仅当所有行都具有 quantity IS NULL 时,第二个查询才会失败。所以它应该在大多数情况下可用。 (而且速度更快。)

Postgres 13 或更新版本

使用标准 SQL 子句 WITH TIES:

SELECT id
FROM   product
ORDER  BY quantity DESC NULLS LAST
FETCH  FIRST 1 ROWS WITH TIES;

db<>fiddle here

适用于任意数量的 NULL 个值。

The manual:

SQL:2008 introduced a different syntax to achieve the same result, which PostgreSQL also supports. It is:

OFFSET start { ROW | ROWS }
FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES }

In this syntax, the start or count value is required by the standard to be a literal constant, a parameter, or a variable name; as a PostgreSQL extension, other expressions are allowed, but will generally need to be enclosed in parentheses to avoid ambiguity. If count is omitted in a FETCH clause, it defaults to 1. The WITH TIES option is used to return any additional rows that tie for the last place in the result set according to the ORDER BY clause; ORDER BY is mandatory in this case. ROW and ROWS as well as FIRST and NEXT are noise words that don't influence the effects of these clauses.

值得注意的是,WITH TIES 不能与(非标准)短语法 LIMIT n.

一起使用

这是可能最快的解决方案。比您当前的任何一个查询都快。对于 性能 更重要:在 (quantity) 上有一个 index。或者更专业的覆盖索引以允许仅索引扫描(更快一点):

CREATE INDEX ON product (quantity DESC NULLS LAST) INCLUDE (id);

参见:

我们需要 NULLS LAST 来保持 NULL 值按降序排列在最后。参见:

  • Sort by column ASC, but NULL values first?

Postgres 12 或更早版本

NULL 安全查询:

SELECT id, quantity
FROM   product
WHERE  quantity IS NOT DISTINCT FROM (SELECT MAX(quantity) FROM product);

或者,可能更快:

SELECT id, quantity
FROM  (
   SELECT *, rank() OVER (ORDER BY quantity DESC NULLS LAST) AS rnk
   FROM   product
   ) sub
WHERE  rnk = 1;

参见:

  • PostgreSQL equivalent for TOP n WITH TIES: LIMIT "with ties"?

大表的更快替代品: