在SQL Server上查询视图时出现性能问题

3

我目前遇到一些与 SQL Server 2016 Enterprise 相关的性能问题,存在一些奇怪的行为。

我在数据库中创建了一个新模式,然后在此模式内创建了一个视图。

现在,当我直接连接到包含此模式和视图的数据库,并编写一个简单的查询,例如:

SELECT * FROM SCHEMA.VIEW

完成这个过程需要大约30分钟(!)。完全限定的查询也是如此。

SELECT * FROM DB_NAME.SCHEMA.VIEW

现在,如果我先将数据库更改为主数据库或其他用户数据库,然后再在所有数据库中运行查询,它会在大约10秒内完成(!)。两个数据库的数据库属性以及用于数据库文件和日志文件的驱动器都是相同的。

有人知道是什么原因导致了这种巨大的性能问题吗?

我在视图中使用了以下代码:

CREATE VIEW [controlling].[UnitLoginHistory]
AS

    WITH    cte 
            ( 
                    Jahr, UnityId, Unit_UnityId, Analytical_Code, Unit_Code, Unit_Name, Active
            ,       Show_in_org_chart, Begin_Date, End_Date, Unit_Owner_First_Name, Unit_Owner_Last_Name
            ,       Unit_Owner_Login, Unit_Owner_CorporateID, UnityParentId, UnityTypeId, Unit_Level
            ,       Magnitude_Code, ImportDate, ReplicationLevel 
            ) 
    AS
    (
            SELECT
                    dt.Jahr
            ,       a.UnityId 
            ,       a.UnityId 
            ,       a.Analytical_Code 
            ,       a.Unit_Code 
            ,       a.Unit_Name 
            ,       cast(case when a.Active = 'True' then 1 else 0 end as bit)
            ,       a.Show_in_org_chart 
            ,       a.Begin_Date 
            ,       a.End_Date 
            ,       a.Unit_Owner_First_Name 
            ,       a.Unit_Owner_Last_Name 
            ,       a.Unit_Owner_Login 
            ,       a.Unit_Owner_CorporateID 
            ,       a.UnityParentId 
            ,       a.UnityTypeId 
            ,       a.Unit_Level 
            ,       a.Magnitude_Code
            ,       a.ImportDate
            ,       1
            FROM    [Staging_INPUT].[DBO].[OBS_Workunit]    a
            JOIN    (
                            SELECT
                                    YEAR(IMPORTDATE)        Jahr
                            ,       MAX(IMPORTDATE)         Datum
                            FROM    [Staging_INPUT].[DBO].[OBS_Workunit]
                            GROUP   BY
                                    YEAR(IMPORTDATE) 
                    )                                       dt
            ON      a.ImportDate                    =       dt.datum
            WHERE   a.unitytypeid                   =       12
            UNION   ALL
            SELECT
                    b.Jahr
            ,       b.UnityId 
            ,       a.UnityId 
            ,       b.Analytical_Code 
            ,       a.Unit_Code 
            ,       a.Unit_Name 
            ,       cast(case when a.Active = 'True' then 1 else 0 end as bit)
            ,       a.Show_in_org_chart 
            ,       a.Begin_Date 
            ,       a.End_Date 
            ,       a.Unit_Owner_First_Name 
            ,       a.Unit_Owner_Last_Name 
            ,       a.Unit_Owner_Login      
            ,       a.Unit_Owner_CorporateID 
            ,       a.UnityParentId 
            ,       a.UnityTypeId   
            ,       a.Unit_Level 
            ,       a.Magnitude_Code
            ,       a.ImportDate 
            ,       b.ReplicationLevel + 1
            FROM    [Staging_INPUT].[DBO].[OBS_Workunit]    a
            JOIN    cte                                     b
            ON      a.UnityId                       =       b.UnityParentId
            AND     a.ImportDate                    =       b.ImportDate
            AND     a.UnityTypeId                   >=      6 
    )
    ,       Company
    AS
    (
            SELECT  DISTINCT 
                    Jahr
            ,       UnityId
            ,       LEFT(REPLACE(Magnitude_Code,'XE','U'),4) CompanyUID
            FROM    cte
            WHERE   UnityTypeId     =       7
            AND     Active          =       1

    )
    ,       BUs 
    AS
    (
            SELECT  DISTINCT 
                    a.Jahr                          JAHR
            ,       a.UnityId                       UnityId
            ,       c.Analytical_Code               BU_CODE
            ,       c.Unit_Name                     BU_NAME
            ,       b.CompanyUID
            FROM    cte                     a

            JOIN    Company                 b
            ON      a.Jahr          =       b.Jahr
            AND     a.UnityId       =       b.UnityId
            AND     a.Active        =       1

            JOIN    cte                     c
            ON      a.Jahr          =       c.Jahr
            AND     a.UnityId       =       c.Unit_UnityId
            AND     c.Active        =       1

            WHERE   ISNULL(c.Analytical_Code,'') != ''
    )
    SELECT  DISTINCT
            a.JAHR                          JAHR
    ,       a.BU_CODE                       BU_CODE
    ,       a.BU_NAME                       BU_NAME
    ,       'EUROPE\'
    +       b.Unit_Owner_Login              BU_LOGIN
    ,       b.Unit_Owner_Last_Name 
    +       ', '
    +       b.Unit_Owner_First_Name         BU_LOGIN_NAME
    ,       a.CompanyUID                    COMPANY_UID
    FROM    BUs                             a

    JOIN    cte                             b
    ON      a.Jahr                  =       b.Jahr
    AND     a.UnityId               =       b.UnityId
    AND     b.Active                =       1

    UNION   

    SELECT  DISTINCT
            a.Jahr                          JAHR
    ,       a.BU_CODE                       BU_CODE
    ,       a.BU_NAME                       BU_NAME
    ,       c.BU_LOGIN                      BU_LOGIN
    ,       c.BU_LOGIN_NAME                 BU_LOGIN_NAME
    ,       a.CompanyUID                    COMPANY_UID
    FROM    BUs                             a

    CROSS   JOIN (
                    SELECT  DISTINCT
                            [Last Name]     COLLATE Latin1_General_100_CI_AS
                    +       ', '
                    +       [First Name]    COLLATE Latin1_General_100_CI_AS BU_LOGIN_NAME   
                    ,       'EUROPE\'
                    +       [User Id]       COLLATE Latin1_General_100_CI_AS BU_LOGIN  
                    FROM    NAVISION.dbo.Employee
                    WHERE   [Global Dimension 2 Code]       IN      (
                                                                            10061
                                                                    ,       10062
                                                                    )
                    AND     ISNULL([User Id],'')            !=      ''
                 )                          c

