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.

- РЕДАКТИРОВАТЬ - 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

Спасибо,

MCP_infiltrator
источник
10
Вы смотрели на план запроса?
1
Это ОЧЕНЬ конкретная реализация. Какую СУБД вы используете?
Джеймс Андерсон
Я не смотрел на план запроса, я не знал, был ли это конкретный запрос или это был факт, так как это всегда будет работать таким образом.
MCP_infiltrator
3
@MCP_infiltrator Таким образом, планы выполнения не будут эквивалентны, потому что логика не эквивалентна. При использовании, ORкак вы делаете в фактическом запросе выше, вы позволяете двигателю к короткому замыканию. WHERE A AND B OR Cбудет иметь значение true, даже если 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.

Я совершенно уверен, что если вы сгенерируете реальные планы выполнения для обоих ваших запросов, они будут идентичны.

По всей вероятности, второй запрос был выполнен быстрее, потому что вы запустили его вторым , а первый запрос уже извлек все страницы данных из базы данных и оплатил стоимость ввода-вывода. Второй запрос смог прочитать все данные из памяти и выполнить намного быстрее.

Обновить

Фактический источник отклонения, вероятно, что запросы не эквивалентны . У вас есть два разных 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 обрабатывается перед OR) означает, что фактическая логика, выполняемая механизмом:

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

Если вы замените ORсписки INвыражением, логика будет такой:

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

Что в корне отличается.

JNK
источник
2
@MCP_infiltrator Ну, в этом и заключается проблема с предположениями :) Вы действительно должны получить реальные планы для обоих и посмотреть, есть ли разница, я не думаю, что будет.
JNK
4
Хорошо, если у вас есть продвинутый вопрос о БД, вы также можете задать вопрос администраторам баз данных - полное раскрытие, я модератор, но если это сложный вопрос по оптимизации SQL или SQL, у нас есть масса экспертов, особенно для SQL Server
JNK
1
Я только что посмотрел на два плана выполнения, и они очень разные. Запрос с операторами OR занимает 68% затрат в Clustered Index Scan, где оператор IN равен 26%, а также, как представляется, меньше шагов выполнения.
MCP_infiltrator
3
@MCP_infiltrator Нет необходимости, смотрите мои комментарии к вашему оригинальному сообщению наверху. INне эквивалентно вашему ORвыше из-за других условий в вашем WHEREпредложении в фактическом запросе. В основном запросы будут давать разные результаты.
JNK
3
@MCP_infiltrator Нет необходимости размещать идентичный вопрос на DBA.SE, JNK ответил на него (и вы получите аналогичные ответы там.) Если вы все же хотите переместить («перенести») его туда, вы всегда можете пометить его (ваш вопрос) упоминание в поле для комментариев, что вы хотите. Мод будет заботиться.
ypercubeᵀᴹ
7

Лучший способ узнать это - посмотреть на фактический план запроса, используя что-то вроде EXPLAIN. Это должно точно сказать вам, что делает СУБД, и тогда вы сможете лучше понять, почему она более эффективна.

При этом системы СУБД действительно хороши для выполнения операций между двумя таблицами (например, объединениями). Много времени оптимизатора тратится на эти части запросов, потому что они обычно более дороги.

Например, СУБД может отсортировать этот INсписок и, используя индекс item_desc, очень быстро отфильтровать результаты. Вы не можете сделать эту оптимизацию, когда вы перечисляете кучу выборов, как в первом примере.

Когда вы используете IN, вы создаете импровизированную таблицу и фильтруете, используя эти более эффективные методы объединения таблиц.

РЕДАКТИРОВАТЬ : Я опубликовал этот ответ, прежде чем OP упомянул конкретные СУБД. Оказывается, это НЕ так, как SQL Server обрабатывает этот запрос, но может быть допустимым для других систем СУБД. См . Ответ JNK для более точного и точного ответа.

Oleksi
источник
Я полагаю, что кардинальность во многом связана с этим. Это INне было бы так быстро, если бы это был отбор с 100 записями или тысячами.
Роберт Харви
@RobertHarvey Да, это, вероятно, правда, но я бы не ожидал, что это будет намного хуже.
Олекси
Спасибо @Oleksi. Я не знал, что СУБД сделает оператор IN импровизированным списком
MCP_infiltrator
1
-1 - В SQL Server INоператор не преобразуется в таблицу, он обрабатывается идентично серии ORs.
JNK
2
@ Katana314 Если бы EXPLAIN были ключевым словом в SQL Server (который использует OP), я бы с вами согласился, но это не так, он не имеет значения.
JNK