选择现有值或插入新行

时间:2018-05-21 18:18:42

标签: sql postgresql

我刚开始使用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(字符变化)SQL语句中的第3行

2 个答案:

答案 0 :(得分: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。但我想你想要返回结果。将身体改为

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

这样做。

答案 1 :(得分:1)

在您显示的功能中,不需要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版本的解决方案: