2016-06-02 5 views
4

Ich habe zwei Tabellen:T-SQL Hinzufügen von dynamischen Zeilen in resultset

  • Room
  • Contract (Mietverträge)
  • Beziehung: Contract n-1 Room

I Benutzer erhalten Eingabe startDate & endDate. Auf Basis dieses Intervalls frage ich was Contracts wo relevant für die Rooms. Relevante Mittel:

  • A Room wird während des vorgegebenen Intervall gemietet
  • A Room beginnt
  • im gegebenen Intervall gemietet werden
  • A Room Anschläge im gegebenen Intervall gemietet werden

Meine Anfrage ist:

SELECT Room.id, 
     RentContract.activeon, 
     RentContract.expireson 
FROM RentContract 
INNER JOIN Room ON RentContract.roomid = Room.id 
WHERE (RentContract.new_activeon >= @startDate 
     OR RentContract.new_activeon IS NULL) 
    AND (RentContract.new_expireson <= @endDate 
     OR RentContract.new_expireson IS NULL) 

Jetzt muss ich zusätzlich die nicht gemieteten Intervalle für diese Räume anzeigen. Da ich das nicht in meiner Datenbank habe, muss ich einige dynamische Zeilen einfügen, die ich in der gleichen Liste anzeigen werde. Außerdem werde ich eine zusätzliche Spalte im Ergebnis (State) anzeigen, die "Besetzt" für die aktuellen Verträge und "leer" für die "dynamischen" Zeilen anzeigt.

So, als Beispiel, Benutzereingabe: startdate = 2016.05.01, endDate = 01.07.2016

Mein Ergebnis ist gerade jetzt:

enter image description here

Mein gewünschtes Ergebnis ist:

enter image description here

Also brauche ich eigentlich zu „füllen“, um das gesamte Eingabeintervall entweder mit db Aufzeichnungen oder dynamisch re Schnüre


@Rhumborl Ihre Lösung ausgearbeitet fast für mich! Es gibt noch ein kleines Detail:

Ich bekomme einige Ergebnisse, wo ein Vertrag am 2. eines Monats beginnt, also würde ich denke, ich müsste einen "leeren" Eintrag für diesen Tag haben. Z.B. 01.01.2016 bis 01.01.2016 Leer.Hier ist eine Teilmenge von meinem ersten Ergebnis und das Ergebnis, das ich von Ihnen Lösung bekam (I markiere als gelbes das spezifische Szenario):

Erstabfrage:

enter image description here

Rhumborl Abfrage:

enter image description here

Gibt es ein wenig zwicken auf Ihre Abfrage, um das zu lösen?

+2

wäre es besser, wenn Sie Tabellendaten mit dem erwarteten Ausgang – bmsqldev

+0

Sie buchen beide 'mysql' und' sql-server', markiert haben, das ist es? –

+0

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 ... –

Antwort

4

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

das sieht sehr gut aus. Ich werde auschecken und zu dir zurückkommen. Vielen Dank – user1016077

+0

Vielen Dank Ihre Hilfe. Ich habe eine Frage zu einem Detail, bitte überprüfe meine Bearbeitung. – user1016077

+0

Sorry, kleiner Bug im letzten Abschnitt. Wir müssen nur überprüfen, ob das min-Datum größer ist als @ startDate, nicht +/- 1 Tag. Aktualisiert. – Rhumborl

3

Sie können es mit Hilfe von cte tun. Da Sie nicht mehr Daten zur Verfügung gestellt haben - mit arbeiten, was geschrieben wird:

DECLARE @startdate date = '2016-05-01', 
     @enddate date = '2016-07-01' 

;WITH cte AS (
    SELECT r.id, 
      rc.activeon, 
      rc.expireson, 
      'Occupited' as [state] 
    FROM RentContract rc 
    INNER JOIN Room r 
     ON rc.roomid = r.id 
    WHERE (rc.new_activeon >= @startDate OR rc.new_activeon IS NULL) 
     AND (rc.new_expireson <= @endDate OR rc.new_expireson IS NULL) 
) 


SELECT * 
FROM cte 
UNION ALL 
SELECT id, 
     activeon, 
     CASE WHEN expireson < activeon THEN @enddate ELSE expireson END as expireson, 
     'Empty' as [state] 
FROM (
    SELECT id, CAST(DATEADD(day,1,expireson) as date) as activeon, ROW_NUMBER() OVER (ORDER BY expireson ASC) as rn 
    FROM cte 
    ) as s 
INNER JOIN (
    SELECT CAST(DATEADD(day,-1,activeon)as date) as expireson, ROW_NUMBER() OVER (ORDER BY expireson ASC) as rn 
    FROM cte 
    ) as e 
    ON e.rn=s.rn+1 
ORDER BY id, activeon 

Ausgang:

id activeon expireson state 
1 2016-05-01 2016-05-31 Occupited 
1 2016-06-01 2016-06-14 Empty 
1 2016-06-15 2016-06-25 Occupited 
1 2016-06-26 2016-07-01 Empty 
2 2016-05-01 2016-07-01 Occupited 
+0

das sieht gut aus. Ich werde auschecken und zu dir zurückkommen. Vielen Dank – user1016077

Verwandte Themen