Disons que c'est la date d'échantillon provenant d'une jointure de 2 tables. La base de données est Postgres 9.6
id product_id invoice_id amount date
1 PROD1 INV01 2 01-01-2018
2 PROD2 INV02 3 01-01-2018
3 PROD1 INV01 2 05-01-2018
4 PROD1 INV03 1 05-01-2018
5 PROD2 INV02 3 08-01-2018
6 PROD2 INV04 4 08-01-2018
Je veux savoir s'il est possible de manière optimisée de:
id product_id invoice_id amount date 3 PROD1 INV01 2 05-01-2018 4 PROD1 INV03 1 05-01-2018 5 PROD2 INV02 3 08-01-2018 6 PROD2 INV04 4 08-01-2018
Ça signifie:
product_id amount date
PROD1 2 01-01-2018
PROD2 3 01-01-2018
PROD1 2 02-01-2018
PROD2 3 02-01-2018
PROD1 2 03-01-2018
PROD2 3 03-01-2018
PROD1 2 04-01-2018
PROD2 3 04-01-2018
PROD1 3 05-01-2018
PROD2 3 05-01-2018
PROD1 3 06-01-2018
PROD2 3 06-01-2018
PROD1 3 07-01-2018
PROD2 3 07-01-2018
PROD1 3 08-01-2018
PROD2 7 08-01-2018
Quelques réflexions:
Pour la première question, je pourrais obtenir la max(date)
pour chaque PRODx et le choix pour chaque PRODx les lignes qui ont la date=with max(date)
mais je me demandais s'il y avait un moyen plus rapide d'obtenir cela étant donné un grand nombre des recors dans la base de données
Pour la deuxième question, je pourrais générer une série de dates pour l'intervalle nécessaire, puis utiliser WITH rows As
Et faire le regroupement des requêtes par product_id
Et sum
par montant, puis sélectionner pour chaque date les valeurs précédentes de rows
avec un limit 1
mais cela ne semble pas optimisé non plus.
Dans l'attente de toute entrée. Je vous remercie.
Modification ultérieure: essayer de tester DISTINCT ON ().
distinct on(product_id, invoice_id)
alors je n'ai pas seulement les plus récents pour la date la plus récente. S'il y avait des factures_id dans le passé, à côté de la dernière date, elles seront retournéesdistinct on (product_id)
alors il revient de la date la plus récente, mais comme d'habitude, seulement les dernières lignes même si au dernier jour j'ai deux positions pour PROD1.Fondamentalement, j'ai besoin de quelque chose comme "J'ai besoin de la date la plus récente, de tous les product_ids et de leurs facture_ids tout en gardant à l'esprit qu'un product_id peut avoir plusieurs facture_ids"
Édition ultérieure 2:
L'exécution d'une requête comme pour la première question semble être assez rapide:
select product_id, invoice_id, amount
from mytable inner join myOtherTable on...
inner join (select max(date) as last_date, product_id
from mytable
group by product_id) sub on mytable.date =
sub.last_date
Skinning Q # 1 indépendamment et légèrement différent de @ypercube
with cte as (select row_number() over (partition by product_id,
invoice_id
order by dt desc) as rn,
product_id,
invoice_id,
amount,dt
from product )
select product_id, invoice_id,amount,dt
from cte
where rn=1
order by product_id,invoice_id;
product_id | invoice_id | amount | dt
------------+------------+--------+------------
PROD1 | INV01 | 2 | 2018-01-05
PROD1 | INV03 | 1 | 2018-01-05
PROD2 | INV02 | 3 | 2018-01-08
PROD2 | INV04 | 4 | 2018-01-08
(4 rows)
Pour Q # 2, vous êtes sur la bonne voie, mais le SQL aura une jointure croisée (halètement!)
Je pense qu'une fonction avec une boucle/curseur serait plus optimisée (je vais essayer ça dans mon prochain bloc de temps libre)
--the cte will give us the real values
with cte as (select product_id,
sum(amount) as amount,
dt
from product
group by product_id,dt)
select p.product_id,
(select cte.amount --choose the amount
from cte
where cte.product_id = p.product_id
and cte.dt <= d.gdt -- for same day or earlier
order by cte.dt desc
limit 1) as finamt,
d.gdt
from (select generate_series( (select min(dt)
from product), --where clause if some products
--don't have an amount
(select max(dt)
from product),
'1 day'
)::date as gdt) d
cross join --assuming each listed product has an amount on the min date
(select distinct product_id
from product) p
left join --since we need to fill the gaps
cte on ( d.gdt = cte.dt
and p.product_id = cte.product_id)
order by d.gdt, p.product_id
;
Je comprends que vous souhaitez que toutes les lignes avec la dernière date pour chaque produit (liens inclus, c'est-à-dire toutes les lignes avec la dernière date). Cela peut être fait avec la fonction rank()
:
select id, product_id, invoice_id, amount, date
from
( select id, product_id, invoice_id, amount, date,
rank() over (partition by product_id
order by date desc) as rnk
from
-- your joins
) as t
where rnk = 1 ;
J'accepte votre méthode d'édition ultérieure, elle devrait être:
select product_id, invoice_id, amount
from mytable inner join
(select max(date) as last_date, product_id, invoice_id
from mytable
group by product_id) sub
on mytable.date = sub.last_date
and mytable.product_id = sub.product_id
and mytable.invoice_id = sub.invoice_id;
La "clé" doit être le date
, product_id
et invoice_id
.