在窗口函数中如何引用正在定义的列,即引用自身?

Posted

技术标签:

【中文标题】在窗口函数中如何引用正在定义的列,即引用自身?【英文标题】:Within a window function how to refer back to the column that is being defined, namely reference to itself? 【发布时间】:2021-05-28 10:10:16 【问题描述】:

为了计算调整后的成本基数 (ACB),它是价格 * 数量 + 佣金或先前 ACB/股份 * 数量的运行总和,具体取决于是销售还是购买。

我有下表,命名为transaction_t

Date Action Quantity Price Commission
2021-01-02 buy 150 110.21 5.95
2021-01-21 buy 360 106.87 5.95
2021-03-21 sell 360 106.87 5.95

为了计算运行中的“调整成本基数”,我有以下查询:

SELECT 
  SUM(CASE 
        WHEN T.Action in ("buy", "reinvest") THEN T.Quantity
        ​WHEN Action = "sell" THEN -T.Quantity
      END
  ) OVER (ORDER BY T.Date) AS quantity_balance,
  SUM(CASE 
        WHEN T.Action in ("buy", "reinvest") THEN T.Quantity * T.Price + T.Commission
        WHEN T.Action = "sell" THEN T.Quantity * ***(previous_total_acb / previous_quantity_balance)***[1]
      END
  ) OVER (ORDER BY T.Date) AS total_acb
  FROM transaction_t AS T;

此查询无效。因为伪代码previous_total_acb / previous_quantity_balance 指的是在该窗口函数中定义的列。

如何在 SQLite 中完成这项工作?

注意[1]:这里的previous_total_acb 是一个伪代码,我打算让它引用列本身,total_acb。但是 sqlite 似乎不支持这种循环引用。 previous_quantity_balance 指的是同样由窗口函数创建的兄弟列quantity_balance。这似乎也不起作用。

【问题讨论】:

编辑您的问题并解释什么是previous_total_acb 和previous_quantity_balance,更好地使用示例数据和预期结果。 【参考方案1】:

您将使用 CTE(或子查询)来准备回溯计算,然后在最终查询中引用它们。 我不明白你的实际sql。这是一个可能没有意义的简化版本,只是简单地演示了结构:

第一个 CTE 只是给出一些值,在您的情况下,这些值来自您的表:

WITH TBL(d, a, q, p)
AS
(
   SELECT * FROM (
              VALUES (20210102, 'buy',  150, 110.21),
                     (20210121, 'buy',  380, 106.87),
                     (20210321, 'sell', 360, 104.33)
                 )
),

所以实际上你会从WITH开始这里

SRC AS
(
    SELECT d,
           a,
           q,
           p,
           q*p AS cost,
           lag(q*p) OVER (ORDER BY d) AS prev_cost
      FROM TBL
)

产生这个:

+--------+----+---+------+-------+---------+
|d       |a   |q  |p     |cost   |prev_cost|
+--------+----+---+------+-------+---------+
|20210102|buy |150|110.21|16531.5|NULL     |
|20210121|buy |380|106.87|40610.6|16531.5  |
|20210321|sell|360|104.33|37558.8|40610.6  |
+--------+----+---+------+-------+---------+

从那里您可以使用当前值和以前的值来生成计算字段

SELECT d, cost, cost-prev_cost AS diff FROM SRC ORDER BY d;

其中(正如我所说的只是为了演示,在这里没有实际意义)产生

+--------+-------+----------+
|d       |cost   |diff      |
+--------+-------+----------+
|20210102|16531.5|NULL      |
|20210121|40610.6|24079.1   |
|20210321|37558.8|-3051.79  |
+--------+-------+----------+

因此,将它们放在一起并假设您的数据存储在 TBL 中,它看起来像

WITH SRC AS
(
    SELECT d,
           a,
           q,
           p,
           q*p AS cost,
           lag(q*p) OVER (ORDER BY d) AS prev_cost
      FROM TBL
)
SELECT d, cost, cost-prev_cost AS diff FROM SRC ORDER BY d;

您可以根据需要拥有尽可能多的 CTE 来准备“回溯”数据。不确定,但在您的最终查询中使用它之前,您可能需要至少一个中间准备 CTE 来计算 previous_total_acb

【讨论】:

这是相当丰富的信息,但我相信这是正确的方向。 为什么选择 CTE 而不是子查询? @JinghuiNiu 子查询应该可以正常工作。并不意味着听起来教条。 CTE 提供了一些可读性,并且可以被其他 CTE 引用,但如果编写正确,子查询也可以。在您的情况下,如果您需要嵌套子查询(不确定是否需要但可能),CTE 会更容易阅读。

以上是关于在窗口函数中如何引用正在定义的列,即引用自身?的主要内容,如果未能解决你的问题,请参考以下文章

未定义对“JNI_CreateJavaVM”窗口的引用

如何使用窗口函数引用输出行?

如何获得对动画窗口的引用?

JavaScript跳出iframe框架

JavaScript跳出iframe框架

如何在从父窗口创建的 iframe 的 onload 处理程序中获取对 iframe 窗口对象的引用