首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 .NET Java 游戏 视频 人才 外包 培训 数据库 书店 程序员
中国软件网
欢迎您:游客 | 登录 注册 帮助
  • 一道超难SQL函数,请SQL高手指点?
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-04-17 00:17:37 楼主
    表A(自定义日历表)
    [id] [int] NOT NULL ,
    [F_ID] [int] NOT NULL ,
    [IDType] [varchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL ,
    [WeekEndType] [int] NULL ,--周末假日类型(位)1=周六上午上班 2=周六下午上班4=周日上午上班 8=周日上午上班
    [WDayHours] [decimal](18, 1) NULL ,
    [WDayADD] [decimal](18, 1) NULL ,
    表B(明细表)
    [ID] [int] NOT NULL ,
    [FY_ID] [int] NOT NULL ,
    [DDate] [datetime] NULL ,
    [Dtype] [int] NULL ,--假日类型:1=该日期为假日,-1=该日期为工作日(便如黄金周,原来可能是周日的,但公司作了调整为------------上班天)如果是-1,会强制周末为工作日
    [DDesc] [varchar] (200) COLLATE Chinese_PRC_CI_AS NULL
    问题:
    写一个自定义函数计算两个日期差:
    本人已经写好了除周六日的(但如果考虑用户自定义假日表的应该如何处理。)
    现在已经完成功能(周六算0.5天,周日算1天)
    --这个函数只是计算当前日期为周的第几天(没太大的意义)
    Create  Function FuncDATE_GetWeekDay(@pDate DateTime )
    returns Int
    AS
    BEGIN
    Declare @RTN Int
    Declare @WKD Int
       
    Set @WKD=DatePart(W,@pDate)
    if(@WKD=1)
    Set @RTN=7
    else
    Set @RTN=@WKD-1
    Return @RTN
    END
    --算日期差的函数
    --@pram2 结束时间
    --@pram1 开始时间
    --@FID 表AID
    FUNCTION [dbo].[Func_GetDaysOfGongRi](@pram1 varchar(15),@pram2 varchar(15),@FID Int)
    RETURNS decimal(5, 1) AS
    BEGIN
    DECLARE @diffdate INT
    DECLARE @sn INT
    DECLARE @i INT
    DECLARE @ii INT
    DECLARE @RTN decimal(18,1)

    SET @diffdate=DATEDIFF(D,@pram1,@pram2)+1
    SET @sn=[dbo].[Func_GetDateOfweek](@pram1)
    SET @i=convert(int,(@diffdate)%7)
    SET @ii=convert(int,@diffdate/7)

    IF(@i=0)
    BEGIN
      SET @RTN=5.5*@ii
    END
    ELSE
    BEGIN
      SET @pram1=DATEADD(DAY,@ii*7,@pram1)
      SET @RTN=5.5*@ii
      WHILE(DATEDIFF(D,@pram1,@pram2)>=0)
      BEGIN
    IF([dbo].[Func_GetDateOfweek](@pram1)=7)
    BEGIN
        SET @RTN=@RTN
        END
    ELSE IF([dbo].[Func_GetDateOfweek](@pram1)=6)
    BEGIN
        SET @RTN=@RTN+0.5
    END
                    ELSE
    BEGIN
        SET @RTN=@RTN+1
    END
    SET @pram1=DATEADD(DAY,1,@pram1)  
              END
    END
    RETURN @RTN
    END

    问:应该如扣除表A,表B 里定义的日期,算出两日期差。谢谢!

    100  修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-04-17 01:20:391楼 得分:0
    SQL code
    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
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-04-17 07:33:282楼 得分:0
    SQL code
    create function WorkdayDiff(@d1 datetime, @d2 datetime, @id int) returns decimal(5,1) as begin if @d1>@d2 select @d1=@d1+@d2, @d2=@d1-@d2, @d1=@d1-@d2 declare @wet int, @wd int select @wet=WeekEndType from 表A where id=@id declare @T table (date datetime, wd int, workday decimal(5,1)) while @d1<=@d2 begin set @wd=(@@datefirst+datepart(weekday,@d1)-1)%7 insert @T select @d1, @wd, case @wd --Sunday when 0 then (sign(@wet&4)+sign(@wet&8))*0.5 --Saturday when 6 then case when @wet&3=3 then 1 else 0.5 end set @d1=@d1+1 end update @T set workday=case b.Dtype when 1 then 0 else 1 end from @T a join 表B b on a.date=b.DDate return(select sum(workday) from @T) end
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-04-17 08:55:073楼 得分:0
    顶顶!
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-04-18 11:12:534楼 得分:0
    SQL code
    DECLARE @dt datetime SET @dt=GETDATE() DECLARE @number int SET @number=3 --1.指定日期该年的第一天或最后一天 --A. 年的第一天 SELECT CONVERT(char(5),@dt,120)+'1-1' --B. 年的最后一天 SELECT CONVERT(char(5),@dt,120)+'12-31' --2.指定日期所在季度的第一天或最后一天 --A. 季度的第一天 SELECT CONVERT(datetime, CONVERT(char(8), DATEADD(Month, DATEPART(Quarter,@dt)*3-Month(@dt)-2, @dt), 120)+'1') --B. 季度的最后一天(CASE判断法) SELECT CONVERT(datetime, CONVERT(char(8), DATEADD(Month, DATEPART(Quarter,@dt)*3-Month(@dt), @dt), 120) +CASE WHEN DATEPART(Quarter,@dt) in(1,4) THEN '31'ELSE '30' END) --C. 季度的最后一天(直接推算法) SELECT DATEADD(Day,-1, CONVERT(char(8), DATEADD(Month, 1+DATEPART(Quarter,@dt)*3-Month(@dt), @dt), 120)+'1') --3.指定日期所在月份的第一天或最后一天 --A. 月的第一天 SELECT CONVERT(datetime,CONVERT(char(8),@dt,120)+'1') --B. 月的最后一天 SELECT DATEADD(Day,-1,CONVERT(char(8),DATEADD(Month,1,@dt),120)+'1') --C. 月的最后一天(容易使用的错误方法) SELECT DATEADD(Month,1,DATEADD(Day,-DAY(@dt),@dt)) --4.指定日期所在周的任意一天 SELECT DATEADD(Day,@number-DATEPART(Weekday,@dt),@dt) --5.指定日期所在周的任意星期几 --A. 星期天做为一周的第1天 SELECT DATEADD(Day,@number-(DATEPART(Weekday,@dt)+@@DATEFIRST-1)%7,@dt) --B. 星期一做为一周的第1天 SELECT DATEADD(Day,@number-(DATEPART(Weekday,@dt)+@@DATEFIRST-2)%7-1,@dt) Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=2203195 * 关于log记录配对 * log 通常包括portid / sessionid, 该id通常会被重用,但同一时刻不会重复 * 一组记录通常包含两条:先入,后出 */ declare @logdata table ( logid char(36) not null, -- 类似 portid / sessionid, 该id通常会被重用,但同一时刻系统中不会重复出现 logtype tinyint, -- 方向, 1: login|入, 0: logout|出 logtime datetime -- 时间点 ) declare @logI table (id int identity(1,1), logid char(36), logItime datetime) insert into @logI -- 各个有效的进入时间点 select logid, logItime=logtime from @logdata a where logtype = 1 and exists ( -- 本次退出的时间 select min(logtime) from @logdata c where c.logid=a.logid and c.logtype=0 and c.logtime>a.logtime ) and not ( -- 本次退出的时间 ( select min(logtime) from @logdata c where c.logid=a.logid and c.logtype=0 and c.logtime>a.logtime ) > -- 下次进入的时间 ( select min(logtime) from @logdata b where logid=a.logid and logtype=1 and b.logtime>a.logtime ) ) order by logid, logItime declare @logO table (id int identity(1,1), logid char(36), logOtime datetime) insert into @logO -- 各个有效的退出时间点 select logid, logOtime=logtime from @logdata a where logtype = 0 and exists ( -- 本次进入的时间 select max(logtime) from @logdata c where c.logid=a.logid and c.logtype=1 and c.logtime<a.logtime ) and not ( -- 本次进入的时间 ( select max(logtime) from @logdata c where c.logid=a.logid and c.logtype=1 and c.logtime<a.logtime ) < -- 上次退出的时间 ( select max(logtime) from @logdata b where logid=a.logid and logtype=1 and b.logtime>a.logtime ) ) order by logid, logOtime -- 配对: select a.logid, loginTime = logItime, logoutTime = logOtime from @logI a, @logO b where a.id=b.id /** 假设数据存储于表 cdr, 每条记录包括 bgnTime为该记录的开始时间, endTime为该记录的结束时间*/ declare @bgnTime datetime, @endTime datetime set @bgnTime = '2008-4-15 10:00:00.000' set @endTime = '2008-4-15 10:59:59.997' -- 指定时段内的记录数 ----------------------------------------------- -- 按开始时间计算 select count(1) from cdr where bgnTime>=@bgnTime and bgnTime<@endTime -- 按结束时间计算 select count(1) from cdr where endTime>@bgnTime and endTime<=@endTime -- 按时间相交计算 select count(1) from cdr where endTime>@bgnTime and bgnTime<@endTime -- 指定时段内的累计时长 select datediff(second ,case when bgnTime>@bgnTime then bgnTime else @bgnTime end ,case when endTime<@endTime then endTime else @endTime end ) from cdr where endTime>@bgnTime and bgnTime<@endTime -- 指定时段内的最大并发 ----------------------------------------------- -- 各个时间点的并发数 select top 1 * from ( select timepoint, cnt = (select count(1) from cdr b where bgnTime<=timepoint and endTime>timepoint) from (select distinct timepoint=bgnTime from cdr where bgnTime<@endTime and endTime>@bgnTime) as a ) as a order by cnt desc -- 时间连续性分组 ----------------------------------------------- declare @tbgn table (id int identity(1,1), bgnTime datetime) -- 该表每一数据的前一刻没有任何使用记录 insert into @tbgn select distinct bgnTime from cdr a where not exists ( select 1 from cdr b where b.bgnTime<a.bgnTime and b.endTime>=a.bgnTime) order by bgnTime declare @tend table (id int identity(1,1), endTime datetime) -- 该表每一数据的后一刻没有任何使用记录 insert into @tend select distinct endTime from cdr a where not exists ( select 1 from cdr b where b.bgnTime<=a.endTime and b.endTime>a.endTime) order by endTime -- 分组整合 select a.id, bgnTime, endTime from @tbgn a, @tend b where a.id=b.id
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • dawugui
    • 等级:
    发表于:2008-04-18 11:30:565楼 得分:0
    提供一个思路.

    将每年的周日,周六和节假日做成一个表.
    然后生成两个日期之间的所有日期.

    两表一关联就出来,貌似连函数都不必用.
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-04-18 11:33:146楼 得分:0
    收藏学习.
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-04-18 11:53:477楼 得分:0
    帮你顶上去 ,学习
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-04-18 12:48:348楼 得分:0
    收藏
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • utpcb
    • 等级:
    发表于:2008-04-18 12:59:339楼 得分:0
    提供一个思路.

    将每年的周日,周六和节假日做成一个表.
    然后生成两个日期之间的所有日期.

    两表一关联就出来,貌似连函数都不必用.


    UP UP
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-04-18 13:23:1210楼 得分:0
    我刚才还在想要不要也贴一个上来。呵呵。不过想想,都差不多就算了,你看一下,
    如果实际做时碰到什么问题了,再发一下,具体点。到时很高兴帮你弄弄,可是现在这样太长了,没办法弄。呵。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • wynlc
    • 等级:
    发表于:2008-04-18 13:27:1611楼 得分:0
    收藏了
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-04-18 19:51:5612楼 得分:0
    收藏了
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-04-19 13:42:4813楼 得分:0
    收藏
    修改 删除 举报 引用 回复