【问题标题】:postgres - select the "highest scored" column_name from left to rightpostgres - 从左到右选择“得分最高”的 column_name
【发布时间】:2021-02-19 16:02:05
【问题描述】:

(请耐心等待一位自学成才但对 postgres 很着迷的初学者:)

在 POSTGRES 10 中,我提出了一个表格来跟踪编辑和归档照片文件夹的进度。这个进度是按顺序步骤来衡量的,这些步骤通过 foreign_key 链接到另一个表,该表基本上声明 0 =“open”,1 =“work”,2 =“done”。因此,我使用数值来引用这些状态。

为了简单起见,这里是带有一些演示数据的最重要的列

folder_name  |  archive_status  |  step_01  |  step_02  |  step_03  |  ...
----------------------------------------------------------------------------
john         |         *        |     0     |     2     |     0     |  ...
paul         |         *        |     0     |     0     |     2     |  ...
george       |         *        |     2     |     1     |     0     |  ...
ringo        |         *        |     0     |     2     |     0     |  ...

想要的结果应该是这样的:

folder_name  |  archive_status  |  step_01  |  step_02  |  step_03  |  ...
----------------------------------------------------------------------------
john         |     step_02      |     0     |     2     |     0     |  ...
paul         |     step_03      |     0     |     0     |     2     |  ...
george       |     step_01      |     2     |     1     |     0     |  ...
ringo        |     step_02      |     0     |     2     |     0     |  ...

我的两个问题是:

  1. 考虑到我的“步骤”顺序,我想过滤“最高”排名的一个(从左到右)。所以在上面的例子中,“john”已经达到了step_02”,“paul 已经达到了step_03”等等。(请注意,“george ... step_02 = 1”等其他值与此无关。)
  2. 在更新任何值时,我无法在同一个表中完成此操作 - 我是否必须使用 FUNCTION 或 VIEW 或 TRIGGER(或它们的组合)?

我尝试使用聚合函数 https://www.postgresql.org/docs/10/tutorial-agg.html 来处理它,但我有点卡住了,因为我不需要多个输入行,而是在一行中过滤多个列。

--- 更新:新问题---

抱歉,我的初始演示数据不够清晰,这些列更像是一个进度跟踪器,实际上这些列中的每个单元格都可以显示任何值。 (这是答案中的原始解决方案失败的地方,因为如果一行中有两个相同的“最高”值,它将使用第一个出现的值。)所以 SQL 查询应该找到最右边的列,以便说话。当然,我可以在这里想出一些巧妙的“分数计算”,但事实上,使用当前的“矩阵式”设计,事情会容易得多。

同样,根据值 (step_xx) 的 期望结果 (archive_status) 将是:

folder_name  |  archive_status  |  step_01  |  step_02  |  step_03 
--------------------------------------------------------------------
john         |     step_02      |     2     |     2     |     0    
paul         |     step_03      |     1     |     1     |     2   
george       |     step_01      |     2     |     1     |     1   
ringo        |     step_02      |     2     |     2     |     1   

【问题讨论】:

  • 更好的数据模型将状态存储在单独的行而不是单独的列中。

标签: sql postgresql case greatest-n-per-group


【解决方案1】:

一个选项使用greatest()case 表达式:

select t.*,
    case greatest(step_01, step_02, step_03)
        when step_01 then 'step_01'
        when step_02 then 'step_02'
        when step_03 then 'step_03'
    end as archive_status
from mytable t

虽然这可以解决您当前的问题,但我建议您规范化您的设计。每个步骤都应存储在单独的 中,而不是作为列存储在类似(folder_name, step, status) 的结构中。然后你会使用distinct on:

select distinct on (folder_name) t.*
from newtable t
order by folder_name, status desc, step

【讨论】:

  • 这绝对有很大帮助。非常感谢。还将考虑规范我的设计的建议。 – 虽然我刚刚发现我的演示数据不够清晰。我认为这里更多的是一个进度条......见下文。
【解决方案2】:

除了 GMB 的有用答案之外,一个小改动解决了我更新的问题。这是为了找出序列中“最右边”的列。由于 CASE ... WHEN 的执行一旦成功满足第一个条件(“真”)就停止,诀窍就是改变 WHEN 语句的顺序。所以下面的代码对我有用:

   select t.*,
    case greatest(step_01, step_02, step_03)
        when step_03 then 'step_03'
        when step_02 then 'step_02'
        when step_01 then 'step_01'
        else 'step_00'
    end as archive_status
from mytable t

【讨论】:

    猜你喜欢
    • 2015-02-02
    • 2017-07-22
    • 1970-01-01
    • 2013-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多