これは 主キーでソート順が指定されているが、ソートはSELECTで実行される からのスピンオフ質問です。
@ Catcall は、ストレージの順序(クラスター化インデックス)と出力の順序に関してこれを言います
多くの人々は、クラスター化インデックスが出力のソート順を保証すると信じています。しかし、それはそうではありません。これは、ディスク上のストレージ順序を保証します。たとえば、 このブログ投稿 を参照してください。
Hugo Kornelisによるブログ投稿を読みましたが、インデックスがSQLサーバーが特定の順序でレコードを読み取ることを保証するものではないことを理解しています。しかし、自分のシナリオではこれを想定できないことを受け入れるのに苦労していますか?
CREATE TABLE [dbo].[SensorValues](
[DeviceId] [int] NOT NULL,
[SensorId] [int] NOT NULL,
[SensorValue] [int] NOT NULL,
[Date] [int] NOT NULL,
CONSTRAINT [PK_SensorValues] PRIMARY KEY CLUSTERED
(
[DeviceId] ASC,
[SensorId] ASC,
[Date] DESC
) WITH (
FILLFACTOR=75,
DATA_COMPRESSION = PAGE,
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON)
ON [MyPartitioningScheme]([Date])
私の元のクエリはこれでした:
SELECT TOP 1 SensorValue
FROM SensorValues
WHERE SensorId = 53
AND DeviceId = 3819
AND Date < 1339225010
ORDER BY Date DESC
しかし、私はこれを使用することもできます(以下の説明を読んでください)。
SELECT TOP 1 SensorValue
FROM SensorValues
WHERE SensorId = 53
AND DeviceId = 3819
AND Date < 1339225010
ご覧のとおり、テーブルの行は小さく(16バイト)、クラスター化されたインデックスは1つしかありません。私のシナリオでは、テーブルは現時点で100.000.000レコードで構成されています(これはおそらく10倍に増加します)。
データベースサーバーがこのテーブルにクエリを実行するとき、行を見つける方法は2つあります。主キーを探し、それによって値を読み取り、descに返します。日付順、またはテーブル全体をスキャンする必要があります。私の結論は、これらすべてのレコードに対する全テーブルスキャンは非常に遅くなり、データベースサーバーは常に主キーを介してテーブルをシークし、それによってDate DESC
でソートされた値を返すということです。
なぜしないでください必要があるのか、なぜしないのかについて説明します-)指定しない限り、SQL製品が特定の順序で結果セットを返すと仮定します(クラスター化または非クラスター化、BツリーまたはRツリー、kdツリーまたはフラクタルツリーなど)。 DBMSが使用しているエキゾチックなインデックス。
元のクエリは、SensorValues
テーブルを検索し、3つの条件に一致する行を検索し、Date
の降順でそれらの行を並べ替え、それらの最初の行のみを保持し、最後に-選択するようにDBMSに指示しますSensorValue
列のみを返します。
_SELECT TOP 1 SensorValue
FROM SensorValues
WHERE SensorId = 53
AND DeviceId = 3819
AND Date < 1339225010
ORDER BY Date DESC ;
_
これらは、DBMSに指定した非常に特定の順序であり、クエリを実行するたびに結果が同じになる可能性があります(条件に一致し、同じ行が複数ある場合は、一致しない可能性があります) max Date
ですがSensorValue
は異なりますが、会話の残りの部分では、そのような行がテーブルに存在しないと仮定します)。
このクエリを実行するには、DBMSがこれを実行する必要がありますか?いいえ、もちろん違います。あなたはそれを知っています。テーブルを読み取るのではなく、インデックスから読み取ることができます。または、より良い(速い)と思われる場合は、2つのインデックスを使用する場合があります。または3つ。または、キャッシュされた結果(SQL Serverではなく他のDBMSキャッシュクエリ結果)を使用する場合があります。または、並列実行を1回使用し、次に実行するときは使用しない場合があります。または...(実行および実行計画に影響を与えるその他の機能を追加します)。
ただし、行が挿入、削除、または更新されない限り、実行するたびにまったく同じ結果が返されることが保証されています。
今あなたの提案が言うことを見てみましょう:
_SELECT TOP 1 SensorValue
FROM SensorValues
WHERE SensorId = 53
AND DeviceId = 3819
AND Date < 1339225010 ;
_
このクエリは、SensorValues
テーブルを検索し、3つの条件に一致する行を見つけるようにDBMSに指示します。 これらの行を、順序を気にせず、1行のみを保持および-最後に-Date
の降順で並べます。SensorValue
列のみを選択して返します。
したがって、基本的には最初のものと同じですが、条件に一致する1つの結果のみが必要であり、どの結果でもかまいません。
ここで、クラスター化インデックスのため、常に同じ結果が得られると想定できますか?
-毎回このクラスタ化インデックスを使用する場合は、はい。
しかし、それを使用しますか?
-いいえ
何故なの?
-できるので。クエリオプティマイザーは、ステートメントを実行するたびに実行パスを自由に選択できます。その時点でそのステートメントに適していると思われるパスはどれでも
しかし、クラスター化インデックスを使用して結果を取得するための最良/最速の方法ではありませんか?
-いいえ、必ずしもそうではありません。クエリを実行するのは初めてかもしれません。 2回目は、キャッシュされた結果を使用する可能性があります(DBMSにSQL Serverではなくそのような機能がある場合)*)。結果が1000回キャッシュから削除され、別の結果がそこに存在する可能性があります。たとえば、このクエリを直前に実行したとします。
_SELECT TOP 1 SensorValue
FROM SensorValues
WHERE SensorId = 53
AND DeviceId = 3819
AND Date < 1339225010
ORDER BY Date ASC ; --- Notice the `ASC` here
_
キャッシュされた結果(上記のクエリから)は、まだ条件に一致しますが、(必要な)順序付けの最初ではない別の結果です。 そしてあなたはDBMSに注文を気にしないように言いました
OK、キャッシュだけがこれに影響を与えることができますか?
-いいえ、他にもたくさんあります。
*:SQL Serverはクエリ結果をキャッシュしませんが、Enterprise Editionには Advanced Scanning 機能があり、これはクエリの同時実行により異なる結果が得られるという点で似ています。これがいつ始まるのか正確にはわかりません。 (ヒントはthnx @Martin Smithです。)
特に指定しない限り、SQLクエリが特定の順序で結果を返すことを絶対に信頼してはいけないと確信していると思います。もちろん、ORDER BY
_なしでTOP (n)
を使用しないでください。もちろん、結果にn行が必要で、どの行が返されるかを気にしない場合は除きます。