null 是否比 iif(exists) 更好?

Kor*_*esh 2 sql-server sql-server-2014

我试图减少我们应用程序数据库上的巨大性能问题。我们使用 SQL Server 2014,我们有很多视图。

我想知道重写左连接以在预测中使用存在是否有重大影响?

我的问题是:在构建根据相关匹配项的存在投射不同数据的查询时,是否有任何有关性能的最佳实践?

我的意思是,这些查询中的哪一个可以被认为更好?

1):

select
        a.Id,
        case when b.Id is not null 
             then convert(bit,1) 
             else convert(bit,0)
        end IsActive
from A a
left join B b on a.Id = b.Id;
Run Code Online (Sandbox Code Playgroud)

2):

select
        a.Id,
        iif(exists(select top 1 1 from B where Id = a.Id),
            convert(bit,1), 
            convert(bit,0)
           )  IsActive
from A a;
Run Code Online (Sandbox Code Playgroud)

顺便说一句,如果您对查询的形式有任何意见或一般性建议,请随时发表评论。

ype*_*eᵀᴹ 7

首先,这两个查询仅当且仅当 上存在UNIQUE或 主键约束时才等效B (id)。如果没有,那么使用一个而不是另一个或比较它们的效率是没有意义的。

现在,假设有这样的约束,问题是关于CASE和 的EXISTSIIFVSCASE选择正交于LEFT JOINVSEXISTS子查询的选择。意思是,我们可以轻松地用 重写第一个查询,IIF()CASE. 看:

(顺便说一下,我删除了,TOP 1因为它在EXISTS子查询中是多余的)

-- query 2a
select
        a.Id,
        case when exists (select 1 from B where Id = a.Id)
             then convert(bit,1) 
             else convert(bit,0)
        end IsActive
from A a ;
Run Code Online (Sandbox Code Playgroud)

还有第三种方法来重写查询,类似于第二个查询,使用相关子查询但没有EXISTS. 这也可以用CASE或来写,IIF()但我只在case这里写:

-- query 3
select
        a.Id,
        case when (select B.Id from B where B.Id = a.Id) is not null 
             then convert(bit,1) 
             else convert(bit,0)
        end IsActive
from A a ;
Run Code Online (Sandbox Code Playgroud)

所以我们有 3 种方法来编写查询(如果我们计算 IIF 版本,则有 6 种)并且可能还有更多的方法。OUTER APPLY想到使用,所以让我们有第四种方式:

-- query 4
select
        a.Id,
        case when b.Id is not null 
             then convert(bit,1) 
             else convert(bit,0)
        end IsActive
from A a 
  outer apply (select B.Id from B where B.Id = a.Id) b ;
Run Code Online (Sandbox Code Playgroud)

真正的问题当然是哪个更有效。关于CASEvsIIF()可能根本没有区别,两种方式在所有 4 种情况下都产生相同的计划。IIF()已添加到最新版本的 SQL Server 中,但无法执行CASE. 所以我找不到它的任何真正用途,除非对来自 MS-Access 的开发人员进行友好的查询。

不过,对于 4 种不同的方式,没有比自己测试更好的答案了!在你的环境中,你的表/视图、你的数据和大小、你的硬件、你的设置等等。所有这些都可能以一种或另一种方式很重要。

如果我在没有测试的情况下盲目猜测,我会选择这种LEFT JOIN方式,因为相关子查询(如EXISTS第 3 个子查询)可能并不总是产生最佳计划。优化器非常聪明地理解所有查询都是等效的,但这也取决于查询的复杂性。如果AB是基表,好的。但如果它们是复杂的视图,复杂的视图...,那么它可能不是。

如果您打算将此查询定义为视图,然后在这样的查询中使用它:

select * 
from my_view
where IsActive = convert(bit, 0) ;
Run Code Online (Sandbox Code Playgroud)

您可能会遇到性能问题。将值隐藏到视图内的计算列中将禁止优化器使用基础表上的索引。