我正在处理今天编写的查询,必须更改代码 WHERE 子句使用 IN(list of stuff) 过滤器而不是使用类似的东西

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

谢谢你,

有帮助吗?

解决方案

奥列斯基的回答是不正确的。对于 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 在 OR 之前处理)意味着引擎运行的实际逻辑是:

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

如果您更换 OR 列出带有 IN 表达式,逻辑将是:

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

这是完全不同的。

其他提示

最好的判断方法是使用类似的东西查看实际的查询计划 EXPLAIN. 。这应该准确地告诉您 DBMS 正在做什么,然后您可以更好地了解为什么它更高效。

话虽如此,DBMS 系统确实擅长在两个表之间执行操作(例如联接)。优化器的大量时间都花在查询的这些部分上,因为它们通常更昂贵。

例如,DBMS 可以对它进行排序 IN 列出并使用索引 item_desc, ,非常快速地过滤结果。当您像第一个示例中那样列出一堆选择时,您无法进行优化。

当你使用 IN, ,您正在制作一个临时表格并使用这些更有效的表格组合技术进行过滤。

编辑:我在OP提到具体的DBMS之前发布了这个答案。事实证明,这不是 SQL Server 处理此查询的方式,但可能对其他 DBMS 系统有效。看 JNK的回答 以获得更具体、准确的答案。

许可以下: CC-BY-SA归因
scroll top