返回 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
我想 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