Pracuję nad niestandardowym rozwiązaniem konserwacyjnym, korzystając z sys.dm_db_index_physical_stats
widoku. Obecnie odwołuje się do niego z procedury przechowywanej. Teraz, gdy ta procedura przechowywana działa na jednej z moich baz danych, robi to, co chcę, i pobiera listę wszystkich rekordów dotyczących dowolnej bazy danych. Kiedy umieszczam go w innej bazie danych, wyświetla listę wszystkich rekordów odnoszących się tylko do tej bazy danych.
Na przykład (kod na dole):
- Zapytanie uruchomione dla bazy danych 6 pokazuje [wymagane] informacje dla baz danych 1-10.
- Zapytanie uruchomione dla bazy danych 3 pokazuje [wymagane] informacje tylko dla bazy danych 3.
Powodem, dla którego chcę tej procedury specjalnie dla bazy danych 3, jest to, że wolałbym przechowywać wszystkie obiekty konserwacji w tej samej bazie danych. Chciałbym, aby to zadanie znajdowało się w bazie danych konserwacji i działało tak, jakby było w tej bazie danych aplikacji.
Kod:
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
źródło
Odpowiedzi:
Jednym ze sposobów może być wykonanie procedury systemowej,
master
a następnie utworzenie opakowania w bazie danych konserwacji. Pamiętaj, że będzie to działać tylko dla jednej bazy danych na raz.Po pierwsze, w mistrzu:
Teraz w bazie danych konserwacji utwórz opakowanie, które używa dynamicznego SQL do prawidłowego ustawienia kontekstu:
(Powodem, dla którego nazwa bazy danych tak naprawdę nie może być,
NULL
jest to, że nie można dołączyć do rzeczy takich jaksys.objects
isys.indexes
ponieważ istnieją one niezależnie w każdej bazie danych. Być może więc zastosuj inną procedurę, jeśli chcesz uzyskać informacje dotyczące całej instancji).Teraz możesz to nazwać dla dowolnej innej bazy danych, np
I zawsze możesz utworzyć
synonym
w każdej bazie danych, więc nie musisz nawet odwoływać się do nazwy bazy danych konserwacji:Innym sposobem byłoby użycie dynamicznego SQL, jednak to również zadziała tylko dla jednej bazy danych na raz:
Jeszcze innym sposobem byłoby utworzenie widoku (lub funkcji o wartościach przechowywanych w tabeli), aby połączyć nazwy tabel i indeksów wszystkich baz danych, jednak trzeba by na stałe zakodować nazwy baz danych w widoku i zachować je podczas dodawania / usuń bazy danych, które chcesz uwzględnić w tym zapytaniu. Umożliwiłoby to, w przeciwieństwie do innych, pobieranie statystyk dla wielu baz danych jednocześnie.
Po pierwsze widok:
Następnie procedura:
źródło
Cóż, są złe wieści, dobre wieści z haczykiem i kilka naprawdę dobrych wiadomości.
Złe wiadomości
Obiekty T-SQL są wykonywane w bazie danych, w której się znajdują. Istnieją dwa (niezbyt przydatne) wyjątki:
sp_
i istniejącymi w[master]
bazie danych (niezbyt dobra opcja: jedna baza danych na raz, dodawanie czegoś do[master]
, ewentualnie dodawanie synonimów do każdej bazy danych, co należy zrobić dla każdej nowej bazy danych)sp_
przechowywanym proc[master]
.Dobra wiadomość (z haczykiem)
Wielu (być może większość?) Ludzi jest świadomych wbudowanych funkcji, aby uzyskać naprawdę popularne metadane:
Korzystanie z tych funkcji może wyeliminować potrzebę JOIN
sys.databases
(choć ten nie jest tak naprawdę problemem),sys.objects
(preferowane w porównaniu zsys.tables
wykluczeniem widoków indeksowanych) isys.schemas
(brakowało tego i nie wszystko jest wdbo
schemacie ;-). Ale nawet po usunięciu trzech z czterech JOINÓW, wciąż funkcjonalnie jesteśmy w tym samym miejscu, prawda? Źle-o!Jedną z ciekawych funkcji
OBJECT_NAME()
iOBJECT_SCHEMA_NAME()
jest to, że mają opcjonalny drugi parametr dla@database_id
. Oznacza to, że chociaż DOŁĄCZENIE do tych tabel (z wyjątkiemsys.databases
) jest specyficzne dla bazy danych, użycie tych funkcji pozwala uzyskać informacje o całym serwerze. Nawet OBJECT_ID () pozwala na uzyskanie informacji o całym serwerze, nadając mu w pełni kwalifikowaną nazwę obiektu.Włączając te funkcje metadanych do głównego zapytania, możemy uprościć, jednocześnie rozszerzając zakres poza bieżącą bazę danych. Pierwszy przebieg refaktoryzacji zapytania daje nam:
A teraz „złapanie”: nie ma funkcji metadanych, aby uzyskać nazwy indeksu, nie mówiąc już o nazwie obejmującej cały serwer. Więc to o to chodzi? Czy jesteśmy w 90% kompletni i nadal utknęliśmy, że musimy znajdować się w określonych bazach danych, aby uzyskać
sys.indexes
dane? Czy naprawdę musimy utworzyć procedurę składowaną, aby użyć dynamicznego SQL do zapełniania, za każdym razem, gdy uruchamia się nasz główny proc, tabelę temp wszystkichsys.indexes
wpisów we wszystkich bazach danych, abyśmy mogli do niej dołączyć? NIE!Naprawdę dobre wieści
Tak więc pojawia się mała cecha, której niektórzy uwielbiają nienawidzić, ale przy odpowiednim stosowaniu może robić niesamowite rzeczy. Tak: SQLCLR. Dlaczego? Ponieważ funkcje SQLCLR mogą oczywiście przesyłać instrukcje SQL, ale z samej natury przesyłania z kodu aplikacji jest to dynamiczny SQL. W przeciwieństwie do funkcji T-SQL, funkcje SQLCLR mogą wstrzyknąć nazwę bazy danych do zapytania przed jego wykonaniem. Czyli, możemy stworzyć własną funkcję lusterka zdolności
OBJECT_NAME()
iOBJECT_SCHEMA_NAME()
wziąćdatabase_id
i uzyskać informacje o tej bazie danych.Poniższy kod jest tą funkcją. Ale wymaga nazwy bazy danych zamiast identyfikatora, aby nie musiał wykonywać dodatkowego kroku w celu jej wyszukania (co czyni ją nieco mniej skomplikowaną i trochę szybszą).
Jeśli zauważysz, używamy połączenia kontekstowego, które jest nie tylko szybkie, ale działa również w
SAFE
złożeniach. Tak, działa to w Zgromadzeniu oznaczonym jakoSAFE
, więc to (lub jego odmiany) powinno nawet działać w bazie danych Azure SQL Database 12(obsługa SQLCLR została usunięta dość gwałtownie z bazy danych SQL Azure w kwietniu 2016 r . ) .Zatem nasze refaktoryzowanie drugiego zapytania do głównego zapytania daje nam następujące informacje:
Otóż to! Zarówno SQLCLR Scalar UDF, jak i twoja procedura przechowywana T-SQL mogą żyć w tej samej scentralizowanej
[maintenance]
bazie danych. I nie musisz przetwarzać jednej bazy danych na raz; teraz masz funkcje metadanych dla wszystkich zależnych informacji obejmujących cały serwer.PS Nie ma
.IsNull
sprawdzania parametrów wejściowych w kodzie C #, ponieważ obiekt opakowania T-SQL powinien zostać utworzony zWITH RETURNS NULL ON NULL INPUT
opcją:Dodatkowe uwagi:
Opisaną tutaj metodę można również wykorzystać do rozwiązania innych, bardzo podobnych problemów związanych z brakującymi funkcjami metadanych między bazami danych. Następująca sugestia Microsoft Connect jest przykładem takiego przypadku. I widząc, że Microsoft zamknął go jako „Nie naprawi”, jasne jest, że nie są zainteresowani udostępnianiem wbudowanych funkcji, takich jak
OBJECT_NAME()
zaspokojenie tej potrzeby (stąd obejście, które jest zamieszczone w tej Sugestii :-).Dodaj funkcję metadanych, aby uzyskać nazwę obiektu z hobt_id
Aby dowiedzieć się więcej o korzystaniu z SQLCLR, zapoznaj się z serią „ Schody do SQLCLR ”, którą piszę w SQL Server Central (wymagana jest darmowa rejestracja; przepraszam, nie kontroluję zasad tej witryny).
Przedstawiona
IndexName()
powyżej funkcja SQLCLR jest dostępna, wstępnie skompilowana, w łatwym do zainstalowania skrypcie na Pastebin. Skrypt włącza funkcję „CLR Integration”, jeśli nie jest jeszcze włączona, a zespół jest oznaczony jakoSAFE
. Jest kompilowany z wersją .NET Framework w wersji 2.0, dzięki czemu będzie działał w SQL Server 2005 i nowszych (tj. We wszystkich wersjach obsługujących SQLCLR).SQLCLR Funkcja metadanych dla bazy danych IndexName ()
Jeśli ktoś jest zainteresowany
IndexName()
funkcją SQLCLR i ponad 320 innymi funkcjami i procedurami przechowywanymi, jest on dostępny w bibliotece SQL # (której jestem autorem). Należy pamiętać, że chociaż istnieje wersja darmowa, funkcja Sys_IndexName jest dostępna tylko w pełnej wersji (wraz z podobną funkcją Sys_AssemblyName ).źródło