Я работаю над индивидуальным решением обслуживания, используя sys.dm_db_index_physical_stats
представление. В настоящее время у меня есть ссылка из хранимой процедуры. Теперь, когда эта хранимая процедура выполняется в одной из моих баз данных, она делает то, что я хочу, и выводит список всех записей, относящихся к любой базе данных. Когда я помещаю его в другую базу данных, он записывает список всех записей, относящихся только к этой базе данных.
Например (код внизу):
- Выполнение запроса к базе данных 6 показывает [запрошенную] информацию для баз данных 1-10.
- Выполнение запроса к базе данных 3 показывает [запрошенную] информацию только для базы данных 3.
Причина, по которой я хочу эту процедуру специально для третьей базы данных, заключается в том, что я предпочел бы хранить все объекты обслуживания в одной базе данных. Я бы хотел, чтобы эта работа находилась в базе данных обслуживания и работала так, как если бы она была в этой базе данных приложения.
Код:
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
источник
Ответы:
Один из способов - создать системную процедуру,
master
а затем создать оболочку в базе данных обслуживания. Обратите внимание, что это будет работать только для одной базы данных одновременно.Сначала в мастере:
Теперь в вашей базе данных обслуживания создайте оболочку, которая использует динамический SQL для правильной установки контекста:
(Причина, по которой имя базы данных на самом деле не может быть,
NULL
заключается в том, что вы не можете присоединиться к таким вещам, какsys.objects
иsys.indexes
так как они существуют независимо в каждой базе данных. Поэтому, возможно, есть другая процедура, если вам нужна информация для всего экземпляра.)Теперь вы можете вызвать это для любой другой базы данных, например,
И вы всегда можете создать
synonym
в каждой базе данных, так что вам даже не нужно ссылаться на имя базы данных обслуживания:Другой способ - использовать динамический SQL, однако это также будет работать только для одной базы данных одновременно:
Еще один способ - создать представление (или табличную функцию) для объединения имен таблиц и индексов всех ваших баз данных, однако вам придется жестко закодировать имена баз данных в представление и поддерживать их по мере добавления / удалить базы данных, которые вы хотите разрешить включить в этот запрос. Это, в отличие от других, позволит вам получать статистику для нескольких баз данных одновременно.
Во-первых, мнение:
Тогда процедура:
источник
Ну, есть плохие новости, хорошие новости с подвохом и некоторые действительно хорошие новости.
Плохие новости
Объекты T-SQL выполняются в базе данных, в которой они находятся. Есть два (не очень полезных) исключения:
sp_
и которые существуют в[master]
базе данных (не очень хороший вариант: одна БД за раз, добавление чего-либо[master]
, возможно, добавление синонимов к каждой БД, что необходимо сделать для каждой новой БД)sp_
хранимым процессом в[master]
.Хорошие новости (с подвохом)
Многие (возможно, большинство?) Люди знают о встроенных функциях для получения действительно распространенных метаданных:
Использование этих функций может устранить необходимость в соединениях
sys.databases
(хотя эта на самом деле не является проблемой)sys.objects
(предпочтительнее, чемsys.tables
исключение индексированных представлений) иsys.schemas
(вы пропустили это, и не все находится вdbo
схеме ;-). Но даже с удалением трех из четырех JOINов мы все еще функционально не изменились, верно? Неправильный-о!Одной из приятных особенностей функций
OBJECT_NAME()
иOBJECT_SCHEMA_NAME()
является то, что у них есть необязательный второй параметр для@database_id
. Это означает, что, хотя СОЕДИНЕНИЕ к этим таблицам (кромеsys.databases
) зависит от базы данных, использование этих функций позволяет получить информацию для всего сервера. Даже OBJECT_ID () позволяет получить информацию для всего сервера, дав ему полное имя объекта.Включив эти функции метаданных в основной запрос, мы можем упростить и в то же время выйти за пределы текущей базы данных. Первый этап рефакторинга запроса дает нам:
А теперь для «уловки»: нет функции метаданных для получения имен индексов, не говоря уже о серверной. Так это все? Мы на 90% полны и все еще застряли, нуждаясь в определенных базах данных для получения
sys.indexes
данных? Действительно ли нам нужно создать хранимую процедуру, чтобы использовать динамический SQL для заполнения, каждый раз, когда запускается наш основной процесс, временной таблицы всехsys.indexes
записей во всех базах данных, чтобы мы могли присоединиться к ней? НЕТ!Действительно хорошие новости
Таким образом, появляется небольшая особенность, которую некоторые люди любят ненавидеть, но при правильном использовании могут делать удивительные вещи. Да: SQLCLR. Почему? Поскольку функции SQLCLR , очевидно , может подать заявления SQL, но самой природой представления из приложения кода, то есть динамический SQL. Таким образом, в отличие от функций T-SQL, функции SQLCLR могут вводить имя базы данных в запрос перед его выполнением. Это означает, что мы можем создать нашу собственную функцию, чтобы отразить способность
OBJECT_NAME()
иOBJECT_SCHEMA_NAME()
получатьdatabase_id
и получать информацию для этой базы данных.Следующий код является этой функцией. Но он берет имя базы данных вместо идентификатора, поэтому ему не нужно выполнять дополнительный шаг поиска (что делает его немного менее сложным и немного быстрее).
Если вы заметите, мы используем Context Connection, который не только быстрый, но и работает в
SAFE
сборках. Да, это работает в сборке, отмеченной какSAFE
поэтому он (или его разновидности) должен даже работать на базе данных SQL Azure V12(поддержка SQLCLR была довольно неожиданно удалена из базы данных SQL Azure в апреле 2016 года) .Таким образом, наш повторный рефакторинг основного запроса дает нам следующее:
Это оно! И эта Scalar UDF SQLCLR, и ваша хранимая процедура T-SQL обслуживания могут находиться в одной и той же централизованной
[maintenance]
базе данных. И вам не нужно обрабатывать одну базу данных одновременно; теперь у вас есть функции метаданных для всей зависимой информации, которая распространяется на весь сервер.PS В
.IsNull
коде C # нет проверки входных параметров, поскольку объект-обертка T-SQL должен быть создан сWITH RETURNS NULL ON NULL INPUT
опцией:Дополнительные замечания:
Описанный здесь метод также можно использовать для решения других, очень похожих проблем, связанных с отсутствием функций метаданных кросс-базы данных. Следующее предложение Microsoft Connect является примером одного такого случая. И, видя, что Microsoft закрыла его как «Не будет исправлять», становится ясно, что они не заинтересованы в предоставлении встроенных функций, подобных
OBJECT_NAME()
для удовлетворения этой потребности (отсюда и обходной путь, опубликованный в этом предложении :-).Добавьте функцию метаданных, чтобы получить имя объекта из hobt_id
Чтобы узнать больше об использовании SQLCLR, ознакомьтесь с серией статей Stairway to SQLCLR, которую я пишу на SQL Server Central (требуется бесплатная регистрация; извините, я не контролирую политики этого сайта).
IndexName()
Функция SQLCLR , показанная выше , доступны, предварительно скомпилированных, в удобном для установки скрипта на Pastebin. Сценарий включает функцию «Интеграция CLR», если она еще не включена, и сборка помечена какSAFE
. Он скомпилирован с .NET Framework версии 2.0, поэтому он будет работать в SQL Server 2005 и более поздних версиях (т.е. во всех версиях, поддерживающих SQLCLR).SQLCLR Функция метаданных для кросс-базы данных IndexName ()
Если кого-то интересует
IndexName()
функция SQLCLR и более 320 других функций и хранимых процедур, она доступна в библиотеке SQL # (автором которой я являюсь). Обратите внимание, что хотя есть бесплатная версия, функция Sys_IndexName доступна только в полной версии (вместе с аналогичной функцией Sys_AssemblyName ).источник