Wpis z mikrobloga

#mysql #sql #webdev

Mirki mam taki problem do rozwiązania w SQL. W serwisie jest N użytkowników i każdy z nich może odnzaczyć innego użytkownika jako zaufanego. Drugi użytkownik może zrobić również to samo. I chciałbym żeby w kontekście np. użytkownika nr 1 mógł wybrać wszystkie osoby którym on zaufał ale które również odwzajemniły jego zaufanie.

I teraz zastanawiam się jak to zrobić...

myślałem żeby zrobić osobną tabelkę userfriends { userId, friendId , isTrusted } i po prostu w momencie jak użytkownik #1 dodaje do zaufanych użytkownika #2 to dopisywać do userfriends { 1 , 2 , false }. Potem gdy drugi użytkownik dodaje do zaufanego użytkownika nr 1 to dopisywać user_friends { 2 , 1 , false }; i przy akcji dodawania do zaufanych zrobić selecta i jeśli po operacj "dodaj do zaufanych" istnieje sytuacja w której dodajemy do zaufanych osobe która już nas dodała to w obu wierszach oznaczyć isTrusted na true. Potem przy wyszukiwaniu osób ze wzajemnym zaufaniem mogę tylko zrobić select * from userfriends where userId = ? and isTrusted = true....

ale boje się że w produkcyjnej wersji z replikacją bazy danych i skalowaniem poziomym serwisu na N instancji to się zesra bo request w którym #2 dodaje do zaufanych #1 może "wystrzelić" w momencie kiedy operacja dodawania #1 do #2 się nie zakończy i ostatecznie wyjdzie tak że w user
friends będą dwa wpisy bez oznaczenia isTrusted, tj. { 1,2,false } oraz { 2,1,false }.

myślałem też żeby rozbić to na poszczególne operacje tzn. bez pola isTrusted i tylko ddoawać do userfriends ( 1,2 ) w przpyadku gdy #1 zaufa #2 a potem dodać do userfriends (2,1) gdy #2 zaufa #1. Ale wtedy pojawia się problem wyodrębnienia "pokaż mi wszystkich użytkowników którym ja zaufałem a oni odzwajemnili zaufanie", bo nie wiem musiałbym jakoś tabelę user_friends zjoinować samą ze sobą?? bo pytając o to w kontekscie użytkownika #1 musiałbym zrobić coś w stylu (pseudo-sqlem)

SELECT * FROM userfriends WHERE userId = 1 LEFT JOIN userfriends ON friendId = userId LIMIT 1

problem w tym że nie wiem jaka jest wydajność takiego zapytania i czy wogóle jest możliwość zjoinowania tabeli samej ze sobą. ta funkcja będzie dość często używana w systemie więc musi być skalowalna odpowiednio.

myślałem też nad bardziej łopatologicznym rozwiązaniem, przy sktrukturze takiej jak powyżej user_friends { userId , friendId } po prostu zrobić (w kontekscie użytkownika #1):

friends = DB.query('SELECT * FROM user_friends WHERE userId = 1')

i potem ze wszystkich wpisów zwróconych przez bazę pobrać tylko takie które odzwajemniły relacje:
trustedFriends = DB.query( 'SELECT * FROM user_friends WHERE userId IN [...friends] AND friendId = 1' ).

Który sposób najlepszy? A może spróbować jakiegoś innego jeszcze?
  • 5
  • Odpowiedz
  • Otrzymuj powiadomienia
    o nowych komentarzach

@kowalzki: tl;dr

1. użytkownik może dodać innego do zaufanych
2. drugi użytkownik może dodać pierwszego do zaufanych
3. musze mieć możliwość wyciągać z bazy użytkowników którzy wzajemnie sobie
  • Odpowiedz
@larvaexotech: 3 sposób ok, ale zamiast IN użyj EXISTS:
SELECT
cośtam
FROM userFriends uf
WHERE EXISTS (SELECT * FROM userFriends WHERE uf.userId = friendId AND uf.friendId = userId)
  • Odpowiedz