Oracle 查詢行數很少,為什麼不走索引?

chenoracle發表於2022-09-03

問題:

以下查詢涉及 90 行,只佔總行數的 90/10000 ,但使用了全表掃描:

select /*+ gather_plan_statistics */ count ( distinct junk ), count (*)
    from  bricks
    where  weight between 1 and 10;

   COUNT(DISTINCTJUNK)    COUNT(*)
______________________ ___________
     4          90

-------------------------------------------------------------------------------------------
| Id  | Operation     | Name| Starts | E-Rows | A-Rows |   A-Time| Buffers |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     ||      1 |  | 1 |00:00:00.01 |     120 |
|   1 |  SORT AGGREGATE      ||      1 |1 | 1 |00:00:00.01 |     120 |
|   2 |   VIEW         |      VW_DAG_0 |  1 | 92 | 4 |00:00:00.01 |     120 |
|   3 |    HASH GROUP BY     ||      1 |     92 | 4 |00:00:00.01 |     120 |
|*  4 |     TABLE ACCESS FULL| BRICKS|      1 |     92 |90 |00:00:00.01 |     120 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - filter(("WEIGHT"<=10 AND "WEIGHT">=1))
22 rows selected.


以下查詢涉及 1000 行,反而使用了索引:

select /*+ gather_plan_statistics */ count ( distinct junk ), count (*)
    from  bricks
    where  brick_id between 1 and 1000;
 
COUNT(DISTINCTJUNK)   COUNT(*)
------------------- ----------
  4         1000

為什麼涉及行數少的查詢,反而不走索引了?

------------------------------------------------------------------------------------------------------
| Id  | Operation       | Name   | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |   |  1 |     |    1 |00:00:00.01 |  15 |
|   1 |  SORT AGGREGATE        |   |  1 |   1 |    1 |00:00:00.01 |  15 |
|   2 |   VIEW       | VW_DAG_0  |  1 | 995 |    4 |00:00:00.01 |  15 |
|   3 |    HASH GROUP BY       |   |  1 | 995 |    4 |00:00:00.01 |  15 |
|   4 |     TABLE ACCESS BY INDEX ROWID| BRICKS    |  1 | 995 | 1000 |00:00:00.01 |  15 |
|*  5 |      INDEX RANGE SCAN       | BRICKS_PK |  1 | 995 | 1000 |00:00:00.01 |   3 |
------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("BRICK_ID">=1 AND "BRICK_ID"<=1000)
23 rows selected.

下面用不同顏色的積木表示資料,存放積木的盒子表示資料塊。

下面我們有 4 個資料塊,儲存了不同顏色的積木,每個盒子有 2 個積木,下面需要找出紅色積木 :

select * from bricks where colour = 'red';

全表掃描方式:

1. 讀取表中所有塊。

2. 逐個塊進行查詢,選出符合條件的資料。

索引掃描方式

只掃描存在紅色積木的盒子,而不是所有盒子。

使用索引時資料庫訪問多少資料塊,取決於這些行在表中的物理位置

在使用索引時,存在兩個極端情況

1. 需要查詢的資料,全都位於同一個資料塊內。

例如紅色積木,在同一個盒子裡,在索引掃描查詢紅色積木時,只需要查 1 個盒子,即 1 I/O

2. 需要查詢的每條資料,全都分散在不同的資料庫內。

紅色積木,分佈在不同的盒子裡。在索引掃描查詢紅色積木時,只需要兩個盒子,即 2 I/O

或者每個盒子都只有一個紅積木,此時查詢紅色積木需要查詢 5 個盒子,即 5 I/O ,接近全面掃描 I/O 次數。

資料庫如何知道在執行查詢之前使用索引需要訪問多少個塊呢?

實際上資料庫也無法精準計算出資料塊個數,但是可以透過聚簇因子來進行評估。

 

什麼是聚簇因子

Oralce 資料庫系統中最普通,最為常用的即為堆表。

     堆表的資料儲存方式為無序儲存,也就是任意的DML 操作都可能使得當前資料塊存在可用的空閒空間。

     處於節省空間的考慮,塊上的可用空閒空間會被新插入的行填充,而不是按順序填充到最後被使用的塊上。

     上述的操作方式導致了資料的無序性的產生。

     當建立索引時,會根據指定的列按順序來填充到索引塊,預設的情況下為升序。

新建或重建索引時,索引列上的順序是有序的,而表上的順序是無序的,也就是存在了差異,即表現為聚簇因子。

 

