在 Postgres 的 2 个表中递归查询总和
Recursive query with sum in 2 tables in Postgres
我有 2 个表,我需要制作一个带有分页(限制和偏移)的过滤器:
- res_partner(id, name, parent_id) (250k 行)
- account_invoice(id, amount_untaxed, partner_id, created_date) (700k 行)
一个合作伙伴可以有很多 child 和很多帐户发票。
我需要在一个时间段内将所有客户和总发票(amount_untaxed 的总和)关联到他和他们的 child。
示例:
res_partner
id --- name --- parent_id
1 --- Jon
2 --- Snow ---- 1
3 --- Sam ----- 2
account_invoice
id --- amount_untaxed --- partner_id --- created_date
1 ------------------ 5.00 ---------1--------------'2015-09-29 21:37:39.427189'
2 ------------------ 7.00 ---------2--------------'2015-09-29 21:37:39.427189'
3 ------------------ 3.00 ---------3--------------'2015-09-29 21:37:39.427189'
4 ------------------ 9.00 ---------1--------------'2015-09-29 21:37:39.427189'
希望得到:
res_partner --- amount
1 -----------------24
2 -----------------10
3 -----------------3
您可以使用递归存储过程来计算每条记录的值。
CREATE OR REPLACE FUNCTION fnTree(pid int)
RETURNS numeric
AS $$
DECLARE total numeric;
BEGIN
WITH RECURSIVE tree(id, parent_id) AS
(
SELECT id, parent_id
FROM res_partner
WHERE id = pid
UNION ALL
SELECT rp.id, rp.parent_id
FROM res_partner rp
JOIN tree
ON rp.parent_id = tree.id
)
SELECT sum(amount_untaxed) into total
FROM account_invoice ai
WHERE partner_id in (SELECT id FROM tree);
RETURN total;
END;
$$
LANGUAGE plpgsql;
✓
select id, fnTree(id)
from res_partner;
id | fntree
-: | -----:
1 | 24.00
2 | 10.00
3 | 3.00
dbfiddle here
这是使用单个 SQL 查询的方法,它使用 WITH RECURSIVE
、整数数组、包含运算符 (<@
)、数组连接运算符 (||
), 以及将数组转换为结果集的函数 unnest()
.
在WITH RECURSIVE
里面我们为每条记录建立"paths",然后计算总和:
with recursive res(id, name, parent_id, path) as (
select id, name, parent_id, array[id]
from res_partner
where parent_id is null
union all
select
r2.id, r2.name, r2.parent_id, array[r2.id] || res.path
from res_partner r2
join res on res.id = r2.parent_id
)
select
id as res_partner,
--path, -- uncomment to debug
(
select sum(amount_untaxed)
from account_invoice
where
partner_id in (
select id
from res r_in
where r_out.path <@ r_in.path
)
) as amount
from res r_out;
我有 2 个表,我需要制作一个带有分页(限制和偏移)的过滤器:
- res_partner(id, name, parent_id) (250k 行)
- account_invoice(id, amount_untaxed, partner_id, created_date) (700k 行)
一个合作伙伴可以有很多 child 和很多帐户发票。
我需要在一个时间段内将所有客户和总发票(amount_untaxed 的总和)关联到他和他们的 child。 示例:
res_partner
id --- name --- parent_id
1 --- Jon
2 --- Snow ---- 1
3 --- Sam ----- 2
account_invoice
id --- amount_untaxed --- partner_id --- created_date
1 ------------------ 5.00 ---------1--------------'2015-09-29 21:37:39.427189'
2 ------------------ 7.00 ---------2--------------'2015-09-29 21:37:39.427189'
3 ------------------ 3.00 ---------3--------------'2015-09-29 21:37:39.427189'
4 ------------------ 9.00 ---------1--------------'2015-09-29 21:37:39.427189'
希望得到:
res_partner --- amount
1 -----------------24
2 -----------------10
3 -----------------3
您可以使用递归存储过程来计算每条记录的值。
CREATE OR REPLACE FUNCTION fnTree(pid int) RETURNS numeric AS $$ DECLARE total numeric; BEGIN WITH RECURSIVE tree(id, parent_id) AS ( SELECT id, parent_id FROM res_partner WHERE id = pid UNION ALL SELECT rp.id, rp.parent_id FROM res_partner rp JOIN tree ON rp.parent_id = tree.id ) SELECT sum(amount_untaxed) into total FROM account_invoice ai WHERE partner_id in (SELECT id FROM tree); RETURN total; END; $$ LANGUAGE plpgsql;
✓
select id, fnTree(id) from res_partner;
id | fntree -: | -----: 1 | 24.00 2 | 10.00 3 | 3.00
dbfiddle here
这是使用单个 SQL 查询的方法,它使用 WITH RECURSIVE
、整数数组、包含运算符 (<@
)、数组连接运算符 (||
), 以及将数组转换为结果集的函数 unnest()
.
在WITH RECURSIVE
里面我们为每条记录建立"paths",然后计算总和:
with recursive res(id, name, parent_id, path) as (
select id, name, parent_id, array[id]
from res_partner
where parent_id is null
union all
select
r2.id, r2.name, r2.parent_id, array[r2.id] || res.path
from res_partner r2
join res on res.id = r2.parent_id
)
select
id as res_partner,
--path, -- uncomment to debug
(
select sum(amount_untaxed)
from account_invoice
where
partner_id in (
select id
from res r_in
where r_out.path <@ r_in.path
)
) as amount
from res r_out;