使用 UNION ALL 上一行结果的 SQL 表达式

Posted

技术标签:

【中文标题】使用 UNION ALL 上一行结果的 SQL 表达式【英文标题】:SQL Expressions using result of previous row of UNION ALL 【发布时间】:2016-10-01 19:43:51 【问题描述】:

我有这两张表:

Collect: Date, Rute, Value_Collected...
Records: Date, Rute, Value, Type...

在这种情况下,其他列不相关。所以我有这个工作代码:

SELECT
Totals.Date                             AS [Date],
Sum(Totals.Collected)                   AS [Collect],
Sum(Totals.Injected)                    AS [Injections],
Sum(Totals.Spent)                       AS [Expenses],
([Collect] + [Injections]) - [Expenses] AS [Total Day]

FROM (
    SELECT
    Records.Date                        AS [Date],
    0                                   AS [Collected],
    Sum(IIF(Type =  3,Records.Value,0)) AS [Injected],
    Sum(IIF(Type <> 3,Records.Value,0)) AS [Spent]
    FROM Records GROUP BY Records.Date

    UNION ALL

    SELECT
    Collect.Date                 AS [Date],
    Sum(Collect.Value_Collected) AS [Collected],
    0                            AS [Injected],
    0                            AS [Spent]
    FROM Collect GROUP BY Collect.Date
) AS Totals GROUP BY Totals.Date

这给了我这个结果:

|Date       |Collect    |Injections  |Expenses   |Total Day
-----------------------------------------------------------
|5/09/2016  |$47.000   |$0           |$0         |$47.000 |
|6/09/2016  |$20.000   |$0           |$0         |$20.000 |
|7/09/2016  |$13.000   |$0           |$0         |$13.000 |
|11/09/2016 |$122.000  |$0           |$0         |$122.000|
|14/09/2016 |$7.000    |$0           |$0         |$7.000  |
|16/09/2016 |$100.000  |$0           |$7.000     |$93.000 |
|30/09/2016 |$0        |$80.000      |$65.000    |$15.000 |
-----------------------------------------------------------

现在还可以,但是必须像 ([Total Day Of Last Row] + [Collect] + [Injections]) - [Expenses] 而不是 ([Collect] + [Injections]) - [Expenses] 这样计算列 [Total Day] 的公式才能得到这个想要的结果:

|Date       |Collect      |Injections  |Expenses   |Total Day
-------------------------------------------------------------
|5/09/2016  |$47.000      |$0          |$0         |$47.000 |
|6/09/2016  |$20.000      |$0          |$0         |$67.000 |
|7/09/2016  |$13.000      |$0          |$0         |$80.000 |
|11/09/2016 |$122.000     |$0          |$0         |$202.000|
|14/09/2016 |$7.000       |$0          |$0         |$209.000|
|16/09/2016 |$100.000     |$0          |$7.000     |$302.000|
|30/09/2016 |$0           |$80.000     |$65.000    |$317.000|
-------------------------------------------------------------

但我不知道如何获取上一行的[Total Day] 来计算当前行的[Total Day]。如果我尝试在[Totals] 上运行子查询,它会给我一个错误,因为它找不到表Totals。我认为这是因为它不是一个“真正的”表,但我真的不知道如何实现这一点。非常感谢任何帮助。

PS:列和表的名称最初是西班牙文,但翻译目的是为了让您了解我需要完成的工作。

【问题讨论】:

【参考方案1】:

基本上,您需要按累进日期跨字段累积总和,这可以通过相关总和聚合子查询来处理。唯一的挑战是 MS Access 没有像其他 SQL 方言那样的窗口函数来重用派生表查询。所以你需要在子查询中重复联合派生表:

