基于联接中的组合列获取用户排名的问题 table

Issue with getting the rank of a user based on combined columns in a join table

我有 users table,每个用户都有 flights table 的航班。每个航班在 airports table 内都有出发和到达机场关系。我需要做的是为每个用户计算出发和到达列(flights.departure_airport_idflights.arrival_airport_id)的唯一机场,然后通过 dense_rank 为他们分配一个排名,然后检索给定用户 ID 的排名。

基本上,我需要根据所有用户飞抵或飞出的唯一机场数量对所有用户进行排序,然后获得特定用户的排名。

这是我目前的情况:

SELECT u.rank FROM (
    SELECT 
        users.id,
        dense_rank () OVER (ORDER BY count(DISTINCT (flights.departure_airport_id, flights.arrival_airport_id)) DESC) AS rank
    FROM users 
    LEFT JOIN flights ON users.id = flights.user_id
    GROUP BY users.id
) AS u WHERE u.id = 'uuid';

这有效,但实际上 return 并没有达到预期的结果,因为 count(DISTINCT (flights.departure_airport_id, flights.arrival_airport_id)) 计算的是组合的机场 ID,而不是单独计算每个唯一的机场 ID。无论如何,我就是这样理解它的工作原理的……我猜我需要以某种方式在机场 ID 列上使用 UNION 连接,但不知道该怎么做。

我正在使用 Postgres 13.0。

您计算的是 (departure_airport_id、arrival_airpot_id) 的不同对。正如您所建议的,您可以使用 union 获取一列机场 ID(无论它们是出发机场还是到达机场),然后对其应用计数:

SELECT user_id, DENSE_RANK() OVER (ORDER BY cnt DESC) AS user_rank
FROM   (SELECT   u.id AS user_id, COALESCE(cnt, 0) AS cnt
        FROM     users u
        LEFT JOIN     (SELECT user_id, COUNT DISTINCT(airport_id) AS cnt
                       FROM   (SELECT user_id, departure_airport_id AS airport_id
                               FROM   flights
                               UNION
                               SELECT user_id, arrival_airport_id AS airport_id
                               FROM   flights) x
                       GROUP BY u.id) f ON u.id = f.user_id) t

我会推荐一个横向连接到 unpivot,然后聚合和排名:

select *
from (
    select f.user_id, 
        dense_rank() over(order by count(distinct a.airport_id) desc) rn
    from flights f 
    cross join lateral (values 
        (f.departure_airport_id), (f.arrival_airport_id)
    ) a(airport_id)
    group by f.user_id
) t
where user_id = 'uuid'

您并不真的需要 users table 来满足您的需求,除非您确实想让用户无需任何航班(他们都具有相同的最高级别)。如果是:

select *
from (
    select u.id, 
        dense_rank() over(order by count(distinct a.airport_id) desc) rn
    from users u
    left join flights f on f.user_id = u.id
    left join lateral (values 
        (f.departure_airport_id), (f.arrival_airport_id)
    ) a(airport_id) on true
    group by u.id
) t
where id = 'uuid'