新しく作成された列に挿入することはできません


8

私はこのような簡単なテストテーブルを持っています:

CREATE TABLE MyTable (x INT);

トランザクション内で、列を追加してから、新しく作成した列に挿入しようとしています。

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;

上記のコードを実行すると、問題はエラーメッセージになります。

Invalid column name 'SupplementalDividends'.

なぜこれがエラーの原因ですか?列をトランザクションの外の別のバッチで追加すると、機能します。私の問題は、トランザクション内に列を追加することです。なぜエラーなのか?


4
小さいが重要な提案- 常に使用してくださいschema.ObjectName。良い実践に適応するための良いスタート:-)
Kin Shah

回答:


6

これはコンパイル時ではなく実行時の問題であり、それらが同じトランザクション内にあるという事実とは関係がないことを明確にしたいだけです。たとえば、次のテーブルがあるとします。

CREATE TABLE dbo.floob(a int);

次のバッチは正常に解析されます(コンパイル時)が、実行時に質問で言及したエラーが発生します。

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

  SELECT b FROM dbo.floob;
COMMIT TRANSACTION;

Management Studioのクエリエディターでは、次のいずれかの方法でこれを簡単に回避できます。

  1. 最初の2行を強調表示し、実行を押してから、2番目の2行を強調表示して、実行を押します。または、
  2. 次のように、それらの間にバッチ区切りを入れます:

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

これをSQL Serverの外部から実行している場合(たとえば、アプリケーションコードからSQLバッチを送信する場合)、2つのバッチを同様の方法で別々に送信するか、単一のバッチとして送信する必要がある場合は、名前解決を延期するには、動的SQL(Gianlucaの回答のように)を使用します。


1
これが実行時エラーである場合、それをキャッチできるはずですよね?でも、できません
Andriy M

@AndriyMすべてのエラーをキャッチすることはできません。そして、いくつかはこれが示すように、解析と実行の間のどこかで発生します。
アーロンバートランド

@AndriyMさらに、これに加えて、私たちのKiwiの友人同じことを意味する答えをここに持っています。エラーはコンパイル時間には遅すぎ、ランタイムには早すぎるシナリオがあります。両方とも答えますか?動的SQL。(Erlandの記事にもいくつかの例があります。)
アーロンバートランド

10

それは拘束力のある問題です。コードはコンパイル時にテーブルのメタデータにバインドされ、遅延バインドされません。この制限を克服するには、EXECと動的SQLを使用してみてください。

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;

別のオプションは、ストアドプロシージャを使用してデータを挿入することです。レイトバインディングはストアドプロシージャに適用されますが、アドホッククエリには適用されません。繰り返しますが、動的SQLを使用してプロシージャを作成する必要がありますが、パラメーターを渡すのが簡単になる場合があります。

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;

一時的なストアドプロシージャも機能します。

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
私は一時的な手順が好きです-私はそれを考えていなかったでしょう!
Max Vernon

1

同じトランザクション内でテーブルを変更し、その列に値を挿入する理由に興味があります。

毎時間/日ごとに元に戻されない限り、テーブルを(同じ方法で)再度変更する必要はほとんどありません。なぜトランザクション内でそれを行うのですか?

変更はまだコミットされていないため、挿入しようとしても見つかりません。

私のアドバイスは、DDLタスクとDMLタスクを分離することです(少なくとも、とにかく別個のトランザクションで)。


これは1回限りのデータ移行プロジェクトの一部であるため、テーブルを変更して挿入しています。明らかに、関連するコードの小さな断片のみを示しました。
トムバクスター

同じトランザクションで実行する必要なく、次々に実行できます。DDLは独自の暗黙的なトランザクションを作成します(デフォルトの暗黙的なトランザクション設定をオンのままにした場合)。ただし、トランザクションを開始すると、そのトランザクションをCOMMITするまでDDLタスクのImplicitプロパティがバイパスされます。
MguerraTorres 2017

1
実際には、それらは同じバッチ内にあり、トランザクションとは関係ありません。
アーロンバートランド

それは良い点です。私の間違い。
MguerraTorres 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.