Самый простой, хотя и неэффективный (массив может исчерпать память):
select student, (array_agg(grade order by grade desc))[2]
from
student_grades
group by student
Эффективное одно:
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';
Данные испытаний:
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);
Тестовый код:
-- 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
Выход:
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)
EDIT @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;
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;
EDIT
Может быть простой и эффективный из всех, если только Postgresql бы array_max встроенную функцию и облегчает положение LIMIT на агрегатах :-) п LIMIT на агрегацию моя особенность мечта о Postgresql
select student, array_max(array_agg(grade order by grade limit 2))
from
student_grades
group by student;
Хотя это 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;
Устраните свой вопрос, чтобы сказать, какие строки вы хотите вернуть (например, сколько строк). Это поможет. – ijw