CakePHP 如何使用虚拟字段获取实体中的 运行 总数?

CakePHP how to get the running total in an entity using a virtual field?

我有一个名为 deposit 的字段,我正在尝试创建一个名为 balance 的虚拟字段。低于我的期望输出,就像链式求和。

deposit   balance   
100       100
300       400
10        410

我在实体中尝试了以下代码

public $balance = 0;
protected function _getBalance()
{
    $this->balance = $this->balance + $this->deposit;
    return $this->balance;
}

我已经0全部平衡了。

我得到如下结果

deposit   balance   
    100       0
    300       0
    10        0

我怎样才能得到想要的结果?

一个实体不知道其他实体,但为了能够总结余额,这是必需的。

我想到的两个解决方案是 a) 迭代所有结果并修改数据,或者 b) 如果您的 DBMS 支持它们,使用 window 函数创建 运行 总计 SQL 水平。

如果您遍历所有结果,您可以访问前一个结果的余额并计算总和并相应地填充 balance 字段,例如在结果格式化程序中:

$query->formatResults(function (\Cake\Collection\CollectionInterface $results) {
    $previous = null;
    return $results->map(function ($row) use (&$previous) {
        if ($previous === null) {
            $row['balance'] = $row['deposit'];
        } else {
            $row['balance'] = $previous['balance'] + $row['deposit'];
        }
        
        $previous = $row;
        
        return $row;
    });
});

在 SQL 级别 window 函数将允许您总结前几行:

$query->select(function (\Cake\ORM\Query $query) {
    return [
        'deposit',
        'balance' => $query
            ->func()
            ->sum('deposit')
            ->over()
            ->order('id')
            ->rows(null)
    ];
});

这将创建一个 SELECT 子句,如下所示:

SELECT
    deposit,
    (
        SUM(deposit) OVER (
            ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        )
    ) AS balance

其中总和是根据所有先前行计算的,包括当前行。

需要注意的是,构建器上的 window 函数仅从 CakePHP 4.1 开始受支持,在以前的版本中,您必须创建自定义表达式或传递原始 SQL:

$query->select([
    'deposit',
    'balance' => 'SUM(deposit) OVER (
        ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    )'
]);

另见