SQL Server 视图何时更新?

Posted

技术标签:

【中文标题】SQL Server 视图何时更新?【英文标题】:When are SQL Server Views updated? 【发布时间】:2013-02-11 15:08:13 【问题描述】:

我有两个表,每个表大约有 500k 行(并且还在增长)。这些不断发生插入/更新,有时每分钟 100 次。系统在基本插入这些表时存在性能问题,即超时。我们已经调整了索引,并进行了通常的优化。但我想知道这两个表在 5 个视图中被大量连接引用的事实是否可能是有害的。我一直认为,也许是错误的,随着基础表的变化,引用它们的视图也会发生变化。因此,如果表格发生了如此大的变化,那么我们的系统可能会因为不得不不断追赶更新视图而不堪重负。

【问题讨论】:

两个回答都是准确的。视图本质上是在连接中使用子查询的简写。发生超时的原因有很多;既然你说的是超时而不是死锁,我假设插入是从 SQL 外部调用的。您能否提供有关超时如何发生的更多信息?可能是您将超时阈值设置得太低(例如,使用 SqlCommand 对象)。 我非常反对在存储代码中使用视图。如果您可以使用视图,那么您也可以手动编写连接而不使用视图。视图的问题在于,您正在尝试对很可能不需要完整解决方案的场景使用一站式解决方案。请记住,SQL 非常擅长自我优化,它根据使用的表和每个表中使用的特定列来执行此操作。如果您的视图返回 6 列,但您只需要 4 列,那么由于视图,您的查询本质上是低效的。 @Love2Learn - 不完全正确。如果您只从视图中选择 4 列,那么优化器可以使用该信息并生成一个无法提供其他列的计划(如果这样的计划是可行的)。正如我所说,它们的优化适用于整个查询(视图定义扩展到其中)。 我们也会遇到死锁,但大多是超时。你是对的,我们的网站调用具有插入/更新代码的存储过程。不过,我会研究超时。我相信我们已经把它们提高了一点,但可能还不够。 @Damien_The_Unbeliever 那我得研究一下,你说的有道理;我一直认为子查询结果已被评估并传回,但如果不使用其中一列,优化器可以/会将其从整个查询逻辑中排除是有道理的。在表使用方面,如果您加入一个表并且不在视图中使用该表中的任何内容,您仍然会受到性能影响,对吗??? 【参考方案1】:

除非他们是indexed views(你没有在你的问题中提到这个),他们根本没有“更新”。

普通视图类似于 C 中的宏——它们只是隐藏较大表达式的一部分的便捷简写。它们被扩展为引用它们的任何语句的解析树,然后在使用点编译和优化整个树。


对于索引视图,您在很大程度上是正确的 - 视图作为在基表中执行更改的同一事务的一部分进行维护。但是,索引视图的规则已经过设计,因此此更新活动不会产生太大的损失(无需重新查询整个基表即可维护它们)。

【讨论】:

绝对不是索引视图。我会在别处寻找问题,谢谢!【参考方案2】:

这取决于:

1) 如果视图没有被索引,则视图被展开

-- View definition
CREATE VIEW Sales.v_SalesOrderDetail
AS
SELECT  h.SalesOrderID, h.SalesOrderNumber, h.OrderDate, 
        d.SalesOrderDetailID, d.OrderQty, d.UnitPrice, d.LineTotal, 
        p.ProductID, p.Name AS ProductName, p.Color AS ProductColor
FROM    Sales.SalesOrderHeader h
INNER JOIN Sales.SalesOrderDetail d ON h.SalesOrderID = d.SalesOrderID
INNER JOIN Production.Product p ON d.ProductID = p.ProductID
GO

-- View usage
SELECT  v.SalesOrderDetailID, v.OrderQty, v.UnitPrice, v.ProductName
FROM    Sales.v_SalesOrderDetail v
WHERE   v.ProductColor='Red';
GO

如果我们查看执行计划(SSMS:Ctrl + M), 然后我们将看到视图 (FROM Sales.v_SalesOrderDetail v) 被展开并且服务器只查询两个表:Sales.SalesOrderDetail dProduction.Product p。此外,我们可以看到Sales.SalesOrderHeader hSales.SalesOrderDetail d 之间的连接是如何被删除的,因为:

SELECT 子句 (SELECT v.SalesOrderDetailID, v.OrderQty, v.UnitPrice, v.ProductName) 不包括来自 Sales.SalesOrderHeader 表的列,

这两个表之间有一个外键约束和

这个 FK 约束是可信的。

2) 如果视图被索引(意味着视图上定义了UNIQUE CLUSTERED INDEX)并且 SQL Server 版本是企业版,则视图可以或不扩展。如果 edition enterprise 则展开索引视图。我们可以通过使用NOEXPAND 提示来强制服务器不展开 [indexed] 视图:

-- View definition
CREATE VIEW Sales.v_SalesOrderDetail2
WITH SCHEMABINDING
AS
SELECT  h.SalesOrderID, h.SalesOrderNumber, h.OrderDate, 
        d.SalesOrderDetailID, d.OrderQty, d.UnitPrice, d.LineTotal, 
        p.ProductID, p.Name AS ProductName, p.Color AS ProductColor
FROM    Sales.SalesOrderHeader h
INNER JOIN Sales.SalesOrderDetail d ON h.SalesOrderID = d.SalesOrderID
INNER JOIN Production.Product p ON d.ProductID = p.ProductID
GO

-- Defining the UNIQUE CLUSTERED INDEX
CREATE UNIQUE CLUSTERED INDEX PK_v_SalesOrderDetail2
ON Sales.v_SalesOrderDetail2 (SalesOrderDetailID);
GO

-- View usage
SELECT  v.SalesOrderDetailID, v.OrderQty, v.UnitPrice, v.ProductName
FROM    Sales.v_SalesOrderDetail2 v WITH (NOEXPAND)
WHERE   v.ProductColor='Red';
GO

在这种情况下,我们可以看到执行计划 包括Clustered Index Scan PK_v_SalesOrderDetail2 运算符。因此,它使用在第二个视图上定义的索引。

注意:SQL Server bug indexed view + MERGE。

【讨论】:

【参考方案3】:

SQLServer 视图不会被缓存,因此每次请求视图时都会执行查询

【讨论】:

【参考方案4】:

调整索引很好,但可能值得考虑更新这些索引的统计信息的频率和时间。此外,使用缓冲表来保存插入,然后可以每隔 5 或 10 分钟或任何适合您的系统的批量操作插入这些插入可能会有所帮助(将其放在单独的物理磁盘上是个好主意。)这样做会使在 90% 的时间里选择快得多,在另外 10% 的时间里不会比当前差多少。

【讨论】:

以上是关于SQL Server 视图何时更新?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 视图插入更新

更新 SQL Server 中的视图连接列

了解 SQL Server 2008 R2 中的索引视图更新和查询过程

创建TRIGGER以插入,更新和删除使用视图 - SQL SERVER

SQL Server修改表结构后批量更新所有视图

具有多个基表和完整性能的 Microsoft SQL Server (MSSQL) 可更新视图