Impossibile INSERIRE nella colonna appena creata


8

Ho una semplice tabella di test come questa:

CREATE TABLE MyTable (x INT);

All'interno di una transazione, provo ad aggiungere una colonna e quindi inserisco nella colonna appena creata:

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;

Il problema è un messaggio di errore quando eseguo il codice sopra:

Invalid column name 'SupplementalDividends'.

Perché questo causa un errore? Se aggiungo la colonna in un batch diverso, al di fuori della transazione, funzionerà. Il mio problema è che voglio aggiungere la colonna all'interno della transazione. Perché l'errore


4
Un piccolo ma importante suggerimento: utilizzare sempreschema.ObjectName . Un buon inizio per adattare le buone pratiche :-)
Kin Shah,

Risposte:


6

Voglio solo chiarire che questo è un problema in fase di esecuzione, non in fase di compilazione, e non ha nulla a che fare con il fatto che si trovano nella stessa transazione . Ad esempio, supponiamo di avere questa tabella:

CREATE TABLE dbo.floob(a int);

Il seguente batch viene analizzato correttamente (tempo di compilazione), ma in fase di esecuzione ottiene l'errore menzionato nella domanda:

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

  SELECT b FROM dbo.floob;
COMMIT TRANSACTION;

In un editor di query in Management Studio, puoi aggirare facilmente questo problema:

  1. Evidenziando le prime due linee, colpendo esegui, quindi evidenziando le seconde due linee e colpendo esegui; o,
  2. Mettendo un separatore batch tra loro, in questo modo:

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

Se lo stai eseguendo al di fuori di SQL Server (ad es. Inviando un batch SQL dal tuo codice dell'applicazione), puoi semplicemente inviare i due batch separatamente in un modo simile o se devi assolutamente inviarlo come singolo batch, puoi usa l'SQL dinamico (come nella risposta di Gianluca ) per rinviare la risoluzione dei nomi.


1
Se si tratta di un errore di runtime, dovrei essere in grado di rilevarlo, giusto? Non posso , però.
Andriy M,

@AndriyM Impossibile rilevare tutti gli errori, no. E alcuni si verificano a metà strada tra l'analisi e l'esecuzione, come mostra questo.
Aaron Bertrand

@AndriyM Oltre a questo, il nostro amico Kiwi ha una risposta qui che implica lo stesso, ci sono scenari in cui gli errori sono troppo tardi per il tempo di compilazione ma troppo presto per il runtime. Entrambe le risposte? SQL dinamico. (Alcuni esempi anche nell'articolo di Erland .)
Aaron Bertrand

10

È un problema vincolante. Il codice è associato ai metadati della tabella al momento della compilazione e non è associato in ritardo. Prova a utilizzare EXEC e SQL dinamico per superare questa limitazione:

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;

Un'altra opzione è utilizzare una procedura memorizzata per inserire i dati: l'associazione tardiva si applica alla procedura memorizzata, ma non alle query ad hoc. Ancora una volta, è necessario utilizzare SQL dinamico per creare la procedura, ma potrebbe rendere più semplice il passaggio dei parametri:

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;

Una procedura memorizzata temporanea funzionerebbe anche:

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;

1
Mi piace la procedura temporanea - non ci avrei pensato!
Max Vernon,

1

Sono curioso di sapere perché stai modificando una tabella e inserendo un valore in quella colonna all'interno della stessa transazione.

Non c'è quasi alcuna possibilità che tu abbia mai bisogno di modificare nuovamente la tabella (nello stesso modo esatto), a meno che non venga ripristinata ogni ora / giorno, quindi perché farlo all'interno di una transazione?

La tua modifica non è stata ancora impegnata e pertanto non viene trovata quando tenti di inserirla.

Il mio consiglio è di separare le attività DDL e DML (almeno in transazioni separate comunque).


Sto modificando la tabella e inserendola perché fa parte di un progetto di migrazione dei dati una tantum. Ovviamente, ho mostrato solo un piccolo frammento di codice pertinente.
Tom Baxter,

Puoi fare uno dopo l'altro senza doverli fare nelle stesse transazioni. DDL crea le proprie transazioni implicite (presupponendo che sia stata lasciata l'impostazione Transazioni implicite predefinita), ma quando si INIZIA una transazione, si ignora la proprietà implicita di un'attività DDL finché non si COMMITA quella transazione.
MguerraTorres,

1
In realtà è il fatto che si trovano nello stesso batch e non hanno nulla a che fare con le transazioni.
Aaron Bertrand

È un buon punto. Errore mio.
MguerraTorres,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.