You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL如何计算最新连续成功/失败序列长度?

解决PostgreSQL中连续成功/失败序列长度计算问题

我来帮你搞定这个PostgreSQL的连续序列计算需求!你遇到的IGNORE NULLS不支持的问题确实很常见,不过我们可以用PostgreSQL原生支持的窗口函数技巧完美绕开它,下面分两种场景给出方案:

一、生成所有行的连续序列长度(匹配你给出的示例输出)

这个方案会给每一行标记出它所在连续序列的长度,完全符合你提供的示例输出格式:

WITH grouped AS (
  SELECT 
    id, 
    success,
    -- 通过两个row_number的差值生成连续序列的组ID
    row_number() OVER (ORDER BY id DESC) 
    - row_number() OVER (PARTITION BY success ORDER BY id DESC) AS group_id
  FROM results
),
group_lengths AS (
  -- 计算每个连续组的长度
  SELECT 
    group_id, 
    success, 
    COUNT(*) AS length
  FROM grouped
  GROUP BY group_id, success
)
-- 关联回原数据,给每一行加上对应组的长度
SELECT 
  g.id, 
  g.success, 
  gl.length
FROM grouped g
JOIN group_lengths gl 
  ON g.group_id = gl.group_id AND g.success = gl.success
ORDER BY g.id DESC;

原理说明:

  • grouped CTE里的两个row_number():第一个是按id倒序全局编号,第二个是按success分组后倒序编号。当success值连续相同时,两个编号的差值保持不变;一旦success值变化,差值会跳变,这样就把所有连续相同success的行分到了同一个组。
  • group_lengths CTE统计每个组的总行数,也就是该连续序列的长度,最后关联回原数据即可得到每一行对应的序列长度。

二、仅获取最新的连续成功/失败序列长度(核心需求)

如果只需要最新的连续成功序列和连续失败序列的长度,不需要所有行的标记,可以用更简洁的查询:

WITH latest_groups AS (
  SELECT 
    success,
    -- 标记每个行所在的连续组ID
    row_number() OVER (ORDER BY id DESC) 
    - row_number() OVER (PARTITION BY success ORDER BY id DESC) AS group_id,
    -- 计算当前组的长度
    COUNT(*) OVER (PARTITION BY success, 
      row_number() OVER (ORDER BY id DESC) 
      - row_number() OVER (PARTITION BY success ORDER BY id DESC)) AS length,
    -- 标记每个success类型下的组的新旧顺序(1是最新的组)
    ROW_NUMBER() OVER (PARTITION BY success ORDER BY id DESC) AS group_rank
  FROM results
)
-- 只取每个success类型下的最新组的长度
SELECT 
  success, 
  MAX(length) AS sequence_length
FROM latest_groups
WHERE group_rank = 1
GROUP BY success
ORDER BY success;

这个查询会直接返回类似这样的结果:

success | sequence_length
---------+-----------------
 false   | 2
 true    | 3

替代简化方案:

如果你觉得嵌套窗口函数有点复杂,也可以用以下方式直接定位最新的连续组并统计:

WITH latest_change_points AS (
  -- 找到最新的success值变化的位置
  SELECT 
    id, 
    success,
    CASE WHEN lag(success) OVER (ORDER BY id DESC) != success THEN id END AS change_point
  FROM results
),
latest_groups_boundaries AS (
  -- 获取最新的两个连续组的起始ID
  SELECT 
    success,
    COALESCE(MAX(change_point), (SELECT MIN(id) FROM results)) AS start_id
  FROM latest_change_points
  GROUP BY success
)
-- 统计每个最新组的行数
SELECT 
  lgb.success, 
  COUNT(*) AS sequence_length
FROM results r
JOIN latest_groups_boundaries lgb 
  ON r.success = lgb.success AND r.id >= lgb.start_id
GROUP BY lgb.success;

为什么你的原查询有问题?

PostgreSQL在旧版本中确实不支持IGNORE NULLS作为last_value的修饰符(PostgreSQL 11+开始支持,但并不是所有环境都能升级),而我们上面的方案完全基于PostgreSQL原生支持的窗口函数特性,不需要依赖这个可选语法,兼容性更好。

内容的提问来源于stack exchange,提问作者artem_golovin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:51:47