it-swarm.com.de

Effiziente Abfrage der neuesten Datensätze mit Postgresql

Ich muss eine große Abfrage durchführen, möchte aber nur die neuesten Datensätze.

Für einen einzelnen Eintrag würde ich wohl sowas machen

SELECT * FROM table WHERE id = ? ORDER BY date DESC LIMIT 1;

Aber ich muss die neuesten Datensätze für eine große Anzahl (Tausende von Einträgen) von Datensätzen abrufen, aber nur den neuesten Eintrag.

Hier ist was ich habe. Es ist nicht sehr effizient. Ich habe mich gefragt, ob es einen besseren Weg gibt.

SELECT * FROM table a WHERE ID IN $LIST AND date = (SELECT max(date) FROM table b WHERE b.id = a.id);
45
Sheldon Ross

Wenn Sie Ihr Datenmodell nicht ändern möchten, können Sie mit DISTINCT ON Für jeden Eintrag in "a" den neuesten Datensatz aus Tabelle "b" abrufen:

SELECT DISTINCT ON (a.id) *
FROM a
INNER JOIN b ON a.id=b.id
ORDER BY a.id, b.date DESC

Wenn Sie eine "Sortierung" in der Abfrage vermeiden möchten, können Sie einen Index wie diesen hinzufügen könnte, aber ich bin mir nicht sicher:

CREATE INDEX b_id_date ON b (id, date DESC)

SELECT DISTINCT ON (b.id) *
FROM a
INNER JOIN b ON a.id=b.id
ORDER BY b.id, b.date DESC

Alternativ, wenn Sie Datensätze aus der Tabelle "a" sortieren möchten:

SELECT DISTINCT ON (sort_column, a.id) *
FROM a
INNER JOIN b ON a.id=b.id
ORDER BY sort_column, a.id, b.date DESC

Alternative Ansätze

Alle oben genannten Abfragen müssen jedoch noch alle referenzierten Zeilen aus Tabelle "b" lesen. Wenn Sie also viele Daten haben, ist diese möglicherweise immer noch zu langsam.

Sie könnten eine neue Tabelle erstellen, die nur den neuesten "b" -Datensatz für jeden a.id Enthält - oder diese Spalten sogar in die "a" -Tabelle selbst verschieben.

45
intgr

dies könnte effizienter sein. Unterschied: Abfrage für Tabelle b wird nur einmal ausgeführt, Ihre korrelierte Unterabfrage wird für jede Zeile ausgeführt:

SELECT * 
FROM table a 
JOIN (SELECT ID, max(date) maxDate
        FROM table
      GROUP BY ID) b
ON a.ID = b.ID AND a.date = b.maxDate
WHERE ID IN $LIST 
35
manji

On-Methode - Erstellen Sie eine kleine Ableitungstabelle mit den letzten Aktualisierungs-/Einfügezeiten für Tabelle a - Rufen Sie diese Tabelle a_latest auf. Für Tabelle a_latest ist eine ausreichende Granularität erforderlich, um Ihre spezifischen Abfrageanforderungen zu erfüllen. In Ihrem Fall sollte es ausreichen, zu verwenden

CREATE TABLE 
a_latest 
( id INTEGER NOT NULL, 
  date TSTAMP NOT NULL, 
  PRIMARY KEY (id, max_time) );

Verwenden Sie dann eine Abfrage ähnlich der von najmeddine vorgeschlagenen:

SELECT a.* 
FROM TABLE a, TABLE a_latest 
USING ( id, date );

Der Trick ist dann, a_latest aktuell zu halten. Verwenden Sie dazu einen Auslöser für Einfügungen und Aktualisierungen. Ein in plppgsql geschriebener Trigger ist ziemlich einfach zu schreiben. Gerne gebe ich Ihnen ein Beispiel.

Der Punkt hier ist, dass die Berechnung der neuesten Aktualisierungszeit während der Aktualisierungen selbst durchgeführt wird. Dies verlagert einen größeren Teil der Last von der Abfrage weg.

4
youngthing

was denkst du darüber?

select * from (
   SELECT a.*, row_number() over (partition by a.id order by date desc) r 
   FROM table a where ID IN $LIST 
)
WHERE r=1

ich habe es in der Vergangenheit oft benutzt

3
unknown

Wenn Sie viele Zeilen pro ID haben, möchten Sie auf jeden Fall eine korrelierte Unterabfrage. Pro ID wird 1 Index-Lookup durchgeführt, dies ist jedoch schneller als das Sortieren der gesamten Tabelle.

Etwas wie :

SELECT a.id,
(SELECT max(t.date) FROM table t WHERE t.id = a.id) AS lastdate
FROM table2;

Die 'table2', die Sie verwenden, ist nicht die Tabelle, die Sie in Ihrer obigen Abfrage erwähnt haben, da Sie hier eine Liste eindeutiger IDs für eine gute Leistung benötigen. Da Ihre IDs wahrscheinlich FKs in einer anderen Tabelle sind, verwenden Sie diese.

1
peufeu