这些查询基于 AdventureWorks 中的表。AdventureWorks 是 SQL Server 2005 所提供的示例数据库,并可作为写入方式来执行。在创建视图前后,用户可能想用 SQL Server Management Studio 中显示预计的执行计划工具,来查看查询优化器所选择的计划。虽然这些例子说明了优化器选择低成本执行计划的方式,但是 AdventureWorks 示例由于太小而无法显示出性能方面的提升。
在开始运用这些示例之前,确保通过运行下列命令对会话设置正确的选项:
设置 SET ANSI_NULLS ON SET ANSI_PADDING ON SET ANSI_WARNINGS ON SET CONCAT_NULL_YIELDS_NULL ON SET NUMERIC_ROUNDABORT OFF SET QUOTED_IDENTIFIER ON SET ARITHABORT ON
查询 1 SELECT TOP 5 ProductID, Sum(UnitPrice*OrderQty) - Sum(UnitPrice*OrderQty*(1.00-UnitPriceDiscount)) AS Rebate FROM Sales.SalesOrderDetail GROUP BY ProductID ORDER BY Rebate DESC
查询 2 SELECT TOP 5 ProductID, SUM(UnitPrice*OrderQty*UnitPriceDiscount) AS Rebate FROM Sales.SalesOrderDetail GROUP BY ProductID ORDER BY Rebate DESC
CREATE VIEW Vdiscount1 WITH SCHEMABINDING AS SELECT SUM(UnitPrice*OrderQty) AS SumPrice, SUM(UnitPrice*OrderQty*(1.00-UnitPriceDiscount)) AS SumDiscountPrice, COUNT_BIG(*) AS Count, ProductID FROM Sales.SalesOrderDetail GROUP BY ProductID GO CREATE UNIQUE CLUSTERED INDEX VDiscountInd ON Vdiscount1 (ProductID)
视图 2 CREATE VIEW Vdiscount2 WITH SCHEMABINDING AS SELECT SUM(UnitPrice*OrderQty)AS SumPrice, SUM(UnitPrice*OrderQty*(1.00-UnitPriceDiscount))AS SumDiscountPrice, SUM(UnitPrice*OrderQty*UnitPriceDiscount)AS SumDiscountPrice2, COUNT_BIG(*) AS Count, ProductID FROM Sales.SalesOrderDetail GROUP BY ProductID GO CREATE UNIQUE CLUSTERED INDEX VDiscountInd ON Vdiscount2 (ProductID)
SELECT TOP 3 SalesOrderID, SUM(UnitPrice*OrderQty*UnitPriceDiscount) OrderRebate FROM Sales.SalesOrderDetail GROUP BY SalesOrderID ORDER BY OrderRebate DESC 必须用一个单独的索引视图来应付该查询。可修改 Vdiscount2 以包含 SalesOrderID;但是,结果视图将和原始表包含同样多的行,并不会通过使用基表提高性能。
查询 4
该查询可生成每个产品的平均价格。
SELECT p.Name, od.ProductID, AVG(od.UnitPrice*(1.00-od.UnitPriceDiscount)) AS AvgPrice, SUM(od.OrderQty) AS Units FROM Sales.SalesOrderDetail AS od, Production.Product AS p WHERE od.ProductID=p.ProductID GROUP BY p.Name, od.ProductID
CREATE VIEW View3 WITH SCHEMABINDING AS SELECT ProductID, SUM(UnitPrice*(1.00-UnitPriceDiscount)) AS Price, COUNT_BIG(*) AS Count, SUM(OrderQty) AS Units FROM Sales.SalesOrderDetail GROUP BY ProductID GO CREATE UNIQUE CLUSTERED INDEX iv3 ON View3 (ProductID)
SELECT p.Name, od.ProductID, AVG(od.UnitPrice*(1.00-UnitPriceDiscount)) AS AvgPrice, SUM(od.OrderQty) AS Units FROM Sales.SalesOrderDetail AS od, Production.Product AS p WHERE od.ProductID=p.ProductID AND p.Name like '%Red%' GROUP BY p.Name, od.ProductID
查询 6
查询优化器无法对该查询使用“视图 3”。添加的搜索条件 od.UnitPrice>10 包含来自视图定义中表的列,但该列不显示在 GROUP BY 列表中,而搜索谓词也不显示在视图定义中。
SELECT p.Name, od.ProductID, AVG(od.UnitPrice*(1.00-UnitPriceDiscount)) AS AvgPrice, SUM(od.OrderQty) AS Units FROM Sales.SalesOrderDetail AS od, Production.Product AS p WHERE od.ProductID=p.ProductID AND p.Name like '%Red%' GROUP BY p.Name, od.ProductID
查询 7
相反,查询优化器可对“查询 7”使用“视图 3”,因为新的搜索条件 od.ProductID in (1,2,13,41) 中定义的列包含在视图定义的 GROUP BY 子句中。
SELECT p.Name, od.ProductID, AVG(od.UnitPrice*(1.00-UnitPriceDiscount)) AS AvgPrice, SUM(od.OrderQty) AS Units FROM Sales.SalesOrderDetail AS od, Production.Product AS p WHERE od.ProductID = p.ProductID AND od.UnitPrice > 10 GROUP BY p.Name, od.ProductID
CREATE VIEW View4 WITH SCHEMABINDING AS SELECT p.Name, od.ProductID, SUM(od.UnitPrice*(1.00-UnitPriceDiscount)) AS SumPrice, SUM(od.OrderQty) AS Units, COUNT_BIG(*) AS Count FROM Sales.SalesOrderDetail AS od, Production.Product AS p WHERE od.ProductID = p.ProductID AND od.UnitPrice > 10 GROUP BY p.Name, od.ProductID GO CREATE UNIQUE CLUSTERED INDEX VdiscountInd on View4 (Name, ProductID)
查询 8
“视图 4”上相同的索引也将用于在其中添加对表 Sales.SalesOrderHeader 的联接的查询。该查询满足条件:查询 FROM 子句中所列的表是索引视图的 FROM 子句中的表的超集。
SELECT p.Name, od.ProductID, AVG(od.UnitPrice*(1.00-UnitPriceDiscount)) AS AvgPrice, SUM(od.OrderQty) AS Units FROM Sales.SalesOrderDetail AS od, Production.Product AS p, Sales.SalesOrderHeader AS o WHERE od.ProductID = p.ProductID AND o.SalesOrderID = od.SalesOrderID AND od.UnitPrice > 10 GROUP BY p.Name, od.ProductID
SELECT p.Name, od.ProductID, AVG(od.UnitPrice*(1.00-UnitPriceDiscount)) AvgPrice, SUM(od.OrderQty) AS Units FROM Sales.SalesOrderDetail AS od, Production.Product AS p, Sales.SalesOrderHeader AS o WHERE od.ProductID = p.ProductID AND o.SalesOrderID = od.SalesOrderID AND od.UnitPrice > 25 GROUP BY p.Name, od.ProductID 查询 8b
SELECT p.Name, od.ProductID, AVG(od.UnitPrice*(1.00-UnitPriceDiscount)) AS AvgPrice, SUM(od.OrderQty) AS Units FROM Sales.SalesOrderDetail AS od, Production.Product AS p, Sales.SalesOrderHeader AS o WHERE od.ProductID = p.ProductID AND o.SalesOrderID = od.SalesOrderID AND od.UnitPrice > 10 AND o.OrderDate > '20040728' GROUP BY p.Name, od.ProductID
视图 4a
“视图 4a”通过将 UnitPrice 列包含在选择列表和 GROUP BY 子句中,扩展了“视图 4”。“查询 8a”可使用“视图 4a”,因为将进一步筛选 UnitPrice 值(已知大于 10)以便只留下大于 25 的值。以下是间隔归入的一个例子。
CREATE VIEW View4a WITH SCHEMABINDING AS SELECT p.Name, od.ProductID, od.UnitPrice, SUM(od.UnitPrice*(1.00-UnitPriceDiscount)) AS SumPrice, SUM(od.OrderQty) AS Units, COUNT_BIG(*) AS Count FROM Sales.SalesOrderDetail AS od, Production.Product AS p WHERE od.ProductID = p.ProductID AND od.UnitPrice > 10 GROUP BY p.Name, od.ProductID, od.UnitPrice GO CREATE UNIQUE CLUSTERED INDEX VdiscountInd ON View4a (Name, ProductID, UnitPrice) 视图 5
“视图 5”在其选择和 GROUP BY 列表中包含一个表达式。请注意,LineTotal 是一个计算列,因此本身是一个表达式。反过来,该表达式嵌套在对 FLOOR 函数的调用中。
CREATE VIEW View5 WITH SCHEMABINDING AS SELECT FLOOR(LineTotal) FloorTotal, COUNT_BIG(*) C FROM Sales.SalesOrderDetail GROUP BY FLOOR(LineTotal) GO CREATE UNIQUE CLUSTERED INDEX iView5 ON View5(FloorTotal) 查询 9
“查询 9”在其选择和 GROUP BY 列表中包含表达式 FLOOR(LineTotal)。通过对 SQL Server 2005 中表达式的视图匹配的新扩展,该查询使用“视图 5”上的索引。
SELECT TOP 5 FLOOR(LineTotal), Count(*) FROM Sales.SalesOrderDetail GROUP BY FLOOR(LineTotal) ORDER BY COUNT(*) DESC 视图 6
CREATE VIEW View6 WITH SCHEMABINDING AS SELECT SalesOrderID, SalesOrderDetailID, CarrierTrackingNumber, OrderQty, ProductID, SpecialOfferID, UnitPrice, UnitPriceDiscount, rowguid, ModifiedDate FROM Sales.SalesOrderDetail WHERE ModifiedDate IN ( convert(datetime, '2004-07-31', 120), convert(datetime, '2004-07-30', 120), convert(datetime, '2004-07-29', 120) ) GO CREATE UNIQUE CLUSTERED INDEX VEndJulyO4Ind ON View6(SalesOrderID, SalesOrderDetailID) GO
SELECT h.*, SalesOrderDetailID, CarrierTrackingNumber, OrderQty, ProductID, SpecialOfferID, UnitPrice, UnitPriceDiscount, d.rowguid, d.ModifiedDate FROM Sales.SalesOrderHeader as h, Sales.SalesOrderDetail as d WHERE (d.ModifiedDate = '20040729' OR d.ModifiedDate = '20040730') and d.SalesOrderID=h.SalesOrderID
视图 7
开发人员有时还会发现使用索引视图强制专门的完整性约束很方便。例如,可通过索引视图强制约束:“除非列中存在多个 0 值,否则表 T 的列 a 就是唯一的”。下方索引视图“视图 7”就强制了这一约束。如果运行下面的脚本,其将成功运行直至最终的插入操作。该语句被禁止,因为其添加了一个非零重复值。
USE tempdb GO CREATE TABLE T(a int) GO CREATE VIEW View7 WITH SCHEMABINDING AS SELECT a FROM dbo.T WHERE a <> 0 GO CREATE UNIQUE CLUSTERED INDEX IV on View7(a) GO -- legal: INSERT INTO T VALUES(1) INSERT INTO T VALUES(2) INSERT INTO T VALUES(0) INSERT INTO T VALUES(0) -- duplicate 0
-- dissalowed: INSERT INTO T VALUES(2)
七、有关索引视图的常见问题
问:为何对可创建索引的视图类型存在限制?
答:为了确保在逻辑上可对视图进行增量维护,限制创建维护成本较高的视图,并限制 SQL Server 实施的复杂性。较大的视图集不具有确定性并与内容相关;其内容的“更改”独立于 DML 操作。无法对这些内容进行索引。在其定义中调用 GETDATE 或 SUSER_SNAME 的任何视图就属于这类视图。