即使有效写入,查询执行也会变慢

时间:2012-12-26 15:34:33

标签: sql sql-server sql-server-2008 tsql common-table-expression

这是我的疑问:

With cte as (
    Select
        ROW_NUMBER() OVER (Order By  d.OldInstrumentID ) peta_rn,   
        d.DocumentID
    From Documents d
    Inner Join Users u on d.UserID = u.UserID 
    Where 1=1  
       And (d.JurisdictionID = 1 Or DocumentStatusID = 5 Or DocumentStatusID = 9)
       And d.DocumentStatusID <> 3 And  d.DocumentStatusID <> 8 
       And  d.DocumentStatusID <> 7 
       AND ((CreatedByJurisdictionID = 1 Or DocumentStatusID = 5 Or DocumentStatusID = 9 Or CreatedByAccountID IN (Select AccountID From AccountsJurisdictions Where JurisdictionID = 1)))  
      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,
    d.DocumentDate,
    Upper(dbo.GetFlatDocumentName(d.DocumentID)) as FlatDocumentName
From Documents d
Inner Join cte on cte.DocumentID = d.DocumentID 
Where 1=1 
  And peta_rn>=80000 
  AND peta_rn<=80050 
Order by peta_rn

数据库中几乎没有100,000条记录,但此查询大约需要2秒才能获取50条记录。完全不能接受!我甚至在大多数使用连接的列上都有索引。

在CTE的基本子句中进行单个连接,但这是必需的。我知道联盟是杀手,但我需要加入。如果删除这段代码:

And (d.JurisdictionID = 1 Or DocumentStatusID = 5 Or DocumentStatusID = 9)
And d.DocumentStatusID <> 3 
And d.DocumentStatusID <> 8 
And d.DocumentStatusID <> 7 
AND ((CreatedByJurisdictionID = 1 Or DocumentStatusID = 5 Or DocumentStatusID = 9 Or CreatedByAccountID IN (Select AccountID From AccountsJurisdictions Where JurisdictionID = 1))) 
And d.DocumentStatusID = 9

它运行得非常快。在SSMS中显示0秒。有什么办法加快这个查询?我需要那些条件,他们甚至不是那么大。为什么条件会减慢查询速度?我已经在CreatedByAccountID和其他列上有索引。真烦人!

修改

感谢您的回复。更多细节:

许多人建议删除多余的条件。对不起,这段SQL是在代码中动态形成的,我把这个版本粘贴在SSMS和这里。从where子句中删除这些条件无济于事:

Where 1=1
  And (d.JurisdictionID = 1 Or d.DocumentStatusID = 5 Or d.DocumentStatusID = 9)
  And d.DocumentStatusID <> 3 
  And  d.DocumentStatusID <> 8 
  And  d.DocumentStatusID <> 7 
  AND ((CreatedByJurisdictionID = 1 Or DocumentStatusID = 5 Or DocumentStatusID = 9 Or CreatedByAccountID IN (Select AccountID From AccountsJurisdictions Where JurisdictionID = 1)))  
  And d.DocumentStatusID = 9

事实上,一旦我把where子句放慢,它就会慢下来。所以,即便是这个也很慢:

 Where 1=1 
   And (d.JurisdictionID = 1 Or d.DocumentStatusID = 5 Or d.DocumentStatusID = 9)

更多细节。 Row_Number()中的order by子句是决定因素。类型为varchar(14)的OldInstrumentID速度很慢,需要2秒,而如果我按类型为d.DocumentID进行排序,则即使我保留所有这些冗余条件,它也能在0秒内正常工作

这是我的执行计划:

enter image description here

更新

我在每一列上都创建了这样的索引,它似乎运行得非常快。 Woot Woot !!!

Create NonClustered Index   IX_DocumentDate on Documents
(
    DocumentDate     

)
Include(
    JurisdictionID,
    JudgementTypeID,
    IDate,
    EfileDate,
    UserID,
    RecordingDateTime,
    ApprovedBy,
    ACEfileBankAccountID,
    LastStatusChangedDateTime,
    ACEfileCreditCardID,
    EfiledByUserID,
    ITypeID,
    IGroupID,
    InstrumentID,
    OldInstrumentID,
    [CreatedByJurisdictionID],
    CreatedByAccountID,
    [DocumentStatusID]      


      ,[Remarks]
      ,[InternalNotes]
      ,[ParentDocumentID]
      ,[FilingNumber]
      ,[StampData]      
      ,[Receipt]
      ,[ReceiptNo]
      ,[IsReEfiled]      
      ,[ImportedFromInstrumentID]

)

4 个答案:

答案 0 :(得分:1)

条件可能导致查询速度变慢的原因有几个:

  • 我怀疑查询引擎别无选择,只能执行索引或表扫描,以从Documents表中获取DocumentStatusID的值。但是,从我的阅读中,大部分工作都是多余的,因为条件的最后一行表明DocumentStatusID必须始终等于9.如果SQL Server引擎最后处理这个条件,则可能是它正在执行更多的表扫描评估其他条件而不是它需要。可能是您的其他条件可能会重新编写,因为您只会返回DocumentStatusID为9的文档。
  • CTE中有一个子查询,每次请求CTE的值时都会执行该子查询。如果AccountsJurisdictions表很大且AccountID未编入索引,则此处可能存在性能影响。

正如另一个注释,应该删除“WHERE 1 = 1”语句,因为它们将强制SQL Server引擎检索并处理我认为的所有行 - 您可能正在使用此内核查询优化句法。

答案 1 :(得分:1)

您的CTE可以通过多种方式进行简化。特别是,您正在检查d.DocumentStatusID = 9,因此可以删除对documentStatusId的任何其他引用。

但是,我认为性能问题是由复杂in逻辑中嵌入的where子句引起的。 In经常在SQL Server中得到很好的优化。但是,在这种情况下,我猜你得到了一个嵌套循环连接。请尝试进行显式连接:

    Select  ROW_NUMBER() OVER (Order By  d.OldInstrumentID ) peta_rn,   
            d.DocumentID
    From Documents d Inner Join
         Users u left outer join
         on d.UserID = u.UserID 
         (select distinct AccountId
          from AccountsJurisdictions
          Where JurisdictionID = 1
         ) aj1
         on u.CreatedByAccountID = aj1.AccountId and
               (d.JurisdictionID = 1 Or DocumentStatusID = 5 Or DocumentStatusID = 9) AND
               d.DocumentStatusID <> 3 And d.DocumentStatusID <> 8 AND
               d.DocumentStatusID <> 7 AND
               (CreatedByJurisdictionID = 1 Or DocumentStatusID = 5 
                 Or DocumentStatusID = 9 Or aj1.AccountId is not NULL
                ) AND
               d.DocumentStatusID = 9

您还可以尝试在AccountsJurisdictions(JurisdictionId, AccountId)上创建索引,以便索引用于此部分。

答案 2 :(得分:0)

如果d.DocumentStatusID = 9则有很多冗余条件

;With cte as (
               Select  ROW_NUMBER() OVER (Order By  d.OldInstrumentID ) peta_rn, d.DocumentID
               From Documents d
               Join Users u 
                    on d.UserID = u.UserID 
               Where -- 1 = 1 AND
               d.DocumentStatusID = 9 
               AND (CreatedByJurisdictionID = 1  
                    Or CreatedByAccountID IN (Select AccountID From AccountsJurisdictions Where JurisdictionID = 1)
                   )
               -- below redundant as  d.DocumentStatusID = 9
               --(d.JurisdictionID = 1 Or DocumentStatusID = 5 Or DocumentStatusID = 9)
               --And d.DocumentStatusID <> 3 
               --And d.DocumentStatusID <> 8 
               --And  d.DocumentStatusID <> 7 
               --AND (
               --      Or DocumentStatusID = 5 
               --      Or DocumentStatusID = 9 
               --    )  
             ) 
Select  d.DocumentID, d.IsReEfiled, d.IGroupID, d.ITypeID, d.RecordingDateTime -- ...
From Documents d 
Join cte 
  on cte.DocumentID = d.DocumentID 
Where -- 1=1 AND
     peta_rn>=80000 AND peta_rn<=80050 
Order by peta_rn

这是否有必要?
加入用户u.UserID = u.UserID
如果你是FK那么这将永远是真的

答案 3 :(得分:-1)

我会尝试将DocumentstatusIDJurisdictionIDCreatedByJurisdictionID作为CTE中的列,并将非常复杂的join子句作为最终连接的WHERE。

我并不感到惊讶,因为所有这些&#39;不等于&#39;案例和复杂的逻辑,作为一个不等于&#39;本身并不是一个快速的操作。我试图简化所有代码。