如何计算SQLite查询的运行SUM?

时间:2011-04-09 17:19:45

标签: sql sqlite aggregate-functions

如何获得一列是另一列之前所有值的总和?

5 个答案:

答案 0 :(得分:10)

您可以通过将表连接到自身(执行所谓的笛卡尔或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个元素的临时表。我不会在一张大桌子上表演。

答案 1 :(得分:3)

从SQLite 3.25.0开始,自2018-09-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)时间内运行。

答案 2 :(得分:2)

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)解决方案可能不存在。

答案 3 :(得分:0)

如果您有一个不支持OVER的SQLite版本 这是对行的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
*/

答案 4 :(得分:-1)

你必须在你想要的字段中做一笔总结....查询取决于你正在使用的数据库,Oracle允许你这样做:

select id, value, sum(value) as partial_sum over (order by id) from table