通过T-SQL以编程方式引用数据库


11

我正在编写一个存储过程,该过程使用数据库名称作为参数并返回该数据库的索引及其碎片级别的表。该存储过程将存在于我们的DBA数据库(该DB包含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数据库中调用它,则所有结果都将为null,因为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 2012年

Answers:


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。当然可以。我有一个单独的查询来生成碎片整理代码。我将进行运行及其外观。但是,接受这个作为答案。
mdoyle 2012年

需要注意的一件事...它可以在SQL Server 2008R2企业版上运行。标准版不支持“使用(online = on)进行重建”。
datagod 2012年

1
亲爱的,我现在有足够的代表来投票赞成你的答案。:-)这是一个巧妙的查询,但是我喜欢将代码生成查询分开。我喜欢将结果显示为文本,并且能够将其粘贴到工作中。都是非常优雅的解决方案-谢谢!
mdoyle 2012年

8

动态SQL的替代方法是SQLCMD,它可以从命令行,代理作业步骤,Invoke-Sqlcmd Powershell cmdlet 调用或在SSMS中启用。您的SQLCMD语法示例为:

:SETVAR DatabaseName MyDatabase

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

SQLCMD模式是我希望早些了解的那些功能之一。在许多情况下都很方便。


1
使用PowerShell完成此操作也非常简单和优雅。我喜欢你的方法,马克。+1
Thomas Stringer '04年

@Shark好一点,我会在编辑的PowerShell命令。
马克·斯托里·史密斯

这也是一个很好的解决方案,我也会对此进行研究。
mdoyle

很好,有充分的理由为什么TSQL不支持此功能吗?
tbone

0

通常很难从另一个DB中包含的过程中引用一组表。如果将过程作为系统过程安装在Master中,则可以在其他DB上下文中使用它,而无需尝试回头参考。

我认为:如果您的过程以'sp_'开头,那么它将变得普遍可见,并且,如果您在'sys.sp_%'模式中对其进行定义,则可以在其他数据库上下文中使用它。

这将提供在多个DB中进行操作的另一种方式,而不必动态插入DB_name。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.