SQL Server存储过程非常慢

时间:2016-07-30 12:54:43

标签: sql-server performance cursor database-administration

我在1700万条记录表上使用了光标。它真的很慢,比方说每秒30发。我不知道从哪里开始。

它将1个表中的所有数据传输到多个表中,每个表每年都有数据。

以下是代码:

CREATE PROCEDURE [dbo].[Importer2]
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @LogId bigint;
    DECLARE @SensorID int;
    DECLARE @ValueNumeric decimal(12,4);
    DECLARE @ValueString nvarchar(20);
    DECLARE @DateAdded datetime;
    DECLARE @Status_ NVARCHAR(10)
    DECLARE @SQLString_ NVARCHAR(MAX)
    DECLARE @Year_  NVARCHAR(4)
    DECLARE @TableName_ NVARCHAR(100) --= '[ScadaData].[dbo].[2016_123456]'
    DECLARE @TableNameOnly_ NVARCHAR(100)
    DECLARE @ValueStringTMP_ NVARCHAR(20)
    DECLARE @Measure_ datetime

    DECLARE @ImporterCursor AS CURSOR;

    SET @Status_ = 'OK'

    SELECT @LogId = Value 
    FROM dbo.AppSettings  
    WHERE dbo.AppSettings.Setting = 'LastImportedId';

    SET @ImporterCursor = CURSOR FAST_FORWARD FOR
        SELECT * 
        FROM dbo.Logs l 
        WHERE l.LogID > @LogId;

    OPEN @ImporterCursor;

    FETCH NEXT FROM @ImporterCursor INTO @LogId, @SensorID, @ValueNumeric, @ValueString, @DateAdded;

    WHILE @@FETCH_STATUS = 0
    BEGIN
        IF (@DateAdded IS NOT null)
        BEGIN
            SET @TableName_ = '[ScadaData].[dbo].[Y' + CONVERT(varchar,YEAR(@DateAdded)) + 'S' + CONVERT(varchar,@SensorID) + ']'
            SET @TableNameOnly_ = 'Y' + CONVERT(varchar,YEAR(@DateAdded)) + 'S' + CONVERT(varchar,@SensorID)

            --table does not exists. Create one
            IF NOT EXISTS (SELECT 1 FROM ScadaData.dbo.sysobjects 
                           WHERE xtype = 'U' AND name = @TableNameOnly_)
            BEGIN
                SET @SQLString_ = 'CREATE TABLE ' + @TableName_ + '(
                    [LogID] [bigint] IDENTITY(1,1) NOT NULL,
                    [ValueNumeric] [decimal](12, 4) NULL,
                    [ValueString] [nvarchar](20) NULL,
                    [DateAdded] [datetime2](7) NULL DEFAULT (GETUTCDATE()),
                    CONSTRAINT [PK_Logs' + @TableNameOnly_ + '] PRIMARY KEY CLUSTERED ([LogID] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY];
                    CREATE NONCLUSTERED INDEX [DateAddedDESC_' + @TableNameOnly_ + '] ON [dbo].[' + @TableNameOnly_ + '] ([DateAdded] DESC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);'

            EXEC sp_executesql @SQLString_
        END

        --Insert or Update if ValueNumeric is sent
        IF @ValueNumeric IS NOT NULL
        BEGIN
            SET @SQLString_ =               'DECLARE @ValueNumericTmp_  DECIMAL(12,4);'
            SET @SQLString_ = @SQLString_ + 'DECLARE @LogID             BIGINT;'
            SET @SQLString_ = @SQLString_ + 'SELECT TOP 1 @ValueNumericTmp_ = ValueNumeric, @LogID = LogID FROM ' + @TableName_ + ' ORDER BY DateAdded DESC;'
            SET @SQLString_ = @SQLString_ + 'IF (@ValueNumericTmp_ = ' + CONVERT(varchar, @ValueNumeric) + ')'
            SET @SQLString_ = @SQLString_ + '   UPDATE ' + @TableName_ + ' SET DateAdded = ''' + CONVERT(varchar,@DateAdded,121) + ''' WHERE LogID = @LogID;'
            SET @SQLString_ = @SQLString_ + 'ELSE'
            SET @SQLString_ = @SQLString_ + '   INSERT INTO ' + @TableName_ + ' (ValueNumeric, ValueString, DateAdded) VALUES (' + CONVERT(varchar,@ValueNumeric) +', ' + ISNULL(''''  + @ValueString + '''','NULL') + ', GETUTCDATE());'

            EXEC (@SQLString_)
        END

        --Insert or Update if ValueString is sent
        IF @ValueString IS NOT NULL
        BEGIN
            SET @SQLString_ =               'DECLARE @ValueStringTMP_   NVARCHAR(20);'
            SET @SQLString_ = @SQLString_ + 'DECLARE @LogID             BIGINT;'
            SET @SQLString_ = @SQLString_ + 'SELECT TOP 1 @ValueStringTMP_ = ValueString, @LogID = LogID FROM ' + @TableName_ + ' ORDER BY DateAdded DESC;'
            SET @SQLString_ = @SQLString_ + 'IF (@ValueStringTMP_ = ''' + @ValueString + ''')'
            SET @SQLString_ = @SQLString_ + '   UPDATE ' + @TableName_ + ' SET DateAdded = ''' + CONVERT(varchar,@DateAdded,121) + ''' WHERE LogID = @LogID;'
            SET @SQLString_ = @SQLString_ + 'ELSE'
            SET @SQLString_ = @SQLString_ + '   INSERT INTO ' + @TableName_ + ' (ValueNumeric, ValueString, DateAdded) VALUES (' + CONVERT(varchar,@ValueNumeric) +', ''' + @ValueString + ''', GETUTCDATE());'

            EXEC sp_executesql @SQLString_
        END
    END

    UPDATE dbo.AppSettings 
    SET dbo.AppSettings.[Value] = CAST(@LogId AS VARCHAR (50)) 
    WHERE dbo.AppSettings.Setting = 'LastImportedId';

   FETCH NEXT FROM @ImporterCursor INTO @LogId, @SensorID, @ValueNumeric, @ValueString, @DateAdded;
    END

    CLOSE @ImporterCursor;
    DEALLOCATE @ImporterCursor;
END

1 个答案:

答案 0 :(得分:2)

你有很多问题 如前所述,第一个红旗是光标 但是,有时你不能在没有光标的情况下支持逻辑,这是你的理由。

其次,您使用动态SQL。它也有助于提高绩效。

但是,你最大的问题是应用程序设计 您不得从日志中为每个传感器创建单独的表。

简单的解决方案是创建仅一个" tbl_Sensors"带有附加列SensorID的表,并重复使用1700次,而不是重新创建1700个表。