Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Unikalne pary
Forum PHP.pl > Forum > Bazy danych > MySQL
markonix
Bardzo prosty problem.

Mamy tabelę z wiadomościami - id odbiorcy i id wysyłającego.
Chcę stworzyć listę konwersacji czyli po jednej wiadomości z każdej konwersacji.

1 | 2
1 | 2
1 | 3
3 | 1
2 | 1

Wynik oczekiwany dla useraID: 1 - po jednej, ostatniej wiadomości z każdej rozmowy.

2|1
1|3

Niby proste ale wszystkie moje wariacje SELECTa zwracają ładnie po jednej wiadomości ale dla rozmów gdzie była odpowiedź zwraca 2 wiersze 1|2 i 2|1 traktując to jako osobne rozmowy.

Jedna z prób:
  1. SELECT t.* FROM
  2. (SELECT * FROM `ci_messages`
  3. WHERE `sender_id` = 1 OR `receiver_id` = 1
  4. ORDER BY `ci_messages`.`date` DESC) t
  5. GROUP BY t.`sender_id`, t.`receiver_id`
  6. ORDER BY `date` DESC


Jak wytłumaczyć bazie że 1|2 to dla mnie to samo co 2|1 i nie chcę aby się powtarzało?

Inna próba z takim samym rezultatem:

  1. SELECT *
  2. FROM (
  3. SELECT * FROM `ci_messages` WHERE `sender_id` = 1 ORDER BY `ci_messages`.`date` DESC
  4. ) AS sub
  5. GROUP BY `receiver_id`
  6. UNION
  7. SELECT *
  8. FROM (
  9. SELECT * FROM `ci_messages` WHERE `receiver_id` = 1 ORDER BY `ci_messages`.`date` DESC
  10. ) AS sub
  11. GROUP BY `sender_id`

werdan
http://sqlfiddle.com/#!2/e9642/2

  1. SELECT DISTINCT sender_id, receiver_id FROM msg



Coś takiego pasi?
markonix
To była moja pierwsza próba i faktycznie pokazuje w porządku pary, ale tak jak napisałem na początku chce pobrać pary wiadomości czyli całe wiersze.
Tutaj zobrazowany problem z prostym DISTINCT:

http://sqlfiddle.com/#!2/84c5ea/1/0
redeemer
Może tak:
  1. SELECT
  2. IF(
  3. sender_id<receiver_id,
  4. concat(sender_id,'_',receiver_id),
  5. concat(receiver_id,'_',sender_id)
  6. ) AS custom_id,
  7.  
  8. text,
  9. sender_id,
  10. receiver_id
  11. FROM msg
  12. GROUP BY custom_id

Edit: Hmmm, doszedłem do wniosku, że chodziło Ci chyba o coś innego (modyfikacja Twojego zapytania):
  1. SELECT t.* FROM
  2. (SELECT * FROM ci_messages WHERE sender_id = 1 OR receiver_id = 1 ORDER BY ci_messages.id DESC) t
  3. WHERE t.sender_id=1
  4. GROUP BY t.sender_id, t.receiver_id
  5. ORDER BY t.id DESC
http://sqlfiddle.com/#!2/39580a/7
markonix
No właśnie zanim zeedytowałeś zacząłem przerabiać Twoją pierwszą propozycję, która była w porządku ale używała tego dziadowskiego GROUP BY, którego nie da się posortować przed grupowaniem.

Co do drugiej propozycji już na wstępie widzę problem, że nie widzi rozmowy o id 7 - "nowszej" od tej o ID 6.

Tak więc druga Twoja propozycja ma chyba ten sam problem co moje pierwsze próby.

Druga pomysłowa, nie wpadłbym na użycie CONCAT smile.gif
Problem tylko z sortowaniem tak więc nie ma innej opcji jak sub zapytanie.

  1. SELECT t.*
  2. FROM
  3. ( SELECT IF( sender_id<receiver_id, concat(sender_id,'_',receiver_id), concat(receiver_id,'_',sender_id) ) AS custom_id,
  4. ci_messages.*
  5. FROM ci_messages
  6. WHERE `sender_id` = 1
  7. OR `receiver_id` = 1
  8. ORDER BY `date` DESC ) t
  9. GROUP BY custom_id
  10. ORDER BY `date` DESC
redeemer
Ok, w końcu zrozumiałem o co tak naprawdę chodzi smile.gif

Zapytanie bez IF i CONCAT będzie bardziej "eleganckie":
  1. SELECT t.*
  2. FROM
  3. ( SELECT * FROM ci_messages WHERE `sender_id` = 1 OR `receiver_id` = 1 ORDER BY `id` DESC ) t
  4. GROUP BY LEAST(sender_id, receiver_id), GREATEST(sender_id, receiver_id)
  5. ORDER BY `id` DESC
Warto pamiętać, że grupowanie po "sztucznych" kolumnach nie korzysta z indeksów i jeśli to zapytanie ma być często wykonywane, może warto rozważyć dodanie kolejnej kolumny do tabeli (+odpowiednie indeksowanie).

Jestem też prawie pewien, że bez ruszania tabeli da się to zrobić lepiej, ale nie mam ostatnio za dużo wolnego czasu, żeby się bardziej przyjrzeć smile.gif
markonix
Rozumiem, że sztuczne kolumny odnoszą się do rozwiązania z concat czy chodzi Ci o Twoją aktualną propozycję z podzapytaniem?
redeemer
Te dwie propozycje wydajnościowo powinny być tożsame (ewentualnie minimalnie większy narzut pamięci będzie miało zapytanie z użyciem concat). Druga propozycja jednak, ma moim zdaniem "ładniejszy" kod.
markonix
No tak, ale wspomniałeś o tymczasowych kolumnach i ich wydajności.
Faktycznie zapytanie będzie często wykonywane (i jego wariacje) dlatego będę je odchudzał jak tylko się da.

W rozwiązaniu z CONTACT takową kolumną jest custom_id.
W Twoim najnowszym rozwiązaniu nie ma dynamicznie tworzonej kolumny więc wg tego co napisałeś wcześniej powinno być lepsze (pomijam fakt, że lepiej wygląda wizualnie wink.gif
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.