【问题标题】:Generated LinqtoSql Sql 5x slower than SAME EXACT hand-written sql生成 LinqtoSql Sql 比 SAME EXACT 手写 sql 慢 5x
【发布时间】:2011-02-19 03:02:15
【问题描述】:

我有一个在现有 VB6 应用程序中硬编码的 sql 语句。我正在升级 C# 中的新版本并使用 Linq To Sql。我能够让 LinqToSql 生成相同的 sql(在我开始重构之前),但由于某种原因,LinqToSql 生成的 Sql 比原始 sql 慢 5x这是在 LinqPad 中直接运行生成的 Sql

我那微弱的 sql 眼睛能发现的唯一真正区别是 WITH (NOLOCK),如果我添加到 LinqToSql 生成的 sql 中,没有任何区别。

有人可以指出我在这里做错了什么吗?谢谢!

现有的硬编码 Sql(5.0 秒)

SELECT DISTINCT 
CH.ClaimNum, CH.AcnProvID, CH.AcnPatID, CH.TinNum, CH.Diag1, CH.GroupNum, CH.AllowedTotal  
FROM Claims.dbo.T_ClaimsHeader AS CH WITH (NOLOCK) 
WHERE 
CH.ContractID IN ('123A','123B','123C','123D','123E','123F','123G','123H') 
AND ( ( (CH.Transmited Is Null or CH.Transmited = '') 
AND CH.DateTransmit Is Null 
AND CH.EobDate Is Null 
AND CH.ProcessFlag IN ('Y','E') 
AND CH.DataSource NOT IN ('A','EC','EU') 
AND CH.AllowedTotal > 0 ) ) 
ORDER BY CH.AcnPatID, CH.ClaimNum

从 LinqToSql 生成的 Sql(27.6 秒)

-- Region Parameters
DECLARE @p0 NVarChar(4) SET @p0 = '123A'
DECLARE @p1 NVarChar(4) SET @p1 = '123B'
DECLARE @p2 NVarChar(4) SET @p2 = '123C'
DECLARE @p3 NVarChar(4) SET @p3 = '123D'
DECLARE @p4 NVarChar(4) SET @p4 = '123E'
DECLARE @p5 NVarChar(4) SET @p5 = '123F'
DECLARE @p6 NVarChar(4) SET @p6 = '123G'
DECLARE @p7 NVarChar(4) SET @p7 = '123H'
DECLARE @p8 VarChar(1) SET @p8 = ''
DECLARE @p9 NVarChar(1) SET @p9 = 'Y'
DECLARE @p10 NVarChar(1) SET @p10 = 'E'
DECLARE @p11 NVarChar(1) SET @p11 = 'A'
DECLARE @p12 NVarChar(2) SET @p12 = 'EC'
DECLARE @p13 NVarChar(2) SET @p13 = 'EU'
DECLARE @p14 Decimal(5,4) SET @p14 = 0
-- EndRegion
SELECT DISTINCT 
[t0].[ClaimNum], 
[t0].[acnprovid] AS [AcnProvID], 
[t0].[acnpatid] AS [AcnPatID], 
[t0].[tinnum] AS [TinNum], 
[t0].[diag1] AS [Diag1], 
[t0].[GroupNum], 
[t0].[allowedtotal] AS [AllowedTotal]
FROM [Claims].[dbo].[T_ClaimsHeader] AS [t0]
WHERE 
([t0].[contractid] IN (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7)) 
AND (([t0].[Transmited] IS NULL) OR ([t0].[Transmited] = @p8)) 
AND ([t0].[DATETRANSMIT] IS NULL) 
AND ([t0].[EOBDATE] IS NULL) 
AND ([t0].[PROCESSFLAG] IN (@p9, @p10)) 
AND (NOT ([t0].[DataSource] IN (@p11, @p12, @p13))) 
AND ([t0].[allowedtotal] > @p14)
ORDER BY [t0].[acnpatid], [t0].[ClaimNum]

新的 LinqToSql 代码(30 多秒...超时)

