【问题标题】:Range partition skip check范围分区跳过检查
【发布时间】:2010-03-29 05:15:16
【问题描述】:

我们在 oracle 中使用范围分区对年值进行了大量数据分区。我们使用了范围分区,但每个分区只包含一年的数据。当我们编写针对特定年份的查询时,oracle 会从该分区中获取信息,但仍会检查年份是否是我们指定的年份。由于今年列不是索引的一部分,因此它从表中获取年份并进行比较。我们已经看到,每当查询去获取表数据时,它都会变得太慢。

我们能否以某种方式避免 oracle 比较年份值,因为我们肯定知道分区只包含一年的信息。

更新:

  1. 执行分区的年份数据类型为数字类型。

  2. 我们没有选择任何其他列。我只是在执行count(*) 并且没有选择任何列。

  3. 如果我们删除条件并将查询定位到特定分区 select count(*) from table_name partition(part_2004)它更快 尽管 select count(*) from table where year = 2004 慢很多。

  4. 分区在年份列上,这是一个数字,如下所示

    小于 2005 年 part_2004

    小于 2006 年 part_2005

    小于 2007 年 part_2006

...等等

【问题讨论】:

  • 我们在这里讨论什么数据类型(日期、数字、字符)?
  • 分区是在纯日期范围上定义的,还是在基于日期的表达式上定义的(例如TO_CHAR(mydate,'YYYY'))?
  • 伙计们,用更多信息更新了我的问题。感谢您的调查。
  • 我认为 Oracle 不够聪明,无法确定范围分区仅包含一个值。所以它必须再次检查条件。更改为相等分区或按年创建索引怎么样?
  • 按照 Vincent 的建议将年份添加到索引并压缩它有助于我们加快查询速度。

标签: sql oracle partitioning performance


【解决方案1】:

如果没有解释计划或表定义,真的很难判断发生了什么。我的第一个猜测是你有没有year 列的本地分区索引。它们有助于分区上的 COUNT(*),但是当您查询一年时(至少在 10.2.0.3 上),它们似乎没有被使用。

这是一个重现您的发现的小示例(和解决方法):

SQL> CREATE TABLE DATA (
  2     YEAR NUMBER NOT NULL,
  3     ID NUMBER NOT NULL,
  4     extra CHAR(1000)
  5  ) PARTITION BY RANGE (YEAR) (
  6     PARTITION part1 VALUES LESS THAN (2010),
  7     PARTITION part2 VALUES LESS THAN (2011)
  8  );
Table created

SQL> CREATE INDEX ix_id ON DATA  (ID) LOCAL;
Index created

SQL> INSERT INTO DATA 
  2  (SELECT 2009+MOD(ROWNUM, 2), ROWNUM, 'A' FROM DUAL CONNECT BY LEVEL <=1e4);

10000 rows inserted

SQL> EXEC dbms_stats.gather_table_stats(USER, 'DATA', CASCADE=>TRUE);

PL/SQL procedure successfully completed

现在比较两个解释计划:

SQL> SELECT COUNT(*) FROM DATA WHERE YEAR=2010;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=197 Card=1 Bytes=4)
   1    0   SORT (AGGREGATE)
   2    1     PARTITION RANGE (SINGLE) (Cost=197 Card=5000 Bytes=20000)
   3    2       TABLE ACCESS (FULL) OF 'DATA' (TABLE) (Cost=197 Card=5000...)

SQL> SELECT COUNT(*) FROM DATA PARTITION (part1);

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=11 Card=1)
   1    0   SORT (AGGREGATE)
   2    1     PARTITION RANGE (SINGLE) (Cost=11 Card=5000)
   3    2       INDEX (FULL SCAN) OF 'IX_ID' (INDEX) (Cost=11 Card=5000)

如您所见,当您直接查询年份时,使用索引。当您将年份添加到 LOCAL 索引时,它将被使用。我使用 COMPRESS 1 指令告诉 Oracle 压缩第一列。生成的索引与原始索引的大小几乎相同(由于压缩),因此不会影响性能。

SQL> DROP INDEX ix_id;
 Index dropped

SQL> CREATE INDEX ix_id ON DATA (year, ID) LOCAL COMPRESS 1;
Index created

SQL> SELECT COUNT(*) FROM DATA WHERE YEAR=2010;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=12 Card=1 Bytes=4)
   1    0   SORT (AGGREGATE)
   2    1     PARTITION RANGE (SINGLE) (Cost=12 Card=5000 Bytes=20000)
   3    2       INDEX (RANGE SCAN) OF 'IX_ID' (INDEX) (Cost=12 Card=5000...)

【讨论】:

  • 没错……我们在索引中没有“年份”列,是的,它们是本地分区索引。
【解决方案2】:

你确定它只是为了检查年份而上桌吗?也许还涉及其他列?

查询是否应该只对(分区)索引起作用?

如果它无论如何都需要去表,那么额外的检查不会花费太多(如果分区是正确的)。

你能发布查询和执行计划吗?

【讨论】:

    猜你喜欢
    • 2021-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多