2011-08-02 26 views
8

Tôi có một truy vấn đơn giản, chọn 200 hàng đầu được sắp xếp theo một trong các cột được lọc bởi cột được lập chỉ mục khác. Sự rắc rối là tại sao là kế hoạch truy vấn trong PL/SQL Developer cho thấy chỉ số này được sử dụng chỉ khi tôi chọn tất cả các hàng, ví dụ:Chỉ mục sai được sử dụng khi chọn các hàng trên cùng

SELECT * FROM 
(
SELECT * 
FROM cr_proposalsearch ps 
WHERE UPPER(ps.customerpostcode) like 'MK3%' 
ORDER BY ps.ProposalNumber DESC 
) 
WHERE ROWNUM <= 200 

Kế hoạch cho thấy rằng nó sử dụng chỉ số CR_PROPOSALSEARCH_I1, đó là một chỉ mục trên hai cột: PROPOSALNUMBER & UPPER (CUSTOMERNAME), điều này có 0.985s để thực hiện: query with ROWNUM

Nếu tôi thoát khỏi tình trạng ROWNUM, kế hoạch là những gì tôi mong đợi và nó thực thi trong 0.343s : query without ROWNUM

đâu index XIF25CR_PROPOSALSEARCH is on CR_PROPOSALSEARCH (UPPER(CUSTOMERPOSTCODE));

Tại sao?

EDIT: Tôi đã thu thập số liệu thống kê trên cr_proposalsearch bảng và cả hai kế hoạch truy vấn giờ đây cho thấy rằng chúng sử dụng chỉ mục XIF25CR_PROPOSALSEARCH.

+0

Hai truy vấn khác nhau, hai tập hợp kết quả khác nhau, tại sao không nên có hai kế hoạch giải thích khác nhau? – APC

Trả lời

8

Bao gồm ROWNUM thay đổi các tính toán của trình tối ưu hóa về đó là đường dẫn hiệu quả hơn.

Khi bạn thực hiện một truy vấn hàng đầu như thế này, nó không nhất thiết có nghĩa là Oracle sẽ nhận được tất cả các hàng, sắp xếp chúng hoàn toàn, sau đó trả lại hàng đầu. Hoạt động COUNT STOPKEY trong kế hoạch thực hiện cho thấy rằng Oracle sẽ chỉ thực hiện các thao tác cơ bản cho đến khi nó tìm thấy số hàng bạn đã yêu cầu.

Trình tối ưu hóa đã tính toán rằng truy vấn đầy đủ sẽ có được và sắp xếp 77K hàng. Nếu nó sử dụng kế hoạch này cho truy vấn trên cùng, nó sẽ phải thực hiện một loại lớn các hàng đó để tìm ra 200 hàng đầu (nó không nhất thiết phải sắp xếp chúng hoàn toàn, vì nó sẽ không quan tâm đến thứ tự chính xác các hàng ngang qua đầu, nhưng nó sẽ phải xem xét tất cả các hàng đó).

