需要删除连续三天不包括sql中的周末和假日

时间:2016-08-19 19:21:47

标签: sql sql-server

我要求连续三天从表中删除行(如果周末介于两者之间,则排除日期)

CREATE TABLE [dbo].[Test]
(
    [Scanid] [bigint] NULL,
    [Employeeid] [int] NULL,
    [Datescanned] [datetime] NULL
) 

INSERT INTO [dbo].[Test]([Scanid], [Employeeid], [Datescanned])
VALUES (108639, 3820, '2016-04-28 17:12:33.000'),
       (108639, 3820, '2016-04-28 18:05:46.000'),
       (108639, 3820, '2016-04-28 20:58:36.000'),
       (999999, 3820, '2016-04-29 10:08:00.000'),
       (999999, 3820, '2016-04-29 10:12:10.000'),
       (777777, 3820, '2016-05-02 10:12:00.000'),
       (111111, 3820, '2016-04-04 10:12:00.000'),
       (33333, 3820, '2016-04-11 17:23:00.000'),
       (987623, 3820, '2016-04-18 11:12:00.000'),
       (1234, 3820, '2016-05-26 10:00:00.000'),
       (5678, 3820, '2016-05-27 10:00:00.000'),
       (8920, 3820, '2016-05-31 10:00:00.000')

输出:

Scanid  Employeeid  Datescanned
----------------------------------------
108639  3820    2016-04-28 17:12:33.000
108639  3820    2016-04-28 18:05:46.000
108639  3820    2016-04-28 20:58:36.000
999999  3820    2016-04-29 10:08:00.000
999999  3820    2016-04-29 10:12:10.000
777777  3820    2016-05-02 10:12:00.000
111111  3820    2016-04-04 10:12:00.000
33333   3820    2016-04-11 17:23:00.000
987623  3820    2016-04-18 11:12:00.000
1234    3820    2016-05-26 10:00:00.000
5678    3820    2016-05-27 10:00:00.000
8920    3820    2016-05-31 10:00:00.000

我们只能从日期扫描字段中取日期,然后在上面的示例中,我们应该删除连续3个日期的行,从2016-04-28'到' 2016-05-02' (2016-04-30和31是周末,所以我们可以忽略),并删除连续3个日期的行,从2016-05-26'到2016-05-31' (2016-05-29和30th是周末,所以我们可以忽略)。因此,只有结果应该显示2016-04-04,2016-04-11,2016-04-18之前的行,这些日期之前或之后都不会连续3天。

5 个答案:

答案 0 :(得分:1)

以下是您想要的确切输出..

我可以在你的问题中看到一个错误,[即。从' 2016-05-26'删除连续3个日期的行到2016-05-31' ( 2016-05-29 30th 是周末,所以我们可以忽略)'],那些周末天数不正确..正确的日期是 2016-05-28 2016-05-29

DROP TABLE [TestDates]
GO
CREATE TABLE [dbo].[TestDates](
        [Scanid] [bigint] NULL,
        [Employeeid] [int] NULL,
        [Datescanned] [datetime] NULL
        )

    INSERT INTO [dbo].[TestDates]  ([Scanid] ,[Employeeid],[Datescanned])
    VALUES   (108639,3820,'2016-04-28 17:12:33.000'),(108639,3820,'2016-04-28 18:05:46.000'),
             (108639,3820,'2016-04-28 20:58:36.000'),(999999,3820,'2016-04-29 10:08:00.000'),
             (999999,3820,'2016-04-29 10:12:10.000'),(777777,3820,'2016-05-02 10:12:00.000'),
             (111111,3820,'2016-04-04 10:12:00.000'),(33333,3820,'2016-04-11 17:23:00.000'),
             (987623,3820,'2016-04-18 11:12:00.000'),(1234,3820,'2016-05-26 10:00:00.000'),
             (5678,3820,'2016-05-27 10:00:00.00'), (8920, 3820, '2016-05-30 10:00:00.000')
GO

DROP TABLE #t
GO
SELECT DISTINCT Employeeid,CONVERT(date,Datescanned) Datescanned INTO #T
FROM [TestDates]
GO