var contractIds = T_ContractDatas.Where(x => x.EdiSubmissionGroupID == "123-01").Select(x => x.CONTRACTID).ToList();
var processFlags = new List<string> {"Y","E"};
var dataSource = new List<string> {"A","EC","EU"};

var results = (from claims in T_ClaimsHeaders
where contractIds.Contains(claims.contractid)
&& (claims.Transmited == null || claims.Transmited == string.Empty )
&& claims.DATETRANSMIT == null
&& claims.EOBDATE == null
&& processFlags.Contains(claims.PROCESSFLAG)
&& !dataSource.Contains(claims.DataSource)
&& claims.allowedtotal > 0

select new
 {
     ClaimNum = claims.ClaimNum,
     AcnProvID = claims.acnprovid,
     AcnPatID = claims.acnpatid,
     TinNum = claims.tinnum,
     Diag1 = claims.diag1,
     GroupNum = claims.GroupNum,
     AllowedTotal = claims.allowedtotal
 }).OrderBy(x => x.ClaimNum).OrderBy(x => x.AcnPatID).Distinct();

我正在使用上面的常量列表来使 LinqToSql 生成 IN ('xxx','xxx',etc) 否则它会使用同样慢的子查询...

【问题讨论】:

  • 查询计划最大的不同似乎是原始sql中ContractId Index seek只占1%,而Li​​nqToSql查询计划占22%。是参数的原因吗?

标签: c# sql linq-to-sql


【解决方案1】:

比较两个查询的执行计划。 linqtosql 查询使用大量参数,查询优化器将根据参数中可能存在的内容构建执行计划,硬编码 SQL 具有文字值,查询优化器将根据实际值构建执行计划。它可能为文字值生成了一个更有效的计划。最好的办法是尝试找出执行计划中的慢位,并尝试让 linq2sql 产生更好的查询。如果你不能,但你认为你可以手动构建一个,然后创建一个 SP,然后你可以在 linqtosql 中作为数据上下文类的方法公开它。

【讨论】:

    【解决方案2】:

    第一个 SQL 中的硬编码值可能允许查询优化器使用它不知道它可以有效地用于第二个参数化 SQL 的索引。

    另一种可能性是,如果您在 SQL Server Management Studio 中运行手工制作的 SQL,则与 .NET SQL Server 提供程序相比,SSMS 的不同默认设置可能会影响性能。如果是这种情况,在执行命令之前更改 .NET 连接上的一些设置可能会有所帮助(例如 SET ARITHABORT ON),但我不知道您是否可以在 LinqPad 中执行此操作。有关这种可能性的更多信息,请参阅here

    【讨论】:

      【解决方案3】:

      最大的区别是参数。

      如果不分析计划我无法确定,但是 L2S 将查询参数化,以便可以有效地重用它们的计划,避免在服务器上进行过多的查询重新编译。一般来说,这是一件好事,因为它使 SQL Server 上的 CPU 时间保持在较低水平——它不必不断生成和生成相同的计划。

      但是当你使用常量时,L2S 有点过火了。它也将它们参数化,这在某些情况下可能不利于性能。

      戴上我的铝箔千里眼帽,我正在想象你在这张桌子上可能拥有的各种索引结构。例如,您可能在 ProcessFlag 上有一个索引,并且 ProcessFlag 的“Y”和“E”值可能非常少,导致使用硬编码常量的查询仅扫描 ProcessFlag = “Y”和“E”。对于参数化查询,SQL Server 生成一个被判断为对于任意输入最佳的计划。这意味着服务器不能利用你给它的这个小提示(常量)。

      在这一点上,我对您的建议是仔细查看您的索引,并支持涵盖更多 WHERE 条件的复合索引。我敢打赌,通过一些此类分析,您会发现查询性能变得更加相似。 (在这两种情况下都可能有所改善!)

      【讨论】:

        【解决方案4】:

        【讨论】:

          猜你喜欢
          • 2014-07-04
          • 1970-01-01
          • 2010-10-19
          • 1970-01-01
          • 2016-11-29
          • 2015-05-07
          • 1970-01-01
          • 1970-01-01
          • 2021-08-09
          相关资源
          最近更新 更多