SQL IN()与OR


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

谢谢,


10
您看过查询计划了吗?

1
这是非常特定的实现。您正在使用哪个DBMS?
James Anderson

我没有查看查询计划,也不知道这是特定于查询还是事实,因为这样做总是可以的。
MCP_infiltrator

3
@MCP_infiltrator因此执行计划将不等效,因为逻辑不等效。当OR像在上面的实际查询中一样使用时,您可以使引擎短路。 WHERE A AND B OR C即使A AND B为假,如果C为真,也会取值为true。如果您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中指定的操作符优先于ANDbefore进行处理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'如果最后三个条件中的任何一个满足,则它将能够使其余评估短路。
JNK

Answers:


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在OR之前进行处理)意味着引擎运行的实际逻辑是:

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

如果ORIN表达式替换列表,则逻辑将是:

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

根本不同。


2
@MCP_infiltrator假设是问题所在:)您真的应该为两者都制定实际的执行计划,看看是否存在差异,我认为不会有差异。
JNK

4
好吧,如果您有高级数据库问题,也可以向数据库管理员询问 -全面披露,我是那里的主持人,但是如果是高级SQL或SQL优化问题,我们有大量专家,尤其是SQL Server
JNK

1
我只是看了两个执行计划,它们却截然不同。使用OR语句的查询在“聚集索引扫描”中占了68%的成本,其中IN语句为26%,以及似乎更少的执行步骤。
MCP_infiltrator

3
@MCP_infiltrator不需要,请在顶部查看我对原始帖子的评论。 由于实际查询中子句中的其他条件,因此IN与您的ORs 不相等WHERE。基本上,查询将返回不同的结果。
JNK

3
@MCP_infiltrator无需在DBA.SE上发布相同的问题,JNK已回答了该问题(您将在那得到类似的答案。)如果您确实想将其移动(“迁移”)到那里,则可以随时对其进行标记(您的问题)在评论框中提及您想要的内容。国防部将照顾。
ypercubeᵀᴹ

7

最好的判断方法是使用来查看实际的查询计划EXPLAIN。这应该确切地告诉您DBMS在做什么,然后您可以更好地了解为什么它更有效。

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

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

当您使用时IN,您将制作临时表并使用这些更有效的表组合技术进行过滤。

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


我想基数与它有很大关系。IN如果它是其中包含100条记录或1000条记录的子选择,那将不会很快。
罗伯特·哈维

@RobertHarvey是的,这可能是对的,但我也不希望它变得更糟。
Oleksi 2013年

谢谢@Oleksi,我不知道DBMS会将IN语句作为即席列表
MCP_infiltrator

1
-1-在SQL Server中,该IN语句未转换为表,它与一系列ORs 相同。
JNK

2
@ Katana314如果EXPLAIN是SQL Server(OP使用的)中的关键字,我会同意您的看法,但这不是完全无关的。
JNK
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.