2012-07-16 11 views
7

Ich weiß, dass dieser ein wenig verwirrend sein kann, ich versuche nur, den besten Weg zu erwägen, es umzusetzen! Ich habe das jetzt in ein paar Foren gepostet, aber ich habe kein Glück. Hoffentlich kann jemand einige Vorschläge machen, wie man das macht.Liste verfügbarer Zeiten von 2 DateTimes

Beispieltabelle (tbl_Bookings)

ID DateStarted  DateEnded   RoomID 
1 16/07/2012 09:00 16/07/2012 10:00 1 
2 16/07/2012 12:00 16/07/2012 13:00 1 

Grundsätzlich möchte ich 2 mal Datum einzugeben, wie 16/07/2012 16/07/2012 08:30 bis 13:30, und es wird meine Beispieltabelle oben abfragen und "verfügbare" Zeiten zurückgeben, IE, ich möchte, dass es folgendes ausgibt.

16/07/2012 08:30 - 16/07/2012 09:00 
16/07/2012 10:00 - 16/07/2012 12:00 
16/07/2012 13:00 - 16/07/2012 13:30 

Meine Frage ist, dann ist dies durchaus möglich, in SQL? Ich habe versucht, darüber nachzudenken, wie man es in VB macht, und ich kämpfe auch mit diesem. Meine Idee/Versuch wurde mit Ron Savage fn_daterange (siehe unten)

if exists (select * from dbo.sysobjects where name = 'fn_daterange') drop function fn_daterange; 
go 
create function fn_daterange 
(
@MinDate as datetime, 
@MaxDate as datetime, 
@intval as datetime 
) 
returns table 
as 
return 
WITH times (startdate, enddate, intervl) AS 
(
SELECT @MinDate as startdate, @MinDate + @intval - .0000001 as enddate, @intval as intervl 
UNION ALL 
SELECT startdate + intervl as startdate, enddate + intervl as enddate, intervl as intervl 
FROM times 
WHERE startdate + intervl <= @MaxDate 
) 
select startdate, enddate from times; 
go 

Ich wurde dann geht es zu nennen, indem die unten verwenden, aber mein Problem ist, me.DateEnded außerhalb von dr.enddate ist und damit die Auftreten würde ‚0‘ sein:

SELECT dr.startdate, dr.enddate, count(me.DateStarted) as occurrence 
FROM fn_daterange('16/07/2012 08:30', '16/07/2012 13:30', '00:30:00') dr 
LEFT OUTER JOIN tbl_Bookings me 
ON me.DateStarted BETWEEN dr.startdate AND dr.enddate 
AND me.DateEnded BETWEEN dr.startdate AND dr.enddate) 
GROUP BY dr.startdate, dr.enddate 

Kann jemand möglicherweise vorschlagen, einen besseren Weg, dies zu tun, oder hoffentlich eine Lösung für die Art und Weise anbietet ich versuche es noch zu tun?

Vielen Dank im Voraus!

+0

Welche Zeit ist '16.07.2012 09: 90'? –

+0

Tim Schmelte, Easter Egg =) –

+0

