Sqlserver自定义函数Function

本文详细介绍了SQL Server 2008中的自定义函数(包括标量函数、内联表值函数和多语句表值函数),游标(CURSOR)的应用,存储过程(PROCEDURE)的创建及使用,以及触发器(TRIGGER)的设计和实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://www.cnblogs.com/weihengblogs/p/4216755.html

一.FUNCTION: 在sqlserver2008中有3中自定义函数:标量函数/内联表值函数/多语句表值函数,首先总结下他们语法的异同点: 同点:1.创建定义是一样的:                                        a, CREATE FUNCTION F_NAME(传入的参数名称    传入参数的类型)                                        b,RETURNS         返回值类型                                        c,AS 异点:1.标量函数返回的是一个数据类型值,内联表值函数返回的是一个table,而多语句返回的是一个table的变量(类似前面两个的结合);            2.语法的结构:标量函数和多语句函数都是要有begin,,,end,内联表值函数就没有;            3.调用:标量函数要写成在dbo,function_name;
标量函数,利用上篇文章写的数据表,在[T_员工信息]表中查姓名为李异峰员工的工号:

复制代码

复制代码

复制代码

 1 USE SQL_SYSTEM 
 2 GO
 3 CREATE FUNCTION F_GONGHAO(@XINGMING NVARCHAR(5))
 4 RETURNS INT 
 5 AS
 6 BEGIN
 7 DECLARE @GONGHAO INT
 8 SET @GONGHAO =(SELECT Y.工号  FROM[T_员工信息] AS Y WHERE Y.姓名 =@XINGMING )
 9 RETURN @GONGHAO
10 END
11 GO
12 /*上面是标量函数的一个简单的例举,下面就开始调用,注意是在dbo.下*/
13 SELECT [姓名] FROM[T_员工信息] WHERE [姓名]='李异峰'  /**对比的查询*/
14 /*请注意观察,和对比*/
15 SELECT [姓名],DBO.F_GONGHAO ('李异峰')AS 工号 FROM[T_员工信息] WHERE [姓名]='李异峰'
16 GO

复制代码

复制代码

复制代码

 

F5下:    
内联表值函数:他返回的可是一个table哦,比如:筛选出2014.2.28号所有员工的打卡记录:

复制代码

复制代码

复制代码

 1 USE SQL_SYSTEM 
 2 GO
 3 CREATE FUNCTION F_DAKAJILU(@RIQI DATE)
 4 RETURNS TABLE
 5 AS
 6 RETURN(
 7 SELECT* FROM[T_考勤]AS K WHERE K.日期 =@RIQI 
 8 )
 9 GO
10 /*需要注意的就是他的写法上没有begin+end*/
11 /*下面就是将date带入函数中*/
12 SELECT*
13 FROM [F_DAKAJILU]('2014/02/28')
14 GO

复制代码

复制代码

复制代码

F5:

 

多语句表值函数就是,表值函数+内联表值函数综合版:就写个返回当天打卡记录和涉及员工的个人信息:

复制代码

复制代码

复制代码

 1 USE SQL_SYSTEM 
 2 GO
 3 CREATE FUNCTION F_D_DAKAJILU(@RIQI DATE)
 4 RETURNS @TEMP_TABLE TABLE(   /*这里要注意的就是既然是多语句的话,那你就要告诉计算机你要查询的列是哪些*/
 5 [姓名]NVARCHAR(5) NOT NULL,
 6 [工号]INT NOT NULL,
 7 [职位]NVARCHAR(10) NOT NULL,
 8 [部门]NVARCHAR(5) NOT NULL,
 9 [是否夜班]NCHAR(1) NOT NULL,
10 [日期]DATE NOT NULL,
11 [当天上班时间]FLOAT(1)NOT NULL
12 )   
13 /*以上就是要告诉计算机你要的一个基本信息列*/
14 AS
15 BEGIN
16 INSERT INTO @TEMP_TABLE    /*这句的意思是将下面的查询结果插入到@TEMP_TABLE变量中去*/
17 SELECT Y.姓名 ,Y.工号 ,Y.职位 ,Y.部门 ,K.夜班 ,K.日期 ,K.当天上班时间 
18 FROM[T_员工信息] AS Y CROSS JOIN [T_考勤] AS K     /*这里我用的是交叉连接*/
19 WHERE Y.工号 =K.工号 AND K.日期=@RIQI 
20 RETURN 
21 END
22 GO
23 SELECT*
24 FROM[F_D_DAKAJILU]('2014/02/28')
25 GO

复制代码

复制代码

复制代码

