2016-07-30 32 views
0

私は1700万レコードテーブルにカーソルを使用しています。それは実際には遅いです、毎秒30ラウンドと言いましょう。どこから始めたらいいのか分かりません。SQL Serverのストアドプロシージャが極端に遅い

すべてのデータを1つのテーブルから、各ストリームのデータが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

あなたは多くの問題を持っています。
すでに述べたように、最初の赤いフラグはカーソルです。
ただし、カーソルなしのロジックをサポートできない場合もあります。

第2に、動的SQLを使用します。パフォーマンスにも貢献しています。

しかし、最大の問題はアプリケーション設計です。
ログから各センサーの個別のテーブルを作成してはいけません。

簡単な解決策ではなく、1700のテーブルを再作成の1700回を追加の列SensorIDONLY ONEtbl_Sensors」テーブルを作成し、それを再利用することです。

関連する問題