Cách nhanh nhất để xác định nếu bản ghi tồn tại


143

Như tiêu đề cho thấy ... Tôi đang cố gắng tìm ra cách nhanh nhất với ít chi phí nhất để xác định xem một bản ghi có tồn tại trong một bảng hay không.

Truy vấn mẫu:

SELECT COUNT(*) FROM products WHERE products.id = ?;

    vs

SELECT COUNT(products.id) FROM products WHERE products.id = ?;

    vs

SELECT products.id FROM products WHERE products.id = ?;

Giả sử ?được hoán đổi với 'TB100'... cả truy vấn thứ nhất và thứ hai sẽ trả về kết quả chính xác như nhau (giả sử ... 1cho cuộc trò chuyện này). Truy vấn cuối cùng sẽ trả về 'TB100'như mong đợi hoặc không có gì nếu idkhông có trong bảng.

Mục đích là để tìm ra nếu idtrong bảng hay không. Nếu không, chương trình tiếp theo sẽ chèn bản ghi, nếu có, chương trình sẽ bỏ qua nó hoặc thực hiện truy vấn CẬP NHẬT dựa trên logic chương trình khác ngoài phạm vi của câu hỏi này.

Cái nào nhanh hơn và có ít chi phí hơn? (Điều này sẽ được lặp lại hàng chục ngàn lần mỗi lần chạy chương trình và sẽ được chạy nhiều lần trong ngày).

(Chạy truy vấn này với M $ SQL Server từ Java thông qua trình điều khiển JDBC được cung cấp M $)


1
Điều này có thể phụ thuộc vào cơ sở dữ liệu. Ví dụ, đếm trên Postgres khá chậm.
Mike Christensen

Xin lỗi, đây là Java nói chuyện với M $ SQL thông qua trình điều khiển jdbc. Tôi sẽ cập nhật OP của tôi.
SnakeDoc

2

@Nikola Markovinović: bạn sẽ sử dụng nó như thế nào trong trường hợp này?
zerkms

5
@zerkms Phụ thuộc vào bối cảnh. Nếu trong thủ tục lưu trữ, nó sẽ được if exists(select null from products where id = @id); nếu trong một truy vấn được gọi trực tiếp bởi một khách hàng select case when exists (...) then 1 else 0 end.
Nikola Markovinović

Câu trả lời:


170

SELECT TOP 1 products.id FROM products WHERE products.id = ?; sẽ tốt hơn tất cả các đề xuất của bạn vì nó sẽ chấm dứt thực hiện sau khi tìm thấy bản ghi đầu tiên.


5
Không tối ưu hóa có đưa nó vào tài khoản khi tìm kiếm thông qua PK (hoặc bất kỳ khóa duy nhất nào khác) không?
zerkms

3
Ông nói rằng đó là PK, nhưng nếu vậy thì có, trình tối ưu hóa sẽ tính đến điều đó.
Declan_K

3
@Declan_K: có vẻ như quả cầu ma thuật của tôi đã thất bại trong trường hợp này và một cột có tên idlà PK. Vì vậy, +1 cho lời khuyên của bạn.
zerkms

4
Nếu đó không phải là PK, tôi cũng đề nghị đảm bảo có một chỉ mục trên cột đó. Nếu không, truy vấn sẽ phải thực hiện quét bảng thay vì tìm kiếm bảng nhanh hơn.
CD Jorgensen

3
Tôi nghĩ rằng chúng ta nên xem xét câu trả lời @ nenad-zivkovic cho câu hỏi này.
Giulio Caccin 7/07/2015

192

EXISTS(hoặc NOT EXISTS) được thiết kế đặc biệt để kiểm tra nếu có thứ gì đó tồn tại và do đó nên (và là) tùy chọn tốt nhất. Nó sẽ dừng ở hàng đầu tiên khớp với nó vì vậy nó không yêu cầu TOPmệnh đề và nó không thực sự chọn bất kỳ dữ liệu nào nên không có chi phí kích thước của các cột. Bạn có thể sử dụng một cách an toàn SELECT *ở đây - không khác gì SELECT 1, SELECT NULLhoặc SELECT AnyColumn... (thậm chí bạn có thể sử dụng một biểu thức không hợp lệ như thế SELECT 1/0và nó sẽ không bị hỏng ) .

