按Name分组取Version最大值对应的完整Policy表记录的SQL查询问题
解决每个Policy名称对应最高版本完整记录的查询问题
你遇到的问题很典型:当你只聚合出每个Name的最高Version后,没办法直接关联到对应的Id——因为GROUP BY子句里没有包含Id,直接加进去会破坏分组逻辑,要么报错要么返回非预期的结果。下面给你两种可靠的解决方法,适配ActiveRecord场景:
方法1:使用窗口函数(推荐,简洁高效)
窗口函数可以给每个Name分组内的记录按版本倒序排序,然后取排序第一的那条,这是处理这类"每组取Top 1"场景的最优方案之一,支持PostgreSQL、MySQL 8+、SQL Server等主流数据库。
原生SQL写法
SELECT p.* FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY name ORDER BY version DESC) AS rn FROM policies ) p WHERE p.rn = 1;
在ActiveRecord中执行的话:
result = ActiveRecord::Base.connection.exec_query(<<~SQL) SELECT p.* FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY name ORDER BY version DESC) AS rn FROM policies ) p WHERE p.rn = 1; SQL
ActiveRecord链式调用写法(更符合Rails风格)
如果你的模型是Policy,可以用更贴合Rails的写法,返回的是Policy对象集合而非原始查询结果:
Policy.from( Policy.select('*', 'ROW_NUMBER() OVER (PARTITION BY name ORDER BY version DESC) AS rn'), :p ).where('p.rn = 1')
方法2:子查询关联(兼容旧版数据库)
如果你的数据库不支持窗口函数(比如MySQL 5.x),可以先通过子查询获取每个Name的最高版本,再关联原表拿到对应完整记录:
原生SQL写法
SELECT p.* FROM policies p INNER JOIN ( SELECT name, MAX(version) AS max_version FROM policies GROUP BY name ) p_max ON p.name = p_max.name AND p.version = p_max.max_version;
ActiveRecord链式调用写法
Policy.joins( "INNER JOIN (SELECT name, MAX(version) AS max_version FROM policies GROUP BY name) p_max ON policies.name = p_max.name AND policies.version = p_max.max_version" )
为什么你的初始查询有问题?
你之前的查询只拿到了分组后的name和最高version,但Id是单条记录的唯一标识,不属于聚合列。直接把Id加入SELECT列表时,数据库无法确定要返回同组内哪条记录的Id——不同数据库处理逻辑不同,要么直接报错,要么随机返回一条的Id,所以必须通过关联或窗口函数来精准匹配到对应最高版本的完整记录。
内容的提问来源于stack exchange,提问作者Aravind
相关产品推荐
相关产品推荐

