SQL IN () so với OR


23

Tôi đã làm việc với một truy vấn mà tôi đã viết hôm nay phải thay đổi mã từ WHEREmệnh đề để sử dụng bộ lọc IN (danh sách các công cụ) thay vì sử dụng một cái gì đó như

item_desc = 'item 1'
OR item_desc = 'item 2'
OR item_desc = 'item 3'
OR item_desc = 'item 4'

Ở trên chạy trong 15 phút và không trả lại gì, nhưng sau đây đã cho tôi kết quả của tôi được đặt trong 1,5 phút

item_desc IN (
'item 1'
,'item 2'
,'item 3'
,'item 4'
)

Tôi đã làm điều này trong SQL và tự hỏi tại sao IN (danh sách các mục) thực hiện nhanh hơn nhiều so với câu lệnh OR.

- EDIT - SQL Server 2008, tôi xin lỗi vì đã không đưa thông tin này vào vị trí đầu tiên.

Đây là toàn bộ Truy vấn bằng cách sử dụng các ORcâu lệnh:

DECLARE @SD DATETIME
DECLARE @ED DATETIME
SET @SD = '2013-06-01';
SET @ED = '2013-06-15';

-- COLUMN SELECTION
SELECT PV.PtNo_Num AS 'VISIT ID'
, PV.Med_Rec_No AS 'MRN'
, PV.vst_start_dtime AS 'ADMIT'
, PV.vst_end_dtime AS 'DISC'
, PV.Days_Stay AS 'LOS'
, PV.pt_type AS 'PT TYPE'
, PV.hosp_svc AS 'HOSP SVC'
, SO.ord_no AS 'ORDER NUMBER'
--, SO.ent_dtime AS 'ORDER ENTRY TIME'
--, DATEDIFF(HOUR,PV.vst_start_dtime,SO.ent_dtime) AS 'ADM TO ENTRY HOURS'
, SO.svc_desc AS 'ORDER DESCRIPTION'
, OSM.ord_sts AS 'ORDER STATUS'
, SOS.prcs_dtime AS 'ORDER STATUS TIME'
, DATEDIFF(DAY,PV.vst_start_dtime,SOS.prcs_dtime) AS 'ADM TO ORD STS IN DAYS'

-- DB(S) USED
FROM smsdss.BMH_PLM_PtAcct_V PV
JOIN smsmir.sr_ord SO
ON PV.PtNo_Num = SO.episode_no
JOIN smsmir.sr_ord_sts_hist SOS
ON SO.ord_no = SOS.ord_no
JOIN smsmir.ord_sts_modf_mstr OSM
ON SOS.hist_sts = OSM.ord_sts_modf_cd

-- FILTER(S)
WHERE PV.Adm_Date BETWEEN @SD AND @ED
AND SO.svc_cd = 'PCO_REMFOLEY'
OR SO.svc_cd = 'PCO_INSRTFOLEY'
OR SO.svc_cd = 'PCO_INSTFOLEY'
OR SO.svc_cd = 'PCO_URIMETER'

AND SO.ord_no NOT IN (
    SELECT SO.ord_no
    FRROM smsdss.BMH_PLM_PtAcct_V PV
    JOIN smsmir.sr_ord SO
    ON PV.PtNo_Num = SO.episode_no
    JOIN smsmir.sr_ord_sts_hist SOS
    ON SO.ord_no = SOS.ord_no
    JOIN smsmir.ord_sts_modf_mstr OSM
    ON SOS.hist_sts = OSM.ord_sts_modf_cd
    WHERE OSM.ord_sts = 'DISCONTINUE'
    AND SO.svc_cd = 'PCO_REMFOLEY'
    OR SO.svc_cd = 'PCO_INSRTFOLEY'
    OR SO.svc_cd = 'PCO_INSTFOLEY'
    OR SO.svc_cd = 'PCO_URIMETER'
)
ORDER BY PV.PtNo_Num, SO.ord_no, SOS.prcs_dtime

Cảm ơn bạn,


10
Bạn đã xem kế hoạch truy vấn?

1
Đây là thực hiện RẤT cụ thể. DBMS nào bạn đang sử dụng?
James Anderson

Tôi đã không nhìn vào kế hoạch truy vấn, tôi không biết đây là truy vấn cụ thể hay đó là vấn đề thực tế, vì điều này sẽ luôn hoạt động theo cách này.
MCP_infiltrator

3
@MCP_infiltrator Vì vậy, các kế hoạch thực hiện sẽ không tương đương vì logic không tương đương. Khi sử dụng ORnhư bạn làm trong truy vấn thực tế ở trên, bạn cho phép động cơ ngắn mạch. WHERE A AND B OR Csẽ đánh giá là đúng ngay cả khi A VÀ B sai, nếu C đúng. Nếu bạn nói WHERE A and B OR C OR D OR E OR Fnhư bạn làm ở trên, AND có thể được bao gồm. Logic tương đương thực tế sẽ gói gọn các ORchuỗi ở trên trong ngoặc đơn để chúng được coi là một tập hợp : WHERE A AND (B OR C OR D OR E). Đây là cách an INđược điều trị.
JNK

5
Ưu tiên của toán tử trong SQL Server được chỉ định đã ANDđược xử lý trước đó OR, vì vậy truy vấn của bạn ở trên tương đương với WHERE (OSM.ord_sts = 'DISCONTINUE' AND SO.svc_cd = 'PCO_REMFOLEY') OR SO.svc_cd = 'PCO_INSRTFOLEY' OR SO.svc_cd = 'PCO_INSTFOLEY' OR SO.svc_cd = 'PCO_URIMETER'điều kiện có nghĩa là nếu bất kỳ điều kiện nào trong 3 điều kiện cuối cùng là đúng thì nó sẽ có thể làm ngắn mạch phần còn lại của đánh giá.
JNK

Câu trả lời:


28

Câu trả lời của Oleski là không chính xác. Đối với SQL Server 2008, một INdanh sách được cấu trúc lại thành một chuỗi các ORcâu lệnh. Nó có thể khác nhau khi nói MySQL.

Tôi khá chắc chắn rằng nếu bạn tạo các kế hoạch thực hiện thực tế cho cả hai truy vấn của mình thì chúng sẽ giống hệt nhau.

Trong tất cả khả năng, truy vấn thứ hai chạy nhanh hơn vì bạn đã chạy nó lần thứ hai và truy vấn đầu tiên đã lấy tất cả các trang dữ liệu từ cơ sở dữ liệu và trả chi phí IO. Truy vấn thứ hai có thể đọc tất cả dữ liệu từ bộ nhớ và thực thi nhanh hơn rất nhiều.

Cập nhật

Nguồn thực tế của phương sai có khả năng là các truy vấn không tương đương . Bạn có hai ORdanh sách khác nhau dưới đây:

WHERE PV.Adm_Date BETWEEN @SD AND @ED
AND SO.svc_cd = 'PCO_REMFOLEY'
OR SO.svc_cd = 'PCO_INSRTFOLEY'
OR SO.svc_cd = 'PCO_INSTFOLEY'
OR SO.svc_cd = 'PCO_URIMETER'

và sau đó

 WHERE OSM.ord_sts = 'DISCONTINUE'
    AND SO.svc_cd = 'PCO_REMFOLEY'
    OR SO.svc_cd = 'PCO_INSRTFOLEY'
    OR SO.svc_cd = 'PCO_INSTFOLEY'
    OR SO.svc_cd = 'PCO_URIMETER'

Trong cả hai WHEREmệnh đề đó, ưu tiên toán tử (trong đó AND được xử lý trước OR) có nghĩa là logic thực tế được chạy bởi động cơ là:

WHERE (ConditionA AND ConditionB)
OR ConditionC
OR ConditionD
OR ConditionE

Nếu bạn thay thế các ORdanh sách bằng một INbiểu thức, logic sẽ là:

WHERE ConditionA
AND (ConditionB OR ConditionC OR ConditionD OR ConditionE)

Đó là hoàn toàn khác nhau.


2
@MCP_infiltrator Đó là vấn đề với việc đưa ra các giả định :) Bạn thực sự nên có kế hoạch thực hiện thực tế cho cả hai và xem nếu có sự khác biệt, tôi không nghĩ sẽ có.
JNK

4
Chà, nếu bạn có câu hỏi DB nâng cao, bạn cũng có thể hỏi về Quản trị viên cơ sở dữ liệu - công bố đầy đủ, tôi là người điều hành ở đó, nhưng nếu đó là câu hỏi tối ưu hóa SQL hoặc SQL nâng cao, chúng tôi có rất nhiều chuyên gia, đặc biệt là cho SQL Server
JNK

1
Tôi chỉ nhìn vào hai kế hoạch thực hiện và chúng khác nhau. Truy vấn với các câu lệnh OR chiếm 68% chi phí trong Quét chỉ mục cụm, trong đó câu lệnh IN là 26%, cùng với những gì dường như cũng ít bước thực hiện hơn.
MCP_infiltrator

3
@MCP_infiltrator Không cần, xem bình luận của tôi về bài viết gốc của bạn ở đầu. INkhông tương đương với ORs của bạn ở trên vì các điều kiện khác trong WHEREmệnh đề của bạn trong truy vấn thực tế. Về cơ bản các truy vấn sẽ trả về kết quả khác nhau.
JNK

3
@MCP_infiltrator Không cần phải đăng câu hỏi giống hệt nhau tại DBA.SE, JNK đã trả lời nó (và bạn sẽ nhận được câu trả lời tương tự ở đó.) Nếu bạn muốn di chuyển ("di chuyển") ở đó, bạn luôn có thể gắn cờ nó (câu hỏi của bạn) đề cập trong hộp bình luận những gì bạn muốn. Các mod sẽ chăm sóc.
ypercubeᵀᴹ

7

Cách tốt nhất để nói là nhìn vào kế hoạch truy vấn thực tế bằng cách sử dụng một cái gì đó như EXPLAIN. Điều này sẽ cho bạn biết chính xác những gì DBMS đang làm, và sau đó bạn có thể hiểu rõ hơn tại sao nó hiệu quả hơn.

Như đã nói, các hệ thống DBMS thực sự tốt trong việc thực hiện các hoạt động giữa hai bảng (như các phép nối). Rất nhiều thời gian của trình tối ưu hóa được dành cho các phần của các truy vấn này vì chúng thường đắt hơn.

Ví dụ, DBMS có thể sắp xếp INdanh sách đó và, bằng cách sử dụng một chỉ mục trên item_desc, lọc kết quả rất nhanh. Bạn không thể thực hiện tối ưu hóa đó khi bạn liệt kê một loạt các lựa chọn như trong ví dụ đầu tiên.

Khi bạn sử dụng IN, bạn đang tạo một bảng ngẫu hứng và lọc bằng các kỹ thuật kết hợp bảng hiệu quả hơn này.

EDIT : Tôi đã đăng câu trả lời này trước khi OP đề cập đến DBMS cụ thể. Điều này hóa ra KHÔNG phải là cách SQL Server xử lý truy vấn này, nhưng có thể hợp lệ cho các hệ thống DBMS khác. Xem câu trả lời của JNK để có câu trả lời cụ thể, chính xác hơn.


Tôi sẽ tưởng tượng cardinality có liên quan nhiều đến nó. Điều đó INsẽ không nhanh như vậy nếu nó là một mục phụ với 100 bản ghi trong đó, hoặc một nghìn.
Robert Harvey

@RobertHarvey Vâng, điều đó có thể đúng, nhưng tôi cũng không hy vọng nó sẽ tệ hơn nhiều.
Oleksi

Cảm ơn @Oleksi Tôi không biết rằng DBMS sẽ biến câu lệnh IN thành một danh sách ngẫu hứng
MCP_infiltrator

1
-1 - Trong SQL Server, INcâu lệnh không được chuyển đổi thành bảng, nó được xử lý giống hệt với một chuỗi ORs.
JNK

2
@ Katana314 Nếu GIẢI THÍCH là một từ khóa trong SQL Server (mà OP đang sử dụng) tôi sẽ đồng ý với bạn, nhưng nó không vì thế mà nó không liên quan.
JNK
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.