it-swarm.com.de

Wie füge ich zahlreiche Daten in eine temporäre Tabelle ein?

Ich erstelle ein Programm, das eine T-SQL-Abfrage in folgender Form generiert:

DECLARE @In TABLE (Col CHAR(20))
INSERT INTO @In VALUES value1, value2... value1000
GO
INSERT INTO @In VALUES value1001, value1002...

die zweite Anweisung INSERT löst jedoch einen Fehler aus:

Meldung 1087, Ebene 15, Status 2, Zeile 1
Muss die Tabellenvariable "@In" deklarieren.

Was mache ich falsch?

Sie können VALUES (...), (...) verwenden:

INSERT INTO table(colA, colN, ...) VALUES
    (col1A, col1B, ...)
    , ...
    , (colnA, colnB, ...)

Innerhalb:

DECLARE @In TABLE (Col CHAR(20))
INSERT INTO @In VALUES 
    ('value1')
    , ('value2')
    , ...
    , ('value1000')

Es werden alle X Zeilen auf einmal eingefügt. GO wird nicht benötigt. Vor GO deklarierte Variablen existieren nach GO nicht mehr.

7

Vereinfacht gesagt sollte das Batch-Trennzeichen GO entfernt werden (wie in der Antwort von @ Julien angegeben).

Beweisen Sie einfach, dass es funktioniert, versuchen Sie Folgendes:

DECLARE @ValuesPerInsert INT = 1000; -- 1000 works, 1001 fails
DECLARE @SQL NVARCHAR(MAX) = '
DECLARE @In TABLE (Col CHAR(20))';

;WITH cte AS
(
  SELECT    TOP (3523)
          ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS [Num],
          (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) % @ValuesPerInsert) AS [Position]
  FROM  [master].[sys].[all_columns]
) -- select * from cte
SELECT @SQL += CASE cte.[Position]
                  WHEN 1 THEN N';' + NCHAR(0x0D) + NCHAR(0x0A)
                              + N'INSERT INTO @In (Col) VALUES (''Val-'
                              + CONVERT(NVARCHAR(10), cte.[Num]) + N''')'
                  ELSE ', (''Val-' + CONVERT(NVARCHAR(10), cte.[Num]) + N''')'
               END
FROM   cte;

SET @SQL += ';
';

SELECT CONVERT(XML, N'<sql>' + @SQL + N'</sql>') AS [@SQL];

EXEC (@SQL);
GO

Das erzeugt das folgende SQL:

DECLARE @In TABLE (Col CHAR(20));
INSERT INTO @In (Col) VALUES ('Val-1'), ('Val-2'), ('Val-3'), ('Val-4'), ..., ('Val-1000');
INSERT INTO @In (Col) VALUES ('Val-1001'), ('Val-1002'), ('Val-1003'), ..., ('Val-2000');
INSERT INTO @In (Col) VALUES ('Val-2001'), ('Val-2002'), ('Val-2003'), ..., ('Val-3000');
INSERT INTO @In (Col) VALUES ('Val-3001'), ('Val-3002'), ('Val-3003'), ..., ('Val-3523');

Und Sie werden auf der Registerkarte "Nachrichten" sehen:

(1000 row(s) affected)

(1000 row(s) affected)

(1000 row(s) affected)

(523 row(s) affected)

Natürlich gibt es bei Verwendung der Liste VALUES ein Maximum von 1000 Werten. Wenn Sie versuchen, 1001 Zeilen pro INSERT zu erstellen, wird die folgende Fehlermeldung angezeigt:

Nachricht 10738, Ebene 15, Status 1, Zeile 6
Die Anzahl der Zeilenwertausdrücke in der INSERT-Anweisung überschreitet die maximal zulässige Anzahl von 1000 Zeilenwerten.

Wenn Sie jedoch mehr als 1000 Zeilen pro INSERT -Anweisung einfügen möchten, können Sie das Konstrukt INSERT INTO ... SELECT Verwenden und jede Zeile mit einem UNION ALL Kombinieren:

DECLARE @SQL NVARCHAR(MAX) = '
DECLARE @In TABLE (Col CHAR(20));
';

;WITH cte AS
(
  SELECT    TOP (3523)
          ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS [Num]
  FROM  [master].[sys].[all_columns]
) -- select * from cte
SELECT @SQL += CASE cte.[Num]
                  WHEN 1 THEN N'INSERT INTO @In (Col)' + NCHAR(0x0D) + NCHAR(0x0A)
                              + N'  SELECT ''Val-1'''
                  ELSE NCHAR(0x0D) + NCHAR(0x0A) + N'  UNION ALL'
                       + NCHAR(0x0D) + NCHAR(0x0A) + N'  SELECT ''Val-'
                       + CONVERT(NVARCHAR(10), cte.[Num]) + N''''
               END
FROM   cte;

SET @SQL += ';
';

SELECT CONVERT(XML, N'<sql>' + @SQL + N'</sql>') AS [@SQL];

EXEC (@SQL);
GO

Das erzeugt das folgende SQL:

DECLARE @In TABLE (Col CHAR(20));
INSERT INTO @In (Col)
  SELECT 'Val-1'
  UNION ALL
  SELECT 'Val-2'
  UNION ALL
  SELECT 'Val-3'
  UNION ALL
  SELECT 'Val-4'
  UNION ALL
  SELECT 'Val-5'
  ...
  UNION ALL
  SELECT 'Val-3522'
  UNION ALL
  SELECT 'Val-3523';

Und Sie werden auf der Registerkarte "Nachrichten" sehen:

(3523 row(s) affected)

Aber ich würde nicht viel über 4500 Zeilen in einer Anweisung hinausgehen: Sie möchten eine Sperreneskalation vermeiden, die die gesamte Tabelle sperrt (naja, es sei denn, die Tabelle ist partitioniert nd die Option zum Eskalieren, indem Sie stattdessen die Partition sperren der Tabelle ist aktiviert), und das tritt bei etwa 5000 Sperren auf.

Und das wird gesagt, da Sie dies aus App-Code generieren, können Sie möglicherweise Ihren Ansatz ändern und a verwenden, je nachdem, warum Sie INSERT Anweisungen generieren Stattdessen Table-Valued Paramater (TVP). Mit einem TVP können Sie die Daten aus der App direkt in eine Abfrage oder eine gespeicherte Prozedur als Tabellenvariable streamen. In diesem Fall würden Sie einfach Folgendes tun:

INSERT INTO SchemaName.RealTable (Col)
  SELECT tmp.Col
  FROM   @TVPvariable;

Wenn Sie jedoch ein portables Bereitstellungsskript benötigen, ist dies keine Option.

2
Solomon Rutzky