使用 PostgreSQL v9.1。我有以下表格:
CREATE TABLE foo
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
type VARCHAR(60) NOT NULL UNIQUE
);
CREATE TABLE bar
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
description VARCHAR(40) NOT NULL UNIQUE,
foo_id BIGINT NOT NULL REFERENCES foo ON DELETE RESTRICT
);
假设第一个表foo
是这样填充的:
INSERT INTO foo (type) VALUES
( 'red' ),
( 'green' ),
( 'blue' );
bar
有没有办法通过引用表轻松插入行foo
?还是我必须分两步完成,首先查找foo
我想要的类型,然后在 中插入一个新行bar
?
这是一个伪代码示例,显示了我希望可以完成的操作:
INSERT INTO bar (description, foo_id) VALUES
( 'testing', SELECT id from foo WHERE type='blue' ),
( 'another row', SELECT id from foo WHERE type='red' );
您的语法几乎很好,需要在子查询周围加上一些括号,它会起作用:
在DB-Fiddle测试
另一种方法,如果您要插入很多值,则使用更短的语法:
清楚的
INSERT
LEFT [OUTER] JOIN
而不是[INNER] JOIN
意味着val
保留所有行中的行,即使在foo
. 而是NULL
输入 forfoo_id
(如果定义了列,则会引发异常NOT NULL
)。子查询中的
VALUES
表达式与@ypercube 的CTE 相同。公用表表达式提供了额外的功能,并且在大型查询中更易于阅读,但它们也构成了优化障碍(直到 Postgres 12)。当以上都不需要时,子查询通常会快一点。您可能需要显式类型转换。由于
VALUES
表达式不直接附加到表(如 inINSERT ... VALUES ...
),因此无法派生类型并且使用默认数据类型,除非显式键入。这可能不适用于所有情况。在第一行就足够了,其余的都在排队。INSERT
同时缺少 FK 行foo
要在单个 SQL 语句中动态创建缺失的条目,CTE 很有帮助:Postgres 9.6 的旧sqlfiddle - 在 9.1 中的工作方式相同。另请参阅下面的新小提琴!
注意要插入的另外两行。两者都是紫色的,目前尚不存在
foo
。两行来说明DISTINCT
第一个INSERT
语句中的需要。分步说明
第一个 CTE
sel
提供多行输入数据。val
可以将带有表达式的子查询VALUES
替换为表或子查询作为源。立即追加预先存在的LEFT JOIN
行。所有其他行都采用这种方式。foo
foo_id
type
foo_id IS NULL
第二个 CTE
ins
将不同的新类型 (foo_id IS NULL
) 插入到foo
中,并返回新生成的foo_id
- 以及type
要加入的插入行。最终的外部
INSERT
现在可以为每一行插入一个foo_id
:要么是预先存在的类型,要么是在步骤 2 中插入的。严格来说,两个插入都是“并行”发生的,但由于这是一条语句,默认
FOREIGN KEY
约束不会抱怨。默认情况下,引用完整性在语句的末尾强制执行。如果您同时运行多个这样的查询,则会出现很小的竞争条件。看:
如果有的话,真的只发生在高并发负载下。与另一个答案中宣传的缓存解决方案相比,机会非常小。
重复使用功能
创建一个 SQL 函数,该函数将复合类型的数组作为参数并用于
unnest(param)
代替VALUES
表达式。或者,如果此类数组的语法看起来过于混乱,请使用逗号分隔的字符串作为参数
_param
。例如表格:然后用它来替换
VALUES
上面语句中的表达式:Postgres 9.5 或更高版本中的 UPSERT 功能
为参数传递创建自定义行类型。我们可以没有它,但它更简单:
功能:
称呼:
db<>在这里摆弄
对于具有并发事务的环境,快速且坚如磐石。
除了上面的查询,这个函数...
... apply
SELECT
orINSERT
onfoo
:type
插入 FK 表中不存在的任何内容。假设大多数类型已经存在。... apply
INSERT
orUPDATE
(true "UPSERT") onbar
:如果description
已经存在,type
则更新它 - 但前提是它确实发生了变化。看:VARIADIC
...通过函数参数将值作为众所周知的行类型传递。注意默认最多 100 个函数参数!看:还有许多其他方法可以传递多行...
有关的:
抬头。您基本上需要 foo id 将它们插入到 bar 中。
不是postgres特定的,顺便说一句。(并且您没有那样标记它) - 这通常是 SQL 的工作方式。这里没有捷径。
但是,在应用程序方面,您可能在内存中缓存了 foo 项。我的表通常最多有 3 个唯一字段:
例子:
显然,当您想将某些内容链接到帐户时 - 从技术上讲,您首先必须获取 Id - 但鉴于标识符和代码一旦存在就永远不会改变,内存中的正缓存可以阻止大多数查找访问数据库。