SQL: Cách kiểm tra đúng nếu bản ghi tồn tại


206

Trong khi đọc một số tài liệu liên quan đến Điều chỉnh SQL, tôi thấy điều này:

SELECT COUNT(*) :

  • Đếm số lượng hàng.
  • Thường được sử dụng không đúng cách để xác minh sự tồn tại của một bản ghi.

SELECT COUNT(*)thật là xấu không?

Cách thích hợp để xác minh sự tồn tại của một bản ghi là gì?

Câu trả lời:


251

Tốt hơn là sử dụng một trong các cách sau:

-- Method 1.
SELECT 1
FROM table_name
WHERE unique_key = value;

-- Method 2.
SELECT COUNT(1)
FROM table_name
WHERE unique_key = value;

Sự thay thế đầu tiên sẽ cho bạn không có kết quả hoặc một kết quả, số thứ hai nên bằng 0 hoặc một.

Tài liệu bạn đang sử dụng bao nhiêu tuổi? Mặc dù bạn đã đọc lời khuyên tốt, nhưng hầu hết các trình tối ưu hóa truy vấn trong tối ưu hóa RDBMS gần đây SELECT COUNT(*), vì vậy trong khi có sự khác biệt về lý thuyết (và cơ sở dữ liệu cũ hơn), bạn không nên nhận thấy bất kỳ sự khác biệt nào trong thực tế.


1
Tôi sẽ làm rõ rằng tôi dự định "khóa duy nhất" với mệnh đề "key = value" nhưng ngoài ra tôi vẫn đứng sau câu trả lời của mình.
Martin Schapendonk

1
ĐỒNG Ý. Với tiền đề đó thực sự truy vấn sẽ trả về chỉ một hoặc không bản ghi. NHƯNG: Câu hỏi không giới hạn trong một cột duy nhất. Ngoài ra: Số truy vấn thứ 2 (1) tương đương với đếm (*) từ POV thực tế.
Martin Ba

1
Câu hỏi cho biết "cách thích hợp để xác minh sự tồn tại của bản ghi A". Tôi giải thích rằng là số ít, như trong: 1 bản ghi. Sự khác biệt giữa đếm (*) và đếm (1) đã được bao phủ bởi câu trả lời của tôi. Tôi thích đếm (1) vì nó không dựa vào triển khai RDBMS cụ thể.
Martin Schapendonk

192

Tôi không muốn sử dụng chức năng Count cả:

IF [NOT] EXISTS ( SELECT 1 FROM MyTable WHERE ... )
     <do smth>

Ví dụ: nếu bạn muốn kiểm tra xem người dùng có tồn tại hay không trước khi chèn nó vào cơ sở dữ liệu, truy vấn có thể trông như thế này:

IF NOT EXISTS ( SELECT 1 FROM Users WHERE FirstName = 'John' AND LastName = 'Smith' )
BEGIN
    INSERT INTO Users (FirstName, LastName) VALUES ('John', 'Smith')
END

Chúng tôi sử dụng nó (xác minh) khi muốn làm một cái gì đó, sau đó câu trả lời của bạn đầy đủ hơn.
Abner Escócio


20

Bạn có thể dùng:

SELECT 1 FROM MyTable WHERE <MyCondition>

Nếu không có bản ghi khớp với điều kiện, thì recordset kết quả sẽ trống.


Ý bạn là TOP 1? -> (CHỌN TOP 1 TỪ MyTable WHERE <MyCondition>)
Jacob

6
Không, ý tôi là chính xác là "1"
Cătălin Pitiș

