Sự khác biệt giữa EXISTS và IN trong SQL?


443

Sự khác biệt giữa mệnh đề EXISTSINmệnh đề trong SQL là gì?

Khi nào chúng ta nên sử dụng EXISTS, và khi nào chúng ta nên sử dụng IN?

Câu trả lời:


224

Các existstừ khóa có thể được sử dụng theo cách đó, nhưng thực sự nó dự định như là một cách để đếm tránh:

--this statement needs to check the entire table
select count(*) from [table] where ...

--this statement is true as soon as one match is found
exists ( select * from [table] where ... )

Điều này hữu ích nhất khi bạn có các ifcâu lệnh có điều kiện, vì existscó thể nhanh hơn rất nhiều count.

Được insử dụng tốt nhất khi bạn có một danh sách tĩnh để vượt qua:

 select * from [table]
 where [field] in (1, 2, 3)

Khi bạn có một bảng trong một incâu lệnh, việc sử dụng một bảng sẽ có ý nghĩa hơn join, nhưng chủ yếu là không quan trọng. Trình tối ưu hóa truy vấn sẽ trả về cùng một kế hoạch. Trong một số triển khai (chủ yếu là cũ hơn, chẳng hạn như Microsoft SQL Server 2000), incác truy vấn sẽ luôn có một kế hoạch tham gia lồng nhau , trong khi joincác truy vấn sẽ sử dụng lồng nhau, hợp nhất hoặc băm khi thích hợp. Các triển khai hiện đại hơn thông minh hơn và có thể điều chỉnh kế hoạch ngay cả khi inđược sử dụng.


2
Bạn có thể giải thích thêm về "Khi bạn có một bảng trong một câu lệnh, việc sử dụng một phép nối sẽ hợp lý hơn, nhưng nó không thực sự quan trọng. Trình tối ưu hóa truy vấn sẽ trả về cùng một kế hoạch."? Không phải là phần tối ưu hóa truy vấn, phần mà bạn có thể sử dụng JOINthay thế cho IN.
farthVader

select * from [table] where [field] in (select [field] from [table2])trả về cùng kết quả (và gói truy vấn) như select * from [table] join [table2] on [table2].[field] = [table].[field].

@Sander nó không: truy vấn đầu tiên trả về tất cả các cột từ table, trong khi truy vấn thứ hai trả về mọi thứ từ tabletable2. Trong một số cơ sở dữ liệu SQL (hầu hết cũ hơn), intruy vấn sẽ được triển khai dưới dạng tham gia lồng nhau, trong khi jointruy vấn có thể được lồng, sáp nhập, băm, v.v. - bất cứ điều gì nhanh nhất.
Keith

2
Được rồi, tôi nên có các cột được chỉ định trong mệnh đề chọn, nhưng bạn nên cập nhật câu trả lời của mình vì nó nói rõ rằng các truy vấn "sẽ trả về cùng một kế hoạch".

existscó thể được sử dụng trong một tuyên bố trường hợp, vì vậy chúng cũng có thể có ích theo cách đó, ví dụselect case when exists (select 1 from emp where salary > 1000) then 1 else 0 end as sal_over_1000
smooth_smoothie

125

EXISTSsẽ cho bạn biết liệu một truy vấn có trả về bất kỳ kết quả nào không. ví dụ:

SELECT * 
FROM Orders o 
WHERE EXISTS (
    SELECT * 
    FROM Products p 
    WHERE p.ProductNumber = o.ProductNumber)

IN được sử dụng để so sánh một giá trị với nhiều giá trị và có thể sử dụng các giá trị bằng chữ, như thế này:

SELECT * 
FROM Orders 
WHERE ProductNumber IN (1, 10, 100)

Bạn cũng có thể sử dụng kết quả truy vấn với INmệnh đề, như sau:

SELECT * 
FROM Orders 
WHERE ProductNumber IN (
    SELECT ProductNumber 
    FROM Products 
    WHERE ProductInventoryQuantity > 0)

3
Truy vấn cuối cùng là nguy hiểm vì nó có thể thất bại trong trường hợp truy vấn con không trả về bất kỳ kết quả nào. Mệnh đề 'in' yêu cầu ít nhất 1 đối số ...
user2054927

40
@ user2054927 Truy vấn cuối cùng sẽ trả về chính xác không có hàng nếu truy vấn con không trả về hàng - không có gì nguy hiểm về điều đó!
Tony Andrew

Câu trả lời tốt nhất.
Aminadav Glickshtein

81

Dựa trên trình tối ưu hóa quy tắc :

  • EXISTSnhanh hơn nhiều so với INkhi kết quả truy vấn phụ rất lớn.
  • INnhanh hơn EXISTS, khi kết quả truy vấn phụ rất nhỏ.

Dựa trên tối ưu hóa chi phí :

  • Không có sự khác biệt.

21
Bằng chứng về lập luận của bạn? Tôi không nghĩ IN sẽ nhanh hơn EXISTS bao giờ!
Nawaz

22
@Nawaz Làm thế nào về bằng chứng tại sao IN luôn chậm hơn EXISTS?
ceving

2
Thực hiện tối ưu hóa truy vấn xấu? Tôi đã có vẻ như thế này (mặc dù không chính xác là tình huống này) xảy ra trong một
RDBM

1
EXISTS trả về các giá trị Boolean hoàn toàn, luôn nhanh hơn so với việc phải so sánh các chuỗi hoặc các giá trị lớn hơn loại BIT / Boolean. IN có thể hoặc không thể là một so sánh Boolean. Vì lập trình thích sử dụng EXPLICIT cho sự ổn định (một phần của ACID), nên EXISTS thường được ưa thích hơn.
clifton_h

2
Tại sao điều này được nâng cấp rất nhiều lần? Hoàn toàn không có lý do tại sao tuyên bố dựa trên giả định này nói chung là đúng.
Lukas Eder

40

Tôi giả sử bạn biết những gì họ làm và do đó được sử dụng khác nhau, vì vậy tôi sẽ hiểu câu hỏi của bạn là: Khi nào nên viết lại SQL để sử dụng IN thay vì EXISTS hoặc ngược lại.

Đó có phải là một giả định công bằng?


Chỉnh sửa : Lý do tôi hỏi là trong nhiều trường hợp, bạn có thể viết lại SQL dựa trên IN để sử dụng EXISTS thay vào đó và ngược lại, và đối với một số công cụ cơ sở dữ liệu, trình tối ưu hóa truy vấn sẽ xử lý hai cách khác nhau.

Ví dụ:

SELECT *
FROM Customers
WHERE EXISTS (
    SELECT *
    FROM Orders
    WHERE Orders.CustomerID = Customers.ID
)

có thể được viết lại thành:

SELECT *
FROM Customers
WHERE ID IN (
    SELECT CustomerID
    FROM Orders
)

hoặc tham gia:

SELECT Customers.*
FROM Customers
    INNER JOIN Orders ON Customers.ID = Orders.CustomerID

Vì vậy, câu hỏi của tôi vẫn là, người đăng ban đầu tự hỏi về những gì IN và EXISTS làm, và do đó làm thế nào để sử dụng nó, hoặc anh ta yêu cầu viết lại SQL bằng cách sử dụng IN để sử dụng EXISTS thay vào đó, hoặc ngược lại, sẽ là một ý tưởng tốt?


12
Tôi không biết về OP, nhưng tôi muốn câu trả lời cho câu hỏi này! Khi nào tôi nên sử dụng EXISTS thay vì IN với truy vấn con trả về ID?
Roy Tinker

8
trong JOIN, bạn sẽ cần mộtDISTINCT
Jaider

4
cuộc biểu tình tuyệt vời, nhưng khá nhiều câu hỏi chưa được trả lời
Junchen Liu

28
  1. EXISTSnhanh hơn nhiều so với INkhi kết quả truy vấn con rất lớn.
    INnhanh hơn EXISTSkhi kết quả truy vấn con rất nhỏ.

    CREATE TABLE t1 (id INT, title VARCHAR(20), someIntCol INT)
    GO
    CREATE TABLE t2 (id INT, t1Id INT, someData VARCHAR(20))
    GO
    
    INSERT INTO t1
    SELECT 1, 'title 1', 5 UNION ALL
    SELECT 2, 'title 2', 5 UNION ALL
    SELECT 3, 'title 3', 5 UNION ALL
    SELECT 4, 'title 4', 5 UNION ALL
    SELECT null, 'title 5', 5 UNION ALL
    SELECT null, 'title 6', 5
    
    INSERT INTO t2
    SELECT 1, 1, 'data 1' UNION ALL
    SELECT 2, 1, 'data 2' UNION ALL
    SELECT 3, 2, 'data 3' UNION ALL
    SELECT 4, 3, 'data 4' UNION ALL
    SELECT 5, 3, 'data 5' UNION ALL
    SELECT 6, 3, 'data 6' UNION ALL
    SELECT 7, 4, 'data 7' UNION ALL
    SELECT 8, null, 'data 8' UNION ALL
    SELECT 9, 6, 'data 9' UNION ALL
    SELECT 10, 6, 'data 10' UNION ALL
    SELECT 11, 8, 'data 11'
  2. Truy vấn 1

    SELECT
    FROM    t1 
    WHERE   not  EXISTS (SELECT * FROM t2 WHERE t1.id = t2.t1id)

    Truy vấn 2

    SELECT t1.* 
    FROM   t1 
    WHERE  t1.id not in (SELECT  t2.t1id FROM t2 )

    Nếu trong t1id của bạn có giá trị null thì Truy vấn 1 sẽ tìm thấy chúng, nhưng Truy vấn 2 không thể tìm thấy tham số null.

    Tôi có nghĩa là INkhông thể so sánh bất cứ điều gì với null, vì vậy nó không có kết quả cho null, nhưng EXISTScó thể so sánh mọi thứ với null.


