2010-03-08 32 views
11

Tôi có một câu lệnh SQL mà tôi muốn lấy dữ liệu 1200 ep_codes bằng cách sử dụng mệnh đề IN. Khi tôi bao gồm hơn 1000 ep_codes bên trong mệnh đề IN, Oracle cho biết tôi không được phép làm điều đó. Để khắc phục điều này, tôi đã cố gắng để thay đổi mã SQL như sau:Oracle SQL: Cách sử dụng hơn 1000 mục bên trong mệnh đề IN

SELECT period, ... 
FROM my_view 
WHERE period = '200912' 
     ... 
     AND ep_codes IN (...1000 ep_codes...) 
     OR ep_codes IN (...200 ep_codes...) 

Mã này đã được thực hiện thành công nhưng kết quả là lạ (kết quả tính toán được tìm nạp cho tất cả các giai đoạn, không chỉ cho 200.912, mà không phải là những gì tôi muốn). Có thích hợp để thực hiện điều đó bằng cách sử dụng OR giữa các điều khoản IN hoặc tôi nên thực thi hai mã riêng biệt là một với 1000 và mã còn lại có 200 tập mã không?


Giải pháp của Pascal Martin hoạt động hoàn hảo. Cảm ơn tất cả những người đóng góp với các đề xuất có giá trị.

Trả lời

16

Không chắc chắn rằng việc sử dụng quá nhiều giá trị trong một IN() là tốt, thực sự - đặc biệt là cho buổi biểu diễn.

Khi bạn nói "kết quả là lạ", có thể điều này là do sự cố với dấu ngoặc đơn? Điều gì nếu bạn cố gắng này, thay vì những gì bạn đề xuất:

SELECT ... 
FROM ... 
WHERE ... 
     AND (
      ep_codes IN (...1000 ep_codes...) 
      OR ep_codes IN (...200 ep_codes...) 
    ) 

Liệu nó làm cho kết quả ít lạ?

+0

Tôi đã cập nhật câu hỏi để làm cho mọi việc rõ ràng về kết quả kỳ lạ. Bây giờ tôi đang thử mã như bạn đã đề xuất (có thêm dấu ngoặc đơn). Ngay sau khi kết quả được tìm nạp, tôi sẽ cho bạn biết. –

+0

Cảm ơn, nó hoạt động OK ngay bây giờ. Một lần nữa tôi thấy rằng các dấu ngoặc phụ thêm là rất quan trọng. –

+0

Bạn được chào đón :-) - có, dấu ngoặc đơn có thể hữu ích ;-) –

25

Cách được khuyến nghị để xử lý việc này trong Oracle là tạo Bảng tạm thời, ghi các giá trị vào đây và sau đó tham gia vào điều này. Sử dụng mệnh đề IN được tạo động có nghĩa là trình tối ưu hóa truy vấn thực hiện 'phân tích cú pháp cứng' của mọi truy vấn.

create global temporary table LOOKUP 
(
    ID NUMBER 
) on commit delete rows; 

-- Do a batch insert from your application to populate this table 
insert into lookup(id) values (?) 

-- join to it 
select foo from bar where code in (select id from lookup) 
+2

Gần đây, chúng tôi đã thay đổi một số mã bằng cách sử dụng "trong" để sử dụng bảng tra cứu tạm thời. Đối với một số truy vấn, hiệu suất tăng đáng kể từ việc mất vài phút đến trong vòng một giây. – Rene

+1

Nhưng đây có phải là việc triển khai hợp lệ cho nhiều yêu cầu song song với máy chủ không? Theo như tôi hiểu có thể có một số giao dịch mở trên máy chủ Oracle, cố gắng tạo bảng 'LOOKUP' –

+0

@Ilya: Xem http://stackoverflow.com/questions/8240810/when-will-data-in-oracle-session -temporary-table-get-deleted.Với "trên các hàng xóa cam kết", dữ liệu chỉ nên hiển thị trong phạm vi giao dịch. – shelley

1

Có vẻ như ý tưởng tốt hơn, cả về hiệu suất và tính bảo trì, để đặt mã trong một bảng riêng biệt.

SELECT ... 
FROM ... 
WHERE ... 
    AND ep_code in (select code from ep_code_table) 
0

bạn có thể chèn 1200 ep_code giá trị vào một bảng tạm thời và sau đó INNER JOIN vào bảng đó để lọc các hàng để thay thế?

SELECT a.* 
FROM mytable a 
INNER JOIN tmp ON (tmp.ep_code = a.ep_code) 
WHERE ... 
+0

Tôi không chắc chắn về điều đó. Tôi đang sử dụng một số quan điểm mà CNTT đã cung cấp cho tôi. –

6

Thực ra bạn có thể sử dụng bộ sưu tập/nhiều bộ nhớ tại đây. Bạn sẽ cần một loại bảng số để lưu trữ chúng.

CREATE TYPE NUMBER_TABLE AS TABLE OF NUMBER; 
... 
SELECT * 
FROM my_view 
WHERE period MEMBER OF NUMBER_TABLE(1,2,3...10000) 

Đọc thêm về multisets here:

+0

Điều này là rất thanh lịch cú pháp nhưng hiệu suất là đáng thất vọng. Sử dụng cú pháp này để chọn một giá trị khóa chính, CBO chọn một INDEX FAST FULL SCAN. –

+0

Thử nghiệm với một vài nghìn id và một bảng có kích thước vừa phải (40K hàng), nó vẫn còn khoảng 3x nhanh hơn tham gia vào một bảng tạm thời (do tham gia băm). Điều đó không tính thời gian để tạo và điền vào bảng tạm thời. –

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