简体   繁体   English

PostgreSQL 9.6 - 插入冲突后更新返回的地方

[英]PostgreSQL 9.6 - INSERT ON CONFLICT DO UPDATE WHERE RETURNING

I have created and automated a custom workflow for eagerly synchronizing a series of materialized views. 我创建并自动化了一个自定义工作流程,以便热切地同步一系列物化视图。 After trying several different approaches (for one to many relationships), I have found the most reliable synchronization workflow is to delete all records that may have been impacted and then insert the new records. 在尝试了几种不同的方法(针对一对多关系)后,我发现最可靠的同步工作流程是删除可能已受影响的所有记录,然后插入新记录。

DELETE FROM
    some_materialized_view
WHERE
    set_of_records_key = some_value;

INSERT INTO
    some_materialized_view
SELECT
    *
FROM
    some_query_generating_some_materialized_view;

Note: some_query_generating_some_materialized_view is a complex read operation that takes a non-trivial amount of resources to execute. 注意:some_query_generating_some_materialized_view是一个复杂的读取操作,需要执行非常少量的资源。 Additionally, some_materialized_view is heavily indexed with several foreign keys and other constraints. 另外,some_materialized_view被多个外键和其他约束严重索引。

This feels extremely heavy handed. 这感觉非常沉重。 This workflow comes with excessive delete and insert operations that are often times needless as some of the deleted records may have been identical, or similar enough to be a candidate for an UPDATE. 此工作流程带有过多的删除和插入操作,这些操作通常是不必要的,因为某些已删除的记录可能相同或类似,足以成为UPDATE的候选者。

I would prefer something like the following: 我更喜欢以下内容:

DELETE FROM
    some_materialized_view
USING
    (
        SELECT
            unique_key
        FROM
            some_materialized_view
        WHERE
            set_of_records_key = some_value

        EXCEPT
        INSERT INTO
            some_materialized_view
        SELECT
            *
        FROM
            some_query_generating_some_materialized_view
        ON CONFLICT (...) DO UPDATE
        SET 
            foo = EXCLUDED.foo,
            bar = EXCLUDED.bar,
            ...
        WHERE
            some_materialized_view <> EXCLUDED
        RETURNING
            unique_key
    ) AS sub_query
WHERE
    some_materialized_view.unique_key = sub_query.unique_key;

The problem is in the ON CONFLICT ... DO UPDATE ... WHERE ... RETURNING clause. 问题出在ON CONFLICT ... DO UPDATE ... WHERE ... RETURNING子句中。

as addressed in this question: How to use RETURNING with ON CONFLICT in PostgreSQL? 正如这个问题所述: 如何在PostgreSQL中使用RETURNING和ON CONFLICT?

the RETURNING clause only returns impacted records. RETURNING子句仅返回受影响的记录。 So records not impacted are not returned, and thus (in the example above) deleted inappropriately. 因此,不会返回未受影响的记录,因此(在上面的示例中)不恰当地删除了。

It seems the only way to get RETURNING to actually return all records is unnecessarily update identical records by removing the WHERE some_materialized_view <> EXCLUDED clause, or run some_query_generating_some_materialized_view again in another EXCEPT clause... both options are also not ideal. 似乎让RETURNING实际返回所有记录的唯一方法是通过删除WHERE some_materialized_view <> EXCLUDED子句来不必要地更新相同的记录,或者在另一个EXCEPT子句中再次运行some_query_generating_some_materialized_view ...这两个选项也不理想。

So, what am I missing? 那么,我错过了什么? Are there other options available? 还有其他选择吗? If not, in general , is it preferred to perform a complex, resource intensive, read operation over a needless UPDATE (remembering the associated index maintenance and check constraints)? 如果不是, 通常情况下 ,是否优先在不必要的UPDATE上执行复杂的,资源密集的读取操作(记住相关的索引维护和检查约束)?

Note: I'm not including EXPLAIN ANALYZE results as this is not specific to a single query, rather a question in general. 注意:我不包括EXPLAIN ANALYZE结果,因为这不是特定于单个查询,而是一般的问题。 For the sake of maintainability and sanity this project needs to be consistent and this technique is used several times with tables of different structures and use cases (some read heavy, others write heavy). 为了可维护性和健全性,该项目需要保持一致,并且这种技术多次使用不同结构和用例的表(一些读重,另一些写重)。

  • Pseudo code (I don't like pseudo code) 伪代码(我不喜欢伪代码)
  • demonstrating chained CTE 展示链式CTE
  • ignoring locking,serialisation and races. 忽略锁定,序列化和比赛。
  • (and semantics/context from the question) (和问题的语义/上下文)

WITH fresh AS ( -- workhorse: Called only and exactly once
        SELECT <keyfields> -- *
        FROM some_query_generating_some_materialized_view
        )
,upd AS ( -- update existing rows
        UPDATE some_materialized_view mv
        SET foo = fr.foo, bar = fr.bar
        FROM fresh fr
        WHERE mv.<keyfields = fr.<keyfields>
        RETURNING mv.<keyfields>
        )
/* 
, del AS ( 
        no deletes ???
        )
 */
        -- insert non existing rows.
INSERT INTO some_materialized_view mv ( <targetfields> )
SELECT fr.<srcfields>
FROM fresh fr
WHERE NOT EXISTS (
        SELECT *
        FROM upd nx
        WHERE nx.<keyfields> = fr.<keyfields>
        );

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM