SQL IN () проти АБО


23

Я працював із запитом, про який я писав сьогодні, треба було змінити код із WHEREпункту, щоб використовувати фільтр IN (список речей), а не використовувати щось подібне

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

Наведене пробігло протягом 15 хвилин і нічого не повернуло, але наступне дало мені свій результат встановити за 1,5 хвилини

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

Я зробив це в SQL і мені цікаво, чому IN (список елементів) виконується так швидше, ніж оператор OR.

- EDIT - SQL Server 2008, я вибачаюся за те, що не ставте цю частину інформації на перше місце.

Ось Запит у повному обсязі з використанням ORоператорів:

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

Дякую,


10
Ви подивилися план запитів?

1
Це ДУЖЕ специфічно для реалізації. Які СУБД ви використовуєте?
Джеймс Андерсон

Я не дивився на план запитів, не знав, чи це запит конкретний, чи це насправді, як це завжди працюватиме таким чином.
MCP_infiltrator

3
@MCP_infiltrator Отже, плани виконання не будуть еквівалентними, оскільки логіка не еквівалентна. Використовуючи, ORяк ви робите, у фактичному запиті вище, ви дозволяєте двигуну короткого замикання. WHERE A AND B OR Cбуде оцінено як істинне, навіть якщо A І B помилкові, якщо C - правда. Якщо ви говорите, WHERE A and B OR C OR D OR E OR Fяк ви робите вище, це AND можна визначити. Фактична еквівалентна логіка инкапсулировать ORряд вище в дужках , тому вони розглядаються як набір: WHERE A AND (B OR C OR D OR E). Ось як INповодиться.
JNK

5
Попередження оператора в SQL Server вказано, що ANDоброблялося раніше OR, тому ваш запит вище еквівалентний тому, 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'що означає, що будь-яке з останніх 3 умов є істинним, воно зможе коротко замикати решту оцінок.
JNK

Відповіді:


28

Відповідь Олеського неправильна. Для SQL Server 2008 INсписок перетворюється на низку ORзаяв. Це може бути інакше, наприклад, в MySQL.

Я впевнений, що якби ви створили фактичні плани виконання для обох запитів, вони були б однаковими.

Ймовірно, другий запит запустився швидше, тому що ви запустили його другим , а перший запит вже витягнув усі сторінки даних із бази даних і сплатив вартість IO. Другий запит зміг прочитати всі дані з пам'яті та виконати набагато швидше.

Оновлення

Дійсне джерело дисперсії, ймовірно, запити не еквівалентні . У вас є два різних ORсписки нижче:

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'

і пізніше

 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'

В обох цих WHEREзастереженнях преференція оператора (де AND обробляється перед АБО) означає, що фактична логіка, якою керує двигун:

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

Якщо ви заміните ORсписки INвиразом, логіка буде такою:

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

Що кардинально відрізняється.


2
@MCP_infiltrator Ну, це проблема в тому, щоб робити припущення :) Ви дійсно повинні отримати фактичні плани виконання для обох і подивитися, чи є різниця, я не думаю, що буде.
JNK

4
Добре, якщо у вас є розширене питання щодо БД, ви також можете задати питання адміністраторам баз даних - повне розкриття інформації, я там модератор, але якщо це розширене питання оптимізації SQL або SQL, у нас є маса експертів, особливо для SQL Server
JNK

1
Я просто переглянув два плани виконання, і вони відрізняються між собою. Запит з операторами АБО займає 68% вартості в кластерному скануванні індексів, де оператор IN становить 26%, а також, мабуть, і меншими кроками виконання.
MCP_infiltrator

3
@MCP_infiltrator Не потрібно, дивіться мої коментарі до вашого початкового допису вгорі. INне еквівалентний вашим ORs вище, оскільки інші умови вашого WHEREпункту в фактичному запиті. В основному запити дадуть різні результати.
JNK

3
@MCP_infiltrator На DBA.SE не потрібно публікувати однакове запитання, JNK відповів на нього (і ви отримаєте подібні відповіді там.) Якщо ви хочете перенести ("перенести") його там, хоча ви завжди можете позначити його (ваше запитання), вказуючи у полі для коментарів те, що ви хочете. Модники подбають.
ypercubeᵀᴹ

7

Найкращий спосіб сказати - подивитися власне план запитів, використовуючи щось подібне EXPLAIN. Це має точно сказати вам, що робить СУБД, і тоді ви можете набагато краще зрозуміти, чому це більш ефективно.

Зважаючи на це, системи СУБД справді добре виконують операції між двома таблицями (наприклад, приєднується). На ці частини запитів витрачається багато часу оптимізатора, оскільки вони, як правило, дорожчі.

Наприклад, СУБД може сортувати цей INсписок і, використовуючи індекс на item_desc, фільтрувати результати дуже швидко. Не можна робити цю оптимізацію, коли ви перераховуєте купу виділень, як у першому прикладі.

Під час використання INви створюєте імпровізовану таблицю та фільтруєте, використовуючи ці ефективніші методи комбінування таблиць.

EDIT : Цю відповідь я опублікував до того, як OP згадував конкретні СУБД. Виявляється, це НЕ так, як SQL Server ставиться до цього запиту, але може бути дійсним для інших систем СУБД. Дивіться відповідь JNK для більш конкретної, точної відповіді.


Я думаю, що кардинальність має багато спільного з цим. ЦеIN було б не так швидко, якби це був підбір з 100 записів у ньому, або тисяча.
Роберт Харві

@RobertHarvey Так, це, мабуть, так, але я не очікував, що це буде набагато гірше.
Олексі

Дякую @Oleksi Я не знав, що СУБД зробить оператор IN імпровізованим списком
MCP_infiltrator

1
-1 - У SQL Server INоператор не перетворюється в таблицю, він трактується ідентично серії ORs.
JNK

2
@ Katana314 Якби EXPLAIN було ключовим словом у SQL Server (який використовує ОП), я би погодився з вами, але це не так, це не актуально.
JNK
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.