;WITH cte_cnt
AS
(
 SELECT Employeeid, MIN(Datescanned) AS FROM_DATE
       ,MAX(Datescanned) AS TO_DATE
       , COUNT('A') AS DayDiff
 FROM (
      SELECT Employeeid,Datescanned,
            ROW_NUMBER() OVER(ORDER BY Datescanned) AS ROW_NUMBER,
            DATEDIFF(D, ROW_NUMBER() OVER(ORDER BY Datescanned)
            ,CASE WHEN DATENAME(dw, cast (Datescanned as datetime)-1) = 'Sunday' THEN DATEADD(DAY, -2, Datescanned)  ELSE Datescanned END) AS Diff
        FROM #t) AS dt
 GROUP BY Employeeid, Diff )
DELETE t
--SELECT *
FROM  cte_cnt  c
      JOIN [TestDates] t
            ON c.Employeeid=t.Employeeid
WHERE CAST(t.Datescanned as DATE) BETWEEN c.FROM_DATE AND  c.TO_DATE and c.DayDiff=3
GO

SELECT *
FROM [TestDates]
GO

答案 1 :(得分:0)

也许这个?:

delete from Test
where not exists (
    select 1
    from Test t2
    where cast(t2.Datescanned as date)
        between
            dateadd(day,
                case datepart(dayofweek, cast(Test.Datescanned as date))
                    when 1 then -4 when 2 then -4
                    else -2
                end,
                cast(Test.Datescanned as date)
            )
        and
            dateadd(day,
                case datepart(dayofweek, cast(Test.Datescanned as date))
                    when 4 then 4 when 5 then 4
                    else 2
                end,
                cast(Test.Datescanned as date)
            )
)

答案 2 :(得分:0)

不考虑假期的解决方案是

SELECT
  t.*
FROM (SELECT DISTINCT
  CAST(t1.datescanned AS date) first_date,
  CAST(t2.datescanned AS date) second_date,
  CAST(t3.datescanned AS date) third_date
FROM test t1
JOIN test t2 --add a join condition for employeeid as well
  ON DATEDIFF(dd, CAST(t1.datescanned AS date), CAST(t2.datescanned AS date)) = 1
  OR (DATEPART(WEEKDAY, CAST(t2.datescanned AS date)) = 2
  AND DATEDIFF(dd, CAST(t1.datescanned AS date), CAST(t2.datescanned AS date)) = 3)
JOIN test t3 --add a join condition for employeeid as well
  ON DATEDIFF(dd, CAST(t2.datescanned AS date), CAST(t3.datescanned AS date)) = 1
  OR (DATEPART(WEEKDAY, CAST(t3.datescanned AS date)) = 2
  AND DATEDIFF(dd, CAST(t2.datescanned AS date), CAST(t3.datescanned AS date)) = 3)
 ) x
JOIN test t
  ON CAST(t.datescanned AS date) = x.first_date
  OR CAST(t.datescanned AS date) = x.second_date
  OR CAST(t.datescanned AS date) = x.third_date

自我加入表格两次,每次都在

  • 日期差异为1或
  • 3周末发生并检查工作日是否是星期一(工作日= 2)

Sample demo

结果给出了需要删除的行。但需要注意的是,如果没有间隙,这将连续超过3天。在这种情况下,您需要解释是否要在第3天停止删除。

答案 3 :(得分:0)

以前的脚本很好。我会做一个改进,周末和假日检查,添加一个功能,并在选择语句中调用它。 这是一个你可以使用的简单函数(我假设你有一个名为Holiday的表,它保存每个州的所有假日日期)

Create FUNCTION [dbo].[IsHolidayOrWeekend] 
(
    @date DateTime,
    @stateId int
)
RETURNS Bit
AS
BEGIN
    declare @dayOfWeek VARCHAR(9);
    set @dayOfWeek = DATEName(DW, @date);

    IF(@dayOfWeek = 'Saturday' OR @dayOfWeek = 'Sunday') 
        RETURN 1;
    ELSE
    begin
        set @date = cast(@date as date) -- Remove the time portion 
        RETURN IsNull((SELECT 1 from Holiday where StateId = @provinceId and HolidayDate = @date ), 0)
    end;
END

答案 4 :(得分:0)

除了周末之外,我还有一个额外的要求,即igonre假期。我创建了Test_calendar表,删除了所有假日和周末,并将行号分配给活动天。然后这是我使用的代码。如果我们有数百万行,它可以工作,但可能不是很快。对我来说,数据很小,所以它将完成工作。如果您可以简化删除过程以使其快速,请告诉我。

SELECT  distinct scanid as [badgeid] , Employeeid,CONVERT(date, Datescanned) as Datescanned,RN
   into #test1
    FROM [dbo].[Test] a
     inner join Test_Calendar b
  on CONVERT(date, a.Datescanned)=b.Cal_date
    order by Datescanned asc


  declare @min int
  declare @max int
  declare @i int
  select @min=MIN(rn) from #test1
  select @max=Max(rn) from #test1
  while(@min<@max)
  begin
  select @i=COUNT(*) from #test1 where rn in(@min ,@min+1,@min+2)
  if(@i=3)
  select *  from #test1 where rn in(@min ,@min+1,@min+2)
  set @min=@min+1 
  end