在我的公司,我们有几个带有MS SQL数据库服务器的环境(SQL 2008 R2,SQL 2014)。为简单起见,让我们考虑一个TEST环境和一个PROD环境以及每个环境中的两个sql server。让服务器名为 srTest1 , srTest2 , srProd1 , srProd2 ,每个服务器都运行默认的MS SQL Server实例。我们使用多个数据库,比如DataDb,ReportDb,DWHDb。
我们希望在TEST和PROD的T-SQL中保留相同的源代码,但问题是每个环境中上述数据库的体系结构或分布:
TEST:
PROD:
现在,比如说,在ReportDb中,我们编写存储过程,其中有许多SELECT引用DataDb和DWHDb中的表和其他对象。为了使源代码尽可能通用,我们决定为每个环境中的每个数据库服务器上的每个数据库创建链接服务器,并根据它们为其创建的数据库命名。因此,这些链接的服务器就是:
我们将相应地调整存储过程中的源。例如:
而不是
SELECT * FROM DataDb.dbo.Contact
我们会写
SELECT * FROM lnkDataDb.DataDb.dbo.Contact
上面的示例对于执行查询的数据库(ReportDb)位于与引用表(DataDb)不同的服务器上的情况是合理的。 TEST环境的情况如何。但在PROD中却不是这样。这是我在这里关注的表现。 SQL Server会将SELECT视为"远程查询"事实上,无论它是否是对本地对象的引用。
现在,它是最重要的部分:
如果您查看这3个查询的实际执行计划,您会看到一件有趣的事情:
(1) SELECT * FROM DataDb.dbo.Contact
(2) SELECT * FROM srProd1.DataDb.dbo.Contact
(3) SELECT * FROM lnkDataDb.DataDb.dbo.Contact
前两个(查询#1和#2)具有相同的执行计划(尽可能最快),即使您使用引用#2表联系人的四部分名称方式也是如此。 最后一个查询有一个不同的计划(远程查询,因此更慢)。
问题是:
你能以某种方式创建一个自己的链接服务器(相同的sql server实例,实际上是默认实例)作为"别名"到主机的名称( srProd1 ),以便强制SQL服务器将其理解为本地而不是问题"远程执行"计划?的
非常感谢任何提示
的Pavel
答案 0 :(得分:1)
最近我发现了一种解决方法,它似乎比使用自指向链接服务器的解决方案更有效,更优雅地解决了这类问题。
如果你在多个SQL服务器上使用多个数据库工作(例如报告),并且服务器上数据库的物理分布是一个挑战,因为它可能因环境而异(例如TEST与PROD),I建议:
尽可能使用三部分数据库对象名称。如果对象是本地的,那么执行计划也是本地的,因此是有效的。
示例:
SELECT * FROM DataDb.dbo.Contact
如果您碰巧在不同的SQL服务器实例中运行上述查询(例如,驻留在不同的物理机上,但这不一定,即使在同一台机器上也可以安装其他SQL服务器实例),如果您要使用由四部分组成的名称:
SELECT * FROM lnkDataDb.DataDb.dbo.Contact
然后你可以使用以下技巧来解决这个问题:
我们假设 lnkDataDb 指向 srTest2 ,并且您正在从 srTest1 执行查询。现在,您将在本地服务器上创建一个“虚假”数据库 DataDb ( srTest1 )。这个虚假的DataDb不包含真正的数据库对象(没有表,没有视图,没有存储过程,没有UDF等)。只应在其中定义同义词。 (并且在它中也应该有与srTest2上的真实DataDb中相同的模式)。这些同义词的名称应与 srTest2 中DataDb中的实际db-object对应项完全相同。例如:
-- To be executed on srTest1.
EXEC sp_addlinkedserver
@server = N'lnkDataDb',
@srvproduct = N'',
@provider = N'SQLNCLI',
@datasrc = N'srTest2'
;
GO
CREATE DATABASE [DataDb];
GO
USE [DataDb];
GO
CREATE SYNONYM dbo.Contact FOR lnkDataDb.DataDb.dbo.Contact;
GO
现在,如果您想从 srTest2 中驻留在数据库 DataDb 中的表 dbo.Contact 中选择行,并且您正在执行来自 srTest1 的查询,您将使用一个简单的由三部分组成的表名:
SELECT * FROM DataDb.dbo.Contact
当然,在 srTest1 上,这不是一个表,它只是引用 srTest2 上同名表的同义词。但是,这就是诀窍,您使用相同的查询语法,就像在真实数据库对象所在的 srTest2 上执行它一样。
这种方法有缺点:
优点胜过上述缺点:
总结
如果引用的对象是本地的,则查询将作为本地执行,如果引用的对象是远程的,则查询将作为远程执行,但 T-SQL脚本始终是相同的 。您不必更改其中的字母。