2016-07-20 10 views
4

Ich bin auf der Suche nach einer Möglichkeit, die am besten bewerteten überlappenden Segmente auszuwählen. Tabelle würde wie folgt aussehen:Wählen Sie am besten überlappende Segment SQL

CODE START    STOP     RANK 
shift 2016-07-20 05:00 AM 2016-07-20 08:00 AM 5 
late 2016-07-20 05:00 AM 2016-07-20 05:08 AM 1 
break 2016-07-20 06:00 AM 2016-07-20 06:30 AM 2 

Das ist, was ich meine Ausgabe sein mag:

CODE START    STOP     
late 2016-07-20 05:00 AM 2016-07-20 05:08 AM 
shift 2016-07-20 05:08 AM 2016-07-20 06:00 AM 
break 2016-07-20 06:00 AM 2016-07-20 06:30 AM 
shift 2016-07-20 06:30 AM 2016-07-20 08:00 AM 

So ziemlich viel würde ich nur gerne sehen, was die bestplatzierten Segment sagt über diese Staat der Person, aber wenn sie keinen anderen Staat als das Standardsegment "Verschiebung" hatten, dann zeigen Sie einfach, dass sie auf ihrer Verschiebung sind.

Macht es Sinn? Bitte schieße auf Fragen oder Lösungsvorschläge. Ich kann mir in diesem Moment scheinbar nichts vorstellen. Ich kann die am besten bewerteten Segmente auswählen, aber nicht, wenn sie sich überschneiden.

EDIT: Wie Sie auf meinem gewünschten Ausgang der Verschiebung Segment außer Kraft gesetzt wird durch die spät Segment sehen kann, die einen höheren Rang hat (niedrigere Zahl bedeutet einen höheren Rang, wie üblich in der Rangfolge) von 05 : 00 AM bis 05:08 AM, aber ab 05:08 AM, da kein Segment es übersteuert, gehen wir zurück zu unserem Standardsegment shift von 05:08 AM bis 06:00 AM.

Dann gibt es eine geplante Pause Segment von 06.00 und 06.30 Uhr, die wieder überschreibt die Verschiebung Segment. Nachdem dies abgeschlossen ist, gehen wir zurück zu unserem Standardsegment shift von 06:30 Uhr bis 08:00 Uhr, wenn die shift endet.

Ich hoffe, das macht Sinn.

Antwort

1

Yay, ein SQL-Puzzle, ich kann nicht widerstehen! : D

Hier ist eine mögliche Lösung. Ich habe nicht SQLServer bei der Hand (my favorite database :) verwenden), aber SQL sollte vor allem tragbar sein:

create or replace table ranges(
     code varchar, 
     beg timestamp_ntz, 
     end timestamp_ntz, 
     rank integer); 
insert into ranges values 
     ('shift', '2016-07-20 05:00:00', '2016-07-20 08:00:00', 5), 
     ('late', '2016-07-20 05:00:00', '2016-07-20 05:00:08', 1), 
     ('break', '2016-07-20 06:00:00', '2016-07-20 06:30:00', 2); 

WITH PERIODS AS (
    select beg, lead(beg, 1) over (order by beg) AS end 
    from (select beg from ranges union select end from ranges) 
), 
MATCHING_RANGES AS (
    select periods.beg, periods.end, ranges.code, ranges.rank 
    from periods 
    join ranges on (periods.beg >= ranges.beg and periods.end <= ranges.end) 
    where periods.end is not null 
), 
RANKED_RANGES AS ( 
    select beg, end, code, row_number() over (partition by beg order by rank) in_period_rank 
    from matching_ranges 
) 
select code, beg, end from ranked_ranges 
where in_period_rank = 1 
order by beg; 

-------+---------------------+---------------------+ 
CODE |   BEG   |   END   | 
-------+---------------------+---------------------+ 
late | 2016-07-20 05:00:00 | 2016-07-20 05:00:08 | 
shift | 2016-07-20 05:00:08 | 2016-07-20 06:00:00 | 
break | 2016-07-20 06:00:00 | 2016-07-20 06:30:00 | 
shift | 2016-07-20 06:30:00 | 2016-07-20 08:00:00 | 
-------+---------------------+---------------------+ 

Erklärung (I „im Bereich“ für die ursprüngliche Tabelle zu verwenden, und „Perioden“ für Scheiben von diesen, wie das, was Sie in der Ausgabe wollen):

  • in ZEITEN schaffen wir alle verschiedene Momente in der Zeit, und die Verwendung LAG das nächste zu finden.Die Ausgabe ist:

    ---------------------+---------------------+ 
         BEG   |   END   | 
    ---------------------+---------------------+ 
    2016-07-20 05:00:00 | 2016-07-20 05:00:08 | 
    2016-07-20 05:00:08 | 2016-07-20 06:00:00 | 
    2016-07-20 06:00:00 | 2016-07-20 06:30:00 | 
    2016-07-20 06:30:00 | 2016-07-20 08:00:00 | 
    2016-07-20 08:00:00 | [NULL]    | 
    ---------------------+---------------------+ 
    
  • Dann in MATCHING_RANGES, für jede "Zeit" finden wir alle möglichen Bereiche von der definierten Tabelle (auch die letzte Zeile zu entfernen, NULL), Ausgang:

    ---------------------+---------------------+-------+------+ 
         BEG   |   END   | CODE | RANK | 
    ---------------------+---------------------+-------+------+ 
    2016-07-20 05:00:00 | 2016-07-20 05:00:08 | shift | 5 | 
    2016-07-20 05:00:00 | 2016-07-20 05:00:08 | late | 1 | 
    2016-07-20 05:00:08 | 2016-07-20 06:00:00 | shift | 5 | 
    2016-07-20 06:00:00 | 2016-07-20 06:30:00 | shift | 5 | 
    2016-07-20 06:00:00 | 2016-07-20 06:30:00 | break | 2 | 
    2016-07-20 06:30:00 | 2016-07-20 08:00:00 | shift | 5 | 
    ---------------------+---------------------+-------+------+ 
    

    Beachten Sie, wie das schafft alle Kombinationen von Bereichen und Perioden, die für jede Zeile berechnen wir die Priorität innerhalb seiner Periode

  • dann in RANKED_RANGES entsprechen:

    ---------------------+---------------------+-------+----------------+ 
         BEG   |   END   | CODE | IN_PERIOD_RANK | 
    ---------------------+---------------------+-------+----------------+ 
    2016-07-20 05:00:00 | 2016-07-20 05:00:08 | late | 1    | 
    2016-07-20 05:00:00 | 2016-07-20 05:00:08 | shift | 2    | 
    2016-07-20 05:00:08 | 2016-07-20 06:00:00 | shift | 1    | 
    2016-07-20 06:00:00 | 2016-07-20 06:30:00 | break | 1    | 
    2016-07-20 06:00:00 | 2016-07-20 06:30:00 | shift | 2    | 
    2016-07-20 06:30:00 | 2016-07-20 08:00:00 | shift | 1    | 
    ---------------------+---------------------+-------+----------------+ 
    
  • Und dann wählen wir einfach die Reihen mit Rang 1 :)

+0

Sounds freaking awesome ... Ich habe keine Chance zu testen es in meiner Umgebung habe aber die Lösungen sehen sehr plausibel und sehr intelligent ... Danke, dass du dir die Zeit genommen hast. Ich werde es testen und wenn alles richtig funktioniert, werde ich deine Antwort so gut signieren! :) –