两个表。作者和书本
我正在将一本书添加到书桌中。 如果在作者表中已经列出了作者,则获取作者的ID并将其插入到Book行中。 如果作者不在作者表中,则插入一个新作者,并使用ID插入到Book行中。
此功能运行良好。 数据库将正确响应并使用下面的代码(不是实际代码,而是经过改进的版本),并适当地引用或创建行。
我还希望查询返回Book行,这很好。 在所有经过测试的条件下,“书”行始终会返回,无论是具有现有作者的书还是具有已知作者的书。
当我现在想将它与author表连接起来以重新获得作者详细信息时,就会出现问题。
现在-> 如果我插入具有已知作者的书,则该功能非常完善,并且可以按预期完美返回该行。 如果我插入具有新作者的书,则仍会创建新作者,仍会插入新书,但会返回零行。
我不确定为什么会发生这种情况,或者我将如何处理该行。
CREATE TABLE author (id PRIMARY KEY, name VARCHAR (255));
CREATE TABLE book (id PRIMARY KEY, title VARCAR (255), author REFERENCES author (id));
WITH
s AS (
SELECT id FROM author
WHERE name = 'British Col'
),
i AS (
INSERT INTO author(name)
SELECT ('Eoin Colfer')
WHERE NOT EXISTS (select 1 from s)
RETURNING id
),
j AS (
SELECT id FROM s
UNION ALL
SELECT id FROM i
),
ins AS (
INSERT INTO book
(title, author)
SELECT 'Artemis Fowl', j.id
FROM j
RETURNING *
)
SELECT ins.*, author.*
FROM ins
JOIN author
ON ins.author = author.id
;
答案 0 :(得分:0)
这与PostgreSQL中常用表表达式的行为有关。
根据文档(https://www.postgresql.org/docs/current/queries-with.html):
WITH中的子语句彼此并发执行 并与主要查询。因此,在使用数据修改时 WITH中的语句,指定的实际更新顺序 发生是不可预测的。所有语句都以相同的方式执行 快照(请参阅第13章),因此他们无法“看到”彼此的影响 在目标表上。这减轻了 行更新的实际顺序不可预测,这意味着 返回数据是沟通变更之间的唯一方法 与WITH子语句和主查询不同。一个例子 是在
WITH t AS ( UPDATE products SET price = price * 1.05 RETURNING * ) SELECT * FROM products;
外部SELECT将在操作之前返回原始价格 更新...
最后一句话(在代码段下面)很关键。
最后,您针对author表的查询返回的数据与在CTE中插入语句之前 一样。
另一种方法是在可以使用变量的函数中完成这项工作。
首先,建议对表进行一些更改:
CREATE TABLE author
(
id SERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE -- Unique for ON CONFLICT later
);
CREATE TABLE book
(
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
author_id INT NOT NULL REFERENCES author (id),
UNIQUE (title, author_id) -- Prevent duplicates
);
示例功能
CREATE OR REPLACE FUNCTION add_book (in_book_title TEXT, in_author_name TEXT)
RETURNS TABLE
(
author_id INT,
book_id INT,
author_name TEXT,
book_title TEXT
)
AS $$
#variable_conflict use_column
DECLARE
var_author_id INT;
var_book_id INT;
BEGIN
-- Upsert author, return id
INSERT INTO author (name)
VALUES (in_author_name)
ON CONFLICT (name) DO
UPDATE SET name = EXCLUDED.name -- Do update to allow use of returning
RETURNING id INTO var_author_id;
-- Upsert book, return id
INSERT INTO book (title, author_id)
VALUES (in_book_title, var_author_id)
ON CONFLICT (title, author_id) DO
UPDATE SET title = EXCLUDED.title -- Do update to allow use of returning
RETURNING id INTO var_book_id;
-- Return the record using your join (similar)
RETURN QUERY
SELECT a.id, b.id, a.name, b.title
FROM author a
INNER JOIN book b
ON a.id = b.author_id
WHERE b.id = var_book_id;
END;
$$ LANGUAGE PLPGSQL VOLATILE;
用法:
SELECT * FROM add_book('Artemis Fowl', 'Eoin Colfer');