2013-02-05 16 views
6

Mam tabeli wiadomości, które wygląda tak:Postgresql kwerendy skrzynki

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

Jeżeli „nitka” to grupa komunikatów pomiędzy danym SENDER_ID i receiver_id chcę zapytanie do powrotu najnowsza 10 komunikaty dla ostatnich 10 wątków gdzie identyfikator nadawcy lub odbiorcy jest podanym identyfikatorem.

oczekiwany wynik, przy czym podane id_użytkownika wynosi 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 | 
+-----------------------------------------------+ 

do wysokości 10 komunikatów pomiędzy, na przykład, użytkownik 5 i 2 (powyżej są 4) i ograniczenie do 10 nici (powyżej tam są 3).

Próbowałem z tego rodzaju kwerendy przy użyciu podzapytania, ale nie udało się uzyskać drugi limit liczby odrębnych wątków.

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; 

Zastanawiam tworząc nową tabelę zawierającą wątek thread_id pole który byłby połączeniem SENDER_ID + receiver_id a potem po prostu łącząc w wiadomościach, ale mam ukrytych podejrzenia, że ​​powinno to być wykonalne z tylko jednym stole .

+0

Czy możesz pokazać zapytanie, które wypróbowałeś i oczekiwane dane wyjściowe na podstawie przykładowych danych, które pokazałeś powyżej? – bonCodigo

+0

Po prostu, aby być czystym, pytasz o 1 wiadomość na wątek (zwracając co najwyżej 10 wierszy) lub 10 wiadomości na wątek (zwracając co najwyżej 100 wierszy). –

+0

@ Kwestionariusz Pytam o 10 wiadomości na wątek (wracając do co najwyżej 100 wierszy) – johnnymire

Odpowiedz

0

nie testowałem, ale wygląda na to, zapomniałem LIMIT 10 na swoim podzapytania który daje 10 najbardziej najnowsze wątki:

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; 

(mam dość wydrukowanego SQL więc łatwiej jest powiedzieć, co się dzieje.)

1

Zamieszczam to, aby pokazać, co można zrobić.

Naprawdę nie polecam korzystania z niego.

Byłoby znacznie lepiej zrobić dwa oddzielne zapytania: 1, aby pobrać 10 najnowszych wątków i 1 powtórzony, aby cofnąć 10 ostatnich wiadomości dla każdego wątku.

Jednak możesz osiągnąć swój cel za pomocą rank()window function, jak pokazano poniżej.

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 

Istnieje kilka różnych zapytań, które pozwolą osiągnąć cel dzięki funkcjom okien, z których żadna nie jest szczególnie czysta.

1

Tworzenie Thread tabela wygląda źle z powodu dublowania danych, ale widok może pomóc:

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

Zmień min(created_at) do max(created_at) jeśli data wątku jest być data jego ostatniej wiadomości, zamiast najstarszy.

to może być łączony z powrotem do wiadomości po prostu z:

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

tidiest zapytania mogę sobie wyobrazić, aby rozwiązać problem w ciągu jednego zapytania jest następujący:

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 

Kolumna row_number() over (partition by sender_id, receiver_id order by created_at desc) będzie zawierał numer wiersza każdej wiadomości w każdym wątku (będzie podobny do numeru rekordu, jeśli uruchomisz osobne zapytanie do zapytania tylko dla jednego wątku). Oprócz tego numeru wiersza, możesz przesłać zapytanie do samej wiadomości, jeśli jest ona zawarta w 10 najwyższych wątkach (wykonanych przez to (m.sender_id, m.receiver_id) in ...query.... I w końcu, jak chcesz tylko 10 najwyższych wiadomości, ograniczasz liczbę wierszy do 10 lub mniejszą.

2

Sugerowałbym biorąc odpowiedź couling i nieznacznie modyfikując je tak, że nie zapewnia skutecznego dwa zapytania przy użyciu wspólnego wyrażenia tabeli:

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; 

Ma to tę zaletę, że pozwala planista mieć całkiem dobry pomysł kiedy używać indeksów tutaj. W istocie każda z trzech części do zapytania jest niezależnie zaplanowana.

Powiązane problemy