Как создать таблицу SQL Server, составленную из средних значений другой таблицы, которая не запаздывает? - PullRequest
0 голосов
/ 29 апреля 2019

Моя таблица SQL Server работает особенно медленно, и я не могу понять, почему. Эта таблица имеет 7 столбцов, 5 из которых являются функциональными скалярами, которые все вычисляют среднесуточные аналогичные данные в другой таблице за определенный день. Первые два столбца - это просто отметка времени для каждого дня и itemId для конкретного элемента, который создает данные.

Первые два столбца являются первичными ключами, и функции являются детерминированными, но не сохраняются, потому что функция использует пользовательские данные, я подозреваю, потому что я выбираю из этой таблицы необработанных данных в своих функциях, но в этом я не уверен.

Если я установлю тип одного из моих столбцов как усредненную функцию, то для расчета 20 записей потребуется 5 секунд. Это слишком медленно для нашего приложения и вызывает ошибки. Каков наилучший способ сделать это? если текущая настройка является наилучшей, как я могу уменьшить вызванную задержку? В основном я хочу избежать жесткого кодирования данных, как того требуют мои коллеги, поскольку мне нужно выяснить, каким образом таблица SQL будет автоматически заполняться каждый день и для каждого нового ItemId, добавляемого в таблицу необработанных данных.

Спасибо!

Я уже пытался сделать функцию детерминированной. Пользовательские данные не позволяют мне сделать ее сохраняемой, что, я думаю, может быть возможным увеличением скорости, поскольку средние значения обычно не нужно менять после того, как они сделаны.

Это на Microsoft Windows Server под управлением SQL Server 2017

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

Запрос занимает около 5 секунд для загрузки, когда у меня есть 20 строк, и это ужасно, так как мне понадобятся сотни строк и для него не более 1 секунды

1 Ответ

0 голосов
/ 29 апреля 2019

На основании типов данных, которые вы указали выше, операторы 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)

Что будет оптимальным, зависит от вашего распределения данных, значений и количества строк, которые я не могу знать здесь.

...