Posts tagged metadane
[PL] Nowe obiekty systemowe w SQL Server 2012 RC0
Dec 12th
Posted by Pawel Potasinski in Denali
Całkiem niedawno Aaron Bertrand (SQL Server MVP) opublikował na swoim blogu listę nowych obiektów systemowych, które pojawiły się w SQL Server 2012 RC0, a nie było ich w SQL Server 2012 CTP3. Pomyślałem, że przydałaby się analogiczna lista, ale w odniesieniu do obiektów z SQL Server 2008 R2.
Jak taką listę przyrządzić? W moim przypadku było to dziecinnie proste :-) Mam na jednej maszynie dwie instancje SQL Server – 2012 RC0 i 2008 R2. Wystarczyło na jednej z nich stworzyć serwer dołączony do drugiej i już można było pisać zapytania porównujące metadane obu instancji w stylu:
-- Nowe obiekty SELECT QUOTENAME(SCHEMA_NAME([schema_id])) + '.' + QUOTENAME(name) AS object, type_desc FROM sys.system_objects WHERE is_ms_shipped = 1 EXCEPT SELECT (QUOTENAME(SCHEMA_NAME([schema_id])) + '.' + QUOTENAME(name)) COLLATE database_default, type_desc COLLATE database_default FROM [ASUS\SQL2008R2].master.sys.system_objects WHERE is_ms_shipped = 1 ORDER BY 1; -- Obiekty usunięte SELECT (QUOTENAME(SCHEMA_NAME([schema_id])) + '.' + QUOTENAME(name)) COLLATE database_default AS object, type_desc COLLATE database_default AS type_desc FROM [ASUS\SQL2008R2].master.sys.system_objects WHERE is_ms_shipped = 1 EXCEPT SELECT QUOTENAME(SCHEMA_NAME([schema_id])) + '.' + QUOTENAME(name), type_desc FROM sys.system_objects WHERE is_ms_shipped = 1 ORDER BY 1;
Powyższe zapytania były uruchamiane na instancji SQL Server 2012 RC0, zaś ASUS\SQL2008R2 to nazwa linked servera wskazującego na instancję SQL Server 2008 R2.
Co z tego wyszło?
Nowe obiekty systemowe
| Nazwa obiektu | Typ obiektu |
| [INFORMATION_SCHEMA].[SEQUENCES] | VIEW |
| [sys].[availability_databases_cluster] | VIEW |
| [sys].[availability_group_listener_ip_addresses] | VIEW |
| [sys].[availability_group_listeners] | VIEW |
| [sys].[availability_groups] | VIEW |
| [sys].[availability_groups_cluster] | VIEW |
| [sys].[availability_read_only_routing_lists] | VIEW |
| [sys].[availability_replicas] | VIEW |
| [sys].[column_store_dictionaries] | VIEW |
| [sys].[column_store_index_stats] | VIEW |
| [sys].[column_store_segments] | VIEW |
| [sys].[database_filestream_options] | VIEW |
| [sys].[dm_db_database_page_allocations] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_db_fts_index_physical_stats] | VIEW |
| [sys].[dm_db_log_space_usage] | VIEW |
| [sys].[dm_db_objects_disabled_on_compatibility_level_change] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_db_uncontained_entities] | VIEW |
| [sys].[dm_exec_describe_first_result_set] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_exec_describe_first_result_set_for_object] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_filestream_non_transacted_handles] | VIEW |
| [sys].[dm_fts_index_keywords_by_property] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_fts_semantic_similarity_population] | VIEW |
| [sys].[dm_hadr_auto_page_repair] | VIEW |
| [sys].[dm_hadr_availability_group_states] | VIEW |
| [sys].[dm_hadr_availability_replica_cluster_nodes] | VIEW |
| [sys].[dm_hadr_availability_replica_cluster_states] | VIEW |
| [sys].[dm_hadr_availability_replica_states] | VIEW |
| [sys].[dm_hadr_cluster] | VIEW |
| [sys].[dm_hadr_cluster_members] | VIEW |
| [sys].[dm_hadr_cluster_networks] | VIEW |
| [sys].[dm_hadr_database_replica_cluster_states] | VIEW |
| [sys].[dm_hadr_database_replica_states] | VIEW |
| [sys].[dm_hadr_instance_node_map] | VIEW |
| [sys].[dm_hadr_name_id_map] | VIEW |
| [sys].[dm_logconsumer_cachebufferrefs] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_logconsumer_privatecachebuffers] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_logpool_consumers] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_logpool_hashentries] | VIEW |
| [sys].[dm_logpool_sharedcachebuffers] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_logpool_stats] | VIEW |
| [sys].[dm_logpoolmgr_freepools] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_logpoolmgr_respoolsize] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_logpoolmgr_stats] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_os_cluster_properties] | VIEW |
| [sys].[dm_os_memory_broker_clerks] | VIEW |
| [sys].[dm_os_server_diagnostics_log_configurations] | VIEW |
| [sys].[dm_os_volume_stats] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[dm_os_windows_info] | VIEW |
| [sys].[dm_resource_governor_resource_pool_affinity] | VIEW |
| [sys].[dm_server_memory_dumps] | VIEW |
| [sys].[dm_server_registry] | VIEW |
| [sys].[dm_server_services] | VIEW |
| [sys].[dm_tcp_listener_states] | VIEW |
| [sys].[filetable_system_defined_objects] | VIEW |
| [sys].[filetables] | VIEW |
| [sys].[fn_hadr_backup_is_preferred_replica] | SQL_SCALAR_FUNCTION |
| [sys].[fn_MSxe_read_event_stream] | SQL_INLINE_TABLE_VALUED_FUNCTION |
| [sys].[fulltext_semantic_language_statistics_database] | VIEW |
| [sys].[fulltext_semantic_languages] | VIEW |
| [sys].[GeographyCollectionAggregate] | AGGREGATE_FUNCTION |
| [sys].[GeographyConvexHullAggregate] | AGGREGATE_FUNCTION |
| [sys].[GeographyEnvelopeAggregate] | AGGREGATE_FUNCTION |
| [sys].[GeographyUnionAggregate] | AGGREGATE_FUNCTION |
| [sys].[GeometryCollectionAggregate] | AGGREGATE_FUNCTION |
| [sys].[GeometryConvexHullAggregate] | AGGREGATE_FUNCTION |
| [sys].[GeometryEnvelopeAggregate] | AGGREGATE_FUNCTION |
| [sys].[GeometryUnionAggregate] | AGGREGATE_FUNCTION |
| [sys].[registered_search_properties] | VIEW |
| [sys].[registered_search_property_lists] | VIEW |
| [sys].[resource_governor_resource_pool_affinity] | VIEW |
| [sys].[sequences] | VIEW |
| [sys].[sp_audit_write] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_availability_group_command_internal] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_begin_parallel_nested_tran] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_commit_parallel_nested_tran] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_db_increased_partitions] | SQL_STORED_PROCEDURE |
| [sys].[sp_describe_first_result_set] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_describe_undeclared_parameters] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_filestream_force_garbage_collection] | SQL_STORED_PROCEDURE |
| [sys].[sp_filestream_recalculate_container_size] | SQL_STORED_PROCEDURE |
| [sys].[sp_flush_commit_table_on_demand] | SQL_STORED_PROCEDURE |
| [sys].[sp_fulltext_semantic_register_language_statistics_db] | SQL_STORED_PROCEDURE |
| [sys].[sp_fulltext_semantic_unregister_language_statistics_db] | SQL_STORED_PROCEDURE |
| [sys].[sp_get_redirected_publisher] | SQL_STORED_PROCEDURE |
| [sys].[sp_help_spatial_geography_histogram] | SQL_STORED_PROCEDURE |
| [sys].[sp_help_spatial_geometry_histogram] | SQL_STORED_PROCEDURE |
| [sys].[sp_kill_filestream_non_transacted_handles] | SQL_STORED_PROCEDURE |
| [sys].[sp_migrate_user_to_contained] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_MS_marksystemobject] | SQL_STORED_PROCEDURE |
| [sys].[sp_MScheckIsPubOfSub] | SQL_STORED_PROCEDURE |
| [sys].[sp_MSgetversion] | SQL_STORED_PROCEDURE |
| [sys].[sp_MSsetupnosyncsubwithlsnatdist_cleanup] | SQL_STORED_PROCEDURE |
| [sys].[sp_MSsetupnosyncsubwithlsnatdist_helper] | SQL_STORED_PROCEDURE |
| [sys].[sp_new_parallel_nested_tran_id] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_redirect_publisher] | SQL_STORED_PROCEDURE |
| [sys].[sp_repl_generateevent] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_rollback_parallel_nested_tran] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_sequence_get_range] | SQL_STORED_PROCEDURE |
| [sys].[sp_server_diagnostics] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_showmemo_xml] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_validate_redirected_publisher] | SQL_STORED_PROCEDURE |
| [sys].[sp_validate_replica_hosts_as_publishers] | SQL_STORED_PROCEDURE |
| [sys].[trace_xe_action_map] | USER_TABLE |
| [sys].[trace_xe_event_map] | USER_TABLE |
Kilka obserwacji:
- pojawiło się ponad 100 nowych obiektów – sporo,
- wśród nowych obiektów widać sporą grupę funkcji agregujących przeznaczonych do pracy z danymi przestrzennymi,
- widać pokaźną grupę obiektów zwracających metadane i informacje o HADR,
- z jakichś powodów nadal dodawane są procedury rozszerzone,
- kilka obiektów wygląda całkiem ciekawie – np. sp_server_diagnostics.
Obiekty, które były w 2008 R2, ale nie ma ich w SQL Server 2012 RC0
| Nazwa obiektu | Typ obiektu |
| [sys].[database_principal_aliases] | VIEW |
| [sys].[sp_ActiveDirectory_Obj] | SQL_STORED_PROCEDURE |
| [sys].[sp_ActiveDirectory_SCP] | SQL_STORED_PROCEDURE |
| [sys].[sp_ActiveDirectory_Start] | SQL_STORED_PROCEDURE |
| [sys].[sp_batch_params] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_dboption] | SQL_STORED_PROCEDURE |
| [sys].[sp_dropalias] | SQL_STORED_PROCEDURE |
| [sys].[sp_fetchLOBfromcookie] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_MSgetversion] | EXTENDED_STORED_PROCEDURE |
| [sys].[sp_processmail] | SQL_STORED_PROCEDURE |
| [sys].[xp_adsirequest] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_deletemail] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_findnextmsg] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_get_mapi_default_profile] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_get_mapi_profiles] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_MSADEnabled] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_MSADSIObjReg] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_MSADSIObjRegDB] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_MSADSIReg] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_readmail] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_sendmail] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_startmail] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_stopmail] | EXTENDED_STORED_PROCEDURE |
| [sys].[xp_test_mapi_profile] | EXTENDED_STORED_PROCEDURE |
Komentarze do listy obiektów usuniętych w porównaniu do wersji 2008 R2:
- usuniętych zostało sporo procedur rozszerzonych,
- na liście znalazła się m.in. procedura sp_dboption, którą można było spotkać choćby w skrypcie tworzącym bazę danych Northwind (wiem, wiem, to zabytek, ale niektórzy nadal jej używają do celów edukacyjnych),
- sporo obiektów dotyczy usługi SQLMail, która została skutecznie zastąpiona usługą Database Mail w poprzednich wersjach SQL Servera.
Zadanie domowe dla chętnych – wyprodukować listy nowych kolumn i kolumn o zmienionych długościach w widokach systemowych w SQL Server 2012 RC0 w porównaniu do wersji 2008 R2. Nie powinno to być wielkie wyzwanie :-) Generalnie, zachęcam do podobnych zabaw, ponieważ w ten sposób odkrywamy nowe obiekty systemowe, poznajemy dogłębnie warstwę metadanych systemu, a to – według mnie – podstawowa wiedza o systemie, dzięki której DBA umie wydobywać stosowne informacje z najgłębszych zakamarków systemu.
[PL] Skryptowanie w SQL Server 2008 – Proste typy danych użytkownika
Aug 16th
Posted by Pawel Potasinski in Skryptowanie w SQL Server 2008
Intro
Typy danych użytkownika tworzymy zazwyczaj z myślą, by ewentualne zmiany długości, precyzji czy skali przeprowadzać w sposób prostszy, bo scentralizowany. Niestety, Microsoft nie daje nam w SQL Server polecenia ALTER TYPE, ale i tak używamy własnych typów, a w razie potrzeby jakiejkolwiek zmiany przeprowadzamy operację opisaną tutaj: [EN] Call for voting – ALTER TYPE in SQL Server. W tej notce pokażę, jak można skryptować proste typy danych stworzone przez użytkowników w bazie danych. Nie będę opisywał skryptowania typów tabelarycznych i typów stworzonych w CLR.
Skryptowanie typów prostych
Zadanie: zeskryptować wszystkie proste typy danych użytkownika w bazie danych.
Dla każdego typu trzeba stworzyć kod sprawdzający, czy typ nie istnieje (trzeba zajrzeć do widoku sys.types) oraz wygenerować kod polecenia CREATE TYPE o składni jak poniżej (na podstawie Books Online):
CREATE TYPE [ schema_name. ] type_name FROM base_type [ ( precision [ , scale ] ) ] [ NULL | NOT NULL ];
Rozwiązanie:
USE AdventureWorks2008R2;
SET NOCOUNT ON;
SELECT
'IF NOT EXISTS (
SELECT * FROM sys.types
WHERE [schema_id] = SCHEMA_ID(' +
QUOTENAME(SCHEMA_NAME([schema_id]), '''') + ') AND name = N' +
QUOTENAME(name, '''') + '
)
CREATE TYPE ' + QUOTENAME(SCHEMA_NAME([schema_id])) + '.' + QUOTENAME(name) + '
FROM ' + TYPE_NAME(system_type_id) +
CASE
WHEN system_type_id IN (
165, --varbinary
167, --varchar
173, --binary
175, --char
231, --nvarchar
239 --nchar
)
THEN '(' +
CONVERT(
varchar(4),
CASE
WHEN max_length = -1
THEN 'max'
ELSE max_length
END
) + ')'
WHEN system_type_id IN (
41, --time
42, --datetime2
43 --datetimeoffset
)
THEN '(' + CONVERT(char(1), scale) + ')'
WHEN system_type_id IN (
106, --decimal
108 --numeric
)
THEN '(' +
CONVERT(varchar(2), precision) + ',' +
CONVERT(varchar(2), scale) + ')'
WHEN system_type_id = 62 --float
THEN '(' + CONVERT(varchar(2), precision) + ')'
ELSE ''
END + ' ' +
CASE
WHEN is_nullable = 0
THEN 'NOT NULL'
ELSE 'NULL'
END + ';
GO'
FROM sys.types
WHERE is_user_defined = 1 --typ użytkownika
AND is_table_type = 0 --nie typ tabelaryczny
AND is_assembly_type = 0; --nie typ CLR
GO
Fragment wyniku:
Uwagi: w Management Studio ustaw Results to Text, sugeruję też zwiększenie maksymalnej długości tekstu w jednej kolumnie zwracanej w wynikach zapytań (w menu głównym kliknij Tools – Options – Query Results – SQL Server – Results to Text i w polu Maximum number of characters displayed in each column wpisz 8192, a następnie uruchom ponownie Management Studio).
Komentarz do kodu:
- metadane typów zwraca widok sys.types (ale metadane typów tabelarycznych są też w sys.table_types),
- typy też mają dwuczłonowe nazwy (znajdują się w schematach) – stąd użycie funkcji SCHEMA_NAME,
- funkcja QUOTENAME jak zwykle służy mi do otaczania identyfikatorów nawiasami kwadratowymi i apostrofami,
- do uzyskania nazwy typu systemowego użytego na potrzeby danego typu użytkownika wykorzystałem funkcję TYPE_NAME wywołaną na identyfikatorze typu systemowego trzymanym w kolumnie system_type_id w widoku sys.types,
- przy niektórych typach systemowych niezbędne jest podanie maksymalnej długości (typy binarne i tekstowe), dokładności (data i czas), precyzji (float) albo precyzji i skali (decimal/numeric),
- dzięki kolumnie is_nullable z widoku sys.types mogłem określić, czy typ pozwala domyślnie na przechowywanie wartości NULL, czy nie,
- w klauzuli WHERE odfiltrowałem typy systemowe, typy tabelaryczne i typy CLR.
Słowo podsumowania
Skryptowanie typów prostych jest stosunkowo łatwe. W zasadzie wszystkie niezbędne metadane są do odczytania z jednego widoku systemowego (sys.types). A przy okazji można się choćby oswoić z niekoniecznie oczywistym faktem – typy też są trzymane w schematach. Skryptowanie pozostałych typów użytkownika (tabelarycznych i CLR) jest już nieco trudniejsze. Ale to temat na być może kolejny wpis na moim blogu :-)
[EDYCJA] Dziękuję Krzysiowi Stachyrze, który wypatrzył w tym wpisie czeski błąd – oczywiście SQL Server nie daje polecenia ALTER TYPE. ALTER TABLE na szczęście mamy :-) [/EDYCJA]
[PL] Skryptowanie w SQL Server 2008 – Obiekty proceduralne
Aug 8th
Posted by Pawel Potasinski in Skryptowanie w SQL Server 2008
Pilot
Umiejętność korzystania z metadanych w celu skryptowania obiektów w bazach danych (tabel, procedur, itd.) przydaje się w życiu administratora i programisty baz danych. Sam wykorzystuję skryptowanie do cyklicznego tworzenia kopii kodu T-SQL obiektów w bazach danych (na wypadek, gdyby akurat okazało się, że w bazie uszkodzeniu uległ któryś z obiektów przechowujących metadane). Zapytasz, czemu nie użyć do tego mechanizmu skryptowania oferowanego przez Management Studio? Otóż skryptowanie z użyciem własnego kodu T-SQL daje mi więcej swobody, a ponadto skryptowanie w Management Studio ma swoje wady (patrz tutaj: [EN] SQL Server – Don’t (sp_)rename your database objects) i na dodatek często występują spore problemy w przypadku, gdy baza danych zawiera sporo obiektów (jakoś listowanie pokaźnej listy obiektów nie wychodzi narzędziu Management Studio – często aplikacja się zawiesza albo odpowiada wyjątkiem klasy OutOfMemoryException – patrz tutaj: Dziennik pokładowy MSSQL 2008 (wpis 0×0011) – Problem z Generate Script…).
Dlatego postanowiłem napocząć na blogu nową serię wpisów poświęconych tworzeniu własnego kodu T-SQL do skryptowania. Zaczynam od skryptowania obiektów proceduralnych – funkcji, procedur, widoków i wyzwalaczy. Potem spróbuję pokazać, jak można skryptować typy danych użytkownika, więzy, obiekty CLR i tabele. Tam, gdzie ilość kodu nie będzie wykraczała poza możliwości percepcji, będę się starał pokazać pełne rozwiązanie, ale na przykład w przypadku tabel już wiem, że nie będę podawał pełnego rozwiązania, a tylko podsunę kawałek kodu i parę pomysłów na dobry początek :-)
Kod będę tworzył z myślą o skryptowaniu w SQL Server 2008 i SQL Server 2008 R2. Przy odrobinie wysiłku kod będzie do zaadoptowania na potrzeby SQL Server 2005.
Funkcje, procedury, widoki
Zadanie: zeskryptować wszystkie funkcje, procedury i widoki w wybranej bazie danych.
Rozwiązanie:
USE AdventureWorks2008R2; GO SET NOCOUNT ON; DECLARE @SQL TABLE ( LineId int IDENTITY(1,1) NOT NULL PRIMARY KEY, Line nvarchar(4000) NOT NULL ); DECLARE @schema_name sysname, @object_name sysname, @type_desc nvarchar(60), @type char(2), @definition nvarchar(max), @pindex int; DECLARE CursorObjects CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR SELECT SCHEMA_NAME(o.[schema_id]), o.name, o.type_desc COLLATE database_default, o.type COLLATE database_default, m.definition FROM sys.objects AS o INNER JOIN sys.sql_modules AS m ON m.[object_id] = o.[object_id] WHERE o.type IN ('FN', 'IF', 'P', 'TF', 'V') AND o.is_ms_shipped = 0 ORDER BY SCHEMA_NAME(o.[schema_id]), o.name; OPEN CursorObjects; FETCH NEXT FROM CursorObjects INTO @schema_name, @object_name, @type_desc, @type, @definition; WHILE @@FETCH_STATUS = 0 BEGIN SET @pindex = 0; -- generujemy PRINT INSERT INTO @SQL (Line) SELECT 'PRINT N''-- ' + @type_desc + ': ' + REPLACE( QUOTENAME(@schema_name) + '.' + QUOTENAME(@object_name), '''', '''''' ) + ''''; IF @definition IS NOT NULL BEGIN -- generujemy DROP INSERT INTO @SQL (Line) SELECT 'IF OBJECT_ID(N''' + REPLACE( QUOTENAME(@schema_name) + '.' + QUOTENAME(@object_name), '''', '''''' ) + ''', N''' + (@type) + ''') IS NOT NULL'; INSERT INTO @SQL (Line) SELECT ' DROP ' + CASE WHEN @type = 'P' THEN 'PROCEDURE' WHEN @type IN ('FN', 'IF', 'TF') THEN 'FUNCTION' ELSE 'VIEW' END + ' ' + QUOTENAME(@schema_name) + '.' + QUOTENAME(@object_name) + ';'; -- poprzedzamy definicję słowem GO INSERT INTO @SQL (Line) SELECT 'GO'; -- kroimy definicję na poszczególne wiersze SET @pindex = PATINDEX('%' + CHAR(13) + CHAR(10) + '%', @definition); WHILE @pindex > 0 BEGIN INSERT INTO @SQL(Line) SELECT REPLACE(SUBSTRING(@definition, 1, @pindex), CHAR(13), ''); SET @definition = SUBSTRING(@definition, @pindex + 2, LEN(@definition)); SET @pindex = PATINDEX('%' + CHAR(13) + CHAR(10) +'%', @definition); END; END; ELSE BEGIN INSERT INTO @SQL (Line) SELECT 'PRINT N''-- *** This object is encrypted ***'''; END; -- kończymy definicję słowem GO INSERT INTO @SQL (Line) SELECT 'GO'; FETCH NEXT FROM CursorObjects INTO @schema_name, @object_name, @type_desc, @type, @definition; END; CLOSE CursorObjects; DEALLOCATE CursorObjects; SELECT Line AS ' ' FROM @SQL ORDER BY LineId; GO
Fragment wyniku:
Uwagi: w Management Studio ustaw Results to Text, sugeruję też zwiększenie maksymalnej długości tekstu w jednej kolumnie zwracanej w wynikach zapytań (w menu głównym kliknij Tools – Options – Query Results – SQL Server – Results to Text i w polu Maximum number of characters displayed in each column wpisz 8192, a następnie uruchom ponownie Management Studio).
Komentarz do kodu:
- definicje obiektów są trzymane w widoku sys.sql_modules w kolumnie definition, wyjątek stanowią obiekty z opcją WITH ENCRYPTION użytą w definicji (takie obiekty są “szyfrowane”),
- listę obiektów stworzonych przez użytkowników w bazie danych zwraca widok sys.objects (ale jest też widok sys.all_objects, w którym dodatkowo są zawarte metadane obiektów systemowych),
- fragmenty generowanego skryptu T-SQL są dodawane w kursorze (iteracja = definicja jednego obiektu) do zmiennej tabelarycznej @SQL (linie są numerowane autonumeracją w kolumnie LineId, zakładam maksymalną długość jednej linii kodu w obiekcie na 4000 znaków),
- zapytanie filtruje obiekty wg typu – wybiera funkcje trzech rodzajów, procedury i widoki, jest też filtr odrzucający ewentualne obiekty systemowe (kolumna is_ms_shipped w widoku sys.objects),
- przed definicją każdego obiektu dorzucam kod wykrywający obecność obiektu w bazie danych i usuwający obiekt w przypadku, gdy takowy istnieje,
- kolumny type i type_desc z widoku sys.objects mają na sztywno przypisane collation, stąd użycie funkcji COLLATE (inaczej nie dałoby się sklejać tych kolumn z tekstem, gdy collation bazy jest inne niż collation wspomnianych kolumn),
- operuję zawsze dwuczłonowymi nazwami obiektów (dzięki temu nazwa jest jednoznaczna w kontekście bazy danych) – do uzyskania nazwy schematu obiektu wykorzystuję funkcję SCHEMA_NAME,
- funkcja REPLACE jest przeze mnie użyta do “wyescape’owania” apostrofów w nazwach obiektów (swoją drogą, jak ktoś używa niestandardowych identyfikatorów zawierających dziwne znaczki i/lub słowa kluczowe, sam prosi się o kłopoty),
- funkcja QUOTENAME pozwala otoczyć identyfikatory nawiasami kwadratowymi – zabezpieczam się przed niestandardowymi identyfikatorami.
Wyzwalacze na poziomie bazy
Zadanie: zeskryptować wszystkie wyzwalacze w wybranej bazie danych.
Rozwiązanie:
-- wyzwalacze DML i DDL (ON DATABASE) USE AdventureWorks2008R2; GO SET NOCOUNT ON; DECLARE @SQL TABLE ( LineId int IDENTITY(1,1) NOT NULL PRIMARY KEY, Line nvarchar(4000) NOT NULL ); DECLARE @schema_name sysname, @object_name sysname, @type_desc nvarchar(60), @parent_class int, @definition nvarchar(max), @pindex int; DECLARE CursorObjects CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR SELECT OBJECT_SCHEMA_NAME(t.[object_id]), t.name, t.type_desc COLLATE database_default, t.parent_class, m.definition FROM sys.triggers AS t INNER JOIN sys.sql_modules AS m ON m.[object_id] = t.[object_id] WHERE t.is_ms_shipped = 0 ORDER BY OBJECT_SCHEMA_NAME(t.[object_id]), t.name; OPEN CursorObjects; FETCH NEXT FROM CursorObjects INTO @schema_name, @object_name, @type_desc, @parent_class, @definition; WHILE @@FETCH_STATUS = 0 BEGIN SET @pindex = 0; -- generujemy PRINT INSERT INTO @SQL (Line) SELECT 'PRINT N''-- ' + @type_desc + ': ' + REPLACE( ISNULL(QUOTENAME(@schema_name) + '.', '') + QUOTENAME(@object_name), '''', '''''' ) + ''';'; IF @definition IS NOT NULL BEGIN -- generujemy DROP INSERT INTO @SQL (Line) SELECT 'IF EXISTS (SELECT * FROM sys.triggers WHERE name = N''' + REPLACE( ISNULL(QUOTENAME(@schema_name) + '.', '') + QUOTENAME(@object_name), '''', '''''' ) + ''')'; INSERT INTO @SQL (Line) SELECT ' DROP TRIGGER ' + ISNULL(QUOTENAME(@schema_name) + '.', '') + QUOTENAME(@object_name) + CASE WHEN @parent_class = 0 THEN ' ON DATABASE' ELSE '' END + ';'; -- poprzedzamy definicję słowem GO INSERT INTO @SQL (Line) SELECT 'GO'; -- kroimy definicję na poszczególne wiersze SET @pindex = PATINDEX('%' + CHAR(13) + CHAR(10) + '%', @definition); WHILE @pindex > 0 BEGIN INSERT INTO @SQL(Line) SELECT REPLACE(SUBSTRING(@definition, 1, @pindex), CHAR(13), ''); SET @definition = SUBSTRING(@definition, @pindex + 2, LEN(@definition)); SET @pindex = PATINDEX('%' + CHAR(13) + CHAR(10) +'%', @definition); END; END; ELSE BEGIN INSERT INTO @SQL (Line) SELECT 'PRINT N''-- *** This object is encrypted ***'''; END; -- kończymy definicję słowem GO INSERT INTO @SQL (Line) SELECT 'GO'; FETCH NEXT FROM CursorObjects INTO @schema_name, @object_name, @type_desc, @parent_class, @definition; END; CLOSE CursorObjects; DEALLOCATE CursorObjects; SELECT Line AS ' ' FROM @SQL ORDER BY LineId; GO
Fragment wyniku:
Uwagi: w Management Studio ustaw Results to Text, sugeruję też zwiększenie maksymalnej długości tekstu w jednej kolumnie zwracanej w wynikach zapytań (w menu głównym kliknij Tools – Options – Query Results – SQL Server – Results to Text i w polu Maximum number of characters displayed in each column wpisz 8192, a następnie uruchom ponownie Management Studio).
Komentarz do kodu:
- listę wyzwalaczy w bazie danych zwraca widok sys.triggers,
- fragmenty generowanego skryptu T-SQL są dodawane w kursorze (iteracja = definicja jednego obiektu) do zmiennej tabelarycznej @SQL (linie są numerowane autonumeracją w kolumnie LineId, zakładam maksymalną długość jednej linii kodu w obiekcie na 4000 znaków),
- drobnym wyzwaniem było rozróżnienie wyzwalaczy DML i DDL – każdy z tych typów wyzwalaczy wymaga nieco innego kodu przy usuwaniu, posłużyłem się kolumna parent_class z widoku sys.triggers,
- ponieważ w widoku sys.triggers nie ma kolumny schema_id, musiałem użyć funkcji OBJECT_SCHEMA_NAME do wyłuskania nazwy schematu,
- przy okazji taka obserwacja – wyzwalacz DML można utworzyć tylko w tym samym schemacie, w którym znajduje się nadrzędna tabela/widok.
Wyzwalacze na poziomie serwera
Zadanie: zeskryptować wszystkie wyzwalacze DDL na poziomie serwera.
Rozwiązanie:
USE master; GO SET NOCOUNT ON; DECLARE @SQL TABLE ( LineId int IDENTITY(1,1) NOT NULL PRIMARY KEY, Line nvarchar(4000) NOT NULL ); DECLARE @object_name sysname, @type_desc nvarchar(60), @definition nvarchar(max), @pindex int; DECLARE CursorObjects CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR SELECT t.name, t.type_desc COLLATE database_default, m.definition FROM sys.server_triggers AS t INNER JOIN sys.server_sql_modules AS m ON m.[object_id] = t.[object_id] WHERE t.is_ms_shipped = 0 ORDER BY t.name; OPEN CursorObjects; FETCH NEXT FROM CursorObjects INTO @object_name, @type_desc, @definition; WHILE @@FETCH_STATUS = 0 BEGIN SET @pindex = 0; -- generujemy PRINT INSERT INTO @SQL (Line) SELECT 'PRINT N''-- ' + @type_desc + ': ' + REPLACE( QUOTENAME(@object_name), '''', '''''' ) + ''';'; IF @definition IS NOT NULL BEGIN -- generujemy DROP INSERT INTO @SQL (Line) SELECT 'IF EXISTS (SELECT * FROM sys.server_triggers WHERE name = N''' + REPLACE( @object_name, '''', '''''' ) + ''')'; INSERT INTO @SQL (Line) SELECT ' DROP TRIGGER ' + QUOTENAME(@object_name) + ' ON ALL SERVER;' -- poprzedzamy definicję słowem GO INSERT INTO @SQL (Line) SELECT 'GO'; -- kroimy definicję na poszczególne wiersze SET @pindex = PATINDEX('%' + CHAR(13) + CHAR(10) + '%', @definition); WHILE @pindex > 0 BEGIN INSERT INTO @SQL(Line) SELECT REPLACE(SUBSTRING(@definition, 1, @pindex), CHAR(13), ''); SET @definition = SUBSTRING(@definition, @pindex + 2, LEN(@definition)); SET @pindex = PATINDEX('%' + CHAR(13) + CHAR(10) +'%', @definition); END; END; ELSE BEGIN INSERT INTO @SQL (Line) SELECT 'PRINT N''-- *** This object is encrypted ***'''; END; -- kończymy definicję słowem GO INSERT INTO @SQL (Line) SELECT 'GO'; FETCH NEXT FROM CursorObjects INTO @object_name, @type_desc, @definition; END; CLOSE CursorObjects; DEALLOCATE CursorObjects; SELECT Line AS ' ' FROM @SQL ORDER BY LineId; GO
Fragment wyniku:
Uwagi: w Management Studio ustaw Results to Text, sugeruję też zwiększenie maksymalnej długości tekstu w jednej kolumnie zwracanej w wynikach zapytań (w menu głównym kliknij Tools – Options – Query Results – SQL Server – Results to Text i w polu Maximum number of characters displayed in each column wpisz 8192, a następnie uruchom ponownie Management Studio).
Komentarz do kodu:
- listę wyzwalaczy na poziomie serwera zwraca widok sys.server_triggers,
- definicje widoków na poziomie serwera znajdują się w kolumnie definition widoku sys.server_sql_modules,
- fragmenty generowanego skryptu T-SQL są dodawane w kursorze (iteracja = definicja jednego obiektu) do zmiennej tabelarycznej @SQL (linie są numerowane autonumeracją w kolumnie LineId, zakładam maksymalną długość jednej linii kodu w obiekcie na 4000 znaków),
- tu było bodaj najłatwiej, ponieważ wyzwalacze na poziomie serwera nie mają przypisanego schematu.
Słowo podsumowania
Skryptowanie pojedynczego obiektu proceduralnego to nie problem. SQL Server oferuje procedurę sp_helptext oraz funkcję OBJECT_DEFINITION. Ale jeżeli chcemy hurtowo skryptować obiekty, podany w powyżej kod może okazać się przydatny. Można na jego podstawie zbudować własne procedury do skryptowania i używać ich na codzień. Tu polecam wpis z mojego starego bloga: [PL][util] Piszemy narzędzia w T-SQL cz. 1 – sp_getcolumns.
Zachęcam do zabawy z metadanymi SQL Servera. Można się dzięki takiej zabawie sporo nauczyć. I oczywiście zachęcam do komentowania tego wpisu. Może masz jakieś uwagi, coś nie gra w podanym tu kodzie albo masz lepszy pomysł na realizację takich zadań.
| [EDYCJA] To już druga wersja tego wpisu. W pierwszej kod T-SQL nie bazował na kursorach, a definicja każdego obiektu była zwracana w postaci jednej wartości typu nvarchar(max), co powodowało problem w przypadku, gdy definicja obiektu była dłuższa niż 8192 znaki (a nawet mniej, gdyż Management Studio domyślnie wyświetla w kolumnie bodajże do 255 znaków). Druga wersja powstała dzięki czujności (po raz kolejny) Krzysia Stachyry. Krzysiu, wielki szacunek za cierpliwe testowanie moich wypocin! Tym razem powinno być ciut lepiej :-) [/EDYCJA] |
[PL] SQL Server – Ile czego w buforze danych?
Jul 19th
Posted by Pawel Potasinski in SQL Server
Czy pracując z SQL Server 2005/2008/2008 R2 można dowiedzieć się, która baza danych zajmuje najwięcej pamięci w buforze danych (buffer cache)? Spróbujmy użyć do tego celu widoku dynamicznego sys.dm_os_buffer_descriptors, który przechowuje jeden rekord dla każdej 8-kilobajtowej strony przechowywanej w buforze danych.
SELECT CASE WHEN database_id = 32767 THEN 'mssqlsystemresource' ELSE DB_NAME(database_id) END AS [Database], CONVERT(numeric(38,2),(8.0 / 1024) * COUNT(*)) AS [In buffer cache (MB)] FROM sys.dm_os_buffer_descriptors GROUP BY database_id ORDER BY 2 DESC;
Wynik:
Database In buffer cache (MB) ---------------------------- -------------------- AdventureWorks2008R2 19.59 mssqlsystemresource 13.13 master 3.30 msdb 2.97 ReportServer$SQL2008R2 2.81 ReportServer$SQL2008R2TempDB 2.12 AdventureWorks 1.37 tempdb 1.20 AdventureWorksDW2008R2 1.03 AdventureWorksDW 0.95 AdventureWorksLT 0.94 AdventureWorksLT2008R2 0.94 model 0.50 ...
Tu ciekawostka – pojawia nam się baza niewidoczna w widoku sys.databases – mssqlsystemresource (nazwałem ją tak od nazwy pliku danych), czyli baza systemowa, w której Microsoft ukrył przed użytkownikami (użyszkodnikami?) tabele systemowe.
Idąc dalej, czy można dla wybranej bazy sprawdzić, które obiekty zajmują w tym buforze najwięcej miejsca? Czemu nie? Wystarczy dołożyć kilka złączeń (żeby dowiedzieć się, jakie obiekty rezydują w buforze dołączamy widoki systemowe sys.allocation_units i – w chytry sposób – sys.partitions).
USE AdventureWorks2008R2; GO SELECT QUOTENAME(OBJECT_SCHEMA_NAME(p.object_id)) + '.' + QUOTENAME(OBJECT_NAME(p.object_id)) AS Object, CONVERT(numeric(38,2),(8.0 / 1024) * COUNT(*)) AS [In buffer cache (MB)] FROM sys.dm_os_buffer_descriptors AS d INNER JOIN sys.allocation_units AS u ON d.allocation_unit_id = u.allocation_unit_id INNER JOIN sys.partitions AS p ON (u.type IN (1,3) AND u.container_id = p.hobt_id) OR (u.type = 2 AND u.container_id = p.partition_id) WHERE d.database_id = DB_ID() GROUP BY QUOTENAME(OBJECT_SCHEMA_NAME(p.object_id)) + '.' + QUOTENAME(OBJECT_NAME(p.object_id)) ORDER BY 2 DESC;
Wynik:
Object In buffer cache (MB) ---------------------------------- -------------------- [sys].[sysobjvalues] 12.70 [Sales].[SalesOrderDetail] 9.68 [Sales].[SalesOrderHeader] 5.35 [sys].[sysrowsets] 4.25 [sys].[syscolpars] 3.55 [sys].[sysschobjs] 2.29 [sys].[sysrscols] 1.95 [sys].[sysallocunits] 1.32 [sys].[sysmultiobjrefs] 1.15 [sys].[sysftstops] 0.95 [sys].[sysiscols] 0.93 [sys].[syssingleobjrefs] 0.92 ...
A teraz zadanie domowe – zmodyfikuj powyższe zapytanie, by zwracało ilość megabajtów zajętą przez każdy indeks (sprawdź, które indeksy zajmują w buforze najwięcej miejsca). To nie może być trudne :-)

Nazywam się Paweł Potasiński i pracuję w polskim oddziale Microsoft w dziale Small and Midmarket Solutions & Partners (SMS&P) jako Partner Technology Advisor.




