2016-07-30 16 views
0

Ich verwende einen Cursor auf meine 1700 Millionen Datensätze Tabelle. Es ist wirklich langsam, sagen wir 30 Runden pro Sekunde. Ich habe keine Ahnung, wo ich anfangen soll.Gespeicherte SQL Server-Prozedur extrem langsam

Es überträgt alle Daten aus 1 Tabelle in mehrere Tabellen mit Daten für jeden Datenstrom pro Jahr. Hier

ist der Code:

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

Verwenden Sie nicht den Cursor. Geändert, um Base-Abfrage – Squirrel

+0

Speziell auf 1,7 Milliarden Zeilen zu setzen! – scsimon

Antwort

2

Sie haben eine Menge Probleme.
Wie bereits erwähnt, ist die erste rote Flagge ein Cursor.
Manchmal können Sie jedoch keine Logik ohne Cursor unterstützen und es ist Ihr Fall.

Zweitens verwenden Sie dynamisches SQL. Es trägt auch zur Leistung bei.

ABER, ist Ihr größtes Problem Anwendungsdesign.
Sie MUSS NICHT erstellen individuelle Tabelle für jeden Sensor aus Ihrem Log.

Die einfache Lösung ist NUR EINENtbl_Sensors“ Tisch mit einer zusätzlichen Spalte SensorID und wiederverwenden es 1700 mal statt neu zu 1700 Tabellen zu erstellen.