2016-10-31 1 views
1

J'ai récemment commencé à utiliser postgres et je viens d'un environnement Oracle. Juste curieux de savoir si la requête que j'ai écrite peut être mieux implémentée dans postgres.scinde un enregistrement en plusieurs enregistrements en fonction de la catégorie de programmes regardée

Problème Détails:

J'ai deux tables:

  1. usage_detail
  2. Program_info

Usage_detail contient des informations sur tous les utilisateurs à regarder un canal. Par exemple l'utilisateur A a sa durée de la session pendant 1 heure 10 minutes 0 secondes, à partir de 13h15 aujourd'hui

User start_time   end_time 
A  2016-10-31 13:15:00 2016-10-31 14:25:00 

Table Program_info contient prévue détail du programme et la catégorie correspondante.

Par exemple:

Program_id program_category week_day start_time end_time 
     1 News    Monday  13:00  13:30 
     2 Sports   Monday  13:30  14:30 

La sortie Je cherche est:

User program_category start_time   duration (in seconds) 
    A News    2016-10-31 13:15:00  900 
    A Sports    2016-10-31 13:30:00  3300 

Mon approche actuelle:

Je partageais la durée de start_time et end_time dans l'intervalle de 30 minutes (car la catégorie de programme peut changer toutes les 30 minutes). Comme pour l'exemple que j'ai mentionné, j'ai d'abord créé 3 enregistrements (de 13h15 à 13h30, de 13h30 à 14h00, de 14h00 à 14h25) puis additionné la durée en fonction de la catégorie_programme .

J'ai écrit un code un peu moins lisible, qui génère dynamiquement plusieurs enregistrements sur un enregistrement sans utiliser la fonctionnalité array et unnest de postgres. Quelqu'un peut-il suggérer quelle pourrait être la meilleure façon d'aborder ce problème en utilisant Array/unnest ou toute autre fonctionnalité disponible dans postgres? Je ne cherche pas le code exact, juste la direction ferait.

+1

Dans ma partie du monde, le 31/10/2016 est un lundi pas un dimanche. –

+0

Je l'ai réalisé un peu plus tard, va éditer. Son temps de Diwali en Inde, les vacances sont sur, se sentait comme dimanche. : D – KSN

Répondre

1

Je ne pense pas que vous ayez besoin de générer des lignes. Sur la base de vos données d'exemple, vous pouvez simplement joindre les deux tables.

select * 
from program_info pi 
    join usage_detail ud 
    on to_char(ud.start_time, 'FMday') = lower(pi.week_day) 
    and (pi.start_time, pi.end_time) overlaps (ud.start_time::time, ud.end_time::time) 

(j'ai utilisé user_name au lieu de user, parce que user est un mot-clé réservé)

de noter que la jointure en utilisant to_char(ud.start_time, 'FMday') = lower(pi.week_day) exige que le jour de la semaine à stocker dans la même langue que to_char() va retourner. Il serait préférable de stocker cela comme un nombre, pas une chaîne.

Avec ce résultat, il est possible de calculer l'heure de début et de fin réelle pour chaque programme. Cela peut être fait avec une instruction compliquée case when comparant les informations de temps stockées dans usage_detail avec celle de program_info en vérifiant laquelle des heures de début était la plus grande et laquelle des temps de fin était la plus petite.

Cela peut toutefois être simplifié en utilisant une plage de temps.Malheureusement, il n'y a pas de temps de plage construit, mais il est facile de créer:

create type timerange as range (subtype = time); 

Avec que le début réel et de fin peuvent être calculées en utilisant l'intersection des deux gammes:

select ud.user_name, 
     pi.program_id, 
     pi.program_category, 
     ud.start_time::date as start_day, 
     timerange(pi.start_time, pi.end_time) * timerange(ud.start_time::time, ud.end_time::time) as view_interval 
from program_info pi 
    join usage_detail ud 
    on to_char(ud.start_time, 'FMday') = lower(pi.week_day) 
    and (pi.start_time, pi.end_time) overlaps (ud.start_time::time, ud.end_time::time) 

Le * est le intersection operator pour les gammes. Les rendements ci-dessus ceci:

user_name | program_id | program_category | start_day | view_interval  
----------+------------+------------------+------------+-------------------- 
A   |   1 | News    | 2016-10-31 | [13:15:00,13:30:00) 
A   |   2 | Sports   | 2016-10-31 | [13:30:00,14:25:00) 

Avoir le temps de visualisation réel une gamme peuvent maintenant être utilisés pour obtenir l'affichage final que vous voulez:

with view_times as (
    select ud.user_name, 
      pi.program_id, 
      pi.program_category, 
      ud.start_time::date as start_day, 
      timerange(pi.start_time, pi.end_time) * timerange(ud.start_time::time, ud.end_time::time) as view_interval 
    from program_info pi 
     join usage_detail ud 
     on to_char(ud.start_time, 'FMday') = lower(pi.week_day) 
     and (pi.start_time, pi.end_time) overlaps (ud.start_time::time, ud.end_time::time) 
) 
select user_name, program_id, program_category, 
     start_day + lower(view_interval) as actual_start_time, 
     extract(epoch from (upper(view_interval) - lower(view_interval))) as duration 
from view_times 

Ce retourne:

user_name | program_id | program_category | actual_start_time | duration 
----------+------------+------------------+---------------------+--------- 
A   |   1 | News    | 2016-10-31 13:15:00 |  900 
A   |   2 | Sports   | 2016-10-31 13:30:00 |  3300 

en ligne exemple: http://rextester.com/VNXIG64065

+0

choses impressionnantes, merci beaucoup. J'ai dû faire des changements mineurs comme sur les conditions aux limites pour l'heure (23h45 à 00h15), il a donné une erreur car il ne pouvait pas comprendre la limite supérieure est pour le jour suivant. J'ai ajouté une partie de date à l'intérieur, utilisé un cadre intégré et tout a fonctionné. – KSN