web-dev-qa-db-ja.com

数百万行のテーブルでクエリを高速化する方法

問題:

私は約37mln行で構成される大きなテーブルで作業しています。データには、特定の時間に行われた多くのデバイスの測定値が含まれます。 「2013-09-24 10:45:50」。毎日、これらすべてのデバイスがさまざまな時間にさまざまな間隔で多くの測定値を送信しています。 2013年1月から2013年2月1日までの2か月間の毎日の最も実際の(「実際の」とは、毎日行われたすべての測定から最新のものを意味する)すべての測定を選択するクエリを作成したいです。

問題は、さまざまな列で作成したすべてのインデックスにもかかわらず、このクエリの実行に非常に時間がかかることです。また、測定が指定されたときにmax(MeterDate)とMeasurementsIdを含む補助テーブルも作成しました。 MeterDateにはインデックスを作成できないことに気づきました。これには、インデックスを作成するのに役立たない日時が含まれているからです。だから私はMeterDate-> CONVERT(date、MeterDate)を変換しました。 [dbo]。[Measurements]を使用して補助テーブルを結合した後でも、クエリは高速になりますが、クエリは12秒以上かかり、長すぎます。

テーブルの構造:

Create table [dbo].[Measurements]

[Id] [int] IDENTITY(1,1) NOT NULL,
[ReadType_Id] [int] NOT NULL,
[Device_Id] [int] NULL,
[DeviceInterface] [tinyint] NULL,
[MeterDate] [datetime] NULL,
[MeasureValue] [decimal](18, 3) NULL

Measurementsテーブルのすべての行には、直接MeterDateの測定値が含まれます。 「2008-04-04 13:28:44.473」

直接選択構造:

DECLARE @startdate datetime= '2013-07-01'; 
DECLARE @enddate datetime = '2013-08-01';

SELECT *
FROM [dbo].[Measurements] 
WHERE [MeterDate] BETWEEN @startdate and @enddate 

誰かがテーブルを再構築する方法、新しいクエリを追加する方法、またはクエリを少し高速化する列にインデックスを追加する方法を知っていますか?どんな情報でも事前にありがとう。

編集:

私が使用したテーブルは、このクエリによって作成されました

with t1 as
(
    Select  [Device_Id], [DeviceInterface],  CONVERT(date,  MeterDate) as OnlyDate, Max(MeterDate) as MaxMeterDate
    FROM [dbo].[Measurements] 
    GROUP BY [Device_Id], [DeviceInterface], CONVERT(date,  MeterDate)
)
Select t1.[Device_Id], t1.[DeviceInterface],t1.[OnlyDate], r.Id  
INTO [dbo].[MaxDatesMeasurements]
FROM t1
JOIN [dbo].[Measurements] as r ON r.Device_Id = t1.Device_Id AND r.DeviceInterface = t1.DeviceInterface AND r.MeterDate = t1.MaxMeterDate

次に、新しく作成したテーブル[dbo]。[MaxDatesMeasurements]を古い[dbo]。[Measurements]と結合し、直接の行を選択したかった

DECLARE @startdate datetime= '2013-07-01'; 
DECLARE @enddate datetime = '2013-08-01'; 


Select *
From [dbo].[MaxDatesMeasurements] as t1 
Join [dbo].[Measurements] as t2 on t1.[Id] = t2.[Id] 
WHERE t1.[OnlyDate] BETWEEN @startdate AND @enddate
2
KatarzynaMoss

これが発生する理由としては、 local variables を使用していることが考えられます。

問題は、さまざまな列で作成したすべてのインデックスにもかかわらず、このクエリの実行に非常に時間がかかることです。

同様の設定を使用した例を次に示します。 Stack Overflowスキーマ には、Votesと呼ばれる、このような狭いテーブルがあります。

NUTS

CreationDateにインデックスがない場合、唯一のオプションはクラスター化インデックスをスキャンすることです。ただし、CreationDateのみで作成する場合、クラスター化インデックスをスキャンして述語を適用するよりも、残りの列に対してキールックアップを行う方が安価であると考える場合、オプティマイザーはそのインデックスを使用することを選択できます。

CREATE INDEX ix_yourmom ON dbo.Votes(CreationDate)

このクエリの場合:

DECLARE @StartDate DATETIME = '2010-07-01';
DECLARE @EndDate DATETIME = '2010-07-02';

SELECT *
FROM   dbo.Votes AS v
WHERE  v.CreationDate BETWEEN @StartDate AND @EndDate;
GO

NUTS

不明な変数のカーディナリティの推定 を使用すると、16.4317%になります。これにより、クラスター化されたインデックススキャンが発生し、クエリ全体をカバーするインデックスに対するインデックスリクエストが失われます。

RECOMPILEを使用してクエリを実行すると、 パラメータ埋め込み最適化 が許可されます。

DECLARE @StartDate DATETIME = '2010-07-01';
DECLARE @EndDate DATETIME = '2010-07-02';

SELECT *
FROM   dbo.Votes AS v
WHERE  v.CreationDate BETWEEN @StartDate AND @EndDate
OPTION ( RECOMPILE );

これにより、クエリプランが異なり、より正確な見積もりが得られます。

NUTS

お役に立てれば!

1
Erik Darling