SQL存储过程从应用程序而不是从SSMS(Azure SQL)执行需要数小时

SQL存储过程从应用程序而不是从SSMS(Azure SQL)执行需要数小时,sql,sql-server,azure-sql-database,Sql,Sql Server,Azure Sql Database,我们有一个存储过程,当在.NET代码中从ExecuteOnQuery调用时,它在Azure SQL server上执行需要一个多小时。当存储过程的代码通过SSM执行时,只需6.5分钟 下面是它包含的SQL代码片段(为隐私而重命名的表): TableBookProducts只有1000多万行,该联合的结果集是800万行。在SSMS中执行该语句需要6.5分钟 我们在所有表格上都有索引。ISBN13是主键(聚集),PID是非聚集索引 该语句的编译时间最长为4.9秒,看起来不像是参数嗅探。此存储过程中没

我们有一个存储过程,当在.NET代码中从ExecuteOnQuery调用时,它在Azure SQL server上执行需要一个多小时。当存储过程的代码通过SSM执行时,只需6.5分钟

下面是它包含的SQL代码片段(为隐私而重命名的表):

TableBookProducts只有1000多万行,该联合的结果集是800万行。在SSMS中执行该语句需要6.5分钟

我们在所有表格上都有索引。ISBN13是主键(聚集),PID是非聚集索引

该语句的编译时间最长为4.9秒,看起来不像是参数嗅探。此存储过程中没有硬编码的值或参数

我试着在SSMS中将ARITHABORT设置为OFF和ON,这没有什么区别

是什么导致ExecuteOnQuery持续时间过长

服务器是否已损坏?在SQL Server上可以做些什么来提高性能

  • 避免在insert查询中使用UNION
  • 如果四个表还没有索引,则在它们上创建索引
  • 使用两个insert查询而不是一个。 插入(临时表)从第一个查询中选择数据 插入(临时表)从不存在的第二个查询中选择数据

  • union语句中的两个查询是否应在isbn编号上合并?第一个连接在isbn编号上,第二个连接在PID上。我假设您的查询是正确的。

    运行以下查询以获取错误查询计划的句柄:

    ;WITH x AS 
    (
        SELECT
            qs.[sql_handle], qs.plan_handle,
            txs = qs.statement_start_offset, 
            txe = qs.statement_end_offset,
            [size] = cp.size_in_bytes, 
            [uses] = SUM(cp.usecounts), 
            [last] = MAX(qs.last_execution_time)
        FROM 
            sys.dm_exec_query_stats AS qs
        INNER JOIN 
            sys.dm_exec_cached_plans AS cp 
            ON qs.plan_handle = cp.plan_handle
        GROUP BY 
            qs.[sql_handle], qs.plan_handle, cp.size_in_bytes,
            qs.statement_start_offset, qs.statement_end_offset
    ) 
    SELECT 
        x.plan_handle, 
        size, uses, [last],
        [statement] = COALESCE(NULLIF(
            SUBSTRING(t.[text], x.txs/2, 
              CASE WHEN x.txe = -1 THEN 0 ELSE (x.txe - x.txs)/2 END
              ), ''), t.[text]) 
    FROM x 
    CROSS APPLY sys.dm_exec_sql_text(x.[sql_handle]) AS t
    WHERE (t.text LIKE '%UNION%' AND t.text LIKE '%CompetitorAggregation%');
    
    然后,数据库服务器上的管理员运行以下操作:

    DBCC FREEPROCCACHE (<plan_handle from above query>)
    
    EXEC sp_recompile [<name of the stored procedure>]
    
    IF OBJECT_ID('tempdb..#ProductAggregation') IS NOT NULL DROP TABLE [#ProductAggregation];
    CREATE  TABLE [#ProductAggregation](
    PID decimal(12,0) NOT NULL,
    ISBN13 varchar(13) NOT NULL,
    CompetitorAggregationId varchar(32) NULL,
    EBookWorkId int NULL,
    ProductAggregationId varchar(32) NULL
    
    PRIMARY KEY CLUSTERED (PID, ISBN13)
    );
    
    我发现了一些有趣的东西。插入临时表的非聚集索引的成本非常高。因此,我将临时表修改为以下内容:

    DBCC FREEPROCCACHE (<plan_handle from above query>)
    
    EXEC sp_recompile [<name of the stored procedure>]
    
    IF OBJECT_ID('tempdb..#ProductAggregation') IS NOT NULL DROP TABLE [#ProductAggregation];
    CREATE  TABLE [#ProductAggregation](
    PID decimal(12,0) NOT NULL,
    ISBN13 varchar(13) NOT NULL,
    CompetitorAggregationId varchar(32) NULL,
    EBookWorkId int NULL,
    ProductAggregationId varchar(32) NULL
    
    PRIMARY KEY CLUSTERED (PID, ISBN13)
    );
    
    希望这能加快速度。与插入非聚集索引IX_tmpProductAggregation_PID相关的子树成本(约4000)很大

    更新(3):完成上述优化后,我们的执行时间减少到8分钟

    更新(4):即使进行了上述优化,我们有时也会得到长达1小时的运行时间

    因此,我添加了一个与目标表的左连接,以及源(CompetitorAggregation)和目标(ProductAggregation)之间的DateChanged比较,以将数据集缩小到只包含自上次运行以来更改的记录。这将运行时间减少到1.5分钟。我们必须进行测试,看看是否会再次出现1小时的峰值或其他回归

    更新(5):我也找到了摆脱工会的方法。我已将其分为两个insert语句,并更改了主键,如下所示:

    PRIMARY KEY CLUSTERED (PID, ISBN13) WITH (IGNORE_DUP_KEY = ON)
    

    这将消除查询计划中的独特排序操作。

    创建统计信息可能会导致此类延迟;一个执行计划可能不会触发此类统计数据的创建,而另一个执行计划则会触发。有关应用程序和SSM之间执行计划可能不同的不同原因的详细说明,请参阅(并非专门针对Azure SQL定制,但由于不同设置而导致的执行计划更改适用于所有版本的SQL Server)。我确实看过那篇文章,谢谢。这就是我想到要查看查询计划的地方。另外,我的存储过程没有参数,所以我不认为它是参数嗅探。你有异步统计数据更新吗?您是否尝试过清除计划缓存(
    DBCC FREEPROCCACHE
    )?为什么要使用
    NOLOCK
    ?您是否考虑过将查询重写为带有两个左连接的单个
    BookProducts
    引用?6.5分钟可以吗?这件事看起来很糟糕,所以一个小时我都做不到。除其他建议外,您可以尝试使用UNION ALL而不是UNION,但除非您能向我们展示在每种情况下生成的执行计划,否则我们无法真正提供帮助。我将尝试
    DBCC FREEPROCCACHE
    。问题是,这个存储过程过去需要10-15分钟,直到昨天才部署了一个小更改(甚至与此SQL语句无关)。此语句已经几个月甚至一年没有更改。如果有一个好的计划和一个坏的计划,查询存储应该能够选择/固定好的计划。