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

问题描述 投票:0回答:1

我的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秒

sql sql-server optimization query-optimization
1个回答
0
投票

根据您在上面指定的数据类型,可以完全删除CONVERT语句。此外,由于Time_Stamp比较看起来是“它必须与@Time_Stamp值相同的日子,并且当天不晚于该值”,整个查询可以像这样重写:

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)

哪个是最佳的取决于您的数据分布,值和行数,我从这里无法知道。

© www.soinside.com 2019 - 2024. All rights reserved.