優化器依賴統計資訊產生執行計畫。Oracle 提供三類統計:表統計、欄位統計、索引統計,各自分為表/索引層級、分區層級、子分區層級。本文系統整理相關視圖、關鍵欄位含義、直方圖類型與優化器估算機制。
| 類型 | 層級 | 主要視圖 |
|---|---|---|
| 表統計 | 表/索引層級 | USER_TAB_STATISTICS, USER_TABLES |
| 分區層級 | USER_TAB_STATISTICS, USER_TAB_PARTITIONS | |
| 子分區層級 | USER_TAB_STATISTICS, USER_TAB_SUBPARTITIONS | |
| 欄位統計 | 表/索引層級 | USER_TAB_COL_STATISTICS, USER_TAB_HISTOGRAMS |
| 分區層級 | USER_PART_COL_STATISTICS, USER_PART_HISTOGRAMS | |
| 子分區層級 | USER_SUBPART_COL_STATISTICS, USER_SUBPART_HISTOGRAMS | |
| 索引統計 | 表/索引層級 | USER_IND_STATISTICS, USER_INDEXES |
| 分區層級 | USER_IND_STATISTICS, USER_IND_PARTITIONS | |
| 子分區層級 | USER_IND_STATISTICS, USER_IND_SUBPARTITIONS |
-- 1. 建立測試表 (1000 筆)
CREATE TABLE test AS
SELECT ROWNUM AS id,
ROUND(DBMS_RANDOM.NORMAL * 1000) AS val1,
100 + ROUND(LN(ROWNUM / 3.25 + 2)) AS val2,
100 + ROUND(LN(ROWNUM / 3.25 + 2)) AS val3,
DBMS_RANDOM.STRING('p', 250) AS pad
FROM all_objects
WHERE ROWNUM <= 1000
ORDER BY DBMS_RANDOM.VALUE;
-- 2. 清理負值
UPDATE test SET val1 = NULL WHERE val1 < 0;
-- 3. 建立主鍵與索引
ALTER TABLE test ADD CONSTRAINT test_pk PRIMARY KEY (id);
CREATE INDEX test_val1 ON test (val1);
CREATE INDEX test_val2 ON test (val2);
-- 4. 收集統計資訊 (100% 採樣、歪斜欄位自動建直方圖)
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
OWNNAME => USER,
TABNAME => 'TEST',
ESTIMATE_PERCENT => 100,
METHOD_OPT => 'FOR ALL COLUMNS SIZE SKEWONLY',
CASCADE => TRUE
);
END;
SELECT num_rows, blocks, empty_blocks, avg_space, chain_cnt, avg_row_len FROM user_tab_statistics WHERE table_name = 'TEST'; -- 結果範例 NUM_ROWS BLOCKS EMPTY_BLOCKS AVG_SPACE CHAIN_CNT AVG_ROW_LEN ------- ------ ------------ --------- --------- ----------- 1000 39 0 0 0 265
SELECT column_name, num_distinct, low_value, high_value, density,
num_nulls, avg_col_len, histogram, num_buckets
FROM user_tab_col_statistics WHERE table_name = 'TEST';
DENSITY = 1 / NUM_DISTINCTNONE、FREQUENCY (頻率)、HEIGHT BALANCED (等高)DECLARE
l_val test.val1%TYPE;
BEGIN
FOR v IN (SELECT low_value, high_value FROM user_tab_col_statistics WHERE table_name = 'TEST') LOOP
DBMS_STATS.CONVERT_RAW_VALUE(v.low_value, l_val);
DBMS_OUTPUT.PUT_LINE('LOW : ' || l_val);
DBMS_STATS.CONVERT_RAW_VALUE(v.high_value, l_val);
DBMS_OUTPUT.PUT_LINE('HIGH : ' || l_val);
END LOOP;
END;
優化器需估算 WHERE col = value 回傳行數。資料均勻時用 Min/Max/NDV 即可估算;資料傾斜時需直方圖。
ENDPOINT_VALUE = 該值本身 (數字類型;字串轉換取前 6 Bytes)ENDPOINT_NUMBER = 累積出現次數,相減得單一值頻率-- 查詢 VAL2 的頻率分布
SELECT column_name, endpoint_value, endpoint_number,
endpoint_number - LAG(endpoint_number, 1, 0) OVER (ORDER BY endpoint_number) AS frequency
FROM user_tab_histograms
WHERE table_name = 'TEST' AND column_name = 'VAL2'
ORDER BY endpoint_number;
-- 結果
COLUMN_NAME ENDPOINT_VALUE ENDPOINT_NUMBER FREQUENCY
----------- -------------- --------------- ---------
VAL2 101 8 8
VAL2 102 33 25
VAL2 103 101 68
VAL2 104 286 185
VAL2 105 788 502
VAL2 106 1000 212
EXPLAIN PLAN SET STATEMENT_ID = '101' FOR SELECT * FROM test WHERE val2 = 101; EXPLAIN PLAN SET STATEMENT_ID = '102' FOR SELECT * FROM test WHERE val2 = 102; ... SELECT statement_id, cardinality FROM plan_table WHERE id = 0; -- 結果:CARDINALITY 精確等於 FREQUENCY STATEMENT_ID CARDINALITY ----------- ----------- 101 8 102 25 103 68 104 185 105 502 106 212
ENDPOINT_NUMBER = Bucket 編號SELECT index_name, blevel, leaf_blocks, distinct_keys, num_rows,
clustering_factor, avg_leaf_blocks_per_key, avg_data_blocks_per_key
FROM user_ind_statistics WHERE table_name = 'TEST';
LEAF_BLOCKS / DISTINCT_KEYSCLUSTERING_FACTOR / DISTINCT_KEYSAUTO_SAMPLE_SIZE (11g+),小表用 100%'FOR ALL COLUMNS SIZE AUTO' 讓 Oracle 自動判斷直方圖TRUE 同步收集索引統計DBMS_STATS.GATHER_SCHEMA_STATS 配合維護窗口DBMS_STATS.LOCK_TABLE_STATSDBMS_STATS.EXPORT/IMPORT_*_STATS