Cách tốt nhất để kiểm tra nếu một hàng tồn tại trong bảng MySQL


336

Tôi đang cố gắng tìm hiểu xem một hàng có tồn tại trong một bảng không. Sử dụng MySQL, tốt hơn là thực hiện một truy vấn như thế này:

SELECT COUNT(*) AS total FROM table1 WHERE ...

và kiểm tra xem tổng số có khác không hay tốt hơn là thực hiện một truy vấn như thế này:

SELECT * FROM table1 WHERE ... LIMIT 1

và kiểm tra xem có hàng nào được trả lại không?

Trong cả hai truy vấn, mệnh đề WHERE sử dụng một chỉ mục.

Câu trả lời:


469

Bạn cũng có thể thử EXISTS:

SELECT EXISTS(SELECT * FROM table1 WHERE ...)

và theo các tài liệu , bạn có thể SELECTbất cứ điều gì.

Theo truyền thống, một truy vấn con EXISTS bắt đầu bằng CHỌN *, nhưng nó có thể bắt đầu bằng CHỌN 5 hoặc CHỌN cột1 hoặc bất cứ điều gì cả. MySQL bỏ qua danh sách CHỌN trong một truy vấn con như vậy, vì vậy nó không có gì khác biệt.


30
Kiểm tra với ...EXISTS( SELECT 1/0 FROM someothertable). Đối với SQL Server & Oracle - việc sử dụng *, 1 hoặc NULL không có gì khác biệt vì EXISTS chỉ kiểm tra boolean dựa trên 1+ tiêu chí WHERE phù hợp.
Ngựa vằn OMG

77
Các bạn, nó nói ngay trong tài liệu được liên kết trong câu trả lời này, đoạn 2, "Theo truyền thống, một truy vấn con EXISTS bắt đầu bằng CHỌN *, nhưng nó có thể bắt đầu với CHỌN 5 hoặc CHỌN cột1 hoặc bất cứ điều gì. MySQL bỏ qua danh sách CHỌN trong đó một truy vấn phụ, vì vậy nó không có gì khác biệt. "
mở

12
@ChrisThndry: điều gì xảy ra khi câu lệnh được thực thi? Ý tôi là tập kết quả chứa gì?
Ashwin

13
@Ashwin, nó chứa 0 (không tồn tại) hay 1 (tồn tại).
fedorqui 'SO ngừng làm hại'

10
Tôi nghĩ rằng truy vấn của bạn là không cần thiết, tôi đã thử nghiệm và truy vấn SELECT 1 FROM table1 WHERE col = $var LIMIT 1này nhanh hơn truy vấn của bạn. Vì vậy, lợi thế của truy vấn của bạn là gì?
Shafizadeh

182

Tôi đã thực hiện một số nghiên cứu về chủ đề này gần đây. Cách để thực hiện nó phải khác nếu trường là trường văn bản, trường không phải là duy nhất.

Tôi đã thực hiện một số thử nghiệm với một trường văn bản. Xem xét thực tế rằng chúng ta có một bảng với các mục 1M. 37 mục tương đương với 'một cái gì đó':

  • SELECT * FROM test WHERE texte LIKE '%something%' LIMIT 1với mysql_num_rows() : 0,039061069488525s. (NHANH CHÓNG)
  • SELECT count(*) as count FROM test WHERE text LIKE '%something% : 16.028197050095s.
  • SELECT EXISTS(SELECT 1 FROM test WHERE text LIKE '%something%') : 0.87045907974243s.
  • SELECT EXISTS(SELECT 1 FROM test WHERE text LIKE '%something%' LIMIT 1) : 0,04898986816406s.

Nhưng hiện tại, với trường PK BIGINT, chỉ có một mục nhập bằng '321321':

  • SELECT * FROM test2 WHERE id ='321321' LIMIT 1với mysql_num_rows() : 0,0089840888977051s.
  • SELECT count(*) as count FROM test2 WHERE id ='321321' : 0,00033879280090332s.
  • SELECT EXISTS(SELECT 1 FROM test2 WHERE id ='321321') : 0,00023889541625977s.
  • SELECT EXISTS(SELECT 1 FROM test2 WHERE id ='321321' LIMIT 1): 0,00020313262939453s. (NHANH CHÓNG)

2
Cảm ơn câu trả lời bổ sung. Bạn có thấy sự khác biệt về thời gian giữa hai tùy chọn nhanh nhất cho trường văn bản là khá nhất quán không? Sự khác biệt không có vẻ lớn và sử dụng CHỌN EXISTS (CHỌN 1 ... GIỚI HẠN 1) có vẻ khá tốt trong cả hai trường hợp.
Bernard Chen

1
Bạn nói đúng, sự khác biệt không quá quan trọng đối với các kết quả khác liên quan đến trường văn bản. Tuy nhiên, có thể truy vấn sẽ tốt hơn khi sử dụngSELECT 1 FROM test WHERE texte LIKE '%something%' LIMIT 1
Laurent W.

Tôi đã thử trên mysql và trong trường hợp bạn sử dụng select 1 ... limit 1, việc sử dụng tồn tại là vô ích
Adrien Horgnies

4
@LittleNooby có sự khác biệt. CHỌN EXISTS ... cho giá trị đúng và sai (1 hoặc 0), trong khi CHỌN 1 ... cho 1 hoặc trống. Có sự khác biệt tinh tế giữa giá trị sai và tập rỗng, tùy thuộc vào tình huống của bạn.
Quickpick

