SQL连续几天忽略周末

时间:2018-03-12 12:57:06

标签: sql sql-server-2008

我有一张格式如下的表:

ID    ID1    ID2    DATE

1      1      1     2018-03-01     
2      1      1     2018-03-02
3      1      1     2018-03-05
4      1      1     2018-03-06
5      1      1     2018-03-07
6      2      2     2018-03-05
7      2      2     2018-03-05 
8      2      2     2018-03-06
9      2      2     2018-03-07
10     2      2     2018-03-08

从这张表中我必须得到ID1ID2在该列中相同的所有记录,其中DATE是连续5个工作日(连续5个日期,忽略星期六/星期日缺少日期;忽略假期。)

我真的不知道如何实现这一目标。我做了搜索,但找不到任何帮助我的东西。所以我的问题是,我如何实现以下输出?

ID    ID1    ID2    DATE

1      1      1     2018-03-01     
2      1      1     2018-03-02
3      1      1     2018-03-05
4      1      1     2018-03-06
5      1      1     2018-03-07

SQLFiddle搞乱

3 个答案:

答案 0 :(得分:1)

假设您没有重复项并且工作日仅 ,那么这个特殊情况就有一个简洁的解决方案。我们可以确定前面4行的日期。整整一周,提前4天或提前6天:

select t.*
from (select t.*, lead(dat, 4) over (order by id2, dat) as dat_4
      from t
     ) t
where datediff(day, dat, dat_4) in (4, 6);

这恰好起作用,因为你正在寻找一个完整的一周。

Here是SQL小提琴。

答案 1 :(得分:1)

select t.* from 
(select id1,id2,count(distinct dat) count from t 
group by id1,id2
having count(distinct dat)=5) t1 right join 
t
on t.id1=t1.id1 and t.id2=t1.id2
where count=5

检查一下 -

两周的日期,有效期为10天 http://sqlfiddle.com/#!18/76556/1

两周的日期,有10个非独特日期 http://sqlfiddle.com/#!18/b4299/1

两周的日期少于10但独特 http://sqlfiddle.com/#!18/f16cb/1

答案 2 :(得分:1)

这个查询非常详细,没有LEAD或LAG,这是我午休时能做的最好的。考虑到时间,你可以改进它。

DECLARE @T TABLE
(
    ID INT,
    ID1 INT,
    ID2 INT,
    TheDate DATETIME
)
INSERT @T SELECT 1,1,1,'03/01/2018'
INSERT @T SELECT 2,1,1,'03/02/2018'
INSERT @T SELECT 3,1,1,'03/05/2018'

INSERT @T SELECT 4,1,1,'03/06/2018'
INSERT @T SELECT 5,1,1,'03/07/2018'
--INSERT @T SELECT 5,1,1,'03/09/2018'
INSERT @T SELECT 6,2,2,'03/02/2018'
INSERT @T SELECT 7,2,2,'03/05/2018'
INSERT @T SELECT 8,2,2,'03/05/2018'
--INSERT @T SELECT 9,2,2,'03/06/2018'
INSERT @T SELECT 10,2,2,'03/07/2018'
INSERT @T SELECT 11,2,2,'03/08/2018'
INSERT @T SELECT 12,2,2,'03/15/2018'

INSERT @T SELECT 13,1,1,'04/01/2018'
INSERT @T SELECT 14,1,1,'04/02/2018'
INSERT @T SELECT 15,1,1,'04/05/2018'

--SELECT * FROM @T

DECLARE @LowDate DATETIME = DATEADD(DAY,-1,(SELECT MIN(TheDate) FROM @T))
DECLARE @HighDate DATETIME = DATEADD(DAY,1,(SELECT MAX(TheDate) FROM @T))
DECLARE @DaysThreshold INT = 5
;
WITH Dates AS
(
  SELECT DateValue=@LowDate
  UNION ALL
  SELECT  DateValue + 1 FROM    Dates   
  WHERE   DateValue + 1 < @HighDate  
),
Joined AS
(
    SELECT * FROM Dates LEFT OUTER JOIN  @T T ON T.TheDate=Dates.DateValue 
),
Calculations AS
(
    SELECT 
        ID=MAX(J1.ID),
        J1.ID1,J1.ID2,
        J1.TheDate,
        LastDate=MAX(J2.TheDate), 
        LastDateWasWeekend = CASE WHEN ((DATEPART(DW,DATEADD(DAY,-1,J1.TheDate) ) + @@DATEFIRST) % 7) NOT IN (0, 1) THEN 0 ELSE 1 END, 
        Offset =  DATEDIFF(DAY,MAX(J2.TheDate),J1.TheDate)  
    FROM 
        Joined J1
        LEFT OUTER JOIN Joined J2 ON J2.ID1=J1.ID1 AND J2.ID2=J1.ID2 AND J2.TheDate<J1.TheDate
    WHERE
        NOT J1.ID IS NULL
    GROUP BY J1.ID1,J1.ID2,J1.TheDate
)
,FindValid AS
(
    SELECT
        ID,ID1,ID2,TheDate,
        IsValid=CASE 
            WHEN LastDate=TheDate THEN 0
            WHEN LastDate IS NULL THEN 1
            WHEN Offset=1 THEN 1
            WHEN Offset>3 THEN 0
            WHEN Offset<=3 THEN 
                LastDateWasWeekend
            END
    FROM 
        Calculations
    UNION 
    SELECT DISTINCT ID=NULL,ID1,ID2, TheDate=@HighDate,IsValid=0 FROM @T

),
FindMax As
(
    SELECT 
        This.ID,This.ID1,This.ID2,This.TheDate,MaxRange=MIN(Next.TheDate)
    FROM 
        FindValid This
    LEFT OUTER JOIN FindValid Next ON Next.ID2=This.ID2 AND Next.ID1=This.ID1 AND This.TheDate<Next.TheDate AND Next.IsValid=0 
    GROUP BY 
        This.ID,This.ID1,This.ID2,This.TheDate
),
FindMin AS
(
    SELECT 
        This.ID,This.ID1,This.ID2,This.TheDate,This.MaxRange,MinRange=MIN(Next.TheDate) 
    FROM 
        FindMax This
        LEFT OUTER JOIN FindMax Next ON Next.ID2=This.ID2 AND Next.ID1=This.ID1 AND This.TheDate<Next.MaxRange-- AND Next.IsValid=0 OR Next.TheDate IS NULL 
    GROUP BY 
        This.ID,This.ID1,This.ID2,This.TheDate,This.MaxRange
)
,Final AS
(
SELECT 
    ID1,ID2,MinRange,MaxRange,SequentialCount=COUNT(*) 
FROM
    FindMin
GROUP BY
    ID1,ID2,MinRange,MaxRange    

)

SELECT 
    T.ID,
    T.ID1,
    T.ID2,
    T.TheDate 
FROM @T T 
    INNER JOIN Final ON T.TheDate>= Final.MinRange AND T.TheDate < Final.MaxRange AND T.ID1=Final.ID1 AND T.ID2=Final.ID2
WHERE
    SequentialCount>=@DaysThreshold
OPTION (MAXRECURSION 0)