Pernyataan IF tidak melewatkan TempDB saat Looping melalui Database dengan sp_MSForEachDB

8

[SQL Server 2012 SP2 EE]

Mengapa skrip berikut memberi saya kesalahan terkait 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'

Inilah kesalahan yang saya dapatkan:

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

Itu melakukan pekerjaan yang seharusnya dilakukan. Tetapi saya tidak dapat memikirkan alasan untuk kesalahan tersebut. Saya tahu bahwa databaseID tempdb adalah 2, maka paling tidak seharusnya tidak mencoba untuk mengatur opsi untuk tempdb.

GaganLamba
sumber

Jawaban:

4

CATATAN UNTUK PEMBACA: Silakan baca seluruh pertanyaan, termasuk kode contoh (yaitu bukan hanya judulnya). Pertanyaan ini bukan tentang bagaimana cara terbaik menelusuri basis data, juga bukan tentang mengapa [tempdb] menerima kesalahan ini. OP sudah berusaha untuk menghindari mengeksekusi ALTERpernyataan pada semua basis data sistem (well, 4 yang terlihat) dan menanyakan mengapa pernyataan IF yang seharusnya melewatkan [tempdb] tampaknya tidak melewatkannya.

Mengapa skrip berikut memberi saya kesalahan terkait tempdb?

Alasannya adalah bahwa IFpernyataan hanya mempengaruhi apa yang terjadi ketika kode benar-benar berjalan, tetapi SQL Server masih harus mem-parsing dan mengkompilasi bets sebelum menjalankannya. Kesalahan Parse adalah yang terkait dengan sintaks, seperti memastikan bahwa pernyataan SQL terbentuk dengan benar dan variabel telah dideklarasikan dengan benar:

-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
SELECT @Bob;

mendapat kesalahan berikut:

Msg 137, Level 15, State 2, Line 2
Must declare the scalar variable "@Bob".

Dan berikut ini:

-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
CREATE TABLE b

mendapat kesalahan berikut:

Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'b'.

Jika batch berhasil diurai, maka dikompilasi, pada saat hal-hal seperti izin diperiksa dan beberapa pemeriksaan lainnya dilakukan.

-- 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 di atas terbentuk dengan benar sehingga batch berhasil. Sekarang coba jalankan SQL di atas dengan menekan F5 atau Control-E atau ! Jalankan tombol, dll.

Kali ini Anda mendapatkan kesalahan berikut:

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

meskipun kode IF (1 = 0)memastikan bahwa tidak akan pernah berjalan. Ini berarti bahwa Anda mengalami kesalahan kompilasi. Anda dapat mengatasi jenis kesalahan ini dengan memindahkan kode yang menyinggung ke dalam subproses melalui EXECpanggilan.

Jalankan berikut ini dan itu akan selesai dengan sukses karena apa yang ada di dalam EXEC()tidak diuraikan atau dikompilasi sampai pernyataan itu dijalankan pada saat runtime.

