Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: powiadomienia o aktualnościach
Forum PHP.pl > Forum > PHP
vadergb
Witam,

Mam problem - dokładniej z powiadomieniami o aktualnościach (np. jak ma to nasza-klasa - ktos doda komentarz do profilu i wszystkim znajomym sie powiadomienie pokazuje).

Do glowy przychodzi mi jeden sposob:

tworzymy tabele:
id_powiadomienia | id_uzytkownika | data | typ | tresc

i potem przy dodaniu np komentarza dodaje sie jeszcze xxx powiadomien kazdemu przyjacielowi...

Tylko ze ten sposob jest troche malo wydajny... przypuscmy ktos dodaje jeden glupi komentarz to oprocz niego idzie xxx insertow jeszcze?

Jak zrobic to optymalnie?

pozdrawiam
Suh
Cytat(vadergb @ 7.09.2009, 12:41:47 ) *
przypuscmy ktos dodaje jeden glupi komentarz to oprocz niego idzie xxx insertow jeszcze?


Jak to xxx insertów ? Użytkownik, który dodaje komentarza jest logowany do bazy (w takiej formie tabeli jak podałeś).
Potem w zależności od tego jaki masz pomysł.. czy informujemy każdego użytkownika czy informujemy powiedzmy grupę znajomych tego kto dodał komentarz, robimy odpowiedniego selecta.
vadergb
popatrz kazde powiadomienie trzeba dodac do bazy zalezne od uzytkownika. Moze zle mnie zrozumiales.
Przyklad:
1. Wladek - jego znajomi:
- jozek
- krzysiek


I teraz przpadek jakis Lukasz dodaje komentarz do uzytkownika Wladek:

Wtedy trzeba dodac:
powiadomienie dla jozka i powiadomienie dla Krzyska (czyli 2 inserty - jesli Wladek ma 100 znajomych to 100 insertow)

Kasyx
Ja bym zrobił tak:

Gdy Waldek umieszcza komentarz, dodajesz informacje o tym w odpowiedniej tabeli z 'akcjami'. 1 insert.

Później, jak znajomy Waldka - Krzysiek - zaloguje się, komponujesz dla niego zapytanie tak, by pobrał wszystkie akcje swoich znajomych. Tu masz dwa sposoby:
1) Pobierasz wszystkie akcje z jakiegoś przedziału czasowego (np 3 dni) i wyświetlasz
2) Trudniejsze: pobierasz te dane, zapisujesz do innej tabeli w bazie ID krzyśka oraz ID akcji (z tabeli akcje). Wtedy wyświetlasz to co zostało teraz dodane + starsze wpisy (z poprzedniego zalogowania). W ten sposób Krzysiek ma większą kontrolę. Może np usuwać niektóre wpisy, jak to jest na NK. Insertów będziesz miał sporo, ale kopiujesz tylko numery ID, wiec będą one szybkie
vadergb
Cytat(Kasyx @ 7.09.2009, 13:33:11 ) *
Ja bym zrobił tak:

Gdy Waldek umieszcza komentarz, dodajesz informacje o tym w odpowiedniej tabeli z 'akcjami'. 1 insert.

Później, jak znajomy Waldka - Krzysiek - zaloguje się, komponujesz dla niego zapytanie tak, by pobrał wszystkie akcje swoich znajomych. Tu masz dwa sposoby:
1) Pobierasz wszystkie akcje z jakiegoś przedziału czasowego (np 3 dni) i wyświetlasz
2) Trudniejsze: pobierasz te dane, zapisujesz do innej tabeli w bazie ID krzyśka oraz ID akcji (z tabeli akcje). Wtedy wyświetlasz to co zostało teraz dodane + starsze wpisy (z poprzedniego zalogowania). W ten sposób Krzysiek ma większą kontrolę. Może np usuwać niektóre wpisy, jak to jest na NK. Insertów będziesz miał sporo, ale kopiujesz tylko numery ID, wiec będą one szybkie



Hmm instersuje mnie tylko drugi sposob (jesli by 1 mnie intersowal nie pytalbym).

czyli przy dodawaniu komenta idzie insert do specjalnej tabeli z akcjami (co zrobili,gdzie i jak).

Potem krzysiek sie loguje (wchodzi na strone glowna) wtedy sie te dane aktualizuja.

