Hacer referencia a la base de datos mediante programación a través de T-SQL

11

Estoy escribiendo un procedimiento almacenado que toma el nombre de una base de datos como argumento y devuelve una tabla de los índices de esa base de datos y su nivel de fragmentación. Este procedimiento almacenado vivirá en nuestra base de datos DBA (el DB que contiene tablas que los DBA usan para monitorear y optimizar cosas). Los sistemas en cuestión son todos SQL Server 2008 R2 si eso marca la diferencia.

Tengo la consulta básica resuelta, pero estoy atascado en tratar de proporcionar los nombres reales de los índices. Que yo sepa, esa información está contenida en la vista sys.indexes de cada individuo. Mi problema específico es tratar de hacer referencia a esa vista mediante programación desde el procedimiento almacenado de otra base de datos.

Para ilustrar, esta es la parte de la consulta en cuestión:

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

La consulta funciona bien cuando se ejecuta desde la base de datos identificada por @db_id, porque está utilizando la vista sys.indexes adecuada. Sin embargo, si trato de llamar a esto desde la base de datos DBA, todo se vuelve nulo, ya que la vista sys.indexes es para la base de datos incorrecta.

En términos más generales, necesito poder hacer algo como esto:

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

o

USE @db_name;

He intentado cambiar bases de datos o hacer referencia a otras bases de datos utilizando combinaciones de concatenación de cadenas y funciones OBJECT_NAME / OBJECT_ID / DB_ID y nada parece funcionar. Apreciaría cualquier idea que la comunidad pueda tener, pero sospecho que tendré que actualizar este procedimiento almacenado para residir en cada base de datos individual.

Gracias de antemano por cualquier sugerencia.

mdoyle
fuente
1
Sospecho que necesitará usar SQL dinámico para esto ...
JNK

Respuestas:

10

El SQL dinámico es útil para este tipo de tareas administrativas. Aquí hay un fragmento de un procedimiento almacenado que escribí que no solo obtiene los niveles de desfragmentación, sino que también genera el código para realizar la desfragmentación:

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
fuente
Basado en el comentario de JNK, modifiqué mi consulta para emplear SQL dinámico. Funciona, por supuesto. Tengo una consulta separada para generar el código de desfragmentación. Voy a intentar esto y cómo se ve. Aceptando esto como la respuesta, sin embargo.
mdoyle
Una cosa a tener en cuenta ... esto se ejecuta en SQL Server 2008R2 Enterprise Edition. La "reconstrucción con (en línea = encendido)" no es compatible con la edición estándar.
datagod
1
Dulce, tengo suficiente representante para votar tu respuesta ahora. :-) Esta es una consulta ingeniosa, pero me gusta tener la consulta generadora de código por separado. Me gusta ejecutar los resultados en texto y poder cortar y pegar en un trabajo. Una solución muy elegante de todos modos, ¡gracias!
mdoyle
8

La alternativa al SQL dinámico es SQLCMD , que se puede invocar desde la línea de comandos, un paso de trabajo del agente, el cmdlet Invoke-Sqlcmd Powershell o habilitado en SSMS . Su ejemplo en la sintaxis SQLCMD sería:

:SETVAR DatabaseName MyDatabase

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

El modo SQLCMD es una de esas características que desearía haber conocido antes. Práctico en muchas situaciones.

Mark Storey-Smith
fuente
1
Esto también es bastante simple y elegante de lograr con PowerShell. Me gusta tu enfoque, Mark. +1
Thomas Stringer el
@Shark Buen punto, editaré el cmdlet de PowerShell.
Mark Storey-Smith
También es una buena solución, lo investigaré también.
mdoyle
Esto es bueno, ¿hay una buena razón por la cual esto no es compatible con TSQL?
tbone
0

Por lo general, es difícil hacer referencia a un conjunto de tablas de un procedimiento contenido en una base de datos diferente. Si instala su procedimiento en Master, como un procedimiento del sistema, puede usarse en otros contextos de base de datos sin intentar referirse a sí mismo.

Creo que: si su procedimiento comienza con 'sp_', entonces se vuelve universalmente visible, y si lo define en el esquema 'sys.sp_%', puede usarse en otros contextos de base de datos.

Esto proporcionaría una forma alternativa de operar en múltiples bases de datos sin tener que conectar dinámicamente el DB_name.

silvertc
fuente