F5:

 

 

二:CURSOR,当要检索复杂的数据的每条数据的时候就可以用到它,类似于c语言的指针一样,他可以在你的数据表中阅历每条数据和更新。 1.新写个cursor阅历数据表(T_员工信息)的数据过程: 1.1.声明一个只读cursor:

复制代码

复制代码

复制代码

1 USE SQL_SYSTEM 
2 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
3 BEGIN TRANSACTION CURSOR_READ_T_员工信息
4 DECLARE CUR_T_员工信息 SCROLL CURSOR FOR
5 SELECT Y.姓名 ,Y.工号 ,Y.职位 ,Y.部门  FROM[T_员工信息]AS Y ORDER BY Y.工号 ASC 
6 COMMIT TRANSACTION CURSOR_READ_T_员工信息
7 GO

复制代码

复制代码

复制代码

1.2.打开:

1 OPEN GLOBAL CUR_T_员工信息
2 GO

1.3.阅历每条数据:

复制代码

复制代码

复制代码

 1 /*声明4个变量用来接收暂存游标中的数据*/
 2 DECLARE @XINGMING NVARCHAR(3),@GONGHAO INT,@ZHIWEI NVARCHAR(10),@BUMEN NVARCHAR(8)
 3 /*利用全局变量求取当前数据表中的数据行数和*/
 4 PRINT'当前数据表中有'+CAST(@@CURSOR_ROWS AS NVARCHAR(6))+'行数据。'
 5 /*读取第一条数据并存入暂存变量中*/
 6 FETCH FIRST FROM[CUR_T_员工信息]INTO @XINGMING,@GONGHAO,@ZHIWEI,@BUMEN
 7 /*利用@@FETCH_STATUS返回的数值来确定cursor在数据表中读取的情况=0为fetch执行成功*/
 8 WHILE(@@FETCH_STATUS =0)
 9 BEGIN
10 PRINT'姓名:'+@XINGMING+' 工号:'+CONVERT(VARCHAR(3),@GONGHAO)+' 职位:'+@ZHIWEI+' 部门:'+@BUMEN
11 FETCH NEXT FROM[CUR_T_员工信息] INTO @XINGMING,@GONGHAO,@ZHIWEI,@BUMEN
12 END
13 GO

复制代码

复制代码

复制代码

F5:

1.4.用完后就关闭和释放:

1 /*关闭*/
2 CLOSE GLOBAL CUR_T_员工信息
3 /*释放内存*/
4 DEALLOCATE GLOBAL CUR_T_员工信息
5 GO

这样的话就实现了读取每条记录的功能,然后就可以用它来更新相关的数据条(所有的工号值+100):

复制代码

复制代码

复制代码

1 USE SQL_SYSTEM 
2 /*下面声明和打开update——cursor*/
3 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
4 BEGIN TRANSACTION TR_UPDATE_YG 
5 DECLARE CUR_T_YG  CURSOR FOR
6 SELECT YG.姓名 ,YG.工号 ,YG.职位 ,YG.部门  FROM[T_yuangongxinxi]AS YG FOR 
7 UPDATE 
8 OPEN GLOBAL CUR_T_YG
9 COMMIT TRANSACTION TR_UPDATE_YG

复制代码

复制代码

复制代码

UPDATE:

复制代码

复制代码

复制代码

1 PRINT'当前有'+CONVERT(VARCHAR(3),@@CURSOR_ROWS)+'条数据行。'
2 FETCH NEXT FROM[CUR_T_YG]
3 WHILE(@@FETCH_STATUS=0)
4 BEGIN
5 UPDATE[T_yuangongxinxi] 
6 SET[工号] =[工号]+100
7 WHERE CURRENT OF CUR_T_YG
8 FETCH NEXT FROM[CUR_T_YG] 
9 END

复制代码

复制代码

复制代码

SELECT:

关闭释放:

1 CLOSE GLOBAL CUR_T_YG
2 DEALLOCATE GLOBAL CUR_T_YG

 

 

三:PROCEDURE,存储过程是利用SQL Server所提供的Transact-SQL语言所编写的程序,同时也能在高级语言上调用其存储过程。 3.1.无参数:说一个调用查询数据表的pro:

复制代码

复制代码

复制代码

1 USE SQL_SYSTEM 
2 GO
3 CREATE PROCEDURE PRO_SELECT_T
4 WITH RECOMPILE 
5 AS
6 SELECT* FROM[T_yuangongxinxi] 
7 GO
8  EXECUTE:
9 EXECUTE PRO_SELECT_T

复制代码

复制代码

