将 Oracle 中的 CONNECT_BY_ISLEAF 替换为 Postgres

Replace CONNECT_BY_ISLEAF in Oracle to Postgres

如何将此查询转换为其等效的 postgresql 分层查询? 如何替换postgresql中的CONNECT_BY_ISLEAF函数?

SELECT emp_id,mgr_id,name,SYS_CONNECT_BY_PATH (name,'/') PATH ,CONNECT_BY_ISLEAF  ISLEAF   
FROM employee
START WITH  (emp_id = 345) 
CONNECT BY   NOCYCLE (PRIOR emp_id = mgr_id)

这在 Oracle 中使用递归 Sub-Query 分解子句(a.k.a。通用 Table 表达式)是等效的。它应该映射(可能在语法上有一些变化)到 PostgreSQL:

WITH cte ( emp_id, mgr_id, name, path, leaf ) AS (
  SELECT emp_id,
         mgr_id,
         name,
         '/' || name,
         CASE WHEN EXISTS( SELECT 1 FROM employee m WHERE m.mgr_id = e.emp_id )
              THEN 0 ELSE 1 END
  FROM   employee e
  WHERE  emp_id = 345
UNION ALL
  SELECT e.emp_id,
         e.mgr_id,
         e.name,
         c.path || '/' || e.name,
         CASE WHEN EXISTS( SELECT 1 FROM employee m WHERE m.mgr_id = e.emp_id )
              THEN 0 ELSE 1 END
  FROM   employee e
         INNER JOIN cte c
         ON( e.mgr_id = c.emp_id )
)
SELECT * FROM cte;

(注意:这不考虑分层查询的 NOCYCLE 子句 - 如果这是必要的,那么您将需要建立一种机制来消除这些连接。)

递归查询是使用 Postgres 中的递归 common table expression 完成的。

您可以模拟 Oracle 的 level,只需为每次迭代递增一个值,然后在外部查询中比较该值。

可以使用 sub-query 检查叶子 - 类似于 MT0 所做的

nocycle 可以通过记住所有已处理的行并向递归部分添加 where 条件来完成,如果员工已被处理则停止。

通过携带初始emp_id通过各级,还可以模拟Oracle的connect_by_root

with recursive cte (emp_id, mgr_id, name, path, level, visited, root_id) AS 
(
  select emp_id,
         mgr_id,
         name,
         '/' || name,
         1 as level, 
         array[emp_id] as visited, 
         emp_id as root_id
  from   employee e
  where  emp_id = 345
  union all
  select c.emp_id,
         c.mgr_id,
         c.name,
         concat_ws('/', p.path, c.name),
         p.level + 1, 
         p.visited || c.emp_id, 
         p.root_id
  from employee c
   join cte p on p.emp_id = c.mgr_id
  where c.emp_id <> all(p.visited)
)
SELECT e.*, 
       not exists (select * from cte p where p.mgr_id = e.emp_id) as is_leaf
FROM cte e;

在线示例:http://rextester.com/TSMVV17478