我的问题是对 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
中。这取决于表 import
中的 dm_name
或 rm_name
是否为空。所以我的理论方法是这样的:
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 告诉我:
syntax error at or near "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 表无法组合,因为它们根据设备类型具有不同的特殊列。
最佳答案
你不能嵌套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
则不插入任何内容不返回行。
如果您必须处理可能与此操作冲突的并发写入访问,快速修复方法是在同一事务中运行此语句之前锁定相关表。
关于sql - 将数据修改 CTE 中的 INSERT 语句与 CASE 表达式组合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28992241/