【问题标题】:Choosing or filtering columns based on ranks of multiple fields in PostgreSQL根据 PostgreSQL 中多个字段的排名选择或过滤列
【发布时间】:2017-12-21 19:45:12
【问题描述】:

在其中一张表中,我有多个字段,其中有一个排名字段。所有这些字段都有一个共同的分组属性,我需要根据该属性找到可以存在于组的任何记录中的排名最高的列值。例如,让我们考虑以下数据:

+---------+---------------+-----------+-----------------+-------------+----------------------+------------+
| Country |     City      | City_Rank |     Artist      | Artist_Rank |        Movie         | Movie_Rank |
+---------+---------------+-----------+-----------------+-------------+----------------------+------------+
| USA     | Las Vegas     |         2 | Louis C.K       |           2 | Justice League       |          3 |
| USA     | New York City |         3 | Michael Flynn   |           3 | IT                   |          1 |
| USA     | Los Angeles   |         1 | Matt Lauer      |           1 | Get Out              |          2 |
| UK      | Leeds         |         2 | Jack Maynard    |           3 | Beauty and the Beast |          2 |
| UK      | Manchester    |         3 | Charlie Gard    |           1 | Wonder Woman         |          1 |
| UK      | London        |         1 | Shannon Mathews |           2 | Logan                |          3 |
+---------+---------------+-----------+-----------------+-------------+----------------------+------------+

现在我需要 CityArtistMovie 的 Rank 1,按单个记录中的 Country 分组。所以预期的输出是:

+---------+------------------+--------------------+-------------------+
| Country | Best_Ranked_City | Best_Ranked_Artist | Best_Ranked_Movie |
+---------+------------------+--------------------+-------------------+
| USA     | Los Angeles      | Matt Lauer         | IT                |
| UK      | London           | Charlie Gard       | Wonder Woman      |
+---------+------------------+--------------------+-------------------+

我有更多的属性,我有排名字段。我可以通过为每个排名字段(其中 rank=1)形成上述多个数据集并通过组字段连接这些数据集来获得所需的输出。

但是,由于表中有数百万条记录,这是一件相当昂贵的事情,并且多次过滤和加入此数据集似乎并不是解决此问题的最佳方法。通过在其上应用一些业务逻辑,我使用Rank() windows 函数达到了每个字段的排名。

我希望仅在可能的情况下使用 Window 函数进一步解决此问题。

【问题讨论】:

  • 这听起来像是一个 SQL 问题,但代码是 html 格式的。如果是 sql 问题,您可以发布您需要帮助的 SQL 吗?如果不是,也许可以澄清一下究竟有什么帮助。
  • 我已经重新格式化为 ASCII 表 - 我想这就是你想要的?

标签: sql postgresql-9.4 rank


【解决方案1】:

我已经使用 Rank() 窗口到达每个字段的行列 通过在其上应用一些业务逻辑来发挥作用。

我猜有一些查询会计算排名,然后执行a pivot operation 以生成问题中显示的汇总表。
最好消除枢轴操作,以便此查询生成的输入数据如下所示:

| country | category |            cat_value | rank_value |
|---------|----------|----------------------|------------|
|      UK |   Artist |         Jack Maynard |          3 |
|      UK |   Artist |      Shannon Mathews |          2 |
|      UK |   Artist |         Charlie Gard |          1 |
|      UK |     City |                Leeds |          2 |
|      UK |     City |           Manchester |          3 |
|      UK |     City |               London |          1 |
|      UK |    Movie |                Logan |          3 |
|      UK |    Movie | Beauty and the Beast |          2 |
|      UK |    Movie |         Wonder Woman |          1 |
|     USA |   Artist |            Louis C.K |          2 |
|     USA |   Artist |        Michael Flynn |          3 |
|     USA |   Artist |           Matt Lauer |          1 |
|     USA |     City |            Las Vegas |          2 |
|     USA |     City |          Los Angeles |          1 |
|     USA |     City |        New York City |          3 |
|     USA |    Movie |       Justice League |          3 |
|     USA |    Movie |                   IT |          1 |
|     USA |    Movie |              Get Out |          2 |

如果这是不可能的,那么这个结果集可以使用:

SELECT Country, 'City' as category, City as cat_value, City_Rank as rank_value
FROM Table1
UNION ALL
SELECT Country, 'Artist' as category, Artist as cat_value, Artist_Rank as rank_value
FROM Table1
UNION ALL
SELECT Country, 'Movie' as category, Movie as cat_value, Movie_Rank as rank_value
FROM Table1

如果您取消透视此表,则选择 rank=1 的项目非常容易,只需执行以下操作:

SELECT * FROM unpivot_table WHERE rank_value = 1

然后可以对其结果进行另一个支点。


最终查询可能如下所示(现场演示:http://sqlfiddle.com/#!17/05e53/5

With unpivot_me As (
SELECT Country, 'City' as category, City as cat_value, City_Rank as rank_value
FROM Table1
UNION ALL
SELECT Country, 'Artist' as category, Artist as cat_value, Artist_Rank as rank_value
FROM Table1
UNION ALL
SELECT Country, 'Movie' as category, Movie as cat_value, Movie_Rank as rank_value
FROM Table1
)


SELECT Country,
       Max( case when category = 'City' Then cat_value End) As Best_Ranked_City,
       Max( case when category = 'Artist' Then cat_value End) As Best_Ranked_Artist,
       Max( case when category = 'Movie' Then cat_value End) As Best_Ranked_Movie
FROM unpivot_me
WHERE rank_value = 1 
GROUP BY Country

| country | best_ranked_city | best_ranked_artist | best_ranked_movie |
|---------|------------------|--------------------|-------------------|
|      UK |           London |       Charlie Gard |      Wonder Woman |
|     USA |      Los Angeles |         Matt Lauer |                IT |

【讨论】:

  • 感谢您的回复。我还没有应用透视操作来到达数据集。数据是我的基表,如我的帖子中所示,没有排名字段。我根据一些业务逻辑得出了排名。
  • 添加到我之前的评论:我的表有数百万条记录,我希望对大约 8-9 个字段进行排名。据我了解,这个数据集的联合 8-9 次将非常昂贵,并且可能类似于过滤排名为 1 的数据集然后加入它们。
  • 是的,反透视将非常昂贵。出于这个原因,最好更改计算排名并将它们分配给值的查询是这样一种方式,它会生成一个按行而不是像现在那样按列生成的结果集(如问题所示)。换句话说 - 分配排名的查询也必须重写。
  • 能够使用相同等级的窗口功能解决它。尝试使用 sqlfiddle 后发布了答案。假期后将尝试实际数据。感谢您的帮助:-)
【解决方案2】:

使用窗口函数 max() 并在其中放置了一个条件,其中排名为 1,按国家/地区划分。这为所有国家/地区的所需列获取了第一排名值。后来使用值为 1 的排名字段之一对其进行过滤(可以使用任何可用的排名字段进行过滤)。这是 SQL:http://sqlfiddle.com/#!17/05e53/18

With T1 as (
select Country, max(case when City_Rank =1 then City else '' end) 
over (partition by Country) as Best_Ranked_City, City_Rank, 
max(case when Artist_Rank =1 then Artist else '' end) 
over (partition by Country) as Best_Ranked_Artist, max(case when 
Movie_Rank =1 then Movie else '' end) 
over (partition by Country) as Best_Ranked_Movie 
from Table1
  )
select Country, Best_Ranked_City, Best_Ranked_Artist, Best_Ranked_Movie 
from T1 where city_rank=1;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多