2016-07-30 77 views
0

我在1700万条记录表上使用了一个游标。它真的很慢,比方说每秒30发。我不知道从哪里开始。SQL Server存储过程极其缓慢

它将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

不使用游标。更改为设置基本查询 – Squirrel

+0

特别是在17亿行! – scsimon

回答

2

你有很多的问题。
如前所述,第一个红旗是一个游标。
但是,有时您无法支持没有游标的逻辑,这是您的情况。

其次,你使用动态SQL。它也有助于表现。

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

的简单的解决方案是创建一个附加列SensorIDONLY ONEtbl_Sensors”表并重复使用1700倍而不是重新创建1700页的表。