Tidak Dapat MEMASUKKAN Ke dalam Kolom yang Baru Dibuat

8

Saya memiliki tabel tes sederhana seperti ini:

CREATE TABLE MyTable (x INT);

Dalam suatu transaksi, saya mencoba untuk menambahkan kolom dan kemudian memasukkan ke dalam kolom yang baru dibuat:

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;

Masalahnya adalah pesan kesalahan ketika saya menjalankan kode di atas:

Invalid column name 'SupplementalDividends'.

Mengapa ini menyebabkan kesalahan? Jika saya menambahkan kolom dalam kumpulan yang berbeda, di luar transaksi, itu akan berfungsi. Masalah saya adalah saya ingin menambahkan kolom dalam transaksi. Mengapa kesalahan?

Tom Baxter
sumber
4
Saran kecil namun penting - selalu gunakanschema.ObjectName . Awal yang baik untuk mengadaptasi praktik yang baik :-)
Kin Shah

Jawaban:

6

Hanya ingin mengklarifikasi bahwa ini adalah masalah pada saat run time, bukan waktu kompilasi, dan tidak ada hubungannya dengan fakta bahwa mereka berada dalam transaksi yang sama . Misalnya, mari kita asumsikan kita memiliki tabel ini:

CREATE TABLE dbo.floob(a int);

Batch berikut mem-parsing berhasil (waktu kompilasi), tetapi saat runtime mendapat kesalahan yang Anda sebutkan dalam pertanyaan:

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

  SELECT b FROM dbo.floob;
COMMIT TRANSACTION;

Di editor kueri di Management Studio, Anda dapat menyiasatinya dengan mudah, baik dengan:

  1. Sorot dua baris pertama, tekan eksekusi, kemudian sorot dua baris kedua, dan tekan eksekusi; atau,
  2. Menempatkan pemisah batch di antara mereka, seperti:

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

Jika Anda menjalankan ini dari luar SQL Server (mis. Mengirim batch SQL dari kode aplikasi Anda), Anda dapat mengirim dua batch secara terpisah dengan cara yang sama, atau jika Anda benar-benar perlu mengirimkannya sebagai batch tunggal, Anda dapat gunakan SQL dinamis (seperti pada jawaban Gianluca ) untuk menunda resolusi nama.

Aaron Bertrand
sumber
1
Jika ini adalah kesalahan runtime, saya seharusnya bisa menangkapnya, benar? Tapi aku tidak bisa .
Andriy M
@AndriyM Tidak bisa menangkap semua kesalahan, tidak. Dan beberapa terjadi di antara parse dan eksekusi, seperti yang ditunjukkan ini.
Aaron Bertrand
@AndriyM Selanjutnya, teman Kiwi kami memiliki jawaban di sini yang menyiratkan hal yang sama, ada skenario di mana kesalahan terlalu terlambat untuk waktu kompilasi tetapi terlalu dini untuk runtime. Keduanya menjawab? SQL dinamis. (Beberapa contoh dalam artikel Erland juga.)
Aaron Bertrand
10

Ini masalah yang mengikat. Kode terikat ke metadata tabel pada waktu kompilasi dan tidak terikat terlambat. Coba gunakan EXEC dan SQL dinamis untuk mengatasi batasan ini:

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;

Pilihan lain adalah menggunakan prosedur tersimpan untuk memasukkan data: mengikat terlambat berlaku untuk prosedur tersimpan, tetapi tidak untuk permintaan ad-hoc. Sekali lagi, Anda harus menggunakan SQL dinamis untuk membuat prosedur, tetapi bisa membuatnya lebih mudah bagi Anda untuk melewati parameter:

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;

Prosedur tersimpan sementara akan bekerja juga:

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
sumber
1
Saya suka prosedur sementara - saya tidak akan memikirkan itu!
Max Vernon
1

Saya ingin tahu mengapa Anda mengubah tabel dan memasukkan nilai ke dalam kolom itu dalam transaksi yang sama.

Hampir tidak ada kemungkinan bahwa Anda perlu mengubah tabel (dengan cara yang persis sama) lagi, kecuali jika dikembalikan setiap jam / hari, jadi mengapa melakukannya dalam transaksi?

Perubahan Anda belum dilakukan, dan karena itu tidak ditemukan ketika Anda mencoba memasukkannya.

Saran saya adalah untuk memisahkan tugas-tugas DDL dan DML Anda (setidaknya dalam transaksi yang terpisah pula).

MguerraTorres
sumber
Saya mengubah tabel dan menyisipkan karena ini adalah bagian dari proyek migrasi data satu kali. Jelas, saya hanya menunjukkan potongan kecil kode yang relevan.
Tom Baxter
Anda dapat melakukan satu demi satu tanpa harus melakukannya dalam transaksi yang sama. DDL membuat transaksi implisitnya sendiri (dengan anggapan Anda membiarkan pengaturan Transaksi Implisit default diaktifkan), tetapi ketika Anda MULAI transaksi, Anda melewati properti Implisit dari tugas DDL hingga Anda KOMIT transaksi itu.
MguerraTorres
1
Sebenarnya fakta bahwa mereka berada dalam kelompok yang sama, dan tidak ada hubungannya dengan transaksi.
Aaron Bertrand
Itu poin yang bagus. Kesalahanku.
MguerraTorres