如果找不到引用表中的值,则在表中插入行

时间:2018-05-30 13:34:30

标签: sql postgresql

我有以下两个表:

CREATE TABLE employee
(
  id serial NOT NULL,
  name character(32) NOT NULL,
  CONSTRAINT employee_id_pkey PRIMARY KEY (id),
  CONSTRAINT employee_name_ukey UNIQUE (name)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE employee
  OWNER TO postgres;

CREATE TABLE worklog
(
  id serial NOT NULL,
  activity character(32),
  employee integer NOT NULL,
  "time" timestamp without time zone NOT NULL DEFAULT now(),
  CONSTRAINT log_id_pkey PRIMARY KEY (id),
  CONSTRAINT fk_employee FOREIGN KEY (employee)
      REFERENCES employee (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
  OIDS=FALSE
);
ALTER TABLE worklog
  OWNER TO postgres;

-- Index: fki_employee

-- DROP INDEX fki_employee;

CREATE INDEX fki_employee
  ON worklog
  USING btree
  (employee);

我想做这样的事情:

insert into
    worklog (activity, employee)
values
('work in progress',
    coalesce(
        (select id from employee where name = 'jonathan'),
        (insert into employee (name) values ('jonathan') returning id)
    )
);

但是,这会返回错误:

ERROR:  syntax error at or near "into"
LINE 8:   (insert into employee (name) values ('jonathan') returning...)

我已经在某处读过,我可以事先在employee中插入'name',并使用ON CONFLICT ...机制忽略可能的重复键错误。但是,使用这种方法对我来说有两个问题:

  1. 我正在使用postgres 9.4和AFAIK ON CONFLICT构造需要9.5
  2. 更重要的是,员工不在数据库中的情况极为罕见。因此,大多数情况下,领先的插入只是浪费时间。
  3. 对于postgres 9.4,我的想法是否可行?

1 个答案:

答案 0 :(得分:1)

这样的事情应该有效:

with new_name as (
   insert into employee (name) 
   select 'jonathan'
   where not exists (select * 
                     from employee
                     where name = 'jonathan')
   returning id
), name as (
   select id
   from employee
   where name = 'jonathan'
   union all
   select id
   from new_name
)
insert into worklog (activity, employee)
select 'work in progress', id 
from name;

请注意,这对于并发执行来说是安全 - 如果两个事务同时运行,您可能会使用不同的ID两次使用相同的员工名称。

第二个CTE name只返回一行。 如果在第一步中插入了一行,则第二个CTE将看不到它,因此union的第二部分将返回插入行的ID。如果未插入任何行,则UNION的第一部分将返回现有员工,但第二部分将不返回行。

Quote from the manual

  

因此,在WITH中使用数据修改语句时,指定更新实际发生的顺序是不可预测的。所有语句都使用相同的快照执行(参见第13章),因此它们无法“看到”彼此对目标表的影响

     

这减轻了行更新的实际顺序的不可预测性的影响,并且意味着RETURNING数据是在不同的WITH子语句和主查询之间传递更改的唯一方式

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

相关问题