1
để cho phép trình tối ưu hóa truy vấn đến ngay cả khi bạn không đọc / cần các bộ dữ liệu còn lại, bạn nên nêu CHỌN TOP 1 1 TỪ ... Ở ĐÂU ... (hoặc sử dụng các gợi ý truy vấn phù hợp cho
RDBS

3
Bản thân toán tử Exists cố gắng truy xuất chỉ thông tin tối thiểu tuyệt đối, do đó, việc thêm TOP 1 không làm gì ngoài việc thêm 5 ký tự vào kích thước truy vấn. - sqlservercentral.com/bloss/sqlinthewild/2011/04/05/ từ
AquaAlex

13

Các câu trả lời khác là khá tốt, nhưng cũng sẽ hữu ích khi thêm LIMIT 1(hoặc tương đương , để ngăn việc kiểm tra các hàng không cần thiết.


3
Nếu bất kỳ truy vấn "kiểm tra sự tồn tại" nào trả về nhiều hơn một hàng, tôi nghĩ sẽ hữu ích hơn khi kiểm tra lại mệnh đề WHERE của bạn thay vì GIỚI HẠN số lượng kết quả.
Martin Schapendonk

2
Tôi nghĩ rằng Giới hạn được sử dụng trong Oracle chứ không phải trong Máy chủ SQL
Chaianu Gupta

7
Tôi đang xem xét trường hợp chúng có thể là nhiều hàng một cách hợp pháp - trong đó câu hỏi là: "Có (một hoặc nhiều) hàng thỏa mãn điều kiện này không?" Trong trường hợp đó, bạn không muốn xem xét tất cả chúng, chỉ một.
JesseW

1
@Shantanu - Tôi biết, đó là lý do tại sao tôi liên kết với bài viết (rất thông qua) en.wikipedia giải thích các hình thức khác.
JesseW

11
SELECT COUNT(1) FROM MyTable WHERE ...

sẽ lặp qua tất cả các hồ sơ. Đây là lý do nó là xấu để sử dụng cho sự tồn tại kỷ lục.

tôi sẽ dùng

SELECT TOP 1 * FROM MyTable WHERE ...

Sau khi tìm thấy 1 bản ghi, nó sẽ chấm dứt vòng lặp.


Trong trường hợp SELECT TOP 1nó sẽ thực sự chấm dứt sau khi tìm thấy một hoặc nó tiếp tục tìm thấy tất cả để có thể nói cái nào là HÀNG ĐẦU?
Eirik H

3
Tái bút: Để chắc chắn tôi luôn luônIF EXISTS (SELECT TOP 1 1 FROM ... WHERE ..)
Eirik H

toán tử Star sẽ buộc DBMS truy cập vào chỉ mục được nhóm thay vì chỉ các chỉ số cần thiết cho điều kiện nối của bạn. Vì vậy, tốt hơn là sử dụng valua không đổi, kết quả là chọn top 1 1 .... Điều đó sẽ trả về 1 hoặc DB-Null, tùy thuộc vào điều kiện có khớp hay không.
eFloh

nó đẹp. Tôi thích cái đầu tiên.
isxaker

10

Bạn có thể dùng:

SELECT COUNT(1) FROM MyTable WHERE ... 

hoặc là

WHERE [NOT] EXISTS 
( SELECT 1 FROM MyTable WHERE ... )

Điều này sẽ hiệu quả hơn so với việc SELECT *bạn chỉ cần chọn giá trị 1 cho mỗi hàng, thay vì tất cả các trường.

Cũng có một sự khác biệt tinh tế giữa COUNT (*) và COUNT (tên cột):

  • COUNT(*) sẽ đếm tất cả các hàng, bao gồm cả null
  • COUNT(column name) sẽ chỉ tính lần xuất hiện không null của tên cột

2
Bạn đang đặt ra giả định sai lầm rằng DBMS bằng cách nào đó sẽ kiểm tra tất cả các cột đó. Sự khác biệt về hiệu năng giữa count(1)count(*)sẽ chỉ khác nhau trong DBMS chết não nhất.
paxdiablo

2
Không, tôi đang nói rằng bạn thực sự dựa vào các chi tiết triển khai khi bạn nói rằng nó sẽ hiệu quả hơn. Nếu bạn thực sự muốn đảm bảo bạn có được hiệu suất tốt nhất, bạn nên lập hồ sơ cho việc triển khai cụ thể bằng dữ liệu đại diện hoặc quên hoàn toàn về nó. Bất cứ điều gì khác có khả năng gây hiểu lầm và có thể thay đổi mạnh mẽ khi chuyển (ví dụ) từ DB2 sang MySQL.
paxdiablo

1
Tôi muốn làm rõ rằng tôi không bỏ qua câu trả lời của bạn. Nó hữu ích. Điều duy nhất tôi gặp phải là vấn đề về hiệu quả vì chúng tôi đã thực hiện các đánh giá trong DB2 / z và thấy rằng không có sự khác biệt thực sự giữa count(*)count(1). Cho dù đó là trường hợp của DBMS khác ', tôi không thể nói.
paxdiablo

3
"Bất cứ điều gì khác có khả năng gây hiểu lầm và có thể thay đổi mạnh mẽ khi chuyển (ví dụ) từ DB2 sang MySQL" Bạn có nhiều khả năng bị cắn bởi sự suy giảm hiệu năng của SELECT COUNT (*) khi di chuyển DBMS so với sự khác biệt triển khai trong CHỌN 1 hoặc QUẬN (1). Tôi là một người tin tưởng vững chắc trong việc viết mã thể hiện rõ nhất chính xác những gì bạn muốn đạt được, thay vì dựa vào các trình tối ưu hóa hoặc trình biên dịch để mặc định cho hành vi mong muốn của bạn.
Winston Smith

1
Tuyên bố gây hiểu lầm "COUNT (*)" có nghĩa là 'đếm toàn bộ các hàng'. Nó không yêu cầu truy cập vào bất kỳ cột cụ thể. Và trong hầu hết các trường hợp, thậm chí sẽ không yêu cầu quyền truy cập vào chính hàng vì số lượng chỉ số duy nhất là đủ.
James Anderson

9

Bạn có thể dùng:

SELECT 1 FROM MyTable WHERE... LIMIT 1

Sử dụng select 1để ngăn chặn việc kiểm tra các lĩnh vực không cần thiết.

Sử dụng LIMIT 1 để ngăn chặn việc kiểm tra các hàng không cần thiết.


3
Điểm hay nhưng Giới hạn hoạt động trên MySQL và PostgreSQL, hoạt động hàng đầu trên SQL Server, bạn nên lưu ý câu trả lời của mình
Leo Gurdian

0

Tôi đang sử dụng cách này:

IIF(EXISTS (SELECT TOP 1 1 
                FROM Users 
                WHERE FirstName = 'John'), 1, 0) AS DoesJohnExist

0

Tùy chọn khác:

SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [MyTable] AS [MyRecord])
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
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.