带有“选择 x 不为空”的 SQL Server 视图需要很长时间才能完成

Posted

技术标签:

【中文标题】带有“选择 x 不为空”的 SQL Server 视图需要很长时间才能完成【英文标题】:SQL Server view with a 'select where x is not null' takes ages to complete 【发布时间】:2011-04-07 13:34:41 【问题描述】:

我有一个复杂的视图,这里描述:View of multiple tables. Need to remove "doubles" defined by 1 table

我在其中使用了Cross Apply,代码是这样的:(请查看上面的网址以了解视图)

    SELECT    dbo.InstellingGegevens.INST_SUBTYPE
            , dbo.InstellingGegevens.INST_BRON
            , dbo.InstellingGegevens.INST_INSTELLINGSNUMMER
            , dbo.InstellingGegevens.INST_NAAM
            , dbo.InstellingGegevens.INST_KORTENAAM
            , dbo.InstellingGegevens.INST_VESTIGINGSNAAM
            , dbo.InstellingGegevens.INST_ROEPNAAM
            , dbo.InstellingGegevens.INST_STATUUT
            , dbo.InstellingGegevens.ONDERWIJSNIVEAU_REF
            , dbo.InstellingGegevens.ONDERWIJSSOORT_REF
            , dbo.InstellingGegevens.DATUM_TOT
            , dbo.InstellingGegevens.DATUM_VAN
            , dbo.InstellingGegevens.VERBOND_REF
            , dbo.InstellingGegevens.VSKO_LID
            , dbo.InstellingGegevens.NET_REF
            , dbo.Instellingen.Inst_ID
            , dbo.Instellingen.INST_TYPE
            , dbo.Instellingen.INST_REF
            , dbo.Instellingen.INST_LOC_REF
            , dbo.Instellingen.INST_LOCNR
            , dbo.Instellingen.Opt_KalStandaard
            , dbo.InstellingTelecom.INST_TEL
            , dbo.InstellingTelecom.INST_FAX
            , dbo.InstellingTelecom.INST_EMAIL
            , dbo.InstellingTelecom.INST_WEB
            , dbo.InstellingAdressen.SOORT
            , dbo.InstellingAdressen.STRAAT
            , dbo.InstellingAdressen.POSTCODE
            , dbo.InstellingAdressen.GEMEENTE
            , dbo.InstellingAdressen.GEM_REF
            , dbo.InstellingAdressen.FUSIEGEM_REF
            , dbo.InstellingAdressen.FUSIEGEM
            , dbo.InstellingAdressen.ALFA_G
            , dbo.InstellingAdressen.PROVINCIE
            , dbo.InstellingAdressen.BISDOM
            , dbo.InstellingAdressen.ARRONDISSEMENT
            , dbo.InstellingAdressen.GEWEST
            , dbo.InstellingContPersDirecteurs.AANSPREKING
            , dbo.InstellingContPersDirecteurs.CONTACTPERSOON
            , dbo.InstellingContPersDirecteurs.FUNCTIE
            , InstellingLogin.Inst_Gebruikersnaam
            , InstellingLogin.Inst_Concode
            , InstellingLogin.Inst_DirCode
            , InstellingLogin.DOSSNR
            , InstellingLogin.Instelling_ID
FROM dbo.InstellingGegevens 
RIGHT OUTER JOIN dbo.Instellingen 
    ON dbo.InstellingGegevens.INST_TYPE = dbo.Instellingen.INST_TYPE 
    AND dbo.InstellingGegevens.INST_REF = dbo.Instellingen.INST_REF 
    AND dbo.InstellingGegevens.INST_LOC_REF = dbo.Instellingen.INST_LOC_REF
    AND dbo.InstellingGegevens.INST_LOCNR = dbo.Instellingen.INST_LOCNR 
LEFT OUTER JOIN dbo.InstellingTelecom 
    ON dbo.InstellingGegevens.INST_TYPE = dbo.InstellingTelecom.INST_TYPE 
    AND dbo.InstellingGegevens.INST_REF = dbo.InstellingTelecom.INST_REF 
    AND dbo.InstellingGegevens.INST_LOC_REF = dbo.InstellingTelecom.INST_LOC_REF 
