1

大規模なデータセットで完了するのに非常に長い時間がかかるクエリがあります。SQLサーバーのテーブル値関数まで追跡したと思います。

クエリは、2 つの日付間の印刷使用量の違いを返すように設計されています。そのため、プリンタの使用量が x の時点で 100 回、ya の日付で 200 回だった場合、使用量が 100 回変化したことを反映する行を返す必要があります。

これらの測定値は定期的に (毎日ではなく) 取得され、MeterReadings というテーブルに格納されます。テーブル値関数のコードは次のとおりです。これは、別の SQL クエリから呼び出され、デバイス テーブルで返されたテーブルを内部結合で結合して、追加のデバイス情報を取得します。

以下を最適化する方法についてのアドバイスをいただければ幸いです。

ALTER FUNCTION [dbo].[DeviceUsage]
-- Add the parameters for the stored procedure here
( @StartDate DateTime , @EndDate DateTime )
RETURNS table
AS
RETURN
(

SELECT      MAX(dbo.MeterReadings.ScanDateTime) AS MX,
        MAX(dbo.MeterReadings.DeviceTotal - reading.DeviceTotal) AS TotalDiff, 
        MAX(dbo.MeterReadings.TotalCopy - reading.TotalCopy) AS CopyDiff,
        MAX(dbo.MeterReadings.TotalPrint - reading.TotalPrint) AS PrintDiff,
        MAX(dbo.MeterReadings.TotalScan - reading.TotalScan) AS ScanDiff,
        MAX(dbo.MeterReadings.TotalFax - reading.TotalFax) AS FaxDiff,
        MAX(dbo.MeterReadings.TotalMono - reading.TotalMono) AS MonoDiff,
        MAX(dbo.MeterReadings.TotalColour - reading.TotalColour) AS ColourDiff, 
        MIN(reading.ScanDateTime) AS MN, dbo.MeterReadings.DeviceID

FROM        dbo.MeterReadings INNER JOIN (SELECT * FROM dbo.MeterReadings WHERE     
        (dbo.MeterReadings.ScanDateTime > @StartDate) AND 
        (dbo.MeterReadings.ScanDateTime < @EndDate) ) 
        AS reading ON dbo.MeterReadings.DeviceID = reading.DeviceID

WHERE       (dbo.MeterReadings.ScanDateTime > @StartDate) AND (dbo.MeterReadings.ScanDateTime < @EndDate)

GROUP BY    dbo.MeterReadings.DeviceID);
4

2 に答える 2

0

値は時間の経過とともにのみ増加する可能性があるという仮定の下で、それは確かに単純化することができます。

SELECT
  DeviceID,
  MIN(ScanDateTime)                      AS MN,
  MAX(ScanDateTime)                      AS MX,
  MAX(DeviceTotal ) - MIN(DeviceTotal)   AS TotalDiff,
  MAX(TotalCopy   ) - MIN(TotalCopy  )   AS CopyDiff,
  MAX(TotalPrint  ) - MIN(TotalPrint )   AS PrintDiff,
  MAX(TotalScan   ) - MIN(TotalScan  )   AS ScanDiff,
  MAX(TotalFax    ) - MIN(TotalFax   )   AS FaxDiff,
  MAX(TotalMono   ) - MIN(TotalMono  )   AS MonoDiff,
  MAX(TotalColour ) - MIN(TotalColour)   AS ColourDiff
FROM
  dbo.MeterReadings
WHERE
      ScanDateTime > @StartDate
  AND ScanDateTime < @EndDate
GROUP BY
  DeviceID

1, 3, 5, 7, 9これは、日付を読んでいて、レポートしたい2 -> 8場合は、 reading 7-を想定していますreading 3。私はあなたが欲しかったと思っただろうreading 7- reading 1

上記のクエリは、比較的狭い範囲で問題ないはずです。時間の範囲がMAX() - MIN()非常に長い場合、は多数の行で動作します。これは、次の方法でさらに改善できる可能性があります(必要な2つの行だけを検索するための相関サブクエリを使用)。

副次的な利点として、値が上下する可能性がある場合でも、これは機能します。

