如何根据特定设置创建访问不同表的“物化事物”
Posted
技术标签:
【中文标题】如何根据特定设置创建访问不同表的“物化事物”【英文标题】:How to create a "materialized something" that accesses different tables, depending on a specific setting 【发布时间】:2012-09-23 15:44:39 【问题描述】:我想要一个程序访问表/视图/存储过程等(物化的东西,我们称之为 X),它抽象了三个基本表中包含的数据的真实位置(表在所有位置都有相同的定义)。
我希望 X 从某个地方(可能是一个表)获取服务器名称、目录名称和表名称,并访问特定的三个基本表。 X 的调用者不知道正在调用哪些特定的表。
如何在 SQL Server (2008) 中执行此操作?
【问题讨论】:
你为什么不试试存储过程? 你能解释一下在不同数据库中拥有一个视图的观点吗?这实际上意味着不同的事情,具体取决于上下文?并且对数据库 A 中视图的引用 总是 是指相同的特定表,还是除了位置之外还涉及其他上下文? 如果你只有一个视图,视图如何知道你要引用哪些数据?您不能参数化视图。我认为你需要退后一步,更好地解释你的业务需求,因为如果你已经决定这是你需要解决的方法,我相信你走错了路。 如果你的程序可以指示 SQL Server 更新某个表来说“嘿,这就是我现在想要这个视图的意思”为什么不能只说“嘿,这是数据我现在就追”?另外,如果两个不同的用户同时使用你的程序,你期望会发生什么?一个用户的要求会优先于其他用户吗? @John 仍然不知道您的程序的两个实例如何不会相互运行。正如我多次说过的那样,您想要的东西在视图中是不可能的。视图无法从某处获取元数据以确定运行位置。因此,请再次定义您的业务需求,而不是告诉我们您想要一个可以在运行时动态确定其源表的视图。 【参考方案1】:像函数一样,视图不能使用动态 SQL - 它不能在某处找到一些元数据引用并进行相应调整。
我认为与您想要的最接近的是synonym。假设您有三个不同的数据库,A
、B
和 C
。在A
中,您希望视图引用的表是dbo.foo
,在B
中是dbo.bar
,在C
中是dbo.splunge
。那么你可以在每个数据库中创建一个这样的同义词:
USE A;
GO
CREATE SYNONYM dbo.YourCommonViewName FOR dbo.foo;
GO
USE B;
GO
CREATE SYNONYM dbo.YourCommonViewName FOR dbo.bar;
GO
USE C;
GO
CREATE SYNONYM dbo.YourCommonViewName FOR dbo.splunge;
GO
现在这在技术上不是一个视图,但在每个数据库中你可以说...
SELECT <cols> FROM dbo.YourCommonViewName;
...它将返回数据库特定表中的数据。
在存储过程中执行此操作会简单得多。假设您将服务器、数据库和表名存储在某个表中,例如dbo.lookup
:
CREATE TABLE dbo.lookup
(
id INT PRIMARY KEY,
[server] SYSNAME,
[database] SYSNAME,
[table] SYSNAME,
active BIT NOT NULL DEFAULT (0)
);
-- you may want a constraint or trigger to ensure
-- only one row can be active at any one time.
INSERT dbo.lookup(id, [server], [database], [table])
SELECT 1,N'serverA',N'databaseA',N'tableA'
UNION ALL SELECT 2,N'serverB',N'databaseB',N'tableB';
现在你的程序可以说:
UPDATE dbo.lookup SET active = 1 WHERE ... ?
你的存储过程可以是:
CREATE PROCEDURE dbo.whatever
AS
BEGIN
SET NOCOUNT ON;
DECLARE @sql NVARCHAR(MAX);
SELECT @sql = N'SELECT <cols> FROM ' + QUOTENAME([server])
+ '.' + QUOTENAME([database]) + '.dbo.' + QUOTENAME([table])
FROM dbo.lookup WHERE active = 1;
EXEC sp_executesql @sql;
END
GO
我仍然不明白这一点,并且我不知道当两个不同的用户希望同时调用您的程序时您打算做什么,并且他们每个人都应该从不同的位置获得结果。
【讨论】:
真的吗?表值函数中没有动态 SQL?我不知道这个限制。 @a_horse_with_no_name 您可以使用CASE
和变量,所以我想您可以动态执行此操作,但您不能使用 EXEC() 或 EXEC sp_executesql。尽管如此,我还不清楚我们是否正确地解释了实际的业务需求——如果目标是用单一视图来做到这一点,那么他就不走运了。
谢谢。我不是因为最初的问题才问的,而是因为我不敢相信 SQL Server 有这样的限制(我更习惯于 Oracle 和 PostgreSQL)【参考方案2】:
同意 Aaron 关于视图和函数不能使用动态 sql 的事实。
您仍然可以做的是构建一个 clr 表值函数。在那里,您可以使用 .net 代码并查询您想要的任何内容。并相应地为您构建数据并输出您需要的内容。
所以不要像
那样查询数据select * from myview
可以查询
select * from dbo.clr_mymockupview()
【讨论】:
【参考方案3】:-
为您的远程服务器创建同义词。
创建您的 VIEW 以使用 UNION ALL 将您的位置连接在一起。
既然您说的是“表”,请在 UNION ALL 之前加入您的表,希望 MS 将远程执行 JOIN。
【讨论】:
与所有位置进行连接不是一种选择。开销太大。我只想获取某个位置的值。 从未有过连接多个 MS SQL Server 的乐趣,但 Oracle 足够聪明,可以远程运行查询,如果有帮助的话。这就是为什么我说“希望”。【参考方案4】:对数据库、服务器和目录使用带参数的联合查询:
Select col1, col2, <etc.>, 'table1' as tablename, 'server1' as servername, 'catalog1' as catname from server1.catalog1.table1
Union Select col1, col2, <etc.>, 'table2' as tablename, 'server2' as servername, 'catalog2' as catname from server2.catalog2.table2
Union Select col1, col2, <etc.>, 'table3' as tablename, 'server3' as servername, 'catalog3' as catname from server3.catalog3.table3
然后根据您的 3 个条件进行过滤。这可能不会很快,但会与 std. SQL。
【讨论】:
与所有位置进行连接不是一种选择。开销太大。我只想获取某个位置的值。以上是关于如何根据特定设置创建访问不同表的“物化事物”的主要内容,如果未能解决你的问题,请参考以下文章
如何让每个用户根据他们在 JAAS 中的权限/角色访问特定位置的资源?