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

Hug*_*ugo 11 sql sqlite aggregate-functions

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

rel*_*dom 19

自 SQLite 3.25.0 起,自 2018-09-15 起,支持窗口函数及其关键字OVER。您的问题的答案现在很简单:

SELECT Country, Gdp, SUM(Gdp) OVER (ROWS UNBOUNDED PRECEDING)
FROM CountryGdp;
Run Code Online (Sandbox Code Playgroud)

这是执行您请求的最小查询,但它没有定义任何顺序,所以这里是一个更合适的方法。

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;
Run Code Online (Sandbox Code Playgroud)

无论如何,查询应该在 O(N) 时间内运行。

  • 当我写答案时,我想我检查了查询计划操作码输出。它使用协程来获得 O(N) 运行时间。我怀疑新版本的情况是否会变得更糟,但我想这是可能的。另外,谁知道如果稍微改变顺序、索引或其他什么,它会产生什么输出。据我所知,SQL 并没有真正承诺任何性能特征。 (2认同)

Dio*_*lis 13

您可以通过将表连接到自身来执行此操作(执行所谓的笛卡尔或交叉连接).请参阅以下示例.

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;
Run Code Online (Sandbox Code Playgroud)

给出一个包含国家及其人均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
...
Run Code Online (Sandbox Code Playgroud)

请注意,这可能是一个非常耗费资源的操作,因为如果一个表有N个元素,它将创建一个具有N*N个元素的临时表.我不会在一张大桌子上表演.

  • @Andriy M对!WHERE子句使交叉连接成为三角形。 (2认同)

rel*_*dom 5

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;
Run Code Online (Sandbox Code Playgroud)

排序和比较处理重复项,COALESCE是否可以忽略 NULL。

如果你有一个好的索引,这应该是 O(N log N)。由于 SQLite 不支持游标,因此不依赖外部应用程序可能不存在 O(N) 解决方案。

  • 哎哟...我只想知道一件事,简单地检索整个集合然后在实际的编程代码中进行分组和计算不是更好吗?为什么要为这种难以理解的语法烦恼呢? (2认同)