2017-02-01 4 views
1

Betrachten Sie eine Tabelle, die im Laufe der Zeit die Lieblingsfarben verfolgt.Aufräumen Überlappung Gültig von - gültig bis

drop table favourites; 

create table favourites(
    person_id varchar2(10) not null 
    ,valid_from date   not null 
    ,valid_to date 
    ,color  varchar2(10) not null 
    ,constraint favourites_pk primary key(person_id, valid_from) 
); 

insert into favourites values('Ronnie', date '1979-09-12', null,    'Green'); 
insert into favourites values('Ronnie', date '2000-01-01', date '2016-12-31', 'Blue'); 

commit; 

Die Tabelle besagt, dass ‚Ronnie‘ von 1979.09.12 grün mag, und dann von 2000.01.01 er mag blau. Als Ronnie am ersten Tag des Jahres 2017 aufwacht, mag er Blue nicht mehr.

Ich muss eine Tabelle mit einem einmaligen Skript aufräumen, aber ich bin auf dem oben gezeigten bestimmten Fall fest. Die beste Idee bis jetzt ist es, die gültigen Daten auf der Grundlage der valid_from Datums neu zu berechnen, aber in diesem Fall zerstöre ich tatsächlich Informationen: Wie unten zu sehen ist, ist "grün" nicht mehr der Favorit am 01.01.2017, was es sein sollte.

select person_id 
     ,valid_from  
     ,valid_to 
     ,lead(valid_from,1) over(partition by person_id order by valid_from)-1 as valid_to2 
     ,color   
    from favourites t; 


PERSON_ID VALID_FROM VALID_TO NEW_VALID_TO COLOR 
--------- ---------- ---------- ------------ ------ 
Ronnie  1979-09-12 null  1999-12-31  Green 
Ronnie  2000-01-01 2016-12-31 null   Blue 

Wie kann ich einen aditional Datensatz erzeugen, so dass das Endergebnis sein wird:

PERSON_ID VALID_FROM VALID_TO COLOR 
--------- ---------- ---------- ------ 
Ronnie  1979-09-12 1999-12-31 Green 
Ronnie  2000-01-01 2016-12-31 Blue 
Ronnie  2017-01-01 null  Green 

bearbeiten Es gibt keine rationale Logik hinter dem valid_to Datum. Es ist das, was die Benutzer einsetzen, und deshalb gibt es alle möglichen verrückten Überschneidungen. Der Nullwert bedeutet "für immer" oder 9999-12-31, wenn er leichter zu verstehen ist. Ich möchte diesen Entwurf reparieren, indem ich eine andere Tabelle erstelle, aber zuerst brauche ich einen Weg, um die alten Daten zu reparieren.

Vielleicht ist es einfacher, es so zu betrachten.

Green |----------------------------------> 
Blue   |------| 

ich die Überlappung durch die Berechnung ein neues valid_to Datums zu beheben, zerstören ich Informationen:

Green |--------| 
Blue   |------| 

es wie folgt festgelegt werden muss:

Green |--------| 
Blue   |------| 
Green     |--------> 

Das Problem in den ursprünglichen Daten-Set nur Manifeste für die zweite Abfrage (aufgrund der Überlappung). Die anderen beiden zeigen das korrekte Ergebnis.

select * 
    from favourites 
where (date '1995-01-01' >= valid_from) 
    and (date '1995-01-01' <= valid_to or valid_to is null); 

select * 
    from favourites 
where (date '2015-01-01' >= valid_from) 
    and (date '2015-01-01' <= valid_to or valid_to is null); 

select * 
    from favourites 
where (date '2025-01-01' >= valid_from) 
    and (date '2025-01-01' <= valid_to or valid_to is null); 
+0

Ist dies das einzige Beispiel für Überlappung? –

+1

Warum sollte die Farbe in der neu eingefügten Zeile grün sein? kann es null sein? –

+0

woher weißt du, dass er 2017 wieder grün mag? Gibt es irgendwo eine Standardregel? – tbone

Antwort

0

Wenn Ihr Beispiel die eine Art der Überlappung, die Ihnen wichtig sind, dann können Sie dies mit union all behandeln. Ich denke, das ist die Abfrage, die Sie wollen:

select person_id, color, valid_from, 
     coalesce(valid_to, lead(valid_from) over (partition by person_id order by valid_from)-1) as valid_to2 
from favourites t 
union all 
select person_id, color, valid_to + 1 as valid_from, NULL as valid_to 
from favourites f 
where valid_from is null and 
     exists (select 1 from favourites f2 where f2.person_id = f.person_id and f2.valid_from > f.valid_from); 

Durch die einzige Art, ich meine, dass nur ein valid_fromNULL ist und die Aufzeichnungen nicht anders überlappen. Wenn Sie eine mehr generierte Frage haben, sollten Sie eine andere Frage mit geeigneten Daten und Erläuterungen dazu, was mit den Überschneidungen zu tun ist, stellen.

0

Das Enddatum für die aktuelle Zeile ist einfach (subtrahiert 1 Tag vom Datum der nächsten Zeile). Sie sollten jedoch eine neue Zeile generieren, die eine union all erfordern würde. Außerdem wird die Standardfarbe aus der Zeile ausgewählt, in der valid_tonull ist. (Sie müssen die Logik in der CTE ändern, wenn dies nicht der Fall ist).

WITH CTE AS 
(SELECT PERSON_ID, 
     COLOR, 
     VALID_FROM, 
     ROW_NUMBER() OVER(PARTITION BY PERSON_ID ORDER BY VALID_FROM DESC) AS RNUM, 
     COALESCE(VALID_TO, LEAD(VALID_FROM) OVER (PARTITION BY PERSON_ID 
                ORDER BY VALID_FROM)-1) AS VALID_TO_NEW, 
     MAX(CASE WHEN VALID_TO IS NULL THEN COLOR END) 
        OVER(PARTITION BY PERSON_ID ORDER BY VALID_FROM) AS DEFAULT_COLOR 
    FROM FAVOURITES) 
SELECT PERSON_ID, 
     VALID_FROM, 
     VALID_TO_NEW, 
     COLOR 
FROM CTE 
UNION ALL 
--Generates a new last row with the default color if it exists 
SELECT PERSON_ID, 
     VALID_TO_NEW+1, 
     NULL, 
     DEFAULT_COLOR 
FROM CTE 
WHERE RNUM=1 

Sample Demo