2013-02-05 21 views
6

Ho una tabella I messaggi che assomiglia a questo:PostgreSQL interrogazione posta in arrivo

    Messages 
+-----+------------+-------------+--------------+ 
| id | sender_id | receiver_id | created_at | 
+-----------------------------------------------+ 
| 1 |  1  |  2  | 1/1/2013 | 
| 2 |  1  |  2  | 1/1/2013 | 
| 3 |  2  |  1  | 1/2/2013 | 
| 4 |  3  |  2  | 1/2/2013 | 
| 5 |  3  |  2  | 1/3/2013 | 
| 6 |  5  |  4  | 1/4/2013 | 
+-----------------------------------------------+ 

Qualora un 'filo' è un gruppo di messaggi tra un dato sender_id e receiver_id voglio una query per restituire il più recente 10 messaggi per i 10 thread più recenti in cui il sender_id o il receiver_id è un dato id.

uscita prevista se proposta user_id è 5:

+-----+------------+-------------+--------------+ 
| id | sender_id | receiver_id | created_at | 
+-----------------------------------------------+ 
| 1 |  5  |  2  | 1/4/2013 | 
| 2 |  5  |  2  | 1/4/2013 | 
| 3 |  2  |  5  | 1/4/2013 | 
| 4 |  3  |  5  | 1/4/2013 | 
| 5 |  5  |  2  | 1/3/2013 | 
| 6 |  5  |  4  | 1/3/2013 | 
+-----------------------------------------------+ 

fino ad un limite di 10 messaggi tra, per esempio, l'utente 5 e 2 (sopra ci sono 4) e un limite di 10 fili (sopra ci sono 3).

Ho provato con questo tipo di query utilizzando una sottoquery ma non sono riuscito a ottenere il secondo limite sul numero di thread distinti.

SELECT * FROM (SELECT DISTINCT ON (sender_id, receiver_id) messages.* 
FROM messages 
WHERE (receiver_id = 5 OR sender_id = 5) ORDER BY sender_id, receiver_id, 
created_at DESC) 
q ORDER BY created_at DESC 
LIMIT 10 OFFSET 0; 

sto considerando la creazione di una nuova tabella contenente un campo Discussione ID_Thread che sarebbe la concatenazione di sender_id + receiver_id e poi basta entrare in messaggi ma ho un sospetto subdolo che dovrebbe essere fattibile con un solo tavolo .

+0

Puoi mostrare la query che hai provato e l'output previsto in base ai dati di esempio che hai mostrato sopra? – bonCodigo

+0

Giusto per essere chiari stai chiedendo 1 messaggio per thread (restituendo al massimo 10 righe) o 10 messaggi per thread (restituendo al massimo 100 righe). –

+0

@couling Sto chiedendo 10 messaggi per thread (restituendo al massimo 100 righe) – johnnymire

risposta

0

non ho ancora testato questo, ma sembra che hai dimenticato la LIMIT 10 sul sottoquery che vi dà i 10 thread più recenti-:

SELECT 
    * 
FROM 
    (SELECT DISTINCT ON 
    (sender_id, receiver_id) messages.* 
    FROM 
    messages 
    WHERE 
    (receiver_id = 5 OR sender_id = 5) 
    ORDER BY 
    sender_id, receiver_id, created_at DESC 
    LIMIT 
    10) 
    q 
ORDER BY 
    created_at DESC 
LIMIT 
    10 
OFFSET 
    0; 

(ho abbastanza stampata l'SQL in modo che è più facile capire cosa sta succedendo.)

1

Sto postando questo per mostrare cosa si può fare.

Non consiglio davvero di usarlo.

Sarebbe molto meglio fare due query separate: 1 per recuperare i 10 thread più recenti e 1 ripetuto per richiamare i 10 messaggi più recenti per ogni thread.

Tuttavia, è possibile raggiungere il proprio obiettivo con lo rank()window function come illustrato di seguito.

select * from (
     select message.*, 
      rank() over (partition by message.sender, message.receiver 
           order by sent desc) 
     from sof_messages message, 
      (
      select sender, 
        receiver, 
        max(sent) 
       from sof_messages 
      where receiver = <user> 
       or sender = <user> 
      group by sender, 
        receiver 
      order by 3 
      limit 10 
      ) thread 
     where message.sender = thread.sender 
     and message.receiver = thread.receiver 
    ) message_list 

where rank <= 10 

Ci sono un paio di domande diverse che raggiungere il tuo obiettivo con funzioni finestra, nessuno di loro particolarmente pulito.

1

Creazione di una tabella Thread sembra sbagliato a causa della duplicazione dei dati, ma la vista può aiutare:

CREATE VIEW threads AS 
    SELECT sender_id, receiver_id, min(created_at) AS t_date 
    FROM messages 
    GROUP BY sender_id,receiver_id; 

Change min(created_at) a max(created_at) se la data del thread è quello di essere alla data della sua più recente messaggio, piuttosto che il il più vecchio.

allora può essere unito di nuovo ai messaggi semplicemente con:

SELECT ... FROM messages JOIN threads USING (sender_id,receiver_id) 
2

La query più ordinata potevo immaginare di risolvere il problema entro una query è la seguente:

select * from (
    select row_number() 
    over (partition by sender_id, receiver_id order by created_at desc) as rn, m.* 
    from Messages m 
    where (m.sender_id, m.receiver_id) in (
    select sender_id, receiver_id 
    from Messages 
    where sender_id = <id> or receiver_id = <id> 
    group by sender_id, receiver_id 
    order by max(created_at) desc 
    limit 10 offset 0 
) 
) res where res.rn <= 10 

La colonna row_number() over (partition by sender_id, receiver_id order by created_at desc) conterrà il numero di riga di ciascun messaggio all'interno di ciascun thread (sarà come il numero di record se si esegue una query separata per interrogare solo per un thread). A parte questo numero di riga, si interroga il messaggio stesso se è contenuto nei 10 thread più in alto (creato da quello (m.sender_id, m.receiver_id) in ...query.... Infine, poiché si desidera solo 10 messaggi più in alto, il numero di riga deve essere inferiore o uguale a 10.

2

vorrei suggerire di prendere la risposta di Couling e modificando leggermente in modo che esso non fornisce effettivamente due query utilizzando un'espressione di tabella comune:

WITH threads (sender_id, receiver_id, latest) as (
     select sender, 
       receiver, 
       max(sent) 
      from sof_messages 
     where receiver = <user> 
      or sender = <user> 
     group by sender, 
       receiver 
     order by 3 
     limit 10 
), 
messages ([messages fields listed here], rank) as (
     select m.*, 
       rank() over (partition by (sender, receiver), order by sent desc) 
      from sof_messages 
      WHERE (sender, receiver) in (select (sender, receiver) from threads)) 
SELECT * from messages where rank <= 10; 

Questo ha il vantaggio di consentire al progettista di avere una buona idea di quando usare gli indici qui. In sostanza ciascuno dei tre pezzi della query è pianificato indipendentemente.

Problemi correlati