2

这是我在搜索表单上使用的动态查询,它在 SSMS 中以毫秒为单位运行,大约在 300 到 400 毫秒之间:

exec sp_executesql N'set arithabort off;
set transaction isolation level read uncommitted;

With cte as 
  (Select ROW_NUMBER() OVER 
      (Order By  Case When d.OldInstrumentID IS NULL 
          THEN d.LastStatusChangedDateTime Else d.RecordingDateTime End 
       desc) peta_rn,   
      d.DocumentID
   From Documents d
   Inner Join Users u on d.UserID = u.UserID 
   Inner Join IGroupes ig on ig.IGroupID = d.IGroupID
   Inner Join ITypes it on it.ITypeID = d.ITypeID 
   Where 1=1  
       And (CreatedByAccountID = @0 Or DocumentStatusID = @1 Or DocumentStatusID = @2 )  
       And (d.JurisdictionID = @3 Or DocumentStatusID = @4 Or DocumentStatusID = @5)   
       AND (  d.DocumentStatusID = 9  ) 
   ) 
Select d.DocumentID, d.IsReEfiled, d.IGroupID, d.ITypeID, d.RecordingDateTime, 
    d.CreatedByAccountID, d.JurisdictionID, 
    Case When d.OldInstrumentID IS NULL THEN d.LastStatusChangedDateTime 
        Else d.RecordingDateTime End as LastStatusChangedDateTime, 
    dbo.FnCanChangeDocumentStatus(d.DocumentStatusID,d.DocumentID) as CanChangeStatus, 
    d.IDate, d.InstrumentID, d.DocumentStatusID,ig.Abbreviation as IGroupAbbreviation, 
    u.Username, j.JDAbbreviation, inf.DocumentName,
    it.Abbreviation as ITypeAbbreviation, d.DocumentDate, 
    ds.Abbreviation as DocumentStatusAbbreviation,
    Upper(dbo.GetFlatDocumentName(d.DocumentID)) as FlatDocumentName 
From Documents d 
Left Join IGroupes ig On d.IGroupID = ig.IGroupID 
Left Join ITypes it On d.ITypeID = it.ITypeID 
Left Join Users u On u.UserID = d.UserID 
Left Join DocumentStatuses ds On d.DocumentStatusID = ds.DocumentStatusID 
Left Join InstrumentFiles inf On d.DocumentID = inf.DocumentID 
Left Join Jurisdictions j on j.JurisdictionID = d.JurisdictionID 
Inner Join cte on cte.DocumentID = d.DocumentID 
Where 1=1 
    And peta_rn>=@6 AND peta_rn<=@7 
Order by peta_rn',
N'@0 int,@1 int,@2 int,@3 int,@4 int,@5 int,@6 bigint,@7 bigint',
@0=44,@1=5,@2=9,@3=1,@4=5,@5=9,@6=94200,@7=94250

这个 sql 是用 C# 代码形成的,where 子句是根据用户在搜索表单中搜索的值动态添加的。从一页移动到第二页大约需要 3 秒。我在搜索的大多数列上已经有了必要的索引。

知道为什么我的 Ado.Net 代码会变慢吗?

更新:不确定执行计划是否会有所帮助,但它们是:

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

4

3 回答 3

1

SQL Server 可能为 ADO.NET 连接创建了不适当的查询计划。我们已经看到与 ADO 类似的问题,通常的解决方案是清除所有查询计划并再次运行慢查询 - 这可能会创建更好的计划。要清除查询计划,最通用的解决方案是更新相关表的统计信息。就像你的下一个:

update statistics documents with fullscan

对涉及的其他表执行相同操作,然后从 ADO.NET 运行慢速查询(之前不要运行 SSMS)。

请注意,这种时间不一致可能暗示查询或数据库设计错误 - 至少对我们来说通常是这样 :)

于 2013-03-01T09:21:33.957 回答
0

.net 默认使用 UTF 字符串,这相当于 NVARCHAR 而不是 VARCHAR。

当您在 dot net 中执行 WHERE ID = @foo 时,您可能会隐式执行

WHERE CONVERT(ID, NVARCHAR) = @foo

结果是这个 where 子句不能被索引,并且必须被表扫描。解决方案是实际将每个参数作为 DbParameter 传递到 SqlCommand 中,并将 DbType 设置为 VARCHAR(在字符串的情况下)。

如果 .net 参数比等效的 SQL 列“更宽”,则 Int 类型当然会发生类似的情况。

PS“证明”此问题的最简单方法是在 SSMS 中运行您的查询,并执行以下操作

DECLARE @p0 INT = 123
DECLARE @p1 NVARCHAR = "foobar" //etc etc

并与

DECLARE @p0 INT = 123
DECLARE @p1 VARCHAR = "foobar" //etc etc
于 2013-03-01T08:27:52.707 回答
0

如果在 SSMS 中重复运行查询,数据库可能会重复使用之前创建的执行计划,并且所需的数据可能已经缓存在内存中。

我在您的查询中注意到了几件事:

  • CTE 连接用户、IGroupes 和 IType,但连接的记录未在 SELECT 中使用

  • CTE 对计算表达式执行 ORDER BY(请注意(未索引)排序中的 85% 成本)

可能用可以索引的计算持久列替换 CASE 表达式可以加快执行速度。

  • 请注意,ORDER BY 是对连接 4 个表产生的数据执行的

  • CTE 状态的 WHERE 条件AND d.DocumentStatusID = 9,但 AND 的其他 DocumentStatusID

  • 对 8 个 JOINed 表的结果执行分页。

最有可能创建一个中间 CTE,它根据peta_rn改进性能过滤第一个 CTE

于 2013-03-01T09:05:06.130 回答