3

私は SQL データベースと ASP.NET Web アプリケーションを使用しており、ほとんどのクエリには SQL max 関数が含まれています。

たとえば、次のクエリは約 1 秒かかります。ASP.NET Web サイトと SSMS の両方で実行するのに 36 秒 (プロファイラーを使用する場合)。

SELECT MAX(CONVERT(FLOAT,ISNULL(Runhrs,Runho))) - 
       MIN(CONVERT(FLOAT,ISNULL(Runhrs,Runho))) AS ACTUALHOURSRUN 
FROM REPORTINGSYSTEM.DBO.HL_LOGS 
WHERE ID_LOCATION = @ID_LOCATION AND 
       CONVERT(VARCHAR,TIME_STAMP,102) 
       BETWEEN @STARTDATE AND @ENDDATE

問題のテーブルには約があります。5,000,000 レコードと 45 列。

実行時間を短縮するためにクエリを実行する最良/最速/最も効率的な方法は何ですか?

前もって感謝します...

4

5 に答える 5

2

ID_LOCATION と TIME_STAMP のインデックスは良い候補です。

ただし、TIME_STAMP フィールドを VARCHAR に変換しているため、インデックスを使用できなくなります。代わりに、クエリを作り直して、CONVERT を実行する必要がないようにし、代わりに DATETIME 値を操作します。

例えば

SELECT MAX(CONVERT(FLOAT,ISNULL(Runhrs,Runho))) - MIN(CONVERT(FLOAT,ISNULL(Runhrs,Runho))) AS ACTUALHOURSRUN 
FROM REPORTINGSYSTEM.DBO.HL_LOGS 
WHERE ID_LOCATION = @ID_LOCATION AND TIME_STAMP >= @STARTDATE AND TIME_STAMP < @ENDDATE

@STARTDATE と @ENDDATE も DATETIME パラメーターであることを確認します (これは TIME_STAMP 列のデータ型であると想定しています)

また、Runhrs/Runho 列のデータ型は何ですか? FLOAT/DECIMAL としてまだ格納されていない場合、それが最も適切なデータ型であると思われるのではないでしょうか?

于 2013-02-27T14:34:39.010 に答える
1

実行する必要があることがいくつかあります。

  • 検索する列にインデックスが作成されていることを確認してくださいID_LOCATION-およびにインデックスが必要ですTIME_STAMP(場所全体の日付または日付全体の場所によってクエリを実行する他のクエリがある場合は、個別のインデックスを定義することを検討してください。それ以外の場合は、単一の結合インデックスが機能します)。
  • タイムスタンプを文字列に変換するのをやめる- クエリにネイティブ データ型を使用する@STARTDATE@ENDDATE、条件を次のように置き換えます。TIME_STAMP between CONVERT(datetime,@STARTDATE) and CONVERT(datetime,@ENDDATE)

これらの 2 つの変更により、クエリが高速化されます。特に 2 つ目: 現在、CONVERT(VARCHAR,TIME_STAMP,102)クエリ オプティマイザーは、場所に一致するすべてのものに対してフル スキャンを実行するか、または にインデックスがない場合はフル テーブル スキャンを実行しID_LOCATIONます。インデックス検索では、レコード数を許容レベルまで下げる必要があります。

最後に行う項目はCONVERT(FLOAT,ISNULL(Runhrs, Runho))、最初の 2 つの変更を行ってもクエリの速度が不十分な場合は、 に変更MAX(CONVERT(FLOAT,ISNULL(Runhrs,Runho)))CONVERT(FLOAT, MAX(ISNULL(Runhrs,Runho)))、 に対して同じ変更を行うことMINです。これは、 と のタイプに応じて、機能する場合と機能しない場合がRunhrsありRunhoます。

于 2013-02-27T14:38:27.180 に答える
0
SELECT CONVERT(FLOAT, MAX(Runhrs)), CONVERT(FLOAT, MAX(Runho),
       CONVERT(FLOAT, MIN(Runhrs)), CONVERT(FLOAT, MIN(Runho)
FROM REPORTINGSYSTEM.DBO.HL_LOGS 
WHERE ID_LOCATION = @ID_LOCATION
  AND TIME_STAMP BETWEEN @STARTDATE AND @ENDDATE

SQL を呼び出すコードで自分で減算を行います。(これが .net コードです。)

ID_LOCATION、TIME_STAMP、Runhrs、Runho にインデックスが必要です。4 つのフィールドすべてを持つ 1 つのインデックス。たぶん2つのインデックス。

CREATE INDEX REPORTINGSYSTEM.DBO.HL_LOGS ON ID_LOCATION, TIME_STAMP, Runhrs
CREATE INDEX REPORTINGSYSTEM.DBO.HL_LOGS ON ID_LOCATION, TIME_STAMP, Runho
于 2013-02-27T14:37:52.347 に答える
0
AND CONVERT(VARCHAR,TIME_STAMP,102)

これは実行する方法ではありません。速度が低下します。不要な関数を DATA に適用しないでください。オプティマイザがこのフィールドでインデックスを使用する機会を奪ってしまいます。@STARTDATE逆に、 +@ENDDATEfield と同じデータ型に変換します。(または、このように考えて、2 つの変数と比較できるように ~ 5,000,000 回の変換を行っています)

[time_stamp] BETWEEN @STARTDATE AND @ENDDATE

これが実際に意味することを理解してください:

[time_stamp] > = @STARTDATE AND [time_stamp] <= @ENDDATE

つまり、両方の「境界」日時が含まれます。通常、境界の包括的性質を回避し、次のように綴る方が簡単で正確です。

[time_stamp] > = @STARTDATE AND [time_stamp] < @ENDDATE (with @enddate
being "next day at 00:00:00:000)

MAX(CONVERT(FLOAT,ISNULL(Runhrs,Runho))) -
MIN(CONVERT(FLOAT,ISNULL(Runhrs,Runho))) AS ACTUALHOURSRUN

うわー、これはたくさんの関数呼び出しです! ここで問題になっているのは純粋MAX()/MIN()ではありません。

Max(Runhrs)前述のように単に etc を探すことをお勧めします。その後、減算を行います。

于 2013-02-28T11:40:07.437 に答える
0

まず、Rhnhrs を数値型として保存します。その後、変換を行う必要はありません。

次に、 にインデックスを作成することで、これを高速化しますhl_logs(id_location, time_stamp)。投げることもできRunhrsますRunho。4 つの列すべてに (この順序で) インデックスを使用すると、クエリで元のデータに移動する必要さえありません。

whereインデックスを使用するには、ステートメントを次のように変更する必要があります。

 time_stamp bewteen @startTimeStamp and @EndTimeStamp

変数が関数の引数である場合、SQL エンジンはインデックスを使用しません。

結果のクエリは次のようになります。

select max(coalesce(runhrs, runho)) - min(coalesce(runhrs, runho) as Actual
from REPORTINGSYSTEM.DBO.HL_LOGS 
WHERE ID_LOCATION = @ID_LOCATION AND 
      TIME_STAMP BETWEEN cast(@STARTDATE as datetime) AND cast(@ENDDATE as datetime)
于 2013-02-27T14:36:49.947 に答える