【问题标题】:Column doesn't exist when using WITH statement PostgreSQL使用 WITH 语句 PostgreSQL 时列不存在
【发布时间】:2021-03-27 07:09:02
【问题描述】:

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

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 之外进行试验。

【问题讨论】:

  • 只是你的函数的WITH.....; 部分返回一个错误(“错误:列“深度”不存在位置:68”),您应该首先修复它。如果函数的定义不正确,则无法使用函数,因此“我期望的是我可以正常使用该函数”是 ??见:sqlfiddle.com/#!15/9caba/18

标签: sql postgresql plpgsql column-alias


【解决方案1】:

表达式depth=1 测试列depth 是否等于值1 并返回一个布尔值。但是你永远不会给这个布尔表达式一个合适的名字。

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

您是否打算将值 1 命名为 depth 的别名?那么你需要在递归部分使用1 as depthip.depth + 1 as depth

在最终选择中,您遇到了同样的错误 - 使用布尔表达式而不是列别名

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

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

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

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

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');

【讨论】:

  • 我尝试使用自定义类型设置参数类型“CREATE TYPE IDType AS (i​​d uuid);”,但是我不知道如何调用带有表参数的函数,你能修改一下在参数中使用该类型回答并演示如何调用该函数?
  • 而且当我运行您的答案时,有一个错误提示“SQL 错误 [42P13]: ERROR: VARIADIC parameter must be an array”。
  • @muazhari:我已经修正了可变参数的定义
  • 只需将参数声明为variadic p_root_id idtype[])即可。为什么要创建一个额外的type 来封装uuid?这完全是矫枉过正。但如果你真的想要一种特殊的数据类型,domain 可能是更好的选择。
  • 额外的列有什么用?在输出中?附加参数?一旦您有一个解决您最初问题的答案,请不要将您的问题扩展到一个全新的主题。如果您还有其他问题,那么ask 另一个问题
猜你喜欢
  • 1970-01-01
  • 2018-10-26
  • 2011-08-13
  • 2020-07-08
  • 1970-01-01
  • 2018-08-23
  • 2016-11-03
  • 2022-12-31
  • 2017-09-29
相关资源
最近更新 更多