我的问题是Erwin Brandstetter在this thread关于正确使用WITH
的优秀答案的某种延伸。
我的旧查询如下所示:
WITH x AS (
INSERT INTO d (dm_id)
SELECT dm_id
FROM dm, import i
WHERE dm.dm_name = i.dm_name
RETURNING d_id
), y AS (
INSERT INTO z (d_id)
SELECT d_id
FROM x
RETURNING z_id
)
INSERT INTO port (z_id)
SELECT z_id
FROM y;
这就像一个魅力。但是现在,添加了另一个表(r
)(与表d
相同的结构),并且可能需要将d_id
或r_id
添加到表中z
。这取决于表dm_name
中rm_name
或import
是否为空。所以我的理论方法是这样的:
SELECT dm_name, rm_name
,CASE WHEN dm_name != '' THEN
WITH x AS (
INSERT INTO d (dm_id)
SELECT dm_id
FROM dm, import i
WHERE dm.dm_name = i.dm_name
RETURNING d_id
), y AS (
INSERT INTO z (d_id)
SELECT d_id
FROM x
RETURNING z_id
)
INSERT INTO port (z_id)
SELECT z_id
FROM y
END
,CASE WHEN rm_name != '' THEN
WITH x AS (
INSERT INTO r (rm_id)
SELECT rm_id
FROM rm, import i
WHERE rm.rm_name = i.rm_name
RETURNING r_id
), y AS (
INSERT INTO z (r_id)
SELECT r_id
FROM x
RETURNING z_id
)
INSERT INTO port (z_id)
SELECT z_id
FROM y
END
FROM import;
但PostgreSQL告诉我:
“INSERT INTO port(z_id)”或其附近的语法错误
虽然查询的那部分应该是正确的,因为它已经有效 我希望你能帮我解决这个问题。 :)
为了更好地理解 - 这是表结构:
CREATE TABLE import (
dm_name character varying,
rm_name character varying
-- many other columns which are not relevant
);
CREATE TABLE dm (
dm_id integer NOT NULL, -- serial
dm_name character varying
-- plus more columns
);
CREATE TABLE d (
d_id integer NOT NULL, -- serial
dm_id integer -- references dm.dm_id
-- plus more columns
);
CREATE TABLE rm (
rm_id integer NOT NULL, -- serial
rm_name character varying
-- plus more columns
);
CREATE TABLE r (
r_id integer NOT NULL, -- serial
rm_id integer -- references rm.rm_id
-- plus more columns
);
CREATE TABLE z (
z_id integer NOT NULL, -- serial
r_id integer, -- references r.r_id
d_id integer -- references d.d_id
-- plus more columns
);
CREATE TABLE port (
p_id integer NOT NULL, -- serial
z_id integer, -- references z.z_id
-- plus more columns
);
导入表不知道在雾化过程中生成的ID。 dm和rm表用于已从导入表中提取的设备模型。 d和r表适用于实际设备。由于端口只能有r设备或d设备或者没有,因此引入了z表,在端口表中只有一个字段代表所有可能性。 d / r和dm / rm表不能组合,因为它们具有不同的特殊列,具体取决于设备类型。
答案 0 :(得分:3)
您无法在INSERT
表达式中嵌套CASE
语句。从我所看到的,这种完全不同的方法应该做到:
您实际上并不需要外部SELECT
。
dm_name
/ rm_name
在dm
/ rm
中定义为唯一,而非空(<> ''
)。您应该有一个CHECK
约束来确保。
d_id
中r_id
和z
的列默认值为NULL(默认值)。
dm_name
和rm_name
互斥如果两者都不在同一时间。
WITH d1 AS (
INSERT INTO d (dm_id)
SELECT dm.dm_id
FROM import
JOIN dm USING (dm_name)
RETURNING d_id
)
, r1 AS (
INSERT INTO r (rm_id)
SELECT rm.rm_id
FROM import
JOIN rm USING (rm_name)
RETURNING r_id
)
, z1 AS (
INSERT INTO z (d_id, r_id)
SELECT d_id, r_id
FROM d1 FULL JOIN r1 ON FALSE
RETURNING z_id
)
INSERT INTO port (z_id)
SELECT z_id
FROM z1;
FULL JOIN .. ON FALSE
生成一个派生表,其中d1
和r1
的所有行都为相应的其他列附加了NULL(两者之间没有重叠)。所以我们只需要一个INSERT
而不是两个。次要优化。
dm_name
和rm_name
可以共存WITH i AS (
SELECT dm.dm_id, rm.rm_id
FROM import
LEFT JOIN dm USING (dm_name)
LEFT JOIN rm USING (rm_name)
)
, d1 AS (
INSERT INTO d (dm_id)
SELECT dm_id FROM i WHERE dm_id IS NOT NULL
RETURNING dm_id, d_id
)
, r1 AS (
INSERT INTO r (rm_id)
SELECT rm_id FROM i WHERE rm_id IS NOT NULL
RETURNING rm_id, r_id
)
, z1 AS (
INSERT INTO z (d_id, r_id)
SELECT d1.d_id, r1.r_id
FROM i
LEFT JOIN d1 USING (dm_id)
LEFT JOIN r1 USING (rm_id)
WHERE d1.dm_id IS NOT NULL OR
r1.rm_id IS NOT NULL
RETURNING z_id
)
INSERT INTO port (z_id)
SELECT z_id FROM z1;
如果两个版本都不存在,两个版本也可以使用。
如果INSERT
没有返回行,则 SELECT
不插入任何内容。
如果必须处理可能与此操作冲突的并发写访问,则快速修复将在同一事务中运行此语句之前锁定所涉及的表。