Merujuk basis data secara terprogram melalui T-SQL

11

Saya menulis prosedur tersimpan yang menggunakan nama database sebagai argumen dan mengembalikan tabel indeks basis data dan tingkat fragmentasi mereka. Prosedur tersimpan ini akan hidup di basis data DBA kami (DB yang berisi tabel yang digunakan DBA untuk memantau dan mengoptimalkan hal-hal). Sistem yang dimaksud adalah semua SQL Server 2008 R2 jika itu membuat perbedaan.

Saya memiliki kueri dasar yang berhasil, tetapi saya terjebak mencoba memberikan nama aktual indeks. Sepengetahuan saya, informasi itu terkandung dalam pandangan sys.indexes masing-masing individu. Masalah khusus saya adalah mencoba untuk mereferensikan pandangan yang terprogram dari prosedur tersimpan database lain.

Sebagai ilustrasi, ini adalah bagian dari pertanyaan yang dipermasalahkan:

FROM sys.dm_db_index_physical_stats(@db_id,NULL,NULL,NULL,NULL) p
INNER JOIN sys.indexes b ON p.[object_id] = b.[object_id] 
    AND p.index_id = b.index_id 
    AND b.index_id != 0

Permintaan bekerja dengan baik ketika dieksekusi dari database yang diidentifikasi oleh @db_id, karena menggunakan tampilan sys.indexes yang tepat. Namun, jika saya mencoba untuk memanggil ini dari database DBA, semuanya muncul nol, karena tampilan sys.indexes adalah untuk database yang salah.

Dalam istilah yang lebih umum, saya harus dapat melakukan sesuatu seperti ini:

DECLARE @db_name NVARCHAR(255) = 'my_database';
SELECT * FROM @db_name + '.sys.indexes';

atau

USE @db_name;

Saya telah mencoba mengalihkan basis data atau mereferensikan basis data lain dengan menggunakan kombinasi rangkaian string dan fungsi OBJECT_NAME / OBJECT_ID / DB_ID dan sepertinya tidak ada yang berhasil. Saya menghargai ide apa pun yang mungkin dimiliki komunitas, tetapi saya curiga saya harus memperlengkapi kembali prosedur tersimpan ini untuk berada di setiap basis data individu.

Terima kasih sebelumnya atas saran Anda.

mdoyle
sumber
1
Anda harus menggunakan SQL dinamis untuk dugaan saya ini ...
JNK

Jawaban:

10

SQL dinamis sangat berguna untuk jenis tugas administrasi ini. Berikut ini cuplikan dari prosedur tersimpan yang saya tulis yang tidak hanya mendapatkan level defragmentasi, tetapi juga menghasilkan kode untuk melakukan defragmentasi:

select @SQL = 
'
select getdate(),
       ''' + @@ServerName + ''',
       ''' + @DatabaseName + ''',
       so.Name,
       si.Name,
       db_id(''' + @DatabaseName + '''),
       ips.object_id,
       ips.index_id,
       ips.index_type_desc,
       ips.alloc_unit_type_desc,
       ips.index_depth,
       ips.avg_fragmentation_in_percent,
       ips.fragment_count,
       avg_fragment_size_in_pages,
       ips.page_count,
       ips.record_count,
       case
         when ips.index_id = 0 then ''alter table [' + @DatabaseName + '].'' + ss.name + ''.['' + so.name + ''] rebuild with (online = on)''
         else ''alter index '' + si.name + '' on [' + @DatabaseName + '].'' + ss.name + ''.['' + so.name + ''] rebuild with (online = on)''
       end
  from sys.dm_db_index_physical_stats(db_id(''' + @DatabaseName + '''),null,null,null, ''' + @SampleMode + ''') ips
  join [' + @DatabaseName + '].sys.objects so  on so.object_id = ips.object_id
  join [' + @DatabaseName + '].sys.schemas ss  on ss.schema_id = so.schema_id
  join [' + @DatabaseName + '].sys.indexes si  on si.object_id = ips.object_id
                      and si.index_id  = ips.index_id
order by so.Name, ips.index_id
'

exec (@SQL)
datagod
sumber
Berdasarkan komentar JNK, saya mengubah permintaan saya untuk menggunakan SQL dinamis. Tentu saja berhasil. Saya memiliki permintaan terpisah untuk menghasilkan kode defrag. Saya akan menjalankan ini dan tampilannya. Namun, menerima ini sebagai jawabannya.
mdoyle
Satu hal yang perlu diperhatikan ... ini berjalan pada SQL Server 2008R2 Enterprise Edition. "Rebuild with (online = on)" tidak didukung pada edisi standar.
datagod
1
Manis, saya punya cukup perwakilan untuk memperbarui jawaban Anda sekarang. :-) Ini adalah permintaan yang licin, tapi saya suka memisahkan permintaan pembuatan kode. Saya suka menjalankan hasil ke teks dan bisa memotong-n-tempelkan ke pekerjaan. Semua solusi yang sangat elegan - terima kasih!
mdoyle
8

Alternatif untuk SQL dinamis adalah SQLCMD , yang dapat dipanggil dari baris perintah, langkah pekerjaan agen, cmdlet Invoke-Sqlcmd Powershell atau diaktifkan di SSMS . Contoh Anda dalam sintaks SQLCMD adalah:

:SETVAR DatabaseName MyDatabase

SELECT * FROM $(DatabaseName).sys.indexes;

Mode SQLCMD adalah salah satu fitur yang ingin saya ketahui sebelumnya. Berguna dalam banyak situasi.

Mark Storey-Smith
sumber
1
Ini juga cukup sederhana dan elegan untuk dicapai dengan PowerShell. Saya suka pendekatan Anda, Mark. +1
Thomas Stringer
@ Hiu Poin bagus, saya akan mengedit cmdlet PowerShell masuk
Mark Storey-Smith
Juga solusi yang bagus, saya akan melihat ini juga.
mdoyle
Ini bagus, apakah ada alasan bagus mengapa ini tidak didukung di TSQL?
tulang
0

Biasanya sulit untuk merujuk satu set tabel dari prosedur yang terdapat dalam DB yang berbeda. Jika Anda menginstal prosedur Anda di Master, sebagai prosedur sistem, maka prosedur ini dapat digunakan dalam konteks DB lainnya tanpa mencoba merujuk kembali ke prosedur itu sendiri.

Saya berpikir bahwa: jika prosedur Anda dimulai dengan 'sp_' maka itu menjadi terlihat secara universal, dan jika Anda mendefinisikannya dalam skema 'sys.sp_%', maka itu dapat digunakan dalam konteks DB lainnya.

Ini akan memberikan cara alternatif untuk beroperasi dalam banyak DB tanpa harus mencolokkan DB_name secara dinamis.

silvertc
sumber