聚簇因子是資料庫在收集表統計資訊時計算得出的一個計算器。

它會統計當前資料是否和上一條資料在同一個塊中。

如果不在一個塊中,聚簇因子計算加 1

如果在同一個塊中,計數器保持不變。

所以聚簇因子的下限是表的塊數,上限是表的行數。

塊數 <= 聚簇因子 <= 行數

聚簇因子越高,索引中的順序行在整個表中的分散度越高,索引效率可能越低。

 

聚簇因子是基於表上索引列上的一個值,每一個索引都有一個聚簇因子。
    用於描述索引塊上與表塊上儲存資料在順序上的相似程度,也就說表上的資料行的儲存順序與索引列上順序是否一致。

 

在全索引掃描中, CF 的值基本上等同於物理 I/O 或塊訪問數,如果相同的塊被連續讀,則 Oracle 認為只需要 1 次物理 I/O
    好的 CF 值接近於表上的塊數,而差的 CF 值則接近於表上的行數。
    聚簇因子在索引建立時就會透過表上存存在的行以及索引塊計算獲得

Oracle 如何計算聚簇因子

     執行或預估一次全索引掃描。

     檢查索引塊上每一個rowid 的值,檢視是否前一個 rowid 的值與後一個指向了相同的資料塊,如果指向了不相同的資料塊則 CF 的值增加 1

當索引塊上的每一個rowid 被檢查完畢,即得到最終的 CF 值。

 

這也就是為什麼在最開始的兩個 SQL ,查詢行數少的為什麼不走索引,還是因為聚簇因子的原因。

 

下面透過實驗來看下聚簇因子的影響

建立環境

建立表bricks和索引,及全域性臨時表bricks_temp,並在最後蒐集統計資訊:


conn cjc/******

復位隨機數

exec dbms_random.seed ( 0 );


建立表bricks,

create table bricks ( 
  brick_id not null constraint bricks_pk primary key,
  colour   not null,
  shape    not null,
  weight   not null,
  insert_date not null,
  junk     default lpad ( 'x', 50, 'x' ) not null 
) as
  with rws as (
    select level x from dual
    connect by level <= 10000
  )
    select rownum brick_id, 
           case ceil ( rownum / 2500 )
             when 4 then 'red'
             when 1 then 'blue'
             when 2 then 'green'
             when 3 then 'yellow'
           end colour, 
           case mod ( rownum, 4 )
             when 0 then 'cube'
             when 1 then 'cylinder'
             when 2 then 'pyramid'
             when 3 then 'prism'
           end shape,
           round ( dbms_random.value ( 1, 1000 ) ),
           date'2022-01-01' + ( rownum/24 ) + ( mod ( rownum, 24 ) / 36 ) insert_date,
           lpad ( ascii ( mod ( rownum, 26 ) + 65 ), 50, 'x' )
    from   rws;

檢視錶結構

SQL> desc bricks
 Name   Null?    Type
 ----------------------------------------- -------- ----------------------------
 BRICK_ID   NOT NULL NUMBER
 COLOUR    NOT NULL VARCHAR2(6)
 SHAPE   NOT NULL VARCHAR2(8)
 WEIGHT    NOT NULL NUMBER
 INSERT_DATE   NOT NULL DATE
 JUNK   NOT NULL VARCHAR2(200)


  建立臨時表bricks_temp

create global temporary table bricks_temp as
  select * from bricks
  where  1 = 0;

建立索引,索引列weight

create index brick_weight_i on 
  bricks ( weight );

建立索引,索引列shape

create index brick_shape_i on 
  bricks ( shape );

建立索引,索引列colour

create index brick_colour_i on 
  bricks ( colour );

建立索引,索引列insert_date

create index brick_insert_date_i on 
  bricks ( insert_date );

收集表統計資訊

EXEC DBMS_STATS.GATHER_TABLE_STATS('CJC','BRICKS',estimate_percent=>100,method_opt=> 'FOR ALL INDEXED COLUMNS',CASCADE=> TRUE);

檢視資料,bricks表有10000行:

SQL> select count(*) from bricks;
   COUNT(*)
___________
      10000

查詢資料

SQL>
SELECT
    brick_id,
    colour,
    shape,
    weight,
    insert_date
FROM
    bricks
WHERE
    ROWNUM <= 9;

 BRICK_ID COLOUR SHAPE        WEIGHT INSERT_DATE
---------- ------ -------- ---------- ------------------
 1 blue   cylinder   64 01-JAN-22
 2 blue   pyramid  829 01-JAN-22
 3 blue   prism   233 01-JAN-22
 4 blue   cube  219 01-JAN-22
 5 blue   cylinder  371 01-JAN-22
 6 blue   pyramid   70 01-JAN-22
 7 blue   prism   461 01-JAN-22
 8 blue   cube  953 01-JAN-22
 9 blue   cylinder  944 01-JAN-22
9 rows selected.

junk列長度為1000,目的是將行撐大,沒有在結果集中顯示。


索引什麼時候有用?

大家通常認為,當索引定位表中的很少幾行時,它被認為是有用的。

但很少有多少?這個很難界定。我們透過示例來了解一下。


bricks表現有5個索引:

set line 300
col cols for a25
select ui.index_name, 
       listagg ( uic.column_name, ',' )
         within group ( order by column_position ) cols
from   user_indexes ui
join   user_ind_columns uic
on     ui.index_name = uic.index_name
where  ui.table_name = 'BRICKS'
group  by ui.index_name;
INDEX_NAME       COLS
------------------------------ -------------------------
BRICKS_PK           BRICK_ID
BRICK_COLOUR_I       COLOUR
BRICK_INSERT_DATE_I       INSERT_DATE
BRICK_SHAPE_I       SHAPE
BRICK_WEIGHT_I       WEIGHT


以下查詢涉及90行,只佔總行數的90/10000,但使用了全表掃描:

select /*+ gather_plan_statistics */ count ( distinct junk ), count (*)
    from  bricks
    where  weight between 1 and 10;
   COUNT(DISTINCTJUNK)    COUNT(*)
