2016-06-30 9 views
0

Ich brauche alle Datensätze in TABLE_A zu bekommen, wo zumindest die letzten Status frei sind (bezogen auf Inspection_Date) und der Room_ID existiert nicht in TABLE_B.TSQL: Die Top-Aufzeichnungen einer bestimmten Partition (bedingt)

Dies ist eine vereinfachte Tabelle I als Beispiel verwenden:

TABLE_A:

Room_Id Status Inspection_Date 
    ------------------------------------- 
    1  vacant  5/15/2015 
    2  occupied 5/21/2015 
    2  vacant  1/19/2016 
    1  occupied 12/16/2015 
    4  vacant  3/25/2016 
    3  vacant  8/27/2015 
    1  vacant  4/17/2016 
    3  vacant  12/12/2015 
    3  vacant  3/22/2016 
    4  occupied 2/2/2015 
    4  vacant  3/24/2015 

Table_B:

Room_Id Status Inspection_Date 
    ------------------------------------ 
    1  vacant  5/15/2015 
    2  occupied  5/21/2015 
    2  vacant  1/19/2016 
    1  vacant  12/16/2015 
    1  vacant  4/17/2016 

Mein Ergebnis soll wie folgt aussehen:

Room_Id Status Inspection_Date 
    --------------------------------- 
    3  vacant  8/27/2015 
    3  vacant  12/12/2015 
    3  vacant  3/22/2016 
    4  occupied 2/2/2015 
    4  vacant  3/24/2015 
    4  vacant  3/25/2016 

Ich habe es auf diese Weise versucht, es mit dem Beispiel funktioniert aber nicht mit meinen Daten arbeiten .. die Logik ist nicht vollständig:

With cteA As 
(
Select *, Row_Number() Over (Partition By Room_ID, Status Order By  Inspection_Date Desc) RowNum From Table_A 
) 
Select * From Table_A Where Room_Id In 
(
Select Room_Id 
    From cteA 
    Where Room_Id Not In (Select Room_Id From Table_B) 
     And Status = 'vacant' And RowNum > 1 
) 
    Order By Room_Id, Inspection_Date 

Hier ist das Schema:

CREATE TABLE TABLE_A (`Room_Id` int, 
         `Status` varchar(55), 
         `Inspection_Date` Date 
        ); 

INSERT INTO TABLE_A (Room_Id, Status, Inspection_Date) 
VALUES (1, 'vacant',  '5/15/2015'), 
     (2, 'occupied', '5/21/2015'), 
     (2, 'vacant',  '1/19/2016'), 
     (1, 'occupied', '12/16/2015'), 
     (4, 'vacant',  '3/25/2016'), 
     (3, 'vacant',  '8/27/2015'), 
     (1, 'vacant',  '4/17/2016'), 
     (3, 'vacant',  '12/12/2015'), 
     (3, 'vacant',  '3/22/2016'), 
     (4, 'occupied',  '2/2/2015'), 
     (4, 'vacant',  '3/24/2015'); 

CREATE TABLE TABLE_B (`Room_Id` int, 
         `Status` varchar(55),   
         `Inspection_Date` Date 
        ); 

INSERT INTO TABLE_B (Room_Id, Status, Inspection_Date) 
VALUES 
     (1, 'vacant',  '5/15/2015'), 
     (2, 'occupied', '5/21/2015'), 
     (2, 'vacant',  '1/19/2016'), 
     (1, 'vacant',  '12/16/2015'), 
     (1, 'vacant',  '4/17/2016'),; 

Antwort

0

Ihre Bedingungen werden fast direkt in eine Abfrage übersetzt.

select a.* 
from (select a.*, 
      sum(case when status = 'vacant' then 1 else 0 end) over (partition by room_id) as num_vacant 
     from table_a a 
     where not exists (select 1 
         from table_b b 
         where b.room_id = a.room_id 
         ) 
    ) a 
where num_vacant >= 2; 

EDIT: Sie können Fensterfunktionen für den vakanten Zähl- und not exists für die Beziehung zu table_b verwenden

Wenn Sie die letzten beiden vakanten sein wollen, können Sie tun, um dieses letzte Datensatz finden, das nicht -vacant und dann die, die zählen größer als das:

select a.* 
from (select a.*, 
      sum(case when a2.max_nonvacant > a.inspection_date then 0 else 1) over (partition by room_id) as num_vacant_last 
     from table_a a outer apply 
      (select max(inspection_date) as max_nonvacant 
      from table_a a2 
      where a2.room_id = a.room_id and a2.status <> 'vacant' 
      ) a2 
     where not exists (select 1 
         from table_b b 
         where b.room_id = a.room_id 
         ) 
    ) a 
where num_vacant_last >= 2; 
+0

vielen Dank fro die schnelle Antwort leider funktioniert es nicht, die letzten (am nächsten Tage, bis heute) 2 Instanzen erinnern müssen immer frei sein .. –

1

