我已经创建并自动化了一个自定义工作流,用于急切地同步一系列物化视图。在尝试了几种不同的方法(对于一对多关系)后,我发现最可靠的同步工作流程是删除所有可能受到影响的记录,然后插入新记录。
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
结果,因为这不是特定于单个查询,而是一般的问题。为了可维护性和健全性,该项目需要保持一致,并且该技术多次用于具有不同结构和用例的表(一些读取繁重,其他写入繁重)。