Ссылка на базу данных программно через T-SQL

11

Я пишу хранимую процедуру, которая принимает имя базы данных в качестве аргумента и возвращает таблицу индексов этой базы данных и их уровень фрагментации. Эта хранимая процедура будет храниться в нашей базе данных DBA (база данных, содержащая таблицы, которые используются администраторами баз данных для мониторинга и оптимизации). Все рассматриваемые системы являются SQL Server 2008 R2, если это имеет значение.

У меня есть базовый запрос, но я застрял при попытке предоставить фактические имена индексов. Насколько мне известно, эта информация содержится в представлении sys.indexes каждого пользователя. Моя конкретная проблема - попытка ссылаться на это представление программно из хранимой процедуры другой базы данных.

Чтобы проиллюстрировать это, это часть запроса:

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

Запрос работает нормально при выполнении из базы данных, идентифицируемой @db_id, потому что он использует правильное представление sys.indexes. Однако, если я попытаюсь вызвать это из базы данных DBA, все получится нулевым, поскольку представление sys.indexes предназначено для неверной базы данных.

В более общем плане, я должен быть в состоянии сделать что-то вроде этого:

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

или же

USE @db_name;

Я пытался переключать базы данных или ссылаться на другие базы данных, используя комбинации конкатенации строк и функций OBJECT_NAME / OBJECT_ID / DB_ID, но ничего не работает. Я был бы признателен за любые идеи, которые могут возникнуть у сообщества, но подозреваю, что мне придется переоснащать эту хранимую процедуру, чтобы размещать ее в каждой отдельной базе данных.

Спасибо заранее за любые предложения.

mdoyle
источник
1
Вам нужно использовать динамический SQL для этого, я подозреваю ...
JNK

Ответы:

10

Динамический SQL пригодится для этих типов административных задач. Вот фрагмент хранимой процедуры, которую я написал, которая не только получает уровни дефрагментации, но также генерирует код для выполнения дефрагментации:

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
источник
Основываясь на комментарии JNK, я изменил свой запрос для использования динамического SQL. Это работает, конечно. У меня есть отдельный запрос для генерации кода дефрагментации. Я попробую и как это выглядит. Однако, принимая это как ответ.
mdoyle
Стоит отметить, что это работает на SQL Server 2008R2 Enterprise Edition. «Пересборка с (online = on)» не поддерживается в стандартной версии.
Датагод
1
Милый, у меня достаточно представителя, чтобы проголосовать за твой ответ. :-) Это сложный запрос, но мне нравится разделять запрос на генерацию кода. Мне нравится запускать результаты в текст и возможность вырезать и вставить в работу. Все-таки очень элегантное решение - спасибо!
Mdoyle
8

Альтернативой динамическому SQL является SQLCMD , который можно вызвать из командной строки, шага задания агента, командлета Invoke-Sqlcmd Powershell или включить в SSMS . Ваш пример в синтаксисе SQLCMD будет:

:SETVAR DatabaseName MyDatabase

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

Режим SQLCMD - одна из тех функций, о которых я хотел бы знать раньше. Удобно во многих ситуациях.

Марк Стори-Смит
источник
1
Это также довольно просто и элегантно сделать с помощью PowerShell. Мне нравится твой подход, Марк. +1
Томас Стрингер
@ Shark Хорошо, я отредактирую командлет powershell.
Марк Стори-Смит,
Также хорошее решение, я тоже посмотрю на это.
mdoyle
Это хорошо, есть ли хорошая причина, почему это не поддерживается в TSQL?
августа
0

Обычно сложно сослаться на один набор таблиц из процедуры, содержащейся в другой БД. Если вы устанавливаете свою процедуру в Master как системную процедуру, то ее можно использовать в других контекстах БД, не пытаясь сослаться на себя.

Я думаю, что: если ваша процедура начинается с 'sp_', то она становится универсально видимой, и если вы определяете ее в схеме 'sys.sp_%', то она может использоваться в других контекстах БД.

Это обеспечило бы альтернативный способ работы в нескольких БД без необходимости динамического подключения имени_БД.

silvertc
источник