Referenciando banco de dados programaticamente via T-SQL

11

Estou escrevendo um procedimento armazenado que usa um nome de banco de dados como argumento e retorna uma tabela dos índices desse banco de dados e seu nível de fragmentação. Este procedimento armazenado permanecerá em nosso banco de dados DBA (o banco de dados que contém tabelas que os DBAs usam para monitorar e otimizar as coisas). Os sistemas em questão são todos do SQL Server 2008 R2, se isso faz diferença.

Eu tenho a consulta básica elaborada, mas estou tentando fornecer os nomes reais dos índices. Que eu saiba, essas informações estão contidas na exibição de sys.indexes de cada indivíduo. Meu problema específico é tentar referenciar essa exibição programaticamente do procedimento armazenado de outro banco de dados.

Para ilustrar, esta é a parte da consulta em questão:

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

A consulta funciona bem quando executada no banco de dados identificado por @db_id, porque está usando a exibição sys.indexes adequada. Se eu tentar chamar isso do banco de dados DBA, no entanto, tudo será nulo, pois a exibição sys.indexes é para o banco de dados errado.

Em termos mais gerais, eu preciso ser capaz de fazer algo assim:

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

ou

USE @db_name;

Tentei alternar bancos de dados ou fazer referência a outros bancos de dados usando combinações de concatenação de strings e funções OBJECT_NAME / OBJECT_ID / DB_ID e nada parece funcionar. Aprecio todas as idéias que a comunidade possa ter, mas suspeito que precisarei refazer esse procedimento armazenado para residir em cada banco de dados individual.

Agradecemos antecipadamente por quaisquer sugestões.

mdoyle
fonte
1
Você vai precisar usar SQL dinâmico para este suspeito ...
JNK

Respostas:

10

O SQL dinâmico é útil para esses tipos de tarefas administrativas. Aqui está um trecho de um procedimento armazenado que escrevi que não apenas obtém os níveis de desfragmentação, mas também gera o código para fazer a desfragmentação:

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
fonte
Com base no comentário de JNK, alterei minha consulta para empregar SQL dinâmico. Funciona, é claro. Eu tenho uma consulta separada para gerar o código de desfragmentação. Vou dar uma olhada e como fica. Aceitando isso como a resposta, no entanto.
mdoyle
Uma coisa a observar ... isso é executado no SQL Server 2008R2 Enterprise Edition. O "reconstruir com (online = ativado)" não é suportado na edição padrão.
datagod
1
Doce, tenho representante suficiente para votar sua resposta agora. :-) Essa é uma consulta simples, mas eu gosto de separar a consulta geradora de código. Gosto de executar os resultados em texto e poder recortar e colar em um trabalho. Solução muito elegante da mesma forma - obrigado!
mdoyle
8

A alternativa ao SQL dinâmico é o SQLCMD , que pode ser chamado na linha de comando, em uma etapa da tarefa do agente, no cmdlet Invoke-Sqlcmd Powershell ou ativado no SSMS . Seu exemplo na sintaxe do SQLCMD seria:

:SETVAR DatabaseName MyDatabase

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

O modo SQLCMD é um desses recursos que eu gostaria de conhecer antes. Útil em muitas situações.

Mark Storey-Smith
fonte
1
Isso também é bastante simples e elegante de realizar com o PowerShell. Eu gosto da sua abordagem, Mark. 1
Thomas Stringer
@Shark Bom ponto, eu vou editar o cmdlet PowerShell no.
Mark Storey-Smith
Também é uma boa solução, vou analisar isso também.
precisa saber é
Isso é legal, existe um bom motivo para isso não ser suportado no TSQL?
tbone
0

Geralmente é difícil fazer referência a um conjunto de tabelas de um procedimento contido em um banco de dados diferente. Se você instalar o procedimento no Master, como um procedimento do sistema, ele poderá ser usado em outros contextos do banco de dados sem tentar se referir a si próprio.

Penso que: se o seu procedimento começar com 'sp_', ele se tornará universalmente visível e, se você o definir no esquema 'sys.sp_%', poderá ser usado em outros contextos de banco de dados.

Isso forneceria uma maneira alternativa de operar em vários bancos de dados sem a necessidade de conectar o DB_name dinamicamente.

silvertc
fonte