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

ROW_NUMBER() OVER PARTITION偶现跳过行号1问题及解决方案咨询

解决SQL分区行号过滤导致的分区跳过问题

嘿,我来帮你理清这个问题的根源,然后给你几个可行的解决方案:

问题根源分析

你的核心问题出在窗口函数计算和过滤操作的顺序颠倒了。原查询里,你先给所有RollAction记录生成了分区行号,再通过关联RollPermission、Roll以及WHERE条件过滤行。这就导致某些分区中row=1、row=2的行因为不满足关联条件被过滤掉,剩下的行虽然属于该分区,但行号大于1,最终被WHERE row=1直接排除,看起来就像“跳过了这个分区”。

至于你提到“移除所有WHERE和内联仍有行号跳过”,这大概率是个误解——ROW_NUMBER()函数在分区内是连续且唯一的,每个分区的行号必然从1开始递增,绝对不会跳过。你可以单独执行下面的查询验证:

SELECT RollId, TimeStamp,
       ROW_NUMBER() OVER (PARTITION BY RollId ORDER BY TimeStamp DESC) AS row
FROM RollAction
WHERE RollType = 'Test' AND Deleted = 0 AND UserId = 5
ORDER BY RollId, row;

你会看到每个RollId分区的行号都是从1开始连续的,不会出现跳号情况。

解决方案

方案1:先过滤关联,再生成行号(推荐)

把关联RollPermission、Roll的逻辑移到窗口函数计算之前,这样生成的行号是基于已经符合关联条件的记录,每个分区的row=1就是该分区中最新且满足要求的行,不会再出现“跳过分区”的问题。

修改后的SQL:

SELECT a.*, rp.TestId, r.Deleted
FROM (
    -- 先筛选符合关联条件的RollAction,再生成行号
    SELECT ra.*,
           ROW_NUMBER() OVER (PARTITION BY ra.RollId ORDER BY ra.TimeStamp DESC) AS row
    FROM RollAction ra
    INNER JOIN RollPermission rp 
        ON rp.RoId = ra.RollId 
        AND rp.RoType = ra.RoType 
        AND rp.UserId = ra.UserId 
        AND rp.Deleted = 0
    INNER JOIN Roll r 
        ON r.Id = ra.RollId 
        AND r.RoType = ra.RollType 
        AND r.Deleted = 0
    WHERE ra.Action = 'Fetched' 
      AND ra.RollType = 'Test' 
      AND ra.Deleted = 0 
      AND ra.UserId = 5
) a
WHERE a.row = 1
ORDER BY a.TimeStamp DESC
OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY;

方案2:取每个分区过滤后剩余行的最小行号

如果你坚持要先给所有RollAction生成行号再过滤,那可以用MIN(row) OVER (PARTITION BY RollId)标记每个分区剩余行的最小行号,再筛选出这些行:

SELECT filtered.*, rp.TestId, r.Deleted
FROM (
    SELECT a.*,
           -- 标记每个分区剩余行的最小行号
           MIN(a.row) OVER (PARTITION BY a.RollId) AS min_row_in_partition
    FROM (
        SELECT *, 
               ROW_NUMBER() OVER (PARTITION BY RollId ORDER BY TimeStamp DESC) AS row
        FROM RollAction
        WHERE Action = 'Fetched' 
          AND RollType = 'Test' 
          AND Deleted = 0 
          AND UserId = 5
    ) a
    INNER JOIN RollPermission rp 
        ON rp.RoId = a.RollId 
        AND rp.RoType = a.RoType 
        AND rp.UserId = a.UserId 
        AND rp.Deleted = 0
    INNER JOIN Roll r 
        ON r.Id = a.RollId 
        AND r.RoType = a.RollType 
        AND r.Deleted = 0
) filtered
WHERE filtered.row = filtered.min_row_in_partition
ORDER BY filtered.TimeStamp DESC
OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY;

这个方案会保留每个分区中剩余行里最新的那一条,不管它的原始行号是多少。

方案3:用CTE优化可读性

和方案1逻辑一致,但用CTE(公共表表达式)写法更清晰,适合复杂查询的维护:

WITH FilteredRollActions AS (
    SELECT ra.*, rp.TestId, r.Deleted
    FROM RollAction ra
    INNER JOIN RollPermission rp 
        ON rp.RoId = ra.RollId 
        AND rp.RoType = ra.RoType 
        AND rp.UserId = ra.UserId 
        AND rp.Deleted = 0
    INNER JOIN Roll r 
        ON r.Id = ra.RollId 
        AND r.RoType = ra.RollType 
        AND r.Deleted = 0
    WHERE ra.Action = 'Fetched' 
      AND ra.RollType = 'Test' 
      AND ra.Deleted = 0 
      AND ra.UserId = 5
),
RankedActions AS (
    SELECT *,
           ROW_NUMBER() OVER (PARTITION BY RollId ORDER BY TimeStamp DESC) AS row
    FROM FilteredRollActions
)
SELECT *
FROM RankedActions
WHERE row = 1
ORDER BY TimeStamp DESC
OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY;

总结

方案1和方案3是最优选择,它们先过滤无效记录再生成行号,不仅逻辑清晰,性能也更好。方案2适合特殊场景,但因为要先计算所有行的行号,性能会略差一些。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:13:56