【问题标题】:select max of a column using range partition使用范围分区选择列的最大值
【发布时间】:2012-10-11 07:18:57
【问题描述】:

我有一个按数字(参数实例 ID)值划分范围的表。我想选择同一列的 max+1 值。我在 parameterinstanceid 上有全局非分区索引。

select /*+ parallel(a,32,8) */ max(parameterinstanceid) +1 from parameterinstance a;

在检查解释计划时,我看到它正在对表进行索引全扫描(最小值/最大值)。我想通过首先检查最大分区来做到这一点,如果它不包含任何数据,然后是下一个分区,按 desc 顺序。我可以编写一个过程来做到这一点,但我想知道是否有一个简单的查询。 http://www.oramoss.com/blog/2009/06/no-pruning-for-minmax-of-partition-key.html ..看来这是一个未解决的问题。

编辑:

分区名称为 PI_P01,PI_P02,...PI_P10,PI_PMAXVALUE

解释计划是:

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2808487136

-----------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |     1 |     8 |    34   (0)| 00:00:01 |       |       |
|   1 |  SORT AGGREGATE             |       |     1 |     8 |            |          |       |       |
|   2 |   PARTITION RANGE ALL       |       |     1 |     8 |    34   (0)| 00:00:01 |     1 |    11 |
|   3 |    INDEX FULL SCAN (MIN/MAX)| PI_PK |     1 |     8 |    34   (0)| 00:00:01 |     1 |    11 |
-----------------------------------------------------------------------------------------------------

和表结构:

 Name                           Null?    Type
 ------------------------------ -------- -----------------
 PARAMETERINSTANCEID            NOT NULL NUMBER
 PARAMINSTANCE2PARAMSETVERSION  NOT NULL NUMBER
 PARAMINSTANCE2PARAMDEFINITION  NOT NULL NUMBER
 PARAMINSTANCE2PARENTPARAM               NUMBER
 SEQUENCE                                NUMBER
 X_CTCV_CONV_ID                          VARCHAR2(50 CHAR)
 X_CONV_RUN_NO                           NUMBER

以及表上的索引:

    INDEX_NAME                     POSITION COLUMN_NAME 
------------------------------ -------- -----------------------------
PI_UK                                 1 PARAMINSTANCE2PARAMSETVERSION
PI_UK                                 2 PARAMINSTANCE2PARAMDEFINITION
PI_PK                                 1 PARAMETERINSTANCEID            
PI_PAD_FK_I                           1 PARAMINSTANCE2PARAMDEFINITION
PI_PI_FK_I                            1 PARAMINSTANCE2PARENTPARAM      

