代码之家  ›  专栏  ›  技术社区  ›  TryingToImprove

按与日期最匹配的行排序,重复

  •  0
  • TryingToImprove  · 技术社区  · 8 年前

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

    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 回复  |  直到 8 年前
        1
  •  1
  •   Ivan Starostin    8 年前
    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

        2
  •  0
  •   TryingToImprove    8 年前

    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