#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 или дату подписки.