0
votes

Utiliser SQL pour identifier les périodes de temps avec les dates de début et de fin

Je prépare des données pour un projet de recherche et j'ai des problèmes avec le défi suivant. Si possible, je voudrais tout faire avec SQL ou PL SQL (bien que je sois un vrai novice avec PL).

Supposons que nous ayons le tableau suivant (notez que la périphérie est la ligne souhaitée que j'essaie de créer): < / p> xxx

Les colonnes et le défi:

  • person_id : Les données ci-dessus sont toutes pour une personne (que les données sont au niveau de la transaction).
  • code : Ce code peut être un Start_Period, End_Period ou tout code aléatoire. Chaque code Start_Period doit avoir un code end_period correspondant. Le défi de ce problème est d'identifier toutes les paires de démarrage / fin pour créer la colonne Période_id. une nuance de ce défi: le code End_period est invalide s'il est dans 28 jours du code start_period. Par exemple, le code End_period de la ligne 2 n'est pas valide car il est le 15 janvier, seulement 14 jours après le 1er janvier. Au lieu de cela, le code End_Period valide est sur la ligne 5, car il est supérieur à 28 jours plus tard.
  • date : date de la transaction
  • périphérique_id : la ligne souhaitée - Cette information n'est pas actuellement sur la table.

1 commentaires

Cela semble être un problème des lacunes et des îles. Le site a déjà plusieurs questions sur de tels sujets pour Oracle. Peut-être que l'une de leurs réponses peut vous aider? Vérifiez-les


3 Réponses :


0
votes

Comptez simplement le nombre de périodes de démarrage à travers chaque ligne:

select t.*,
       sum(case when code = 'Start_period' then 1 else 0 end) over (partition by person_id order by date) as period_id
from t;


3 commentaires

Pourquoi avez-vous écrit "juste compter" mais utilisez "somme"? Utilisez la fonction de comptage à la place.


@ AKK0RD87. . . Le somme () fait un compte. En fait, compte (*) n'est pas vraiment nécessaire; C'est juste somme (1) . Si Oracle a appuyé la syntaxe standard, j'utiliserais un filtre compteur (*) (où code = 'start_period'). . . .


Qui parle de "Count (*)"? J'ai dit à propos de "Compte (dossier ... fin)" ou "comptage (décodage (...))".



0
votes

Voici une réponse en utilisant le match_recognize toujours intéressant. Notez que vous ne devriez pas réellement nommer les colonnes 'code' ou «date», puisqu'ils sont des mots-clés réservés.

match_recognize fonctionne sur plusieurs lignes et essaie de faire correspondre un motif donné. Dans votre cas, vous essayez de faire correspondre un modèle d'un code de départ, suivi de zéro ou d'autres codes d'extrémité non valides / autres codes, suivi d'un code d'extrémité valide. P>

WITH test_vals AS (
    SELECT 1 as person_ID,'Start_period' as my_code,to_date('Jan 1','mon dd') as my_date FROM DUAL
    UNION ALL SELECT 1,'End_period',to_date('Jan 15','mon dd') FROM DUAL
    UNION ALL SELECT 1,'Random_code1',to_date('Feb 15','mon dd') FROM DUAL
    UNION ALL SELECT 1,'Random_code2',to_date('Feb 28','mon dd') FROM DUAL
    UNION ALL SELECT 1,'End_period',to_date('March 31','mon dd') FROM DUAL
    UNION ALL SELECT 1,'Start_period',to_date('May 31','mon dd') FROM DUAL
    UNION ALL SELECT 1,'End_period',to_date('June 11','mon dd') FROM DUAL
    UNION ALL SELECT 1,'End_period',to_date('October 28','mon dd') FROM DUAL
)

SELECT m.person_id,
       m.my_code,
       m.my_date,
       m.period_id
FROM test_vals t
match_recognize(
    PARTITION BY person_id
    ORDER BY my_date
    MEASURES
        match_number() AS period_id /* Return the match number as the period ID */
    ALL ROWS PER match
    pattern (
        start_code /* Match a single start code */
        (invalid_end_code | other_code)* /* Match zero or more invalid end codes or other codes */
        valid_end_code /* Match a single end code */
    )
    define
        start_code AS my_code = 'Start_period', /* Start codes are always valid */
        valid_end_code AS my_code = 'End_period' AND (my_date - FIRST(my_date)) > 28, /* End codes are only valid if they come more than 28 days after the start of the pattern match */
        invalid_end_code AS my_code = 'End_period' AND (my_date - FIRST(my_date)) <= 28,
        other_code AS my_code NOT IN ('Start_period', 'End_period')
) m


0 commentaires

0
votes

J'utiliserais des CTE récursives, comme ici: xxx

Dbfiddle Demo

La logique consiste à utiliser la colonne chg qui change de la période de direction. CHG est défini sur 1 lorsque le code est période d'extrémité et la date supérieure à la date de début précédemment mémorisée. Dans la période d'étape suivante est incrémenté, chg réinitialiser à zéro et nouveau jeu de date de début.


0 commentaires