it-swarm.com.de

Abrufen von Zeilen mit dem neuesten Datum für jedes Element

Angenommen, dies ist das Beispieldatum, das aus einer Verknüpfung von 2 Tabellen stammt. Datenbank ist 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

Ich möchte wissen, ob es auf optimierte Weise möglich ist:

  1. Holen Sie sich alle PRODx mit ihren jeweiligen INVx, die das neueste Datum haben, aber pro product_id. Bitte beachten Sie, dass Datensätze, die von einem Tag nicht verwendet wurden, möglicherweise an einen neuen gemeldet werden. Das heisst:
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
  1. Erhalten Sie täglich summierte Beträge für jeden PRODx, füllen Sie jedoch die Lücken mit den vorherigen, wenn kein Tag vorhanden ist.

Das heisst:

 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

Ein paar Gedanken:

  1. Für die erste Frage konnte ich die max(date) für jeden PRODx und die Auswahl für jeden PRODx die Zeilen mit der date=with max(date) erhalten, aber ich habe mich gefragt, ob es bei einer großen Anzahl einen schnelleren Weg gibt, dies zu erhalten von Rekordern in der Datenbank

  2. Für die zweite Frage könnte ich eine Reihe von Daten für das benötigte Intervall generieren und dann WITH rows As Verwenden und die Abfrage nach product_id Und sum nach Betrag gruppieren und dann für auswählen jedes Datum die vorherigen Werte von rows mit einem limit 1, aber das klingt auch nicht so optimiert.

Ich freue mich auf jede Eingabe. Vielen Dank.

Später bearbeiten: Versuchen Sie, DISTINCT ON () auszuprobieren.

  • Wenn ich distinct on(product_id, invoice_id) habe, erhalte ich nicht nur die neuesten für das letzte Datum. Wenn in der Vergangenheit neben dem letzten Datum auch rechnungs-IDs vorhanden waren, werden diese zurückgegeben
  • Wenn ich distinct on (product_id) habe, kehrt es vom letzten Datum zurück, aber wie üblich nur die letzten Zeilen, auch wenn ich am letzten Tag zwei Positionen für PROD1 habe.

Grundsätzlich benötige ich etwas wie "Ich benötige für das letzte Datum alle Produkt-IDs und ihre Rechnungs-IDs, wobei zu berücksichtigen ist, dass eine Produkt-ID mehrere Rechnungs-IDs haben kann".

Später bearbeiten 2:

Das Ausführen einer Abfrage wie für die erste Frage scheint relativ schnell zu sein:

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 
7
Alin

Skinning Q # 1 unabhängig und etwas anders als @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)

Für Q # 2 sind Sie auf dem richtigen Weg, aber die SQL wird einen Cross Join haben (keuchen!)

Ich denke, eine Funktion mit einer Schleife/einem Cursor wäre optimierter (das werde ich in meinem nächsten freien Zeitblock versuchen).

--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
;
5
amacvar

Ich verstehe, dass Sie für jedes Produkt alle Zeilen mit dem neuesten Datum möchten (einschließlich Krawatten, d. H. Alle Zeilen mit dem letzten Datum). Dies kann mit der Funktion rank() erfolgen:

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 ;
3
ypercubeᵀᴹ

Ich bin damit einverstanden, dass Sie später bearbeiten: Es sollte sein:

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;

Der "Schlüssel" sollte date, product_id Und invoice_id Sein.

0
user166779