LƯU Ý CHO NGƯỜI ĐỌC: Vui lòng đọc toàn bộ câu hỏi, bao gồm mã ví dụ (không chỉ tiêu đề). Câu hỏi này không phải là về cách chu kỳ tốt nhất thông qua cơ sở dữ liệu, cũng không phải là lý do tại sao [tempdb] nhận được lỗi này. OP đã cố gắng tránh thực thi các ALTER
câu lệnh trên tất cả các cơ sở dữ liệu hệ thống (tốt, 4 câu lệnh hiển thị) và đang hỏi tại sao câu lệnh IF nên bỏ qua [tempdb] dường như không bỏ qua nó.
Tại sao đoạn script sau lại cho tôi một lỗi liên quan đến tempdb?
Lý do là IF
câu lệnh chỉ ảnh hưởng đến những gì xảy ra khi mã thực sự đang chạy, nhưng SQL Server vẫn phải phân tích cú pháp và biên dịch lô trước khi thực thi nó. Lỗi phân tích cú pháp là những lỗi liên quan đến cú pháp, chẳng hạn như đảm bảo rằng các câu lệnh SQL được hình thành đúng và các biến đã được khai báo đúng:
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
SELECT @Bob;
nhận được lỗi sau:
Msg 137, Level 15, State 2, Line 2
Must declare the scalar variable "@Bob".
Và như sau:
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
CREATE TABLE b
nhận được lỗi sau:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'b'.
Nếu lô phân tích thành công, thì nó được biên dịch, tại thời điểm đó, những thứ như quyền được kiểm tra và một số kiểm tra khác được thực hiện.
-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
IF (1 = 0)
BEGIN
ALTER AUTHORIZATION ON DATABASE::[tempdb] TO [sa];
ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
END;
SQL ở trên được hình thành đúng cách để phân tích cú pháp hàng loạt thành công. Bây giờ hãy thử thực thi SQL trên bằng cách nhấn F5 hoặc Control-E hoặc ! Nút thực thi , vv
Lần này bạn gặp lỗi sau:
Msg 5058, Level 16, State 1, Line 4
Option 'RECOVERY' cannot be set in database 'tempdb'.
mặc dù IF (1 = 0)
đảm bảo rằng mã sẽ không bao giờ chạy. Điều này có nghĩa là bạn đang chạy vào một lỗi biên dịch. Bạn có thể khắc phục các loại lỗi này bằng cách di chuyển mã vi phạm vào một quy trình con thông qua một EXEC
cuộc gọi.
Thực hiện các thao tác sau và nó sẽ hoàn thành thành công vì những gì bên trong EXEC()
không được phân tích cú pháp hoặc biên dịch cho đến khi câu lệnh đó được thực thi trong thời gian chạy.
IF (1 = 0)
BEGIN
EXEC('
ALTER AUTHORIZATION ON DATABASE::[tempdb] TO [sa];
ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
');
END;
Để tóm tắt:
Đầu tiên, hãy xem xét các sp_MSForEachDB
chu kỳ thông qua cơ sở dữ liệu và thực thi SQL đã qua của bạn sau khi thay thế ?
bằng tên cơ sở dữ liệu hiện tại. Vì vậy, khi con trỏ bên trong sp_MSForEachDB
được [tempdb]
, nó thực hiện như sau:
IF ( (select database_id from sys.databases where name = ''tempdb'') > 4)
BEGIN
ALTER AUTHORIZATION ON DATABASE::tempdb TO [sa];
ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
END
Thứ hai, có một số bước mà SQL Server thực hiện khi bạn thực hiện một lô truy vấn:
- Phân tích
- Biên dịch
- Thực hiện thực tế
Điều quan trọng là phải hiểu rằng đây là các bước riêng biệt và có thể tạo ra lỗi trước khi tiếp tục bước tiếp theo (và do đó hủy xử lý tiếp theo trước khi chuyển sang bước tiếp theo). Trong trường hợp ở đây, lỗi xảy ra ở Bước 2 - Biên dịch - như đã được chứng minh trong ví dụ thứ 2 đến cuối cùng ở trên (lỗi đầu tiên bắt đầu IF (1 = 0)
). Việc IF (1 = 0)
ngăn chặn mã bên trong BEGIN...END
khối không bao giờ chạy, nhưng lỗi vẫn xảy ra. Do đó, lỗi không xảy ra do một nỗ lực thực tế để chạy hai ALTER
câu lệnh.
Lý do gói các ALTER
câu lệnh bên trong EXEC()
hàm hoạt động là vì SQL Server sẽ không phân tích cú pháp và biên dịch những gì bên trong EXEC()
cho đến khi EXEC()
thực sự chạy. Tại thời điểm đó, IF ( (select database_id from sys.databases where name = ''?'') > 4)
câu lệnh sẽ được phép chạy vì lô không bị lỗi trong quá trình Biên dịch và sẽ biến nó thành Thi hành, và IF
câu lệnh sẽ bỏ qua [tempdb]
và "Tùy chọn 'RECOVERY' không thể được đặt trong cơ sở dữ liệu 'tempdb'" sẽ không xảy ra.