如何创建由另一个不滞后的表的平均值组成的SQL Server表?

时间:2019-04-29 19:27:00

标签: sql sql-server optimization query-optimization

我的SQL Server表运行特别慢,我不知道为什么。该表有7列,其中5列是函数标量,它们全部计算特定日期另一张表中相似数据的每日平均值。前两列只是每天的时间戳,以及正在创建数据的特定项目的itemId。

前两列都是主键,这些函数是确定性的,但由于该函数使用用户数据而无法保留,我怀疑是因为我从函数中的原始数据表中进行选择,但是我不确定。

如果将某列的类型设置为平均函数,则最终需要5秒钟来计算20个条目。这对于我们的应用程序来说太慢了,并且会导致错误。做这个的最好方式是什么?如果当前设置是最好的,如何减少造成的延迟?我主要是想避免像同事们希望的那样对数据进行硬编码,因为我需要找出一种方法,使SQL表每天自动填充,并为添加到原始数据表中的每个新ItemId自动填充

谢谢!

我已经尝试过使函数具有确定性,用户数据使我无法使其保持不变,我认为这可能会提高速度,因为平均数通常不需要在完成后就更改。

这是在运行SQL Server 2017的Microsoft Windows Server上

CREATE TABLE [dbo].[DCP_AvgData]
(
    [AssetID] [NVARCHAR](255) NOT NULL,
    [Time_Stamp] [DATETIME2](7) NOT NULL,
    [DeviceFlowYesterday] AS ([dbo].[AVERG]([Time_Stamp], [AssetID])),

    CONSTRAINT [PK_DCP_AvgData] 
        PRIMARY KEY CLUSTERED ([Time_Stamp] ASC, [AssetID] ASC)
                    WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)
GO

ALTER FUNCTION [dbo].[AVERG]
    (--@floatVal FLOAT,
     @Time_Stamp DATETIME2(7), 
     @AssetID NVARCHAR(255))
RETURNS FLOAT
WITH SCHEMABINDING
AS
BEGIN
    -- Declare the return variable here
    DECLARE @ResultVar FLOAT
    DECLARE @result FLOAT
    DECLARE @time DATETIME2(7)

    SET @result = (SELECT MAX(Pump1Yesterday) 
                   FROM dbo.DCP_FloatData
                   WHERE @AssetID = AssetID 
                     AND CONVERT(DATETIME2(7), Time_Stamp, 121) >= CONVERT(DATETIME2(7), DATEADD(dd, 0, DATEDIFF(dd, 0, @Time_Stamp)), 121) 
                     AND CONVERT(DATETIME2(7), Time_Stamp, 121) <= CONVERT(DATETIME2(7), @Time_Stamp, 121) 
                     AND Pump1Yesterday>5);

    -- Return the result of the function
    RETURN @ResultVar
END

当我有20行时,查询需要大约5秒钟的时间来加载,这太可怕了,因为我需要数百行,并且最多只能有1秒的时间

1 个答案:

答案 0 :(得分:0)

根据上面指定的数据类型,CONVERT语句可以完全删除。另外,由于Time_Stamp比较看起来是“它必须与@Time_Stamp值在同一天,并且一天中不得早于该值”,因此整个查询可以这样重写: / p>

ALTER FUNCTION [dbo].[AVERG]
(
    --@floatVal FLOAT,
    @Time_Stamp Datetime2(7), 
    @AssetID nvarchar(255)
)
RETURNS FLOAT
WITH SCHEMABINDING
AS
BEGIN
    -- Declare the return variable here
    DECLARE @ResultVar FLOAT
    SET @ResultVar=( SELECT MAX(Pump1Yesterday) FROM dbo.DCP_FloatData
        where @AssetID=AssetID 
        AND Time_Stamp >= CAST(@Time_Stamp as Date) 
        AND Time_Stamp <= @Time_Stamp
        AND Pump1Yesterday>5);

    -- Return the result of the function
    RETURN @ResultVar
END

您还应该在数据源表(DCP_FloatData)上有一个索引,知道该表中Time_Stamp字段的数据类型将很有帮助。我假设此代码为DATETIME2(7)

最后,您的数据源应具有索引。根据上面的代码,这两个索引之一应该是最佳的:

CREATE INDEX Idx_DCP_FloatData_AssetId_Time_Stamp_Include
ON DCP_FloatData (AssetId, Time_Stamp) INCLUDE (Pump1Yesterday)

-- OR

CREATE INDEX Idx_DCP_DCP_FloatData_AssetId_Time_Stamp_Pump1Yesterday
ON DCP_FloatData (AssetId, Time_Stamp, Pump1Yesterday)

哪种最佳选择取决于您的数据分布,值和行数,我从这里无法得知。