2016-08-19 340 views
3

我有一個要求從表中有連續三天刪除行(排除天,如果週末之間進來)需要刪除連續三天不包括週末和節假日在SQL

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 

我們只能從日期掃描的字段獲取日期,然後在上面的例子中,我們應該刪除從'2016-04-28'到'2016-05-02'(2016-04-30和31日是週末,所以我們可以忽略),並刪除連續3日從'2016-05-26'到'2016-05-31'的行'(2016-05-29和30日是週末,所以我們可以忽略)。所以只有結果應顯示排在2016-04-04,2016-04-11,2016-04-18的天數之前或之後沒有連續3天。

+0

我刪除了'mysql'標籤並添加了sql server,因爲它的語法看起來像這樣/ –

+2

這進入了一些棘手的編碼。首先,你需要一個包含節日數據的表格,因爲這不是任何地方的SQL。創建一個包含未來20年所有日期的日期表可能是最簡單的,用一個位標誌來指示該日是否爲工作日。一旦存在,確定需要刪除的日期變得非常簡單。 –

回答

1

這是你想要的精確輸出..

我可以看到你的問題,[即一個錯誤。從「2016年5月26日」,以連續3日刪除行「2016年5月31日」(2016年5月29日是週末,所以我們可以忽略)「],那些週末天不正確..正確日期爲2016-05-28 and 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 
+0

我測試了它,它的工作完美。我對你編碼的方式印象深刻。謝謝。現在我還有額外的要求來避免假期。所以我想我需要使用日曆時間表來獲得只有工作日和內部連接它將我的表和連續刪除三個行 – Vamsi

+0

偉大..我知道一旦你完成了假期日曆..順便也給一個upvote也.. :) –

+0

我創建了Test_calendar表,刪除所有節假日和週末,並將行號分配給活動日期。然後這是我使用的代碼。它工作,但如果我們有數百萬行,可能不是最快的。對我而言,數據很小,因此可以完成工作。請讓我知道,如果你可以簡化刪除過程,使其更快。 – Vamsi

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) 
      ) 
) 
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天停止刪除。

0

以前的腳本很好。我會改進和週末和節假日檢查,添加一個函數,並在選擇語句中調用它。 下面是一個簡單的函數,你可以使用(我假設你有一個名爲假日表,其中包含每個國家都放假日期)

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 
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 @[email protected]+1 
    end 
相關問題