Kann nicht in neu erstellte Spalte EINFÜGEN

8

Ich habe eine einfache Testtabelle wie diese:

CREATE TABLE MyTable (x INT);

Innerhalb einer Transaktion versuche ich, eine Spalte hinzuzufügen und dann in die neu erstellte Spalte einzufügen:

BEGIN TRANSACTION;
    PRINT 'Adding column, ''SupplementalDividends'', to MyTable table.';

    ALTER TABLE MyTable
        ADD SupplementalDividends DECIMAL(18,6);

    PRINT 'Column added successfully....';

    PRINT 'Ready to INSERT into MyTable ...';

    INSERT INTO MyTable (x, SupplementalDividends)
        VALUES (1, 3.2);

    PRINT '**** CHANGES COMPLETE -- COMMITTING.';
COMMIT TRANSACTION;

Das Problem ist eine Fehlermeldung, wenn ich den obigen Code ausführe:

Invalid column name 'SupplementalDividends'.

Warum verursacht dies einen Fehler? Wenn ich die Spalte außerhalb der Transaktion in einem anderen Stapel hinzufüge, funktioniert dies. Mein Problem ist, dass ich die Spalte innerhalb der Transaktion hinzufügen möchte . Warum der Fehler?

Tom Baxter
quelle
4
Ein kleiner aber wichtiger Vorschlag - immer verwendenschema.ObjectName . Ein guter Anfang, um gute Praktiken anzupassen :-)
Kin Shah

Antworten:

6

Ich möchte nur klarstellen, dass dies zur Laufzeit ein Problem ist, nicht zur Kompilierungszeit, und nichts damit zu tun hat, dass sie sich in derselben Transaktion befinden . Nehmen wir zum Beispiel an, wir haben diese Tabelle:

CREATE TABLE dbo.floob(a int);

Der folgende Stapel wird erfolgreich analysiert (Kompilierungszeit), aber zur Laufzeit wird der Fehler angezeigt, den Sie in der Frage erwähnt haben:

BEGIN TRANSACTION;
  ALTER TABLE dbo.floob ADD b int;

  SELECT b FROM dbo.floob;
COMMIT TRANSACTION;

In einem Abfrageeditor in Management Studio können Sie dies einfach umgehen, indem Sie:

  1. Markieren Sie die ersten beiden Zeilen, drücken Sie Ausführen, markieren Sie dann die zweiten beiden Zeilen und drücken Sie Ausführen. oder,
  2. Setzen Sie einen Batch- Separator zwischen sie, wie folgt:

    BEGIN TRANSACTION;
      ALTER TABLE dbo.floob ADD c int;
    
    GO
    
      SELECT c FROM dbo.floob;
    COMMIT TRANSACTION;
    

Wenn Sie dies von außerhalb von SQL Server ausführen (z. B. Senden eines SQL-Stapels aus Ihrem Anwendungscode), können Sie die beiden Stapel einfach auf ähnliche Weise separat senden, oder wenn Sie ihn unbedingt als einzelnen Stapel senden müssen, können Sie dies Verwenden Sie dynamisches SQL (wie in Gianlucas Antwort ), um die Namensauflösung zu verschieben.

Aaron Bertrand
quelle
1
Wenn dies ein Laufzeitfehler ist, sollte ich ihn abfangen können, richtig? Ich kann es aber nicht .
Andriy M
@AndriyM Kann nicht alle Fehler abfangen, nein. Und einige treten irgendwo zwischen Parsen und Ausführen auf, wie dieser zeigt.
Aaron Bertrand
@AndriyM Darüber hinaus hat unser Kiwi-Freund hier eine Antwort , die dasselbe impliziert. Es gibt Szenarien, in denen Fehler für die Kompilierungszeit zu spät, für die Laufzeit jedoch zu früh sind. Beide Antworten? Dynamisches SQL. (Einige Beispiele auch in Erlands Artikel .)
Aaron Bertrand
10

Es ist ein verbindliches Problem. Der Code wird zur Kompilierungszeit an die Metadaten der Tabelle gebunden und ist nicht zu spät gebunden. Versuchen Sie, EXEC und dynamisches SQL zu verwenden, um diese Einschränkung zu überwinden:

