具有多个基表和完整性能的 Microsoft SQL Server (MSSQL) 可更新视图
Posted
技术标签:
【中文标题】具有多个基表和完整性能的 Microsoft SQL Server (MSSQL) 可更新视图【英文标题】:Microsoft SQL Server (MSSQL) updatable view with multiple base tables and full performance 【发布时间】:2014-01-21 14:25:51 【问题描述】:我正在使用 MSSQL 2008 R2。它具有可更新视图的便利功能。例如,如果我有一个表 t 映射 id 到名称:
create table t (id int not null primary key, name varchar(100) not null unique)
然后是另一个表格,提供一些 id 和更多信息:
create table u (id int not null primary key references t, info varchar(100) not null)
为方便起见,一个视图可以让我看到 u 中添加了 name 列的行:
create view v as select u.*, t.name from u u join t t on u.id = t.id
那么我现在可以按名称而不是 id 进行更新:
update v set info = 'foo' where name = 'fred'
最方便。但是如果我想删除 'fred' 的行会怎样?
delete v where name = 'fred' -- Fails
我得到了错误
视图或函数“v”不可更新,因为修改会影响多个基表。
正如SQL Updatable View with joined tables(指 Oracle,但 MSSQL 的情况似乎相同)中所解释的,您可以对多个基表拥有可更新视图,只要只保留一个 键 表;粗略地说,这是表格中的任何行在视图中最多出现一次的地方。在上面的视图中,我们可以看到 t 和 u 都是保留键的表。但是我们可以通过调整视图定义来作弊:
create view v as
select u.*, (select t.name from t t where t.id = u.id) as name
from u u
这提供了与以前相同的行,但现在允许更新:
update v set info = 'foo' where name = 'fred'
从语义上讲,来自 t 的任何行在视图中最多出现一次仍然是正确的,但是因为我们没有以正常方式加入 t,所以我们没有遇到更新限制。此外,我们还可以从此视图中删除:
delete from v where name = 'fred'
这样做是正确的,从基础表 u 中删除而不是从 t 中删除。很明显,早期的观点表示为简单的连接,没有办法判断“删除”操作是应该从 u 还是从 t(或两者)删除行。
对于许多“选择”查询,使用重写视图的执行计划有点不同,所以我可能希望它在某些情况下执行得慢一些。遗憾的是优化器无法看到(在这种特殊情况下,存在唯一索引)两个视图具有相同的数据。
您还可以使用函数制作可更新的视图:
create function dbo.get_name(@id int) returns varchar(100) as begin
declare @r varchar(100)
select @r = name from t where id = @id
return @r
end
create view v as select *, dbo.get_name(id) as name from u
这可能会给出不同的(通常更复杂的)查询计划,因此它可能会更慢。
所以我们有两种可能的方法来制作可更新的视图,但它们并不完全令人满意。让更新和删除操作正常工作会很好,但要确保视图在选择查询上的执行不会比两个表的简单连接更差;也许您可以给查询引擎一些提示。有人可以提出一些建议吗?
【问题讨论】:
【参考方案1】:(select t.name from t t where t.id = u.id)
是一个非常 Macgyver 的把戏,你绕过了只能删除一个表的限制。
我可以建议的一个解决方案是使用而不是触发器,这将允许您个性化删除语句将对视图执行的操作。
自定义触发器可能不会影响视图的自动优化。
一些网站对此进行了更深入的介绍,并举了一些例子: http://blogs.msdn.com/b/anthonybloesch/archive/2009/02/16/insteadoftriggerspart1.aspx 和 http://www.mssqltips.com/sqlservertip/1804/using-instead-of-triggers-in-sql-server-for-dml-operations/
【讨论】:
是的,“代替”触发器听起来像是答案。理想的可能是以某种方式自动生成视图和触发器定义;从基于“Macgyver 技巧”的视图开始,创建具有相同行(但查询计划更快)的视图,并使用一些“替代”触发器来生成所需的插入和删除语义。以上是关于具有多个基表和完整性能的 Microsoft SQL Server (MSSQL) 可更新视图的主要内容,如果未能解决你的问题,请参考以下文章