1

SQL サーバーで %15 の CPU 時間を消費している次のクエリがあります。

declare
  @JobQueueId int = NULL,   @JobTypeId int = NULL,   @JobTypeIdHasValue BIT = 0,   @TargetId int = NULL,   @TargetIdHasValue BIT = 0,   
  @JobMessage nvarchar(max) = NULL,   @ComputerName nvarchar(60) = NULL,   @ComputerNameHasValue BIT = 0,   @StartedOn datetime = NULL,   
  @StartedOnHasValue BIT = 0,   @CompletedOn datetime = NULL,   @CompletedOnHasValue BIT = 0,   @SkipOrderedBy datetime = NULL,   
  @SkipOrderedByHasValue BIT = 0,   @SkipOrderedOn datetime = NULL,   @SkipOrderedOnHasValue BIT = 0,   @Attempts int = NULL,   
  @AttemptsHasValue BIT = 0,   @FailResult nvarchar(max) = NULL,   @FailResultHasValue BIT = 0,   @CreatedBy uniqueidentifier = NULL,   
  @CreatedOn datetime = NULL  



  SET TRANSACTION ISOLATION LEVEL READ COMMITTED    

  SELECT   [JobQueueId],   [JobTypeId],   [TargetId],   [JobMessage],   [ComputerName],   [StartedOn],   [CompletedOn],   [SkipOrderedBy],     [SkipOrderedOn],   [Attempts],   FailResult],   [CreatedBy],   [CreatedOn]  
  FROM      [dbo].[JobQueue]  

  WHERE   
  ([JobQueueId] = @JobQueueId OR @JobQueueId IS NULL)    
  AND ([JobTypeId] = @JobTypeId OR (@JobTypeId IS NULL AND @JobTypeIdHasValue = 0))   
  AND ([TargetId] = @TargetId OR (@TargetId IS NULL AND @TargetIdHasValue = 0))   
  AND ([JobMessage] = @JobMessage OR @JobMessage IS NULL)   
  AND ([ComputerName] = @ComputerName OR (@ComputerName IS NULL AND @ComputerNameHasValue = 0))   
  AND ([StartedOn] = @StartedOn OR (@StartedOn IS NULL AND @StartedOnHasValue = 0))   
  AND ([CompletedOn] = @CompletedOn OR (@CompletedOn IS NULL AND @CompletedOnHasValue = 0))   
  AND ([SkipOrderedBy] = @SkipOrderedBy OR (@SkipOrderedBy IS NULL AND @SkipOrderedByHasValue = 0))   
  AND ([SkipOrderedOn] = @SkipOrderedOn OR (@SkipOrderedOn IS NULL AND @SkipOrderedOnHasValue = 0))   
  AND ([Attempts] = @Attempts OR (@Attempts IS NULL AND @AttemptsHasValue = 0))   
  AND ([FailResult] = @FailResult OR (@FailResult IS NULL AND @FailResultHasValue = 0))   
  AND ([CreatedBy] = @CreatedBy OR @CreatedBy IS NULL)   
  AND ([CreatedOn] = @CreatedOn OR @CreatedOn IS NULL)    

このクエリを最適化するのに役立つポインタを誰かが提供できますか?

4

2 に答える 2

1

これはひどい、ひどいクエリです。AND と OR (およびそれらのパターン) の数が非常に多いため、インデックスに関する静的な推論は役に立たなくなります。そして、それが非常にひどい主な理由は、それが「ユニバーサル」であるためです。静的であるべき多くの作業 (@JobQueueId が null であるかどうかのチェックなど) が SQL サーバーに任されています。

どんな種類のクエリ準備からも利益を得られないため、動的な方法でクエリを構築し、特定のケースごとにデータベースサーバーに選択を最適化させる必要があります。

于 2013-02-12T21:38:49.817 に答える
1

クエリは特定のパラメーターを優先するプランをキャッシュする可能性が高いため、さまざまなパラメーターがそのプランからあまり恩恵を受けない可能性があります (「パラメーター スニッフィング」で検索すると、これが非常に一般的な問題であることがわかります)。 . いくつかのオプション:

  1. OPTION (RECOMPILE)ステートメントに追加します。これは毎回のコンパイルのために少しコストがかかりますが、少なくとも予測可能であり、一部のパラメーターでは 0.5 秒で実行され、他のパラメーターでは長時間実行されるというような大規模なスイングは必要ありません。
  2. OPTIMIZE FOR UNKNOWN最適化のために以前にキャッシュされたパラメーターを使用しないようにするために使用します。
  3. サーバーレベルのオプションを (経由でsp_configure)オンにしOptimize for ad hoc workloads(ここここを参照)、動的 SQL を使用するようにクエリを変更します。つまり、次のようにクエリを作成します。

    DECLARE @sql NVARCHAR(MAX);
    
    SET @sql = N'SELECT ... FROM [dbo].[JobQueue] WHERE 1=1';
    
    IF @JobQueueId IS NOT NULL
      SET @sql = @sql + N' AND JobQueueId = @JobQueueId';
    
    -- ... repeat for other params
    
    EXEC sp_executesql 
      @sql,
      N'@JobQueueId INT, ..., @CreatedOn DATETIME',
      @JobQueueId, ..., @CreatedOn;
    

参照: http://www.sommarskog.se/dynamic_sql.html#Dyn_search

于 2013-02-12T19:51:05.397 に答える