Tôi có một chương trình máy khách đang thực hiện một truy vấn đối với Chế độ xem bên ngoài nối một bảng này sang bảng khác. Hiệu suất rất tệ và tôi đã cố gắng điều chỉnh nó bằng cách thêm chỉ mục đúng. Truy vấn trong câu hỏi chỉ thực sự sử dụng bảng thứ hai, vì vậy tôi đã trực tiếp kiểm tra bảng đó.
Tôi đã tìm thấy (một số) chỉ mục hoạt động tốt cho truy vấn đối với bảng nhưng khi tôi chuyển nó sang sử dụng Chế độ xem, chúng đã dừng sử dụng bất kỳ chỉ mục nào và thay vào đó chỉ quét toàn bộ trên cả hai bảng. Vì các bảng này lớn (2-3 triệu hàng mỗi cái) nên điều này rất chậm.
Để đơn giản kiểm tra, tôi đã thay đổi truy vấn để bỏ qua và chỉ kết hợp phép nối ngoài vào chính truy vấn đó. Điều này đã tái tạo thành công vấn đề, nhưng để lại bí ẩn về lý do tại sao tham gia bên ngoài sẽ không sử dụng các chỉ mục.
Đây là bảng, với tất cả các chỉ mục tôi đã thêm trong khi kiểm tra:
CREATE TABLE TEST_DATA
(ID NUMBER(11,0) PRIMARY KEY,
FORMATTED_RESULT VARCHAR2(255 BYTE),
F_RESULT NUMBER,
IDNUM NUMBER(11,0),
IDNUM_DESCRIPTION VARCHAR2(128 BYTE),
LAB_NUMBER NUMBER(11,0),
SEQ_NUMBER NUMBER(11,0),
ORDERNO NUMBER(11,0),
SUPPL_FORMATTED_RESULT VARCHAR2(255 BYTE),
SUPPL_IDNUM NUMBER(11,0),
SUPPL_IDNUM_DESCRIPTION VARCHAR2(128 BYTE),
SUPPL_UNIT VARCHAR2(16 BYTE)
) ;
CREATE UNIQUE INDEX TEST_LN_SQN_ORDER ON TEST_DATA (LAB_NUMBER, SEQ_NUMBER, ORDERNO) ;
CREATE INDEX TEST_LN_SQN ON TEST_DATA (LAB_NUMBER, SEQ_NUMBER) ;
CREATE INDEX TD_CUIDD_CUFR ON TEST_DATA (UPPER(COALESCE(SUPPL_IDNUM_DESCRIPTION,IDNUM_DESCRIPTION)), UPPER(COALESCE(SUPPL_FORMATTED_RESULT,FORMATTED_RESULT))) ;
CREATE INDEX TD_UFR_IDN ON TEST_DATA (UPPER(FORMATTED_RESULT), IDNUM) ;
CREATE INDEX TD_UIDD_UFR ON TEST_DATA (UPPER(IDNUM_DESCRIPTION), UPPER(FORMATTED_RESULT)) ;
CREATE INDEX TD_CUFR_CIDN_SN_LN ON TEST_DATA (UPPER(COALESCE(SUPPL_FORMATTED_RESULT,FORMATTED_RESULT)), COALESCE(SUPPL_IDNUM,IDNUM), SEQ_NUMBER, LAB_NUMBER) ;
CREATE INDEX TD_SN_LN_CUFR_CIDN ON TEST_DATA (SEQ_NUMBER, LAB_NUMBER, UPPER(COALESCE(SUPPL_FORMATTED_RESULT,FORMATTED_RESULT)), COALESCE(SUPPL_IDNUM,IDNUM)) ;
CREATE INDEX TD_CUFR_CIDN ON TEST_DATA (UPPER(COALESCE(SUPPL_FORMATTED_RESULT,FORMATTED_RESULT)), COALESCE(SUPPL_IDNUM,IDNUM)) ;
Đây là bảng khác (bảng mà chúng tôi không thực sự sử dụng cho truy vấn này)
CREATE TABLE REQUEST_INFO
(NUMBER(11,0) PRIMARY KEY,
CHARGE_CODE VARCHAR2(32 BYTE),
LAB_NUMBER NUMBER(11,0),
SEQ_NUMBER NUMBER(11,0)
) ;
CREATE INDEX RI_LN_SN ON REQUEST_INFO (LAB_NUMBER, SEQ_NUMBER) ;
CREATE INDEX RI_SN_LN ON REQUEST_INFO (SEQ_NUMBER, LAB_NUMBER) ;
Vì vậy, trước tiên, đây là truy vấn trực tiếp vào bảng duy nhất, sử dụng thành công một trong các chỉ mục.
-- GOOD, Uses index : TD_CUFR_CIDN_SN_LN
select td.LAB_NUMBER
from test_DATA td
where UPPER(COALESCE(SUPPL_FORMATTED_RESULT,FORMATTED_RESULT))='491(10)376'
and COALESCE(TD.SUPPL_IDNUM, TD.IDNUM)=40549
;
Bây giờ đây là truy vấn sử dụng cả hai bảng với một phép nối bên trong . Điều này cũng sử dụng các chỉ mục và chạy nhanh.
-- GOOD, Uses indexes : TD_CUFR_CIDN_SN_LN AND RI_SN_LN
select TD.LAB_NUMBER
from REQUEST_INFO RI
JOIN TEST_DATA TD ON TD.LAB_NUMBER = RI.LAB_NUMBER AND TD.SEQ_NUMBER = RI.SEQ_NUMBER
where UPPER(COALESCE(TD.SUPPL_FORMATTED_RESULT,TD.FORMATTED_RESULT))='491(10)376'
and COALESCE(TD.SUPPL_IDNUM, TD.IDNUM)=40549
Và đây là cùng một truy vấn với Left Outer Join, vì nó được viết trong dạng xem. Điều này KHÔNG sử dụng bất kỳ chỉ mục nào và chạy rất chậm.
-- BAD, does not use indexes
select TD.LAB_NUMBER
from REQUEST_INFO RI
LEFT JOIN TEST_DATA TD ON TD.LAB_NUMBER = RI.LAB_NUMBER AND TD.SEQ_NUMBER = RI.SEQ_NUMBER
where UPPER(COALESCE(TD.SUPPL_FORMATTED_RESULT,TD.FORMATTED_RESULT))='491(10)376'
and COALESCE(TD.SUPPL_IDNUM, TD.IDNUM)=40549
;
Bây giờ trước khi bất cứ ai nói nó: truy vấn này thực sự là một cách logic giống với trước đó. Điều này là do mệnh đề WHERE đang lọc trên các cột từ bảng bên ngoài (TD), điều này có hiệu quả / hợp lý biến một phép nối ngoài thành một phép nối bên trong (đây là lý do tại sao điều kiện xảy ra trong mệnh đề ON so với mệnh đề WHERE).
Bây giờ, chỉ để thêm vào sự kỳ lạ, tôi quyết định xem điều gì sẽ xảy ra nếu tôi làm cho sự ép buộc từ bên ngoài đến bên trong bùng nổ hơn:
-- GOOD, Uses indexes : TD_CUFR_CIDN_SN_LN AND RI_SN_LN
select TD.LAB_NUMBER
from REQUEST_INFO RI
LEFT JOIN TEST_DATA TD ON TD.LAB_NUMBER = RI.LAB_NUMBER AND TD.SEQ_NUMBER = RI.SEQ_NUMBER
where UPPER(COALESCE(TD.SUPPL_FORMATTED_RESULT,TD.FORMATTED_RESULT))='491(10)376'
and COALESCE(TD.SUPPL_IDNUM, TD.IDNUM)=40549
and TD.LAB_NUMBER IS NOT NULL
;
Thật đáng kinh ngạc, điều này đã làm việc!
Vì vậy, câu hỏi ở đây là, 1) TẠI SAO Oracle không tự tìm ra điều này?
Và 2) Có một số cài đặt hoặc Chỉ mục, v.v. mà tôi có thể tạo sẽ khiến Oracle tìm ra điều này một cách chính xác và sử dụng các chỉ mục không?
Xem xét bổ sung:
Chế độ xem được sử dụng bởi một loạt các truy vấn và ứng dụng khách khác, vì vậy tôi không thể thay đổi nó thành một tham gia bên trong cho một truy vấn này.
Máy khách đang tạo truy vấn, do đó rất khó / không thể thay đổi truy vấn với các điều kiện trường hợp đặc biệt kỳ quặc như: " Sử dụng chế độ xem này cho dữ liệu này, trừ khi bạn chỉ cần các cột này từ một bảng này, sau đó sử dụng một cột khác xem "hoặc" khi bạn cần các cột này và chỉ các cột này từ một bảng này, sau đó thêm 'IS NOT NULL' vào mệnh đề WHERE "
Bất kỳ đề xuất hoặc hiểu biết sẽ được hoan nghênh.
CẬP NHẬT: Tôi cũng đã thử nó trên Oracle 11g, tôi nhận được kết quả chính xác ở đó.
Mỗi yêu cầu, đây là đầu ra Giải thích Kế hoạch, đầu tiên là phiên bản tốt, nơi nó sử dụng các chỉ mục:
Rows Plan COST Predicates
3 SELECT STATEMENT 8
3 HASH JOIN 8 Access:TD.LAB_NUMBER=RI.LAB_NUMBER AND TD.SEQ_NUMBER=RI.SEQ_NUMBER
3 NESTED LOOPS 8
STATISTICS COLLECTOR
3 INDEX RANGE SCAN TD_CUFR_CIDN_SN_LN 4 Access:UPPER(COALESCE(SUPPL_FORMATTED_RESULT,FORMATTED_RESULT))='491(10)376' AND COALESCE(SUPPL_IDNUM,IDNUM)=40549, Filter:TD.LAB_NUMBER IS NOT NULL
1 INDEX RANGE SCAN RI_SN_LN 2 Access:TD.SEQ_NUMBER=RI.SEQ_NUMBER AND TD.LAB_NUMBER=RI.LAB_NUMBER
1 INDEX FAST FULL SCAN RI_SN_LN 2
Và bây giờ là phiên bản xấu:
Rows Plan COST Predicates
31939030 SELECT STATEMENT 910972
FILTER Filter:UPPER(COALESCE(SUPPL_FORMATTED_RESULT,FORMATTED_RESULT))='491(10)376' AND COALESCE(SUPPL_IDNUM,IDNUM)=40549
31939030 HASH JOIN OUTER 910972 Access:TD.LAB_NUMBER(+)=RI.LAB_NUMBER AND TD.SEQ_NUMBER(+)=RI.SEQ_NUMBER
6213479 TABLE ACCESS FULL REQUEST_INFO 58276
56276228 TABLE ACCESS FULL TEST_DATA 409612