SQL表值函数优化/改进

时间:2012-06-14 10:55:07

标签: sql sql-server

我们的查询需要花费很长时间才能完成大型数据集。我想我已经将它跟踪到SQL服务器中的表值函数。

该查询旨在返回两个日期之间的打印使用差异。因此,如果打印机在日期x使用100,在日期y使用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);

2 个答案:

答案 0 :(得分:0)

您的查询似乎计算每个特定设备的时间范围内所有读数的叉积。这在语义上有效,因为MIN和MAX聚合不关心重复。但这很慢。如果您要将100个日期与自己进行比较,则需要处理10,000行。

我建议你在整个时间间隔内计算每个度量/列的MIN和MAX值,然后减去它们。这样你就不需要加入,你需要一次传递数据。像这样:

select Diff = MAX(col) - MIN(col)
from readings
group by DeviceID

答案 1 :(得分: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()将在大量行上运行。然后可以使用以下内容进一步改进(使用相关的子查询来查找您想要的两行)。

作为一个附带好处,即使价值也可以下降,这也会有效。

(我假设设备表存在更简单的查询和更快的性能。)

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)