2011-07-05 42 views
7

Tôi đang thực hiện truy vấn 'chọn' đơn giản trong vòng lặp Java như được hiển thị bên dưới. Kích thước của Danh sách có thể tăng lên đến 10000+. Làm cách nào để cải thiện tốc độ truy vấn? Bất kỳ ví dụ hoặc lời khuyên nào được đánh giá cao. Cảm ơn.Tối ưu hóa tốc độ thực hiện truy vấn "chọn" trong vòng lặp lớn

Lưu ý rằng tôi cần truy xuất tất cả dữ liệu trong mỗi cột của bảng đó, vì vậy đó là lý do tại sao dấu hoa thị (*) được sử dụng.

List<String> valueList = .... 
Connection conn = null; 
PreparedStatement ps = null; 
ResultSet rs = null; 

try { 
    DriverManager.registerDriver(new oracle.jdbc.OracleDriver()); 
    conn = DriverManager.getConnection(dbURL, dbUsername, dbPassword); 
    for (int m = 0; m < valueList.size() ; m++) {  
     String sql = "SELECT * FROM WORKSHEET WHERE " + sheetId + " = '" +  
         valueList.get(m) + "'"; 
     ps = conn.prepareStatement(sql); 
     rs = ps.executeQuery(); 
     // retreive data....   
    } 
} 

Chỉnh sửa: Cuối cùng, có một số cách để tăng tốc truy vấn này. Tôi đang sử dụng cách thứ hai vì nó ngăn chặn lỗi ORA-04031 trong tương lai.

  1. Sử dụng truy vấn 'SELECT' được tham số hóa với mệnh đề 'IN'.
  2. Tạo bảng lồng nhau và đúc mảng/danh sách các mục đến từ JDBC đến bảng được tạo lồng nhau.
  3. Tạo bảng tạm thời và chèn danh sách các mục. Sau đó thực hiện JOIN với bảng chính (1 truy vấn) và nhận kết quả.
+9

