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

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

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

  1. CREATE OR REPLACE VIEW Union_Test AS
  2. (
  3. SELECT
  4. C.Id
  5. , C.Code
  6. , C.Name
  7. , S.Id AS Status_Id
  8. FROM
  9. Client C
  10. INNER JOIN
  11. Status S ON S.Id = 1
  12. UNION ALL
  13. SELECT
  14. CA.Id
  15. , CA.Code
  16. , CA.Name
  17. , S.Id AS Status_Id
  18. FROM
  19. Client_Archive CA
  20. INNER JOIN
  21. Status S ON S.Id = 2
  22. );

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

  1. SELECT
  2. Id
  3. , ROW_NUMBER() OVER (ORDER BY Code) AS Rownum
  4. FROM
  5. Union_Test

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

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

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

  1. CREATE OR REPLACE VIEW Union_Test AS
  2. (
  3. SELECT
  4. C.Id
  5. , C.Code
  6. , C.Name
  7. , S.Id AS Status_Id
  8. FROM
  9. Client C
  10. INNER JOIN
  11. Status S ON S.Id = 1
  12. );

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

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

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

  1. CREATE OR REPLACE VIEW Union_Test AS
  2. (
  3. SELECT
  4. C.Id
  5. , C.Code
  6. , C.Name
  7. , 1 AS Status_Id
  8. FROM
  9. Client C
  10. UNION ALL
  11. SELECT
  12. CA.Id
  13. , CA.Code
  14. , CA.Name
  15. , 2 AS Status_Id
  16. FROM
  17. Client_Archive CA
  18. );

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

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

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

暂无答案!

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

相关问题