You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免在MariaDB视图中重复计算字段

问题描述

我在MariaDB中创建了一个用于统计库存贷方(Credit)、借方(Debit)和余额(balance)的视图,原SQL如下:

SELECT pitem,
    Bdate,
    credit,
    debit, 
    sum(ifnull(bal, 0)) OVER (PARTITION BY pitem ORDER BY bdate, DESC1) balance, 
    DESC1
FROM (
    SELECT a.Pitem,
        a.Bdate,
        a.Trn,
        If((trn=1 AND a.qty>0), a.Qty, 0) AS Credit,
        If(trn=0 OR a.qty<0, abs(a.Qty), 0) AS Debit,
        (If((trn=1 AND a.qty>0), a.Qty, 0)) - (If(trn=0 OR a.qty<0, abs(a.Qty), 0)) AS Bal,
        a.Desc1
    FROM (
        SELECT tblphmrefill.RfItem AS PItem,
            tblphmrefill.rfDate AS BDate,
            tblphmrefill.rfQty AS Qty,
            CONCAT(
                if(rfqty>0, 'Purchase No_', 'Discard_BMW_'), 
                coalesce(tblphmrefill.RfInvoice, 0), '_Vend_', rfsuppID
            ) AS DESC1,
            1 AS Trn
        FROM tblphmrefill
        UNION (
            SELECT invoicerefundphm.phmItem AS pItem,
                invoice.Bdate,
                invoicerefundphm.Qty,
                CONCAT('Refund_', billno, '_', invoice.BName) AS DESC1,
                1 AS Trn
            FROM invoice
            INNER JOIN invoicerefundphm 
            ON invoice.BilID = invoicerefundphm.Billno
        )
        UNION (
            SELECT invoicephm.phmItem AS PItem,
                invoice.BDate,
                invoicephm.Qty,
                CONCAT('Sales_', billno, '_', invoice.BName),
                0 AS Trn
            FROM invoicephm
            INNER JOIN invoice 
            ON invoicephm.Billno = invoice.BilID 
            ORDER BY invoice.bdate, invoice.bilid ASC
        )
    ) AS a 
) phmtbatch_int 
Order by Bdate, DESC1 Asc 

当前问题是计算Bal字段时,重复执行了Credit和Debit的计算逻辑。我尝试用用户变量复用计算结果:

SELECT a.Pitem,
     a.Bdate,
     a.Trn,
     @Credit := (If((trn=1 AND a.qty>0), a.Qty, 0)) AS Credit,
     @Debit := (If(trn=0 OR a.qty<0, abs(a.Qty), 0)) AS Debit,
     (@Credit - @Debit) AS Bal,
     a.Desc1

但MariaDB抛出Error 1351,提示:

view contains variable or parameter which is not allowed

请问有没有办法避免重复计算Credit、Debit来得到Bal字段?


解决方案

方法1:嵌套派生表分层计算

新增一层派生表先计算Credit和Debit,外层直接引用这两个字段计算Bal,避免重复逻辑:

SELECT pitem,
    Bdate,
    credit,
    debit, 
    sum(ifnull(bal, 0)) OVER (PARTITION BY pitem ORDER BY bdate, DESC1) balance, 
    DESC1
FROM (
    SELECT 
        sub.Pitem,
        sub.Bdate,
        sub.Trn,
        sub.Credit,
        sub.Debit,
        sub.Credit - sub.Debit AS Bal,
        sub.Desc1
    FROM (
        SELECT a.Pitem,
            a.Bdate,
            a.Trn,
            If((trn=1 AND a.qty>0), a.Qty, 0) AS Credit,
            If(trn=0 OR a.qty<0, abs(a.Qty), 0) AS Debit,
            a.Desc1
        FROM (
            SELECT tblphmrefill.RfItem AS PItem,
                tblphmrefill.rfDate AS BDate,
                tblphmrefill.rfQty AS Qty,
                CONCAT(
                    if(rfqty>0, 'Purchase No_', 'Discard_BMW_'), 
                    coalesce(tblphmrefill.RfInvoice, 0), '_Vend_', rfsuppID
                ) AS DESC1,
                1 AS Trn
            FROM tblphmrefill
            UNION (
                SELECT invoicerefundphm.phmItem AS pItem,
                    invoice.Bdate,
                    invoicerefundphm.Qty,
                    CONCAT('Refund_', billno, '_', invoice.BName) AS DESC1,
                    1 AS Trn
                FROM invoice
                INNER JOIN invoicerefundphm 
                ON invoice.BilID = invoicerefundphm.Billno
            )
            UNION (
                SELECT invoicephm.phmItem AS PItem,
                    invoice.BDate,
                    invoicephm.Qty,
                    CONCAT('Sales_', billno, '_', invoice.BName),
                    0 AS Trn
                FROM invoicephm
                INNER JOIN invoice 
                ON invoicephm.Billno = invoice.BilID 
                ORDER BY invoice.bdate, invoice.bilid ASC
            )
        ) AS a 
    ) AS sub
) phmtbatch_int 
Order by Bdate, DESC1 Asc 

