view在postgresql中使用union all和join,对其运行order by查询时不使用索引

oyjwcjzk  于 2021-07-29  发布在  Java
关注(0)|答案(0)|浏览(305)

下面是我尝试运行的代码的精简版本:

CREATE OR REPLACE VIEW Union_Test AS
(
    SELECT 
          C.Id
        , C.Code
        , C.Name
        , S.Id AS Status_Id 
    FROM 
          Client C 
          INNER JOIN 
          Status S ON S.Id = 1

    UNION ALL

    SELECT 
          CA.Id
        , CA.Code
        , CA.Name
        , S.Id AS Status_Id 
    FROM 
          Client_Archive CA 
          INNER JOIN 
          Status S ON S.Id = 2
);

与软删除和1个表(客户机)不同,当客户机被删除时,该客户机被移动到客户机存档表中。上面的视图用于恢复整个客户机集。我希望保持表相对干净,因此将status\u id添加到视图中,而不是表中。
然后执行以下代码。这并不是我所要做的,因为我实际上是根据页面大小变量返回页码的,但是下面的代码可以解决这个问题。

SELECT 
      Id
    , ROW_NUMBER() OVER (ORDER BY Code) AS Rownum 
FROM 
      Union_Test

client(1000000行)和client_archive(1行)表在id(pk)、code和name上有btree索引status表在id(pk)和code上有btree索引。这些是作为健全性检查添加的,因为状态表只有5行。
上述查询生成以下计划:

|QUERY PLAN                                                                                          |
|----------------------------------------------------------------------------------------------------|
|WindowAgg  (cost=165869.97..183369.97 rows=1000000 width=45) (actual time=8908.954..9490.758 rows=10|
|  ->  Sort  (cost=165869.97..168369.97 rows=1000000 width=37) (actual time=8908.933..9062.302 rows=1|
|        Sort Key: "*SELECT* 1".code                                                                 |
|        Sort Method: quicksort  Memory: 102702kB                                                    |
|        ->  Result  (cost=0.00..66212.13 rows=1000000 width=37) (actual time=0.027..604.587 rows=100|
|              ->  Append  (cost=0.00..56212.12 rows=1000000 width=37) (actual time=0.027..495.570 ro|
|                    ->  Subquery Scan on "*SELECT* 1"  (cost=0.00..51210.03 rows=999999 width=37) (a|
|                          ->  Nested Loop  (cost=0.00..41210.04 rows=999999 width=159) (actual time=|
|                                ->  Seq Scan on status s  (cost=0.00..1.06 rows=1 width=0) (actual t|
|                                      Filter: (id = 1)                                              |
|                                      Rows Removed by Filter: 4                                     |
|                                ->  Seq Scan on client c  (cost=0.00..31208.99 rows=999999 width=37)|
|                    ->  Subquery Scan on "*SELECT* 2"  (cost=0.00..2.09 rows=1 width=37) (actual tim|
|                          ->  Nested Loop  (cost=0.00..2.08 rows=1 width=159) (actual time=0.033..0.|
|                                ->  Seq Scan on client_archive ca  (cost=0.00..1.01 rows=1 width=37)|
|                                ->  Seq Scan on status s_1  (cost=0.00..1.06 rows=1 width=0) (actual|
|                                      Filter: (id = 2)                                              |
|                                      Rows Removed by Filter: 4                                     |
|Planning Time: 0.299 ms                                                                             |
|Execution Time: 9553.810 ms                                                                         |

查询中没有使用索引,执行起来需要将近10秒。
如果我删除查询的union all和client\u archive部分,如下所示:

CREATE OR REPLACE VIEW Union_Test AS
(
    SELECT 
          C.Id
        , C.Code
        , C.Name
        , S.Id AS Status_Id 
    FROM 
          Client C 
          INNER JOIN 
          Status S ON S.Id = 1    
);

当我运行上面的rownum查询时,我得到以下计划:

|QUERY PLAN                                                                                          |
|----------------------------------------------------------------------------------------------------|
|WindowAgg  (cost=0.42..156177.45 rows=999999 width=45) (actual time=0.048..2059.087 rows=999999 loop|
|  ->  Nested Loop  (cost=0.42..141177.46 rows=999999 width=37) (actual time=0.038..1355.683 rows=999|
|        ->  Index Scan using client_code_idx on client c  (cost=0.42..128676.41 rows=999999 width=37|
|        ->  Materialize  (cost=0.00..1.07 rows=1 width=0) (actual time=0.000..0.000 rows=1 loops=999|
|              ->  Seq Scan on status s  (cost=0.00..1.06 rows=1 width=0) (actual time=0.012..0.013 r|
|                    Filter: (id = 1)                                                                |
|                    Rows Removed by Filter: 4                                                       |
|Planning Time: 0.213 ms                                                                             |
|Execution Time: 2125.573 ms                                                                         |

考虑到表上的索引,这是我所期望的计划。
如果我删除与状态表的联接并恢复查询的union all和client\ U存档部分,如下所示:

CREATE OR REPLACE VIEW Union_Test AS
(
    SELECT 
          C.Id
        , C.Code
        , C.Name
        , 1 AS Status_Id 
    FROM 
          Client C 

    UNION ALL

    SELECT 
          CA.Id
        , CA.Code
        , CA.Name
        , 2 AS Status_Id 
    FROM 
          Client_Archive CA 
);

当我运行上面的rownum查询时,我得到以下计划:

|QUERY PLAN                                                                                          |
|----------------------------------------------------------------------------------------------------|
|WindowAgg  (cost=0.56..163684.56 rows=1000000 width=45) (actual time=0.032..2579.727 rows=1000000 lo|
|  ->  Result  (cost=0.56..148684.56 rows=1000000 width=37) (actual time=0.023..1840.123 rows=1000000|
|        ->  Merge Append  (cost=0.56..138684.56 rows=1000000 width=37) (actual time=0.022..1694.317 |
|              Sort Key: c.code                                                                      |
|              ->  Index Scan using client_code_idx on client c  (cost=0.42..128676.41 rows=999999 wi|
|              ->  Index Scan using client_archive_code_idx on client_archive ca  (cost=0.13..8.14 ro|
|Planning Time: 0.176 ms                                                                             |
|Execution Time: 2647.764 ms                                                                         |

再一次-和预期的差不多。看起来,视图中的unionall和join的组合,然后对视图的查询中的orderby,产生了一个次优的计划。或者我遗漏了一些明显的东西。我对postgresql比较陌生,所以后者可能就是这样!
一旦数据被缩减到我要查找的页面上,我就不能在以后添加状态,因为按状态排序(显然,在上面的示例中,排序是通过代码完成的)是提供给最终用户的选项之一。
我知道我可以将所有内容移动到一个客户机表中,甚至可以对状态id文本(本例中为1或2)进行排序,而无需连接到状态表-但这否定了我尝试对db设计所做的部分工作。我也不打算创建一个带有索引的物化视图,或者创建一个保存客户机和客户机存档数据的client\u all表,除非没有其他选项。
谢谢,里奇。

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题