IF EXISTS (SELECT * FROM Products WHERE id = ?)
BEGIN
--do what you need if exists
END
ELSE
BEGIN
--do what needs to be done if not
END

Điều này trước tiên không phải thực thi câu lệnh CHỌN, sau đó thực thi câu lệnh IF EXISTS ... gây thêm chi phí và do đó có nhiều thời gian xử lý hơn?
SnakeDoc

7
@SnakeDoc số Existshoạt động theo selectcách thức mà nó thoát ra ngay sau khi một hàng được tìm thấy. Hơn nữa tồn tại chỉ đơn thuần là lưu ý sự tồn tại của bản ghi, chứ không phải giá trị thực trong bản ghi, tiết kiệm nhu cầu tải hàng từ đĩa (tất nhiên giả sử tiêu chí tìm kiếm được lập chỉ mục). Đối với chi phí chung if- bạn sẽ phải dành thời gian rất nhỏ này.
Nikola Markovinović

1
@ NikolaMarkovinović điểm thú vị. Tôi không chắc liệu Index có tồn tại trên trường này không và SQL mới của tôi không biết cách tìm hiểu. Tôi đang làm việc với DB này từ Java thông qua JDBC và cơ sở dữ liệu được đặt ở xa trong một colo ở đâu đó. Tôi chỉ được cung cấp một "bản tóm tắt cơ sở dữ liệu" chỉ chi tiết các trường tồn tại trong mỗi bảng, loại của chúng và bất kỳ FK hoặc PK nào. Điều này có thay đổi gì không?
SnakeDoc

3
@SnakeDoc Để tìm hiểu về cấu trúc bảng, bao gồm các khóa và chỉ mục nước ngoài, hãy chạy sp_help table_name . Các chỉ mục là rất cần thiết khi cần lấy một vài hàng trong số nhiều, sử dụng select tophoặc exists; nếu họ không có mặt thì động cơ sql sẽ phải thực hiện quét bảng. Đây là tùy chọn tìm kiếm bảng ít mong muốn nhất. Nếu bạn không được phép tạo chỉ mục, bạn sẽ phải liên lạc với nhân viên kỹ thuật ở phía bên kia để tìm hiểu xem họ có tự điều chỉnh chúng hay họ mong bạn đề xuất chỉ mục.
Nikola Markovinović

1
@Konstantin Bạn có thể làm một cái gì đó nhưSELECT CASE WHEN EXISTS(..) THEN 1 ELSE 0 END;
Nenad Zivkovic

21

Không gì có thể đánh bại -

SELECT TOP 1 1 FROM products WHERE id = 'some value';

Bạn không cần phải đếm để biết nếu có dữ liệu trong bảng. Và không sử dụng bí danh khi không cần thiết.


5
Mặc dù tên của nó idkhông phải là khóa chính. Vì vậy, mặc dù bạn không tính nhưng bạn vẫn cần tìm tất cả các hồ sơ phù hợp, có thể là hàng ngàn trong số chúng. Về răng cưa - mã là công việc liên tục trong tiến trình. Bạn không bao giờ biết khi nào bạn sẽ phải quay lại. Aliasing giúp ngăn ngừa lỗi thời gian chạy ngu ngốc; ví dụ, tên cột duy nhất không cần bí danh không còn là duy nhất nữa bởi vì ai đó đã tạo một cột cùng tên trong một bảng khác, được nối.
Nikola Markovinović

Vâng, chắc chắn bạn đúng rồi. Bí danh giúp ích rất nhiều nhưng tôi không nghĩ nó có sự khác biệt nào khi không sử dụng phép nối. Vì vậy, tôi đã nói không sử dụng nó nếu không cần thiết. :) Và bạn có thể tìm thấy một cuộc thảo luận dài ở đây về việc kiểm tra sự tồn tại. :)
AgentQuery

3
Tôi không biết tại sao tôi lại hiểu thuật ngữ này aliasing. Thuật ngữ đúng là qualifying. Dưới đây là lời giải thích dài hơn của Alex Kuznetzov . Về các truy vấn bảng đơn - bây giờ là bảng đơn . Nhưng sau đó, khi phát hiện ra lỗi và bạn đang cố gắng giữ lũ, khách hàng rất lo lắng, bạn tham gia một bảng khác chỉ để đối mặt với thông báo lỗi - thông báo có thể sửa được dễ dàng, nhưng không phải lúc này, một cú đánh nhỏ - và bạn sửa lỗi nhớ không bao giờ để lại một cột ...
Nikola Markovinović

