我有一个在现有VB6应用程序中硬编码的sql语句。我正在升级C#中的新版本并使用Linq To Sql。我能够让LinqToSql生成相同的sql(在我开始重构之前),但由于某种原因,LinqToSql生成的Sql比原始sql 慢5倍。 这是在LinqPad中运行生成的Sql Directly 。
我微薄的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'等),否则它会使用同样慢的子查询......
答案 0 :(得分:4)
比较两个队列的执行计划。 linqtosql查询使用大量参数,查询优化器将根据参数中的MIGHT构建执行计划,硬编码SQL具有文字值,查询优化器将根据实际值构建执行计划。它可能会为字面值创建一个更有效的计划。最好的办法是尝试发现执行计划中的慢点,并尝试获取linq2sql以产生更好的查询。如果您不能,但您认为可以手动构建一个,那么创建一个SP,然后您可以在linqtosql中将其作为方法公开在数据上下文类中。
答案 1 :(得分:2)
第一个SQL中的硬编码值可能允许查询优化器使用它不知道它可以有效地用于第二个参数化SQL的索引。
另一种可能性是,如果您在SQL Server Management Studio中运行手工制作的SQL,则与.NET SQL Server提供程序相比,SSMS的不同默认设置可能会影响性能。如果是这种情况,在执行命令之前更改.NET连接上的某些设置可能会有所帮助(例如,SET ARITHABORT ON),但我不知道您是否可以在LinqPad中执行此操作。有关此可能性的详细信息,请参阅here。
答案 2 :(得分:1)
最大的不同是参数。
如果不分析计划,我无法确定,但L2S参数化查询,以便可以有效地重用它们的计划,避免在服务器上进行过多的查询重新编译。通常,这是一件好事,因为它可以使SQL Server上的CPU时间保持较低 - 它不必继续生成和生成并生成相同的计划。
但是当你使用常量时,L2S有点过火。它也对它们进行参数化,这在某些情况下可能对性能有害。
穿上我的铝箔透视帽,我可以看到你在桌子上可能有的各种索引结构。例如,您可能只有ProcessFlag上的索引,并且ProcessFlag的“Y”和“E”值可能很少,导致使用硬编码常量的查询仅扫描ProcessFlag =的值“Y”和“E”。对于参数化查询,SQL Server生成一个计划,该计划被判断为任意输入的最佳选择。这意味着服务器无法利用您提供的这个小提示(常量)。
此时我给您的建议是仔细查看您的索引并使用复合索引,这些索引会覆盖更多WHERE条件。我敢打赌,通过一些类型的分析,您会发现查询性能变得更加相似。 (在两种情况下都可能有所改善!)
答案 3 :(得分:0)