惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Check Point Blog
Microsoft Security Blog
Microsoft Security Blog
aimingoo的专栏
aimingoo的专栏
V
V2EX
博客园 - 【当耐特】
T
Tailwind CSS Blog
Apple Machine Learning Research
Apple Machine Learning Research
量子位
MyScale Blog
MyScale Blog
Hugging Face - Blog
Hugging Face - Blog
大猫的无限游戏
大猫的无限游戏
The Cloudflare Blog
月光博客
月光博客
I
InfoQ
WordPress大学
WordPress大学
Martin Fowler
Martin Fowler
T
The Blog of Author Tim Ferriss
爱范儿
爱范儿
小众软件
小众软件
罗磊的独立博客
Recent Announcements
Recent Announcements
Blog — PlanetScale
Blog — PlanetScale
J
Java Code Geeks
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

博客园 - tohen

浏览器视频文件分段缓存合并成完整的视频 我爷爷吸烟,我爸爸也吸烟,轮到我不能断了香火 线程封装组件(BackgroundWorker)和线程(Thread) SQL2008R2的 遍历所有表更新统计信息 和 索引重建 对比:重建索引与更新统计 试试SQLServer 2014的内存优化表 Sql server2014 内存优化表 本地编译存储过程 SQL Server 2016 查询存储性能优化小结 测试创建表变量对IO的影响 公用表表达式(CTE) 获取应用程序根目录物理路径(Web and Windows) 【别人的老师VS你的老师 】同样是老师,差别怎么这么大呢!? SQL Server 用SSMS查看依赖关系有时候不准确,改用代码查 SQL Server 百万级数据提高查询速度的方法 SQL server 数据库备份还原Sql SQL Server读懂语句运行的统计信息 SET STATISTICS TIME IO PROFILE ON SQL Server对Xml字段的操作 为什么洗澡时你会灵感乍现 SQL Server存储过程中使用表值作为输入参数示例
在计算列中创建索引提高性能
tohen · 2016-12-21 · via 博客园 - tohen

前言:
在理解计算列上的索引之前,先了解计算列的基本知识。计算列由可以使用同一表中的其他列的表达式计算得来。表达式可以是非计算列的列名、常量、函数,也可以是用一个或多个运算符连接的上述元素的任意组合。表达式不能为子查询。
默认情况下,计算列是一个虚拟的列,并且可以在调用时重新计算,直到在CREATE TABLE或者ALTER TABLE 命令中使用PERSISTED。
如果列定义成PERSISTED,会存放计算值,并存放在原始列上更新后的汇总值,不能对计算列进行INSERT、UPDATE。

准备工作:
首先要了解是否有需要在计算列上创建索引,计算列在下面情况可以考虑创建索引:
1、 如果计算列的数据来源于IMAGE,TEXT和NTEXT数据类型,只能作为非聚集索引的部分列。
2、 计算列表达式不能是REAL或者FLOAT数据类型。
3、 计算列必须明确。
4、 计算列必须具有稳定性。可以使用COLUMNPROPERTY函数的IsDeterministic属性来判断是否稳定。
5、 如果函数使用了任何函数,不管是自定义还是系统内置的,那么表和函数的拥有者必须是相同的。
6、 不能用于通过聚集函数获得的函数值上的列。
7、 需要开启下面的配置:
1、 ARITHABORT
2、 CONCAT_NULL_YIELDS_NULL
3、 QUOTED_IDENTIFIER
4、 ANSI_WARNINGS
5、 ANSI_NULLS
6、 ANSI_PADDING
7、 NUMERIC_ROUNDABORT——OFF,其他为ON 。

步骤:
1、 创建一个测试表:

USE AdventureWorks2012
GO
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
SET ARITHABORT ON
SET CONCAT_NULL_YIELDS_NULL ON
SET QUOTED_IDENTIFIER ON
SET NUMERIC_ROUNDABORT OFF

SELECT salesorderid ,
salesorderdetailid ,
carriertrackingnumber ,
orderqty ,
productid ,
specialofferid ,
unitprice
INTO salesorderdetaildemo
FROM AdventureWorks2012.sales.salesorderdetail
go


2、 现在创建一个用于计算列的自定义函数,并添加计算列NetPrice到新表中,这个列通过自定义函数UDFTotalAmount来计算值:

CREATE FUNCTION dbo.UDFTotalAmount
(
@TotalPrice NUMERIC(10, 3) ,
@Freight TINYINT
)
RETURNS NUMERIC(10, 3)
WITH SCHEMABINDING
AS
BEGIN
DECLARE @NetPrice NUMERIC(10, 3)
SET @NetPrice = @TotalPrice +( @totalprice * @Freight / 100 )
RETURN @NetPrice
END
GO

--添加计算列:
ALTER TABLE SalesOrderDetailDemo ADD [NetPrice] AS
dbo.UDFTotalAmount(OrderQty*UnitPrice,5)
GO

3、 现在在表上创建一个聚集索引。使得表不会再是堆表,然后按照前面说的,修改相关的SET选项,然后开启STATISTICS,记住目前没有创建任何索引在计算列上:

--创建聚集索引
CREATE CLUSTERED INDEX idx_SalesOrderID_SalesOrderDetailID_SalesOrderDetailDemo ON
SalesOrderDetailDemo(SalesOrderID,SalesOrderDetailID)
GO

--开启统计数据
SET STATISTICS IO ON
SET STATISTICS TIME ON
GO

--执行查询
SELECT *
FROM SalesOrderDetailDemo
WHERE NetPrice > 5000
GO

得到结果:

SQL Server 分析和编译时间:
CPU 时间= 920 毫秒,占用时间= 967 毫秒。
SQL Server 分析和编译时间:
CPU 时间= 0 毫秒,占用时间= 5 毫秒。

(3864 行受影响)
表'salesorderdetaildemo'。扫描计数1,逻辑读取757 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

SQL Server 执行时间:
CPU 时间= 780 毫秒,占用时间= 1643 毫秒。


4、 在创建索引到计算列前,先检查是否符合创建条件:

SELECT COLUMNPROPERTY(OBJECT_ID('SalesOrderDetailDemo'), 'NetPrice',
'IsIndexable') AS 'Indexable?' ,
COLUMNPROPERTY(OBJECT_ID('SalesOrderDetailDemo'), 'NetPrice',
'IsDeterministic') AS 'Deterministic?' ,
OBJECTPROPERTY(OBJECT_ID('UDFTotalAmount'), 'IsDeterministic') ,
'UDFDeterministic?' ,
COLUMNPROPERTY(OBJECT_ID('SalesOrderDetailDemo'), 'NetPrice',
'IsPrecise') AS 'Precise?'


5、 现在在计算列上创建索引,如果你前面说的条件都满足,那么可以创建了:

CREATE INDEX idx_SalesOrderDetailDemo_NetPrice
ON SalesOrderDetailDemo(NetPrice)
GO
然后再次执行查询:

SET STATISTICS IO ON
SET STATISTICS TIME ON
GO
SELECT *
FROM SalesOrderDetailDemo
WHERE NetPrice > 5000
GO

6、 结果如下:
SQL Server 分析和编译时间:
CPU 时间= 0 毫秒,占用时间= 0 毫秒。
SQL Server 分析和编译时间:
CPU 时间= 0 毫秒,占用时间= 0 毫秒。
SQL Server 分析和编译时间:
CPU 时间= 0 毫秒,占用时间= 3 毫秒。
SQL Server 分析和编译时间:
CPU 时间= 0 毫秒,占用时间= 0 毫秒。

(3864 行受影响)
表'salesorderdetaildemo'。扫描计数1,逻辑读取757 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

SQL Server 执行时间:
CPU 时间= 780 毫秒,占用时间= 1534 毫秒。

分析:
在计算列创建一个索引,存储键值到叶子节点并在SELECT的时候利用索引的统计信息,在大部分的情况下是工作得很好的。但是也有很多情况下不能用计算列。
在统计数据上,可以看到SQLServer Parse和Compile 时间还有SQLServer执行时间。如果数据量很大,那么创建了索引在计算列上的效能提高将会很明显。