Câu lệnh IF không bỏ qua TempDB khi Looping thông qua cơ sở dữ liệu với sp_MSForEachDB


8

[Máy chủ SQL 2012 SP2 EE]

Tại sao đoạn script sau lại cho tôi một lỗi liên quan đến tempdb?

    exec sp_MSForEachDB '
    IF ( (select database_id from sys.databases where name = ''?'') > 4)
    BEGIN 
    ALTER AUTHORIZATION ON DATABASE::? TO [sa];
    ALTER DATABASE [?] SET RECOVERY SIMPLE;
    END'

Đây là lỗi tôi nhận được:

  Msg 5058, Level 16, State 1, Line 5
  Option 'RECOVERY' cannot be set in database 'tempdb'.

Nó làm công việc mà nó phải làm. Nhưng tôi không thể nghĩ ra lý do cho lỗi này. Tôi biết rằng cơ sở dữ liệu của tempdb là 2, nhưng ít nhất nó không nên cố gắng đặt tùy chọn cho tempdb.

Câu trả lời:


4

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 ALTERcâ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à IFcâ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 EXECcuộ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_MSForEachDBchu 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:

  1. Phân tích
  2. Biên dịch
  3. 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...ENDkhố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 ALTERcâu lệnh.

Lý do gói các ALTERcâ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à IFcâ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.


Câu hỏi của tôi là - Tại sao câu lệnh "if" cho phép ID của tempdb (nghĩa là 2) được gửi đến khối câu lệnh if nếu nó không thỏa mãn điều kiện lớn hơn 4.
GaganLamba

1
@GaganLamba Tôi hiểu câu hỏi của bạn và trả lời nó rất cụ thể. Bạn đã chạy ví dụ của tôi? Như tôi đã nói trong câu trả lời, đây là thời gian biên dịch, không phải thời gian chạy, lỗi. Do đó, cả IFcâu lệnh và bất kỳ câu lệnh SQL nào khác (bao gồm cả hai câu ALTER) đều được thực thi tại thời điểm này. Câu IFlệnh không cho phép ID của tempdb được gửi đến những gì bên trong khối BEGIN/ ENDvà mã thậm chí không chạy các ALTERcâu lệnh vào thời điểm này. Lỗi đang được SQL Server ném ra vì nó đang xác thực SQL trước khi thực thi. Tôi đã thêm một phần tóm tắt vào cuối.
Solomon Rutzky

3

Msg 5058, Cấp 16, Trạng thái 1, Dòng 5 Tùy chọn 'THU HỒI' không thể được đặt trong cơ sở dữ liệu 'tempdb'.

Nó làm công việc mà nó phải làm. Nhưng tôi không thể nghĩ ra lý do cho lỗi này.

Trước hết, không cần phải sử dụng ms_foreachdbtài liệu không có giấy tờ và xấu đến mức bạn có thể lặp lại bằng cách sử dụng con trỏ đơn giản. Liên quan đến lỗi bạn đang cố gắng thay đổi mô hình khôi phục của tất cả các cơ sở dữ liệu including tempdbnhưng bạn không thể thay đổi mô hình khôi phục của tempdb, bạn không thể thực hiện bất kỳ thao tác sao lưu nào trên đó là lý do tại sao bạn nhận được thông báo lỗi này. Điều này không được Microsoft cho phép. Vui lòng đọc thêm về các hoạt động bạn có thể làm trên tempdb


1
Tôi nhận thấy rằng ms_foreachdb không có giấy tờ và tôi không nên sử dụng nó. Tôi cũng hiểu rằng tempdb không được phép sao lưu hoặc thay đổi tùy chọn khôi phục. Nhưng câu hỏi của tôi vẫn chưa được trả lời là tại sao máy chủ SQL lại cố gắng thay đổi tùy chọn cho TEMPDB? ID của TEMPDB, là 2, thậm chí không được trả lại.
GaganLamba

1
@GaganLamba SQL Server không thực sự cố gắng thay đổi tùy chọn cho TEMPDB. Các ALTERbáo cáo chỉ đơn thuần là được xác nhận , không được thực hiện. Tôi cung cấp chi tiết trong câu trả lời của tôi .
Solomon Rutzky

3

Ngoài thực tế là bạn không thể thay đổi tùy chọn khôi phục cho tempdb, Bạn không cần một vòng lặp cho những gì bạn đang làm:

Chạy trong SSMS bằng cách nhấn CTRL+T

select 'alter authorization on database::' + quotename(name) + ' to [sa];' + char(10) + 'alter database ' + quotename(name) + ' set recovery simple;'
from sys.databases
where database_id > 4
    and state_desc = 'ONLINE'
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.