EBENE

  1. Für jedes Zimmer in TABLE_A das letzte Datum wählen (als lastdate)

  2. für jedes Zimmer in TABLE_A vorheriges Datum wählen (als prevLastDate)

  3. room_ids von lastdate erhalten, die den Status 'frei' hat (als lastDateVacant)

  4. Get room_ids von prevLastDate, die den Status 'frei' hat (wie prevLastDateVacant)

  5. Filter TABLE_A nur IDs zu haben, die dort in lastDateVacant und prevLastDateVacant (innen)

  6. Filter TABLE_A nur IDs zu haben, die nicht in Table_B sind (linke Außen + IS NULL)

Als Ergebnis Sie haben:

WITH lastDate AS (
    SELECT room_id AS room,MAX(inspection_date) AS date 
    FROM "TABLE_A" 
    GROUP BY room_id 
), prevLastDate AS (
    SELECT room_id AS room,MAX(inspection_date) AS date 
    FROM "TABLE_A" a 
    INNER JOIN lastDate ON a.room_id = lastDate.room and a.inspection_date < lastDate.date 
    GROUP BY room_id 
), lastDateVacant AS (
    SELECT room_id AS room FROM "TABLE_A" 
    WHERE (room_id,inspection_date) IN (
     SELECT room, date FROM lastDate 
    ) AND status = 'vacant' 
), prevLastDateVacant AS (
    SELECT room_id AS room FROM "TABLE_A" 
    WHERE (room_id,inspection_date) IN (
     SELECT room, date FROM prevLastDate 
    ) AND status = 'vacant' 
) 

SELECT a.* FROM "TABLE_A" a 
INNER JOIN lastDateVacant 
    ON a.room_id = lastDateVacant.room 
INNER JOIN prevLastDateVacant 
    ON a.room_id = prevLastDateVacant.room 
LEFT OUTER JOIN "TABLE_B" AS b 
    ON a.room_id = b.room_id  
WHERE b.room_id IS NULL 
ORDER BY a.room_id ASC, a.inspection_date DESC 

Fensterfunktion

Nicht sicher, ob die Syntax für TSQL die gleiche ist, aber hier ist die kürzere Variante:

  1. Rang mit partion von Raum und oder nach Datum geordnet

  2. prüft IDs mit Rang 1 und 2 mit ‚frei‘ -Status, durch ID Gruppierung und mit ihnen aufgetreten mehr als einmal

mit Zimmer AS ( wählen Zimmer aus ( wählen room_id als Raum, Status, inspection_date Datum, RANK AS RANK() OVER (PARTITION BY room_id ORDER BY inspection_date DESC) von "TABLE_A" ) wo (Rang in (1,2) und status = 'vakant') Gruppe von Raum Zählung mit ()> 1 ) einem SELECT. FROM "TABLE_A" a INNER JOIN Raum ON a.room_id room.room = LEFT OUTER JOIN "Table_B" AS b ON a.room_id = b.room_id
WHERE b.room_id NULL ORDER BY A ist. room_id ASC, a.inspection_date DESC

+0

Schön gemacht! Vielen Dank –

+0

FYI - Die Syntax ist in der Tat ein wenig aus, aber die Logik funktioniert. Ich werde es bearbeiten, wenn es Ihnen nichts ausmacht. Danke noch einmal ! https://connect.microsoft.com/SQLServer/feedback/details/299231/add-support-for-ansi-standard-row-value-constructors –

+0

nach der Verwendung in prod habe ich einige Diskrepanzen gefunden Ich habe einen anderen Code gefunden das funktioniert, ich werde es nach dem Posten dieses Kommentars veröffentlichen. –

0

Ich habe diesen Test: extrahiert alle room_id, die die letzten zwei-Status (Gleichgestellte) in Bezug auf die inspection_date (absteigend) Berücksichtigung

select * from TABLE_A WHERE [Room_Id] IN 
(
    SELECT [Room_Id] FROM 
    (SELECT ROW_NUMBER() OVER(PARTITION BY [Room_Id] ORDER BY [Inspection_Date] DESC) AS id, 
     [Room_Id],[Status],[Inspection_Date] 
     FROM TABLE_A 
    ) AA 
    WHERE AA.ID <=2 
--selecting the last two Inspection_Date 
and [Status] = 'vacant' 
      GROUP BY [Room_Id],[Status] HAVING COUNT(*) >1   
) 
AND 
[Room_Id] NOT IN (SELECT Room_Id FROM TABLE_B) 
order by Room_Id, Inspection_Date desc 
0

Das ist für mich gearbeitet, und ich habe immer wieder überprüft.

with Rooms as (
select 
    Room_Id, Status, 
    row_number() over (partition by Room_Id order by Inspection_Date desc) as rn 
from TABLE_A 
), Candidates as (
select Room_Id from Rooms group by Room_Id 
having sum(case when rn in (1, 2) and Status = 'vacant' then 1 else null end) = 2 
) 
select * from TABLE_A 
where Room_Id in (select Room_Id from Candidates except select Room_Id from TABLE_B) 
order by Room_Id, Inspection_Date desc