Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQL表值函数优化/改进_Sql_Sql Server - Fatal编程技术网

SQL表值函数优化/改进

SQL表值函数优化/改进,sql,sql-server,Sql,Sql Server,我们有一个查询,需要很长时间才能用一个大数据集完成。我想我已经找到了SQL server中的表值函数 该查询旨在返回两个日期之间的打印使用差异。因此,如果打印机在日期x的使用率为100,在日期y的使用率为200,则需要返回一行,以反映其使用率更改为100 这些读数定期采集,但不是每天采集,并存储在一个名为MeterReadings的表格中。表值函数的代码如下所示。然后从另一个SQL查询调用它,该查询将设备表上的返回表与内部联接联接起来,以获取额外的设备信息 如能就如何优化以下内容提供任何建议,将

我们有一个查询,需要很长时间才能用一个大数据集完成。我想我已经找到了SQL server中的表值函数

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

您的查询似乎是计算每个特定设备在一个时间范围内的所有读数的叉积。这在语义上是有效的,因为最小和最大聚合不关心重复项。但这是非常缓慢的。如果要将100个日期与其进行比较,则需要处理10000行

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

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

假设一个值只能随时间增加,那么它当然可以简化

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
如果需要,也可以修改此选项,将开始日期作为@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)

此函数仅在值仅不断增加时执行您要求的操作。从一个日期到下一个日期,任何值都会减少吗?另外,如果在1号有读数,那么在3号,但是您的@startDate是2号,哪个日期应该提供第一个读数?我的理解是它应该是第一个,但是这里的代码使用第三个。谢谢你的回复。如果读数下降,则有一个字段设置为false,这样我就可以很容易地过滤掉它。在应用程序的上下文中,我永远不会期望使用率下降。同样在回答你的第二点时,你的假设是正确的。查询应该从第一个返回。您的示例代码使用的是第三个,而不是第一个。因为这里的答案是基于你的例子,他们也一样。我将编辑我的答案来展示如何做到这一点,但它将不可避免地给出与您当前函数不同的结果。非常感谢。这个答案很完美。你是对的,这大大缩短了查询时间。啊,是的,谢谢。我知道以前的查询哪里出错了。现在一切正常,非常感谢。
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)