4

完了するのに非常に長い時間がかかるSQLクエリ(SQL Server 2008 R2の場合)があります。もっと良い方法があるのだろうか?

SELECT @count = COUNT(Name)
FROM Table1 t
WHERE t.Name = @name AND t.Code NOT IN (SELECT Code FROM ExcludedCodes)

Table1には約9千万行が含まれており、名前とコードでインデックスが付けられています。ExcludedCodesには約30行しかありません。

このクエリはストアドプロシージャ内にあり、約40k回呼び出されます。プロシージャが完了するまでにかかる合計時間は、27分です。クエリする行数と回数が多いため、これが私の最大のボトルネックだと思います。それはそれをします。

したがって、これを最適化するための良い方法を知っているなら、それは大いにありがたいです!最適化できない場合は、27分でスタックしていると思います...

編集

に変更するNOT INNOT EXISTS、時間が10:59に短縮されたので、それだけで大きなメリットが得られます。以下に示すように、group byステートメントを実行しようとしますが、ストアドプロシージャを完全に書き直す必要があり、時間がかかる場合があります...(前に述べたように、SQLは得意ではありませんが、開始しています私に成長するために。^^)

4

5 に答える 5

5

クエリ自体がより速く応答するようにするための回避策に加えて、それがこのセットにあるかどうかを示す列をテーブルに維持することを検討しましたか?多くのメンテナンスが必要ですが、ExcludedCodesテーブルが頻繁に変更されない場合は、そのメンテナンスを行う方がよい場合があります。たとえば、BIT列を追加できます。

ALTER TABLE dbo.Table1 ADD IsExcluded BIT;

これをNULLではなく、デフォルトで0にします。次に、フィルター処理されたインデックスを作成できます。

CREATE INDEX n ON dbo.Table1(name)
  WHERE IsExcluded = 0;

これで、テーブルを1回更新する必要があります。

UPDATE t
  SET IsExcluded = 1
  FROM dbo.Table1 AS t
  INNER JOIN dbo.ExcludedCodes AS x
  ON t.Code = x.Code;

そして継続的には、両方のテーブルのトリガーでこれを維持する必要があります。これを設定すると、クエリは次のようになります。

SELECT @Count = COUNT(Name)
  FROM dbo.Table1 WHERE IsExcluded = 0;

編集

「NOTINがLEFTJOINより遅い」については、数千行だけで実行した簡単なテストです。

ここに画像の説明を入力してください

編集2

このクエリがあなたが求めているものを実行せず、40Kループよりもはるかに効率的である理由がわかりません。

SELECT src.Name, COUNT(src.*)
  FROM dbo.Table1 AS src
  INNER JOIN #temptable AS t
  ON src.Name = t.Name
  WHERE src.Code NOT IN (SELECT Code FROM dbo.ExcludedCodes)
  GROUP BY src.Name;

または、LEFT JOINに相当するもの:

SELECT src.Name, COUNT(src.*)
  FROM dbo.Table1 AS src
  INNER JOIN #temptable AS t
  ON src.Name = t.Name
  LEFT OUTER JOIN dbo.ExcludedCodes AS x
  ON src.Code = x.Code
  WHERE x.Code IS NULL
  GROUP BY src.Name;

私は27分もかからずにこれらのクエリのいずれかにお金をかけます。両方のクエリを順番に実行すると、27分かかる1つのクエリよりもはるかに高速になることをお勧めします。

最後に、インデックス付きビューを検討することもできます。テーブルの構造と、制限に違反しているかどうかはわかりませんが、IMHOを調査する価値があります。

于 2012-06-25T19:39:24.980 に答える
4

あなたはこれが約40K回呼び出されると言います。なぜ?カーソルの中にありますか?もしそうなら、あなたは本当にカーソルが必要です。@nameに必要な値を一時テーブルに入れてインデックスを付けてから、それに結合することはできませんか?

select t.name, count(t.name) 
from table t
join #name n on t.name = n.name 
where NOT EXISTS (SELECT Code FROM ExcludedCodes WHERE Code = t.code)
group by t.name

これにより、1つのクエリですべての結果が得られる可能性があり、40Kの個別のクエリよりもほぼ確実に高速です。もちろん、すべての名前のカウントが必要な場合は、さらに簡単です

select t.name, count(t.name) 
    from table t
NOT EXISTS (SELECT Code FROM ExcludedCodes WHERE Code = t
group by t.name
于 2012-06-25T19:32:16.390 に答える
2

NOT EXISTS通常、よりもパフォーマンスが優れてNOT INいますが、システムでテストする必要があります。

SELECT @count = COUNT(Name)
FROM Table1 t
WHERE t.Name = @name AND NOT EXISTS (SELECT 1 FROM ExcludedCodes e WHERE e.Code = t.Code)

クエリについて詳しく知らなければ、具体的な最適化の提案(つまり、コピー/貼り付けに適したコード)を提供するのは困難です。本当に40,000回実行する必要がありますか?可能であれば、ストアドプロシージャを作り直す必要があるようです。procの開始時に上記を1回実行し、結果を一時テーブルに挿入して、インデックスを保持してからTable1、このクエリを実行する代わりにそれに参加することができます。

この特定のビットは、クエリを27分間実行するボトルネックではない可能性があります。たとえば、これらの9000万行にカーソルを使用していますか、それともWHERE句でスカラー値のUDFを使用していますか?

于 2012-06-25T19:27:30.897 に答える
1

クエリを一度実行して、テーブル変数または一時テーブルにデータを入力することを考えたことはありますか?何かのようなもの

insert into #temp (name, Namecount)
values Name, Count(name)
from table1 
where name not in(select code from excludedcodes)
group by name

また、除外されたコードテーブルがある程度静的である限り、フィルタリングされたインデックスを使用できる可能性があることを忘れないでください。

于 2012-06-25T19:31:33.950 に答える
0

実行計画の評価を開始します。計算するのに最も重い部分はどれですか?2つのテーブル間の関係については、インデックス付きの列でJOINを使用してください。インデックスはクエリの実行を最適化します。

于 2012-06-25T19:31:56.593 に答える