Saya sedang mengerjakan solusi perawatan khusus menggunakan sys.dm_db_index_physical_stats
tampilan. Saat ini saya telah dirujuk dari prosedur tersimpan. Sekarang ketika prosedur tersimpan itu berjalan di salah satu basis data saya, ia melakukan apa yang saya inginkan dan menarik daftar semua catatan mengenai basis data apa pun. Ketika saya letakkan di database yang berbeda itu menarik daftar semua catatan yang berkaitan hanya dengan DB itu.
Misalnya (kode di bawah):
- Query run terhadap Database 6 menunjukkan informasi [yang diminta] untuk database 1-10.
- Query run terhadap Database 3 memperlihatkan informasi [yang diminta] hanya untuk database 3.
Alasan saya menginginkan prosedur ini secara khusus pada basis data tiga adalah karena saya lebih suka menyimpan semua objek pemeliharaan di dalam basis data yang sama. Saya ingin pekerjaan ini ada di basis data pemeliharaan dan berfungsi seolah-olah berada di basis data aplikasi itu.
Kode:
ALTER PROCEDURE [dbo].[GetFragStats]
@databaseName NVARCHAR(64) = NULL
,@tableName NVARCHAR(64) = NULL
,@indexID INT = NULL
,@partNumber INT = NULL
,@Mode NVARCHAR(64) = 'DETAILED'
AS
BEGIN
SET NOCOUNT ON;
DECLARE @databaseID INT, @tableID INT
IF @databaseName IS NOT NULL
AND @databaseName NOT IN ('tempdb','ReportServerTempDB')
BEGIN
SET @databaseID = DB_ID(@databaseName)
END
IF @tableName IS NOT NULL
BEGIN
SET @tableID = OBJECT_ID(@tableName)
END
SELECT D.name AS DatabaseName,
T.name AS TableName,
I.name AS IndexName,
S.index_id AS IndexID,
S.avg_fragmentation_in_percent AS PercentFragment,
S.fragment_count AS TotalFrags,
S.avg_fragment_size_in_pages AS PagesPerFrag,
S.page_count AS NumPages,
S.index_type_desc AS IndexType
FROM sys.dm_db_index_physical_stats(@databaseID, @tableID,
@indexID, @partNumber, @Mode) AS S
JOIN
sys.databases AS D ON S.database_id = D.database_id
JOIN
sys.tables AS T ON S.object_id = T.object_id
JOIN
sys.indexes AS I ON S.object_id = I.object_id
AND S.index_id = I.index_id
WHERE
S.avg_fragmentation_in_percent > 10
ORDER BY
DatabaseName, TableName, IndexName, PercentFragment DESC
END
GO
sumber
Jawaban:
Salah satu caranya adalah dengan membuat prosedur sistem
master
dan kemudian membuat pembungkus dalam database pemeliharaan Anda. Perhatikan bahwa ini hanya akan berfungsi untuk satu basis data pada satu waktu.Pertama, di master:
Sekarang, di database pemeliharaan Anda, buat pembungkus yang menggunakan SQL dinamis untuk mengatur konteks dengan benar:
(Alasan mengapa nama basis data tidak dapat benar-benar
NULL
adalah karena Anda tidak dapat bergabung dengan hal-hal sepertisys.objects
dansys.indexes
karena mereka ada secara independen di setiap basis data. Jadi, mungkin ada prosedur yang berbeda jika Anda menginginkan informasi luas.)Sekarang Anda dapat memanggil ini untuk basis data lain, mis
Dan Anda selalu dapat membuat
synonym
di setiap basis data sehingga Anda bahkan tidak perlu merujuk nama basis data pemeliharaan:Cara lain adalah dengan menggunakan SQL dinamis, namun ini juga hanya akan bekerja untuk satu database pada suatu waktu:
Namun cara lain adalah dengan membuat tampilan (atau fungsi bernilai tabel) untuk menyatukan tabel dan nama indeks semua database Anda, namun Anda harus meng-hard-code nama-nama database ke dalam tampilan, dan mempertahankannya saat Anda menambahkan / hapus database yang ingin Anda izinkan dimasukkan dalam kueri ini. Ini tidak seperti yang lain, ini memungkinkan Anda untuk mengambil statistik untuk banyak basis data sekaligus.
Pertama, tampilan:
Lalu prosedurnya:
sumber
Nah, ada kabar buruk, kabar baik dengan tangkapan, dan beberapa kabar baik.
Berita buruknya
Objek T-SQL mengeksekusi dalam database di mana mereka berada. Ada dua pengecualian (tidak terlalu berguna):
sp_
dan yang ada dalam[master]
database (bukan pilihan bagus: satu DB pada suatu waktu, menambahkan sesuatu ke[master]
, mungkin menambahkan Sinonim untuk setiap DB, yang harus dilakukan untuk setiap DB baru)sp_
proc yang tersimpan di[master]
.Berita bagus (dengan tangkapan)
Banyak (mungkin sebagian besar?) Orang menyadari fungsi builtin untuk mendapatkan beberapa meta-data yang sangat umum:
Menggunakan fungsi-fungsi ini dapat menghilangkan kebutuhan untuk GABUNGAN untuk
sys.databases
(meskipun yang ini tidak benar-benar masalah),sys.objects
(lebih disukai daripadasys.tables
yang tidak termasuk Indexed Views), dansys.schemas
(Anda kehilangan satu itu, dan tidak semuanya ada dalamdbo
skema ;-). Tetapi bahkan dengan menghapus tiga dari empat BERGABUNG, kita masih secara fungsional berada di tempat yang sama, kan? Salah-o!Salah satu fitur
OBJECT_NAME()
danOBJECT_SCHEMA_NAME()
fungsi yang bagus adalah mereka memiliki parameter opsional kedua@database_id
. Berarti, sementara BERGABUNG ke tabel tersebut (kecuali untuksys.databases
) adalah basis data khusus, menggunakan fungsi-fungsi ini membuat Anda mendapatkan informasi di seluruh server. Bahkan OBJECT_ID () memungkinkan untuk info seluruh server dengan memberinya nama objek yang sepenuhnya memenuhi syarat.Dengan menggabungkan fungsi meta-data ini ke dalam kueri utama, kami dapat menyederhanakan sementara pada saat yang sama memperluas di luar database saat ini. Pass pertama refactoring kueri memberi kita:
Dan sekarang untuk "tangkapan": tidak ada fungsi meta-data untuk mendapatkan nama Indeks, apalagi yang server-wide. Jadi begitu ya? Apakah kita pada 90% selesai dan masih terjebak perlu dalam database tertentu untuk mendapatkan
sys.indexes
data? Apakah kita benar-benar perlu membuat prosedur tersimpan untuk menggunakan SQL Dinamis untuk mengisi, setiap kali proc utama kita berjalan, tabel temp semuasys.indexes
entri di semua database sehingga kita bisa BERGABUNG dengannya? TIDAK!Berita bagus
Maka muncullah sebuah fitur kecil yang beberapa orang suka benci, tetapi ketika digunakan dengan benar, dapat melakukan beberapa hal luar biasa. Yap: SQLCLR. Mengapa? Karena fungsi SQLCLR jelas dapat mengirimkan pernyataan SQL, tetapi pada dasarnya mengirimkan dari kode aplikasi, itu adalah Dynamic SQL. Jadi tidak seperti fungsi T-SQL, fungsi SQLCLR dapat menyuntikkan nama database ke dalam kueri sebelum menjalankannya. Artinya, kita dapat membuat fungsi kita sendiri untuk mencerminkan kemampuan
OBJECT_NAME()
danOBJECT_SCHEMA_NAME()
untuk mengambildatabase_id
dan mendapatkan info untuk database itu.Kode berikut adalah fungsi itu. Tetapi dibutuhkan nama database daripada ID sehingga tidak perlu melakukan langkah ekstra untuk mencarinya (yang membuatnya sedikit kurang rumit dan sedikit lebih cepat).
Jika Anda perhatikan, kami menggunakan Koneksi Konteks, yang tidak hanya cepat, tetapi juga berfungsi di
SAFE
Assemblies. Yap, ini berfungsi di Majelis yang ditandai sebagaiSAFE
, sehingga (atau variasi dari itu) bahkan harus bekerja pada Azure SQL Database V12(dukungan untuk SQLCLR telah dihapus, agak tiba-tiba, dari Azure SQL Database pada bulan April 2016) .Jadi refactoring pass kedua kami dari pertanyaan utama memberi kami yang berikut:
Itu dia! Baik SQLCLR Scalar UDF ini dan Prosedur Penyimpanan T-SQL pemeliharaan Anda dapat hidup dalam
[maintenance]
database terpusat yang sama . DAN, Anda tidak perlu memproses satu basis data sekaligus; sekarang Anda memiliki fungsi meta-data untuk semua info dependen yang mencakup seluruh server.PS Tidak ada
.IsNull
pengecekan parameter input dalam kode C # karena objek pembungkus T-SQL harus dibuat denganWITH RETURNS NULL ON NULL INPUT
opsi:Catatan tambahan:
Metode yang dijelaskan di sini juga dapat digunakan untuk menyelesaikan masalah lain yang sangat mirip yaitu hilangnya fungsi meta-basis data database. Saran Microsoft Connect berikut ini adalah contoh dari satu kasus seperti itu. Dan, melihat bahwa Microsoft telah menutupnya sebagai "Won't Fix", jelas bahwa mereka tidak tertarik menyediakan fungsi built-in seperti
OBJECT_NAME()
untuk memenuhi kebutuhan ini (maka Solusi yang diposting pada Saran itu :-).Tambahkan fungsi metadata untuk mendapatkan nama objek dari hobt_id
Untuk mempelajari lebih lanjut tentang penggunaan SQLCLR, silakan lihat seri Stairway to SQLCLR yang saya tulis di SQL Server Central (pendaftaran gratis diperlukan; maaf, saya tidak mengontrol kebijakan situs itu).
Fungsi
IndexName()
SQLCLR yang ditunjukkan di atas tersedia, sudah dikompilasi sebelumnya, dalam skrip yang mudah dipasang di Pastebin. Script memungkinkan fitur "Integrasi CLR" jika belum diaktifkan, dan Majelis ditandai sebagaiSAFE
. Itu dikompilasi melawan .NET Framework versi 2.0 sehingga akan bekerja di SQL Server 2005 dan yang lebih baru (yaitu semua versi yang mendukung SQLCLR).SQLCLR Fungsi Meta-data untuk cross-database IndexName ()
Jika ada yang tertarik pada
IndexName()
fungsi SQLCLR dan lebih dari 320 fungsi lainnya dan prosedur tersimpan, ini tersedia di pustaka SQL # (yang saya penulis). Harap dicatat bahwa meskipun ada versi gratis, fungsi Sys_IndexName hanya tersedia dalam versi lengkap (bersama dengan fungsi Sys_AssemblyName serupa ).sumber