慢慢查询“UNION ALL”视图

时间:2012-01-27 09:25:12

标签: sql performance postgresql indexing union-all

我有一个数据库视图,它基本上包含两个SELECT UNION ALL个查询,如下所示:

CREATE VIEW v AS
SELECT time, etc. FROM t1 // #1...
UNION ALL
SELECT time, etc. FROM t2 // #2...

问题是选择表格

SELECT ... FROM v WHERE time >= ... AND time < ...

表现真的很慢。

SELECT#1和#2都非常快,索引正确等等:当我创建视图v1和v2时:

CREATE VIEW v1 AS
SELECT time, etc. FROM t1 // #1...

CREATE VIEW v2 AS
SELECT time, etc. FROM t2 // #2...

同样的SELECT,具有与上述相同的WHERE条件,可以单独使用它们。

关于哪里可能是问题以及如何解决问题的任何想法?

(简而言之,它是最近的Postgres版本之一。)

编辑:添加匿名查询计划(请访问@filiprem以获取指向真棒工具的链接):

V1:

Aggregate  (cost=9825.510..9825.520 rows=1 width=53) (actual time=59.995..59.995 rows=1 loops=1)
  ->  Index Scan using delta on echo alpha  (cost=0.000..9815.880 rows=3850 width=53) (actual time=0.039..53.418 rows=33122 loops=1)
          Index Cond: (("juliet" >= 'seven'::uniform bravo_victor oscar whiskey) AND ("juliet" <= 'november'::uniform bravo_victor oscar whiskey))
          Filter: ((NOT victor) AND ((bravo_sierra five NULL) OR ((bravo_sierra)::golf <> 'india'::golf)))

V2:

Aggregate  (cost=15.470..15.480 rows=1 width=33) (actual time=0.231..0.231 rows=1 loops=1)
  ->  Index Scan using yankee on six charlie  (cost=0.000..15.220 rows=99 width=33) (actual time=0.035..0.186 rows=140 loops=1)
          Index Cond: (("juliet" >= 'seven'::uniform bravo oscar whiskey) AND ("juliet" <= 'november'::uniform bravo oscar whiskey))
          Filter: (NOT victor)

ν:

Aggregate  (cost=47181.850..47181.860 rows=1 width=0) (actual time=37317.291..37317.291 rows=1 loops=1)
  ->  Append  (cost=42.170..47132.480 rows=3949 width=97) (actual time=1.277..37304.453 rows=33262 loops=1)
        ->  Nested Loop Left Join  (cost=42.170..47052.250 rows=3850 width=99) (actual time=1.275..37288.465 rows=33122 loops=1)
              ->  Hash Left Join  (cost=42.170..9910.990 rows=3850 width=115) (actual time=1.123..117.797 rows=33122 loops=1)
                      Hash Cond: ((alpha_seven.two)::golf = (quebec_three.two)::golf)
                    ->  Index Scan using delta on echo alpha_seven  (cost=0.000..9815.880 rows=3850 width=132) (actual time=0.038..77.866 rows=33122 loops=1)
                            Index Cond: (("juliet" >= 'seven'::uniform bravo_victor oscar whiskey_two) AND ("juliet" <= 'november'::uniform bravo_victor oscar whiskey_two))
                            Filter: ((NOT victor) AND ((bravo_sierra five NULL) OR ((bravo_sierra)::golf <> 'india'::golf)))
                    ->  Hash  (cost=30.410..30.410 rows=941 width=49) (actual time=1.068..1.068 rows=941 loops=1)
                            Buckets: 1024  Batches: 1  Memory Usage: 75kB
                          ->  Seq Scan on alpha_india quebec_three  (cost=0.000..30.410 rows=941 width=49) (actual time=0.010..0.486 rows=941 loops=1)
              ->  Index Scan using mike on hotel quebec_sierra  (cost=0.000..9.630 rows=1 width=24) (actual time=1.112..1.119 rows=1 loops=33122)
                      Index Cond: ((alpha_seven.zulu)::golf = (quebec_sierra.zulu)::golf)
        ->  Subquery Scan on "*SELECT* 2"  (cost=34.080..41.730 rows=99 width=38) (actual time=1.081..1.951 rows=140 loops=1)
              ->  Merge Right Join  (cost=34.080..40.740 rows=99 width=38) (actual time=1.080..1.872 rows=140 loops=1)
                      Merge Cond: ((quebec_three.two)::golf = (charlie.two)::golf)
                    ->  Index Scan using whiskey_golf on alpha_india quebec_three  (cost=0.000..174.220 rows=941 width=49) (actual time=0.017..0.122 rows=105 loops=1)
                    ->  Sort  (cost=18.500..18.750 rows=99 width=55) (actual time=0.915..0.952 rows=140 loops=1)
                            Sort Key: charlie.two
                            Sort Method:  quicksort  Memory: 44kB
                          ->  Index Scan using yankee on six charlie  (cost=0.000..15.220 rows=99 width=55) (actual time=0.022..0.175 rows=140 loops=1)
                                  Index Cond: (("juliet" >= 'seven'::uniform bravo_victor oscar whiskey_two) AND ("juliet" <= 'november'::uniform bravo_victor oscar whiskey_two))
                                  Filter: (NOT victor)

juliettime

8 个答案:

答案 0 :(得分:9)

这似乎是飞行员错误的情况。 “v”查询计划从至少5个不同的表中进行选择。

现在,您确定已连接到正确的数据库吗?也许有一些时髦的search_path设置?也许t1和t2实际上是视图(可能在不同的模式中)?也许你是以某种方式从错误的观点中选择的?

澄清后编辑:

您正在使用名为“加入移除”的全新功能:http://wiki.postgresql.org/wiki/What%27s_new_in_PostgreSQL_9.0#Join_Removal

