我们的查询需要花费很长时间才能完成大型数据集。我想我已经将它跟踪到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);
答案 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)