PostgreSQL计算三角数:使用generate_series求和及替代GROUP BY
计算三角数:无需GROUP BY保持输入顺序的SQL实现
需求背景
要计算三角数(即从0到n的所有自然数之和,等价于1到n的求和结果,加0不影响最终值),但实现中碰到两个问题:
- 最初尝试在CASE语句里直接对
generate_series(1,n)求和,触发语法错误; - 改用
LATERAL关联generate_series实现求和后,因用了GROUP BY导致输出顺序和输入的n顺序不一致。
问题分析
- 直接在CASE或SUM中嵌套
generate_series不合法:generate_series返回的是多行结果集,不能直接作为SUM的参数,SQL语法不支持这种写法。 GROUP BY会打乱原始顺序:GROUP BY默认会对分组字段排序,导致输出的n无法保持输入时的先后顺序。
正确解决方案
通过LATERAL逐行处理每个n的求和,同时给输入数据添加行号标记原始顺序,完全不需要GROUP BY:
-- 模拟带原始顺序的输入数据 WITH input_numbers AS ( SELECT n, -- 生成行号标记输入时的原始顺序 ROW_NUMBER() OVER () AS original_order FROM (VALUES (4), (2), (5), (1)) AS t(n) ) SELECT inp.n, -- 1到n的和即为三角数,n=0时返回0 COALESCE(tri_sum.sum_val, 0) AS triangular_number FROM input_numbers inp -- LATERAL为每个n单独生成数列并求和 LEFT JOIN LATERAL ( SELECT SUM(x) AS sum_val FROM generate_series(1, inp.n) AS x ) tri_sum ON true -- 按原始输入顺序输出结果 ORDER BY inp.original_order;
更高效的替代方案
三角数有现成的数学公式:n*(n+1)/2,用公式计算比生成数列求和效率高得多,同样能保持原始顺序:
WITH input_numbers AS ( SELECT n, ROW_NUMBER() OVER () AS original_order FROM (VALUES (4), (2), (5), (1)) AS t(n) ) SELECT n, CASE WHEN n >=0 THEN n*(n+1)/2 ELSE 0 END AS triangular_number FROM input_numbers ORDER BY original_order;
内容的提问来源于stack exchange,提问作者OlChopper
相关产品推荐
相关产品推荐

