Database di riferimento a livello di codice tramite T-SQL


11

Sto scrivendo una procedura memorizzata che accetta un nome di database come argomento e restituisce una tabella degli indici di quel database e il loro livello di frammentazione. Questa procedura memorizzata vivrà nel nostro database DBA (il DB che contiene le tabelle utilizzate dai DBA per monitorare e ottimizzare le cose). I sistemi in questione sono tutti SQL Server 2008 R2 se ciò fa la differenza.

Ho elaborato la query di base, ma sono bloccato nel tentativo di fornire i nomi effettivi degli indici. Per quanto ne so, tali informazioni sono contenute nella vista sys.indexes di ogni individuo. Il mio problema specifico è provare a fare riferimento a tale vista a livello di codice dalla stored procedure di un altro database.

Per illustrare, questa è la parte della query in questione:

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 query funziona correttamente quando viene eseguita dal database identificato da @db_id, poiché utilizza la vista sys.indexes corretta. Se provo a chiamarlo dal database DBA, tuttavia, tutto risulta nullo, poiché la vista sys.indexes è per il database errato.

In termini più generali, devo essere in grado di fare qualcosa del genere:

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

o

USE @db_name;

Ho provato a cambiare database o fare riferimento ad altri database usando combinazioni di concatenazioni di stringhe e OBJECT_NAME / OBJECT_ID / DB_ID e nulla sembra funzionare. Apprezzerei tutte le idee che la comunità potrebbe avere, ma sospetto che dovrò riorganizzare questa procedura memorizzata per risiedere in ogni singolo database.

Grazie in anticipo per eventuali suggerimenti.


1
Dovrei usare SQL dinamico per questo sospetto ...
JNK,

Risposte:


10

SQL dinamico è utile per questi tipi di attività amministrative. Ecco uno snippet da una stored procedure che ho scritto che non solo ottiene i livelli di deframmentazione, ma genera anche il codice per eseguire la deframmentazione:

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)

Sulla base del commento di JNK ho modificato la mia query per utilizzare SQL dinamico. Funziona, ovviamente. Ho una query separata per generare il codice di deframmentazione. Ci darò una prova e come appare. Accettando questa come risposta, comunque.
mdoyle,

Una cosa da notare ... funziona su SQL Server 2008R2 Enterprise Edition. "Ricostruisci con (online = attivo)" non è supportato nell'edizione standard.
datagod

1
Dolce, ho abbastanza rappresentante per votare la tua risposta adesso. :-) Questa è una query semplice, ma mi piace avere la query di generazione del codice separata. Mi piace eseguire i risultati in testo e riuscire a tagliarli e incollarli in un lavoro. Soluzione molto elegante lo stesso - grazie!
mdoyle,

8

L'alternativa all'SQL dinamico è SQLCMD , che può essere richiamato dalla riga di comando, una fase del lavoro dell'agente, il cmdlet Invoke- Sqlcmd Powershell o abilitato in SSMS . Il tuo esempio nella sintassi SQLCMD sarebbe:

:SETVAR DatabaseName MyDatabase

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

La modalità SQLCMD è una di quelle funzionalità che vorrei conoscere prima. Comodo in molte situazioni.


1
Questo è anche abbastanza semplice ed elegante da realizzare con PowerShell. Mi piace il tuo approccio, Mark. +1
Thomas Stringer,

@Shark Bene, modificherò il cmdlet di PowerShell.
Mark Storey-Smith,

Anche una buona soluzione, esaminerò anche questo.
mdoyle,

Questo è carino, c'è una buona ragione per cui questo non è supportato in TSQL?
tbone

0

Di solito è difficile fare riferimento a un set di tabelle da una procedura contenuta in un diverso DB. Se installi la tua procedura in Master, come procedura di sistema, può essere utilizzata in altri contesti DB senza provare a fare riferimento a se stessa.

Penso che: se la tua procedura inizia con "sp_", diventa universalmente visibile e se la definisci nello schema "sys.sp_%", può essere utilizzata in altri contesti DB.

Ciò fornirebbe un modo alternativo di operare su più DB senza dover collegare dinamicamente DB_name.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.