BEGIN TRANSACTION;
    PRINT 'Adding column, ''SupplementalDividends'', to MyTable table.';

    ALTER TABLE MyTable
        ADD SupplementalDividends DECIMAL(18,6);

    PRINT 'Column added successfully....';

    PRINT 'Ready to INSERT into MyTable ...';
    EXEC('
    INSERT INTO MyTable (x, SupplementalDividends)
        VALUES (1, 3.2);
    ')

    PRINT '**** CHANGES COMPLETE -- COMMITTING.';
COMMIT TRANSACTION;

Eine andere Option ist die Verwendung einer gespeicherten Prozedur zum Einfügen der Daten: Die späte Bindung gilt für gespeicherte Prozeduren, jedoch nicht für Ad-hoc-Abfragen. Auch hier müssten Sie dynamisches SQL verwenden, um die Prozedur zu erstellen, aber es könnte Ihnen das Übergeben von Parametern erleichtern:

BEGIN TRANSACTION;
    PRINT 'Adding column, ''SupplementalDividends'', to MyTable table.';

    ALTER TABLE MyTable
        ADD SupplementalDividends DECIMAL(18,6);

    PRINT 'Column added successfully....';

    PRINT 'Ready to INSERT into MyTable ...';

    EXEC('
    CREATE PROCEDURE insData @p1 int, @p2 DECIMAL(18,6)
    AS
    BEGIN 
        INSERT INTO MyTable (x, SupplementalDividends)
        VALUES (@p1, @p2);
    END')

    EXEC InsData 1, 3.2;

    PRINT '**** CHANGES COMPLETE -- COMMITTING.';
COMMIT TRANSACTION;

Eine temporär gespeicherte Prozedur würde ebenfalls funktionieren:

BEGIN TRANSACTION;
    PRINT 'Adding column, ''SupplementalDividends'', to MyTable table.';

    ALTER TABLE MyTable
        ADD SupplementalDividends DECIMAL(18,6);

    PRINT 'Column added successfully....';

    PRINT 'Ready to INSERT into MyTable ...';

    EXEC('
    CREATE PROCEDURE #insData @p1 int, @p2 DECIMAL(18,6)
    AS
    BEGIN 
        INSERT INTO MyTable (x, SupplementalDividends)
        VALUES (@p1, @p2);
    END')

    EXEC #InsData 1, 3.2;

    PRINT '**** CHANGES COMPLETE -- COMMITTING.';
COMMIT TRANSACTION;
spaghettidba
quelle
1
Ich mag das vorübergehende Verfahren - daran hätte ich nicht gedacht!
Max Vernon
1

Ich bin gespannt, warum Sie innerhalb derselben Transaktion eine Tabelle ändern und einen Wert in diese Spalte einfügen.

Es besteht fast keine Chance, dass Sie die Tabelle jemals wieder ändern müssen (genau so), es sei denn, sie wird jede Stunde / jeden Tag zurückgesetzt. Warum also innerhalb einer Transaktion?

Ihre Änderung wurde noch nicht festgeschrieben und wird daher nicht gefunden, wenn Sie versuchen, sie einzufügen.

Mein Rat ist, Ihre DDL- und DML-Aufgaben zu trennen (zumindest in getrennten Transaktionen).

MguerraTorres
quelle
Ich ändere die Tabelle und füge sie ein, da dies Teil eines einmaligen Datenmigrationsprojekts ist. Offensichtlich habe ich nur einen kleinen Ausschnitt des relevanten Codes gezeigt.
Tom Baxter
Sie können eine nach der anderen ausführen, ohne sie in denselben Transaktionen ausführen zu müssen. DDL erstellt seine eigenen impliziten Transaktionen (vorausgesetzt, Sie haben die Standardeinstellung für implizite Transaktionen aktiviert gelassen). Wenn Sie jedoch eine Transaktion BEGINNEN, umgehen Sie die implizite Eigenschaft einer DDL-Task, bis Sie diese Transaktion festschreiben.
MguerraTorres
1
Es ist eigentlich die Tatsache, dass sie sich in derselben Charge befinden und nichts mit Transaktionen zu tun haben.
Aaron Bertrand
Das ist ein guter Punkt. Mein Fehler.
MguerraTorres