如何在document_versions表中筛选指定文档ID的非最大版本记录?
嘿,我来帮你搞定这个问题!你遇到的核心问题应该是没有在计算最大版本的子查询里限定目标document_id范围,导致拿到的是全局最大版本而非每个文档自己的最大版本,自然筛选结果不对。完全可以在第二个子查询里应用document_id约束,甚至这是必须的,下面给你几种可行的方案:
方案1:用窗口函数(最直观简洁)
窗口函数能轻松按document_id分组,给每个版本排序,直接排除掉每个组的第一条(最大版本):
SELECT dv.* FROM ( SELECT *, -- 按document_id分组,版本倒序排序,最大版本排第1位 ROW_NUMBER() OVER (PARTITION BY document_id ORDER BY version DESC) AS version_rank FROM document_versions -- 先筛选出指定的document_id列表,缩小范围 WHERE document_id IN (1, 2, 3) -- 替换成你的目标ID列表 ) dv -- 排除每个文档的最大版本(rank=1的记录) WHERE dv.version_rank > 1;
如果你的表中同一个document_id可能有重复的最大版本(比如多个记录version相同且都是最大值),可以把ROW_NUMBER()换成RANK(),这样会把所有并列最大的版本都排除掉。
方案2:子查询预计算目标文档的最大版本
先单独计算出每个目标document_id的最大版本,再关联原表排除这些记录:
SELECT dv.* FROM document_versions dv -- 关联子查询得到的每个目标文档的最大版本 JOIN ( SELECT document_id, MAX(version) AS max_version FROM document_versions -- 这里必须加document_id约束,只计算目标文档的最大版本 WHERE document_id IN (1, 2, 3) GROUP BY document_id ) max_versions ON dv.document_id = max_versions.document_id -- 排除等于最大版本的记录 WHERE dv.version < max_versions.max_version;
这个方案的好处是子查询只计算目标文档的最大版本,性能更优,尤其是当表数据量很大的时候。
方案3:用相关子查询直接比较
如果不想用JOIN,也可以用相关子查询直接判断当前记录的版本是不是该文档的最大值:
SELECT dv.* FROM document_versions dv WHERE dv.document_id IN (1, 2, 3) -- 当前记录的版本小于该文档的最大版本 AND dv.version < ( SELECT MAX(version) FROM document_versions dv2 WHERE dv2.document_id = dv.document_id );
这个写法更简洁,但如果表数据量很大,可能需要确保(document_id, version)有索引,否则性能会受影响。
关键提醒
你之前的写法出错,大概率是因为计算最大版本的子查询没有限定document_id范围,导致拿到的是整个表的最大版本,而非每个目标文档自己的最大值。比如如果文档A的最大版本是5,文档B的最大版本是3,全局最大是5,那没加约束的子查询会把所有版本小于5的记录都保留,这就会错误保留文档B的所有记录(包括它的最大版本3)。所以一定要在计算最大版本的子查询里加上document_id的筛选条件,或者用窗口函数的分组逻辑自动处理每个文档的最大值。
内容的提问来源于stack exchange,提问作者Warren Krewenki

