【发布时间】:2011-08-02 04:04:24
【问题描述】:
如何获得一列是另一列之前所有值的总和?
【问题讨论】:
-
请提供示例数据和预期输出。
标签: sql sqlite aggregate-functions
如何获得一列是另一列之前所有值的总和?
【问题讨论】:
标签: sql sqlite aggregate-functions
从 SQLite 3.25.0 开始,自 2018 年 9 月 15 日起,支持 window functions 及其关键字 OVER。您的问题的答案现在很简单:
SELECT Country, Gdp, SUM(Gdp) OVER (ROWS UNBOUNDED PRECEDING)
FROM CountryGdp;
这是执行您请求的最小查询,但它没有定义任何顺序,因此这是一种更合适的方法。
SELECT
Country,
Gdp,
SUM(Gdp) OVER (
ORDER BY Country -- Window ordering (not necessarily the same as result ordering!)
ROWS BETWEEN -- Window for the SUM includes these rows:
UNBOUNDED PRECEDING -- all rows before current one in window ordering
AND CURRENT ROW -- up to and including current row.
) AS RunningTotal
FROM CountryGdp
ORDER BY Country;
无论如何,查询应该在 O(N) 时间内运行。
【讨论】:
您可以通过将表格与自身连接起来(执行所谓的笛卡尔或cross join)来实现。请参阅以下示例。
SELECT a.name, a.gdppc, SUM(b.gdppc)
FROM gdppc AS a, gdppc AS b WHERE b.gdppc <= a.gdppc
GROUP BY b.id ORDER BY a.gdppc;
给定一张包含国家及其人均 GDP 的表格,它将为您提供 GDP 的总和。
Democratic Republic of Congo|329.645|329.645
Zimbabwe|370.465|700.11
Liberia|385.417|1085.527
Burundi|399.657|1485.184
Eritrea|678.954|2164.138
Niger|711.877|2876.015
Central African Republic|743.945|3619.96
Sierra Leone|781.594|4401.554
Togo|833.803|5235.357
Malawi|867.063|6102.42
Mozambique|932.511|7034.931
...
请注意,这可能是一个非常耗费资源的操作,因为如果一个表有 N 个元素,它将创建一个具有 N*N 个元素的临时表。我不会在大桌子上执行它。
【讨论】:
像Diomidis Spinellis 这样的交叉连接解决方案建议花费 O(N^2) 时间。如果您能忍受复杂的代码,递归 CTE 可以更快地工作。
这会产生与他相同的输出。
WITH RECURSIVE running(id, name, gdppc, rt) AS (
SELECT row1._rowid_, row1.name, row1.gdppc, COALESCE(row1.gdppc,0)
FROM gdppc AS row1
WHERE row1._rowid_ = (
SELECT a._rowid_
FROM gdppc AS a
ORDER BY a.gdppc, a.name, a._rowid_
LIMIT 1)
UNION ALL
SELECT row_n._rowid_, row_n.name, row_n.gdppc, COALESCE(row_n.gdppc,0)+running.rt
FROM gdppc AS row_n INNER JOIN running
ON row_n._rowid_ = (
SELECT a._rowid_
FROM gdppc AS a
WHERE (a.gdppc, a.name, a._rowid_) > (running.gdppc, running.name, running.id)
ORDER BY a.gdppc, a.name, a._rowid_
LIMIT 1))
SELECT running.name, running.gdppc, running.rt
FROM running;
排序和比较处理重复,COALESCE 可以忽略 NULL。
如果你有一个好的索引,这应该是 O(N log N)。由于 SQLite 不支持游标,如果不依赖外部应用程序,可能不存在 O(N) 解决方案。
【讨论】:
如果您的 SQLite 版本不支持 OVER
这是对 group_concat 行字符串使用递归的另一种方法。
在 SQLite 版本 3.22.0 2018-01-22 18:45:57 group_concat 按数据库顺序返回行。创建一个公用表表达式,并按照示例中的表 work1 对其进行排序。
/* cumulative running total using group_concat and recursion
adapted from https://blog.expensify.com/2015/09/25/the-simplest-sqlite-common-table-expression-tutorial/
*/
WITH RECURSIVE work2 AS (
SELECT NULL AS name, NULL AS gdppc, 0 AS cum, (select group_concat(name) from work1) AS gcname, (select group_concat(gdppc) from work1) AS gcgdppc
UNION
SELECT
CASE
WHEN INSTR(gcname, ',' )>0 THEN
SUBSTR(gcname, 0, INSTR(gcname,','))
ELSE
gcname
END,
CASE
WHEN INSTR(gcgdppc, ',' )>0 THEN
SUBSTR(gcgdppc, 0, INSTR(gcgdppc,','))
ELSE
gcgdppc
END,
CASE
WHEN INSTR(gcgdppc, ',' )>0 THEN
cum + SUBSTR(gcgdppc, 0, INSTR(gcgdppc,','))
ELSE
cum + gcgdppc
END,
CASE
WHEN INSTR( gcname, ',' )>0 THEN
SUBSTR( gcname, INSTR( gcname, ',' )+1 )
ELSE
NULL
END,
CASE
WHEN INSTR(gcgdppc, ',' )>0 THEN
SUBSTR( gcgdppc, INSTR( gcgdppc, ',' )+1 )
ELSE
NULL
END
FROM work2
WHERE gcgdppc IS NOT NULL
),
/* SQLite version 3.22.0 2018-01-22 18:45:57
group_concat ignores ORDER BY when specified against the base table
but does appear to follow the order of a common table expression
*/
work1 AS (select * from gdppc order by gdppc),
gdppc AS (SELECT 'Burundi' AS name,399.657 AS gdppc
UNION
SELECT 'Democratic Republic of Congo', 329.645
UNION
SELECT 'Liberia',385.417
UNION
SELECT 'Zimbabwe',370.465)
select name,gdppc,cum from work2 where name IS NOT NULL;
/* result
Democratic Republic of Congo|329.645|329.645
Zimbabwe|370.465|700.11
Liberia|385.417|1085.527
Burundi|399.657|1485.184
*/
【讨论】:
您必须在您想要的字段中进行求和...。查询取决于您使用的数据库,Oracle 允许您这样做:
select id, value, sum(value) as partial_sum over (order by id) from table
【讨论】: