1

我已经创建并自动化了一个自定义工作流,用于急切地同步一系列物化视图。在尝试了几种不同的方法(对于一对多关系)后,我发现最可靠的同步工作流程是删除所有可能受到影响的记录,然后插入新记录。

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;

注意: some_query_generating_some_materialized_view 是一个复杂的读取操作,需要大量的资源来执行。此外, some_materialized_view 使用多个外键和其他约束进行大量索引。

这感觉非常沉重。此工作流带有过多的删除和插入操作,这些操作通常是不必要的,因为某些已删除的记录可能相同,或相似到足以成为 UPDATE 的候选者。

我更喜欢以下内容:

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;

问题出在ON CONFLICT ... DO UPDATE ... WHERE ... RETURNING条款中。

如本问题所述:How to use RETURNING with ON CONFLICT in PostgreSQL?

RETURNING 子句仅返回受影响的记录。所以不受影响的记录不会被返回,因此(在上面的例子中)被不恰当地删除了。

似乎RETURNING真正返回所有记录的唯一方法是通过删除WHERE some_materialized_view <> EXCLUDED子句不必要地更新相同的记录,或者在另一个子句中再次运行 some_query_generating_some_materialized_view EXCEPT......这两个选项也不理想。

那么,我错过了什么?还有其他选择吗?如果不是,一般来说,是否更愿意执行复杂的、资源密集型的读取操作而不是不必要的 UPDATE(记住相关的索引维护和检查约束)?

注意:我不包括EXPLAIN ANALYZE结果,因为这不是特定于单个查询,而是一般的问题。为了可维护性和健全性,该项目需要保持一致,并且该技术多次用于具有不同结构和用例的表(一些读取繁重,其他写入繁重)。

4

1 回答 1

0
  • 伪代码(我不喜欢伪代码)
  • 演示链式 CTE
  • 忽略锁定、序列化和竞争。
  • (以及问题的语义/上下文)

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>
        );
于 2016-12-27T18:24:38.043 回答