IF (1 = 0)
BEGIN
  EXEC('
    ALTER AUTHORIZATION ON DATABASE::[tempdb] TO [sa];
    ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
  ');
END;

Untuk meringkas:
Pertama, pertimbangkan bahwa sp_MSForEachDBsiklus melalui database dan mengeksekusi pass-in SQL Anda setelah mengganti ?dengan nama database saat ini. Jadi ketika kursor di dalam sp_MSForEachDBsampai ke [tempdb], itu efektif melakukan hal berikut:

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

Kedua, ada beberapa langkah yang dilakukan SQL Server ketika Anda menjalankan batch permintaan:

  1. Parse
  2. Menyusun
  3. Eksekusi Aktual

Penting untuk memahami bahwa ini adalah langkah-langkah terpisah dan dapat menghasilkan kesalahan sebelum melanjutkan ke langkah berikutnya (dan karenanya membatalkan pemrosesan lebih lanjut sebelum melanjutkan ke langkah berikutnya). Dalam kasus di sini, kesalahan terjadi pada Langkah 2 - Kompilasi - sebagaimana terbukti pada contoh ke-2 hingga terakhir di atas (yang pertama dimulai dengan IF (1 = 0)). The IF (1 = 0)mencegah bagian dalam kode BEGIN...ENDblok dari yang pernah berjalan, namun kesalahan masih terjadi. Karenanya kesalahan tidak terjadi karena upaya aktual untuk menjalankan kedua ALTERpernyataan.

Alasan yang membungkus ALTERpernyataan di dalam EXEC()fungsi berfungsi adalah karena SQL Server tidak akan menguraikan dan mengkompilasi apa yang ada di dalam EXEC()sampai EXEC()benar-benar berjalan. Pada saat itu, IF ( (select database_id from sys.databases where name = ''?'') > 4)pernyataan akan diizinkan berjalan karena bets tidak akan gagal selama Kompilasi dan akan membuatnya untuk Eksekusi, dan IFpernyataan itu akan melompati [tempdb]dan "Opsi 'PEMULIHAN' tidak dapat diatur dalam kesalahan 'tempdb' basis data" tidak akan terjadi.

Solomon Rutzky
sumber
Pertanyaan saya adalah - Mengapa pernyataan "jika" memungkinkan ID tempdb (yaitu, 2) dikirim ke blok pernyataan if jika tidak memenuhi syarat lebih besar dari 4.
GaganLamba
1
@GaganLamba Saya mengerti pertanyaan Anda dan menjawabnya dengan sangat spesifik. Apakah Anda menjalankan contoh saya? Seperti yang saya nyatakan dalam jawaban, ini adalah waktu kompilasi, bukan waktu berjalan, kesalahan. Oleh karena itu baik IFpernyataan maupun pernyataan SQL lainnya (termasuk keduanya ALTER) sedang dieksekusi pada saat ini. The IFpernyataan tidak memungkinkan ID tempdb untuk dikirim ke apa yang ada di dalam BEGIN/ ENDblok, dan kode ini bahkan tidak menjalankan ALTERpernyataan pada saat ini. Kesalahan dilemparkan oleh SQL Server karena memvalidasi SQL sebelum menjalankannya. Saya menambahkan bagian ringkasan ke bagian akhir.
Solomon Rutzky
3

Msg 5058, Level 16, Negara 1, Baris 5 Pilihan 'RECOVERY' tidak dapat diatur dalam database 'tempdb'.

Itu melakukan pekerjaan yang seharusnya dilakukan. Tetapi saya tidak dapat memikirkan alasan untuk kesalahan tersebut.

Pertama-tama tidak perlu menggunakan ms_foreachdbyang tidak berdokumen dan seberapa buruk Anda dapat mengulang menggunakan kursor sederhana. Mengenai kesalahan Anda mencoba untuk mengubah model pemulihan semua database including tempdbtetapi Anda tidak dapat mengubah model pemulihan tempdb juga Anda tidak dapat melakukan operasi cadangan di atasnya itu sebabnya Anda mendapat pesan kesalahan ini. Ini tidak diizinkan oleh Microsoft. Baca lebih lanjut tentang operasi yang dapat Anda lakukan di tempdb

Shanky
sumber
1
Saya mengerti maksud Anda bahwa ms_foreachdb tidak berdokumen dan saya tidak boleh menggunakannya. Saya juga mengerti bahwa tempdb tidak seharusnya dicadangkan atau mengubah opsi pemulihan. Tapi pertanyaan saya masih belum terjawab mengapa SQL server mencoba mengubah opsi untuk TEMPDB? ID TEMPDB, yaitu 2, bahkan tidak dikembalikan.
GaganLamba
1
@GaganLamba SQL Server sebenarnya tidak berusaha mengubah opsi untuk TEMPDB. The ALTERpernyataan yang hanya sedang divalidasi , tidak dieksekusi. Saya memberikan detail dalam jawaban saya .
Solomon Rutzky
3

Terlepas dari kenyataan bahwa Anda tidak dapat mengubah opsi pemulihan untuk tempdb, Anda tidak perlu perulangan untuk apa yang Anda lakukan:

Jalankan di SSMS dengan menekan 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'
Kin Shah
sumber