【问题讨论】:

    标签: sql plsql database-partitioning


    【解决方案1】:

    如果你在分区键上有一个全局索引,你应该得到一个这样的计划:

    ----------------------------------------------------------------------------
    | Id  | Operation                  | Rows  | Bytes | Cost (%CPU)| Time     |
    ----------------------------------------------------------------------------
    |   0 | SELECT STATEMENT           |     1 |     9 |     2   (0)| 00:00:01 |
    |   1 |  SORT AGGREGATE            |     1 |     9 |            |          |
    |   2 |   INDEX FULL SCAN (MIN/MAX)|     1 |     9 |     2   (0)| 00:00:01 |
    ----------------------------------------------------------------------------
    

    这里MIN/MAX很重要,表示Oracle会stop at the first entry,绕过分区表。

    您提供的链接不同,因为分区键没有被索引(因此它生成 FULL TABLE SCAN 而不是 INDEX FULL SCAN

    如果您在分区键上没有索引,似乎 Jonathan Lewis 建议的 TOP-N 查询可能完全符合您的要求。这是我使用表格test 得到的计划,如您链接中的示例所示:

    explain plan for 
    select * from (select col_date_part_key 
                     from test 
                    order by col_date_part_key desc) 
     where rownum = 1
    
    -------------------------------------------------------------------------
    | Id  | Operation                | Name | Rows  | Bytes | Pstart| Pstop |
    -------------------------------------------------------------------------
    |   0 | SELECT STATEMENT         |      |     1 |     9 |       |       |
    |*  1 |  COUNT STOPKEY           |      |       |       |       |       |
    |   2 |   PARTITION RANGE ALL    |      |     1 |     9 |    12 |     1 |
    |   3 |    VIEW                  |      |     1 |     9 |       |       |
    |*  4 |     SORT ORDER BY STOPKEY|      |     1 |     9 |       |       |
    |   5 |      TABLE ACCESS FULL   | TEST |     1 |     9 |    12 |     1 |
    -------------------------------------------------------------------------
    

    如您所见,Oracle 将从最后一个分区 (Pstart=12) 开始,然后一直走到第一个分区 (Pstart=1) 直到获得一行 (Rows=1)。


    更新

    我已经用我希望与您的设置相似的设置运行了另一个测试,并且我找到了一个不同的、更合乎逻辑的计划。设置:

    create table parameterinstance  (
       PARAMETERINSTANCEID           NUMBER             NOT NULL,
       PARAMINSTANCE2PARAMSETVERSION NUMBER             NOT NULL,
       PARAMINSTANCE2PARAMDEFINITION NUMBER             NOT NULL,
       PARAMINSTANCE2PARENTPARAM     NUMBER                     ,
       SEQUENCE                      NUMBER                     ,
       X_CTCV_CONV_ID                VARCHAR2(50 CHAR)          ,
       X_CONV_RUN_NO                 NUMBER                     
    ) partition by range (PARAMETERINSTANCEID)
    (  partition p1 values less than (1000) storage (initial 64k),
       partition p2 values less than (2000) storage (initial 64k),
       partition p3 values less than (3000) storage (initial 64k),
       partition p4 values less than (4000) storage (initial 64k),
       partition p5 values less than (5000) storage (initial 64k),
       partition p6 values less than (6000) storage (initial 64k),
       partition p7 values less than (7000) storage (initial 64k),
       partition p8 values less than (8000) storage (initial 64k),
       partition p9 values less than (9000) storage (initial 64k),
       partition p10 values less than (maxvalue) storage (initial 64k)
    );
    
    CREATE UNIQUE INDEX PI_PK ON parameterinstance(PARAMETERINSTANCEID) local;
    
    insert into parameterinstance  
      (SELECT rownum, rownum, rownum, '', '', rpad('x', 50, 'x'), '' 
         from dual connect by level <= 1e4);
    

    11gR2 我得到以下计划:

    -----------------------------------------------------------------------------
    | Id  | Operation                   | Name  | Rows  | Bytes | Pstart| Pstop |
    -----------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |       |     1 |    13 |       |       |
    |   1 |  PARTITION RANGE ALL        |       |     1 |    13 |    10 |     1 |
    |   2 |   SORT AGGREGATE            |       |     1 |    13 |       |       |
    |   3 |    INDEX FULL SCAN (MIN/MAX)| PI_PK |     1 |    13 |    10 |     1 |
    -----------------------------------------------------------------------------
    

    请注意,分区是按适当的顺序列出的(从 10 到 1)。 9iR2 中的计划有所不同:

    -----------------------------------------------------------------------------
    | Id  | Operation                   | Name  | Rows  | Bytes | Pstart| Pstop |
    -----------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |       |     1 |     4 |       |       |
    |   1 |  SORT AGGREGATE             |       |     1 |     4 |       |       |
    |   2 |   PARTITION RANGE ALL       |       |       |       |     1 |    10 |
    |   3 |    INDEX FULL SCAN (MIN/MAX)| PI_PK | 10000 | 40000 |     1 |    10 |
    -----------------------------------------------------------------------------
    

    看来9i和11g之间进行了一些优化。是时候升级了吗?

    【讨论】:

    • 我的表在同一列上确实包含索引,但它包含大量数据(1 亿+),因此查询需要大量时间来处理(30-40 分钟)。这就是为什么我正在考虑使用该方法按 desc 顺序扫描每个分区。即使在最大查询中,解释计划也将 pstart 显示为 1,pstop 显示为 11。如果不是反向 pstart=11 和 pstop =1,我无法将我的解释计划发布为它在生产服务器上,但是从分区中选择最大值的解释计划远小于从表中选择最大值。
    • 我刚刚检查过,如果我从表中选择 min 它需要不到一秒钟,因为 pstart 是 1 但从表中选择 max 因为相同而需要很长时间。如何反转pstart 到 11 和 pstop 到 1?
    • 请发布您的 CREATE TABLE 语句以及相关索引以及您的解释计划。如果你在 11gR2 上有一个 全局 索引,你应该得到一个 FULL INDEX SCAN (MIN/MAX),即使表很大,它也只会获取一行。
    • 如果您不想发布生产说明计划/创建表语句,请创建一个带有测试表的类似案例,然后我们可以比较我们的结果。
    • 好的,您的索引是本地分区的,而不是全局的。也许这解释了差异。您运行的是哪个版本的 Oracle?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-21
    • 1970-01-01
    • 2020-12-23
    相关资源
    最近更新 更多