GO 

执行时间和统计数据:

use NAVISION

GO

set statistics io on 
set statistics time on

select * 
from NAVISION.dbo.UnitLoginHistory 
where Jahr = 2017 
order by 1,2

SQL Server解析和编译时间: CPU时间 = 0毫秒,经过时间 = 0毫秒。

(共影响34119行) 表'Worktable'。扫描计数1607,逻辑读取253696,物理读取0,预读取读取1238,大型对象逻辑读取0,大型对象物理读取0,大型对象预读取读取0。 表'U415 Altran Engineering GmbH$Employee'。扫描计数1,逻辑读取128,物理读取0,预读取读取0,大型对象逻辑读取0,大型对象物理读取0,大型对象预读取读取0。 表'U388 Altran Aviation GmbH$Employee'。扫描计数1,逻辑读取42,物理读取0,预读取读取0,大型对象逻辑读取0,大型对象物理读取0,大型对象预读取读取0。 表'U354 Altran Service GmbH$Employee'。扫描计数1,逻辑读取210,物理读取0,预读取读取0,大型对象逻辑读取0,大型对象物理读取0,大型对象预读取读取0。 表'U353 AIH Holding GmbH Co KG$Employee'。扫描计数1,逻辑读取934,物理读取0,预读取读取0,大型对象逻辑读取0,大型对象物理读取0,大型对象预读取读取0。 表'OBS_Workunit'。扫描计数46286,逻辑读取10430933,物理读取0,预读取读取0,大型对象逻辑读取0,大型对象物理读取0,大型对象预读取读取0。

