Посилання на базу даних програмно через T-SQL


11

Я пишу збережену процедуру, яка приймає ім'я бази даних як аргумент і повертає таблицю індексів цієї бази даних та рівня їх фрагментації. Ця збережена процедура буде жити в нашій базі даних DBA (БД, що містить таблиці, які 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, і, здається, нічого не працює. Буду вдячний за будь-які ідеї спільноти, але підозрюю, що мені доведеться перезавантажити цю збережену процедуру, щоб розміститись у кожній окремій базі даних.

Заздалегідь дякую за будь-які пропозиції.


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)

На підставі коментаря JNK я змінив свій запит, щоб використовувати динамічний SQL. Це працює, звичайно. У мене є окремий запит для генерації коду defrag. Я дам це пробіг і як це виглядає. Однак, приймаючи це як відповідь.
mdoyle

Варто зазначити, що це працює на SQL Server 2008R2 Enterprise Edition. "Перебудова з (онлайн = увімкнено)" не підтримується в стандартній версії.
datagod

1
Мила, у мене достатньо представника, щоб зараз проголосувати вашу відповідь. :-) Це плавний запит, але мені подобається, що запит, що генерує код, розділений. Мені подобається запускати результати в текст і бути в змозі вирізати-вставити в роботу. Дуже елегантне рішення все одно - дякую!
mdoyle

8

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

:SETVAR DatabaseName MyDatabase

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

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


1
Це також досить просто і елегантно здійснити за допомогою PowerShell. Мені подобається твій підхід, Марку. +1
Томас Стрінгер

@Shark Добре, я відредагую командлет повноважень.
Марк Сторі-Сміт

Також гарне рішення, я також розберуся в цьому.
mdoyle

Це приємно, чи є вагомі причини, чому це не підтримується в TSQL?
tbone

0

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

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

Це забезпечило б альтернативний спосіб роботи в декількох БД, не потребуючи динамічного підключення імені DB_.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.