过滤 UNION ALL 结果比过滤每个子查询慢得多

Zil*_*ilk 7 postgresql performance view union postgresql-performance

编辑:请参阅结尾以获得更简单的示例)

我在一个名为“cases”(135k 行,29 列)的表中搜索。此表中的某些行具有父子关系类型(不同类型),这意味着对于这些记录,必须混合使用父/子字段来过滤和显示。

我已经确定了四种不同的父子关系并为它们创建了视图:

  • caselist_no_specials:不是子记录,按原样使用记录数据;总共 116106 行。
  • caselist_disputes_with_ipr:子记录;共 138 行。
  • caselist_mark_children:子记录;总共 18132 行。
  • caselist_design_children:子记录;共 671 行。

这些视图的结果不重叠,共同覆盖了表格的 100%。

当我选择所有这些的联合并分别过滤每个视图时,查询需要大约 9 毫秒。选择所有视图的联合并过滤其结果大约需要 500 毫秒。

我还在没有视图的情况下对此进行了测试,内联了它们包含的查询,但没有产生可衡量的改进。

这是快速查询(解释):

  SELECT  c.*
    FROM  caselist_no_specials c
    JOIN  case_clients cacl ON cacl.case_id = c.main_id
   WHERE  cacl.client_id = 12046

   UNION ALL

  SELECT  c.*
    FROM  caselist_disputes_with_ipr c
    JOIN  case_clients cacl ON cacl.case_id = c.main_id
   WHERE  cacl.client_id = 12046

   UNION ALL

  SELECT  c.*
    FROM  caselist_mark_children c
    JOIN  case_clients cacl ON cacl.case_id = c.main_id
   WHERE  cacl.client_id = 12046

   UNION ALL

  SELECT  c.*
    FROM  caselist_design_children c
    JOIN  case_clients cacl ON cacl.case_id = c.main_id
   WHERE  cacl.client_id = 12046

ORDER BY  sort_nr,
          id;
Run Code Online (Sandbox Code Playgroud)

如您所见,每个视图的连接和过滤器都是重复的。试图避免重复产生了这个查询,这需要更长的时间(解释):

  SELECT  x.*
    FROM  (
              SELECT * FROM caselist_no_specials
              UNION ALL
              SELECT * FROM caselist_disputes_with_ipr
              UNION ALL
              SELECT * FROM caselist_mark_children
              UNION ALL
              SELECT * FROM caselist_design_children
          ) x
    JOIN  case_clients cacl ON cacl.case_id = x.main_id
   WHERE  cacl.client_id = 12046
ORDER BY  x.sort_nr,
          x.id;
Run Code Online (Sandbox Code Playgroud)

是否有可能以某种方式让 PostgreSQL 知道外部查询上的过滤器/连接可以应用于内部子查询?

或者有没有其他方法可以避免单独过滤每个视图?此查询的面向用户的表单有 20 多个过滤器字段,并且可以有多达 14 个附加表的 JOIN。

PostgreSQL 是在 Linux 上运行的 9.4.7 版。


编辑:我创建了一个非常简单的示例,简单地使用 3 个视图对原始表进行分区,但不涉及其他表(以及两种变体的查询计划)。回想起来,这是我首先应该用作示例的。

小智 5

您要求数据库在查询一中执行的操作是: 给我表 A 中的所有已过滤的 给我表 B 中的所有已过滤的 给我表 C 中的所有已过滤的 给我来自表 D 中的所有已过滤的 然后联合。

在第二个查询中,您首先获取所有数据,然后才进行连接和过滤。UNION 查询上的 JOIN 和 WHERE 并不能真正让您对任何内容建立索引,显然运行速度较慢。(它与服务器变体或操作系统无关)。