在C#Web应用程序中插入查询时间,从SQL Server Management Studio运行正常

Rus*_*uss 14 c# sql-server asp.net

我正在尝试从我的C#Web应用程序运行插入查询.当我从SQL Server Management Studio运行查询时,插入查询大约需要五分钟才能完成.从应用程序运行时,它会在30分钟后超时(是分钟,而不是秒).

我从VS调试器中获取了实际的SQL语句并从Mgmt Studio运行它并且工作正常.

所有这些都是从我的开发环境运行,而不是生产环境.查询正在进行时,没有其他SQL Server活动.我正在使用SQL Server 2008 R2进行开发.MS VS 2010 Express,Asp.Net 4.0.SQL Server Mgmt Studio 10.

有一个类似的问题从未得到解答:来自C#.NET的SQL server timeout 2000

这是来自以下的SET选项:dbcc useroptions

Option                  MgtStudio      Application
----------------------- -------------- --------------
textsize                2147483647     -1
language                us_english     us_english
dateformat              mdy            mdy
datefirst               7              7
lock_timeout            -1             -1
quoted_identifier       SET            SET
arithabort              SET            NOT SET
ansi_null_dflt_on       SET            SET
ansi_warnings           SET            SET
ansi_padding            SET            SET
ansi_nulls              SET            SET
concat_null_yields_null SET            SET
isolation level         read committed read committed
Run Code Online (Sandbox Code Playgroud)

只有文本化和arithabort是不同的.

任何想法为什么在查询执行时间有这么大的差异,以及我可以做些什么来缩小这种差异?

我不确定查询会有多大用处,特别是因为包含模式会太多.无论如何,这里是:

INSERT INTO GeocacherPoints
            (CacherID,
             RegionID,
             Board,
             Control,
             Points)
SELECT z.CacherID,
       z.RegionID,
       z.Board,
       21,
       z.Points
FROM   (SELECT CacherID,
               gp.RegionID,
               Board=gp.Board + 10,
               ( CASE
                   WHEN (SELECT COUNT(*)
                         FROM   Geocache g
                                JOIN GeocacheRegions r
                                  ON ( r.CacheID = g.ID )
                         WHERE  r.RegionID = gp.RegionID
                                AND g.FinderPoints >= 5) < 20 THEN NULL
                   ELSE (SELECT SUM(y.FinderPoints) / 20
                         FROM   (SELECT x.FinderPoints,
                                        ROW_NUMBER() OVER (ORDER BY x.FinderPoints DESC, x.ID) AS Row
                                 FROM   (SELECT g.FinderPoints,
                                                g.ID
                                         FROM   Geocache g
                                                JOIN Log l
                                                  ON ( l.CacheID = g.ID )
                                                JOIN Geocacher c
                                                  ON ( c.ID = l.CacherID )
                                                JOIN GeocacheRegions r
                                                  ON ( r.CacheID = g.ID )
                                         WHERE  YEAR(l.LogDate) = @Year
                                                AND g.FinderPoints >= 5
                                                AND c.ID = gp.CacherID
                                                AND r.RegionID = gp.RegionID) x) y
                         WHERE  y.Row <= 20)
                 END ) Points
        FROM   GeocacherPoints gp
               JOIN Region r
                 ON r.RegionID = gp.RegionID
        WHERE  gp.Control = 21
               AND r.RegionType IN ( 'All', 'State' )
               AND gp.Board = @Board - 10) z
WHERE  z.Points IS NOT NULL
       AND z.Points >= 1 
Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 7

ARITHABORT 经常被误诊为原因.

事实上,自2005年版本ANSI_WARNINGS开启以来(因为它在你的两个连接中)ARITHABORT无论如何都是隐含的,并且这个设置没有实际效果.

但它确实有副作用.允许ANSI_WARNINGS不关闭设置的ARITHABORT情况用作计划缓存键之一,这意味着具有不同设置的会话无法共享彼此的计划.

当您在SSMS中运行查询时,无法重用为您的应用程序缓存的执行计划,除非它们都具有相同的计划缓存密钥,因此它会获得一个新的计划,该计划会"嗅探"当前正在测试的参数值.您的应用程序计划可能是针对不同的参数值编译的.此问题称为"参数嗅探".

您可以检索并比较两个执行计划

SELECT usecounts, cacheobjtype, objtype, text, query_plan, value as set_options
FROM sys.dm_exec_cached_plans 
CROSS APPLY sys.dm_exec_sql_text(plan_handle) 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) 
cross APPLY sys.dm_exec_plan_attributes(plan_handle) AS epa
where text like '%INSERT INTO GeocacherPoints (CacherID,RegionID,Board,Control,Points)%' 
and attribute='set_options' and text not like '%this query%'
Run Code Online (Sandbox Code Playgroud)

XML中的参数部分告诉您参数的编译时间值.

请参阅应用程序中的慢速,SSMS中的快速?更多地了解性能之谜.

执行计划

您已经提供了估计的执行计划而不是实际的执行计划,但可以看出只有第一个查询计划被参数化并且它是针对以下值编译的.

        <ParameterList>
          <ColumnReference Column="@Dec31" ParameterCompiledValue="'2013-12-31'" />
          <ColumnReference Column="@Jan1" ParameterCompiledValue="'2013-01-01'" />
          <ColumnReference Column="@Board" ParameterCompiledValue="(71)" />
        </ParameterList>
Run Code Online (Sandbox Code Playgroud)

第二个执行计划使用变量而不是参数.这显着改变了事情.

DECLARE @Board INT
DECLARE @Jan1 DATE
DECLARE @Dec31 DATE

SET @Board=71
SET @Jan1='January 1, 2013'
SET @Dec31='December 31, 2013'

INSERT INTO GeocacherPoints
Run Code Online (Sandbox Code Playgroud)

SQL Server不会嗅探变量的特定值,并生成类似于使用OPTIMIZE FOR UNKNOWN提示的通用计划.该计划中的估计行数远远高于第一个计划中的行数.

你没有说明哪个是快速计划,哪个是缓慢的计划.如果使用变量的那个更快,那么您可能需要更新统计信息,您可能会遇到此处描述的问题统计,行估计和升序日期列如果使用参数的那个更快,那么您将能够实现变量嗅探并获得它通过使用OPTION (RECOMPILE)提示来考虑实际的变量值.