Как учитывать связи Postgresql rank ()

#sql #postgresql

#sql #postgresql

Вопрос:

У меня есть таблица teams с 30 строками, в которой хранится несколько статистических данных в виде атрибутов. Например, цели для, цели против и т. Д., И я создал представление, которое использует rank () и хорошо ранжирует записи. Вот сокращенный пример запроса и результирующая таблица:

 SELECT name,
   points, 
   rank() OVER (ORDER BY points DESC) AS point_tank
FROM teams;


         name          | points    | point_rank
----------------------- ----------- ----------------
 Team 1                |        14 |              1
 Team 2                |        11 |              2
 Team 3                |         9 |              3
 Team 4                |         9 |              3
  

Я хотел бы добавить дополнительный столбец, который возвращал бы логическое значение в зависимости от того, является ли ранг привязкой. например, команда 3 и команда 4 в этом примере. Это может выглядеть примерно так:

          name          | points    | point_rank     | tie
----------------------- ----------- ---------------- ----------------
 Team 1                |        14 |              1 |          false
 Team 2                |        11 |              2 |          false
 Team 3                |         9 |              3 |           true
 Team 4                |         9 |              3 |           true
  

Есть идеи? Или я неправильно подхожу к этому и злоупотребляю rank () здесь? Заранее спасибо!

Ответ №1:

Вы могли бы использовать CTE, а затем использовать функции задержки / опережения для проверки связей:

 with ranked as (
  SELECT name,
        points, 
        rank() OVER (ORDER BY points DESC) AS point_rank
  FROM teams
)
select name, points, point_rank, 
       (   point_rank = lag(point_rank, 1, -1::bigint) over (order by point_rank) 
        or point_rank = lead(point_rank, 1, -1::bigint) over (order by point_rank)
       ) as is_tie
from ranked;
  

Значение по умолчанию для функции задержки и опережения необходимо для первой и последней строки, чтобы избежать проверки там на null.

Пример: https://dbfiddle.uk/-01aFLr4

Комментарии:

1. Спасибо, это сработало и помогло мне лучше понять CTE.

2. Ссылка для примера предназначена только для Patreon, так что вот еще один вариант поиграть с ней: extendsclass.com/postgresql/b11b28f

3. Хорошая правка в dbfiddle, спасибо!

Ответ №2:

Одним из вариантов было бы поместить ваш текущий запрос в общее табличное выражение, а затем использовать его для определения того, какие ранги дублируются:

 WITH cte AS (
    SELECT name,
           points, 
           rank() OVER (ORDER BY points DESC) AS point_rank
    FROM teams;
)

SELECT cte.name,
       cte.points,
       cte.point_rank
       CASE WHEN t.point_rank IS NOT NULL THEN 'false' ELSE 'true' END AS tie
FROM cte
LEFT JOIN
(
    SELECT point_rank
    FROM cte
    GROUP BY point_rank
    HAVING COUNT(*) = 1
) t
    ON cte.point_rank = t.point_rank
  

Ответ №3:

 SELECT name
   , points
   , rank() OVER (rrr) AS point_rank
   -- , count(*) OVER (ppp) AS ppp_cnt
   , rank() OVER (pp2) AS sub_rank
   , (COUNT(*) OVER (ppp) > 1) AS is_tie
FROM teams
WINDOW ppp AS (PARTITION BY points )
        , pp2 AS (PARTITION BY points ORDER BY ctid )
        , rrr AS (ORDER BY points DESC)
ORDER BY points DESC
        ;
  

Результат (я добавил две дополнительные строки):


 DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 6
  name  | points | point_rank | sub_rank | is_tie 
-------- -------- ------------ ---------- --------
 Team_1 |     14 |          1 |        1 | f
 Team_2 |     11 |          2 |        1 | f
 Team_3 |      9 |          3 |        1 | t
 Team_4 |      9 |          3 |        2 | t
 Team_5 |      5 |          5 |        1 | t
 Team_6 |      5 |          5 |        2 | t
(6 rows)
  

Комментарии:

1. Это ORDER BY ctid кажется подозрительным, поскольку ctid изменится при обновлении строки (и ГОРЯЧИЕ обновления не могут быть использованы)

2. Да, это подозрительно, но оно не должно быть стабильным, оно используется только для устранения неоднозначности связей в «sub_rank». Но вместо этого вы могли бы использовать название команды или -number или дату подписки.