[SQL Injection Hooooorrrrraaay!] (Http://bobby-tables.com/) * Sidenote: * Sử dụng truy vấn được tham số hóa cũng sẽ cung cấp cho bạn hiệu suất tốt hơn. – Bobby

+2

Bạn đã đưa ra lý do cho * - bất kỳ lý do nào bạn không sử dụng truy vấn được tham số hóa? –

+0

Giảm tổng số truy vấn được thực hiện bằng cách truy xuất lại nhiều cặp sheetID/m trong mỗi truy vấn ('WHERE pair1 hoặc pair2 hoặc pair3 etc ...') sẽ giảm một số chi phí. –

Trả lời

6

Có hai điều cần xem xét khi cố gắng tăng tốc độ này lên:

  1. Execute truy vấn này chỉ một lần cho tất cả của sheetid
  2. Hãy chắc chắn rằng bạn đang thực hiện cùng một truy vấn mỗi lần, bằng cách không thể xác định rõ giá trị in. Vì các giá trị này có thể thay đổi, mỗi truy vấn sẽ trông giống như truy vấn trước đó, nhưng chỉ với một vài giá trị khác nhau. Điều này sẽ không cho phép Oracle sử dụng lại truy vấn trước đó và dẫn đến SQL không thể chia sẻ trong nhóm dùng chung. Điều này sẽ điền vào các hồ bơi được chia sẻ. Làm điều này đủ lâu và bạn sẽ nhận được thông báo lỗi ORA-04031.

Cách để thực hiện là sử dụng các loại SQL. Đây là một ví dụ trong PL/SQL. Bạn có thể sử dụng cùng một nguyên tắc trong Java.

Đầu tiên tạo ra một bảng với vạn sheetId của:

SQL> create table worksheet (sheetid) 
    2 as 
    3 select level 
    4  from dual 
    5 connect by level <= 10000 
    6/

Table created. 

Tạo một kiểu SQL:

SQL> create type mynumbers is table of number; 
    2/

Type created. 

Trong code của bạn, điền một thể hiện của kiểu SQL với các giá trị trong "valuelist của bạn "và sử dụng toán tử TABLE để chuyển loại thành giá trị bảng:

SQL> declare 
    2 valuelist mynumbers := mynumbers(23,124,987,6123,8923,1,7139); 
    3 begin 
    4 for r in 
    5 (select ws.sheetid 
    6  from worksheet ws 
    7   , table(valuelist) vl 
    8  where ws.sheetid = vl.column_value 
    9 ) 
10 loop 
11  dbms_output.put_line(r.sheetid); 
12 end loop; 
13 end; 
14/
1 
23 
124 
987 
6123 
7139 
8923 

PL/SQL procedure successfully completed. 

Bây giờ bạn chỉ có một SQL trong hồ bơi được chia sẻ của bạn và chỉ một thực thi truy vấn này, thay vì hàng nghìn.

4

Thời gian chủ yếu dành để chuẩn bị và thực hiện truy vấn.

Nếu thay vào đó bạn chạy một truy vấn trả về tất cả kết quả của bạn, điều đó sẽ làm cho mọi thứ nhanh hơn rất nhiều.

ví dụ:

String where = "(1=0) " 
// first build the where string 
for (int m = 0; m < valueList.size() ; m++) { 
    where = where + " OR (" + sheetId + " = '" + valueList.get(m) + "'"; 
    } 
// then run a single query 
sql = "SELECT * FROM WORKSHEET WHERE " + where; 
ps = conn.prepareStatement(sql); 
rs = ps.executeQuery(); 
// retrieve and handle data. .... 
+1

Hmmm ... Bạn có nghĩ thế không? Ông nói "Kích thước của Danh sách có thể tăng lên đến 10000+" –

+1

Tôi vẫn sẽ đi theo cách này! Chi phí cho việc chạy mỗi truy vấn là những gì đang làm chậm anh ta. Tôi đoán là sẽ cho một câu lệnh IN cũng sẽ hoạt động: xây dựng SQL để nói WHERE sheetID IN (1,2,3,4 ...) Điều đó thực sự đẹp hơn để đọc và ngắn hơn rất nhiều. – Eljakim

+0

Tôi nghĩ rằng bằng cách sử dụng OR, bạn tránh giới hạn 1000 mục cho một mệnh đề IN trong Oracle, điều này là tốt. Nhưng tôi chắc chắn sẽ sử dụng một StringBuilder cho tất cả các String nối. – sudocode

3

Bạn phải chuẩn bị truy vấn sql với TRÊN Statment và sau đó thực hiện truy vấn chỉ một lần ...

+0

IN tuyên bố không thể làm việc vì giới hạn 1000. –

+0

Dù sao tôi đang cố giải quyết nó bằng cách tách chúng bằng 'OR' và 'UNION' –

0

Tôi không biết nếu nó sẽ là một sự cải tiến, nhưng bạn có thể thử chọn tất cả ghi và kiểm tra bằng mã Java của bạn nếu các kết quả phù hợp sheetId. Đây là một cái gì đó bạn nên thời gian để biết những gì là tốt hơn.

1

Oracle có thể nhận tối đa 1000 tham số trong mệnh đề IN. Vì vậy, nếu bạn sử dụng câu lệnh chuẩn bị, bạn sẽ giảm số lần lặp 1000 lần.Chỉ cần chia danh sách thành từng phần của 1000 phần tử.

2

Dưới đây là một vài ý tưởng khác.

  1. Tạo bảng tạm thời và chèn các mục danh sách (10k) của bạn. Sau đó, thực hiện một tham gia vào bảng chính của bạn (1 truy vấn) và nhận được kết quả của bạn.

  2. Tạo một thủ tục lưu sẵn để lấy danh sách các mục (qua bảng lồng nhau) làm đầu vào và trả về kết quả được đặt qua tham số ngoài.

Tôi muốn chọn tùy chọn 1. vì nó đơn giản hơn đối với tôi và có thể nhanh hơn. Nhưng bạn cần phải cẩn thận về các phiên đồng thời, v.v. Không chắc chắn cách bạn muốn xử lý nhiều phiên (liệu chúng có chia sẻ dữ liệu này không, chúng có danh sách dữ liệu riêng biệt không?).

Cần xem xét điều gì đó.

+1

Với bảng tạm thời toàn cầu, Oracle xử lý đồng thời cho bạn (mỗi phiên có bảng trống riêng của nó để bắt đầu). –

0

Đó là một điều nhỏ, nhưng nếu bạn định xây dựng truy vấn động (không sử dụng các biến liên kết), bạn nên sử dụng createStatement, chứ không phải là standardStatement. Có một số lượng nhỏ của overhead với preparStatement mà bạn không cần.

1

Bạn có thể thử điều gì đó với mệnh đề: sheetId IN ('1', '2', '3', '4') Và hãy đảm bảo rằng cột sheetId có khóa chỉ mục.

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