Skip to content

POSTGRESQL-RECURSIVE

例子 ​

sql
WITH RECURSIVE r AS (  
    SELECT phpg_nbphs.organize_institution.organize_id, organize_institution.organize_name  
    FROM phpg_nbphs.organize_institution  
    WHERE organize_id = 11  
      and invalid = '1'  
    union ALL  
    SELECT phpg_nbphs.organize_institution.organize_id, organize_institution.organize_name  
    FROM phpg_nbphs.organize_institution,  
         r  
    WHERE organize_institution.parent_id = r.organize_id  
      and organize_institution.invalid = '1'  
)  
select * from r;
WITH RECURSIVE r AS (  
    SELECT phpg_nbphs.organize_institution.organize_id, organize_institution.organize_name  
    FROM phpg_nbphs.organize_institution  
    WHERE organize_id = 11  
      and invalid = '1'  
    union ALL  
    SELECT phpg_nbphs.organize_institution.organize_id, organize_institution.organize_name  
    FROM phpg_nbphs.organize_institution,  
         r  
    WHERE organize_institution.parent_id = r.organize_id  
      and organize_institution.invalid = '1'  
)  
select * from r;

这个写的不错 PostgreSQL中RECURSIVE递归查询使用总结

sql
create table document_directories  
(  
    id         bigserial                                          not null  
        constraint document_directories_pk  
            primary key,  
    name       text                                               not null,  
    created_at timestamp with time zone default CURRENT_TIMESTAMP not null,  
    updated_at timestamp with time zone default CURRENT_TIMESTAMP not null,  
    parent_id  bigint                   default 0                 not null  
);  
  
comment on table document_directories is '文档目录';  
  
comment on column document_directories.name is '名称';  
  
comment on column document_directories.parent_id is '父级id';  
  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (1, '中国', '2020-03-28 15:55:27.137439', '2020-03-28 15:55:27.137439', 0);  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (2, '上海', '2020-03-28 15:55:40.894773', '2020-03-28 15:55:40.894773', 1);  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (3, '北京', '2020-03-28 15:55:53.631493', '2020-03-28 15:55:53.631493', 1);  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (4, '南京', '2020-03-28 15:56:05.496985', '2020-03-28 15:56:05.496985', 1);  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (5, '浦东新区', '2020-03-28 15:56:24.824672', '2020-03-28 15:56:24.824672', 2);  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (6, '徐汇区', '2020-03-28 15:56:39.664924', '2020-03-28 15:56:39.664924', 2);  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (7, '漕宝路', '2020-03-28 15:57:14.320631', '2020-03-28 15:57:14.320631', 6);
create table document_directories  
(  
    id         bigserial                                          not null  
        constraint document_directories_pk  
            primary key,  
    name       text                                               not null,  
    created_at timestamp with time zone default CURRENT_TIMESTAMP not null,  
    updated_at timestamp with time zone default CURRENT_TIMESTAMP not null,  
    parent_id  bigint                   default 0                 not null  
);  
  
comment on table document_directories is '文档目录';  
  
comment on column document_directories.name is '名称';  
  
comment on column document_directories.parent_id is '父级id';  
  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (1, '中国', '2020-03-28 15:55:27.137439', '2020-03-28 15:55:27.137439', 0);  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (2, '上海', '2020-03-28 15:55:40.894773', '2020-03-28 15:55:40.894773', 1);  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (3, '北京', '2020-03-28 15:55:53.631493', '2020-03-28 15:55:53.631493', 1);  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (4, '南京', '2020-03-28 15:56:05.496985', '2020-03-28 15:56:05.496985', 1);  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (5, '浦东新区', '2020-03-28 15:56:24.824672', '2020-03-28 15:56:24.824672', 2);  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (6, '徐汇区', '2020-03-28 15:56:39.664924', '2020-03-28 15:56:39.664924', 2);  
INSERT INTO document_directories (id, name, created_at, updated_at, parent_id) VALUES (7, '漕宝路', '2020-03-28 15:57:14.320631', '2020-03-28 15:57:14.320631', 6);

查询,根据名字进行查询

sql
WITH RECURSIVE r AS (  
    SELECT id, name  
    FROM document_directories where id = 1  
    union  
    SELECT x.id, x.name  
    FROM document_directories x,  
         r  
    WHERE x.parent_id = r.id  
) select * from r;


