范围分区跳过检查

Pra*_*ash 2 sql oracle performance partitioning

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

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

更新:

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

  2. 我们没有选择任何其他列.我只是在执行a 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

......等等

Vin*_*rat 6

如果没有解释计划或表格定义,很难说出发生了什么.我的第一个猜测是你有没有year列的LOCAL分区索引.它们帮助分区上的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
Run Code Online (Sandbox Code Playgroud)

现在比较两个解释计划:

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)
Run Code Online (Sandbox Code Playgroud)

如您所见,直接查询年份时使用索引.将年份添加到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...)
Run Code Online (Sandbox Code Playgroud)