SQL Server 性能优化之——T-SQL TVF和标量函数
上一篇介绍了关于“临时表、表变量和Union优化”这次转向关注定义函数——也就是表-值函数、标量函数。
UDF(用户定义函数,User defined Function)对于集中精力处理业务逻辑很方便,因为可以在UDF中指定一组业务逻辑,其中可以设计多个存储过程和一些特定的查询语句。但是,由于UDF对CPU的大量请求可能导致性能下降
1. TVF(表-值行数Table-Valued Functions)
一般情况,当使用TVF与一个对象内联接,如果该对象没有索引将会导致TVF像索引扫描或表扫描一样做扫描操作。
作为一个选择,可以创建临时表,临时表上创建适当的聚集索引或非聚集索引。
详情如下:
- 创建适当的临时表。
- 根据T-SQL创建适当的聚集索引和非聚集索引。
- 将TVF的数据插入到临时表中。
- 用临时表和相关的列替换每一个TVF。
- 在查询语句执行结束后,删除临时表。
注意,临时表的性能提升是超过表参数,在上一篇博客中提到的,表参数不支持索引。
例子:
a. 创建TVF:
1: use [MyDemo]
2: go
3: alter FUNCTION Dep_Salaries1
4: (
5: @empid int
6: )
7: RETURNS @table table
8: (
9: Department int,
10: Salary_Max int,
11: Salary_Min int
12: )
13: AS
14: BEGIN
15: declare @Department int = (select S.deptid from Employees s where s.empid=@empid)
16: insert into @table
17: SELECT S.deptid , max (Salary) , MIN(Salary) FROM Employees s inner join Departments T ON S.deptid =T.deptid group by S.deptid having S.deptid =@Department
18: RETURN
19: END
20: GO
b. 使用TVF的低性能T-SQL:
1: alter procedure Unperformant_SP1
2: @empid int
3: as
4: begin
5: select T.deptid as department_name , s.* from Dep_Salaries1 (@empid )S inner join Departments T ON S.Department =T.deptid
6: end
c. 使用临时表代替TVF:
1: go
2: alter procedure Performant_SP1
3: @empid int
4: as
5: begin
6: create table #table
7: (
8: Department int,
9: Salary_Max int,
10: Salary_Min int
11: )
12: create clustered index #table_index1 on #table (Department)
13: insert into #table select * from Dep_Salaries1 (@empid )
14: select T.deptid as department_name , s.* from #table S inner join Departments T ON S.Department =T.deptid
15: end
在使用具体的查询和数据时,还是应该进行必要的性能测试,发现最适合自己情况的解决方案。
2. 标量函数
标量函数,对于确定存储过程或特定查询语句的聚合值、累计值、差分值非常方便的,但是对性能是有损失的,尤其使用大数据,标量函数将执行每一个记录。
3. 替代标量函数
1). 临时表
使用临时表,但是这个解决方案有一点不同于TVF的情况,这里希望完全放弃标量函数并且也不去直接使用内部T-SQL代码。
2). 持久化确定的计算列
持久化确定的计算列值不是每次选择都重新计算该列,而只是在创建时计算一次。因此,这时可以添加不同的T-SQL语句提高性能,因为这样可以减少进程的开销。
这个功能可以通过下面步骤添加:
- 增加一个新的计算列存储标量函数的结果。
- 启用这个计算列的持久化功能。
- 在列(不管是主键列还是包含列)上设置适当的索引。
但是要注意持久化功能还是有一些限制,如:
i. 计算列不应该使用任何其他记录的聚合功能。
ii. 计算列不应该使用调用外部系统过程的功能。
iii. 计算列不应该使用任何其他表的其他字段的功能。
iv. 计算列生成最好是使用系统提供的功能,例如:Convert、Cast、Replace等等,并且开发者不能创建UDF,因为UDF通常和该功能相矛盾。
这仅仅是适用于持久化的功能,但是可以添加计算列索引,应该通过确定计算数据的精确类型(如,INT、 Bigint、 DateTime和decimal)精确列的类型。如果数据类型不精确,可以添加这些列为索引的包含列的一部分,但不是主键列的一部分。
3). 使用计划更新工作
如果不可能使用持久化确定的计算列,可以创建普通列并同时创建计划更新工作,更新这些列的标量函数输出,然后用T-SQL代替标量函数并且在T-SQL中使用这些列。具体如下:
a. 创建标量函数:
1: use [Workshops]
2: go
3: create FUNCTION Salary_Tax
4: (
5: @empid int
6: )
7: RETURNS float
8: AS
9: BEGIN
10: declare @salary int = (select (S.salary-100) from Employees s where s.empid=@empid)
11: RETURN @salary
12: END
13: GO
14: --性能低些的标量函数
15: Select empid ,dbo.Salary_Tax (empid) as 'SalaryWithTax' from Employees
b. 使用临时表替换标量函数:
1: Create Table #temp (Empid int primary key clustered , Salary_Tax float)
2: Create nonclustered index #temp_Index1 on #temp (Empid ) include (Salary_Tax )
3: insert into #temp select Empid ,(Salary-100) as salary_Tax from Employees
4: select * from #temp
c. 使用持久化确定的计算列:
1: ALTER TABLE dbo.Employees ADD Salary_Tax AS Salary-100 PERSISTED
2: Create nonclustered index Employees_Index1 on Employees (Empid, Salary_Tax )
3: select empid ,Salary_Tax from Employees
d. 使用计划工作代替标量函数:
1: ALTER TABLE dbo.Employees ADD Salary_Tax1 float, update_flag bit
2: ALTER TABLE dbo.Employees ADD CONSTRAINT DF_Employees_update_flag DEFAULT 0 FOR update_flag
3: Schedule the below DML update by an appropriate frequency according to your workload
4: Update Employees set Salary_Tax1=Salary-100 WHERE UPDATE_Flag=0
5: Then you can include the below select query within your stored procedure.
6: select empid , Salary_Tax1 from Employees
上班时间到了!
期待下一篇吧!
任何的优化的不是绝对的,只有适应自己环境才是最好的,性能测试是必要。
- UESTC 1591 An easy problem A【线段树点更新裸题】
- 关关的刷题日记05 —— Leetcode 219. Contains Duplicate II
- 关关的刷题日记05 —— Leetcode 217. Contains Duplicate 方法1和方法2
- HDU 2602 Bone Collector(01背包裸题)
- Appium+python自动化13-native和webview切换
- HDU 2639 Bone Collector II(01背包变形【第K大最优解】)
- 专知内容生产基石-数据爬取采集利器WebCollector 介绍
- python实现字符串模糊匹配
- 动态规划之01背包详解【解题报告】
- hihoCoder #1078 : 线段树的区间修改(线段树区间更新板子题)
- HDU 2546 饭卡(01背包裸题)
- 漫谈文件系统
- AI知识搜索利器:基于ElasticSearch构建专知实时高性能搜索系统
- 【深度干货】专知主题链路知识推荐#5-机器学习中似懂非懂的马尔科夫链蒙特卡洛采样(MCMC)入门教程01
- MySQL 教程
- MySQL 安装
- MySQL 管理与配置
- MySQL PHP 语法
- MySQL 连接
- MySQL 创建数据库
- MySQL 删除数据库
- MySQL 选择数据库
- MySQL 数据类型
- MySQL 创建数据表
- MySQL 删除数据表
- MySQL 插入数据
- MySQL 查询数据
- MySQL where 子句
- MySQL UPDATE 查询
- MySQL DELETE 语句
- MySQL LIKE 子句
- mysql order by
- Mysql Join的使用
- MySQL NULL 值处理
- MySQL 正则表达式
- MySQL 事务
- MySQL ALTER命令
- MySQL 索引
- MySQL 临时表
- MySQL 复制表
- 查看MySQL 元数据
- MySQL 序列 AUTO_INCREMENT
- MySQL 处理重复数据
- MySQL 及 SQL 注入
- MySQL 导出数据
- MySQL 导入数据
- MYSQL 函数大全
- MySQL Group By 实例讲解
- MySQL Max()函数实例讲解
- mysql count函数实例
- MYSQL UNION和UNION ALL实例
- MySQL IN 用法
- MySQL between and 实例讲解
- Python Tricks 使用 pywinrm 远程控制 Windows 主机的方法
- PHP学习记录之常用的魔术常量详解
- laravel orm 关联条件查询代码
- PHP实现财务审核通过后返现金额到客户的功能
- Laravel框架中的路由和控制器操作实例分析
- php利用array_search与array_column实现二维数组查找
- Python实现手绘图效果实例分享
- php如何把表单内容提交到数据库
- Laravel5.5 视图 – 创建视图和数据传递示例
- 浅谈Python爬虫原理与数据抓取
- PHP封装cURL工具类与应用示例
- php中isset与empty函数的困惑与用法分析
- 布隆过滤器(bloom filter)及php和redis实现布隆过滤器的方法
- PHP使用反向Ajax技术实现在线客服系统详解
- PHP设计模式之适配器模式(Adapter)原理与用法详解