максимальная функция с более чем одним результатом для каждой группы

#sql #postgresql

#sql #postgresql

Вопрос:

Я ищу помощь с запросом postgresql.

Пример входных данных выглядит следующим образом:

 pk  name
=========================
1   | notebook cz-2001
2   | notebook cz-2002
3   | notebook cz-2003
4   | notebook cz-2003
5   | notebook cz-2003
6   | notebook cz-2004
7   | notebook cz-2004
8   | notebook cz-2004
9   | notebook cz-2004
10  | notebook cz-2005
11  | notebook cz-2006
12  | notebook cz-2007
13  | notebook cz-2008
14  | notebook cz-2009
  

Некоторые строки имеют одинаковое значение в столбце name (notebook cz-2003, notebook cz-2004)

Я могу получить одно максимальное значение pk для сгруппированных имен с помощью этого запроса:

 select * from test_group where pk in
(
select max(pk) from test_group group by name order by name
)
  

Результат:

 pk  name
=========================
1   | notebook cz-2001
2   | notebook cz-2002
5   | notebook cz-2003
9   | notebook cz-2004
10  | notebook cz-2005
11  | notebook cz-2006
12  | notebook cz-2007
13  | notebook cz-2008
14  | notebook cz-2009
  

но я понятия не имею, и это мой вопрос, как получить, например, два (три, четыре …) наивысших значения pk для сгруппированных имен.

Ожидаемый результат, например, для двух самых высоких pk:

 pk  name
=========================
1   | notebook cz-2001
2   | notebook cz-2002
4   | notebook cz-2003
5   | notebook cz-2003
8   | notebook cz-2004
9   | notebook cz-2004
10  | notebook cz-2005
11  | notebook cz-2006
12  | notebook cz-2007
13  | notebook cz-2008
14  | notebook cz-2009
  

Не могли бы вы дать мне несколько советов, как этого добиться?

Спасибо

JP

Ответ №1:

Для этого можно использовать row_number() функцию window:

демонстрация: db<>скрипка

 SELECT pk, name
FROM
(
    SELECT *, ROW_NUMBER() OVER (PARTITION BY name ORDER BY pk DESC)
    FROM my_table
) s
WHERE row_number <= 2    -- or any other value
  

row_number() оконная функция добавляет номер строки в определенную упорядоченную группу. Здесь ваша группа — это name столбец, а порядок — по pk (по убыванию, потому что вы хотите, чтобы сначала считались самые высокие числа).

С WHERE помощью предложения вы можете отфильтровать первые n записей (которые были пронумерованы 1, 2, …) или любые другие элементы, которые вам нравятся.

Ответ №2:

Одним из методов являются оконные функции:

 select tg.*
from (select tg.*,
             row_number() over (partition by name order by pk desc) as seqnum
      from test_group tg
     ) tg
where seqnum <= ?;
  

Другой метод — более соответствующий вашему текущему подходу — это коррелированный подзапрос:

 select tg.*
from test_group tg
where tg.pk >= any (select tg2.pk
                    from test_group tg2
                    where tg2.name = tg.name
                   );