2016-12-11 2 views
0

Ich habe eine Tabelle Schema wie folgt aus:Bestellen Reihen durch engste Übereinstimmung mit der Wiederholung bisher

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 
); 

und Daten wie:

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); 

Die BIGINT zum Speichern der Zeitstempel als Nummer (https://msdn.microsoft.com/da-dk/library/system.datetime.ticks(v=vs.110).aspx). Wie Unix Timestamp.

Ich möchte SELECT die Ereignisse, die einem Zeitstempel am nächsten ist, aber ich kann nicht herausfinden, wie es geht. Ich bin darauf gekommen, aber ich bin mir nicht sicher, ob es überhaupt richtig ist.

Man könnte sagen, dass ich von ihm die Zeitpläne, die Zeit bis zum nächsten Ereignis finden wollen

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 

Hier habe ich einige Tests Fälle haben;

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

'BIGINT' ist keine geeignete Datentyp für Datums-/Zeitwerte. Ihre Daten sind nicht sinnvoll. Wie wäre es mit einem Tabellenformat von Beispielwerten mit erwarteten Ergebnissen? –

+0

'BIGINT' ist in Ordnung für das Speichern des Datums als C# DateTime-Ticks. - wäre das gleiche, wenn es epoch war – TryingToImprove

+0

C# versteht SQL-Server-Datentypen. Dennoch, wenn Sie speziell "Ticks" brauchen - das sind die DTO's. Passen Sie das DB-Modell nicht an, um clientseitige (sekundäre) Anforderungen zu erfüllen. Domain sagt "Sie brauchen Datum und Uhrzeit" - also speichern Sie es als Datetime. –

Antwort

0

Nach viel Versuch und Irrtum fand ich die richtige Mathematik, um die Abfrage zu tun.

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 
+0

Nun, vielleicht könnte dies Ihnen helfen, den Nutzen der Datum/Uhrzeit-Typen in der Datenbank zu verstehen. –

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

+0

Intervall könnte alles sein, Sekunden, Stunden und Tage, deshalb habe ich eine Nummer verwendet. – TryingToImprove

+1

Ändern Sie das erste Argument in der DATEADD-Funktion. –

Verwandte Themen