Выберите строку со вторым по величине значением внутри группы - PullRequest
1 голос
/ 03 июня 2019

У меня есть таблица как таковая (tbl):

+----+-----+------+-----+
| pk | grp | attr | val |
+----+-----+------+-----+
|  0 |   0 | ohif |   4 |
|  1 |   0 | foha |  56 |
|  2 |   0 | slns |   2 |
|  3 |   1 | faso |  11 |
|  4 |   1 | tepj |   4 |
|  5 |   2 | bnda |  12 |
|  6 |   2 | ojdf |   9 |
|  7 |   2 | anaw |   1 |
+----+-----+------+-----+

Я хотел бы выбрать одну строку из каждой группы, в частности, строку с наивысшим значением val для каждой группы.

Т.е., я хочу эту таблицу:

+----+-----+------+-----+
| pk | grp | attr | val |
+----+-----+------+-----+
|  1 |   0 | ohif |   4 |
|  3 |   1 | tepj |   4 |
|  5 |   2 | ojdf |   9 |
+----+-----+------+-----+

Вот решение, которое я придумал:

SELECT DISTINCT ON (grp)
pk,
(
    SELECT innertbl.grp
    FROM tbl AS innertbl
    WHERE innertbl.grp = tbl.grp
    ORDER BY innertbl.val DESC
    LIMIT 1 OFFSET 1
) AS grp,
(
    SELECT innertbl.attr
    FROM tbl AS innertbl
    WHERE innertbl.grp = tbl.grp
    ORDER BY innertbl.val DESC
    LIMIT 1 OFFSET 1
) AS attr,
(
    SELECT innertbl.val
    FROM tbl AS innertbl
    WHERE innertbl.grp = tbl.grp
    ORDER BY innertbl.val DESC
    LIMIT 1 OFFSET 1
) AS val
FROM tbl

Однако это неэффективно, так как для каждого столбца требуется подзапрос для каждого столбца.

Я на Postgres 10.

Ответы [ 2 ]

1 голос
/ 03 июня 2019

Вы можете использовать оконную функцию внутри подзапроса, чтобы получить то, что вам нужно:

SELECT
    pk, grp, attr, val
FROM (
    SELECT
        pk, grp, attr, val,
        row_number() OVER(PARTITION BY grp ORDER BY val DESC) AS seq
    FROM
        tbl
    ) data
WHERE
    seq = 2

row_number () обеспечивает последовательность внутри группы (используя PARTITION BY).

0 голосов
/ 03 июня 2019

Ответ @Nick был тем, что я собирался опубликовать, но чтобы добавить одну вещь, которую я бы сказал, если вы хотите пропустить дубликаты, вы можете использовать функцию dens_rank :

SELECT pk, grp, attr, val FROM (
  SELECT
    *,
    DENSE_RANK() OVER (PARTITION BY grp ORDER BY val DESC) AS seqnum
  FROM
    mytable
) t WHERE seqnum = 2;
...