通过最接近的匹配顺序排列日期与重复

时间:2016-12-11 18:39:02

标签: sql sql-server schedule

我有一个像这样的表格方案:

CREATE TABLE MyEvents 
(
    Id INT PRIMARY KEY NOT NULL,
    Name NVARCHAR(MAX) NOT NULL
);

CREATE TABLE MyEventSchedules 
(
    Id INT PRIMARY KEY NOT NULL,
    MyEventId INT NOT NULL,
    StartDate BIGINT NOT NULL,
    TimeOfDay BIGINT NOT NULL,
    Interval BIGINT NOT NULL
);

数据如:

INSERT INTO MyEvents (Id, Name) 
VALUES (1, 'Event #1');
-- StartDate: new DateTime(2016, 5, 1), TimeOfDay: 16:00 - repeats every 3 day
INSERT INTO MyEventSchedules (Id, MyEventId, StartDate, TimeOfDay, Interval) 
VALUES (1, 1, 635976576000000000, 576000000000, 2592000000000);
-- StartDate: new DateTime(2016, 5, 15), TimeOfDay: 12:00 - repeats every 30 day
INSERT INTO MyEventSchedules (Id, MyEventId, StartDate, TimeOfDay, Interval) 
VALUES (2, 1, 635988672000000000, 432000000000,  25920000000000);

INSERT INTO MyEvents (Id, Name) 
VALUES (2, 'Event #2');

-- StartDate: new DateTime(2016, 5, 2), TimeOfDay: 14:00 - repeats every day
INSERT INTO MyEventSchedules (Id, MyEventId, StartDate, TimeOfDay, Interval) 
VALUES (3, 2, 635977440000000000, 504000000000, 864000000000);

INSERT INTO MyEvents (Id, Name) 
VALUES (3, 'Event #3');

-- StartDate: new DateTime(2016, 5, 2), TimeOfDay: 22:00 - repeats every day
INSERT INTO MyEventSchedules (Id, MyEventId, StartDate, TimeOfDay, Interval) 
VALUES (4, 3, 635977440000000000, 792000000000, 864000000000);

INSERT INTO MyEvents (Id, Name) 
VALUES (4, 'Event #4');

-- StartDate: new DateTime(2016, 5, 2), TimeOfDay: 18:00 - repeats every day
INSERT INTO MyEventSchedules (Id, MyEventId, StartDate, TimeOfDay, Interval) 
VALUES (5, 4, 635977440000000000, 648000000000, 864000000000);

BIGINT用于将时间戳存储为数字(https://msdn.microsoft.com/da-dk/library/system.datetime.ticks(v=vs.110).aspx)。像Unix时间戳一样。

我想SELECT最接近时间戳的事件,但我无法弄清楚如何做到这一点。我想出了这个,但我不确定它是否是正确的方式。

你可以说我希望通过它的时间表找到下一个事件的时间

DECLARE @CurrentTime BIGINT = 635979600000000000;  -- new DateTime(2016, 5, 4, 12, 0, 0).Ticks

SELECT * 
FROM MyEvents
JOIN MyEventSchedules ON (MyEventSchedules.MyEventId = MyEvents.Id)
OUTER APPLY (
    SELECT 
        ((@CurrentTime - x.StartDate) / x.Interval) AS NumberOfTimesRepeated,
        ((((@CurrentTime - x.StartDate) / x.Interval) * x.Interval)) AS test,
        (@CurrentTime - ((@CurrentTime - x.StartDate) / x.Interval)) Value
    FROM MyEventSchedules x
    WHERE x.MyEventId = MyEvents.Id) as Sort
ORDER BY Sort.Value

这里我有一些测试用例;

SELECT MyEvents.Id 
FROM MyEvents
JOIN MyEventSchedules ON (MyEventSchedules.MyEventId = MyEvents.Id)
-- ORDER BY "closest to @CurrentTime"

-- Expected (1, 2, 4, 3)


DECLARE @CurrentTime BIGINT = 635978052000000000; -- new DateTime(2016, 5, 2, 17, 0 , 0).Ticks

SELECT MyEvents.Id 
FROM MyEvents
JOIN MyEventSchedules ON (MyEventSchedules.MyEventId = MyEvents.Id)
-- ORDER BY "closest to @CurrentTime"

-- Expected (4, 3, 2, 1)


DECLARE @CurrentTime BIGINT = 635989068000000000; -- new DateTime(2016, 5, 15, 11, 0, 0).Ticks

SELECT MyEvents.Id 
FROM MyEvents
JOIN MyEventSchedules ON (MyEventSchedules.MyEventId = MyEvents.Id)
-- ORDER BY "closest to @CurrentTime"

-- Expected (1, 2, 4, 3)

2 个答案:

答案 0 :(得分:1)

declare @MyEventSchedules table
(
  ID INT IDENTITY(1,1) PRIMARY KEY,
  MyEventId INT,
  StartDateAndTime DATETIME,
  Interval TINYINT /* days, I suppose */
)

declare @CurrentTime datetime = GETDATE()

insert into @MyEventSchedules(MyEventId, StartDateAndTime, Interval)
values
(1, '20161102 16:00',  3),
(1, '20161116 12:00', 30),
(1, '20161112 01:00', 30),
(2, '20160502 14:00',  1),
(3, '20160502 22:00',  1)

;with cteEvents as
(
  select distinct e.MyEventID from @MyEventSchedules e
),
cteSched as
(
  select
    s.MyEventID,
    s.StartDateAndTime,
    s.Interval,
    s.Interval * CAST(DATEDIFF(DD, s.StartDateAndTime, @CurrentTime) / s.Interval AS INT) IntervalDiff
  from @MyEventSchedules s
  where s.StartDateAndTime <= @CurrentTime
)
select *
from cteEvents e
outer apply
(
  select top (1)
    --s.IntervalDiff,
    CASE
      WHEN DATEADD(DD, s.IntervalDiff, s.StartDateAndTime) < @CurrentTime
      THEN DATEADD(DD, s.IntervalDiff + s.Interval, s.StartDateAndTime)
      ELSE DATEADD(DD, s.IntervalDiff, s.StartDateAndTime)
    END NextRun
  from cteSched s
  where s.MyEventID = e.MyEventID
  order by NextRun ASC
) s

enter image description here

答案 1 :(得分:0)

经过大量的反复试验后,我找到了正确的数学运算来进行查询。

DECLARE @CurrentTime BIGINT = 635978772000000000; 

SELECT MyEvents.*, Sort.*, Sort.Value / 864000000000 AS DaysToNext, Sort.Value / 36000000000 AS HourstoNext FROM MyEvents
OUTER APPLY (
    SELECT 
        CASE WHEN MIN(x.StartDate + x.TimeOfDay + ((((@CurrentTime - x.StartDate) / x.Interval)) * x.Interval) - @CurrentTime) < 0
            THEN MIN(x.StartDate + x.TimeOfDay + ((((@CurrentTime - x.StartDate) / x.Interval)+1) * x.Interval) - @CurrentTime)
            ELSE MIN(x.StartDate + x.TimeOfDay + ((((@CurrentTime - x.StartDate) / x.Interval)) * x.Interval) - @CurrentTime)
        END
        as Value
    FROM 
        MyEventSchedules x
    WHERE x.MyEventId = MyEvents.Id AND x.StartDate <= @CurrentTime
) Sort

WHERE Sort.Value IS NOT NULL

ORDER BY Sort.Value