【问题标题】:Manage insert update scheduled tasks管理插入更新计划任务
【发布时间】:2017-12-21 03:54:03
【问题描述】:

我不习惯处理计划任务,我需要一些建议(我的想法是好是坏

我正在设计一个每 20 分钟运行一次的函数。此函数从 json 文件(我无法控制)中检索数据并将数据插入到数据库中。

当我这样做时,我不认为这会在数据库视图中产生唯一 ID 问题,即每次更新的数据都是相同的。

我想过做两个功能:

1:第一次插入(INSERT)

2:根据ID更新数据(UPDATE)

@Component
public class LoadSportsCompetition {

  @PostConstruct
  public void insert() {
    // 1 : get json data
    // 2 : insert in DB
  }

  @Scheduled(cron="0 0/20 * * * ?")
  public void update() {
    // 1 : get json data
    // 2 : update rows by ID      
  }

}

【问题讨论】:

  • 您的实际问题是什么?
  • 我只是在徘徊,是好是坏
  • 您使用的是哪个版本的 PostgreSQL?如果使用较新的 (9.5+) 版本,您可能希望使用 INSERT ... ON CONFLICT DO UPDATE ... ; 并一步完成所有操作。见INSERT
  • 是的,我用的是较新的,所以即使是相同的 id 也不会触发错误。
  • 如果存在主键冲突(或您指定的任何其他类型的冲突),而不是执行标准INSERTUPDATE 部分将发生。此更新仍会产生错误(例如违反CHECKREFERENCES 约束,如果它们存在,或违反某些type 限制,例如尝试将'hello' 放入char(3)) .

标签: spring postgresql scheduled-tasks


【解决方案1】:

在 PostgreSQL 9.5 及更高版本中处理此问题的(最可能的)最佳方法是使用INSERT ... ON CONFLICT ... DO UPDATE

假设这是您的原始表格(非常简单,为了这个示例):

CREATE TABLE tbl
(
    tbl_id INTEGER,
    payload JSONB,

    CONSTRAINT tbl_pk 
       PRIMARY KEY (tbl_id)
) ;

我们用起始数据填充它:

INSERT INTO tbl
   (tbl_id, payload)
VALUES
   (1, '{"a":12}'),
   (2, '{"a":13, "b": 25}'),
   (3, '{"a":15, "b": [12,13,14]}'),
   (4, '{"a":12, "c": "something"}'),
   (5, '{"a":13, "x": 1234.567}'),
   (6, '{"a":12, "x": 1234.789}') ;

现在我们执行非冲突插入(即:不会执行 ON CONFLICT ... DO):

-- A normal insert, no conflict
INSERT INTO tbl
   (tbl_id, payload)
VALUES
   (7, '{"x": 1234.56, "y": 3456.78}')
ON CONFLICT ON CONSTRAINT tbl_pk DO 
UPDATE
   SET payload = excluded.payload ;   -- Note: the excluded pseudo-table comprises the conflicting rows

现在我们执行一个INSERT,它将产生一个PRIMARY KEY 冲突,该冲突将由ON CONFLICT 子句处理并执行更新

-- A conflicting insert
INSERT INTO tbl
   (tbl_id, payload)
VALUES
   (3, '{"a": 16, "b": "I don''t know"}')
ON CONFLICT ON CONSTRAINT tbl_pk DO 
UPDATE
   SET payload = excluded.payload ;  

现在,两行插入会在一行发生冲突,然后插入另一行:

-- Now one of each
-- A conflicting insert
INSERT INTO tbl
   (tbl_id, payload)
VALUES
   (4, '{"a": 18, "b": "I will we updated"}'),
   (9, '{"a": 17, "b": "I am nuber 9"}')
ON CONFLICT ON CONSTRAINT tbl_pk DO UPDATE
   SET payload = excluded.payload ; 

我们现在检查表格:

SELECT * FROM tbl ORDER BY tbl_id ;
tbl_id |有效载荷 -----: | :------------------------------------ 1 | {“一”:12} 2 | {“a”:13,“b”:25} 3 | {“a”:16,“b”:“我不知道”} 4 | {“a”:18,“b”:“我会更新”} 5 | {“a”:13,“x”:1234.567} 6 | {“a”:12,“x”:1234.789} 7 | {“x”:1234.56,“y”:3456.78} 9 | {“a”:17,“b”:“我是 nuber 9”}

您的代码应该循环遍历传入的数据,获取它,并执行所有INSERT/UPDATE(有时称为MERGEUPSERT)一行,或分批,多行VALUES .

您可以在 dbfiddle here

获取所有代码

还有一种选择,如果您分批工作,它更适合。使用WITH 语句,该语句有一个UPDATE 子句,后跟一个INSERT

-- Avoiding (most) concurrency issues.

BEGIN TRANSACTION ;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ;

WITH data_to_load (tbl_id,  payload) AS
(
    VALUES
       (3, '{"a": 16, "b": "I don''t know"}' :: jsonb),
       (4, '{"a": 18, "b": "I will we updated"}'),
       (7, '{"x": 1234.56, "y": 3456.78}'),
       (9, '{"a": 17, "b": "I am nuber 9"}')
), 
update_existing AS
(
    UPDATE
        tbl
    SET 
        payload = data_to_load.payload
    FROM
        data_to_load
    WHERE
        tbl.tbl_id = data_to_load.tbl_id
)
-- Insert the non-existing
INSERT INTO
    tbl
    (tbl_id, payload)
SELECT
    tbl_id, payload
FROM
    data_to_load
WHERE
    data_to_load.tbl_id NOT IN (SELECT tbl_id FROM tbl) ;

COMMIT TRANSACTION ;

您将获得相同的结果,正如您在 dbfiddle here 中看到的那样。


在这两种情况下,准备好进行错误处理,并准备好重试您的事务,如果它们因并发操作同时修改您的数据库而发生冲突。你的事务可以是显式的(如第二种情况),或者隐式,如果你有某种自动提交每个INSERT

【讨论】:

  • 很好的解释,我做了一个简单的技巧,只插入一次然后更新。稍后我可能会集成您的解决方案
猜你喜欢
  • 2011-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-27
  • 2015-12-23
  • 1970-01-01
  • 1970-01-01
  • 2016-11-19
相关资源
最近更新 更多