1
Không thể bỏ qua điều đó bây giờ. Cảm ơn!! :)
AgentQuery

15
SELECT CASE WHEN EXISTS (SELECT TOP 1 *
                         FROM dbo.[YourTable] 
                         WHERE [YourColumn] = [YourValue]) 
            THEN CAST (1 AS BIT) 
            ELSE CAST (0 AS BIT) END

Cách tiếp cận này trả về một boolean cho bạn.


1
Có thể bỏ qua câu lệnh Top và câu lệnh * để làm cho nó nhanh hơn một chút, vì Exist sẽ thoát khi nó tìm thấy một bản ghi, vì vậy một cái gì đó như thế này: CHỌN CASE KHI EXISTS (CHỌN 1 TỪ dbo. [YourTable] WHERE [YourColumn] = [YourValue]) THÌ CAST (1 AS BIT) ELSE CAST (0 AS BIT) END
Stefan Zvonar

Đề xuất này không đề cập đến lý do tại sao điều này sẽ nhanh hơn các câu lệnh tồn tại / không tồn tại trong SQL Server tích hợp. Nếu không có bất kỳ điểm chuẩn nào, tôi sẽ khó có thể tin rằng một tuyên bố trường hợp sẽ mang lại kết quả nhanh hơn so với phản hồi đúng / sai ngay lập tức.
Bonez024

8

Bạn cũng có thể dùng

 If EXISTS (SELECT 1 FROM dbo.T1 WHERE T1.Name='Scot')
    BEGIN
         --<Do something>
    END 

ELSE    
     BEGIN
       --<Do something>
     END

7

Đừng nghĩ ai đã đề cập đến nó, nhưng nếu bạn chắc chắn dữ liệu sẽ không thay đổi bên dưới bạn, bạn cũng có thể muốn áp dụng gợi ý NoLock để đảm bảo dữ liệu không bị chặn khi đọc.

SELECT CASE WHEN EXISTS (SELECT 1 
                     FROM dbo.[YourTable] WITH (NOLOCK)
                     WHERE [YourColumn] = [YourValue]) 
        THEN CAST (1 AS BIT) 
        ELSE CAST (0 AS BIT) END

3
SELECT COUNT(*) FROM products WHERE products.id = ?;

Đây là giải pháp cơ sở dữ liệu quan hệ chéo hoạt động trong tất cả các cơ sở dữ liệu.


6
Tuy nhiên bạn buộc các db để lặp qua tất cả hồ sơ, rất chậm trên bảng lớn
amd

@amd quan tâm giải thích vì sao?
UmNyobe

@amd bình luận của bạn làm cho ý nghĩa tổng thể. Truy vấn này nhiều hơn TÌM TẤT CẢ hơn TẤT CẢ.
UmNyobe

1

Dưới đây là cách đơn giản nhất và nhanh nhất để xác định xem một bản ghi có tồn tại trong cơ sở dữ liệu hay không Điều tốt là nó hoạt động trong tất cả các DB quan hệ

SELECT distinct 1 products.id FROM products WHERE products.id = ?;

0
create or replace procedure ex(j in number) as
i number;
begin
select id into i from student where id=j;
if i is not null then
dbms_output.put_line('exists');
end if;
exception
   when no_data_found then
        dbms_output.put_line(i||' does not exists');

end;

2
Có thể mã của bạn hoạt động rất tốt, nhưng sẽ tốt hơn nếu bạn thêm một số thông tin bổ sung để dễ hiểu hơn.
idmean

0

Tôi đã sử dụng điều này trong quá khứ và nó không yêu cầu quét toàn bộ bảng để xem có gì tồn tại không. Nó siêu nhanh ...

UPDATE TableName SET column=value WHERE column=value
IF @@ROWCOUNT=0
BEGIN
     --Do work
END             

0

Đối với những người vấp phải điều này từ nền tảng của MySQL hoặc Oracle - MySQL hỗ trợ mệnh đề LIMIT để chọn số lượng bản ghi giới hạn, trong khi Oracle sử dụng ROWNUM.

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.