【问题标题】:Write a Postgres Get or Create SQL Query编写 Postgres 获取或创建 SQL 查询
【发布时间】:2013-04-13 23:42:02
【问题描述】:

我想编写一个 Postgres SQL 语句,说明查找颜色为 X 和亮度为 Y 的用户。如果该用户存在,则返回其所有行数据。如果没有,请创建一个新行并传递其他信息。这两个单独的语句会做这样的事情:

Select (color, brightness, size, age) FROM mytable WHERE color = 'X' AND brightness= 'Y';

如果没有返回任何东西,那么执行这个:

INSERT INTO mytable (color, brightness, size, age) VALUES (X, Y, big, old);

有没有办法将这些组合成一个查询??

【问题讨论】:

  • INSERT 语句上看到postgresql 的RETURNING 子句扩展,我最初希望可以使用联合在一个语句中将插入和选择结合起来,但我尝试了,不幸的是读写真的不可能这样混。

标签: sql postgresql


【解决方案1】:

在 SQL DBMS 中,select-test-insert 方法是一个错误:没有什么能阻止另一个进程在您的 selectinsert 语句之间插入“缺失”行。改为这样做:

INSERT INTO mytable (color, brightness, size, age)
SELECT color, brightness, size, age 
FROM mytable
WHERE NOT EXISTS (
    select 1 from 
    from mytable
    where color = 'X' and brightness = 'Y'
);
SELECT (color, brightness, size, age) 
FROM mytable 
WHERE color = 'X' AND brightness= 'Y';

您应该能够将整个文本作为单个“查询”传递给 DBMS。您可能需要考虑将其转换为存储过程。

【讨论】:

  • 与此答案相关,有助于更好地理解:stackoverflow.com/a/13342031/399726
  • 无法理解这个答案如何收集到这么多包含语法错误的选票。 Postgres 中的 INSERT 没有选项 insert ... where ...。你的意思可能是insert into mytable (color, brightness, size, age) SELECT 'X', 'Y', 1.2, 3.4 where not exists...
  • 我同意 select-test-insert 不安全,但是原子插入呢?我认为如果您锁定行,那么 select-test-insert 方法不会有任何问题。
【解决方案2】:
with sel as (
    select color, brightness, size, age
    from mytable
    where color = 'X' and brightness = 'Y'
), ins as (
    insert into mytable (color, brightness, size, age)
    select 'X', 'Y', 6.2, 40
    where not exists (
        select 1 from sel
    )
    returning color, brightness, size, age
)
select color, brightness, size, age
from ins
union
select color, brightness, size, age
from sel

【讨论】:

    【解决方案3】:

    在此处添加我的解决方案。这与@Clodoaldo Neto 和@astef 的解决方案略有不同。

    WITH ins AS (
      INSERT INTO mytable (color, brightness, size, age)
      VALUES ('X', 'Y', 'big', 'old')
      ON CONFLICT (color) DO NOTHING
      RETURNING *
    )
    SELECT * FROM ins
    UNION
    SELECT * FROM mytable
      WHERE color = 'X';
    

    我发现 astef 的解决方案不适合我的目的:它不执行“获取或创建”的“获取”部分!如果该值已经存在,则什么都不会发生。

    语句末尾的联合确保如果没有插入该值(因为它已经存在),我们仍然可以从表中检索该值。

    【讨论】:

    • 我发现了上述问题:如果存在冲突,您的表的序列号计数器将在每次执行查询时递增。这并不理想。
    【解决方案4】:

    如果您的列参与唯一索引约束,您可以使用自 9.5 版以来可用的方法:

    INSERT INTO mytable (color, brightness, size, age)
    VALUES ('X', 'Y', 'big', 'old')
    ON CONFLICT (color) DO NOTHING;
    

    (假设您在 color 上有唯一索引)。

    文档是基尔:https://www.postgresql.org/docs/9.5/static/sql-insert.html

    【讨论】:

    • 这没有回答问题,它询问如何获取创建一行。
    • 确实如此。你这样做,然后你做 SELET,然后总是成功。所以你做“(插入或什么都不做)并得到”。
    • @Mitar...它没有。您假设有一些冲突,因此这是一个 Upsert 查询...UPDATE 或 INSERT。他们具体要求的是 GET 或 CREATE。这意味着您想查询某行,如果它不存在,则创建一个新行……可能根本没有任何冲突。例如,您可能想查询具有特定 ID 的行,如果它不存在,您只想创建一个具有默认值的新行(随机生成的新 ID)
    猜你喜欢
    • 1970-01-01
    • 2012-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-24
    • 2012-12-03
    相关资源
    最近更新 更多