当前位置: 首页 > 面试题库 >

如何使用INSERT``ON CONFLICT''更新所有列?

何华灿
2023-03-14
问题内容

我有一个带有单个主键的表。当我尝试执行插入操作时,尝试插入具有现有键的行可能会导致冲突。我要允许插入更新所有列吗?有什么简单的语法吗?我试图让它“
upsert”所有列。

我正在使用PostgreSQL 9.5.5。


问题答案:

UPDATE语法 要求
显式命名目标列。避免这种情况的可能原因:

  • 您有很多列,只是想缩短语法。
  • 除了唯一列,您不 知道 列名。

"All columns" 必须以匹配顺序和匹配数据类型表示 “目标表的所有列” (或至少 “表的前导列”
)。否则,您仍然必须提供目标列名称的列表。

测试表:

CREATE TABLE tbl (
   id    int PRIMARY KEY
 , text  text
 , extra text
);

INSERT INTO tbl AS t
VALUES (1, 'foo')
     , (2, 'bar');

1. DELETEINSERT在单个查询中

不知道除以外的任何列名称id

仅适用于 “目标表的所有列” 。尽管语法甚至适用于前导子集,但目标表中多余的列将使用DELETE和重置为NULL INSERT

INSERT ... ON CONFLICT ...需要UPSERT()来避免在并发写入负载下的并发/锁定问题,这仅是因为在Postgres中没有通用的方法来锁定尚不存在的行(
值锁定 )。

您的特殊要求仅会影响UPDATE零件。可能的复杂性不适用于影响 现有
行的地方。那些被正确锁定。简化一些,您可以将案例减少为DELETEINSERT

WITH data(id) AS (              -- Only 1st column gets explicit name!
   VALUES
      (1, 'foo_upd', 'a')       -- changed
    , (2, 'bar', 'b')           -- unchanged
    , (3, 'baz', 'c')           -- new
   )
, del AS (
   DELETE FROM tbl AS t
   USING  data d
   WHERE  t.id = d.id
   -- AND    t <> d              -- optional, to avoid empty updates
   )                             -- only works for complete rows
INSERT INTO tbl AS t
TABLE  data                      -- short for: SELECT * FROM data
ON     CONFLICT (id) DO NOTHING
RETURNING t.id;

在Postgres的MVCC模型,一个UPDATE是大致相同DELETE,并INSERT反正(除了某些极端情况并发,HOT更新和大列值线的存储出)。由于您仍然想替换所有行,因此只需删除之前的冲突行INSERT。删除的行将保持锁定状态,直到提交事务。在INSERT可能只找到冲突行对以前不存在的键值,如果并发事务发生并发插入(在后DELETE,但在此之前的INSERT)。

在这种特殊情况下,您将丢失受影响的行的其他列值。没有异常。但是,如果竞争查询具有相同的优先级,那么这几乎不是问题:另一个查询在 某些
行中胜出。另外,如果另一个查询是类似的UPSERT,则它的替代方法是等待该事务提交,然后立即进行更新。“获胜”可能是痛苦的胜利。

关于“空更新”:

  • 如何(或可以)在多列上选择DISTINCT?

不,我的查询必须赢!

好的,您要求它:

WITH data(id) AS (                   -- Only 1st column gets explicit name!
   VALUES                            -- rest gets default names "column2", etc.
     (1, 'foo_upd', NULL)              -- changed
   , (2, 'bar', NULL)                  -- unchanged
   , (3, 'baz', NULL)                  -- new
   , (4, 'baz', NULL)                  -- new
   )
, ups AS (
   INSERT INTO tbl AS t
   TABLE  data                       -- short for: SELECT * FROM data
   ON     CONFLICT (id) DO UPDATE
   SET    id = t.id
   WHERE  false                      -- never executed, but locks the row!
   RETURNING t.id
   )
, del AS (
   DELETE FROM tbl AS t
   USING  data     d
   LEFT   JOIN ups u USING (id)
   WHERE  u.id IS NULL               -- not inserted !
   AND    t.id = d.id
   -- AND    t <> d                  -- avoid empty updates - only for full rows
   RETURNING t.id
   )
, ins AS (
   INSERT INTO tbl AS t
   SELECT *
   FROM   data
   JOIN   del USING (id)             -- conflict impossible!
   RETURNING id
   )
SELECT ARRAY(TABLE ups) AS inserted  -- with UPSERT
     , ARRAY(TABLE ins) AS updated   -- with DELETE & INSERT;

如何?

  • 第一CTEdata仅提供数据。可以是一张桌子。
  • 第二CTE ups:UPPERT。冲突的行id不会更改,但也会 锁定
  • 第三CTEdel删除冲突的行。他们保持锁定状态。
  • 第四CTEins插入 整行 。只允许同一笔交易
  • 最终的SELECT仅用于演示以显示发生了什么。

要检查空更新测试(之前和之后),请执行以下操作:

SELECT ctid, * FROM tbl; -- did the ctid change?

(注释掉)检查行中的任何更改都AND t <>d可以使用NULL值,因为我们正在根据手册比较两个类型化的行值:

两个NULL字段值被视为相等,并且NULL被认为大于非NULL

2.动态SQL

这也适用于前导列的子集,并保留现有值。

技巧是让Postgres动态地使用系统目录中的列名构建查询字符串,然后执行它。



 类似资料:
  • 我有一个只有一个主键的表。当我试图插入时,可能会因为试图用现有键插入一行而引起冲突。我想允许插入更新所有列?有什么简单的语法吗?我试图让它“向上插入”所有列。 我使用PostgreSQL 9.5.5。

  • 我基本上是在中更新一个值,并使用将其发送到片段。所有片段都应该在上显示相同的值。 我的问题是我的应用程序有3个片段页面,只有中间的页面(Fragment_B)在更新... 为什么所有的片段不同时更新?该值从不显示在Fragment_A和fragment_c上。 应该更新所有的片段,但它只更新片段B。但是我试图调试这个问题,并创建了和SetText,它适用于每个片段。我不知道为什么会这样。有人能帮帮

  • 问题内容: 我正在尝试更新所有以’agg%’和column_name=’%userid%’之类的字符串开头的表…但是即使我能够找到选择具有特定列的所有表的选项,我也看不到在线的此类示例名称和表名称我需要执行相同的操作来更新这些表,如下所示: 帮助将不胜感激。 谢谢。 问题答案: 获取您条件的更新查询 执行

  • 问题内容: 我有一个地图对象,该对象放置在控制器中的Spring 中,并转发到jsp视图以填充选择。第一次填充后,我想用我使用jquery AJAX检索的json对象替换用于填充select的map对象,并使用jQuery.parseJSON转换为对象。我可以用json对象的内容动态替换select的全部内容吗? 问题答案: 对于实际修改选项,您实际上并不需要jQuery。您可以通过分配框属性的属

  • 我有一个租户表,一次只能有一个租户处于活动状态。 要激活租户,我使用以下代码。是否有更好的方法使用spring data mongo更改所有行的特定列。

  • 问题内容: 是否可以强制所有客户端使用socket.io更新?我尝试了以下操作,但是当新客户端连接时,它似乎没有更新其他客户端: 服务器端JavaScript: 我正在尝试向所有客户端发送一条消息,其中包含当前已连接的用户数,它可以正确发送用户数量…。但是,客户端本身似乎不会更新,直到刷新页面为止。我希望这是实时发生的。 客户端JavaScript: 问题答案: 实际上,它根本没有向其他客户端发送