web-dev-qa-db-ja.com

ストレージの順序と結果の順序

これは 主キーでソート順が指定されているが、ソートは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でソートされた値を返すということです。

8
m__

なぜしないでください必要があるのか​​、なぜしないのかについて説明します-)指定しない限り、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に指示します。 これらの行をDateの降順で並べます。、順序を気にせず、1行のみを保持および-最後に-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、キャッシュだけがこれに影響を与えることができますか?
-いいえ、他にもたくさんあります。

  • 他のインデックスは、当時、DBMSによってこのクエリに適していると見なされていました。
  • 開発者が、このクラスター化インデックスを変更または完全に削除しました。
  • あなたまたは他の開発者がオプティマイザがCIよりも使用する方が効率的であると判断した別のインデックスを追加しました。
  • 新しいバージョンに更新し、新しいオプティマイザーにマイナーなバグがあるか、実行プランのランク付けと選択の方法が変更されています。
  • 統計を更新しました。
  • 代わりに並列実行が選択されました。

*:SQL Serverはクエリ結果をキャッシュしませんが、Enterprise Editionには Advanced Scanning 機能があり、これはクエリの同時実行により異なる結果が得られるという点で似ています。これがいつ始まるのか正確にはわかりません。 (ヒントはthnx @Martin Smithです。)


特に指定しない限り、SQLクエリが特定の順序で結果を返すことを絶対に信頼してはいけないと確信していると思います。もちろん、ORDER BY_なしでTOP (n)を使用しないでください。もちろん、結果にn行が必要で、どの行が返されるかを気にしない場合は除きます。

15
ypercubeᵀᴹ