http://rhaas.blogspot.com/2010/06/why-join-removal-is-cool.html

当涉及union all时,该功能似乎没有启动。您可能只需要使用所需的两个表重写视图。

另一个编辑: 您似乎正在使用聚合(例如“从v中选择计数(*)”与“select * from v”),这可能会在连接删除时获得截然不同的计划。我想如果没有发布实际的查询,视图和表格定义以及使用的计划,我们就不会走得太远......

答案 1 :(得分:5)

我相信你的查询正在执行类似于:

(
   ( SELECT time, etc. FROM t1 // #1... )
   UNION ALL
   ( SELECT time, etc. FROM t2 // #2... )
)
WHERE time >= ... AND time < ...

优化器难以优化。即它在应用UNION ALL子句之前首先执行WHERE但是,您希望它在WHERE 之前应用UNION ALL子句

你不能将WHERE条款放在CREATE VIEW吗?

CREATE VIEW v AS
( SELECT time, etc. FROM t1  WHERE time >= ... AND time < ... )
UNION ALL
( SELECT time, etc. FROM t2  WHERE time >= ... AND time < ... )

或者,如果视图不能包含WHERE子句,那么,您可以保留两个视图,并在需要时使用UNION ALL子句执行WHERE

CREATE VIEW v1 AS
SELECT time, etc. FROM t1 // #1...

CREATE VIEW v2 AS
SELECT time, etc. FROM t2 // #2...

( SELECT * FROM v1 WHERE time >= ... AND time < ... )
UNION ALL
( SELECT * FROM v2 WHERE time >= ... AND time < ... )

答案 2 :(得分:2)

我不知道Postgres,但是在索引的情况下,一些RMDB处理比BETWEEN更差的比较运算符。 我会尝试使用BETWEEN。

SELECT ... FROM v WHERE time BETWEEN ... AND ...

答案 3 :(得分:1)

一种可能性是在每次调用时动态发出一个新的SQL,而不是创建一个视图,并在union查询的每个SELECT中集成where子句

SELECT time, etc. FROM t1
    WHERE time >= ... AND time < ...
UNION ALL
SELECT time, etc. FROM t2
    WHERE time >= ... AND time < ...

编辑:

您可以使用参数化功能吗?

CREATE OR REPLACE FUNCTION CallMyView(t1 date, t2 date)
RETURNS TABLE(d date, etc.)
AS $$
    BEGIN
        RETURN QUERY
            SELECT time, etc. FROM t1
                WHERE time >= t1 AND time < t2
            UNION ALL
            SELECT time, etc. FROM t2
                WHERE time >= t1 AND time < t2;
    END;
$$ LANGUAGE plpgsql;

呼叫

SELECT * FROM CallMyView(..., ...);

答案 4 :(得分:1)

合并两张桌子。添加一列以指示原始表。如有必要,请将原始表名替换为仅选择相关部分的视图。问题解决了!

查看超类/子类db设计模式可能对您有用。

答案 5 :(得分:0)

尝试使用UNION DISTINCT而不是UNION ALL创建视图。看看它是否给出了错误的结果。看看它是否提供更快的性能。

如果它给出了错误的结果,请尝试将表上的SQL操作映射回关系的关系操作。关系的要素总是截然不同的。您的模型可能存在根本性的问题。

我对您展示的查询计划中的LEFT JOINS深感怀疑。没有必要执行LEFT JOINS以获得您似乎正在选择的结果。

答案 6 :(得分:0)

在11g上遇到相同的场景:

情景1:

CREATE VIEW v AS
  SELECT time, etc. FROM t1 // #1...

以下查询运行得很快,计划看起来没问题:

SELECT ... FROM v WHERE time >= ... AND time < ...

情景2:

CREATE VIEW v AS
  SELECT time, etc. FROM t2 // #2...

以下查询运行得很快,计划看起来没问题:

SELECT ... FROM v WHERE time >= ... AND time < ...

场景3,使用UNION ALL:

CREATE VIEW v AS
  SELECT time, etc. FROM t1 // #1...
  UNION ALL
  SELECT time, etc. FROM t2 // #2...

以下运行缓慢。计划拆分t1和t2(也是视图)并将它们组装成一系列大型联盟。时间过滤器正在适当地应用于各个组件,但它仍然非常慢:

SELECT ... FROM v WHERE time >= ... AND time < ...

我很高兴能在t1加上t2的时间内获得一次,但它已经超过了两倍。在这种情况下,添加parallel提示对我来说很有用。它将所有内容重新安排到一个更好的计划中:

SELECT /*+ parallel */ ... FROM v WHERE time >= ... AND time < ...

答案 7 :(得分:-3)

我认为我没有太多要点将其作为评论发布,所以我将其作为答案发布

我不知道PostgreSQL如何在幕后工作,我想你可能会得到一个线索,如果它是Oracle,所以它就是Oracle如何工作

您的 UNION ALL 视图速度较慢,因为在场景后面,来自 SELECT#1 #2 的记录会暂时合并表格首先是在运行中创建的,然后你的 SELECT ... FROM v WHERE time&gt; = ... AND time&lt; ... 在此临时表上执行。由于#1 #2 都已编入索引,因此它们按预期单独运行,但此临时表未编入索引(当然)并且正在选择最终记录从这个临时表中导致响应较慢。

现在,至少,我认为没有办法让它更快+视图+非物化

除了运行 SELECT#1 #2 以及显式UNION之外,还有一种方法是使用更快的存储过程或函数应用程序编程语言(如果是这种情况),在此过程中,您对每个索引表进行单独调用,然后组合结果,这不像 SELECT ... FROM v WHERE time&gt; = ..和时间&lt; ...... :(