Tylko jak je aktualizwoac - robisz skrypt ktory przeleci wszystkie akcje twoich przyjaciol (od daty ostatniego wejscia na strone powiadomieniami/logowaniem - ostatniej aktualizacji twoich powiadomien (ale np nie wiecej niz ostatnich 50) )?

W sumie to jednym zapytaniem daloby sie ta liste pobrac:
czyli 50 pobran + 50 insertow sie klania(co prawda insert to jest typu values(id_akcji, twoje_id) ) ale jednak troche ich jest ;]

Kasyx
Inaczej.

Dla przykładu Władek (ID 1) wstawił komentarz komuś. Podobnie jak Jan (ID 2).

Ten fakt zapisywany jest do tabeli 'akcje' w której masz: id akcji, datę, opis akcji, id osoby wykonującej akcję. W sumie 1 akcja - 1 insert.


Teraz loguje się Krzysiek, który ma znajomych ID 1 i ID 2.
Krzysiek wykonuje zapytanie pobierające te akcje, które są (1 zapytanie). Teraz faktycznie, trzeba wykonać 50 insertów, które zapiszą Krzyśkowi bieżące wydarzenia. A pobrać wszystko możesz 1 Selectem

CHYBA, że ułatwisz sobie zadanie i zamiast robić 50 insertów, zrobisz pole TEXT, w którym po odpowiednim sformatowaniu (dla przykładu po przecinku) wstawisz ID wszystkich wydarzeń, które danego delikwenta będą interesować.
WTEDY:
1 select na pobranie nowych akcji
1 insert na dopisanie listy ID
2 selecty na pobranie całej listy (pierwszy żeby wydobyć interesujące nas ID, drugi by pobrać akcje).
To jest chyba faktycznie lepsze, lecz ma swoje minusy. Usunięcie rekordu nie będzie już takie trywialne, bo trzeba przelecieć stringa i wyszukać odpowiedni numerek.
vadergb
Cytat(Kasyx @ 7.09.2009, 15:23:23 ) *
Inaczej.

Dla przykładu Władek (ID 1) wstawił komentarz komuś. Podobnie jak Jan (ID 2).

Ten fakt zapisywany jest do tabeli 'akcje' w której masz: id akcji, datę, opis akcji, id osoby wykonującej akcję. W sumie 1 akcja - 1 insert.


Teraz loguje się Krzysiek, który ma znajomych ID 1 i ID 2.
Krzysiek wykonuje zapytanie pobierające te akcje, które są (1 zapytanie). Teraz faktycznie, trzeba wykonać 50 insertów, które zapiszą Krzyśkowi bieżące wydarzenia. A pobrać wszystko możesz 1 Selectem

CHYBA, że ułatwisz sobie zadanie i zamiast robić 50 insertów, zrobisz pole TEXT, w którym po odpowiednim sformatowaniu (dla przykładu po przecinku) wstawisz ID wszystkich wydarzeń, które danego delikwenta będą interesować.
WTEDY:
1 select na pobranie nowych akcji
1 insert na dopisanie listy ID
2 selecty na pobranie całej listy (pierwszy żeby wydobyć interesujące nas ID, drugi by pobrać akcje).
To jest chyba faktycznie lepsze, lecz ma swoje minusy. Usunięcie rekordu nie będzie już takie trywialne, bo trzeba przelecieć stringa i wyszukać odpowiedni numerek.



To raczej nie do konca dobry sposob: bo jak ktos ma juz np 30 powiadomien i 10 jest nowych:

wchodzi i selectem pobiera te 10 zapytan - ale jak teraz dodaje je? pobiera stringa (usuwa ostatnie 10 id i dodaje nowe)? to raczej kiepsko by sie sprawdzilo
krowal
Robiłem coś takiego tylko że jeszcze bardziej skomplikowane niż na nk. Coś ala facebook z dodatkową możliwością komentowania powiadomień oraz oznaczenia czy się je lubi czy nie. Dodatkowo każdy user mógł usunąć sobie każde powiadomienie które widział i nie miało to wpływu na wyświetlanie go dla pozostałych userów którzy to powiadomienie również otrzymali, oraz... powiadomienia nie były dublowane w bazie !
Wszystko można zapisać jednorazowo i nie ma potrzeby powielać powiadomień. Później wystarczy tylko DOBRE zapytanie i będzie działać jak należy smile.gif

