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 | ...
我的两个问题是:
- 按顺序考虑我的“步数”,我想过滤掉排名“最高”的步数(从左到右)。所以在上面的例子中,“john”已经达到 step_02”,”paul 已经达到 step_03” 等等。(注意其他值,例如“george ... step_02 = 1 " 与此无关。)
- 在更新任何值时,我无法在同一个 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
(请耐心等待一个自学成才但对 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 | ...
我的两个问题是:
- 按顺序考虑我的“步数”,我想过滤掉排名“最高”的步数(从左到右)。所以在上面的例子中,“john”已经达到 step_02”,”paul 已经达到 step_03” 等等。(注意其他值,例如“george ... step_02 = 1 " 与此无关。)
- 在更新任何值时,我无法在同一个 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