有没有更快的方法在 Postgres 中获得 UNION ALL 行为?

Sam*_*Sam 4 postgresql performance union

我的 Postgres 数据库中有一个表 (t0),其中的数据如下所示:

t1_id  t2_id
1      1      
2      1
2      1
4      null
4      null
5      null
Run Code Online (Sandbox Code Playgroud)

我有一个查询要返回我想要的结果:

t1_id  t2_id
1      1
4      null
5      null
Run Code Online (Sandbox Code Playgroud)

我的查询看起来像这样:

(
  SELECT DISTINCT ON (t2_id) t1_id, t2_id
  FROM t0
  WHERE t2_id IS NOT NULL
)
UNION ALL
(
  SELECT DISTINCT ON (t1_id) t1_id, t2_id
  FROM t0
  WHERE t2_id IS NULL
)
Run Code Online (Sandbox Code Playgroud)

有没有更快的方法来执行这样的操作?这还不错,但是我在几个地方(使用连接)这样做,所有这些重复的查询似乎都会减慢速度。看来必须有更好的办法了。

这是小提琴形式的查询:http://sqlfiddle.com/#!15 /d41d8/3603

Erw*_*ter 5

对于简单的情况,我只能想到对查询进行微小的改进:

(
  SELECT DISTINCT ON (t2_id)
         t1_id, t2_id
  FROM   t0
  WHERE  t2_id IS NOT NULL
  ORDER  BY t2_id, t1_id  -- to get consistent results
)
UNION ALL
(
  SELECT DISTINCT ON (t1_id)
         t1_id, NULL      -- cheaper
  FROM   t0
  WHERE  t2_id IS NULL
  -- if you retrieve more columns, add ORDER BY, too
)
Run Code Online (Sandbox Code Playgroud)
  • 正如ypercube 提到的,您需要添加ORDER BY明确的表达式列表才能获得确定性结果。

  • 您可以在查询的第二部分中使用常量NULL而不是。t2_id也与以下指数支撑相关。

  • 性能的关键是索引。尝试使用两个部分索引来匹配查询的两个部分:

    CREATE INDEX t0_part1_idx ON t0 (t2_id, t1_id) WHERE t2_id IS NOT NULL;
    CREATE INDEX t0_part2_idx ON t0 (t1_id)        WHERE t2_id IS NULL;
    
    Run Code Online (Sandbox Code Playgroud)

    您可能想也可能不想包含额外的列以允许仅索引扫描

根据表大小和数据分布,可能有更快的替代方案

单身的SELECT

如果你想把它压缩成一个SELECT

SELECT DISTINCT ON (coalesce(t2_id, t1_id), t2_id)
       t1_id, t2_id
FROM   t0
ORDER  BY coalesce(t2_id, t1_id), t2_id, t1_id;
Run Code Online (Sandbox Code Playgroud)

等效,但排序顺序除外。如果您希望速度更快,请尝试使用函数索引:

CREATE INDEX t0_func_idx ON t0 (coalesce(t2_id, t1_id), t2_id, t1_id);
Run Code Online (Sandbox Code Playgroud)