主キーでソート順が指定されているが、SELECTでソートが実行されている


15

センサーデータをSensorValuesテーブルに格納しています。テーブルと主キーは次のとおりです。

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

実行計画

編集:代わりにこれを行うことはできますか?

テーブルはDeviceId、SensorId、Dateでソートされているため、1つのDeviceIdと1つのSensorIdのみを指定してSELECTを実行するため、出力セットはすでにDate DESCでソートされているはずです。だから、次の質問がすべての場合に同じ結果をもたらすのだろうか?

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010

以下の@Catcallによると、ソート順は保存順と同じではありません。つまり、返された値が既にソートされた順序にある​​とは想定できません。

編集:私はこのCROSS APPLYソリューションを試してみましたが、運はありません

@Martin Smithは、パーティションに対して結果を適用することを提案しました。この類似の問題について説明しているブログ投稿(パーティション化されたテーブルの非クラスター化インデックス)を見つけ、スミスが提案したものとやや似たソリューションを試しました。ただし、ここで運はありません。実行時間は元のソリューションと同等です。

WITH Boundaries(boundary_id)
AS
(
  SELECT boundary_id
  FROM sys.partition_functions pf
  JOIN sys.partition_range_values prf ON pf.function_id = prf.function_id
  WHERE pf.name = 'PF'
  AND prf.value <= 1339225010
  UNION ALL
  SELECT max(boundary_id) + 1
  FROM sys.partition_functions pf
  JOIN sys.partition_range_values prf ON pf.function_id = prf.function_id
  WHERE pf.name = 'PF'
  AND prf.value <= 1339225010
),
Top1(SensorValue)
AS
(
  SELECT TOP 1 d.SensorValue
  FROM Boundaries b
  CROSS APPLY
  (
    SELECT TOP 1 SensorValue
      FROM SensorValues
      WHERE  SensorId = 53
        AND DeviceId = 3819
        AND "Date" < 1339225010
        AND $Partition.PF(Date) = b.boundary_id
        ORDER BY Date DESC
  ) d
  ORDER BY d.Date DESC
)
SELECT SensorValue
FROM Top1

OPTION MAXDOP 1は役に立ちません。以下の@Martin Smithで指定されているように、パーティション化が原因であるように思われます
...-m__

回答:


13

パーティション化されていないテーブルの場合、次のプランを取得します

計画1

には単一のシーク述語がありSeek Keys[1]: Prefix: DeviceId, SensorId = (3819, 53), Start: Date < 1339225010ます。

SQL Serverが最初の2つの列で等値シークを実行し、(インデックスがで定義されているように)1339225010順序付けられた範囲シークを開始できることを意味しますFORWARD[Date] DESC

TOPオペレータは、最初の行が放出された後、シークから複数の行を要求して停止します。

パーティション構成と機能を作成するとき

CREATE PARTITION FUNCTION PF (int)
AS RANGE LEFT FOR VALUES (1000, 1339225009 ,1339225010 , 1339225011);
GO
CREATE PARTITION SCHEME [MyPartitioningScheme]
AS PARTITION PF
ALL TO ([PRIMARY] );

そして、テーブルに次のデータを入力します

INSERT INTO [dbo].[SensorValues]    
/*500 rows matching date and SensorId, DeviceId predicate*/
SELECT TOP (500) 3819,53,1, ROW_NUMBER() OVER (ORDER BY (SELECT 0))           
FROM master..spt_values
UNION ALL
/*700 rows matching date but not SensorId, DeviceId predicate*/
SELECT TOP (700) 3819,52,1, ROW_NUMBER() OVER (ORDER BY (SELECT 0))           
FROM master..spt_values
UNION ALL 
/*1100 rows matching SensorId, DeviceId predicate but not date */
SELECT TOP (1100) 3819,53,1, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) + 1339225011      
FROM master..spt_values

SQL Server 2008の計画は次のようになります。

計画2

シークから放出される実際の行数は500です。プランはシーク述語を示しています

Seek Keys[1]: Start: PtnId1000 <= 2, End: PtnId1000 >= 1, 
Seek Keys[2]: Prefix: DeviceId, SensorId = (3819, 53), Start: Date < 1339225010

ここで説明するスキップスキャンアプローチを使用していることを示す

クエリオプティマイザーが拡張され、1つの条件でシークまたはスキャン操作をPartitionID(論理先頭列として)および場合によっては他のインデックスキー列で実行でき、その後、別の条件で第2レベルのシークを実行できます。 1つ以上の追加列で、第1レベルのシーク操作の資格を満たす個別の値ごと。

このプランはシリアルプランであるため、特定のクエリについては、SQL Serverがパーティションを降順で処理したことを確認するとdate、元のプランTOPが引き続き機能し、最初に一致した行が処理された後に処理を停止できるようです続行して残りの499件の一致を出力するのではなく、見つかりました。

実際、2005年の計画はそのアプローチをとっているように見えます

2005年の計画

2008年に同じ計画を立てるのが簡単かどうか、またはそれをシミュレートするためにOUTER APPLYオンが必要になるかどうかはわかりませんsys.partition_range_values



9

多くの人は、クラスター化インデックスが出力の並べ替え順序を保証すると信じています。しかし、それはそれがすることではありません。ディスク上のストレージの順序を保証します

たとえば、このブログ投稿、およびこの長い議論を参照してください。


1
さて、以前、OPは「日付列で並べ替えられた値を保存しているので、並べ替えは行われないと考えていたでしょう」と述べました。したがって、少なくとも問題の一部は、クラスター化インデックスの機能に関する誤解です。とにかくそれをまっすぐにするのは良いことだと思う。
マイクシェリル 'キャットリコール'

たぶん私は頑固なだけです(だから私を許してください;-))。とにかく、Hugo Kornelisのブログ記事を読んだことがありますが、とても簡単です。ただし、彼の例では、1つのクラスター化インデックスと1つの非クラスター化インデックスを使用しています。非クラスター化インデックスはサイズが小さいため、実行プランで使用されています。私の場合、クラスタ化インデックスは1つしかありませんが、SQLサーバーは間違った順序で値を返すことができます(使用する小さなインデックスがなく、全テーブルスキャンが非常に遅いです)。
m__

これを新しい質問(トピック外)に移動しました
m__

5

並列計画のためにSORTが必要であると推測しています。私はこれを薄暗い遠いブログ記事に基づいています:しかし、MSDNこれを見つけました

だから、MAXDOP 1で試して、何が起こるか見てみましょう...

また、@ sql kiwiのSimple Exchange に関するブログ投稿で、「Exchange Operator」の下にあると思われます。そして「DOP依存」はこちら


私はdate以前にパーティション機能をセットアップすることを気にしませんでしたが。今、私は持っているし、パーティション分割しているようです、2005年の犯人は、おそらくこの特定のクエリに対してより良い振る舞いをしています。
マーティンスミス

1

基本的にあなたは正しいです-主キーは「DeviceId、SensorId、Date」の順序であるため、キーのデータは日付でソートされていないため、使用できません。あなたの鍵が異なるため、「日付とデバイスSensorId」にあった場合、キーのデータが考え日付でソートされ、そのように使用することができ...


私はすでにあなたが述べた方法でキーを変更しようとしたので、申し訳ありません。とにかく、3つすべての列に非クラスター化インデックスを作成してみて、何が得られるかを確認します。(不足しているインデックスの探求は続きます
; ;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.