18

ちょっとした質問があります。テーブルの単一の最大値を取得しようとするとき。どちらの方がよいですか?

SELECT MAX(id) FROM myTable WHERE (whatever)

また

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

Microsoft SQL Server 2012 を使用しています

4

4 に答える 4

34

実行計画を調べて自分でテストできるため、違いはありません。idがクラスター化インデックスの場合、順序付けられたクラスター化インデックススキャンが表示されます。インデックスが作成されていない場合でも、テーブルスキャンまたはクラスター化インデックススキャンのいずれかが表示されますが、どちらの場合も順序付けされません。

このTOP 1アプローチは、行から他の値を取得する場合に役立ちます。これは、サブクエリで最大値を取得してから結合するよりも簡単です。行から他の値が必要な場合は、両方の場合で同点を処理する方法を指示する必要があります。

そうは言っても、計画が異なる可能性があるシナリオがいくつかあるため、列にインデックスが付けられているかどうか、および単調に増加しているかどうかに応じてテストすることが重要です。単純なテーブルを作成し、50000行を挿入しました。

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

私のシステムでは、これにより、a / cが1から50000、b / dが3から9994、e / gが2010-01-01から2011-05-16、f/hが2009-04-28からの値が作成されました。 2012-01-01。

まず、インデックス付きの単調に増加する整数列aとcを比較してみましょう。aにはクラスター化されたインデックスがあり、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番目のクエリの大きな問題は、とは異なりMAX、ソートが必要なことです。これが4と比較して3です。

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

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

これは、これらすべてのクエリバリエーションに共通する問題です。MAXインデックス付けされていない列に対しては、クラスター化インデックススキャンに便乗してストリーム集計TOP 1を実行できますが、よりコストのかかる並べ替えを実行する必要があります。

私はテストを行い、b + d、e + g、およびf+hのテスト全体でまったく同じ結果を確認しました。

したがって、より多くの標準準拠コードを生成することに加えて、基になるテーブルとインデックス(コードを本番環境に置いた後に変更される可能性があります)に依存するMAXことを優先して使用することには、潜在的なパフォーマンス上の利点があるように思われます。TOP 1したがって、これ以上の情報がなければ、MAX望ましいと思います。

(そして、前に言ったように、TOP 1追加の列をプルする場合、実際にはあなたが求めている動作かもしれません。それがあなたが求めているものである場合は、 MAX+JOINメソッドもテストする必要があります。)

于 2012-07-01T20:35:03.643 に答える
6

最初のものは確かに意図がより明確です。

この特定のクエリについては、パフォーマンスに大きな違いはないはずです(に行がない場合は結果が異なりますが、実際にはほとんど同じである必要がありますmyTable)。クエリを調整する正当な理由がない限り(たとえば、実績のあるパフォーマンスの問題)、常にコードの意図を示すものを選択してください。

于 2012-07-01T20:30:39.667 に答える
4

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

于 2012-07-01T20:31:24.880 に答える
0

TOP 1 の並べ替え演算子は、計画では高すぎると思いますが。TOP 1、TOP 100、および TOP 101 で試してみたところ、最後のサブツリーではすべての行をソートする必要があるという事実にもかかわらず、サブツリーの推定コストはすべて同じでした。– マーティン スミス 7 月 2 日 6:53

この例では、1 行または 100 行が必要かどうかにかかわらず、オプティマイザーは同じ量の作業を行う必要があります。つまり、テーブルからすべての行を読み取ります (クラスター化インデックス スキャン)。次に、それらのすべての行を並べ替えます (並べ替え操作)。列 C..最後に、どれが必要かを表示します。

SELECT TOP (1) b FROM dbo.x ORDER BY b DESC
option(recompile); 
SELECT TOP (100) b FROM dbo.x ORDER BY b DESC
option(recompile); 

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

于 2012-07-13T14:44:34.310 に答える