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 int
AS
BEGIN
    DECLARE @workday int,@i int,@bz bit,@dt datetime
    IF @dt_begin>@dt_end
        SELECT @bz=1,@dt=@dt_begin,@dt_begin=@dt_end,@dt_end=@dt
    ELSE
        SET @bz=0
    SELECT @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_end
    BEGIN
        SELECT @workday=CASE 
            WHEN (@@DATEFIRST+DATEPART(Weekday,@dt_begin)-1)%7 BETWEEN 1 AND 5
            THEN @workday+1 ELSE @workday END,
            @dt_begin=@dt_begin+1
    END
    RETURN(CASE WHEN @bz=1 THEN -@workday ELSE @workday END)
END
GO



/*=================================================================*/

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 datetime
AS
BEGIN
    DECLARE @bz int
    --增加整周的天数
    SELECT @bz=CASE WHEN @workday<0 THEN -1 ELSE 1 END
        ,@date=DATEADD(Week,@workday/5,@date)
        ,@workday=@workday%5
    --增加不是整周的工作天数
    WHILE @workday<>0 
        SELECT @date=DATEADD(Day,@bz,@date),
            @workday=CASE WHEN (@@DATEFIRST+DATEPART(Weekday,@date)-1)%7 BETWEEN 1 AND 5
                THEN @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)             --假日名称
GO

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 int
AS
BEGIN
    IF @dt_begin>@dt_end
        RETURN(DATEDIFF(Day,@dt_begin,@dt_end)
            +1-(
                SELECT COUNT(*) FROM tb_Holiday
                WHERE HDate BETWEEN @dt_begin AND @dt_end))
    RETURN(-(DATEDIFF(Day,@dt_end,@dt_begin)
        +1-(
            SELECT COUNT(*) FROM tb_Holiday
            WHERE HDate BETWEEN @dt_end AND @dt_begin)))
END
GO

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 datetime
AS
BEGIN
    IF @workday>0
        WHILE @workday>0
            SELECT @date=@date+@workday,@workday=count(*)
            FROM tb_Holiday
            WHERE HDate BETWEEN @date AND @date+@workday
    ELSE
        WHILE @workday<0
            SELECT @date=@date+@workday,@workday=-count(*)
            FROM tb_Holiday
            WHERE HDate BETWEEN @date AND @date+@workday
    RETURN(@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)  --工作时数(分钟)
)
GO

if 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 int
AS
BEGIN
    DECLARE @worktime int
    IF DATEDIFF(Day,@date_begin,@date_end)=0
        SELECT @worktime=SUM(DATEDIFF(Minute,
            CASE WHEN CONVERT(VARCHAR,@date_begin,108)>time_start
                THEN CONVERT(VARCHAR,@date_begin,108)
                ELSE time_start END,
            CASE WHEN CONVERT(VARCHAR,@date_end,108)<time_end
                THEN CONVERT(VARCHAR,@date_end,108)
                ELSE time_end END))
        FROM tb_worktime 
        WHERE time_end>CONVERT(VARCHAR,@date_begin,108)
            AND time_start<CONVERT(VARCHAR,@date_end,108)
    ELSE
        SET @worktime
            =(SELECT SUM(CASE
                    WHEN CONVERT(VARCHAR,@date_begin,108)>time_start
                    THEN DATEDIFF(Minute,CONVERT(VARCHAR,@date_begin,108),time_end)
                    ELSE worktime END)
                FROM tb_worktime 
                WHERE time_end>CONVERT(VARCHAR,@date_begin,108))
            +(SELECT SUM(CASE 
                    WHEN CONVERT(VARCHAR,@date_end,108)<time_end
                    THEN DATEDIFF(Minute,time_start,CONVERT(VARCHAR,@date_end,108))
                    ELSE worktime END)
                FROM tb_worktime 
                WHERE time_start<CONVERT(VARCHAR,@date_end,108))
            +CASE 
                WHEN DATEDIFF(Day,@date_begin,@date_end)>1 
                THEN (DATEDIFF(Day,@date_begin,@date_end)-1)
                    *(SELECT SUM(worktime) FROM tb_worktime)
                ELSE 0 END
    RETURN(@worktime)
