Nếu không có kế hoạch giải thích hoặc bảng định nghĩa, thật khó để biết điều gì đang xảy ra. Dự đoán đầu tiên của tôi là bạn có các chỉ mục được phân vùng LOCAL mà không có year
cột. Chúng trợ giúp với COUNT (*) trên một phân vùng, tuy nhiên, chúng dường như không được sử dụng khi bạn truy vấn một năm (ít nhất là trên 10.2.0.3).
Đây là một ví dụ nhỏ mô phỏng lại phát hiện của bạn (và một cách giải quyết):
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
Bây giờ hãy so sánh hai kế hoạch giải thích:
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)
Như bạn có thể thấy, chỉ mục không được sử dụng khi bạn truy vấn trực tiếp năm. Khi bạn thêm năm vào chỉ mục ĐỊA PHƯƠNG, nó sẽ được sử dụng. Tôi đã sử dụng lệnh COMPRESS 1 để yêu cầu Oracle nén cột đầu tiên. Chỉ mục kết quả có kích thước gần bằng với chỉ mục gốc (nhờ nén) nên hiệu suất sẽ không bị ảnh hưởng.
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...)