sql大全(二)
sql大全(二)
承接sql大全(一)
总纲:
13. 工作日处理函数(标准节假日)
14. 工作日处理函数(自定义节假日)
15. 计算工作时间的函数
16. 复杂年月处理
17. 交叉表
18. 任意两个时间之间的星期几的次数
19. 统计--交叉表+日期+优先
20. 各种字符串分拆处理函数
21. 各种字符串合并处理示例
13.工作日处理函数(标准节假日)
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[f_WorkDay]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[f_WorkDay]GO--计算两个日期相差的工作天数
CREATE FUNCTION f_WorkDay(@dt_begin datetime, --计算的开始日期@dt_end datetime --计算的结束日期)RETURNS intASBEGINDECLARE @workday int,@i int,@bz bit,@dt datetimeIF @dt_begin>@dt_endSELECT @bz=1,@dt=@dt_begin,@dt_begin=@dt_end,@dt_end=@dtELSESET @bz=0SELECT @i=DATEDIFF(Day,@dt_begin,@dt_end)+1,@workday=@i/7*5,@dt_begin=DATEADD(Day,@i/7*7,@dt_begin)WHILE @dt_begin<=@dt_endBEGINSELECT @workday=CASEWHEN (@@DATEFIRST+DATEPART(Weekday,@dt_begin)-1)%7 BETWEEN 1 AND 5THEN @workday+1 ELSE @workday END,@dt_begin=@dt_begin+1ENDRETURN(CASE WHEN @bz=1 THEN -@workday ELSE @workday END)ENDGO/*=================================================================*/
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[f_WorkDayADD]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[f_WorkDayADD]GO--在指定日期上,增加指定工作天数后的日期
CREATE FUNCTION f_WorkDayADD(@date datetime, --基础日期@workday int --要增加的工作日数)RETURNS datetimeASBEGINDECLARE @bz int--增加整周的天数
SELECT @bz=CASE WHEN @workday<0 THEN -1 ELSE 1 END,@date=DATEADD(Week,@workday/5,@date),@workday=@workday%5--增加不是整周的工作天数
WHILE @workday<>0SELECT @date=DATEADD(Day,@bz,@date),@workday=CASE WHEN (@@DATEFIRST+DATEPART(Weekday,@date)-1)%7 BETWEEN 1 AND 5THEN @workday-@bz ELSE @workday END--避免处理后的日期停留在非工作日上
WHILE (@@DATEFIRST+DATEPART(Weekday,@date)-1)%7 in(0,6)SET @date=DATEADD(Day,@bz,@date)RETURN(@date)END
14.工作日处理函数(自定义节假日)
if exists (select * from dbo.sysobjects where id = object_id(N'[tb_Holiday]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)drop table [tb_Holiday]GO--定义节假日表
CREATE TABLE tb_Holiday(HDate smalldatetime primary key clustered, --节假日期Name nvarchar(50) not null) --假日名称GOif exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[f_WorkDay]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[f_WorkDay]GO--计算两个日期之间的工作天数
CREATE FUNCTION f_WorkDay(@dt_begin datetime, --计算的开始日期@dt_end datetime --计算的结束日期)RETURNS intASBEGINIF @dt_begin>@dt_endRETURN(DATEDIFF(Day,@dt_begin,@dt_end)+1-(SELECT COUNT(*) FROM tb_HolidayWHERE HDate BETWEEN @dt_begin AND @dt_end))RETURN(-(DATEDIFF(Day,@dt_end,@dt_begin)+1-(SELECT COUNT(*) FROM tb_HolidayWHERE HDate BETWEEN @dt_end AND @dt_begin)))ENDGOif exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[f_WorkDayADD]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[f_WorkDayADD]GO--在指定日期上增加工作天数
CREATE FUNCTION f_WorkDayADD(@date datetime, --基础日期@workday int --要增加的工作日数)RETURNS datetimeASBEGINIF @workday>0WHILE @workday>0SELECT @date=@date+@workday,@workday=count(*)FROM tb_HolidayWHERE HDate BETWEEN @date AND @date+@workdayELSEWHILE @workday<0SELECT @date=@date+@workday,@workday=-count(*)FROM tb_HolidayWHERE HDate BETWEEN @date AND @date+@workdayRETURN(@date)END
15.计算工作时间的函数
if exists (select * from dbo.sysobjects where id = object_id(N'[tb_worktime]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)drop table [tb_worktime]GO--定义工作时间表
CREATE TABLE tb_worktime(ID int identity(1,1) PRIMARY KEY, --序号time_start smalldatetime, --工作的开始时间time_end smalldatetime, --工作的结束时间worktime AS DATEDIFF(Minute,time_start,time_end) --工作时数(分钟))GOif exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[f_WorkTime]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[f_WorkTime]GO--计算两个日期之间的工作时间
CREATE FUNCTION f_WorkTime(@date_begin datetime, --计算的开始时间@date_end datetime --计算的结束时间)RETURNS intASBEGINDECLARE @worktime intIF DATEDIFF(Day,@date_begin,@date_end)=0SELECT @worktime=SUM(DATEDIFF(Minute,CASE WHEN CONVERT(VARCHAR,@date_begin,108)>time_startTHEN CONVERT(VARCHAR,@date_begin,108)ELSE time_start END,CASE WHEN CONVERT(VARCHAR,@date_end,108)<time_endTHEN CONVERT(VARCHAR,@date_end,108)ELSE time_end END))FROM tb_worktimeWHERE time_end>CONVERT(VARCHAR,@date_begin,108)AND time_start<CONVERT(VARCHAR,@date_end,108)ELSESET @worktime=(SELECT SUM(CASEWHEN CONVERT(VARCHAR,@date_begin,108)>time_startTHEN DATEDIFF(Minute,CONVERT(VARCHAR,@date_begin,108),time_end)ELSE worktime END)FROM tb_worktimeWHERE time_end>CONVERT(VARCHAR,@date_begin,108))+(SELECT SUM(CASEWHEN CONVERT(VARCHAR,@date_end,108)<time_endTHEN DATEDIFF(Minute,time_start,CONVERT(VARCHAR,@date_end,108))ELSE worktime END)FROM tb_worktimeWHERE time_start<CONVERT(VARCHAR,@date_end,108))+CASEWHEN DATEDIFF(Day,@date_begin,@date_end)>1THEN (DATEDIFF(Day,@date_begin,@date_end)-1)*(SELECT SUM(worktime) FROM tb_worktime)ELSE 0 ENDRETURN(@worktime)END
16.复杂年月处理
--定义基本数字表
declare @T1 table(代码 int,名称 varchar(10),参加时间 datetime,终止时间 datetime)insert into @T1select 12,'单位1','2003/04/01','2004/05/01'union all select 22,'单位2','2001/02/01','2003/02/01'union all select 42,'单位3','2000/04/01','2003/05/01'union all select 25,'单位5','2003/04/01','2003/05/01'--定义年表
declare @NB table(代码 int,名称 varchar(10),年份 int)insert into @NBselect 12,'单位1',2003union all select 12,'单位1',2004union all select 22,'单位2',2001union all select 22,'单位2',2002union all select 22,'单位2',2003--定义月表
declare @YB table(代码 int,名称 varchar(10),年份 int,月份 varchar(2))insert into @YBselect 12,'单位1',2003,'04'union all select 22,'单位2',2001,'01'union all select 22,'单位2',2001,'12'--为年表+月表数据处理准备临时表
select top 8246 y=identity(int,1753,1)into #tby from(select id from syscolumns) a,(select id from syscolumns) b,(select id from syscolumns) c--为月表数据处理准备临时表
select top 12 m=identity(int,1,1)into #tbm from syscolumns/*--数据处理--*/
--年表数据处理
select a.*from(select a.代码,a.名称,年份=b.yfrom @T1 a,#tby bwhere b.y between year(参加时间) and year(终止时间)) a left join @NB b on a.代码=b.代码 and a.年份=b.年份where b.代码 is null--月表数据处理
select a.*from(select a.代码,a.名称,年份=b.y,月份=right('00'+cast(c.m as varchar),2)from @T1 a,#tby b,#tbm cwhere b.y*100+c.m between convert(varchar(6),参加时间,112)and convert(varchar(6),终止时间,112)) a left join @YB b on a.代码=b.代码 and a.年份=b.年份 and a.月份=b.月份where b.代码 is nullorder by a.代码,a.名称,a.年份,a.月份--删除数据处理临时表
drop table #tby,#tbm
17.交叉表
--示例
--示例数据
create table tb(ID int,Time datetime)insert tb select 1,'2005/01/24 16:20'union all select 2,'2005/01/23 22:45'union all select 3,'2005/01/23 0:30'union all select 4,'2005/01/21 4:28'union all select 5,'2005/01/20 13:22'union all select 6,'2005/01/19 20:30'union all select 7,'2005/01/19 18:23'union all select 8,'2005/01/18 9:14'union all select 9,'2005/01/18 18:04'go--查询处理:
select case when grouping(b.Time)=1 then 'Total' else b.Time end,[Mon]=sum(case a.week when 1 then 1 else 0 end),[Tue]=sum(case a.week when 2 then 1 else 0 end),[Wed]=sum(case a.week when 3 then 1 else 0 end),[Thu]=sum(case a.week when 4 then 1 else 0 end),[Fri]=sum(case a.week when 5 then 1 else 0 end),[Sat]=sum(case a.week when 6 then 1 else 0 end),[Sun]=sum(case a.week when 0 then 1 else 0 end),[Total]=count(a.week)from(select Time=convert(char(5),dateadd(hour,-1,Time),108)--时间交界点是1am,所以减1小时,避免进行跨天处理
,week=(@@datefirst+datepart(weekday,Time)-1)%7--考虑@@datefirst对datepart的影响
from tb)a right join(select id=1,a='16:00',b='19:59',Time='[5pm - 9pm)' union allselect id=2,a='20:00',b='23:59',Time='[9pm - 1am)' union allselect id=3,a='00:00',b='02:59',Time='[1am - 4am)' union allselect id=4,a='03:00',b='07:29',Time='[4am - 8:30am)' union allselect id=5,a='07:30',b='11:59',Time='[8:30am - 1pm)' union allselect id=6,a='12:00',b='15:59',Time='[1pm - 5pm)')b on a.Time>=b.a and a.Time<b.bgroup by b.id,b.Time with rolluphaving grouping(b.Time)=0 or grouping(b.id)=1go--删除测试
drop table tb/*--测试结果
Mon Tue Wed Thu Fri Sat Sun Total-------------- ----- ----- ----- ----- ----- ------ ---- -------[5pm - 9pm) 0 1 2 0 0 0 0 3[9pm - 1am) 0 0 0 0 0 0 2 2[1am - 4am) 0 0 0 0 0 0 0 0[4am - 8:30am) 0 0 0 0 1 0 0 1[8:30am - 1pm) 0 1 0 0 0 0 0 1[1pm - 5pm) 1 0 0 1 0 0 0 2Total 1 2 2 1 1 0 2 9(所影响的行数为 7 行)--*/
18.任意两个时间之间的星期几的次数
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[f_weekdaycount]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[f_weekdaycount]GO/*--计算任意两个时间之间的星期几的次数(横向显示)
本方法直接判断 @@datefirst 做对应处理不受 sp_language 及 set datefirst 的影响--邹建 2004.08(引用请保留此信息)--*//*--调用示例
select * from f_weekdaycount('2004-9-01','2004-9-02')--*/create function f_weekdaycount(@dt_begin datetime,@dt_end datetime)returns tableasreturn(select 跨周数,周一=case awhen -1 then case when 1 between b and c then 1 else 0 endwhen 0 then case when b<=1 then 1 else 0 end+case when c>=1 then 1 else 0 endelse a+case when b<=1 then 1 else 0 end+case when c>=1 then 1 else 0 endend,周二=case awhen -1 then case when 2 between b and c then 1 else 0 endwhen 0 then case when b<=2 then 1 else 0 end+case when c>=2 then 1 else 0 endelse a+case when b<=2 then 1 else 0 end+case when c>=2 then 1 else 0 endend,周三=case awhen -1 then case when 3 between b and c then 1 else 0 endwhen 0 then case when b<=3 then 1 else 0 end+case when c>=3 then 1 else 0 endelse a+case when b<=3 then 1 else 0 end+case when c>=3 then 1 else 0 endend,周四=case awhen -1 then case when 4 between b and c then 1 else 0 endwhen 0 then case when b<=4 then 1 else 0 end+case when c>=4 then 1 else 0 endelse a+case when b<=4 then 1 else 0 end+case when c>=4 then 1 else 0 endend,周五=case awhen -1 then case when 5 between b and c then 1 else 0 endwhen 0 then case when b<=5 then 1 else 0 end+case when c>=5 then 1 else 0 endelse a+case when b<=5 then 1 else 0 end+case when c>=5 then 1 else 0 endend,周六=case awhen -1 then case when 6 between b and c then 1 else 0 endwhen 0 then case when b<=6 then 1 else 0 end+case when c>=6 then 1 else 0 endelse a+case when b<=6 then 1 else 0 end+case when c>=6 then 1 else 0 endend,周日=case awhen -1 then case when 0 between b and c then 1 else 0 endwhen 0 then case when b<=0 then 1 else 0 end+case when c>=0 then 1 else 0 endelse a+case when b<=0 then 1 else 0 end+case when c>=0 then 1 else 0 endendfrom(select 跨周数=case when @dt_begin<@dt_endthen (datediff(day,@dt_begin,@dt_end)+7)/7else (datediff(day,@dt_end,@dt_begin)+7)/7 end,a=case when @dt_begin<@dt_endthen datediff(week,@dt_begin,@dt_end)-1else datediff(week,@dt_end,@dt_begin)-1 end,b=case when @dt_begin<@dt_endthen (@@datefirst+datepart(weekday,@dt_begin)-1)%7else (@@datefirst+datepart(weekday,@dt_end)-1)%7 end,c=case when @dt_begin<@dt_endthen (@@datefirst+datepart(weekday,@dt_end)-1)%7else (@@datefirst+datepart(weekday,@dt_begin)-1)%7 end)a)go
19.统计--交叉表+日期+优先
--交叉表,根据优先级取数据,日期处理
create table tb(qid int,rid nvarchar(4),tagname nvarchar(10),starttime smalldatetime,endtime smalldatetime,startweekday int,endweekday int,startdate smalldatetime,enddate smalldatetime,d int)insert tb select 1,'A1','未订','08:00','09:00',1 ,5 ,null ,null ,1union all select 1,'A1','未订','09:00','10:00',1 ,5 ,null ,null ,1union all select 1,'A1','未订','10:00','11:00',1 ,5 ,null ,null ,1union all select 1,'A1','装修','08:00','09:00',null,null,'2005-1-18','2005-1-19',2--union all select 1,'A1','装修','09:00','10:00',null,null,'2005-1-18','2005-1-19',2
union all select 1,'A1','装修','10:00','11:00',null,null,'2005-1-18','2005-1-19',2union all select 1,'A2','未订','08:00','09:00',1 ,5 ,null ,null ,1union all select 1,'A2','未订','09:00','10:00',1 ,5 ,null ,null ,1union all select 1,'A2','未订','10:00','11:00',1 ,5 ,null ,null ,1--union all select 1,'A2','装修','08:00','09:00',null,null,'2005-1-18','2005-1-19',2
union all select 1,'A2','装修','09:00','10:00',null,null,'2005-1-18','2005-1-19',2--union all select 1,'A2','装修','10:00','11:00',null,null,'2005-1-18','2005-1-19',2
go/*--楼主这个问题要考虑几个方面
1. 取星期时,set datefirst 的影响2. 优先级问题3. qid,rid 应该是未知的(动态变化的)--*/--实现的存储过程如下
create proc p_qry@date smalldatetime --要查询的日期asset nocount ondeclare @week int,@s nvarchar(4000)--格式化日期和得到星期
select @date=convert(char(10),@date,120),@week=(@@datefirst+datepart(weekday,@date)-1)%7,@s=' '
select id=identity(int),* into #tfrom(select top 100 percentqid,rid,tagname,starttime=convert(char(5),starttime,108),endtime=convert(char(5),endtime,108)from tbwhere (@week between startweekday and endweekday)or(@date between startdate and enddate)order by qid,rid,starttime,d desc)aselect @s=@s+N',['+rtrim(rid)+N']=max(case when qid='+rtrim(qid)
+N' and rid=N'''+rtrim(rid)
+N''' then tagname else N'''' end)'
from #t group by qid,ridexec('select starttime,endtime'+@s+'from #t awhere not exists(select * from #twhere qid=a.qid and rid=a.ridand starttime=a.starttimeand endtime=a.endtimeand id<a.id)group by starttime,endtime')go--调用
exec p_qry '2005-1-17'exec p_qry '2005-1-18'go--删除测试
drop table tbdrop proc p_qry/*--测试结果
starttime endtime A1 A2--------- ------- ---------- ----------08:00 09:00 未订 未订09:00 10:00 未订 未订10:00 11:00 未订 未订starttime endtime A1 A2--------- ------- ---------- ----------08:00 09:00 装修 未订09:00 10:00 未订 装修10:00 11:00 装修 未订--*/
20.各种字符串分拆处理函数
--各种字符串分函数
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[f_splitSTR]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[f_splitSTR]GO--3.2.1 循环截取法
CREATE FUNCTION f_splitSTR(@s varchar(8000), --待分拆的字符串@split varchar(10) --数据分隔符)RETURNS @re TABLE(col varchar(100))ASBEGINDECLARE @splitlen intSET @splitlen=LEN(@split+'a')-2WHILE CHARINDEX(@split,@s)>0BEGININSERT @re VALUES(LEFT(@s,CHARINDEX(@split,@s)-1))SET @s=STUFF(@s,1,CHARINDEX(@split,@s)+@splitlen,' ')ENDINSERT @re VALUES(@s)RETURNENDGO/*==============================================*/
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[f_splitSTR]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[f_splitSTR]GO--3.2.3.1 使用临时性分拆辅助表法
CREATE FUNCTION f_splitSTR(@s varchar(8000), --待分拆的字符串@split varchar(10) --数据分隔符)RETURNS @re TABLE(col varchar(100))ASBEGIN--创建分拆处理的辅助表(用户定义函数中只能操作表变量)
DECLARE @t TABLE(ID int IDENTITY,b bit)INSERT @t(b) SELECT TOP 8000 0 FROM syscolumns a,syscolumns bINSERT @re SELECT SUBSTRING(@s,ID,CHARINDEX(@split,@s+@split,ID)-ID)FROM @tWHERE ID<=LEN(@s+'a')AND CHARINDEX(@split,@split+@s,ID)=IDRETURNENDGO/*==============================================*/
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[f_splitSTR]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[f_splitSTR]GOif exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[tb_splitSTR]') and objectproperty(id,N'IsUserTable')=1)drop table [dbo].[tb_splitSTR]GO--3.2.3.2 使用永久性分拆辅助表法
--字符串分拆辅助表
SELECT TOP 8000 ID=IDENTITY(int,1,1) INTO dbo.tb_splitSTRFROM syscolumns a,syscolumns bGO--字符串分拆处理函数
CREATE FUNCTION f_splitSTR(@s varchar(8000), --待分拆的字符串@split varchar(10) --数据分隔符)RETURNS TABLEASRETURN(SELECT col=CAST(SUBSTRING(@s,ID,CHARINDEX(@split,@s+@split,ID)-ID) as varchar(100))FROM tb_splitSTRWHERE ID<=LEN(@s+'a')AND CHARINDEX(@split,@split+@s,ID)=ID)GO/*==============================================*/
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[f_splitSTR]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[f_splitSTR]GO--3.2.5 将数据项按数字与非数字再次拆份
CREATE FUNCTION f_splitSTR(@s varchar(8000), --待分拆的字符串@split varchar(10) --数据分隔符)RETURNS @re TABLE(No varchar(100),Value varchar(20))ASBEGIN--创建分拆处理的辅助表(用户定义函数中只能操作表变量)
DECLARE @t TABLE(ID int IDENTITY,b bit)INSERT @t(b) SELECT TOP 8000 0 FROM syscolumns a,syscolumns bINSERT @reSELECT No=REVERSE(STUFF(col,1,PATINDEX('%[^-^.^0-9]%',col+'a')-1,' ')),Value=REVERSE(LEFT(col,PATINDEX('%[^-^.^0-9]%',col+'a')-1))FROM(SELECT col=REVERSE(SUBSTRING(@s,ID,CHARINDEX(@split,@s+@split,ID)-ID))FROM @tWHERE ID<=LEN(@s+'a')AND CHARINDEX(@split,@split+@s,ID)=ID)aRETURNENDGO/*==============================================*/
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[f_splitSTR]') and xtype in (N'FN', N'IF', N'TF'))drop function