Sie benötigen ein paar CTEs dies zu erarbeiten. Die Grundidee besteht darin, alle belegten Zeiten so zu nutzen, wie Sie bereits haben, und dann die Daten in dieser Ergebnismenge zu verwenden, um die Lücken für jeden Raum zu finden.
Zuerst ist hier die vollständige Abfrage:
declare @startDate smalldatetime = '20160501',
@endDate smalldatetime = '20160701'
; with occupieds as (
SELECT Room.id,
RentContract.activeon,
RentContract.expireson,
'Occupied' as [State],
-- get ordering of contract for each rooms
row_number() over (partition by roomid order by activeon) SortOrder
FROM RentContract
INNER JOIN Room ON RentContract.roomid = Room.id
WHERE (RentContract.activeon >= @startDate
OR RentContract.activeon IS NULL)
AND (RentContract.expireson <= @endDate
OR RentContract.expireson IS NULL)
),
empties as (
select o1.id, o1.expireson + 1 as activeon, o2.activeon - 1 as expireson, 'Empty' as [State] from occupieds o1
inner join occupieds o2 on o1.id = o2.id and o1.SortOrder = o2.SortOrder - 1
),
extremes as (
select id, @startDate as activeon, min(activeon) - 1 as expireson, 'Empty' as [State] from occupieds group by id
having min(activeon) > @startDate
union all
select id, max(expireson) + 1 as activeon, @endDate as expireson, 'Empty' as [State] from occupieds group by id
having max(expireson) < @enddate
)
select id, activeon, expireson, [State] from occupieds
union all
select id, activeon, expireson, [State] from empties
union all
select id, activeon, expireson, [State] from extremes
order by id, activeon
Lassen Sie uns es brechen
Schritt 1 - belegt Räume
Dies, um Ihre aktuelle Abfrage fast identisch ist. Die einzige Ergänzung ist, dass wir row_number()
verwenden, um eine Bestellung der Verträge für jedes Zimmer zu erhalten. Wir werden das im nächsten Schritt verwenden.
SELECT Room.id,
RentContract.activeon,
RentContract.expireson,
'Occupied' as [State],
-- get ordering of contract for each rooms
row_number() over (partition by roomid order by activeon) SortOrder
FROM RentContract
INNER JOIN Room ON RentContract.roomid = Room.id
WHERE (RentContract.activeon >= @startDate
OR RentContract.activeon IS NULL)
AND (RentContract.expireson <= @endDate
OR RentContract.expireson IS NULL)
Dies ergibt folgende
id | activeon | expireson | State | SortOrder
1 | 2016-05-01 00:00:00 | 2016-05-31 00:00:00 | Occupied | 1
1 | 2016-06-15 00:00:00 | 2016-06-25 00:00:00 | Occupied | 2
2 | 2016-05-01 00:00:00 | 2016-07-01 00:00:00 | Occupied | 1
Schritt 2 - leere Räume zwischen Verträgen
Jetzt haben wir die Verträge für jedes Zimmer und der Reihenfolge, wie sie erscheinen, wir verwenden können, selbst beitreten zwischen einem Vertrag und dem nächsten, um den Datumsbereich auszuarbeiten, ist er leer. So wählen Sie die Zeile, dann verbinden Sie sich auf demselben roomid
mit dem vorherigen SortOrder
. In der obigen Tabelle wird Zeile 1 mit Zeile 2 verknüpft. Dies gibt uns ein Startdatum (das Ablaufdatum für Zeile 1) und ein Enddatum (das Aktiv für Zeile 2). Die wir nur hinzufügen/minus einem Tag, so dass sie sich nicht überlappen:
select o1.id, o1.expireson + 1 as activeon, o2.activeon - 1 as expireson, 'Empty' as [State] from occupieds o1
inner join occupieds o2 on o1.id = o2.id and o1.SortOrder = o2.SortOrder - 1
Schritt 3-
Der letzte Schritt Lücken am Anfang und Ende des Bereichs Verwaltung, wenn Sie ein Zimmer im leer Beginn des Bereichs - dies wird nicht in Schritt 1 berücksichtigt, da für den ersten Vertrag keine "vorherige" Zeile vorhanden ist.
Dazu müssen wir nur das früheste belegte Datum finden und dieses als Ablaufdatum für eine leere Periode verwenden. Wir überprüfen auch, dass dies nach dem Startdatum ist, so dass wir keine Einträge bekommen, die am selben Tag für ein ROM beginnen und enden, das tatsächlich besetzt ist.
Das gleiche gilt für das Enddatum der angewandten - die maximale Ablauf finden und endDate als Verwendungszweck:
select id, @startDate as activeon, min(activeon) - 1 as expireson, 'Empty' as [State] from occupieds group by id
having min(activeon) > @startDate
union all
select id, max(expireson) + 1 as activeon, @endDate as expireson, 'Empty' as [State] from occupieds group by id
having max(expireson) < @enddate
Schritt 4 - es alle zusammen
Wir haben alle Einträge wir jetzt brauchen, so wir gerade Vereinigung der drei Ergebnissätze zusammen:
select id, activeon, expireson, [State] from occupieds
union all
select id, activeon, expireson, [State] from empties
union all
select id, activeon, expireson, [State] from extremes
order by id, activeon
für das Leergut und Extreme CTEs, Sie können sie nur Unterabfragen in der endgültigen Vereinigung machen, aber ich trennte sie für Klarheit
; with occupieds as (
SELECT Room.id,
RentContract.activeon,
RentContract.expireson,
'Occupied' as [State],
row_number() over (partition by roomid order by activeon) SortOrder
FROM RentContract
INNER JOIN Room ON RentContract.roomid = Room.id
WHERE (RentContract.activeon >= @startDate
OR RentContract.activeon IS NULL)
AND (RentContract.expireson <= @endDate
OR RentContract.expireson IS NULL)
)
select id, activeon, expireson, [State] from occupieds
union all
select o1.id, o1.expireson + 1 as activeon, o2.activeon - 1 as expireson, 'Empty' as [State] from occupieds o1
inner join occupieds o2 on o1.id = o2.id and o1.SortOrder = o2.SortOrder - 1
union all
select id, @startDate as activeon, min(activeon) - 1 as expireson, 'Empty' as [State] from occupieds group by id
having min(activeon) > @startDate
union all
select id, max(expireson) + 1 as activeon, @endDate as expireson, 'Empty' as [State] from occupieds group by id
having max(expireson) < @enddate
order by id, activeon
wäre es besser, wenn Sie Tabellendaten mit dem erwarteten Ausgang – bmsqldev
Sie buchen beide 'mysql' und' sql-server', markiert haben, das ist es? –
Beginnen Sie mit einer Liste von zusammenhängenden Daten aus einem CTE, einer Datumstabelle usw., dann LINKEN JOIN zu Ihrer Abfrage. Fügen Sie reichlich COALECSE oder IFNULL für Ihre Status-Etiketten und backen ... –