______________________ ___________
 4          90

SQL> select * from   table(dbms_xplan.display_cursor( format => 'IOSTATS LAST'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID60p9xcp0b6cfh, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count ( distinct junk ), count (*)
    from  bricks     where  weight between 1 and 10
Plan hash value: 2750714649
-------------------------------------------------------------------------------------------
| Id  | Operation     | Name| Starts | E-Rows | A-Rows |   A-Time| Buffers |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     ||      1 |  | 1 |00:00:00.01 |     120 |
|   1 |  SORT AGGREGATE      ||      1 |1 | 1 |00:00:00.01 |     120 |
|   2 |   VIEW         |      VW_DAG_0 |  1 | 92 | 4 |00:00:00.01 |     120 |
|   3 |    HASH GROUP BY     ||      1 |     92 | 4 |00:00:00.01 |     120 |
|*  4 |     TABLE ACCESS FULL| BRICKS|      1 |     92 |90 |00:00:00.01 |     120 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - filter(("WEIGHT"<=10 AND "WEIGHT">=1))
22 rows selected.


以下查詢涉及1000行,反而使用了索引:

select /*+ gather_plan_statistics */ count ( distinct junk ), count (*)
    from  bricks
    where  brick_id between 1 and 1000;
COUNT(DISTINCTJUNK)   COUNT(*)
------------------- ----------
  4        1000

SQL> select * from   table(dbms_xplan.display_cursor( format => 'IOSTATS LAST'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID1s29r51b4ka1b, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count ( distinct junk ), count (*)
    from  bricks     where  brick_id between 1 and 1000
Plan hash value: 301905156
------------------------------------------------------------------------------------------------------
| Id  | Operation       | Name   | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |   |  1 |     |    1 |00:00:00.01 |  15 |
|   1 |  SORT AGGREGATE        |   |  1 |   1 |    1 |00:00:00.01 |  15 |
|   2 |   VIEW       | VW_DAG_0  |  1 | 995 |    4 |00:00:00.01 |  15 |
|   3 |    HASH GROUP BY       |   |  1 | 995 |    4 |00:00:00.01 |  15 |
|   4 |     TABLE ACCESS BY INDEX ROWID| BRICKS    |  1 | 995 | 1000 |00:00:00.01 |  15 |
|*  5 |      INDEX RANGE SCAN       | BRICKS_PK |  1 | 995 | 1000 |00:00:00.01 |   3 |
------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("BRICK_ID">=1 AND "BRICK_ID"<=1000)
23 rows selected.

物理行位置

Oracle資料庫將行存於資料塊中。您可以使用DBMS_rowid查詢行的塊號。例如:
select brick_id,
       dbms_rowid.rowid_block_number ( rowid ) blk#
    from   bricks
    where  mod ( brick_id, 1000 ) = 0;

 

 BRICK_ID BLK#
---------- ----------
      1000  654
      2000  667
      3000  679
      4000  692
      5000  705
      6000  717
      7000  730
      8000  742
      9000  755
     10000  766
10 rows selected.

預設情況下,Oracle資料庫中的表時堆表(Heap table)。這意味著資料庫可以將行放在任何地方。

但是索引是有序的資料結構。新條目必須放在正確的位置。例如,如果在數字列中插入42,則在該列位於41之後,或43之前。

行的物理順序與索引的邏輯順序越接近,該索引就越有效。


Oracle資料庫中最小的I/O單元是資料塊。因此,指向同一資料庫塊的連續索引項越多,在一個I/O中獲取的行就越多。因此,索引就越有效。

下面這個SQL比較難理解,但也是本文中最精彩的部分。同時用到了分析函式和Pivot轉換:

set line 150
with rws as ( 
  select ceil ( brick_id / 1000 ) id, 
         ceil ( 
           dense_rank () over (
             order by dbms_rowid.rowid_block_number ( rowid )
           ) / 10 
         ) rid
  from   bricks
)
  select * from rws
  pivot (
    count (*) for rid in (
      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
    )
  )
  order by id;

首先看一下其中的子查詢rws:

 select ceil ( brick_id / 1000 ) id, 
         ceil ( 
           dense_rank () over (
             order by dbms_rowid.rowid_block_number ( rowid )
           ) / 10 
         ) rid
  from   bricks;

由於bricks表有10000行,因此這個查詢的結果也有10000行,但為了方便最終顯示,id列和rid列都進行了分組。

id被分為10段,也就是1-1000, 1001-2000,…,9001-10000。

rid被分為12段,也就是按dense_rank排序後,從1-10,11-20,…,120-130。


其實這裡有一個隱含條件沒有說,就是此表只有128個資料塊:

SQL> select blocks from user_segments where segment_name = 'BRICKS';
   BLOCKS
_________
      128

以上子查詢再經過pivot的count(*)計數得到以下的結果,橫向是資料塊的分段,縱向是行的分段。此圖可以看出資料的分佈(聚集度或分散度):

ID    1       2  3     45   6      7  8    9      10 11    12
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
 1  861     139  0     00   0      0  0    0       0  0     0
 2    0     721279     00   0      0  0    0       0  0     0
 3    0       0580   4200   0      0  0    0       0  0     0
 4    0       0  0   430    570   0      0  0    0       0  0     0
 5    0       0  0     0    280 720      0  0    0       0  0     0
 6    0       0  0     00 128    840 32    0       0  0     0
 7    0       0  0     00   0      0     808 192       0  0     0
 8    0       0  0     00   0      0  0  653     347  0     0
 9    0       0  0     00   0      0  0    0     523477     0
10    0       0  0     00   0      0  0    0       0393   607
10 rows selected.

例如對於ID 1那行(對應表中的第1-1000行),所有行聚集與1-2段。這2個數加起來正好等於1000(861+139)。同理,其他行加一起也是1000。

以上是從brick_id的視角,而從weight的視角(有1000個不同值,所以它是除100,不是之前的1000),則其分佈如下。分佈比較分散:

SQL>
with rws as ( 
  select ceil ( weight / 100 ) wt, 
         ceil ( 
           dense_rank () over (
             order by dbms_rowid.rowid_block_number ( rowid )
           ) / 10 
         ) rid
  from   bricks
)
  select * from rws
  pivot (
    count (*) for rid in (
      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
    )
  )
  order by wt;

WT    1       2  3     45   6      7  8    9      10 11    12
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
 1  109      76 84    96       98  98     76 83   86      83 93    59
 2  106      90 87    92       88  81     92     102   77      83 89    59
 3   77      85 88    85       91  68     78 73   87      91 93    65
 4   86      94 78    94       80  81    101 93   98      83 72    72
 5   70      84 87    89       90  95     82 71   76      87100    56
 6   81      83 82    77       76  84     92 99   89      96 91    54
 7   70      91 84    80       86  80     75 80   85      87 95    59
 8   81      97 90    84       89  86     62 71   74      80 81    68
 9  102      72 99    66       78  90     89 83   86      91 85    49
10   79      88 80    87       74  85     93 85   87      89 71    66
10 rows selected.

SQL>
select count(distinct weight) from bricks;
COUNT(DISTINCTWEIGHT)
---------------------
                 1000

這意味著和brick_id相比,透過weight獲取同樣行數的資料,資料庫必須進行更多的I/O操作。因此,基於weight的索引不如基於brick_id的索引有效。

這種分佈實際上是由於brick_id是遞增順序插入,而weight是用隨機數生成的(dbms_random.value ( 1, 1000 ))。

因此,準確的說,在確定索引的效率時,重要的是I/O操作的數量(訪問資料庫的次數)。不是訪問多少行!


那麼最佳化器如何知道邏輯順序和物理順序的匹配程度呢?它使用聚集因子(clustering factor)進行估計。


聚集因子(clustering factor)

聚集因子是衡量邏輯索引順序與行的物理表順序匹配程度的指標。資料庫在收集統計資料時計算此值。它的計算基於:

當前索引項對應的行與上一個索引項對應的行在同一塊中,還是不同的塊中?


每次連續索引項位於不同的塊中時,最佳化器都會將計數器加1。最終該值越低,行的聚集性越好,資料庫使用索引的可能性越大。


聚集因子可如下檢視,可以看出,BRICK_WEIGHT_I的聚集因子遠高於BRICKS_PK的聚集因子。

從另一個角度,如果CLUSTERING_FACTOR和BLOCKS數值接近,則表示聚集性越好:

select index_name, clustering_factor, ut.num_rows, ut.blocks
from   user_indexes ui
join   user_tables ut
on     ui.table_name = ut.table_name
where  ui.table_name = 'BRICKS';

INDEX_NAME       CLUSTERING_FACTOR   NUM_ROWSBLOCKS
------------------------------ ----------------- ---------- ----------
BRICK_INSERT_DATE_I     877      10000   127
BRICK_COLOUR_I     119      10000   127
BRICK_SHAPE_I     468      10000   127
BRICK_WEIGHT_I    9572      10000   127
BRICKS_PK         117      10000   12

獲取部分聚集的行

部分聚集(Partly Clustered)指聚集因子比較“平均”。此時資料庫傾向於全表掃描:

select /*+ gather_plan_statistics */ count ( distinct junk ), count (*)
   from  bricks
   where  insert_date >= date'2022-02-01'
   and    insert_date < date'2022-02-21';
COUNT(DISTINCTJUNK)   COUNT(*)
------------------- ----------
  4   480

select * from   table(dbms_xplan.display_cursor( format => 'IOSTATS LAST'));
-------------------------------------------------------------------------------------------
| Id  | Operation     | Name| Starts | E-Rows | A-Rows |   A-Time| Buffers |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     ||      1 |  | 1 |00:00:00.01 |     120 |
|   1 |  SORT AGGREGATE      ||      1 |1 | 1 |00:00:00.01 |     120 |
|   2 |   VIEW     | VW_DAG_0 |      1 |    479 | 4 |00:00:00.01 |     120 |
|   3 |    HASH GROUP BY     ||      1 |    479 | 4 |00:00:00.01 |     120 |
|*  4 |     TABLE ACCESS FULL| BRICKS|      1 |    479 |    480 |00:00:00.01 |     120 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - filter(("INSERT_DATE"<TO_DATE(' 2022-02-21 00:00:00', 'syyyy-mm-dd
      hh24:mi:ss') AND "INSERT_DATE">=TO_DATE(' 2022-02-01 00:00:00', 'syyyy-mm-dd
      hh24:mi:ss')))
25 rows selected.

但是從月的角度看,資料的聚集度還是不錯的:

with rws as ( 
  select trunc ( insert_date, 'mm' ) dt, 
         ceil ( 
           dense_rank () over (
             order by dbms_rowid.rowid_block_number ( rowid )
           ) / 10 
         ) rid
  from   bricks
)
  select * from rws
  pivot (
    count (*) for rid in (
      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
    )
  )
  order by dt;

DT    1       2  3     45   6      7  8    9      10 11    12
------------------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
01-JAN-22  734       0  0     00   0      0  0    0       0  0     0
01-FEB-22  127     545  0     00   0      0  0    0       0  0     0
01-MAR-22    0     315429     00   0      0  0    0       0  0     0
01-APR-22    0       0430   2900   0      0  0    0       0  0     0
01-MAY-22    0       0  0   560   184   0      0  0    0       0  0     0
01-JUN-22    0       0  0     0   666  54      0  0    0       0  0     0
01-JUL-22    0       0  0     00 744      0  0    0       0  0     0
01-AUG-22    0       0  0     00  50    694  0    0       0  0     0
01-SEP-22    0       0  0     00   0    146     574    0       0  0     0
01-OCT-22    0       0  0     00   0      0     266  478       0  0     0
01-NOV-22    0       0  0     00   0      0  0  367     353  0     0
01-DEC-22    0       0  0     00   0      0  0    0     517227     0
01-JAN-23    0       0  0     00   0      0  0    0       0643   101
01-FEB-23    0       0  0     00   0      0  0    0       0  0   506
14 rows selected.

這是因為有幾行在兩個資料塊之間來回跳躍,導致聚集因子變大,從而帶給最佳化器以假象:

with rws as (
  select brick_id,
         to_char ( insert_date, 'DD MON HH24:MI' ) dt, 
         dbms_rowid.rowid_block_number ( rowid ) current_block,
         lag ( dbms_rowid.rowid_block_number ( rowid ) ) over (
           order by insert_date
         ) prev_block
  from   bricks
  where  insert_date >= date '2022-01-01'
  and    insert_date <  date '2022-02-01'
)
  select * from rws
  where  current_block <> prev_block
  order  by dt;

 

 BRICK_ID DT CURRENT_BLOCK PREV_BLOCK
---------- --------------------- ------------- ----------
   96  05 JAN 00:00    644      643
   87  05 JAN 01:00    643      644
   97  05 JAN 01:40    644      643
       174 08 JAN 10:00    645      644
       165 08 JAN 11:00    644      645
       175 08 JAN 11:40    645      644
       166 08 JAN 12:40    644      645
       176 08 JAN 13:20    645      644
       167 08 JAN 14:20    644      645
       177 08 JAN 15:00    645      644
       264 12 JAN 00:00    646      645
       255 12 JAN 01:00    645      646
       265 12 JAN 01:40    646      645
       256 12 JAN 02:40    645      646
       266 12 JAN 03:20    646      645
       257 12 JAN 04:20    645      646
       267 12 JAN 05:00    646      645
       258 12 JAN 06:00    645      646
       268 12 JAN 06:40    646      645
       259 12 JAN 07:40    645      646
       269 12 JAN 08:20    646      645
       346 15 JAN 16:40    647      646
       432 19 JAN 00:00    648      647
       423 19 JAN 01:00    647      648
       433 19 JAN 01:40    648      647
       424 19 JAN 02:40    647      648
       434 19 JAN 03:20    648      647
       425 19 JAN 04:20    647      648
       435 19 JAN 05:00    648      647
       426 19 JAN 06:00    647      648
       436 19 JAN 06:40    648      647
       427 19 JAN 07:40    647      648
       437 19 JAN 08:20    648      647
       428 19 JAN 09:20    647      648
       438 19 JAN 10:00    648      647
       429 19 JAN 11:00    647      648
       439 19 JAN 11:40    648      647
       430 19 JAN 12:40    647      648
       440 19 JAN 13:20    648      647
       431 19 JAN 14:20    647      648
       441 19 JAN 15:00    648      647
       518 22 JAN 23:20    649      648
       604 26 JAN 06:40    650      649
       595 26 JAN 07:40    649      650
       605 26 JAN 08:20    650      649
       596 26 JAN 09:20    649      650
       606 26 JAN 10:00    650      649
       597 26 JAN 11:00    649      650
       607 26 JAN 11:40    650      649
       598 26 JAN 12:40    649      650
       608 26 JAN 13:20    650      649
       599 26 JAN 14:20    649      650
       609 26 JAN 15:00    650      649
       696 30 JAN 00:00    651      650
       687 30 JAN 01:00    650      651
       697 30 JAN 01:40    651      650
       688 30 JAN 02:40    650      651
       698 30 JAN 03:20    651      650
       689 30 JAN 04:20    650      651
       699 30 JAN 05:00    651      650
60 rows selected.

如何解決類似問題呢,從12C開始,改進大部分聚集的行的統計資訊,可透過調整TABLE_CACHED_BLOCKS實現,先不演示TABLE_CACHED_BLOCKS使用方法。


參考:

《甲骨文技術》 -Oracle 開發者效能第 5 課:為什麼我的查詢不使用索引?

https://blog.csdn.net/stevensxiao/article/details/121043980


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29785807/viewspace-2913326/,如需轉載,請註明出處,否則將追究法律責任。

相關文章