2012-07-01 15 views
12

ちょっと質問がありました。テーブルの単一の最大値を取得しようとするとき。どちらがいいですか?SQLパフォーマンスMAX()

SELECT MAX(id) FROM myTable WHERE (whatever) 

または

SELECT TOP 1 id FROM myTable WHERE (whatever) ORDER BY id DESC 

は、私が最初のものは確かに意思で明確にされているMicrosoft SQL Server 2012の

+2

テストしましたか?私は彼らがオプティマイザが良い場合は、同じであることを期待するだろう。 – Hogan

+1

'id'が自動インクリメントの場合、この質問はhttp://stackoverflow.com/questions/590079/for-autoincrement-fields-maxid-vs-top-1-id-order-by-id-descの複製です – Ben

+0

idは任意のタイプの任意の列を意味します –

答えて

26

実行計画を検査して自分自身をテストすることはできますが、違いはありません。 idがクラスタード・インデックスの場合は、順序付きクラスタード・インデックス・スキャンが表示されます。索引付けされていない場合でも、表スキャンまたはクラスタード・インデックス・スキャンのいずれかが表示されますが、いずれの場合も順序付けされません。

TOP 1のアプローチは、行から他の値を引き出す場合に便利です。これは、サブクエリでmaxを引っ張って結合するほうが簡単です。行の他の値が必要な場合は、どちらの場合でもタイを扱う方法を指定する必要があります。

これは、計画が異なる可能性があるシナリオがいくつかあるため、列が索引付けされているかどうか、単調に増加しているかどうかによってテストすることが重要です。スルー2010-01-01からE/G、3と9994の間のB/D、これは1から50000まで/ Cの値を作成し、私のシステムで

CREATE TABLE dbo.x 
(
    a INT, b INT, c INT, d INT, 
    e DATETIME, f DATETIME, g DATETIME, h DATETIME 
); 
CREATE UNIQUE CLUSTERED INDEX a ON dbo.x(a); 
CREATE INDEX b ON dbo.x(b) 
CREATE INDEX e ON dbo.x(e); 
CREATE INDEX f ON dbo.x(f); 

INSERT dbo.x(a, b, c, d, e, f, g, h) 
SELECT 
    n.rn, -- ints monotonically increasing 
    n.a, -- ints in random order 
    n.rn, 
    n.a, 
    DATEADD(DAY, n.rn/100, '20100101'), -- dates monotonically increasing 
    DATEADD(DAY, -n.a % 1000, '20120101'),  -- dates in random order 
    DATEADD(DAY, n.rn/100, '20100101'), 
    DATEADD(DAY, -n.a % 1000, '20120101') 
FROM 
(
    SELECT TOP (50000) 
    (ABS(s1.[object_id]) % 10000) + 1, 
    rn = ROW_NUMBER() OVER (ORDER BY s2.[object_id]) 
    FROM sys.all_objects AS s1 
    CROSS JOIN sys.all_objects AS s2 
) AS n(a,rn); 
GO 

:私は、単純なテーブルを作成し、50000個の行を挿入しました2011-05-16、およびf/hを2009-04-28から2012-01-01まで変更できます。

まず、索引付けされた単調増加整数列aとcを比較してみましょう。 、cは、クラスタ化インデックスはありませんしています

SELECT MAX(a) FROM dbo.x; 
SELECT TOP (1) a FROM dbo.x ORDER BY a DESC; 

SELECT MAX(c) FROM dbo.x; 
SELECT TOP (1) c FROM dbo.x ORDER BY c DESC; 

結果:第4回のクエリで

enter image description here

大きな問題はMAXとは異なり、それはソートを必要とする、ということです。ここで4と比較して3:

enter image description here

enter image description here

これは、これらのクエリー変形の全てに共通の問題であろう。索引付けされていない列に対するMAXは、クラスタ上でピギーバックすることができるであろうインデックススキャンを実行し、ストリーム集約を実行しますが、TOP 1はより高価になるソートを実行する必要があります。

私は、b + d、e + g、およびf + hのテストで全く同じ結果をテストして見ました。