方法2:使用CTE(MariaDB 10.2+支持)

用CTE分层拆解计算逻辑,提升代码可读性:

WITH raw_data AS (
    SELECT tblphmrefill.RfItem AS PItem,
        tblphmrefill.rfDate AS BDate,
        tblphmrefill.rfQty AS Qty,
        CONCAT(
            if(rfqty>0, 'Purchase No_', 'Discard_BMW_'), 
            coalesce(tblphmrefill.RfInvoice, 0), '_Vend_', rfsuppID
        ) AS DESC1,
        1 AS Trn
    FROM tblphmrefill
    UNION (
        SELECT invoicerefundphm.phmItem AS pItem,
            invoice.Bdate,
            invoicerefundphm.Qty,
            CONCAT('Refund_', billno, '_', invoice.BName) AS DESC1,
            1 AS Trn
        FROM invoice
        INNER JOIN invoicerefundphm 
        ON invoice.BilID = invoicerefundphm.Billno
    )
    UNION (
        SELECT invoicephm.phmItem AS PItem,
            invoice.BDate,
            invoicephm.Qty,
            CONCAT('Sales_', billno, '_', invoice.BName),
            0 AS Trn
        FROM invoicephm
        INNER JOIN invoice 
        ON invoicephm.Billno = invoice.BilID 
        ORDER BY invoice.bdate, invoice.bilid ASC
    )
),
calc_credit_debit AS (
    SELECT 
        PItem,
        BDate,
        Trn,
        If((trn=1 AND qty>0), Qty, 0) AS Credit,
        If(trn=0 OR qty<0, abs(Qty), 0) AS Debit,
        DESC1
    FROM raw_data
),
calc_bal AS (
    SELECT 
        PItem,
        BDate,
        Credit,
        Debit,
        Credit - Debit AS Bal,
        DESC1,
        sum(ifnull(Bal, 0)) OVER (PARTITION BY PItem ORDER BY BDate, DESC1) AS balance
    FROM calc_credit_debit
)
SELECT PItem, BDate, Credit, Debit, balance, DESC1
FROM calc_bal
ORDER BY BDate, DESC1 ASC;

方法3:使用LATERAL派生表(MariaDB 10.2+支持)

通过LATERAL在同一层派生表中复用计算结果:

SELECT pitem,
    Bdate,
    credit,
    debit, 
    sum(ifnull(bal, 0)) OVER (PARTITION BY pitem ORDER BY bdate, DESC1) balance, 
    DESC1
FROM (
    SELECT 
        a.Pitem,
        a.Bdate,
        a.Trn,
        calc.Credit,
        calc.Debit,
        calc.Credit - calc.Debit AS Bal,
        a.Desc1
    FROM (
        SELECT tblphmrefill.RfItem AS PItem,
            tblphmrefill.rfDate AS BDate,
            tblphmrefill.rfQty AS Qty,
            CONCAT(
                if(rfqty>0, 'Purchase No_', 'Discard_BMW_'), 
                coalesce(tblphmrefill.RfInvoice, 0), '_Vend_', rfsuppID
            ) AS DESC1,
            1 AS Trn
        FROM tblphmrefill
        UNION (
            SELECT invoicerefundphm.phmItem AS pItem,
                invoice.Bdate,
                invoicerefundphm.Qty,
                CONCAT('Refund_', billno, '_', invoice.BName) AS DESC1,
                1 AS Trn
            FROM invoice
            INNER JOIN invoicerefundphm 
            ON invoice.BilID = invoicerefundphm.Billno
        )
        UNION (
            SELECT invoicephm.phmItem AS PItem,
                invoice.BDate,
                invoicephm.Qty,
                CONCAT('Sales_', billno, '_', invoice.BName),
                0 AS Trn
            FROM invoicephm
            INNER JOIN invoice 
            ON invoicephm.Billno = invoice.BilID 
            ORDER BY invoice.bdate, invoice.bilid ASC
        )
    ) AS a
    LATERAL (
        SELECT 
            If((a.trn=1 AND a.qty>0), a.Qty, 0) AS Credit,
            If(a.trn=0 OR a.qty<0, abs(a.Qty), 0) AS Debit
    ) AS calc
) phmtbatch_int 
Order by Bdate, DESC1 Asc 

以上方法均符合MariaDB视图语法要求,无需使用用户变量,同时避免了重复计算逻辑。

内容的提问来源于stack exchange,提问作者Viney Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 09:17:11