如何在 PL/pgSQL 中的动态选择查询中使用迭代器变量?
Posted
技术标签:
【中文标题】如何在 PL/pgSQL 中的动态选择查询中使用迭代器变量?【英文标题】:How to work with a iterator variable on a dynamic select query in PL/pgSQL? 【发布时间】:2014-11-18 11:10:36 【问题描述】:我需要在 PostgreSQL 9.1 中提供现金流量报告。
在余额行 (BalanceLine) 中,一个函数 (getBalanceLine) 负责获取银行账户的总和及其贷方或借方头寸。
数字行 (num_row
) 需要减少到零,以向函数 getBalanceLine
指示临时表必须是 DROP
ed。该临时表将平衡线结果保存为计算的“工作内存”。
问题是函数getBalanceLine
上的num_row
变量没有按照我的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】:为您的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
。
我在变量前面加上 _
以避免命名冲突。
不要引用语言名称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)
更多解释:
How to use a ring data structure in window functions【讨论】:
Erwin 我非常感谢您的帮助。诀窍是(在步骤 1 中)在 LOOP 子句上操作 BalanceLine。谢谢大家的解释。以上是关于如何在 PL/pgSQL 中的动态选择查询中使用迭代器变量?的主要内容,如果未能解决你的问题,请参考以下文章