假设我有一张桌子
T
,并且上面有一个索引视图V
:
CREATE TABLE dbo.T (id int PRIMARY KEY, b bit NOT NULL, txt varchar(20));
GO
CREATE VIEW dbo.V
WITH SCHEMABINDING AS
SELECT T.Id, T.txt
FROM dbo.T AS T
WHERE T.b = 1;
GO
CREATE UNIQUE CLUSTERED INDEX idx_V ON dbo.V (Id);
在这个简单的示例中,它基本上只是一个过滤索引,但它也可以具有连接等。
我现在想在
T
中选择一些行,其中b = 1
,这里的过滤视图非常有用,而且我使用标准,所以必须使用NOEXPAND
(或者它对于视图匹配来说太复杂):
SELECT Id, txt
FROM V WITH (NOEXPAND);
现在我想将这些行更新为某个值。该视图符合可更新的条件,因此我可以这样做:
UPDATE V
SET txt = 'Foo';
这不使用索引视图来查找要更新的行,即使它需要它们来实际更新视图。我希望它做的是像普通表索引一样使用视图,并识别要从中更新的行,将它们传递给
T
上的聚集索引更新,然后对视图进行更新。所以我尝试这个:
UPDATE V WITH (NOEXPAND)
SET txt = 'Foo';
失败并显示
"Hint 'noexpand' on object 'V' is invalid."
我知道我可以通过这样的查询来解决它:
UPDATE T
SET txt = 'Foo'
FROM T
JOIN V WITH (NOEXPAND) ON V.Id = T.Id;
但这意味着额外的搜索。不仅如此,它还在后续索引视图更新中添加了一个过滤器,以检查行是否与视图匹配(联接视图需要评估联接),显然,它们必须与视图匹配。
有什么方法可以让它按照我想要的方式工作吗?
更新:
将视图放在
FROM
子句中,甚至放在派生表、CTE 或其他视图中都没有帮助。一旦解析器发现它正在用于更新,它就会失败。
没有迹象表明
NOEXPAND
在 Table Hints、Indexed View 或 Updatable Views 文档中不起作用。 UPDATE
声明的文档特别提到不允许某些表提示,但仅排除NOLOCK
和READUNCOMMITTED
(最近更新添加NOEXPAND
但没有解释)
你想要的都是废话。更新始终发生在表中,并根据视图定义随后应用于视图。
这是准确的执行顺序。
您是说您想先更新视图,然后更新表。即使你认为你已经说过了,这就是你在现实中所说的。
因此,无论您希望如何“识别”行,UPDATE语句都会首先进入表,识别表中的行并更改所需的值,然后它会对视图本身进行相同的更新,基于视图的实际定义。
请记住:先是表,然后是视图,而不是相反。这不仅适用于 SQL Server,也适用于提供与索引视图类似功能的所有其他关系数据库引擎。