-- 重新拼接了名字
WITH RECURSIVE r AS (  
    SELECT id, name  
    FROM document_directories where id = 1  
    union  
    SELECT x.id, r.name || '-' || x.name  
    FROM document_directories x,  
         r  
    WHERE x.parent_id = r.id  
) select * from r;

-- 结果如下
1,中国
2,中国-上海
3,中国-北京
4,中国-南京
5,中国-上海-浦东新区
6,中国-上海-徐汇区
7,中国-上海-徐汇区-漕宝路
WITH RECURSIVE r AS (  
    SELECT id, name  
    FROM document_directories where id = 1  
    union  
    SELECT x.id, x.name  
    FROM document_directories x,  
         r  
    WHERE x.parent_id = r.id  
) select * from r;


-- 重新拼接了名字
WITH RECURSIVE r AS (  
    SELECT id, name  
    FROM document_directories where id = 1  
    union  
    SELECT x.id, r.name || '-' || x.name  
    FROM document_directories x,  
         r  
    WHERE x.parent_id = r.id  
) select * from r;

-- 结果如下
1,中国
2,中国-上海
3,中国-北京
4,中国-南京
5,中国-上海-浦东新区
6,中国-上海-徐汇区
7,中国-上海-徐汇区-漕宝路

递归限制深度,例如最深只有3级,直接限制住,避免不必要的查询。

sql
WITH RECURSIVE r AS (  
    SELECT id, name, 1 deep  
    FROM document_directories where id = 1  
    union  
    SELECT x.id, r.name || '-' || x.name, r.deep + 1  
    FROM document_directories x,  
         r  
    WHERE x.parent_id = r.id and r.deep < 3  
) select * from r;

1,中国,1
2,中国-上海,2
3,中国-北京,2
4,中国-南京,2
5,中国-上海-浦东新区,3
6,中国-上海-徐汇区,3
WITH RECURSIVE r AS (  
    SELECT id, name, 1 deep  
    FROM document_directories where id = 1  
    union  
    SELECT x.id, r.name || '-' || x.name, r.deep + 1  
    FROM document_directories x,  
         r  
    WHERE x.parent_id = r.id and r.deep < 3  
) select * from r;

1,中国,1
2,中国-上海,2
3,中国-北京,2
4,中国-南京,2
5,中国-上海-浦东新区,3
6,中国-上海-徐汇区,3

说明 ​

流程说明

1、计算非递归项。对UNION(但不对UNION ALL),抛弃重复行。把所有剩余的行包括在递归查询的结果中,并且也把它们放在一个临时的工作表中。
2、只要工作表不为空,重复下列步骤:
a计算递归项,用当前工作表的内容替换递归自引用。对UNION(不是UNION ALL),抛弃重复行以及那些与之前结果行重复的行。将剩下的所有行包括在递归查询的结果中,并且也把它们放在一个临时的中间表中。
b用中间表的内容替换工作表的内容,然后清空中间表。

WITH RECURSIVE 使用限制 ​

1、 如果在recursive term中使用LEFT JOIN,自引用必须在“左”边
2、 如果在recursive term中使用RIGHT JOIN,自引用必须在“右”边
3、 recursive term中不允许使用FULL JOIN
4、 recursive term中不允许使用GROUP BY和HAVING
5、 不允许在recursive term的WHERE语句的子查询中使用CTE的名字
6、 不支持在recursive term中对CTE作aggregation
7、 recursive term中不允许使用ORDER BY
8、 LIMIT / OFFSET不允许在recursive term中使用
9、 FOR UPDATE不可在recursive term中使用
10、 recursive term中SELECT后面不允许出现引用CTE名字的子查询
11、 同时使用多个CTE表达式时,不允许多表达式之间互相访问(支持单向访问)
12、 在recursive term中不允许使用FOR UPDATE

CTE 优缺点 ​

1、 可以使用递归 WITH RECURSIVE,从而实现其它方式无法实现或者不容易实现的查询
2、 当不需要将查询结果被其它独立查询共享时,它比视图更灵活也更轻量
3、 CTE只会被计算一次,且可在主查询中多次使用
4、 CTE可极大提高代码可读性及可维护性
5、 CTE不支持将主查询中where后的限制条件push down到CTE中,而普通的子查询支持