如何在PL / pgSQL中的动态选择查询上使用迭代器变量?

时间:2014-11-18 11:10:37

标签: sql postgresql iterator plpgsql window-functions

我需要在PostgreSQL 9.1中提供现金流量报告。

在余额行(BalanceLine)中,函数(getBalanceLine)负责获取银行账户及其信用或借记头的总和。

需要将数字行(num_row)减少到零,以向函数getBalanceLine指示临时表必须DROP。该临时表将平衡线结果保存为计算的“工作记忆”。

问题是函数num_row上的getBalanceLine变量没有根据我LOOP子句的说明而减少。

以下是PL / pgSQL函数:

DROP FUNCTION IF EXISTS report_cash_flow();

CREATE FUNCTION report_cash_flow() RETURNS TABLE(Date date, Company varchar(128), Bank varchar(64), Partner varchar(128), Document varchar(64), Credit numeric, Debit numeric, Line integer, BalanceLine numeric) AS 
$BODY$

DECLARE
    num_row int := 0;

BEGIN

    num_row = ( SELECT
                COUNT(*)
            FROM
                account_move_line aml
                INNER JOIN  res_company rc ON rc.id = aml.company_id
                INNER JOIN  res_partner rp ON rp.id = aml.partner_id
                INNER JOIN  account_journal aj ON aj.id = aml.journal_id
                INNER JOIN  account_account aa ON aa.id = aml.account_id
            WHERE
                aml.state = 'valid'
                AND aml.reconcile_id IS NULL );


    FOR Date, Company, Bank, Partner, Document, Credit, Debit, Line, BalanceLine IN

        SELECT
            aml.date_maturity AS Date,
            rc.name AS Company,
            aj.name AS Bank,
            rp.name AS Partner,
            aml.name AS Document,
            aml.credit AS Credit,
            aml.debit AS Debit,
            num_row AS Line,
            getBalanceLine(aml.credit, aml.debit, num_row) AS BalanceLine
        FROM
            account_move_line aml
            INNER JOIN  res_company rc ON rc.id = aml.company_id
            INNER JOIN  res_partner rp ON rp.id = aml.partner_id
            INNER JOIN  account_journal aj ON aj.id = aml.journal_id
            INNER JOIN  account_account aa ON aa.id = aml.account_id
        WHERE
            aml.state = 'valid'
            AND aml.reconcile_id IS NULL
        ORDER BY
            Document

        LOOP
            num_row := num_row - 1;
            RAISE NOTICE '%', num_row;
            RETURN NEXT;

        END LOOP;

    RETURN;

END;
$BODY$
LANGUAGE 'plpgsql';

SELECT * FROM report_cash_flow();

以下是查询的结果:

date        company     bank    partner     document    credit  debit     line  balanceline
01/10/2013  Company 1   Bank 1  Partner 1   00003621/1  0.00    520.56    4       1.024,00
01/10/2013  Company 1   Bank 2  Partner 2   00003622/1  32.00   0.00      4         922,00
09/10/2014  Company 1   Bank 1  Partner 3   00003623/1  0.00    18009.65  4     -17.087,65
10/10/2014  Company 1   Bank 2  Partner 4   00003624/1  6126.95 0.00      4     -10.960,70

以下是RAISE NOTICE '%', num_row

的结果
NOTICE:  4
NOTICE:  3
NOTICE:  2
NOTICE:  1

1 个答案:

答案 0 :(得分:1)

提供FOR循环行的查询在循环的第一次迭代之前执行 一次 。您必须在循环中调用函数getBalanceLine(),而不是在基本查询中。

然而,你的整个方法都是不必要的冗长和昂贵。

简化,第1步

CREATE FUNCTION report_cash_flow()
  RETURNS TABLE(Date date, Company text, Bank text, Partner text, Document text, Credit numeric, Debit numeric, Line integer, BalanceLine numeric) AS 
$func$
DECLARE
    _iterator int := 0;
BEGIN
    FOR Date, Company, Bank, Partner, Document, Credit, Debit, Line IN
        SELECT  aml.date_maturity AS Date -- alias useless
                rc.name AS Company,
                aj.name AS Bank,
                rp.name AS Partner,
                aml.name AS Document,
                aml.credit AS Credit,
                aml.debit AS Debit,
                count(*) OVER () AS Line  -- = initial num_row
        FROM    account_move_line aml
        JOIN    res_company       rc ON rc.id = aml.company_id
        JOIN    res_partner       rp ON rp.id = aml.partner_id
        JOIN    account_journal   aj ON aj.id = aml.journal_id
        JOIN    account_account   aa ON aa.id = aml.account_id
        WHERE   aml.state = 'valid'
        AND     aml.reconcile_id IS NULL
        ORDER   BY Document
    LOOP
        BalanceLine := getBalanceLine(Credit, Debit, Line - _iterator);
        RETURN NEXT;
        _iterator := _iterator + 1;
        RAISE NOTICE '%', Line - _iterator;         
    END LOOP;
    RETURN;
END
$func$ LANGUAGE plpgsql;
  • 循环执行功能。
  • 无需为计数运行第二个查询。您可以在具有窗口功能的单个SELECT中执行此操作。
  • 在我的情况下,引入一个额外的变量来计算循环,_iterator 我在_前面加上变量以避免命名collisons。
  • 请不要引用语言名称plpgsql,它是一个标识符。

简化,第2步

您可以使用window functions这个单一的普通查询代替:

SELECT aml.date_maturity AS Date
     , rc.name AS Company
     , aj.name AS Bank
     , rp.name AS Partner
     , aml.name AS Document
     , aml.credit
     , aml.debit
     , count(*) OVER () AS Line  -- or the decreasing number?
     , getBalanceLine(aml.credit
                    , aml.debit
                    , count(*) OVER () + 1 -
                      row_number() OVER (ORDER  BY Document)) AS BalanceLine 
FROM   account_move_line aml
JOIN   res_company       rc ON rc.id = aml.company_id
JOIN   res_partner       rp ON rp.id = aml.partner_id
JOIN   account_journal   aj ON aj.id = aml.journal_id
JOIN   account_account   aa ON aa.id = aml.account_id
WHERE  aml.state = 'valid'
AND    aml.reconcile_id IS NULL
ORDER  BY Document;

或使用更复杂的:

count(*) OVER (ORDER BY Document
               ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)

更多解释: