当前位置: 首页 > news >正文

T-SQL 之 自定义函数

  和存储过程很相似,用户自定义函数也是一组有序的T-SQL语句,UDF被预先优化和编译并且作为一个单元进行调用。UDF和存储过程的主要区别在于返回结果的方式。

  使用UDF时可传入参数,但不可传出参数。输出参数的概念被改为健壮的返回值取代了。和系统函数一样,可以返回标量值,这个值的好处是它并不像在存储过程中那样只限于整形数据类型,而是可以返回大多数SQL Server数据类型。

  UDF有以下两种类型:

  [1] 返回标量值的UDF。

  [2] 返回表的UDF。

一、创建

  创建语法:

CREATE FUNCTION [<schema name>.]<function name>
(
[ <@parameter name> [AS] [<schema name>.]<data type> [= <default value> [READONLY]] [,...n] ]
)
RETURNS { <scalar type> | TABLE [(<table definition>)] }
[ WITH [ENCRYPTION] | [SCHEMABINDING] | [RETURNS NULL ON NULL INPUT | CALLED ON NULL INPUT ] |
[EXECUTE AS {CALLER|SELF|OWNER|<'user name'>}]
[AS] { EXTERNAL NAME <externam method> |
BEGIN
[<function statements>]
{RETURN <type as defined in RETURNS clause | RETURN (<SELECT statement>)}
END}[;]

二、返回标量值的UDF

  这种类型的UDF和大多数SQL Server内置函数一样,会向调用脚本或存储过程返回标量值,像GETDATE()或USER()函数就会返回标量值。

  UDF的返回值并不限于整数,而是可以返回除了BLOB、游标(cursor)和时间戳以外的任何有效的SQL Server数据类型(包括用户自定义类型)。即时同为返回整数,UDF也有以下两个吸引人的方面。

  与存储过程不同,用户自定义函数返回值的目的是提供有意义的数据;而对于存储过程来说,返回值只能说明成功或失败,如果失败,则会提供一些关于失败性质的特定信息。
可在查询中内联执行函数(如作为SELECT语句的一部分),而使用存储过程则不行。

  下面创建一个UDF如下:

CREATE FUNCTION DateOnly(@Date DateTime)
  RETURNS varchar(12)
AS
  BEGIN
      RETURN CONVERT(varchar(12),@Date,101)
  END

  然后试着,运用一下:

SELECT * FROM Nx_comment 
WHERE dbo.DateOnly(com_posttime) = '2012.04.28'  --注意前面的dbo是必须的。

  其实以上SQL语句相当于:

  SELECT * FROM Nx_comment 
  WHERE CONVERT(varchar(12),com_posttime,102) = '2012.04.28'

  留意到是用了UDF的SQL语句可读性更加好。显示结果如下:

  

  再来看一个简单的查询:

  SELECT Name,Age,
      (SELECT AVG(Age) FROM Person) AS AvgAge,
       Age - (SELECT AVG(Age) FROM Person) AS Difference 
  FROM Person

  以上SQL查询返回结果集如下:

  

  这里要说明一下,列的意思分别是,姓名,年龄,平均年龄以及与平均年龄的差值。

  下面我们用UDF来实现,先定义两个UDF如下:

CREATE FUNCTION dbo.AvgAge()
  RETURNS int
AS
  BEGIN
      RETURN (SELECT AVG(Age) FROM Person)
  END

GO

CREATE FUNCTION dbo.AgeDifference(@Age int)
  RETURNS int
AS
  BEGIN
      RETURN @Age - dbo.AvgAge();        --在一个UDF内引用另外一个UDF
  END

  然后执行查询:

  SELECT Name,Age,dbo.AvgAge() AS AvgAge,dbo.AgeDifference(Age) as Difference 
  FROM Person

  以上查询在返回结果集上与上面单独的SQL一样,但是为什么我感觉到速度好像慢了很多呢?知道的哥们回复下。

三、返回表的UDF

  SQL Server中的用户自定义函数并不只限于返回标量值,也可以返回表。返回的表在很大程度上和其他表是一样的。可以对返回 表的UDF执行JOIN,甚至对结果应用WHERE条件。

  改为用表作为返回值并不难,对于UDF来说,表就像任何其他SQL Server数据类型一样。

  为了说明情况,我特地建了一张表如下:

  

  创建一个UDF如下:

CREATE FUNCTION dbo.fnContactName()
  RETURNS TABLE
AS
  RETURN (
          SELECT Id,LastName + ',' + FirstName AS Name 
          FROM Man
          )

  然后我们就可以像表一样地用UDF了。

  SELECT * FROM dbo.fnContactName()

  输出结果如下:

  

  现在再来看看一个简单的用法,定义UDF如下:

CREATE FUNCTION dbo.fnNameLike(@LName varchar(20))
  RETURNS TABLE
AS
  RETURN (
          SELECT Id,LastName + ',' + FirstName AS Name 
          FROM Man
          WHERE LastName Like @LName + '%'
          )

  然后查询的时候可以这样用:

  SELECT * FROM dbo.fnNameLike('刘')

  显示结果如下:

  

  没有WHERE子句,没有过滤SELECT列表,就可以反复使用该函数,而不需要进行"剪切和粘贴"。而且本例做得不好,其实完全可以先连接一次其他表,然后再查询,这是存储过程所做不到的。

四、理解确定性

  用户自定义函数可以是确定性的也可以是非确定性的。确定性并不是根据任何参数类型定义的,而是根据函数的功能定义的。如果给定了一组特定的有效输入,每次函数就都能返回相同的结果,那么就说该函数是确定性的。SUM()就是一个确定性的内置函数。3、5、10的总合永远都是18,而GETDATE()的值就是非确定性的,因为每次调用它的时候GETDATE()都会改变。
  为了达到确定性的要求,函数必须满足以下4个条件:

  [1] 函数必须是模式绑定的。这意味着函数所依赖的任何对象会有一个依赖记录,并且在没有删除这个依赖的函数之前都不允许改变这些对象。

  [2] 函数引用的所有其他函数,无论是用户定义的,还是系统定义的,都必须是确定性的。

  [3] 不能引用在函数外部定义的表(可以使用表变量和临时表,只要它们是在函数作用域内定义就行)。

  [4] 不能使用扩展存储过程。

  确定性的重要性在于它显示了是否要在视图或计算列上建立索引。如果可以可靠地确定视图或计算列的结果,那么才允许在视图或计算列上建立索引。这意味着,如果视图或计算列引用非确定性函数,则在该视图或列上将不允许建立任何索引。

  如果判定函数是否是确定性:除了上面描述的规则外,这些信息存储在对象的IsDeterministic属性中,可以利用OBJECTPROPERTY属性检查。

SELECT OBJECTPROPERTY(OBJECT_ID('DateOnly'),'IsDeterministic');  --只是刚才的那个自定义函数

  输出结果如下:

   

   居然是非确定性的。原因在于之前在定义该函数的时候,并没有加上这个"WITH SCHEMABINDING"。

ALTER FUNCTION dbo.DateOnly(@Date date)
  RETURNS date
  WITH SCHEMABINDING  --当加上这一句之后
AS
  BEGIN
    RETURN @Date
  END

  在执行查询,该函数就是确定性的了。

  

相关文章:

  • Comparable和Comparator排序接口
  • 驰骋工作流引擎表单设计控件-关系类控件-明细表(3)
  • 第一篇、C_高精度加法
  • 【域控管理】父域的搭建
  • dos.orm
  • MYSQL 的 IF 函数
  • 深入了解php opcode缓存原理
  • 自媒体平台如何提高推荐量
  • iptables详解
  • Entityframework core 动态添加模型实体
  • JDK 有区分 JAVA SE 和 JAVA EE版本的吗
  • attention 机制
  • 不小心删掉root目录......
  • python 生成器 迭代器
  • Django搭建博客后台
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • Angular4 模板式表单用法以及验证
  • hadoop集群管理系统搭建规划说明
  • HTML-表单
  • interface和setter,getter
  • Invalidate和postInvalidate的区别
  • java 多线程基础, 我觉得还是有必要看看的
  • JSONP原理
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • node和express搭建代理服务器(源码)
  • python学习笔记-类对象的信息
  • Vue2 SSR 的优化之旅
  • Zepto.js源码学习之二
  • 聚类分析——Kmeans
  • 聊聊flink的BlobWriter
  • 免费小说阅读小程序
  • 如何学习JavaEE,项目又该如何做?
  • 设计模式 开闭原则
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 线上 python http server profile 实践
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • #FPGA(基础知识)
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • $.ajax()
  • (4)(4.6) Triducer
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (六)c52学习之旅-独立按键
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (篇九)MySQL常用内置函数
  • (四)JPA - JQPL 实现增删改查
  • (四)汇编语言——简单程序
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • .net core 6 redis操作类
  • .net core webapi 大文件上传到wwwroot文件夹
  • .Net 应用中使用dot trace进行性能诊断
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • .Net程序帮助文档制作