postgres - 从左到右 select "highest scored" column_name

postgres - select the "highest scored" column_name from left to right

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

在 POSTGRES 10 中,我想出了一个 table 来跟踪编辑和存档照片文件夹的进度。这个进度是按顺序步骤衡量的,link 通过 foreign_key 到另一个 table,基本上表示 0 =“打开”,1 =“工作”,2 =“完成”。因此我使用数值来引用这些状态。

为了使问题简单,这里是最重要的列和一些演示数据

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. 在更新任何值时,我无法在同一个 table 内完成此操作 - 我是否必须使用 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   

一个选项使用 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

除了上述 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