SQL WHERE ID IN (id1, id2, Hoài, idn)


170

Tôi cần phải viết một truy vấn để lấy một danh sách lớn các id.

Chúng tôi hỗ trợ nhiều phụ trợ (MySQL, Firebird, SQLServer, Oracle, PostgreQuery ...) vì vậy tôi cần viết một SQL chuẩn.

Kích thước của bộ id có thể lớn, truy vấn sẽ được tạo theo chương trình. Vì vậy, cách tiếp cận tốt nhất là gì?

1) Viết truy vấn bằng IN

SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)

Câu hỏi của tôi ở đây là. Điều gì xảy ra nếu n rất lớn? Ngoài ra, những gì về hiệu suất?

2) Viết truy vấn bằng OR

SELECT * FROM TABLE WHERE ID = id1 OR ID = id2 OR ... OR ID = idn

Tôi nghĩ rằng cách tiếp cận này không có giới hạn n, nhưng hiệu suất nếu n rất lớn thì sao?

3) Viết một giải pháp lập trình:

  foreach (var id in myIdList)
  {
      var item = GetItemByQuery("SELECT * FROM TABLE WHERE ID = " + id);
      myObjectList.Add(item);
  }

Chúng tôi gặp một số vấn đề với cách tiếp cận này khi máy chủ cơ sở dữ liệu được truy vấn qua mạng. Thông thường tốt hơn là thực hiện một truy vấn truy xuất tất cả các kết quả so với thực hiện nhiều truy vấn nhỏ. Có lẽ tôi sai.

Điều gì sẽ là một giải pháp chính xác cho vấn đề này?


1
Tùy chọn 1 giảm đáng kể thời gian phản hồi của máy chủ SQL, chọn 7k ID, trong đó một số không tồn tại. Thông thường truy vấn mất khoảng 1300ms, nó giảm xuống còn 80ms khi sử dụng IN! Tôi đã thực hiện như một giải pháp của bạn 1 + 3. Chỉ cần truy vấn cuối cùng là một, chuỗi truy vấn dài được gửi tới SQL để thực thi.
Piotr Kula

Câu trả lời:


108

Lựa chọn 1 là giải pháp tốt duy nhất.

Tại sao?

  • Tùy chọn 2 thực hiện tương tự nhưng bạn lặp lại tên cột rất nhiều lần; ngoài ra, công cụ SQL không biết ngay rằng bạn muốn kiểm tra xem giá trị có phải là một trong các giá trị trong danh sách cố định hay không. Tuy nhiên, một công cụ SQL tốt có thể tối ưu hóa nó để có hiệu suất tương đương như với IN. Vẫn còn vấn đề dễ đọc mặc dù ...

  • Lựa chọn 3 đơn giản là hiệu suất khủng khiếp. Nó sẽ gửi một truy vấn mỗi vòng lặp và làm hỏng cơ sở dữ liệu với các truy vấn nhỏ. Nó cũng ngăn nó sử dụng bất kỳ tối ưu hóa nào cho "giá trị là một trong những thứ trong danh sách nhất định"


2
Tôi đồng ý nhưng lưu ý rằng danh sách trong bị giới hạn trong nhiều RDMS và vì vậy bạn sẽ cần chúng tôi sử dụng giải pháp của @Ed Guiness nhưng ở đây các bảng tạm thời có sự khác biệt giữa RDBMS. (Hiệu quả cho các vấn đề phức tạp, bạn không thể chỉ sử dụng SQL tiêu chuẩn thuần túy)
mmmmmm

28

Một cách tiếp cận khác có thể là sử dụng một bảng khác để chứa các giá trị id. Bảng khác này sau đó có thể được nối bên trong trên BẢNG của bạn để hạn chế các hàng trả về. Điều này sẽ có lợi thế lớn là bạn sẽ không cần SQL động (có vấn đề vào thời điểm tốt nhất) và bạn sẽ không có mệnh đề IN dài vô hạn.

Bạn sẽ cắt bớt bảng khác này, chèn số lượng lớn các hàng của bạn, sau đó có thể tạo một chỉ mục để hỗ trợ hiệu suất tham gia. Nó cũng sẽ cho phép bạn tách sự tích lũy của các hàng này khỏi việc truy xuất dữ liệu, có thể cung cấp cho bạn nhiều tùy chọn hơn để điều chỉnh hiệu suất.

Cập nhật : Mặc dù bạn có thể sử dụng bảng tạm thời, tôi không có ý ám chỉ rằng bạn phải hoặc thậm chí nên. Một bảng vĩnh viễn được sử dụng cho dữ liệu tạm thời là một giải pháp phổ biến có giá trị vượt ra ngoài mô tả ở đây.


