【问题标题】:SELECT or INSERT a row in one command在一个命令中选择或插入一行
【发布时间】:2011-10-07 00:46:02
【问题描述】:

我使用的是 PostgreSQL 9.0,并且我有一个表,其中只有一个人工键(自动递增序列)和另一个唯一键。 (是的,这个表是有原因的。:))我想通过另一个键查找一个 ID,或者,如果它不存在,则插入它:

SELECT id
FROM mytable
WHERE other_key = 'SOMETHING'

那么,如果不匹配:

INSERT INTO mytable (other_key)
VALUES ('SOMETHING')
RETURNING id

问题:是否可以通过在一个语句中执行这两项操作来节省到数据库的往返行程?如果它不存在,我可以插入该行:

INSERT INTO mytable (other_key)
SELECT 'SOMETHING'
WHERE NOT EXISTS (SELECT * FROM mytable WHERE other_key = 'SOMETHING')
RETURNING id

... 但这并没有给出现有行的 ID。有任何想法吗?对 other_key 有一个唯一的约束,如果有帮助的话。

【问题讨论】:

  • 很遗憾,您曾这么说“此表有理由”同时具有自动递增键和唯一键。如果可能的话,如果使用自动递增代理键,则应该始终拥有一个唯一且非空的键。否则,没有任何东西可以防止重复的真实信息。我并不是说代理键没有它们的用途,但普遍认为两者都有错误的想法是等待发生的数据质量灾难。
  • 处理可能的竞争条件的相关答案:stackoverflow.com/questions/15939902/…

标签: postgresql


【解决方案1】:

你试过联合吗?


编辑 - 这需要 Postgres 9.1:

create table mytable (id serial primary key, other_key varchar not null unique);

WITH new_row AS (
INSERT INTO mytable (other_key)
SELECT 'SOMETHING'
WHERE NOT EXISTS (SELECT * FROM mytable WHERE other_key = 'SOMETHING')
RETURNING *
)
SELECT * FROM new_row
UNION
SELECT * FROM mytable WHERE other_key = 'SOMETHING';

结果:

 id | other_key 
----+-----------
  1 | SOMETHING
(1 row)

【讨论】:

  • 我怀疑它会在 postgres 9.1 中工作。更糟糕的是,9.1 将允许写:with new_row as (insert ... returning *) select * from new_row ... union select * from ...
  • 有趣,+1。当我们升级到 9.1 时,我会记住这一点。
  • @EMP:我同意,很高兴看到这种能力;再说一次,我总体上期待可写的 CTE
  • 您也可以将SELECT * FROM mytable WHERE other_key = 'SOMETHING' 设为 CTE,因为您使用了两次。
  • @MattDiPasquale 我最近看到了,我看到的例子使用了UNION ALL,我不太清楚为什么,除了我认为它会产生性能。 UNIONUNION ALL 都应该只返回一条记录。
【解决方案2】:

不,没有允许您进行选择或插入的特殊 SQL 语法。您可以按照 Ilia 提到的方式创建存储过程,这意味着它不会从客户端到服务器进行往返,但仍会导致两个查询(实际上是三个,如果您计算存储过程本身)。

【讨论】:

  • @GiovanniP - 不,丹尼斯的答案只适用于 9.1,而不是 9.0,无论如何我读到这个问题是想要一个特殊的 sql 语句来做到这一点。
  • @TruongSinh - 有时是或否问题的答案是否定的。他们不可能总是有建设性的。在这种情况下,关于 9.0 版的答案是否定的。因此,我无法理解您对准确答案投反对票的动机。
【解决方案3】:

使用 9.5 我成功地尝试了这个

  • 基于 Denis de Bernardy 的回答
  • 只有1个参数
  • 没有联合
  • 没有存储过程
  • 原子,因此没有并发问题(我认为...)

查询:

WITH neworexisting AS (
    INSERT INTO mytable(other_key) VALUES('hello 2') 
    ON CONFLICT(other_key) DO UPDATE SET existed=true -- need some update to return sth
    RETURNING * 
)
SELECT * FROM neworexisting

第一次调用:

id|other_key|created            |existed|
--|---------|-------------------|-------|
 6|hello 1  |2019-09-11 11:39:29|false  |

第二次调用:

id|other_key|created            |existed|
--|---------|-------------------|-------|
 6|hello 1  |2019-09-11 11:39:29|true   |

首先创建你的表 ;-)

CREATE TABLE mytable (
    id serial NOT NULL,
    other_key text NOT NULL,
    created timestamptz NOT NULL DEFAULT now(),
    existed bool NOT NULL DEFAULT false,
    CONSTRAINT mytable_pk PRIMARY KEY (id),
    CONSTRAINT mytable_uniq UNIQUE (other_key) --needed for on conflict
);

【讨论】:

【解决方案4】:

你可以使用存储过程

IF (SELECT id FROM mytable WHERE other_key = 'SOMETHING' LIMIT 1) < 0 THEN
 INSERT INTO mytable (other_key) VALUES ('SOMETHING')
END IF

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 2021-12-16
    • 2016-06-30
    相关资源
    最近更新 更多