Câu trả lời này là tóm tắt hợp lý về tình cảm của Tom Kite ( asktom.oracle.com/pls/asktom/ mẹo )
Jeromy Pháp

Tôi nghĩ rằng câu trả lời này dựa trên trực giác, đủ công bằng. Nhưng nó không thể là sự thật. Ví dụ, gần như chắc chắn không đúng với Ingres , nó sẽ phân tích cả hai truy vấn SQL tương đương thành cùng một truy vấn QUEL, điều này thiếu SQL - ahem - 'sự phong phú' khi viết cùng một cách nhiều cách.
onedaywhen

Hai truy vấn này tương đương về mặt logic khi và chỉ khi t2.id được định nghĩa là "KHÔNG NULL". Để được cấp các tương đương với không phụ thuộc vào định nghĩa bảng truy vấn thứ 2 nên "SELECT * FROM t1 t1 ĐÂU t1.id không (SELECT t2.id TỪ t2. Nơi t2.id không là null )"
David דודו Markovitz

16

Nếu bạn đang sử dụng INtoán tử, công cụ SQL sẽ quét tất cả các bản ghi được tìm nạp từ truy vấn bên trong. Mặt khác, nếu chúng ta đang sử dụng EXISTS, công cụ SQL sẽ dừng quá trình quét ngay khi tìm thấy kết quả khớp.


10

IN chỉ hỗ trợ các quan hệ bình đẳng (hoặc bất bình đẳng khi đi trước KHÔNG ).
Nó là từ đồng nghĩa với = any / = some , vd

select    * 
from      t1 
where     x in (select x from t2)
;

EXISTS hỗ trợ các loại quan hệ biến thể, không thể biểu thị bằng IN , ví dụ -

select    * 
from      t1 
where     exists (select    null 
                  from      t2 
                  where     t2.x=t1.x 
                        and t2.y>t1.y 
                        and t2.z like '℅' || t1.z || '℅'
                  )
;

Và trên một lưu ý khác -

Sự khác biệt về hiệu suất và kỹ thuật giữa EXISTSIN có thể xuất phát từ việc triển khai / giới hạn / lỗi cụ thể của nhà cung cấp, nhưng nhiều khi chúng không là gì ngoài những huyền thoại được tạo ra do thiếu hiểu biết về cơ sở dữ liệu bên trong.

Định nghĩa, độ chính xác của thống kê, cấu hình cơ sở dữ liệu và phiên bản của trình tối ưu hóa đều có tác động đến kế hoạch thực hiện và do đó đối với các số liệu hiệu suất.


Upvote cho nhận xét của bạn về hiệu suất: không tập trung vào một DBMS cụ thể, chúng ta nên cho rằng tùy thuộc vào trình tối ưu hóa để tìm ra cái gì hoạt động tốt nhất.
Manngo

9

Các Existstừ khóa đánh giá đúng hay sai, nhưng INtừ khóa so sánh tất cả giá trị trong cột truy vấn phụ tương ứng. Một số khác Select 1có thể được sử dụng với Existslệnh. Thí dụ:

SELECT * FROM Temp1 where exists(select 1 from Temp2 where conditions...)

Nhưng INkém hiệu quả nên Existsnhanh hơn.


5

Tôi nghĩ,

  • EXISTSlà khi bạn cần khớp kết quả truy vấn với truy vấn con khác. Các kết quả truy vấn số 1 cần được truy xuất khi kết quả SubQuery khớp. Loại tham gia .. Ví dụ: chọn khách hàng bảng số 1 cũng đã đặt bảng đơn hàng số 2

  • IN là để truy xuất nếu giá trị của một cột cụ thể nằm trong INdanh sách (1,2,3,4,5) Ví dụ: Chọn các khách hàng nằm trong các mã zip sau, tức là các giá trị zip_code nằm trong danh sách (....).

Khi nào nên sử dụng cái này hơn cái kia ... khi bạn cảm thấy nó đọc phù hợp (Giao tiếp có ý định tốt hơn).


4

Sự khác biệt nằm ở đây:

select * 
from abcTable
where exists (select null)

Truy vấn trên sẽ trả về tất cả các bản ghi trong khi bên dưới sẽ trả về trống.

select *
from abcTable
where abcTable_ID in (select null)

Hãy thử và quan sát đầu ra.