复制代码

F5:

3.1.2.EXECUTE PRO_SELECT_T在这里其实就是一个view,还可以把execute调用结果插入到新表:

 

复制代码

复制代码

复制代码

 1 USE SQL_SYSTEM 
 2 SELECT*
 3 INTO AA 
 4 FROM[T_yuangongxinxi] 
 5 GO
 6 TRUNCATE TABLE AA  --TRUNCATE与DELETE的区别就是TR效率高于DE,而且DE清空时会在日志里面留下恢复记录
 7 GO
 8 INSERT INTO AA
 9 EXECUTE PRO_SELECT_T
10 GO

复制代码

复制代码

复制代码

 

结果就不截图了, 3.2.带参pro,写个修改后输出修改的信息: 例如公司的员工经常有职位变动的情况,就写个这方面的,给出变动员工的工号就可以修改该员工的职位,然后将修改的操作反馈出来:

复制代码

复制代码

复制代码

 1 USE SQL_SYSTEM 
 2 GO
 3 CREATE PROCEDURE PRO_DAUP_ZHIWEI
 4 @GONGHAO INT,@ZHIWEI NVARCHAR(10),@RETURNS NVARCHAR(50) OUTPUT
 5 AS
 6 BEGIN
 7 /*获取更新前的职位信息*/
 8 DECLARE @QIAN_RETURN_ZHIWEI NVARCHAR(10),@XINGMING NVARCHAR(3)
 9 SELECT @QIAN_RETURN_ZHIWEI =AA.职位 ,@XINGMING =AA.姓名 
10 FROM[AA]
11 WHERE AA.工号 =@GONGHAO 
12 /*更新*/
13 UPDATE[AA] 
14 SET[职位] =@ZHIWEI WHERE AA.工号 =@GONGHAO 
15 SET @RETURNS = '已经成功将工号为:【'+CONVERT(VARCHAR(3),@GONGHAO)+'】,姓名为:【'+@XINGMING+'】,职位【'+
16 @QIAN_RETURN_ZHIWEI+'】更新为:【'+@ZHIWEI
17 END

复制代码

复制代码

复制代码

EXECUTE:

1 DECLARE @PRINTF NVARCHAR(50);
2 EXECUTE PRO_DAUP_ZHIWEI 101,'SQL工程师',@PRINTF OUTPUT 
3 SELECT @PRINTF AS '更新消息'
4 GO

F5:

PRO就说到这里了;
四:TRIGGER,触发器,类似于一个地雷一样,只要你触犯了他的要求的话,他就开始工作了,也可以利用这个功能去维持或阻挡某些不想发生的错误操作, DDL:如:不许删除某个【AA】表:

复制代码

复制代码

复制代码

1 USE SQL_SYSTEM 
2 GO
3 CREATE TRIGGER DROP_T
4 ON DATABASE FOR DROP_TABLE 
5 AS
6 BEGIN
7 ROLLBACK TRANSACTION 
8 PRINT'不能删除的,因为我已经添加了触发保护!'
9 END

复制代码

复制代码

复制代码

drop:

1 DROP TABLE AA 

F5:

DML,是解决对最低层数据的问题: 在这里就存在临时的2个表:deleted 和 inserted 逻辑(概念)表,要搞明白就要知道trigger的工作原理: insert       操作时:

 

 

delete    操作时:

update   操作时:

简单举个例子来说下update的操作:也就是重点看inserted和deleted表上:

复制代码

复制代码

复制代码

 1 USE [SQL_SYSTEM]
 2 GO
 3 /****** Object:  Trigger [dbo].[UPDATE_T]    Script Date: 03/04/2014 16:04:21 ******/
 4 SET ANSI_NULLS ON
 5 GO
 6 SET QUOTED_IDENTIFIER ON
 7 GO
 8 CREATE TRIGGER [dbo].[UPDATE_T]
 9 ON [dbo].[AA] INSTEAD OF UPDATE
10 AS
11 BEGIN
12 INSERT INTO T_UPDATE_HOU
13 SELECT* 
14 FROM[inserted] 
15 /*将更新成的数据插入到【T_UPDATE_HOU】中*/
16 INSERT INTO  T_UPDATE_QIAN
17 SELECT*
18 FROM[deleted] 
19 /*将更新前的数据插入到【T_UPDATE_QIAN】中*/
20 PRINT'更新完毕,更新前的数据已经写入到【T_UPDATE_QIAN】,更新后的数据插入到【T_UPDATE_HOU】。'
21 END

复制代码

复制代码

复制代码

update:

复制代码

复制代码

