6
votes

Intervalles de date d'aplatissement dans SQL

J'ai une table de base de données où il y a trois colonnes essentielles à cette question:

  • un identifiant de groupe, qui regroupe des rangées ensemble
  • Date de début
  • une date de fin

    Je veux faire une vue de cette table afin que intervalles de date qui se chevauche , qui ont le même identifiant de groupe, sont aplaties.

    Les intervalles de date qui ne se chevauchent pas ne doivent pas être aplaties.

    exemple: xxx

    ... devient ... xxx

    intervalles qui se chevauchent peut-être faites-le, entièrement enfermé par d'autres intervalles, ou ils peuvent être échelonnés, ou ils peuvent même avoir les mêmes dates de départ et / ou de fin.

    Il y a peu similaire IDS. Communément (> 95%), il n'y a qu'une seule ligne avec un ID de groupe particulier. Il y a environ mille identifiants qui apparaissent dans deux rangées; une poignée d'identifiants existant dans trois rangées; aucun qui se trouvent dans quatre rangées ou plus.

    mais je dois être prêt à montrer qu'il peut présenter des identifiants de groupe qui existent dans quatre lignes ou plus.

    Comment puis-je écrire une instruction SQL qui crée une vue qui montre la table Aplaté de cette façon?

    Notez que chaque ligne a également un identifiant unique. Cela n'a aucune intention d'être préservé de quelque manière que ce soit, mais au cas où cela vous aidera à écrire le SQL, je vous laisse savoir.


4 Réponses :


1
votes

Créer 2 fonctions qui renvoient la date de démarrage et de fin aplatie pour un élément spécifique: xxx

Votre vue devrait ensuite revenir, pour chaque élément, ces dates aplaties.
Bien sûr, distinct car divers éléments peuvent entraîner les mêmes dates: xxx


1 commentaires

Merci, je regarde à travers les suggestions. Cela prend du temps depuis que je rencontre des données mauvaises inattendues (telles que la date de début ultérieure que la date de fin), je vais donc essayer de contourner ces choses pour valider les suggestions.



1
votes
   select t1.group_id, least(min(t1.start_date),  min(t2.start_date)),  greatest(max(t1.start_date), max(t2.end_date)) from test_interval t1, test_interval t2 
   where (t1.start_date, t1.end_date) overlaps (t2.start_date, t2.end_date) 
      and t1.rowid <> t2.rowid 
      and t1.group_id = t2.group_id group by t1.group_id;
Such query produces for me list of overlapping intervals. OVERLAPS is an undocumented operator. I only wonder if that won't return wrong result when we got two pair of intervals that are overlapping in pair but not each other. 
Where I used rowid you can use your unique row identifier

2 commentaires

Merci, je regarde à travers les suggestions. Cela prend du temps depuis que je rencontre des données mauvaises inattendues (telles que la date de début ultérieure que la date de fin), je vais donc essayer de contourner ces choses pour valider les suggestions.


Je suis désolé de devoir revenir sur ce que j'ai dit, mais les réponses d'Alexandre sont encore meilleures. Désolé d'avoir à prendre la meilleure réponse, c'est totalement ma faute de ne pas avoir l'air de plus près. :(



2
votes

Tout d'abord, trouvez des intervalles qui ne se poursuivent pas de la séquence qui se chevauchent: xxx pré>

La dernière ligne distingue les intervalles commençant à la même date / heure, en les commandant par un ID d'enregistrement unique (R_ID). P>

