当前位置: 首页 > 知识库问答 >
问题:

如何使用INSERT更新所有列。。。关于冲突。。。?

左丘积厚
2023-03-14

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

我使用PostgreSQL 9.5.5。

共有2个答案

沈曜灿
2023-03-14

id列不是第一列时,Erwin Brandstetter的回答似乎失败了。

在我的例子中,下面使用了他另一个答案中的一个片段来重现“返回ins/ups”功能:

DO
$do$
BEGIN
EXECUTE (
SELECT
'DROP TABLE IF EXISTS res_tbl; CREATE TABLE res_tbl AS
 WITH ins AS (
       INSERT INTO dest
       TABLE  src                      -- short for: SELECT * FROM data
       ON     CONFLICT (id) DO UPDATE
       SET    id = dest.id
       WHERE  false                    -- never executed, but locks the row!
       RETURNING id
    ),
    repl AS (
        UPDATE dest
        SET   (' || string_agg(          quote_ident(column_name), ',') || ')
            = (' || string_agg('src.' || quote_ident(column_name), ',') || ')
        FROM   src
        WHERE  src.id = dest.id
        AND    src <> dest             -- avoids empty updates ¹
        RETURNING dest.id
    )
 SELECT ARRAY(TABLE ins)  AS inserted  -- with UPSERT
      , ARRAY(TABLE repl) AS updated   -- with DYNAMIC UPDATE
;'
FROM   information_schema.columns
WHERE  table_name   = 'src'     -- table name, case sensitive
AND    table_schema = 'public'  -- schema name, case sensitive
AND    column_name <> 'id'      -- all columns except id)
);
END
$do$;

仅适用于所有列都可比较的整行更新(例如jsonb而不是json)。

宣瀚
2023-03-14

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

  • 您有很多列,只想缩短语法

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

测试表:

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

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

不知道除id之外的任何列名。

仅适用于“目标表的所有列”。虽然语法甚至适用于前导子集,但目标表中多余的列将通过DELETEINSERT重置为NULL。

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

您的特殊要求只影响更新部分。在现有行受到影响的情况下,可能出现的复杂情况不适用。那些锁好了。通过进一步简化,您可以将案例简化为删除插入

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模型中,更新基本上与删除插入相同(除了一些并发、热更新和大列值存储不一致的极端情况)。由于您仍想替换所有行,只需在插入之前删除冲突行即可。在提交事务之前,已删除的行将保持锁定状态。如果并发事务碰巧同时插入了以前不存在的键值,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只提供数据。可能是一个表。
  • 第二个CTEup:UPSERT。具有冲突id的行不会被更改,但也会被锁定。
  • 第三个CTEdel删除冲突行。它们保持锁定状态。
  • 第4个CTEins插入整行。仅允许同一事务
  • 最终的SELECT只是为了演示发生了什么。

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

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

(注释掉)检查行和t中是否有任何更改

两个空字段值被视为相等,空字段值被视为大于非空字段值

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

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

代码见相关答案:

>

所有列的批量更新

SQL从一个表的字段更新另一个表的字段

 类似资料:
  • 问题内容: 我有一个带有单个主键的表。当我尝试执行插入操作时,尝试插入具有现有键的行可能会导致冲突。我要允许插入更新所有列吗?有什么简单的语法吗?我试图让它“ upsert”所有列。 我正在使用PostgreSQL 9.5.5。 问题答案: 该语法 要求 显式命名目标列。避免这种情况的可能原因: 您有很多列,只是想缩短语法。 除了唯一列,您不 知道 列名。 必须以匹配顺序和匹配数据类型表示 “目标

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

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

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

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

  • 当您正在向上插入一行时(PostgreSQL) 还有更短的路吗?也就是说:使用所有排除值。 在SQLite中,我曾经做过: