在 Postgres 的 2 个表中递归查询总和

Recursive query with sum in 2 tables in Postgres

我有 2 个表,我需要制作一个带有分页(限制和偏移)的过滤器:

  1. res_partner(id, name, parent_id) (250k 行)
  2. 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;