Pour chaque enregistrement de ce type, nous pouvons obtenir une sélection hiérarchique des enregistrements avec Connect_By_ROOT R_ID distinguant les groupes de pince. Après cela, tout ce dont nous avons besoin, c'est d'obtenir Min / Max pour CLAMP Group (Connect_By_root R_ID est ID de l'enregistrement parent dans le groupe): P>

CREATE TABLE "ANIKIN"."DATECLAP" 
(   
    "R_ID" NUMBER, 
    "GROUP_ID" NUMBER, 
    "START_DATE" DATE, 
    "END_DATE" DATE
) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "ANIKIN" ;


10 commentaires

Merci, je regarde à travers les suggestions. Cela prend du temps depuis que je rencontre des données mauvaises inattendues (telles que la date de début ultérieure que la date de fin), je vais donc essayer de contourner ces choses pour valider les suggestions.


C'est une solution vraiment soignée. Au début, je pensais que cela ne fonctionnait pas mais c'était parce que j'avais des données vraiment mauvaises dans des endroits. Cela semble être une solution unique. Je suis vraiment désolé d'avoir déjà donné la meilleure réponse. Celui-ci semble être juste la chose. EDIT: Modifier la meilleure réponse ici. Désolé pour la confusion.


@Michaelkarnerfors - Il y a des erreurs de syntaxe évidentes, il n'ya donc aucun moyen que cette requête puisse fonctionner comme - est (certainement pas en oracle). Peut vous ou Alexander s'il vous plaît réparer afin que cela puisse être testé. Première erreur rencontrée était l'identifiant r_id n'est pas défini n'importe où.


@Mathguy c'est l'identifiant de la rangée et je substitué qu'avec le mien.


Voulez-vous dire group_id? Sinon, quelle identification gentil ou ligne?


> Notez que chaque ligne a également un identifiant unique. J'ai ajouté colonne r_id qui contient une carte d'identité unique pour l'enregistrement


J'ai compris. Belle solution!


@Alexanderanikin J'ai un problème avec cette solution. Il ne peut sembler gérer ce triplet de données: 2014-09-16 à 2016-07-01, 2014-09-04 à 2014-12-31 et 2014-09-16 à 2014-12-31.


@Michaelkarnerfors Cela fonctionne pour moi, revenant 1 spa de 2014-09-04 à 2016-07-01. Essayez d'abord sélectionner de vérifier la sélection d'enregistrements. Si c'est correct, essayez ceci pour voir la sélection hiérarchique pour un premier enregistrement défini: Sélectionnez D1. *, Niveau, Connect_By_root R_ID à partir de Dateclap D1 Démarrer avec R_ID = ... Connectez-vous par NOCYCLE ANTER GROUP_ID = GROUP_IDE ENTRE DE START_DATE PRÉAL_DATE.


@Alexanderanikin ah, vous avez raison, il y avait une erreur de ma part.



1
votes

Les données d'entrée indiquent une date de fin de 9999-13-31 dans la dernière ligne. Cela devrait être corrigé.

Avec cela dit, il est préférable de choisir une date de fin inventée qui n'est pas exactement 9999-12-31. Dans de nombreux problèmes, il faut ajouter une journée, ou quelques semaines, ou quoi que ce soit, à toutes les dates d'une table; Mais si l'on essaie d'ajouter au 9999-12-31, cela échouera. Je préfère 8999-12-31; Mille ans devrait suffire à la plupart des calculs. {:-) Dans les données de test que j'ai créées pour ma requête, j'ai utilisé cette convention. (La solution peut être facilement adaptée au 9999-12-31 cependant.) P>

Lorsque vous travaillez avec des intervalles de dateTime, rappelez-vous qu'une date pure signifie minuit au début d'une journée. Ainsi, l'année 2016 a la «date de fin» 2017-01-01 (minuit au début de la journée) et l'année 2017 a également la «date de début» 2017-01-01 également. Donc, la table doit avoir la même date de fin et la même date de début des périodes qui se suivent immédiatement - et elles doivent être fusionnées en un seul intervalle. Cependant, un intervalle se termine le 2016-08-31 et celui qui commence le 2016-09-01 ne devrait pas être fusionné ensemble; Ils sont séparés par une journée complète (plus précisément le jour de 2016-08-31 ne sont pas inclus dans l'un ou l'autre intervalle). P>

L'OP n'a pas spécifié comment les dates finales sont censées être interprétées ici. Je suppose qu'ils sont comme décrit dans le dernier paragraphe; Sinon, la solution peut être facilement adaptée (mais elle nécessitera d'ajouter 1 à la fin des dates d'abord, puis de soustraire 1 à la fin - c'est exactement l'un de ces cas lorsque 9999-12-31 n'est pas un bon espace réservé pour "inconnu". ) P>

Solution forte>:: p> xxx pré>

sortie fort> (avec les données fournies): P >

  GROUP_ID START_DATE END_DATE 
---------- ---------- ----------
         1 2016-01-01 2020-01-01
         1 2022-08-31 2030-12-31
         2 2010-03-01 2017-01-01
         3 2001-01-01 8999-12-31


2 commentaires

Le puits 9999-12-31 a de nombreux événements dans la table et il n'y a pas grand chose que je puisse faire à ce sujet. Il est utilisé pour marquer "pour toujours". Vous avez raison dans une date du dernier août et le premier septembre ne devrait pas être fusionné.


Si les dates d'extrémité sont interprétées comme je l'ai décrite (vous avez confirmé qu'ils sont), la solution n'a pas besoin de faire de l'arithmétique sur eux du tout, il fonctionnera donc aussi bien avec 9999-12-31.