END

 

16.复杂年月处理

--定义基本数字表
declare @T1 table(代码 int,名称 varchar(10),参加时间 datetime,终止时间 datetime)
insert into @T1
    select 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 @NB
    select 12,'单位1',2003
    union all select 12,'单位1',2004
    union all select 22,'单位2',2001
    union all select 22,'单位2',2002
    union all select 22,'单位2',2003

--定义月表
declare @YB table(代码 int,名称 varchar(10),年份 int,月份 varchar(2))
insert into @YB
    select 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.y
from @T1 a,#tby b
where 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 c
where 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 null
order 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 all
    select id=2,a='20:00',b='23:59',Time='[9pm - 1am)' union all
    select id=3,a='00:00',b='02:59',Time='[1am - 4am)' union all
    select id=4,a='03:00',b='07:29',Time='[4am - 8:30am)' union all
    select id=5,a='07:30',b='11:59',Time='[8:30am - 1pm)' union all
    select id=6,a='12:00',b='15:59',Time='[1pm - 5pm)'
)b on a.Time>=b.a and a.Time<b.b
group by b.id,b.Time with rollup
having grouping(b.Time)=0 or grouping(b.id)=1
go

--删除测试
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     2
Total          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 table
as
return(
    select 跨周数
        ,周一=case a
            when -1 then case when 1 between b and c then 1 else 0 end
            when  0 then case when b<=1 then 1 else 0 end
                    +case when c>=1 then 1 else 0 end
            else a+case when b<=1 then 1 else 0 end
                +case when c>=1 then 1 else 0 end
            end
        ,周二=case a
            when -1 then case when 2 between b and c then 1 else 0 end
            when  0 then case when b<=2 then 1 else 0 end
                    +case when c>=2 then 1 else 0 end
            else a+case when b<=2 then 1 else 0 end
                +case when c>=2 then 1 else 0 end
            end
        ,周三=case a
            when -1 then case when 3 between b and c then 1 else 0 end
            when  0 then case when b<=3 then 1 else 0 end
                    +case when c>=3 then 1 else 0 end
            else a+case when b<=3 then 1 else 0 end
                +case when c>=3 then 1 else 0 end
            end
        ,周四=case a
            when -1 then case when 4 between b and c then 1 else 0 end
            when  0 then case when b<=4 then 1 else 0 end
                    +case when c>=4 then 1 else 0 end
            else a+case when b<=4 then 1 else 0 end
                +case when c>=4 then 1 else 0 end
            end
        ,周五=case a
            when -1 then case when 5 between b and c then 1 else 0 end
            when  0 then case when b<=5 then 1 else 0 end
                    +case when c>=5 then 1 else 0 end
            else a+case when b<=5 then 1 else 0 end
                +case when c>=5 then 1 else 0 end
            end
        ,周六=case a
            when -1 then case when 6 between b and c then 1 else 0 end
            when  0 then case when b<=6 then 1 else 0 end
                    +case when c>=6 then 1 else 0 end
            else a+case when b<=6 then 1 else 0 end
                +case when c>=6 then 1 else 0 end
            end
        ,周日=case a
            when -1 then case when 0 between b and c then 1 else 0 end
            when  0 then case when b<=0 then 1 else 0 end
                    +case when c>=0 then 1 else 0 end
            else a+case when b<=0 then 1 else 0 end
                +case when c>=0 then 1 else 0 end
            end
    from(
        select 跨周数=case when @dt_begin<@dt_end
                then (datediff(day,@dt_begin,@dt_end)+7)/7
                else (datediff(day,@dt_end,@dt_begin)+7)/7 end
            ,a=case when @dt_begin<@dt_end
                then datediff(week,@dt_begin,@dt_end)-1
                else datediff(week,@dt_end,@dt_begin)-1 end
            ,b=case when @dt_begin<@dt_end
                then (@@datefirst+datepart(weekday,@dt_begin)-1)%7
                else (@@datefirst+datepart(weekday,@dt_end)-1)%7 end
            ,c=case when @dt_begin<@dt_end
                then (@@datefirst+datepart(weekday,@dt_end)-1)%7
                else (@@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       ,1
union all select 1,'A1','未订','09:00','10:00',1   ,5   ,null       ,null       ,1
union all select 1,'A1','未订','10:00','11:00',1   ,5   ,null       ,null       ,1
union 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',2
union all select 1,'A2','未订','08:00','09:00',1   ,5   ,null       ,null       ,1
union all select 1,'A2','未订','09:00','10:00',1   ,5   ,null       ,null       ,1
union 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 --要查询的日期
as
set nocount on
declare @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 #t
from(
    select top 100 percent
        qid,rid,tagname,
        starttime=convert(char(5),starttime,108),
        endtime=convert(char(5),endtime,108)
    from tb
    where (@week between startweekday and endweekday)
        or(@date between startdate and enddate)
    order by qid,rid,starttime,d desc)a

select @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,rid
exec(' 
select starttime,endtime'+@s+' 
from #t a
where not exists(
    select * from #t
    where qid=a.qid and rid=a.rid 
        and starttime=a.starttime
        and endtime=a.endtime
        and id<a.id)
group by starttime,endtime')
go

--调用
exec p_qry '2005-1-17'
exec p_qry '2005-1-18'
go

--删除测试
drop table tb
drop 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))
AS
BEGIN
    DECLARE @splitlen int
    SET @splitlen=LEN(@split+'a')-2
    WHILE CHARINDEX(@split,@s)>0
    BEGIN
        INSERT @re VALUES(LEFT(@s,CHARINDEX(@split,@s)-1))
        SET @s=STUFF(@s,1,CHARINDEX(@split,@s)+@splitlen,' ')
    END
    INSERT @re VALUES(@s)
    RETURN
END
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.3.1 使用临时性分拆辅助表法
CREATE FUNCTION f_splitSTR(
@s   varchar(8000),  --待分拆的字符串
@split varchar(10)     --数据分隔符
)RETURNS @re TABLE(col varchar(100))
AS
BEGIN
    --创建分拆处理的辅助表(用户定义函数中只能操作表变量)
    DECLARE @t TABLE(ID int IDENTITY,b bit)
    INSERT @t(b) SELECT TOP 8000 0 FROM syscolumns a,syscolumns b

    INSERT @re SELECT SUBSTRING(@s,ID,CHARINDEX(@split,@s+@split,ID)-ID)
    FROM @t
    WHERE ID<=LEN(@s+'a') 
        AND CHARINDEX(@split,@split+@s,ID)=ID
    RETURN
END
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

if 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_splitSTR
FROM syscolumns a,syscolumns b
GO

--字符串分拆处理函数
CREATE FUNCTION f_splitSTR(
@s     varchar(8000),  --待分拆的字符串
@split  varchar(10)     --数据分隔符
)RETURNS TABLE
AS
RETURN(
    SELECT col=CAST(SUBSTRING(@s,ID,CHARINDEX(@split,@s+@split,ID)-ID) as varchar(100))
    FROM tb_splitSTR
    WHERE 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))
AS
BEGIN
    --创建分拆处理的辅助表(用户定义函数中只能操作表变量)
    DECLARE @t TABLE(ID int IDENTITY,b bit)
    INSERT @t(b) SELECT TOP 8000 0 FROM syscolumns a,syscolumns b

    INSERT @re 
    SELECT    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 @t
        WHERE ID<=LEN(@s+'a') 
            AND CHARINDEX(@split,@split+@s,ID)=ID)a
    RETURN
END
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
posted @ 2010-09-05 20:30  梦想(胡大利)  阅读(243)  评论(0编辑  收藏  举报