我的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秒
根据您在上面指定的数据类型,可以完全删除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)
哪个是最佳的取决于您的数据分布,值和行数,我从这里无法知道。