5

我正在使用 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 可更新视图(指 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

这可能会给出不同的(通常更复杂的)查询计划,因此它可能会更慢。

所以我们有两种可能的方法来制作可更新的视图,但它们并不完全令人满意。让更新和删除操作正常工作会很好,但要确保视图在选择查询上的执行不会比两个表的简单连接更差;也许您可以给查询引擎一些提示。有人可以提出一些建议吗?

4

1 回答 1

2

(select t.name from t t where t.id = u.id)是一个非常 Macgyver 的技巧,您可以绕过只有一个表用于可删除表的限制。

我可以建议的一种解决方案是使用而不是 triggers,这将允许您个性化 delete 语句将对视图执行的操作。

自定义触发器可能不会影响视图的自动优化。

一些网站通过一些示例对其进行了更深入的了解:http: //blogs.msdn.com/b/anthonybloesch/archive/2009/02/16/insteadoftriggerspart1.aspxhttp://www.mssqltips.com/sqlservertip /1804/使用-instead-of-triggers-in-sql-server-for-dml-operations/

于 2014-04-06T06:50:18.593 回答