查询单独数据库中多个相同表的最有效方法

时间:2009-11-04 19:06:23

标签: sql database sql-server-2005

我有一个服务器(SQL Server 2005),它有多个存档数据库(每季度1个,可追溯到8年),它们在结构上完全相同。

我经常需要查询跨越n个数据库的特定日期范围,通常n小1-3,但我可能需要查询整个集合。

从代码清洁度和性能角度来看,最有效的方法是什么?

当前的解决方案非常特殊,有一组视图可以跨越所有或只是最新的数据库,其他解决方案是生成动态SQL,确定哪些DB包含所需的数据。

显然,理想的解决方案是对表进行分区,但我不能这样做,因为它是第三方提供的数据库

戴夫

编辑:我无法组合数据库,因为它们是第三方控制的,总数据大小约为50GB所以不是很大,最大的表每季度包含大约1.5m行

EDIT2:数据仓库绝对是长期的正确解决方案(它在计划中),但我今天不能这样做:(

5 个答案:

答案 0 :(得分:6)

一种方法:使用sp_msForEachDb。

- 第1轮-------

使用varchar参数调用此系统过程。 (它实际上比这更麻烦,如果你想知道它在做什么,请检查master数据库中的代码。) 参数必须是动态代码的一部分 - 例如,

DECLARE @DemoParameter varchar(1000)
SET @DemoParameter = 'SELECT MyCol from MyTable where CreatedOn between ''Jan 1, 1980'' and ''Dec 21, 2012'''
EXECUTE sp_msForEachDb @DemoParameter

这将针对SQL实例上的每个数据库运行查询,每个数据库返回一个集合 - 除了那些没有必要表的数据库,这会引发错误(特别是系统数据库) 。这导致我们......

- 第2轮---------

在动态代码中,因为数据库是在问号的所有实例上迭代的吗?将替换为当前正在处理的数据库的名称。您可以使用它来过滤要处理的数据库,哪些不是。另请注意,“当前”数据库将由例程更改,您必须自己执行此操作。这给了我们代码:

SET @DemoParameter = '

IF ''?'' like  ''%Foo%''
 BEGIN
    USE ?
    SELECT MyCol from MyTable where CreatedOn between ''Jan 1, 1980'' and ''Dec 21, 2012''

'

这将仅针对名称中包含字符“foo”的数据库运行查询。可能你可以检查每个数据库中是否存在表;其他方法表明了自己。

这将为每个数据库霰弹一个数据集,如果你需要一个整齐有序的数据集,这对数据集没有多大帮助,这让我们... ...

- 第3轮------------

简要地说:创建临时表,并在动态查询中填充它。正如我在下面所示,您可以包含数据库的名称,以及服务器名称 - 当您在几个数据库中查找丢失的数据时非常有用。

创建(或清除)临时表:

IF object_id('tempdb.dbo.##Foo') is null
    CREATE TABLE ##Foo
     (
       ServerName         varchar(100)  not null
      ,DBName             varchar(100)  not null

      --  Add your own columns here
      ,MyCol              int  not null
     )

ELSE
    --Option: Delete this line to not clear on each run
    TRUNCATE TABLE ##Foo

运行代码(这是我的主模板,你可以轻松地在那里工作@DemoParameter):

EXECUTE sp_msForEachDB '
IF ''?'' like  ''%Foo%''
 BEGIN
    USE ?

    INSERT ##Foo
     select @@servername, db_name()
       ,MyCol
      from MyTable
 END
'

...这应该会产生一个包含数据的临时表。测试一下,我写了这个没有实际测试代码,并且typso将 silp in。(#temp表应该和## temp一样工作,我通常用ad-hoc系统支持问题来做这件事)

答案 1 :(得分:3)

这是将要做的事情!

声明
  @Database varchar(8000),
  @Sql varchar(8000)
开始 声明DBName游标LOCAL FAST_FORWARD 对于选择名称      FROM sys.databases      其中名称如'Your_DB_Names%'

打开DBName WHILE(1 = 1) 开始  从DBName获取Next到@Database

如果@@ Fetch_status = -1中断
 如果@@ Fetch_status = -2继续

设置@Sql ='使用'+ @数据库  打印@Sql  执行(@Sql)

SELECT * FROM TABLE - 您的查询 结束 关闭DBName
取消分配DBName END

答案 2 :(得分:2)

我经常这样做,让我告诉你,保持单独的数据库是一个痛苦的人。它迫使你在各地做各种各样的逻辑 - 它首先打破了数据库的封装。

您正在查看的是数据仓库。您应该考虑将所有数据库合并为一个,并将其设置为只读。然后,您可以对实时数据进行每夜/每小时增量备份,并针对您的仓库进行恢复。然后,您的仓库始终是最新的,并且您针对该仓库而不是实时数据运行报告。

这样做的结果是保持您的报告不会杀死您的实时生产数据库,我猜想超过90%的业务需求不需要100%准确的即时数据。

做一次硬件 - 创建一个仓库。 :-)

修改

我过去做过的事情是创建一个我使用的表的视图,并使用链接数据库(如果dbs在其他机器上)

Create view view_tale as 
    select * from activedb.dbo.table
    union
    select * from db1.dbo.table 
    union 
    select * from db2.dbo.table 

隐藏,性能方面,但整齐地解决了这个问题。然后,您仍然只有一次性设置问题(为每个表创建一个您希望查询的视图),以及一个集中的位置进行修改以使您的数据库列表保持最新以进行持续维护,而不是保持N个报告数量到目前为止。

答案 3 :(得分:1)

Danielle的回答对我有用,下面略有改动。我们的服务器上有几十个开发数据库供我们所有客户使用,这些数据库以常规前缀和后缀命名。使用游标可以查看特定组的所有数据库中的所有记录。但是,我必须进行更改,因为“execute”不适用于“use”命令,因此我只使用数据库名称创建了整个命令。

Declare
@Database varchar(8000),
@Sql varchar(8000)
BEGIN Declare DBName Cursor LOCAL FAST_FORWARD For Select name FROM sys.databases where name like 'MyPrefix%MySuffix'

Open DBName WHILE (1=1) Begin Fetch Next From DBName into @Database

if @@Fetch_status = -1 Break
if @@Fetch_status = -2 Continue

set @Sql = 'select * from '+@Database+'MyTable'
print @sql
execute (@sql)
End
Close DBName
Deallocate DBName
END

答案 4 :(得分:0)

根据数据库的大小,将它们合并到一个数据库并正确索引它们实际上可能更好。

您可以编写自己的SSIS包并安排它定期合并数据(每日/每小时/等)。