Kế hoạch cho truy vấn hàng đầu n sử dụng chỉ mục khác để tránh phải sắp xếp. Nó xem xét mỗi hàng theo thứ tự, kiểm tra xem nó có khớp với biến vị ngữ hay không, và nếu nó trả về nó. Khi nó trả về 200 hàng, nó đã xong. Tính toán của nó đã chỉ ra rằng điều này sẽ hiệu quả hơn khi nhận được một số lượng nhỏ các hàng. (Dĩ nhiên, điều đó có thể không đúng, bạn không nói hiệu suất tương đối của các truy vấn này là gì.Nếu một trình tối ưu hóa đã chọn gói này khi bạn yêu cầu tất cả các hàng, nó sẽ phải đọc qua toàn bộ chỉ mục theo thứ tự giảm dần, nhận được mỗi hàng từ bảng bằng ROWID khi nó đi để kiểm tra với biến vị ngữ. Điều này sẽ dẫn đến rất nhiều I/O phụ và kiểm tra nhiều hàng sẽ không được trả lại. Vì vậy, trong trường hợp này, nó quyết định rằng việc sử dụng chỉ mục trên customerpostcode sẽ hiệu quả hơn.

Nếu bạn dần dần tăng số lượng hàng được trả về từ truy vấn trên cùng, có thể bạn sẽ tìm thấy điểm đến mà kế hoạch chuyển từ thứ nhất sang thứ hai. Chỉ cần từ chi phí của hai kế hoạch, tôi đoán điều này có thể là khoảng 1.200 hàng.

+0

thiên tài, ngưỡng là 1166, bạn biết điều này như thế nào? Tôi đã thêm thời gian và chi tiết của chỉ mục khác. Nếu bạn yêu cầu truy vấn sử dụng chỉ mục theo câu trả lời của @Kevin Burton, thời gian luôn luôn là khoảng 0.3s – Tsar

+0

xin lưu ý, số lượng rownum tối đa cho tôi sẽ luôn là 200. Điều này có nghĩa là XIF25CR_PROPOSALSEARCH sẽ không bao giờ được sử dụng? Có tùy chọn tối ưu hóa khác mà tôi có thể sử dụng ở đây không? – Tsar

+0

Tôi đoán rằng chi phí của kế hoạch đầu tiên sẽ mở rộng tuyến tính. 6 x 951 = 5,706, khá gần với chi phí của kế hoạch thứ hai là 5.512. Vì vậy, tôi ước tính rằng điểm tới hạn sẽ gần 6 x 200 = 1200. –

1

Bạn dường như không có chỉ mục hoàn toàn phù hợp. Chỉ số CR_PROPOSALSEARCH_I1 có thể được sử dụng để truy xuất các hàng theo thứ tự giảm dần của thuộc tính PROPOSALNUMBER. Nó có thể được chọn bởi vì Oracle có thể tránh để lấy tất cả các hàng phù hợp, sắp xếp chúng theo mệnh đề ORDER BY và sau đó loại bỏ tất cả các hàng ngoại trừ những hàng đầu tiên.

Nếu không có điều kiện ROWNUM, Oracle sử dụng chỉ mục XIF25CR_PROPOSALSEARCH (bạn không đưa ra bất kỳ chi tiết nào về nó) vì nó có thể khá chọn lọc liên quan đến mệnh đề WHERE. Nhưng nó sẽ yêu cầu để sắp xếp kết quả sau đó. Đây có lẽ là kế hoạch hiệu quả hơn dựa trên giả định rằng bạn sẽ truy xuất tất cả các hàng.

Vì một trong hai chỉ mục là một sự cân bằng (một cách tốt hơn để phân loại, một cách tốt hơn để áp dụng mệnh đề WHERE), các chi tiết như ROWNUM xác định kế hoạch thực hiện mà Oracle chọn.

4

Nếu bạn chắc chắn số liệu thống kê của bạn luôn được cập nhật và điều đó chỉ số này đủ có chọn lọc, bạn có thể nói oracle để sử dụng các chỉ số

SELECT * 
FROM (SELECT /*+ index(ps XIF25CR_PROPOSALSEARCH) */ * 
     FROM  cr_proposalsearch ps 
     WHERE UPPER (ps.customerpostcode) LIKE 'MK3%' 
     ORDER BY ps.proposalnumber DESC) 
WHERE ROWNUM <= 200 

(tôi chỉ muốn giới thiệu phương pháp này như một phương sách cuối cùng)

Nếu tôi được làm điều này tôi sẽ đầu tkprof truy vấn để xem thực sự bao nhiêu công việc nó đang làm,

ví dụ như: chi phí quét phạm vi chỉ số có thể là đường tắt

quên đề cập đến .... Bạn nên kiểm tra cardinality thực tế:

SELECT count(*) FROM cr_proposalsearch ps WHERE UPPER(ps.customerpostcode) like 'MK3%' 

và sau đó so sánh nó với cardinality trong kế hoạch truy vấn.

1

Tình trạng này:

WHERE UPPER(ps.customerpostcode) like 'MK3%' 

là không liên tục, có nghĩa là bạn không thể duy trì một loạt lệnh duy nhất cho nó.

Vì vậy, có hai cách để thực hiện truy vấn này:

  1. Sắp xếp theo số sau đó lọc trên mã.
  2. Lọc mã sau đó sắp xếp theo số.

Phương pháp 1 có thể sử dụng một chỉ mục trên số đó cung cấp cho bạn thời gian thực hiện tuyến tính (top 100 hàng sẽ được lựa chọn nhanh hơn so với đầu 2002 lần, với điều kiện số đó và mã không tương quan).

Phương pháp 2 có thể sử dụng quét phạm vi để lọc thô trên mã (điều kiện phạm vi sẽ giống như code >= 'MK3' AND code < 'MK4'), tuy nhiên, yêu cầu sắp xếp vì thứ tự của số không thể được giữ nguyên trong chỉ mục tổng hợp.

Thời gian sắp xếp tùy thuộc vào số lượng hàng trên cùng bạn chọn, nhưng phụ thuộc này, không giống như phương thức 1, không tuyến tính (bạn luôn cần quét ít nhất một lần).

Tuy nhiên, điều kiện lọc theo phương pháp 2 đủ chọn lọc cho RANGE SCAN với loại tiếp theo là hiệu quả hơn FULL SCAN cho toàn bộ bảng.

Điều này có nghĩa là có điểm tới hạn: cho điều kiện này: ROWNUM <= X có giá trị X để phương pháp 2 trở nên hiệu quả hơn khi giá trị này vượt quá.

Cập nhật:

Nếu bạn luôn được tìm kiếm trên ít nhất 3 ký tự đầu tiên, bạn có thể tạo một chỉ số như thế này:

SUBSTRING(UPPER(customerpostcode), 1, 3), proposalnumber 

và sử dụng nó trong truy vấn này:

SELECT * 
FROM (
     SELECT * 
     FROM cr_proposalsearch ps 
     WHERE SUBSTRING(UPPER(customerpostcode, 1, 3)) = SUBSTRING(UPPER(:searchquery), 1, 3) 
       AND UPPER(ps.customerpostcode) LIKE UPPER(:searchquery) || '%' 
     ORDER BY 
       proposalNumber DESC 
     ) 
WHERE rownum <= 200 

Bằng cách này, thứ tự số sẽ được bảo toàn riêng cho từng bộ mã chia sẻđầu tiênchữ cái sẽ cung cấp cho bạn một quét chỉ mục dày đặc hơn.

Các vấn đề liên quan