【问题标题】:Function to select existing value or insert new row选择现有值或插入新行的功能
【发布时间】:2018-10-31 10:42:21
【问题描述】:

我是使用 PL/pgSQL 的新手,我正在尝试创建一个函数,该函数将查找现有行的 ID,或者在未找到时插入新行并返回新行ID。

下面函数中包含的查询本身可以正常工作,并且函数可以正常创建。但是,当我尝试运行它时,我收到一条错误消息,指出“错误:列引用“id”不明确”。谁能找出我的问题,或者提出更合适的方法来解决这个问题?

create or replace function sp_get_insert_company(
    in company_name varchar(100)
)
returns table (id int)
as $$
begin
    with s as (
        select 
            id
        from 
            companies
        where name = company_name
    ), 
    i as (
        insert into companies (name)
        select company_name 
        where not exists (select 1 from s)
        returning id
    )
    select id
    from i
    union all
    select id
    from s;
end;
$$ language plpgsql;

这就是我调用函数的方式:

select sp_get_insert_company('TEST')

这是我得到的错误:

SQL 错误 [42702]:错误:列引用“id”不明确
详细信息:它可以引用 PL/pgSQL 变量或表列。
其中:PL/pgSQL 函数 sp_get_insert_company(character varying) SQL 语句的第 3 行

【问题讨论】:

  • 嗯,我认为 PL SQL 是在询问您返回的是哪个 ids.id? i.id?
  • @JacobH 我刚刚尝试了来自sticky bit的答案,以定义id的含义(s.idi.id),但它并没有解决问题。我认为这不会有什么不同,因为我 union正在处理两个单独的查询。
  • 尝试为所有内容设置别名,包括 CTE(例如:select c.id from companies c where c.name = company_name),看看这是否能让您更接近。

标签: sql postgresql


【解决方案1】:

正如消息所说,id 在那里两次。一次在查询中,一次在返回类型的表定义中。不知何故,这发生了冲突。

尝试在任何地方限定列表达式。

...
with s as (
    select 
        companies.id
    from 
        companies
    where name = company_name
), 
i as (
    insert into companies (name)
    select company_name 
    where not exists (select 1 from s)
    returning companies.id
)
select i.id
from i
union all
select s.id
from s;
...

通过限定列表达式,DBMS 不再将表的id 与返回类型定义中的id 混淆。

下一个问题是,您的SELECT 没有目标。它会告诉您改为使用PERFORM。但我假设你想返回结果。将body改为

...
RETURN QUERY (
with s as (
    select 
        companies.id
    from 
        companies
    where name = company_name
), 
i as (
    insert into companies (name)
    select company_name 
    where not exists (select 1 from s)
    returning companies.id
)
select i.id
from i
union all
select s.id
from s);
...

这样做。

【讨论】:

  • 我刚刚尝试过,但并没有什么不同。如果确实如此,我会感到惊讶,因为union 接受两个单独的查询并将结果合并在一起。那里不应该有歧义。
  • @RToyo。嗯,你是对的。让我再想想。对不起。
  • @RToyo:原因是我最初错误地假设的另一个原因。但解决方法是一样的。
  • 谢谢你,成功了!感谢您指出我需要return query
【解决方案2】:

在您显示的函数中,不需要returns table (id int)应该总是只返回一个整数 ID。简化为RETURNS int。这也使ERROR: column reference "id" is ambiguous 消失了,因为我们隐式删除了OUT 参数id(在整个功能块中可见)。

也不需要LANGUAGE plpgsql。可以简单地是LANGUAGE sql,那么你也不需要添加RETURNS QUERY。所以:

CREATE OR REPLACE FUNCTION sp_get_insert_company(_company_name text)
  RETURNS int AS
$func$
   WITH s as (
     select c.id  -- still good style to table-qualify all columns
     from   companies c
     where  c.name = _company_name
   ), 
   i as (
     insert into companies (name)
     select _company_name
     where  not exists (select 1 from s)
     returning id
   )
   select s.id from s 
   union all
   select i.id from i
   LIMIT 1;  -- to optimize performance
$func$  LANGUAGE sql;

除了它仍然存在并发问题。在这个密切相关的答案中为您未公开的 Postgres 版本找到合适的解决方案:

【讨论】:

  • 感谢 Erwin 的扩展解释。我最初将该函数编写为一个简单的 TSQL 过程,然后开始添加额外的定义以尝试使其工作。并感谢您指出select or insert 的问题;回家后我会仔细阅读,然后开始对我的代码进行一些更新。
猜你喜欢
  • 2023-04-04
  • 2021-07-03
  • 1970-01-01
  • 2019-11-28
  • 1970-01-01
  • 2020-07-15
  • 1970-01-01
  • 2019-06-13
  • 2010-10-26
相关资源
最近更新 更多