LEFT OUTER JOIN dbo.InstellingAdressen 
    ON dbo.InstellingGegevens.INST_TYPE = dbo.InstellingAdressen.INST_TYPE 
    AND  dbo.InstellingGegevens.INST_REF = dbo.InstellingAdressen.INST_REF 
    AND  dbo.InstellingGegevens.INST_LOC_REF = dbo.InstellingAdressen.INST_LOC_REF 
LEFT OUTER JOIN dbo.InstellingContPersDirecteurs 
    ON dbo.InstellingGegevens.INST_TYPE = dbo.InstellingContPersDirecteurs.INST_TYPE 
    AND dbo.InstellingGegevens.INST_REF = dbo.InstellingContPersDirecteurs.INST_REF 
    AND dbo.InstellingGegevens.INST_LOC_REF = dbo.InstellingContPersDirecteurs.INST_LOC_REF 
CROSS APPLY
      (SELECT  TOP (1) *
        FROM  InstellingLogin AS il
        WHERE Instellingen.INST_LOC_REF = il.Inst_Loc_REF 
            AND Instellingen.INST_LOCNR = il.Inst_Loc_Nr 
            AND Instellingen.INST_REF = il.Inst_InstellingIKON_REF 
            AND Instellingen.INST_TYPE = il.Inst_InstellingIKONType
        ORDER BY CASE 
                    WHEN il.datum_tot IS NULL 
                    THEN 0 ELSE 1 
                    END
                    , il.datum_tot DESC) InstellingLogin

这个视图在大约 1 秒内返回了大约 5.5k 行。这很快!

但是!

当我用 where 子句调用这个视图时:

SELECT *
  FROM [Tink].[dbo].[InstellingAlleDetails]
  where gemeente is not null and (DATUM_TOT is null or DATUM_TOT > GETDATE())
  order by GEMEENTE, POSTCODE,STRAAT, INST_NAAM

返回所有行需要 1 分 20 秒。

当我删除 gemeente is not null 部分时,它又需要 1 秒。

Gemeente 是一个 varchar(255)。我还用Inst_Naam is not null 尝试过,也花了大约 1 分 30 秒。

为什么is not null 需要这么长时间?更重要的是:我该如何解决这个问题?

【问题讨论】:

Inst_Naam 没有出现在此视图中...正如 HLGEM 所说,您确实需要运行两个查询(有/没有 is null 条件),并比较执行计划。如果您可以确定差异,请将它们添加到您的问题中。 【参考方案1】:

我不知道为什么。可能 SQL Server 提出了一个不太好的查询计划。

您可以尝试先在没有gemeente is not null 的情况下运行查询并将结果放入临时表中,然后使用gemeente is not null 查询临时表。

select *
into #TempTable
from YourView

select *
from #TempTable
where gemeente is not null

drop table #TempTable

【讨论】:

所以这意味着创建一个表,或者有一个简单的方法来创建一个临时表? @Stefanvds - 用一些创建临时表的代码更新了答案,使用它并在完成后删除它。【参考方案2】:

首先检查查询的执行计划是否为空,并查看差异。

顺便说一句,这些是否加入了其他视图?这可能会导致巨大的性能问题。

【讨论】:

是的,InstellingContPersDirecteurs 是一个观点,但这不是我认为的问题。没有cross apply,它的工作速度很快(1s)我想它与cross apply有关 我怀疑是CROSS APPLY。对于如此复杂的查询(多个视图、外部连接、交叉应用,它们都加起来),您可能只是达到了这样一个点,即优化器试图从“IS NOT NULL”谓词中获得好处会导致错误的结论。将结果插入中间表,然后处理该中间表,是我个人发现的防止这种情况发生的唯一方法。

以上是关于带有“选择 x 不为空”的 SQL Server 视图需要很长时间才能完成的主要内容,如果未能解决你的问题,请参考以下文章

带有内部联接的 SQL Server 更新

带有服务的 SQL Server 的模拟/外观?

带有 SQL Server 2019 Express 的 Windows Server 2019 上的 Hangfire

Sql Server关于create index include带有包含列的索引

带有SQL Server数据库错误的C#Game Server

带有时间戳和变量的 SQL Server 查询性能