SELECT main.Date, main.Collected AS [Collect], 
       main.Injected As [Injections], main.Spent As [Expenses],
       Ccur((SELECT Sum(sub.Collected) + Sum(sub.Injected) - Sum(sub.Spent) 
             FROM 
               (SELECT Records.Date AS [Date], 0 AS [Collected],
                       Sum(IIF(Type =  3,Records.Value,0)) AS [Injected],
                       Sum(IIF(Type <> 3,Records.Value,0)) AS [Spent]
                FROM Records GROUP BY Records.Date
                UNION ALL
                SELECT Collect.Date AS [Date],
                       Sum(Collect.Value_Collected) AS [Collected],
                       0 AS [Injected],
                       0 AS [Spent]
                FROM Collect GROUP BY Collect.Date
                ) As sub
             WHERE sub.Date <= main.Date)) As [Total Day]

FROM
  (SELECT Records.Date AS [Date], 0 AS [Collected],
          Sum(IIF(Type =  3,Records.Value,0)) AS [Injected],
          Sum(IIF(Type <> 3,Records.Value,0)) AS [Spent]
   FROM Records GROUP BY Records.Date
   UNION ALL
   SELECT Collect.Date AS [Date],
         Sum(Collect.Value_Collected) AS [Collected],
         0 AS [Injected],
         0 AS [Spent]
   FROM Collect GROUP BY Collect.Date
  ) As main

幸运的是,MS Access 确实维护了存储的查询对象,您可以在其中将 UNION 查询保存为单独的查询,并在新查询中引用它以获得更短的脚本:

SELECT main.Date, main.Collected As [Collect], 
       main.Injected As Injections, main.Spent As Expenses,  
       Ccur((SELECT Sum(sub.Collected) + Sum(sub.Injected) - Sum(sub.Spent) 
             FROM RecordsUnionQ As sub
             WHERE sub.Date <= main.Date)) As [Total Day]
FROM RecordsUnionQ As main

使用支持CTE的SQLite 3,您可以使用WITH()子句来保存联合查询,避免在子查询中重复。 CTE 仅在 SQL 语句期间充当临时视图。

WITH main AS
  (SELECT Records.Date AS [Date], 0 AS [Collected],
          Sum(IIF(Type =  3,Records.Value,0)) AS [Injected],
          Sum(IIF(Type <> 3,Records.Value,0)) AS [Spent]
   FROM Records GROUP BY Records.Date
   UNION ALL
   SELECT Collect.Date AS [Date],
         Sum(Collect.Value_Collected) AS [Collected],
         0 AS [Injected],
         0 AS [Spent]
   FROM Collect GROUP BY Collect.Date)

SELECT main.Date, main.Collected AS [Collect], 
       main.Injected As [Injections], main.Spent As [Expenses],
       Ccur((SELECT Sum(sub.Collected) + Sum(sub.Injected) - Sum(sub.Spent) 
             FROM main As sub
             WHERE sub.Date <= main.Date)) As [Total Day]
FROM main;

【讨论】:

非常感谢,您再次救了我的命,您的回答为我解决问题提供了一个很好的方向。我必须重复查询以获取前一天的值并使用它来计算每一行当天的总数,我猜重复查询对性能有非常坏的影响但是......是唯一的方法.. . 我猜 我还有一个小问题,如果这将是 SqLite... 怎么解决?是相同的解决方案吗?或者存在一种更优化的方式来获得相同的结果,我的意思是SELECT (Sum(sub.blah) + Sum(Sub.blah))- Sum(Sub.blah) FROM Totals as Sub WHERE Sub.Date &lt; Totals.Date,而不是重复查询 查看更新 SQLite 示例。效率如何?依靠。视图实际上与 MS Access 中的存储查询相同。 Access 的查询优化器使用存储的查询编译、缓存和查找最佳计划。 非常感谢您的帮助,我一定会更深入地研究WITH()子句,真的很棒。

以上是关于使用 UNION ALL 上一行结果的 SQL 表达式的主要内容,如果未能解决你的问题,请参考以下文章

SQL 中的 UNION 和UNION ALL 有啥区别?

SQL 中的 UNION 和UNION ALL 有啥区别?

mysql 使用UNION ALL问题

UNION和UNION ALL两者之间在性能上的区别

order by 和union all 如何共存

SQL高级查询基础