使用 WITH 语句 PostgreSQL 时列不存在

问题描述

我想创建一个函数,用于获取节点遍历路径。

CREATE TYPE IDType AS (id uuid);

drop function F_ItemPath;
CREATE OR REPLACE FUNCTION F_ItemPath (item record)
RETURNS TABLE (item_id uuid,depth numeric)
AS $$
BEGIN
return QUERY
    WITH recursive item_path AS (
        SELECT ic.parent_item_id,depth=1
        from item_combination ic,item i 
        WHERE ic.child_item_id=i.id
        UNION all
        SELECT ic.parent_item_id,depth=ip.depth + 1
        FROM item_path ip,item_combination ic WHERE ip.parent_item_id=ic.child_item_id
        )
        SELECT item_id=ip.parent_item_id,depth=ip.depth FROM item_path ip;
END; $$
LANGUAGE plpgsql;

select * from F_ItemPath(('55D6F516-7D8F-4DF3-A4E5-1E3F505837A1','FFE2A4D3-267C-465F-B4B4-C7BB2582F1BC'))

有两个问题:

  1. 我尝试使用用户定义类型来设置参数类型CREATE TYPE IDType AS (id uuid);,但我不知道如何使用表参数调用函数
  2. 一个错误提示
sql Error [42703]: ERROR: column ip.depth does not exist
  Where: PL/pgsql function f_itempath(record) line 3 at RETURN QUERY

我期望的是我可以正常使用该函数并且可以从其他表中提供参数。

这是您可以尝试的完整查询http://sqlfiddle.com/#!15/9caba/1
我在 DBEAVER 应用程序中进行了查询,它会有一些不同的错误消息。
我建议你可以在 sqlfiddle 之外试验它。

解决方法

表达式 depth=1 测试列 depth 是否等于值 1 并返回一个布尔值。但是你从来没有给这个布尔表达式一个合适的名字。

此外,您不能将数字与布尔值相加,因此表达式 depth=ip.depth + 1 试图将 1truefalse 的值相加——这显然失败了。如果确实有效,它会再次将该值与列 depth 中的值进行比较。

您是否打算使用名称 1 为值 depth 设置别名?然后你需要在递归部分使用1 as depthip.depth + 1 as depth

在最后的选择中你有同样的错误 - 使用布尔表达式而不是列别名

还强烈建议使用 30 多年前 SQL 标准中引入的显式 JOIN 运算符。

使用 PL/pgSQL 来包装 SQL 查询也有点矫枉过正。一个SQL函数就够了。

使用无类型记录作为参数似乎非常可疑。它不允许您使用例如访问列item.id。但是鉴于您的示例调用,您似乎只想为查询的锚点(非递归)部分传递多个 ID。最好使用数组或 varadic 参数来完成,后者允许使用逗号列出多个参数。

所以你可能想要这样的东西:

drop function f_itempath;
CREATE OR REPLACE FUNCTION f_itempath(variadic p_root_id uuid[])
  RETURNS TABLE (item_id uuid,depth integer)
as
$$  
  WITH recursive item_path AS (
    SELECT ic.parent_item_id,1 as depth
    FROM item_combination ic
    WHERE ic.child_item_id = any(p_root_id) --<< no join needed to access the parameter
    UNION all
    SELECT ic.parent_item_id,ip.depth + 1
    FROM item_path ip
       JOIN item_combination ic ON ip.parent_item_id = ic.child_item_id
  )
  SELECT ip.parent_item_id as item_id,ip.depth 
  FROM item_path ip;
$$
language sql
stable;

然后就可以这样调用了(注意:参数周围没有括号)

select * 
from f_itempath('55d6f516-7d8f-4df3-a4e5-1e3f505837a1','ffe2a4d3-267c-465f-b4b4-c7bb2582f1bc');

select * 
from f_itempath('55d6f516-7d8f-4df3-a4e5-1e3f505837a1','ffe2a4d3-267c-465f-b4b4-c7bb2582f1bc','df366232-f200-4254-bad5-94e11ea35379');

select * 
from f_itempath('55d6f516-7d8f-4df3-a4e5-1e3f505837a1');