2009-11-09 1 views
8

Hey, ho 2 tabelle di PostgreSQL:PostgreSQL, Selezionare tra 2 tavoli, ma solo l'ultimo elemento da tabella 2

1 - documents: id, title 
2 - updates: id, document_id, date 

e alcuni dati:

documenti:

| 1 | Test Title | 

aggiornamenti:

| 1 | 1 | 2006-01-01 | 
| 2 | 1 | 2007-01-01 | 
| 3 | 1 | 2008-01-01 | 

Quindi tutti gli aggiornamenti puntano allo stesso documento, ma tutti con date diverse per gli aggiornamenti.

Quello che sto cercando di fare è fare una selezione dalla tabella dei documenti, ma anche includere l'ultimo aggiornamento basato sulla data.

Come dovrebbe apparire una query come questa? Questo è quello che ho attualmente, ma sto elencando tutti gli aggiornamenti, e non l'ultimo uno come quello che ho bisogno:

SELECT * FROM documents,updates WHERE documents.id=1 AND documents.id=updates.document_id ORDER BY date 

Per includere; Il motivo per cui ho bisogno di questo nella query è che voglio ordinare per data dal modello di aggiornamento!

Edit: Questo script è fortemente semplificato, quindi dovrei essere in grado di creare una query che restituisce un numero qualsiasi dei risultati, ma compreso l'ultimo data aggiornata. Stavo pensando di usare un join interno o un join a sinistra o qualcosa del genere in questo modo !?

risposta

7

è possibile creare una tabella derivata che contiene solo i più recenti "aggiornamenti" record per document_id, e quindi unire "documenti" contro quella:

SELECT d.id, d.title, u.update_id, u."date" 
FROM documents d 
LEFT JOIN 
-- JOIN "documents" against the most recent update per document_id 
(
SELECT recent.document_id, id AS update_id, recent."date" 
FROM updates 
INNER JOIN 
(SELECT document_id, MAX("date") AS "date" FROM updates GROUP BY 1) recent 
ON updates.document_id = recent.document_id 
WHERE 
    updates."date" = recent."date" 
) u 
ON d.id = u.document_id; 

Questo consente di gestire i documenti "non-aggiornato", in questo modo :

pg=> select * from documents; 
id | title 
----+------- 
    1 | foo 
    2 | bar 
    3 | baz 
(3 rows) 

pg=> select * from updates; 
id | document_id | date  
----+-------------+------------ 
    1 |   1 | 2009-10-30 
    2 |   1 | 2009-11-04 
    3 |   1 | 2009-11-07 
    4 |   2 | 2009-11-09 
(4 rows) 

pg=> SELECT d.id ... 
id | title | update_id | date  
----+-------+-----------+------------ 
    1 | foo |   3 | 2009-11-07 
    2 | bar |   4 | 2009-11-09 
    3 | baz |   | 
(3 rows) 
-1

Questo dovrebbe funzionare anche

SELECT * FROM documents, updates 
    WHERE documents.id=1 AND updates.document_id=1 
    AND updates.date = (SELECT MAX (date) From updates) 
+0

No, come scritto al momento, questo non funzionerà. La sottoquery restituisce la "data" più recente dall'intera tabella degli aggiornamenti a prescindere dal documento, pertanto questo produrrà risultati solo se i documenti che si selezionano sono stati aggiornati alla data dell'aggiornamento più recente. – pilcrow

+0

Sei corretto. La sottoquery dovrebbe leggere SELECT MAX (data) Da aggiornamenti dove id = 1) –

2
select * 
from documents 
left join updates 
    on updates.document_id=documents.id 
    and updates.date=(select max(date) from updates where document_id=documents.id) 
where documents.id=?; 

Ha il alcuni vantaggi rispetto ai precedenti risposte:

  • è possibile scrivere document_id solo in un posto che è conveniente;
  • puoi omettere dove e riceverai una tabella di tutti i documenti e i loro ultimi aggiornamenti;
  • è possibile utilizzare criteri di selezione più ampi, ad esempio where documents.id in (1,2,3).

È anche possibile evitare una selezione secondaria utilizzando GROUP BY, ma dovrete elencare tutti i campi di documenti in gruppo dalla clausola:

select documents.*, max(date) as max_date 
    from documents 
    left join updates on documents.id=document_id 
    where documents.id=1 
    group by documents.id, title; 
12

Usa PostgreSQL estensione DISTINCT ON:

SELECT DISTINCT ON (documents.id) * 
FROM document 
JOIN updates 
ON  updates.document_id = document_id 
ORDER BY 
     documents.id, updates.date DESC 

Questo prenderà la prima riga da ciascun cluster document.id nell'ordine ORDER BY.

script di test per verificare:

SELECT DISTINCT ON (documents.id) * 
FROM (
     VALUES 
     (1, 'Test Title'), 
     (2, 'Test Title 2') 
     ) documents (id, title) 
JOIN (
     VALUES 
     (1, 1, '2006-01-01'::DATE), 
     (2, 1, '2007-01-01'::DATE), 
     (3, 1, '2008-01-01'::DATE), 
     (4, 2, '2009-01-01'::DATE), 
     (5, 2, '2010-01-01'::DATE) 
     ) updates (id, document_id, date) 
ON  updates.document_id = documents.id 
ORDER BY 
     documents.id, updates.date DESC