Không thể xác nhận vào cột mới tạo


8

Tôi có một bảng thử nghiệm đơn giản như thế này:

CREATE TABLE MyTable (x INT);

Trong một giao dịch, tôi cố gắng thêm một cột và sau đó chèn vào cột vừa tạo:

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;

Vấn đề là một thông báo lỗi khi tôi chạy đoạn mã trên:

Invalid column name 'SupplementalDividends'.

Tại sao điều này gây ra một lỗi? Nếu tôi thêm cột trong một đợt khác, ngoài giao dịch, nó sẽ hoạt động. Vấn đề của tôi là tôi muốn thêm cột trong giao dịch. Tại sao có lỗi?


4
Một gợi ý nhỏ nhưng quan trọng - luôn luôn sử dụngschema.ObjectName . Một khởi đầu tốt để thích nghi với thực tiễn tốt :-)
Kin Shah

Câu trả lời:


6

Chỉ muốn làm rõ rằng đây là một vấn đề trong thời gian chạy, không phải thời gian biên dịch và không liên quan gì đến thực tế là chúng nằm trong cùng một giao dịch . Ví dụ: giả sử chúng ta có bảng này:

CREATE TABLE dbo.floob(a int);

Các đợt sau phân tích cú pháp thành công (thời gian biên dịch), nhưng trong thời gian chạy, nó gặp lỗi bạn đề cập trong câu hỏi:

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

  SELECT b FROM dbo.floob;
COMMIT TRANSACTION;

Trong trình chỉnh sửa truy vấn trong Management Studio, bạn có thể dễ dàng khắc phục điều này bằng cách:

  1. Làm nổi bật hai dòng đầu tiên, nhấn thực thi, sau đó làm nổi bật hai dòng thứ hai và nhấn thực hiện; hoặc là,
  2. Đặt một dấu tách hàng loạt giữa chúng, như vậy:

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

Nếu bạn đang chạy cái này từ bên ngoài SQL Server (ví dụ: gửi một lô SQL từ mã ứng dụng của bạn), bạn chỉ cần gửi hai lô riêng biệt theo cách tương tự hoặc nếu bạn thực sự cần gửi một lô, bạn có thể sử dụng SQL động (như trong câu trả lời của Gianluca ) để trì hoãn độ phân giải tên.


1
Nếu đây là một lỗi thời gian chạy, tôi sẽ có thể bắt nó, đúng không? Tôi không thể , mặc dù.
Andriy M

@AndriyM Không thể bắt tất cả các lỗi, không. Và một số xảy ra ở đâu đó giữa phân tích cú pháp và thực thi, như điều này cho thấy.
Aaron Bertrand

@AndriyM Hơn nữa, người bạn Kiwi của chúng tôi có một câu trả lời ở đây ngụ ý tương tự, có những tình huống lỗi quá muộn cho thời gian biên dịch nhưng quá sớm cho thời gian chạy. Cả hai câu trả lời? SQL động. (Một số ví dụ trong bài viết của Erland cũng vậy.)
Aaron Bertrand

10

Đó là một vấn đề ràng buộc. Mã được ràng buộc với siêu dữ liệu của bảng tại thời điểm biên dịch và nó không bị ràng buộc muộn. Hãy thử sử dụng EXEC và SQL động để vượt qua giới hạn này:

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;

Một tùy chọn khác là sử dụng một thủ tục được lưu trữ để chèn dữ liệu: ràng buộc muộn áp dụng cho thủ tục được lưu trữ, nhưng không áp dụng cho các truy vấn đặc biệt. Một lần nữa, bạn sẽ phải sử dụng SQL động để tạo thủ tục, nhưng nó có thể giúp bạn dễ dàng truyền tham số hơn:

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;

Một thủ tục lưu trữ tạm thời cũng sẽ làm việc:

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
Tôi thích thủ tục tạm thời - tôi sẽ không nghĩ về điều đó!
Max Vernon

1

Tôi tò mò về lý do tại sao bạn thay đổi một bảng và chèn một giá trị vào cột đó trong cùng một giao dịch.

Hầu như không có cơ hội nào bạn sẽ cần phải thay đổi bảng (cùng một cách chính xác), trừ khi nó được hoàn nguyên mỗi giờ / ngày, vậy tại sao lại thực hiện trong một giao dịch?

Thay đổi của bạn chưa được cam kết và do đó, nó không được tìm thấy khi bạn cố gắng chèn vào nó.

Lời khuyên của tôi là tách biệt các nhiệm vụ DDL và DML của bạn (ít nhất là trong các giao dịch riêng biệt).


Tôi đang thay đổi bảng và chèn vì đây là một phần của dự án di chuyển dữ liệu một lần. Rõ ràng, tôi chỉ hiển thị một đoạn mã nhỏ có liên quan.
Tom Baxter

Bạn có thể thực hiện lần lượt mà không cần phải thực hiện chúng trong cùng một giao dịch. DDL tạo các giao dịch ngầm định của riêng mình (giả sử bạn đã bật cài đặt Giao dịch ngầm định mặc định), nhưng khi bạn BẮT ĐẦU một giao dịch, bạn sẽ bỏ qua thuộc tính tiềm ẩn của tác vụ DDL cho đến khi bạn CAM KẾT giao dịch đó.
MguerraTorres

1
Thực tế là chúng nằm trong cùng một đợt và không liên quan gì đến các giao dịch.
Aaron Bertrand

Đó là một điểm hay. Lỗi của tôi.
MguerraTorres
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.