SQL Server执行时间: CPU时间 = 1363546毫秒,经过时间 = 1455980毫秒。

use Staging_INPUT
GO

set statistics io on 
set statistics time on

select * 
from NAVISION.dbo.UnitLoginHistory 
where Jahr = 2017 
order by 1,2

SQL Server解析和编译时间: CPU时间 = 0毫秒,经过时间 = 0毫秒。

(共34119行受影响) 表“工作表”。扫描计数582,逻辑读取576096,物理读取0,预读取146,lob逻辑读取0,lob物理读取0,lob预读取读取0。 表“工作文件”。扫描计数0,逻辑读取0,物理读取0,预读取0,lob逻辑读取0,lob物理读取0,lob预读取读取0。 表“OBS_Workunit”。扫描计数53573,逻辑读取485656,物理读取0,预读取0,lob逻辑读取0,lob物理读取0,lob预读取读取0。 表“U415 Altran Engineering GmbH$Employee”。扫描计数1,逻辑读取128,物理读取0,预读取0,lob逻辑读取0,lob物理读取0,lob预读取读取0。 表“U388 Altran Aviation GmbH$Employee”。扫描计数1,逻辑读取42,物理读取0,预读取0,lob逻辑读取0,lob物理读取0,lob预读取读取0。 表“U354 Altran Service GmbH$Employee”。扫描计数1,逻辑读取210,物理读取0,预读取0,lob逻辑读取0,lob物理读取0,lob预读取读取0。 表“U353 AIH Holding GmbH Co KG$Employee”。扫描计数1,逻辑读取934,物理读取0,预读取0,lob逻辑读取0,lob物理读取0,lob预读取读取0。

SQL Server执行时间: CPU时间 = 15047毫秒,经过时间 = 28007毫秒。


3
发布您的视图代码。 - user1529235
这似乎相当复杂。 - Evaldas Buinauskas
这是一个非常复杂的查询。第一次运行时间较长,可能是因为系统在寻找正确的执行计划。换句话说,您更改了数据库后,查询似乎可以正常运行。您是否尝试在同一数据库上运行两次以确保没有编译时间成分? - Xedni
1
索引是否已经建立?如果您能够发布执行计划,那就太好了。 - Evaldas Buinauskas
1
所有数据库的排序规则都一样吗?为什么在你的视图中使用“COLLATE Latin1_General_100_CI_AS”? - Alex
显示剩余12条评论
1个回答

0

不同的性能必须归因于不同的执行计划。由于计划取决于数据库上下文而有所不同,这表明影响计划的设置也不同。有许多默认的 SET 选项和属性可以根据数据库而变化,您可能错过了其中一两个。

我建议为这两个数据库生成 CREATE DATABASE 脚本并进行比较。

编辑:

数据库兼容级别 设置的差异可能会影响执行计划。对于处于 110(SQL Server 2012)级别的数据库,SQL Server 将使用旧的基数估算器,而对于处于 120 及更高版本的数据库,除非打开 LEGACY_CARDINALITY_ESTIMATION 数据库范围设置,否则将使用更新的 CE。


@CodingYoshi,为什么这不能是一个答案呢?只有不同的计划才会导致这些症状,而在所有其他条件相等的情况下,不同数据库上下文的计划必须是由于数据库设置/属性引起的,据我所知。 - Dan Guzman
CREATE DATABASE脚本仅在兼容级别上有所不同,NAVISION为130,Staging_INPUT为110,这意味着NAVISION使用SQL Server 2016,而Staging_INPUT使用SQL Server 2012。另一个区别是恢复模式,NAVISION设置为SIMPLE,而Staging_INPUT设置为FULL。所有其他选项完全相同。 - Stefan Corsten
@StefanCorsten,兼容性水平很重要。我在我的回答中添加了更多信息。 - Dan Guzman
嗨,丹,将LEGACY_CARDINALITY_ESTIMATION设置为ON后,NAVISION的性能从大约30分钟降至大约2分钟。在Staging_INPUT上仍然保持在大约10秒左右。这意味着性能有所提高,但仍不相同,也不是最好的... - Stefan Corsten
NavisionStaging_INPUT - Stefan Corsten
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接