Welche DBMS verwendest du, SQL-Server (welche Version)? Im Grunde haben Sie einen Start DateTime und einen EndDateTime (daher ein TimeSpan. Jetzt wollen Sie die Halbstunden finden die frei sind (nicht gebucht). Ist das korrekt? –

Antwort

0

Ich habe keine Ahnung, wie Sie das Problem mit Sets lösen, aber der folgende cursorbasierte Ansatz sollte funktionieren. Dies ist, wie Sie es in VB oder C# tun würden:

CREATE FUNCTION GetAvailableTimes 
(
    @MinDate datetime, 
    @MaxDate datetime 
) 
RETURNS @result TABLE 
(
    DateStarted datetime, 
    DateEnded datetime, 
    RoomID int 
) 
AS 
BEGIN 
    DECLARE @DateStarted datetime 
    DECLARE @DateEnded datetime 
    DECLARE @CurrentDate datetime 
    DECLARE @RoomID int 
    DECLARE @CurrentRoom int 

    DECLARE c CURSOR FOR 
     SELECT DateStarted, DateEnded, RoomID 
     FROM tbl_Bookings 
     WHERE DateStarted BETWEEN @MinDate AND @MaxDate 
      OR DateEnded BETWEEN @MinDate AND @MaxDate 
     ORDER BY RoomID, DateStarted 

    SET @CurrentRoom = 0 

    OPEN c 
    FETCH NEXT FROM c 
    INTO @DateStarted, @DateEnded, @RoomID 

    WHILE @@FETCH_STATUS = 0 
    BEGIN 

     IF @CurrentRoom <> @RoomID BEGIN 
      IF @CurrentRoom <> 0 AND @CurrentDate < @MaxDate BEGIN 
       INSERT INTO @result VALUES (@CurrentDate, @MaxDate, @CurrentRoom) 
      END 

      SET @CurrentDate = @MinDate 
      SET @CurrentRoom = @RoomID 
     END 

     IF @CurrentDate < @DateStarted BEGIN 
      INSERT INTO @result VALUES (@CurrentDate, @DateStarted, @CurrentRoom) 
     END 

     SET @CurrentDate = @DateEnded 

     FETCH NEXT FROM c 
     INTO @DateStarted, @DateEnded, @RoomID 
    END 
    CLOSE c 
    DEALLOCATE c 

    IF @CurrentRoom <> 0 AND @CurrentDate < @MaxDate BEGIN 
     INSERT INTO @result VALUES (@CurrentDate, @MaxDate, @CurrentRoom) 
    END 

    RETURN 
END 

Der folgende Aufruf jetzt das Ergebnis für Ihre Testdaten Sie suchen erhalten.

SELECT * FROM dbo.GetAvailableTimes('20120716 8:30', '20120716 13:30') 

Ich habe auch davon ausgegangen, dass es mehr als ein Zimmer sein könnte und Sie nach freien Zeiten in alle von ihnen suchen.

Ich habe die Funktion nur schnell getestet, daher bin ich mir ziemlich sicher, dass es noch einige Grenzfälle gibt, die nicht richtig adressiert sind. Aber Sie sollten die Idee bekommen.

0

Ich würde dies mit der folgenden Logik nähern. Erstellen Sie einen Datensatz für den Zeitraum zwischen der gewünschten Startzeit und der ersten Startzeit, die in den Daten angezeigt wird, falls vorhanden. Erstellen Sie einen Datensatz für den Zeitraum zwischen der gewünschten Endzeit und der letzten Endzeit, die Sie in den Daten sehen, falls vorhanden. Erstellen Sie dann die Zwischensätze für die Zeiten, die Sie haben.

Die folgende Abfrage hat diese Idee. Ich bin nicht sicher, ob es funktioniert, wenn die gewünschte Start- und Endzeit in der Mitte eines gebuchten Zeitraums liegt.

with const as (select @starttime as StartTime, @endtime as EndTime) 
select * 
from ((Select c.StartTime, MIN(DateStarted), RoomId 
     from tbl_Bookings b cross join const c 
     where b.DateStarted >= c.StartTime 
     group by RoomID 
     having c.StartTime <> MIN(DateStarted) 
    ) union all 
     (Select max(DateEnded), c.EndTime, RoomId 
     from tbl_Bookings b cross join const c 
     where b.DateEnded <= c.EndTime 
     group by RoomID 
     having c.EndTime <> max(DateEnded) 
    ) union all 
     (select * 
     from (select b.DateEnded as DateStarted, min(b.DateStarted) as DateEnded 
      from tbl_Bookings b join 
        tbl_Bookings bnext 
        on b.RoomId = bnext.RoomId and 
        bnext.DateStarted > b.DateStarted cross join 
        const c 
      where b.DateStarted < c.endtime and 
        b.DateEnded > c.StartTime and 
        bnext.DateStart < c.EndTime and 
        bnext.DateEnded > c.StartTime 
      group by b.DateEnded 
      ) b cross join const c 
     where DateStarted <> DateEnded 
    ) 
    ) 

Die letzte Unterabfrage ist ziemlich kompliziert. Es macht einen Self-Join, um das Äquivalent der Funktion lead() zu erhalten.

2

Ich glaube, ich habe eine funktionierende Lösung in SQL. Dies setzt voraus, dass die Daten in tbl_Bookings konsistent sind, dh dass sich für einen gegebenen Raum keine Start-/Endzeiten überlappen. Wahrscheinlich ein einfacherer Weg, aber der Trick bestand darin, die Buchungen zu ordnen und Endzeiten mit den folgenden Startzeiten zu kombinieren.Es gibt zwei zusätzliche Anfragen, um alle Intervalle nach Ihrer angegebenen Start aber vor der ersten Buchung zu erhalten. Ebenso für End.

EDIT: Added WHERE NOT EXISTS Wachen zu den letzten beiden Abfragen bei @Start oder @End fiel innerhalb eines gebuchten Intervall.

DECLARE @Start DateTime = '05/07/2012 08:30' 
DECLARE @End DateTime = '05/07/2012 13:30' 

;WITH Bookings (RoomId, RowNum, Started, Ended) AS (
    SELECT RoomId, 
    ROW_NUMBER() OVER (PARTITION BY RoomId ORDER BY DateStarted) AS RowNum, 
    DateStarted, DateEnded 
    FROM tbl_Bookings 
) 
SELECT RoomId, B.Ended AS S, C.Started AS E 
FROM Bookings B 
CROSS APPLY (
    SELECT B2.Started FROM Bookings B2 
    WHERE B2.RowNum = B.RowNum + 1 
    AND B2.Started <= @End 
    AND B2.RoomId = B.RoomId 
) C 
WHERE B.Ended >= @Start 

UNION 

-- Show any available time from @Start until the next DateStarted, unless @Start 
-- falls within a booked interval. 
SELECT RoomId, @Start, MIN(DateStarted) 
FROM tbl_Bookings 
WHERE DateStarted > @Start 
    AND NOT EXISTS (
     SELECT 1 FROM Bookings WHERE Started < @Start AND Ended > @Start 
    ) 
GROUP BY RoomId 

UNION 

-- Show any available time from the last DateEnded to @End, unless @End 
-- falls within a booked interval. 
SELECT RoomId, MAX(DateEnded), @End 
FROM tbl_Bookings 
WHERE DateEnded < @End 
    AND NOT EXISTS (
     SELECT 1 FROM Bookings WHERE Started < @End AND Ended > @End 
    ) 
GROUP BY RoomId 

Arbeiten SqlFiddle

+1

+1 für die Einführung in SQLFiddle – Qpirate

+0

Nur für "Vollständigkeit" habe ich dies bei [data.se] [hier] (http://data.stackexchange.com/stackoverflow/query/75613/so11511634) dupliziert. –

Verwandte Themen