Wskazówki:
- wybierasz wszystkie powiadomienia o akcjach które popełnili twoi przyjaciele (np WHERE user_id IN (friends_list)) więc w bazie do każdego powiadomienia musisz dopisać id usera który spowodował daną akcję
- możesz też dodawać powiadomienia globalne (dla wszystkich) wtedy user_id może być równe np null
- zamiast usuwać rekordy z tabeli powiadomień możesz dodać dodatkową tabelę gdzie będziesz zapisywał które powiadomienie zostało usunięte dla jakiego użytkownika i potem wybierać powiadomienia z warunkiem 'WHERE id_powiadomienia NOT IN (lista usuniętych powiadomień dla swojego usera)'

Dzięki temu całe wyświetlanie możesz zrobić jednym zapytaniem (+podzapytania) co jest fajne bo możesz je wszystkie posortować wg daty pojawienia się.




vadergb
Cytat(krowal @ 8.09.2009, 12:19:17 ) *
Robiłem coś takiego tylko że jeszcze bardziej skomplikowane niż na nk. Coś ala facebook z dodatkową możliwością komentowania powiadomień oraz oznaczenia czy się je lubi czy nie. Dodatkowo każdy user mógł usunąć sobie każde powiadomienie które widział i nie miało to wpływu na wyświetlanie go dla pozostałych userów którzy to powiadomienie również otrzymali, oraz... powiadomienia nie były dublowane w bazie !
Wszystko można zapisać jednorazowo i nie ma potrzeby powielać powiadomień. Później wystarczy tylko DOBRE zapytanie i będzie działać jak należy smile.gif

Wskazówki:
- wybierasz wszystkie powiadomienia o akcjach które popełnili twoi przyjaciele (np WHERE user_id IN (friends_list)) więc w bazie do każdego powiadomienia musisz dopisać id usera który spowodował daną akcję
- możesz też dodawać powiadomienia globalne (dla wszystkich) wtedy user_id może być równe np null
- zamiast usuwać rekordy z tabeli powiadomień możesz dodać dodatkową tabelę gdzie będziesz zapisywał które powiadomienie zostało usunięte dla jakiego użytkownika i potem wybierać powiadomienia z warunkiem 'WHERE id_powiadomienia NOT IN (lista usuniętych powiadomień dla swojego usera)'

Dzięki temu całe wyświetlanie możesz zrobić jednym zapytaniem (+podzapytania) co jest fajne bo możesz je wszystkie posortować wg daty pojawienia się.



Wszystko spoko a co jak ma 2500 znajomych ?
Robimy: where user_id IN (2500 id) questionmark.gif Nie zebym byl pesymista ale... to moze zabic serwer?
1 zapytanie?
pobieramy liste friendsow ( przpuscmy 250 id)
potem pobieramy liste usunietych id
potem robimy zapytanie where id_user in(lista friendsow) and id_powiadomienia not in (usuniete powiadomienia).

Ja tu widze mase.