1
Hmmm ... Lỗi: [SQL0104] Mã thông báo) không hợp lệ. Trong cả hai trường hợp. Bạn đang giả sử một RDBMS cụ thể?
jmarkmurphy

3

Theo hiểu biết của tôi khi một truy vấn con trả về một NULLgiá trị thì toàn bộ câu lệnh trở thành NULL. Trong trường hợp đó, chúng tôi đang sử dụng EXITStừ khóa. Nếu chúng ta muốn so sánh các giá trị cụ thể trong các truy vấn con thì chúng ta đang sử dụng INtừ khóa.


3

Cái nào nhanh hơn phụ thuộc vào số lượng truy vấn được tìm nạp bởi truy vấn bên trong:

  • Khi truy vấn bên trong của bạn tìm nạp hàng nghìn hàng thì EXIST sẽ là lựa chọn tốt hơn
  • Khi truy vấn bên trong của bạn tìm nạp một vài hàng, thì IN sẽ nhanh hơn

EXIST đánh giá đúng hay sai nhưng IN so sánh nhiều giá trị. Khi bạn không biết bản ghi có tồn tại hay không, bạn nên chọn EXIST


3

Lý do là vì toán tử EXISTS hoạt động dựa trên nguyên tắc ít nhất là tìm thấy. Nó trả về true và dừng bảng quét một lần ít nhất một hàng khớp.

Mặt khác, khi toán tử IN được kết hợp với truy vấn con, MySQL phải xử lý truy vấn con trước, sau đó sử dụng kết quả của truy vấn con để xử lý toàn bộ truy vấn.

Nguyên tắc chung là nếu truy vấn con chứa một khối lượng dữ liệu lớn, toán tử EXISTS cung cấp hiệu suất tốt hơn.

Tuy nhiên, truy vấn sử dụng toán tử IN sẽ thực hiện nhanh hơn nếu tập kết quả được trả về từ truy vấn con là rất nhỏ.


1

Tôi hiểu là cả hai nên giống nhau miễn là chúng ta không xử lý các giá trị NULL.

Lý do tương tự tại sao truy vấn không trả về giá trị cho = NULL vs là NULL. http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/

Đối với đối số boolean và so sánh đi, để tạo ra một boolean cả hai giá trị cần phải được so sánh và đó là cách bất kỳ nếu điều kiện hoạt động. Vì vậy, tôi không hiểu cách IN và EXISTS hành xử khác nhau.



0

Nếu truy vấn con trả về nhiều hơn một giá trị, bạn có thể cần thực hiện truy vấn bên ngoài - nếu các giá trị trong cột được chỉ định trong điều kiện khớp với bất kỳ giá trị nào trong tập kết quả của truy vấn phụ. Để thực hiện nhiệm vụ này, bạn cần sử dụng intừ khóa.

Bạn có thể sử dụng truy vấn con để kiểm tra xem có tồn tại một tập hợp các bản ghi không. Đối với điều này, bạn cần sử dụng existsmệnh đề với một truy vấn con. Các existstừ khóa luôn trả về giá trị đúng hoặc sai.


0

Tôi tin rằng điều này có một câu trả lời đơn giản. Tại sao bạn không kiểm tra nó từ những người đã phát triển chức năng đó trong hệ thống của họ?

Nếu bạn là nhà phát triển MS SQL, đây là câu trả lời trực tiếp từ Microsoft.

IN:

Xác định xem một giá trị được chỉ định phù hợp với bất kỳ giá trị nào trong truy vấn con hoặc danh sách.

EXISTS:

Chỉ định truy vấn con để kiểm tra sự tồn tại của các hàng.



-1

EXISTS có hiệu suất nhanh hơn IN. Nếu Hầu hết các tiêu chí bộ lọc nằm trong truy vấn phụ thì tốt hơn nên sử dụng IN và Nếu hầu hết các tiêu chí bộ lọc nằm trong truy vấn chính thì tốt hơn nên sử dụng EXISTS.


Yêu cầu đó thực sự không được hỗ trợ bởi bất kỳ bằng chứng nào, phải không?
Lukas Eder

-2

Nếu bạn đang sử dụng toán tử IN, công cụ SQL sẽ quét tất cả các bản ghi được tìm nạp từ truy vấn bên trong. Mặt khác, nếu chúng ta đang sử dụng EXISTS, công cụ SQL sẽ dừng quá trình quét ngay khi tìm thấy kết quả khớp.


@ziggy giải thích? Đây là khá nhiều những gì câu trả lời được chấp nhận cũng nói. Trong PHẢI kiểm tra từng bản ghi, tồn tại có thể dừng ngay khi tìm thấy chỉ một.
Ben Thurley

Không, không đúng. INEXISTScó thể tương đương và biến đổi lẫn nhau.
Lukas Eder
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.