2017-07-09 2 views
0

J'essaie de lier une table de contrats à une liste de transactions afin de voir s'il y a eu des dépenses excessives, cependant, les données n'ont pas une clé partagée unique et efficace.Postgresql chevauche des plages de dates et résume les résultats

Voici un exemple de mon contrat tableau:

| buyer_id | supplier_id | start_date | end_date | contract_value | 
| buyer_a | supplier_a | 2015-01-01 | 2017-01-01 | 240000   | 
| buyer_a | supplier_a | 2016-01-01 | 2016-06-01 | 6000   | 
| buyer_a | supplier_b | 2015-01-01 | 2015-12-31 | 100000   | 
| buyer_a | supplier_b | 2017-01-01 | 2017-12-31 | 100000   | 

Voici un exemple de ma table de dépenses:

| buyer_id | supplier_id | month  | trans_value | 
| buyer_a | supplier_a | 2015-01-01 | 1230.12  | 
| buyer_a | supplier_a | 2015-02-01 | 1735.98  | 
| buyer_a | supplier_a | 2015-03-01 | 2242.02  | 

Étant donné que les dates contractuelles se chevauchent (par exemple les contrats avec supplier_a), je peux » Il suffit de lier toutes les transactions pour chaque mois de chaque contrat car cela signifierait que je compte deux fois les transactions pendant la période de chevauchement.

De même, je ne peux pas utiliser max() et min() car toutes les transactions se produisant entre les contrats (par exemple celles avec supplier_b) seront incluses.

Pour autant que je peux dire la meilleure façon de relier ces tables est de retrousser mes contrats table dans une vue afin qu'il ressemble à ceci ... Comme

| buyer_id | supplier_id | month  | value | 
| buyer_a | supplier_a | 2015-01-01 | 10000 | 
| buyer_a | supplier_a | 2015-02-01 | 10000 | 
| buyer_a | supplier_a | 2015-03-01 | 10000 | 
| buyer_a | supplier_a | 2015-04-01 | 10000 | 
| buyer_a | supplier_a | 2015-05-01 | 10000 | 
| buyer_a | supplier_a | 2015-06-01 | 10000 | 
| buyer_a | supplier_a | 2015-07-01 | 10000 | 

longtemps que les valeurs chaque mois sont une part cumulée du contrat, il est facile de lier les transactions sur une colonne triple unique de buyer_id, supplier_id et month, et je peux ensuite identifier toute dépense excessive.

Le problème est que je ne peux même pas commencer à travailler sur la construction de la nouvelle vue. Je pense que je devrais être capable d'utiliser une sous-requête pour «décompresser» la plage de dates en une liste de mois puis quelque chose comme sum (case()) mais je suis hors de ma profondeur.

ps. Je n'ai aucun contrôle sur la façon dont ces données sont publiées, donc je ne peux pas améliorer les données à la source.

Edit: Je voudrais être en mesure de créer une sortie comme ce que je peux ensuite mettre dans un tableau pour montrer trop dépenser:

| buyer_id | supplier_id | month  | monthly_con_val | trans_value | 
| buyer_a | supplier_a | 2015-01-01 | 10000   | 34000  | 
| buyer_a | supplier_a | 2015-02-01 | 10000   | 10000  | 
| buyer_a | supplier_a | 2015-03-01 | 50000   | 8000  | 
| buyer_a | supplier_a | 2015-04-01 | 50000   | 14000  | 
| buyer_a | supplier_a | 2015-05-01 | 50000   | 4000  | 
| buyer_a | supplier_a | 2015-06-01 | 10000   | 3000  | 
| buyer_a | supplier_a | 2015-07-01 | 10000   | 3000  | 
+0

Pouvez-vous donner l'exemple de résultat que vous souhaitez obtenir? – Abelisto

Répondre

1

Quelque chose comme

with 
    -- Sample data 
    contracts(bs_id, start_date, end_date, contract_value) as (values 
    (1, '2015-01-01'::date, '2017-01-01'::date, 240000), 
    (1, '2016-01-01'::date, '2016-06-01'::date, 6000)), 
    spending(bs_id, month, trans_value) as (values 
    (1, '2015-01-01'::date, 1230.12), 
    (1, '2015-02-01'::date, 1735.98), 
    (1, '2016-05-01'::date, 5689.01)), 
    -- End of sample data 
    contracts_monthly as (
    select 
     bs_id, 
     month::date, 
     sum(
     contract_value/(
      (extract(year from end_date)*12 + extract(month from end_date)) - 
      (extract(year from start_date)*12 + extract(month from start_date)))) as monthly_con_val 
    from contracts, generate_series(start_date, end_date, interval '1 month') as month 
    group by bs_id, month 
    order by bs_id, month) 
select 
    * 
from 
    contracts_monthly left join spending using (bs_id, month); 

Pour faire la par exemple plus compact, j'avais fusionné les colonnes buyer_id | supplier_id en colonne unique bs_id.

About generate_series() function