@LittleNooby làm cho một điểm tuyệt vời, đó là dễ dàng bỏ qua. Thiếu trong các bài kiểm tra thời gian ở trên là SELECT 1 FROM test WHERE ..., không có SELECT EXISTSxung quanh nó. Có lẽ là một mái tóc nhanh hơn theo cách đó.
ToolmakerSteve

27

Một ví dụ ngắn về câu trả lời của @ ChrisThndry

Thí dụ:

mysql> SELECT * FROM table_1;
+----+--------+
| id | col1   |
+----+--------+
|  1 | foo    |
|  2 | bar    |
|  3 | foobar |
+----+--------+
3 rows in set (0.00 sec)

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 1);
+--------------------------------------------+
| EXISTS(SELECT 1 FROM table_1 WHERE id = 1) |
+--------------------------------------------+
|                                          1 |
+--------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 9);
+--------------------------------------------+
| EXISTS(SELECT 1 FROM table_1 WHERE id = 9) |
+--------------------------------------------+
|                                          0 |
+--------------------------------------------+
1 row in set (0.00 sec)

Sử dụng bí danh:

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 1) AS mycheck;
+---------+
| mycheck |
+---------+
|       1 |
+---------+
1 row in set (0.00 sec)

18

Trong nghiên cứu của tôi, tôi có thể tìm thấy kết quả đạt được theo tốc độ.

select * from table where condition=value
(1 total, Query took 0.0052 sec)

select exists(select * from table where condition=value)
(1 total, Query took 0.0008 sec)

select count(*) from table where condition=value limit 1) 
(1 total, Query took 0.0007 sec)

select exists(select * from table where condition=value limit 1)
(1 total, Query took 0.0006 sec) 

12

Tôi cảm thấy nó đáng để chỉ ra, mặc dù nó đã được chạm vào trong các bình luận, rằng trong tình huống này:

SELECT 1 FROM my_table WHERE *indexed_condition* LIMIT 1

Là vượt trội so với:

SELECT * FROM my_table WHERE *indexed_condition* LIMIT 1

Điều này là do truy vấn đầu tiên có thể được thỏa mãn bởi chỉ mục, trong khi truy vấn thứ hai yêu cầu tra cứu hàng (trừ khi có thể tất cả các cột của bảng nằm trong chỉ mục được sử dụng).

Thêm LIMITmệnh đề cho phép động cơ dừng lại sau khi tìm thấy bất kỳ hàng nào.

Truy vấn đầu tiên phải tương đương với:

SELECT EXISTS(SELECT * FROM my_table WHERE *indexed_condition*)

Việc gửi các tín hiệu tương tự đến động cơ (1 / * không có sự khác biệt ở đây), nhưng tôi vẫn viết 1 để củng cố thói quen khi sử dụng EXISTS:

SELECT EXISTS(SELECT 1 FROM my_table WHERE *indexed_condition*)

Có thể có ý nghĩa để thêm EXISTSgói nếu bạn yêu cầu trả lại rõ ràng khi không có hàng nào khớp.


4

Đề nghị bạn không sử dụng Countvì đếm luôn tạo thêm tải cho sử dụng db SELECT 1và nó trả về 1 nếu bản ghi của bạn ở ngay nếu không nó trả về null và bạn có thể xử lý nó.


2

Một truy vấn COUNT nhanh hơn, mặc dù có thể không đáng chú ý, nhưng theo như nhận được kết quả mong muốn, cả hai đều là đủ.


4
Đây là DB cụ thể. COUNT (*) được biết là chậm trong PostgreSQL. Tốt hơn là chọn cột PK và xem nếu nó trả về bất kỳ hàng nào.
BalusC

3
COUNT (*) chậm trong InnoDB mặc dù
Sẽ

2

Đôi khi, khá thuận tiện để lấy khóa chính tăng tự động ( id) của hàng nếu nó tồn tại và 0nếu không.

Đây là cách điều này có thể được thực hiện trong một truy vấn duy nhất:

SELECT IFNULL(`id`, COUNT(*)) FROM WHERE ...

Tại sao không chỉ sử dụng IFNULL(id, 0)ở đây thay vì COUNT(*)?
Ethan Hohensee


-1

Tôi sẽ đi với COUNT(1). Nó nhanh hơn COUNT(*)bởi vì COUNT(*)các thử nghiệm để xem liệu có ít nhất một cột trong hàng đó là! = NULL. Bạn không cần điều đó, đặc biệt là vì bạn đã có một điều kiện tại chỗ ( WHEREđiều khoản). COUNT(1)thay vào đó kiểm tra tính hợp lệ của 1, luôn luôn hợp lệ và mất ít thời gian hơn để kiểm tra.


8
-1 Điều này là sai. COUNT (*) không nhìn vào các giá trị cột - nó chỉ đếm số lượng hàng. Xem câu trả lời của tôi tại đây: stackoverflow.com/questions/2876909/ từ
Mark Byers

6
COUNT () chậm hơn nhiều so với EXISTS vì EXISTS có thể quay lại khi lần đầu tiên tìm thấy một hàng
Will

-1

Hoặc bạn có thể chèn phần sql thô vào điều kiện để tôi có 'điều kiện' => mảng ('Member.id KHÔNG IN (CHỌN Thành viên.member_id TỪ tư cách thành viên NHƯ Tư cách thành viên)')


-2

COUNT(*) được tối ưu hóa trong MySQL, do đó, truy vấn cũ có thể sẽ nhanh hơn, nói chung.


2
Bạn đang đề cập đến việc tối ưu hóa mà MyISAM có để chọn số lượng cho cả bảng? Tôi đã không nghĩ rằng điều đó có ích nếu có một điều kiện WHERE.
Bernard Chen
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.