如果存在现有记录,则返回行,但是如果插入新记录,则不返回行

时间:2018-12-23 05:57:58

标签: postgresql

两个表。作者和书本

我正在将一本书添加到书桌中。 如果在作者表中已经列出了作者,则获取作者的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 
;

1 个答案:

答案 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');