复制代码

1 USE SQL_SYSTEM 
2 GO
3 UPDATE[AA] 
4 SET AA.职位 ='SQL高级工程师'
5 WHERE AA.工号=101
6 /*以上是个简单的更新操作,用于update触发*/
7 SELECT* FROM[T_UPDATE_HOU] --修改成的数据
8 SELECT* FROM[T_UPDATE_QIAN] --待修改的数据
9 GO

复制代码

复制代码

复制代码

F5:

在去年的的时候我写了一个利用trigger对多表连接的view进行更新的操作:

http://www.cnblogs.com/liyifeng/archive/2013/05/05/3056968.html

--计算当前月的实际天数 Create FUNCTION dbo.CalcDaysOfMonth (@time varchar(6)) RETURNS int AS BEGIN DECLARE @Days int DECLARE @Month int DECLARE @Year int SET @Year=SUBSTRING(@time,1,4) SET @Month=SUBSTRING(@time,5,6) if( @Month='1' OR @Month='3' OR @Month='5' OR @Month='7' OR @Month='8' OR @Month='10' OR @Month='12' ) set @Days=31 else if( @Month='4' OR @Month='6' OR @Month='9' OR @Month='11' ) set @Days=30; else if(@Year%400=0 OR (@Year%4=0 AND @Year%1000)) set @Days=29 else set @Days=28 RETURN(@Days) END --确定某年某月有多少天 Create FUNCTION DaysInMonth ( @date datetime ) Returns int AS BEGIN RETURN Day(dateadd(mi,-3,DATEADD(m, DATEDIFF(m,0,@date)+1,0))) END --哪一天是输入时间的星期一 Create FUNCTION MondayInDate ( @date datetime ) RETURNS DATETIME AS BEGIN RETURN DATEADD(week, DATEDIFF(week,0,@date),0) END --输入时间的季度的第一天 Create FUNCTION QuarterInDate ( @date datetime ) RETURNS DATETIME AS BEGIN RETURN DATEADD(quarter, DATEDIFF(quarter,0,@date), 0) END --输入时间的季度的天数 Create FUNCTION QuarterDaysInDate ( @date datetime ) RETURNS INT AS BEGIN declare @m tinyint,@time SMALLDATETIME select @m=month(@date) select @m=case when @m between 1 and 3 then 1 when @m between 4 and 6 then 4 when @m between 7 and 9 then 7 else 10 end select @time=datename(year,@date)+'-'+convert(varchar(10),@m)+'-01' return datediff(day,@time,dateadd(mm,3,@time)) END --按指定符号分割字符串,返回分割后的元素个数,方法很简单,就是看字符串中存在多少个分隔符号,然后再加一,就是要求的结果。 Create function Get_StrArrayLength ( @str varchar(1024), --要分割的字符串 @split varchar(10) --分隔符号 ) returns int as begin declare @location int declare @start int declare @length int set @str=ltrim(rtrim(@str)) set @location=charindex(@split,@str) set @length=1 while @location0 begin set @start=@location+1 set @location=charindex(@split,@str,@start) set @length=@length+1 end return @length END --按指定符号分割字符串,返回分割后指定索引的第几个元素,象数组一样方便 Create function Get_StrArrayStrOfIndex ( @str varchar(1024), --要分割的字符串 @split varchar(10), --分隔符号 @index int --取第几个元素 ) returns varchar(1024) as begin declare @location int declare @start int declare @next int declare @seed int set @str=ltrim(rtrim(@str)) set @start=1 set @next=1 set @seed=len(@split) set @location=charindex(@split,@str) while @location0 and @index>@next begin set @start=@location+@seed set @location=charindex(@split,@str,@start) set @next=@next+1 end if @location =0 select @location =len(@str)+1 --这儿存在两种情况:1、字符串不存在分隔符号 2、字符串中存在分隔符号,跳出while循环后,@location为0,那默认为字符串后边有一个分隔符号。 return substring(@str,@start,@location-@start) END select dbo.Get_StrArrayStrOfIndex('8,9,4','',4) --结合上边两个函数,象数组一样遍历字符串中的元素 create function f_splitstr(@SourceSql varchar(8000),@StrSeprate varchar(100)) returns @temp table(F1 varchar(100)) as begin declare @ch as varchar(100) set @SourceSql=@SourceSql+@StrSeprate while(@SourceSql'') begin set @ch=left(@SourceSql,charindex(',',@SourceSql,1)-1) insert @temp values(@ch) set @SourceSql=stuff(@SourceSql,1,charindex(',',@SourceSql,1),'') end return end select * from dbo.f_splitstr('1,2,3,4',',') --全角和半角转换函数 Create FUNCTION f_Convert( @str NVARCHAR(4000), --要转换的字符串 @flag bit --转换标志,0转换成半角,1转换成全角 )RETURNS nvarchar(4000) AS BEGIN DECLARE @pat nvarchar(8),@step int,@i int,@spc int IF @flag=0 Select @pat=N'%[!-~]%',@step=-65248, @str=REPLACE(@str,N' ',N' ') ELSE Select @pat=N'%[!-~]%',@step=65248, @str=REPLACE(@str,N' ',N' ') SET @i=PATINDEX(@pat COLLATE LATIN1_GENERAL_BIN,@str) WHILE @i>0 Select @str=REPLACE(@str, SUBSTRING(@str,@i,1), NCHAR(UNICODE(SUBSTRING(@str,@i,1))+@step)) ,@i=PATINDEX(@pat COLLATE LATIN1_GENERAL_BIN,@str) RETURN(@str) END GO declare @s1 varchar(8000) select @s1='中  2-3456a78STUVabn中国opwxyz' select dbo.f_convert(@s1,0),dbo.f_convert(@s1,1) 函数返回值是表 create table test(id int primary key,name char(10)) insert into test values(1,'test1') insert into test values(2,'test2') insert into test values(3,'test3') insert into test values(4,'test4') 1、标量函数 create function return_count() returns int as begin declare @count int select @count=count(*) from test return @count end --调用 select dbo.return_count() cont --count为显示的列头 --运行结果 --count --4 2、内嵌表值函数 create function return_test() returns table as --begin 内联表值函数不能用begin-end return select name from test --end --调用 select * from return_test() --运行结果 --name --test1 --test2 --test3 --test4 3、多语句表值函数 create function return_test_multi() returns @temp table(id int,name char(10)) as begin insert into @temp select * from test where id in(1,2) return --记住,一定不要忘记写return end --调用 select * from dbo.return_test_multi() --运行结果 --id name --1 test1 --2 test2 在查询结果中增加一个自动增长的ID select id=identity(int, 1, 1), * into #T from testTable select * from #T drop table #T sql删除重复的记录 打开测试数据库test,并以表w01为例,将下面的SQL语句放入sql2000查询分析器中,一段一段执行即可看到效果 ---在sql2000下创建测试数据表 if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[w01]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[w01] ---在sql2005下创建测试数据表,如果是sql2005则用本段来判断数据表是否存在 ---if exists(select 1 from sys.tables where name='w01') ---drop table w01 ----开始创建测试数据库 GO create table w01(gs903 varchar(32),gs1002 varchar(32)) insert into w01 select '1','a' union all select '1','a' union all select '1','a' union all select '2','a' union all select '2','e' union all select '3','b' union all select '3','d' go select * from w01 go ---为表w01添加一个可以表示唯一标示的自增字段ID alter table w01 add [ID] [int] IDENTITY (1, 1) NOT FOR REPLICATION NOT NULL ---查询删除前的数据和记录数:7 select * from w01 select count(*) from w01 ---查询具有重复记录的所有记录;3 select gs903,gs1002,count(*) as count from w01 group by gs903,gs1002 having count(*)>1 order by count desc ---删除重复的数据:2行 delete from w01 where id not in (select max(id) from w01 group by gs903,gs1002) ---看看删除后还有没有重复记录:0 select gs903,gs1002,count(*) as count from w01 group by gs903,gs1002 having count(*)>1 order by count desc ---删除后的数据和记录数:7-2=5 select * from w01 select count(*) from w01 用SQL语句添加删除修改字段 增加字段 alter table docdsp add dspcode char(200) 删除字段 Alter TABLE table_NAME Drop COLUMN column_NAME 修改字段类型 Alter TABLE table_name Alter COLUMN column_name new_data_type 改名 sp_rename 更改当前数据库中用户创建对象(如表、列或用户定义数据类型)的名称。 语法 sp_rename [ @objname = ] 'object_name' , [ @newname = ] 'new_name' [ , [ @objtype = ] 'object_type' ] --假设要处理的表名为: tb --判断要添加列的表中是否有主键 if exists(select 1 from sysobjects where parent_obj=object_id('tb') and xtype='PK') begin print '表中已经有主键,列只能做为普通列添加' --添加int类型的列,默认值为0 alter table tb add 列名 int default 0 end else begin print '表中无主键,添加主键列' --添加int类型的列,默认值为0 alter table tb add 列名 int primary key default 0 end
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值