试图找到列中的第二大值(postgres sql)

13 sql postgresql greatest-n-per-group

我试图找到列中的第二大值,只有第二大值.

select a.name, max(a.word) as word
from apple a
where a.word < (select max(a.word) from apple a)
group by a.name;
Run Code Online (Sandbox Code Playgroud)

出于某种原因,我现在所返回的第二大值和所有较低的值也是幸运的,但幸运的是避免了最大值.

有没有办法来解决这个问题?

小智 16

根据EXPLAIN ANALYZE的说法,这是另一个概念上简单的解决方案,它在2.1百万行的表中以0.1毫秒的速度运行.在只有一个值的情况下,它不返回任何内容.

SELECT a.name, 
(SELECT word FROM apple ap WHERE ap.name=a.name ORDER BY word ASC OFFSET 1 LIMIT 1) 
FROM apple a
Run Code Online (Sandbox Code Playgroud)

请注意,我的表已经有名称,单词和(名称,单词)的现有索引,这允许我像这样使用ORDER BY.


Mic*_*uen 5

最简单,尽管效率低(阵列可以耗尽内存):

select student, (array_agg(grade order by grade desc))[2]
from 
student_grades
group by student
Run Code Online (Sandbox Code Playgroud)

高效的:

create aggregate two_elements(anyelement)
(
sfunc = array_limit_two,
stype = anyarray,
initcond = '{}'
);

create or replace function array_limit_two(anyarray, anyelement) returns anyarray
as 
$$
begin
    if array_upper($1,1) = 2 then
        return $1;
    else
        return array_append($1, $2);
    end if;
end;
$$ language 'plpgsql';
Run Code Online (Sandbox Code Playgroud)

测试数据:

create table student_grades
(
student text,
grade int
);



insert into student_grades values 
('john',70),
('john',80),
('john',90),
('john',100);


insert into student_grades values
('paul',20),
('paul',10),
('paul',50),
('paul',30);


insert into student_grades values
('george',40);
Run Code Online (Sandbox Code Playgroud)

测试代码:

-- second largest
select student, coalesce( (two_elements(grade order by grade desc))[2], max(grade) /* min would do too, since it's one element only */ )
from 
student_grades
group by student


-- second smallest
select student, coalesce( (two_elements(grade order by grade))[2], max(grade) /* min would do too, since it's one element only */ )
from 
student_grades
group by student
Run Code Online (Sandbox Code Playgroud)

输出:

q_and_a=# -- second largest
q_and_a=# select student, coalesce( (two_elements(grade order by grade desc))[2], max(grade) /* min would do too, since it's one element only */ )
q_and_a-# from
q_and_a-# student_grades
q_and_a-# group by student;
 student | coalesce
---------+----------
 george  |       40
 john    |       90
 paul    |       30
(3 rows)


q_and_a=#
q_and_a=# -- second smallest
q_and_a=# select student, coalesce( (two_elements(grade order by grade))[2], max(grade) /* min would do too, since it's one element only */ )
q_and_a-# from
q_and_a-# student_grades
q_and_a-# group by student;
 student | coalesce
---------+----------
 george  |       40
 john    |       80
 paul    |       20
(3 rows)
Run Code Online (Sandbox Code Playgroud)

编辑 @diesel最简单(也有效):

-- second largest
select student, array_min(two_elements(grade order by grade desc))
from 
student_grades
group by student;

-- second smallest
select student, array_max(two_elements(grade order by grade))
from 
student_grades
group by student;
Run Code Online (Sandbox Code Playgroud)

array_max函数:

create or replace function array_min(anyarray) returns anyelement
as
$$
select min(unnested) from( select unnest($1) unnested ) as x
$$ language sql;

create or replace function array_max(anyarray) returns anyelement
as
$$
select max(unnested) from( select unnest($1) unnested ) as x
$$ language sql;
Run Code Online (Sandbox Code Playgroud)

编辑

可能是最简单有效的,如果只有Postgresql会使array_max成为内置函数并在聚合上促进LIMIT子句:-)聚合上的LIMIT子句是我在Postgresql上的梦想功能

select student, array_max( array_agg(grade order by grade limit 2) )
from 
student_grades
group by student;
Run Code Online (Sandbox Code Playgroud)

虽然聚合的LIMIT尚不可用,但请使用:

-- second largest
select student, 

    array_min
    (

        array ( 
               select grade from student_grades 
               where student = x.student order by grade desc limit 2 )

    )

from 
student_grades x
group by student;


-- second smallest
select student, 

    array_max
    (

        array ( 
               select grade from student_grades 
               where student = x.student order by grade limit 2 )

    )

from 
student_grades x
group by student;
Run Code Online (Sandbox Code Playgroud)

  • 我讨论了涉及pgplsql函数的任何内容都是"简单"的陈述.;) (2认同)

Ric*_*iwi 0

一个非常暴力的查询,但它有效

select a.name, a.word
from apple a
where (select count(distinct b.word) from apple b
    where b.word > a.word) = 1
Run Code Online (Sandbox Code Playgroud)