Moze ze zrobic to tak:
select * from powiadomienia as p,friend_list as fl,del_powiadomienia as dp where p.id_user=fl.id_friend and p.id!=dp.id_powiadomienia limit 30
Nie wiem czy to zapytanie w pelni dziala ale w sumie jesli tak to jednym zapytaniem mozna pobrac powiadomienia wszystkie ( nie wiem czy to zapytanie jest wydajne ale ... chyba wydajniejsze niz 50 insertow.

Nie wiem jak mysliscie to najlepsza droga czy moze jest lepsza?
krowal
Ty widzisz masę, a ja widzę trzy, a właściwie jedno zapytanie z zapytaniami zagnieżdżonymi, które i tak wykonają się szybciej niż gdyby wykonywać je pojedynczo i dodatkowo przetwarzać jeszcze przez php. Poza tym 'IN (2500 userów)' nie zajedzie bazy, uwierz mi smile.gif
vadergb
  1. SELECT * FROM powiadomienia AS p,friend_list AS fl,del_powiadomienia AS dp WHERE p.id_user=fl.id_friend AND p.id!=dp.id_powiadomienia LIMIT 30


a to zapytanie nie bylo by szybsze niz wykonywanie tych 3 zapytan i IN?
Kasyx
Cytat(vadergb @ 8.09.2009, 11:41:25 ) *
To raczej nie do konca dobry sposob: bo jak ktos ma juz np 30 powiadomien i 10 jest nowych:

wchodzi i selectem pobiera te 10 zapytan - ale jak teraz dodaje je? pobiera stringa (usuwa ostatnie 10 id i dodaje nowe)? to raczej kiepsko by sie sprawdzilo


To jest dobry sposób. Zależy Ci na odciążeniu bazy danych? Odciążasz ją więc, nie mnożysz danych i masz łatwą kontrolę nad wszystkim co potrzebujesz.
Tylko ceną jest zabawa ze stringami. Ale PHP znacząco tą zabawę ułatwia. Kilka funkcji operujących na odpowiednio spreparowanym stringu załatwi sprawę.
krowal
Cytat(vadergb @ 8.09.2009, 13:31:59 ) *
  1. SELECT * FROM powiadomienia AS p,friend_list AS fl,del_powiadomienia AS dp WHERE p.id_user=fl.id_friend AND p.id!=dp.id_powiadomienia LIMIT 30


a to zapytanie nie bylo by szybsze niz wykonywanie tych 3 zapytan i IN?


Może i jest szybsze tylko, że nie zwraca poprawnych wyników, zrób sobie test i sprawdź smile.gif
vadergb
Cytat(Kasyx @ 8.09.2009, 13:45:16 ) *
To jest dobry sposób. Zależy Ci na odciążeniu bazy danych? Odciążasz ją więc, nie mnożysz danych i masz łatwą kontrolę nad wszystkim co potrzebujesz.
Tylko ceną jest zabawa ze stringami. Ale PHP znacząco tą zabawę ułatwia. Kilka funkcji operujących na odpowiednio spreparowanym stringu załatwi sprawę.



hmm pobierasz selectem pole text (dajmy na to ze 30 id)
potem to 30 id wrzucasz w select z in (30id) i czytasz - to jest dosc szybkie:
Usuwane rekordy sa zadko - wiec tu nie jest problem.
Ale przypuscmy ze masz w polu text 30 id - i teraz doochodzi CI doda 2 akcje to usuwasz 2 ostatnie akcje + dodajesz te 2 akcje ( to tez raczej jakis problem nie jest ).

Ale ma to kilka minusow:
1. Gdy dodasz kogos do znajomych nowego nie masz jego akcji (dopiero od czasu dodania pokazuje Ci sie co zrobil)
2. Ustalasz limit tych akcji np na 30. Do starszych nikt nie ma dostepu.


Sposob Krowal'a o tyle jest dobry ze mozesz pobrac tych akcji xxx - aczkolwiek zapewne jest troche wolniejszy (jak nie o wiele wolniejszy).

Cytat(krowal @ 8.09.2009, 13:54:49 ) *
Może i jest szybsze tylko, że nie zwraca poprawnych wyników, zrób sobie test i sprawdź smile.gif


Hmm moze do konca ono nie dziala - ale robiac podobne zapytanie (moze z left join) daloby rade to pobrac bez in chyba. Pobieramy 2 tabele powiadomienia i liste przyjaciol potem sprawdzamy czy uzytkownik ktory dodal powiadomienie nalezy do przyjaciol uzytkownika.
dr_bonzo
A czemu nie uzyc pierwotnego rozwiazania - czyli ..2500 insertow? Tylko je zoptymalizowac

  1. SELECT user.friend_id AS dla_kogo, $przyjaciel->id AS kto, "tresc powiadomienia blah blah" AS tresc, NOW() AS created_at
  2. FROM user
  3. WHERE user.id = $przyjaciel->id


zwroci ci liste powiadomien, po jednym, tym samym dla kazdego z friendsow danego $przyjaciela.

Teraz tylko zastosowac INSERT INTO ... SELECT ... i juz

  1. INSERT INTO powiadomienia (dla_kogo, kto, tresc, created_at)
  2. SELECT user.friend_id AS dla_kogo, $przyjaciel->id AS kto, "tresc powiadomienia blah blah" AS tresc, NOW() AS created_at
  3. FROM user
  4. WHERE user.id = $przyjaciel->id



--------
Pobieranie jest trywialne i szybkie (1 join, lub zero, jak zapiszesz w powiadomieniu nicka/imie userka).

No i wstawiasz takie dane zazwyczaj RAZ a odczytywane sa 100tki razy - warto wiec posiadac szybszy odczyt niz zapis.
krowal
@dr_bonzo ale w ten sposób powielasz powiadomienia tej samej treści. Jest ok, gdy są one usuwane automatycznie po obejrzeniu, ale jeśli nie to tabela z powiadomieniami wypełnia się w astronomicznym tempie smile.gif
dr_bonzo
@krowal: no i co z tego - ale mam, jak mysle, wydajna baze.

Zastosowanie ma tutaj tradycyjne rownanie:

Kod
pamiec * CPU = const
krowal
No jest to jakieś rozwiązanie, ale mi się bardziej podoba to moje winksmiley.jpg bo imo. łatwiej mi później zarządzać rekordami powiązanymi z pojedynczymi powiadomieniami.
dr_bonzo
@krowal: no jesli miales takie rozbudowane ficzerki do powiadomien, to rzeczywiscie, lepiej je trzymac pojedynczo i przypiac do nich komentarze, status widocznosci itp.
Ale autor topiku nie mial takiej specyficznej sytuacji smile.gif
vadergb
Cytat(dr_bonzo @ 8.09.2009, 14:02:40 ) *
A czemu nie uzyc pierwotnego rozwiazania - czyli ..2500 insertow? Tylko je zoptymalizowac

  1. SELECT user.friend_id AS dla_kogo, $przyjaciel->id AS kto, "tresc powiadomienia blah blah" AS tresc, NOW() AS created_at
  2. FROM user
  3. WHERE user.id = $przyjaciel->id


zwroci ci liste powiadomien, po jednym, tym samym dla kazdego z friendsow danego $przyjaciela.

Teraz tylko zastosowac INSERT INTO ... SELECT ... i juz

  1. INSERT INTO powiadomienia (dla_kogo, kto, tresc, created_at)
  2. SELECT user.friend_id AS dla_kogo, $przyjaciel->id AS kto, "tresc powiadomienia blah blah" AS tresc, NOW() AS created_at
  3. FROM user
  4. WHERE user.id = $przyjaciel->id



--------
Pobieranie jest trywialne i szybkie (1 join, lub zero, jak zapiszesz w powiadomieniu nicka/imie userka).

No i wstawiasz takie dane zazwyczaj RAZ a odczytywane sa 100tki razy - warto wiec posiadac szybszy odczyt niz zapis.


Tu masz jeszcze jeden problem:
np masz 1000 znajomych z czego 100 znajomych nie wchodzi nawet na portal - dodajesz bezsensu 100 rekordow do bazy danych.

Wydaje sie rozwiazanie krowala chyba za najlepsze tylko ten in(lista friendsow) mi nie pasuje tongue.gif moze by sie dalo to zalatwic jednym zapytaniem;]
dr_bonzo
Cytat
np masz 1000 znajomych z czego 100 znajomych nie wchodzi nawet na portal - dodajesz bezsensu 100 rekordow do bazy danych.



  1. INSERT INTO powiadomienia (dla_kogo, kto, tresc, created_at)
  2. SELECT user.friend_id AS dla_kogo, $przyjaciel->id AS kto, "tresc powiadomienia blah blah" AS tresc, NOW() AS created_at
  3. FROM user
  4. WHERE user.id = $przyjaciel->id
  5. AND last_access_time > tydzien_temu
vadergb
Cytat(dr_bonzo @ 9.09.2009, 13:18:21 ) *
  1. INSERT INTO powiadomienia (dla_kogo, kto, tresc, created_at)
  2. SELECT user.friend_id AS dla_kogo, $przyjaciel->id AS kto, "tresc powiadomienia blah blah" AS tresc, NOW() AS created_at
  3. FROM user
  4. WHERE user.id = $przyjaciel->id
  5. AND last_access_time > tydzien_temu


No dobra ale np 100 znajomych nie wchodzilo na portal przez 100 dni ale nagle po 100 weszli - to co powiadomien nie beda mieli ?tongue.gif

krowal
Zawsze możesz zrobić IN(SELECT frind_id FROM friends WHERE user_id = moje_id). Wyjdzie na to samo winksmiley.jpg
dr_bonzo
Cytat
No dobra ale np 100 znajomych nie wchodzilo na portal przez 100 dni ale nagle po 100 weszli - to co powiadomien nie beda mieli ?

Nie beda mieli. Ale... czy beda chcieli czytac powiadomienia sprzed pol roku ze ktos dodal im komentarz do fotki?
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.