1
Nhưng làm thế nào bạn sẽ vượt qua danh sách id mà bạn cần? (Thấy bạn không thể chọn một phạm vi hoặc một cái gì đó tương tự).
raam86

1
@ raam86: danh sách ID có thể đã được lấy bằng cách sử dụng selectcâu lệnh trên bảng khác. Danh sách được thông qua như bảng khác mà bạn đang inner joinchống lại.
bdforbes

19

Những gì Ed Guiness đề xuất thực sự là một sự tăng cường hiệu suất, tôi đã có một truy vấn như thế này

select * from table where id in (id1,id2.........long list)

tôi đã làm gì :

DECLARE @temp table(
            ID  int
            )
insert into @temp 
select * from dbo.fnSplitter('#idlist#')

Sau đó, bên trong tham gia temp với bảng chính:

select * from table inner join temp on temp.id = table.id

Và hiệu suất được cải thiện mạnh mẽ.


1
Xin chào, fnSplitter có phải là một chức năng từ MSSQL không? Bởi vì tôi không thể tìm thấy nó.
WiiMaxx

Đó không phải là một điều tiêu chuẩn. Họ phải có nghĩa là họ đã viết chức năng đó cho mục đích này, hoặc ví dụ có một ứng dụng đã cung cấp nó.
gạch dưới

fnSplitter là một chức năng được tạo bởi Ritu, bạn có thể tìm thấy trên internet / google tương tự của nó
Bashar Abu Shamaa

9

Tùy chọn đầu tiên chắc chắn là lựa chọn tốt nhất.

SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)

Tuy nhiên, xem xét rằng danh sách id rất lớn , giả sử hàng triệu người, bạn nên xem xét kích thước chunk như dưới đây:

  • Chia cho bạn danh sách Id thành các khối có số cố định, giả sử 100
  • Kích thước khối nên được quyết định dựa trên kích thước bộ nhớ của máy chủ của bạn
  • Giả sử bạn có 10000 Id, bạn sẽ có 10000/100 = 100 khối
  • Xử lý một khối tại một thời điểm dẫn đến 100 cuộc gọi cơ sở dữ liệu để chọn

Tại sao bạn nên chia thành khối?

Bạn sẽ không bao giờ có ngoại lệ tràn bộ nhớ, điều rất phổ biến trong các tình huống như của bạn. Bạn sẽ có tối ưu hóa số lượng cuộc gọi cơ sở dữ liệu dẫn đến hiệu suất tốt hơn.

Nó luôn luôn làm việc như quyến rũ đối với tôi. Hy vọng nó cũng sẽ làm việc cho các nhà phát triển đồng nghiệp của tôi :)


4

Thực hiện lệnh CHỌN * TỪ MyTable trong đó id in () trên bảng Azure SQL với 500 triệu bản ghi dẫn đến thời gian chờ> 7 phút!

Làm điều này thay vì trả về kết quả ngay lập tức:

select b.id, a.* from MyTable a
join (values (250000), (2500001), (2600000)) as b(id)
ON a.id = b.id

Sử dụng một tham gia.


3

Trong hầu hết các hệ thống cơ sở dữ liệu, IN (val1, val2, …)và một loạt các ORđược tối ưu hóa cho cùng một kế hoạch.

Cách thứ ba sẽ là nhập danh sách các giá trị vào một bảng tạm thời và tham gia vào bảng đó hiệu quả hơn trong hầu hết các hệ thống, nếu có nhiều giá trị.

Bạn có thể muốn đọc bài viết này:


3

Mẫu 3 sẽ là người thực hiện tồi nhất trong số tất cả vì bạn đang truy cập cơ sở dữ liệu vô số lần mà không có lý do rõ ràng.

Tải dữ liệu vào một bảng tạm thời và sau đó tham gia vào đó sẽ nhanh nhất. Sau đó, IN sẽ hoạt động nhanh hơn một chút so với nhóm OR.


2

Tôi nghĩ bạn có nghĩa là SqlServer nhưng trên Oracle bạn có giới hạn cứng bao nhiêu phần tử IN bạn có thể chỉ định: 1000.


1
Ngay cả SQL Server cũng ngừng hoạt động sau ~ 40k phần tử IN. Theo MSDN: Bao gồm một số lượng giá trị cực lớn (nhiều nghìn) trong mệnh đề IN có thể tiêu tốn tài nguyên và trả về lỗi 8623 hoặc 8632. Để khắc phục sự cố này, hãy lưu trữ các mục trong danh sách IN trong bảng.
jahav
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.