だから、あなたが置かれているの後に変更することができた(より標準準拠のコードを生成することに加えて、基本となるテーブルやインデックスに応じて、TOP 1の賛成でMAXを使用する潜在的なパフォーマンス上の利点がある、と私には思われますプロダクションでのあなたのコード)。だから私は、詳細情報なしで、MAXが好ましいと言います。

(そして、私が前に言ったように、 TOP 1は本当にあなたが追加の列を引っ張っている場合は、後にしている行動かもしれません。あなたはそれはあなたが後にしているものだ場合にも MAX + JOINメソッドをテストしたいと思います。 )

+0

+1 - あなたのパーセンテージを計算するために使用されたSQLはどれですか?または実行計画のXMLを調べましたか?将来の読者にも、これらの計画を尋ねる方法についての知識が与えられるように、あなたの答えにそのことを載せていただければ幸いです。 – Wayne

+1

私の会社の無料ツールであるSQL Sentry Plan Explorerの関連出力を表示しました。 http://sqlsentry.net/ –

+0

[トップNのソートについての良い記事はこちら](http://sqlblog.com/blogs/paul_white/archive/2010/08/27/sorting-row-goals-and) -the-top-100-problem.aspx)誰かが興味があれば。実際にはすべての行をソートする必要はなく( 'TOP 1 'を追跡する必要があります)、ストリーム集約とは異なりメモリの付与が必要です。 –

5

を使用しています。

この特定のクエリでは、重要なパフォーマンスの違いは考えられません(myTableに行がない場合は結果が異なりますが、実際にはほとんど同じです)。クエリを調整する正当な理由(例:実績のあるパフォーマンスの問題)がない限り、常にコードの意図を示すものを選択します。

+3

もう1つの利点は、最初のクエリがDBMSに依存しないということです。つまり、同じクエリを使用して他のほぼすべてのDBMSで実行することができ、それでも機能しますが、2番目のクエリはSQL Server固有の ' 'キーワードはSQL-Serverでのみサポートされています。 –

2

すべてのクエリオプティマイザは、両方のクエリで同じパフォーマンスを持つクエリプランを生成する必要があります。最適化する列にインデックスがある場合、両方のクエリで使用する必要があります。索引がない場合、両方とも完全な表スキャンを生成します。

0

私は、TOP 1ソート演算子がプランでコストオーバーされていると思われます。私はTOP 1、TOP 100、> TOP 101を試してみましたが、すべての行を並べ替える必要があるという事実にもかかわらず、私は同じサブツリーコストを払ってくれました。 - 午前6時53分あなたはオプティマイザがすなわちテーブル(クラスタ化インデックス・スキャン).Thenからすべての行を読んで、この例では仕事の同じ量を行う必要があります1行または100行が必要かどうか

でマーティン・スミス7月2日列Cに索引がないので、すべての行をソートする(ソート操作)。最後に必要なものだけを表示する。

上記のコードを試してください。ここでは、トップ1とトップ100は、b列にインデックスがあるため、差額を示しています。したがって、この場合、すべての行を読み込んで並べ替える必要はありませんが、作業は最終ページポインタに移動することです.1つの行では、インデックスの最後の葉ページの最後の行を読み込みます。 TFor 100行は最後の行を最後のページで見つけ出し、100行を取得するまで後方スキャンを開始します。

+0

これは正しくありません。 [私が与えたリンクは 'TOP N'ソートの仕組みについて説明しています(http://sqlblog.com/blogs/paul_white/archive/2010/08/27/sorting-row-goals-and-the-top)をお読みください。 -100-problem.aspx)。 100はさまざまな方法の間のマジックナンバーですが、Aaronのデモデータを実行しているときに、「TOP 1」と「TOP 50000」のコストが同じであるため、計画の原価計算で考慮されるようには見えません。'TOP 1'の場合、基本的には、MAXが異なる方法で実装されているにもかかわらず、MAXと同じ最大値を追跡する必要があります。 50,000行すべてをソートする必要はありません。 –

+0

50000行をソートしない場合リストにソートされていないと言われた場合、どの値が最大値であるかは分かりますか?列Cにはインデックスがありません。 –

+0

すべてをスキャンし、これまでに見てきた 'TOP 1 'の値とそれぞれを比較します。 50,000行のセット全体を並べ替える必要はありません。 –