我有以下两个表:
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 ...
机制忽略可能的重复键错误。但是,使用这种方法对我来说有两个问题:
对于postgres 9.4,我的想法是否可行?
答案 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的第一部分将返回现有员工,但第二部分将不返回行。
因此,在WITH中使用数据修改语句时,指定更新实际发生的顺序是不可预测的。所有语句都使用相同的快照执行(参见第13章),因此它们无法“看到”彼此对目标表的影响
这减轻了行更新的实际顺序的不可预测性的影响,并且意味着RETURNING数据是在不同的WITH子语句和主查询之间传递更改的唯一方式