返回 postgresql 中的第 n 个最大值

Returning the nth largest value in postgresql

我想 return 使用 for 循环 table 中的第 n 大薪水,但我仍然对语法有疑问。请帮忙

create function get_nth_max(n integer)
returns real
as
$$
declare
nth_max real = max(salary) from company;
pay real;
begin
for pay in select salary from company 
    order by salary = desc limit n 
    loop
        if pay.salary < nth_max then
        nth_max = pay.salary and
end loop;
return nth_max
end;
$$
language plpgsql;

错误信息:

ERROR:  syntax error at or near "desc"
LINE 10:  order by salary = desc limit n loop
                            ^
SQL state: 42601
Character: 182

你不需要为此使用 UDF,只需使用 DENSE_RANK:

WITH cte AS (
    SELECT *, DENSE_RANK() OVER (ORDER BY salary DESC) drnk
    FROM company
)

SELECT salary
FROM cte
WHERE drnk = <value of n here>

所以我缺少分号,没有结束我的 if-statement,还有一个 'dangling' AND。以下代码是工作版本:

create or replace function get_nth_max(n integer)
returns real
as
$$
declare
nth_max real = max(salary) from company;
pay record;
begin
for pay in select salary from company 
    order by salary desc limit n 
    loop
        if pay.salary < nth_max then
        nth_max = pay.salary;
        end if;
    end loop;
return nth_max;
end;
$$
language plpgsql;

首先你需要定义清楚“table中的第n大薪水”

重复项怎么办?如果两个人赚 1000,一个人赚 800,那么 800 是第二高还是第三高? salary 可以是 NULL 吗?如果是这样,忽略 NULL 值?如果行数不够怎么办?

假设...

  • 重复条目只计算 一次 - 所以在我的例子中 800 被认为是第二个。
  • salary 可以是 NULL。忽略 NULL 个值。
  • Return NULL 如果没有足够的行,则没有(没有行)。

正确的解决方案

SELECT salary
FROM  (
   SELECT salary, dense_rank() OVER (ORDER BY salary DESC NULLS LAST) AS drnk
   FROM   company
   ) sub
WHERE  drnk = 8;

这与 Tim 的回答基本相同,但是 NULLS LAST 阻止了 NULL 按降序排在第一位。显然,只有当薪水 可以 NULL 时才需要,但永远不会错。最好支持使用相同排序顺序的索引 DESC NULLS LAST。参见:

  • Sort by column ASC, but NULL values first?

FOR 循环作为概念验证

如果您坚持使用 FOR 循环进行训练,这将是最基本的正确形式:

CREATE OR REPLACE FUNCTION get_nth_max(n integer, OUT nth_max numeric)
  LANGUAGE plpgsql AS
$func$
BEGIN
   FOR nth_max IN
      SELECT DISTINCT salary
      FROM   company
      ORDER  BY salary DESC NULLS LAST
      LIMIT  n
   LOOP
      -- do nothing
   END LOOP;
END
$func$;

使用 numeric 而不是 real,因为我们不想使用 floating-point 数字作为货币价值。

您的版本做了很多不必要的工作。你不需要整体最大值,你不需要检查循环中的任何东西,只需 运行 通过它,最后的赋值将是结果。仍然效率低下,但没有那么多。

值得注意的是,如果只有9行,SELECT get_nth_max(10) returns NULL,而上面的SQL查询returns 没有行。可能相关的细微差别。 (你可以设计一个函数 RETURNS SETOF numeric to return no row for no result ...)

使用 OUT 参数来缩短语法。参见:

  • Returning from a function with OUT parameter