(より単純なクエリとより高速なパフォーマンスのために、Deviceテーブルが存在すると想定しています。)

SELECT
  Device.DeviceID,
  start.ScanDateTime                      AS MN,
  finish.ScanDateTime                     AS MX,
  finish.DeviceTotal - start.DeviceTotal  AS TotalDiff,
  finish.TotalCopy   - start.TotalCopy    AS CopyDiff,
  finish.TotalPrint  - start.TotalPrint   AS PrintDiff,
  finish.TotalScan   - start.TotalScan    AS ScanDiff,
  finish.TotalFax    - start.TotalFax     AS FaxDiff,
  finish.TotalMono   - start.TotalMono    AS MonoDiff,
  finish.TotalColour - start.TotalColour  AS ColourDiff
FROM
  dbo.Device                 AS device
INNER JOIN
  dbo.MeterReadings          AS start
    ON  start.DeviceID = device.DeviceID
    AND start.ScanDateTime = (SELECT MIN(ScanDateTime)
                                FROM dbo.MeterReadings
                               WHERE DeviceID = device.DeviceID
                                 AND ScanDateTime > @startDate
                                 AND ScanDateTime < @endDate)
INNER JOIN
  dbo.MeterReadings          AS finish
    ON  finish.DeviceID     = device.DeviceID
    AND finish.ScanDateTime = (SELECT MAX(ScanDateTime)
                                FROM dbo.MeterReadings
                               WHERE DeviceID = device.DeviceID
                                 AND ScanDateTime > @startDate
                                 AND ScanDateTime < @endDate)

必要に応じて、これを変更して、@startDate以前の最初の日付として開始を取得することもできます。

編集: @startDate以前の最初の日付として読み取り開始を選択するように変更。

SELECT
  Device.DeviceID,
  start.ScanDateTime                                                AS MN,
  finish.ScanDateTime                                               AS MX,
  COALESCE(finish.DeviceTotal, 0) - COALESCE(start.DeviceTotal, 0)  AS TotalDiff,
  COALESCE(finish.TotalCopy  , 0) - COALESCE(start.TotalCopy  , 0)  AS CopyDiff,
  COALESCE(finish.TotalPrint , 0) - COALESCE(start.TotalPrint , 0)  AS PrintDiff,
  COALESCE(finish.TotalScan  , 0) - COALESCE(start.TotalScan  , 0)  AS ScanDiff,
  COALESCE(finish.TotalFax   , 0) - COALESCE(start.TotalFax   , 0)  AS FaxDiff,
  COALESCE(finish.TotalMono  , 0) - COALESCE(start.TotalMono  , 0)  AS MonoDiff,
  COALESCE(finish.TotalColour, 0) - COALESCE(start.TotalColour, 0)  AS ColourDiff
FROM
  dbo.Device                 AS device
LEFT JOIN
  dbo.MeterReadings          AS start
    ON  start.DeviceID = device.DeviceID
    AND start.ScanDateTime = (SELECT MAX(ScanDateTime)
                                FROM dbo.MeterReadings
                               WHERE DeviceID = device.DeviceID
                                 AND ScanDateTime < @startDate)
LEFT JOIN
  dbo.MeterReadings          AS finish
    ON  finish.DeviceID     = device.DeviceID
    AND finish.ScanDateTime = (SELECT MAX(ScanDateTime)
                                FROM dbo.MeterReadings
                               WHERE DeviceID = device.DeviceID
                                 AND ScanDateTime < @endDate)
于 2012-06-14T11:19:22.247 に答える
0

あなたのクエリは、特定のデバイスごとに時間範囲内のすべての読み取り値の外積を計算しているようです。MIN および MAX 集計は重複を気にしないため、これは意味的に機能します。しかし、これは非常に遅いです。100 の日付を比較する場合、10,000 行を処理する必要があります。

時間間隔全体で各メトリック/列の MIN 値と MAX 値を計算し、それらを差し引くことをお勧めします。そうすれば、参加する必要がなく、データの 1 回のパスが必要になります。このような:

select Diff = MAX(col) - MIN(col)
from readings
group by DeviceID
于 2012-06-14T11:04:25.817 に答える