【问题标题】:Postgres 基于另一个表创建具有列值的视图?
【发布时间】:2022-01-23 16:14:00
【问题描述】:

我正在实现一个视图来存储使用昂贵的COUNT(*) 计算的前 10 名用户的排行榜数据。我计划让视图看起来像这样:

id SERIAL PRIMARY KEY
user_id TEXT
type TEXT
rank INTEGER
count INTEGER

-- adding an index to user_id
-- adding a two-column unique index to user_id and type

我无法了解如何创建此视图以正确说明ranktype。本质上,我有一个像这样的大表(约 3000 万行):

+----+---------+---------+----------------------------+
| id | user_id |  type   |         created_at         |
+----+---------+---------+----------------------------+
|  1 |       1 | Diamond | 2021-05-11 17:35:18.399517 |
|  2 |       1 | Diamond | 2021-05-12 17:35:17.399517 |
|  3 |       1 | Diamond | 2021-05-12 17:35:18.399517 |
|  4 |       2 | Diamond | 2021-05-13 17:35:18.399517 |
|  5 |       1 | Clay    | 2021-05-14 17:35:18.399517 |
|  6 |       1 | Clay    | 2021-05-15 17:35:18.399517 |
+----+---------+---------+----------------------------+

通过上面的表格,我正在尝试实现这样的目标:

+----+---------+---------+------+-------+
| id | user_id |  type   | rank | count |
+----+---------+---------+------+-------+
|  1 |       1 | Diamond |    1 |     3 |
|  2 |       2 | Diamond |    2 |     1 |
|  3 |       1 | Clay    |    1 |     2 |
|  4 |       1 | Weekly  |    1 |     5 | -- 3 diamonds + 2 clay obtained between Mon-Sun
|  5 |       2 | Weekly  |    2 |     1 |
+----+---------+---------+------+-------+

Weekly我正在数从上周日到下周日的时间。

仅使用 SQL 是否可行,还是需要某种脚本?如果可行,这将如何实现?值得一提的是,有数千种不同的类型,因此最好不要手动指定type

如果有任何不清楚的地方,请告诉我,我会尽力澄清。谢谢!

【问题讨论】:

  • iduser_id有什么区别?
  • id 本质上只是可以用作主键的行号/id。
  • 可以使用GROUP BY user_id, type 结合RANK() 函数生成结果的前三行。现在,要获取最后两行,您需要明确一周的开始和结束时间。
  • 一周基本上是从星期日(即 12 月 19 日)到星期日(即 12 月 26 日)。
  • @TheImpaler RANK() 函数在这个查询中究竟是如何工作的?

标签: sql postgresql


【解决方案1】:

与“用户”行相比,“每周”行的生成方式不同(我称它们为两个不同的“类别”)。要获得您想要的结果,您可以使用 UNION ALL 组合两个查询。

例如:

select 'u' as category, user_id, type,
  rank() over(partition by type order by count(*) desc) as rk,
  count(*) as cnt
from scores
group by user_id, type
union all
select 'w', user_id, 'Weekly',
  rank() over(order by count(*) desc),
  count(*) as cnt
from scores
group by user_id
order by category, type desc, rk

结果:

 category  user_id  type     rk  cnt 
 --------- -------- -------- --- --- 
 u         1        Diamond  1   3   
 u         2        Diamond  2   1   
 u         1        Clay     1   2   
 w         1        Weekly   1   5   
 w         2        Weekly   2   1   

请参阅DB Fiddle 的运行示例。

注意:为简单起见,我将按时间戳过滤的功能排除在查询之外。如果您确实需要包含过去 7 天(或其他时间段)的行,则只需在两个子查询中添加 WHERE 子句即可。

【讨论】:

    【解决方案2】:

    我想这就是你所说的,对吧?

    WITH scores_plus_weekly AS ((
            SELECT id, user_id, 'Weekly' AS type, created_at
            FROM scores
            WHERE created_at BETWEEN '2021-05-10' AND '2021-05-17'
        )
        UNION (
            SELECT * FROM scores
        ))
    SELECT
        row_number() OVER (ORDER BY CASE "type" WHEN 'Diamond' THEN 0 WHEN 'Clay' THEN 1 ELSE 2 END, count(*) DESC) as "id",
        user_id,
        "type",
        row_number() OVER (PARTITION BY count(*) DESC) as "rank",
        count(*)
    FROM scores_plus_weekly
    GROUP BY user_id, "type"
    ORDER BY "id";
    

    我确定这不是唯一的方法,但我认为结果并不太复杂。该查询首先将原始数据库与本周的所有分数相结合。为了保持一致性,我选择了一个与您的整个示例集相匹配的日期范围。然后它按user_idtype 分组以获取每个组合的计数。 row_numbers 将为您提供总体排名和每种类型的排名。此查询的很大一部分包括按type 排序,因此如果您要加入另一个包含类型顺序或优先级的表,CASE 可能会被简化。

    然后,最后,可以使用CREATE VIEW score_ranks AS 在视图中捕获整个查询,然后是您的查询。

    【讨论】:

    • 感谢您的回答和详细解释!问题:是否可以不必指定钻石/粘土?类型很多,手动添加会比较麻烦。
    • 我稍微简化了示例,但最好的方法是使用另一个包含整数值的表,例如order,以正确的顺序排列您的标签。例如。 (label, order) VALUES ('Diamond', 1), ('Gold', 2), ... ('Clay', 6) 等。然后您可以按标签名称加入该表并获取其order字段作为排序的输入。
    • 我明白了。 TheImpaler 说了一些关于使用RANK() 函数进行排名的事情,关于如何将其与此解决方案结合的任何想法?
    • 我不完全确定他的想法,但这是可以在窗口上使用的另一个功能(OVER (...) 部分)。它的计算方式不同;它从 1 开始并坚持该数字,并且仅针对每个新分区递增。您可以获得与 row_number 类似的结果,或者它可以用作“准备”,对后续查询很有用。
    • 它计算行数并给它们编号。它之后的窗口告诉它何时应该重新开始编号(分区部分)以及它应该以何种顺序递增(首先按类型,然后按降序计数)。我删除了 PARTITION BY TRUE 部分,因为它包含 ORDER BY 子句时是多余的。通过TRUE 进行分区是一种简单的说法,即只有一个分区(例如,不要重置row_number 的计数器)。
    猜你喜欢
    • 2015-03-10
    • 1970-01-01
    • 2021-08-30
    • 1970-01-01
    • 2021-03-07
    • 2016-08-30
    • 2020-12-22
    • 2018-08-03
    • 2021-01-08
    相关资源
    最近更新 更多