Problem with logic when using LEFT JOIN

Asked

Viewed 251 times

7

In my system, there is a system of posts and another of friendships. It follows the structure of tables:

posts: id | usuario | conteudo | data | hora

friendships: id | usuario1 | usuario2 | status

And I’m using the following query to return posts from both the current user and my friends (when status = 2):

SELECT 
    postagem.id, usuario, conteudo, data, hora, usuario1, usuario2, status
FROM 
    postagens postagem
LEFT JOIN
    amizades ON usuario = usuario1 OR usuario = usuario2
WHERE
    status = 2 OR usuario = Igor
ORDER BY
    postagem.id DESC
LIMIT 10

It’s working perfectly, returning both my posts (logged-in user) and those of my friends (user-to-user relationship where status = 2), and here is an example of data contained in the table amizades:

2 | Igor  | Joao | 2
1 | Lucas | Igor | 0

In the above data, when status = 2 means they’re friends, and when status = 0 means they’re not friends.

However, as there is my user (Igor) in the two rows of the table, it duplicates the posts of such user, that is, if there was another row in the table, of which would make the relationship of friendship between my user and any other, would triple the posts. How to solve this?

  • You are passing the user by id?

  • @Andrébaill in fact, where he is usuario = Igor is usuario = '$usuario', I just altered it so you can better understand my problem.

  • Yes, but is the user id or user name? you would have to pass the id_user if it is not

  • That’s the same name...

  • You have to pass the ID in order to use the left Join correctly.

  • SELECT * FROM posts AS C1 LEFT JOIN friendships AS C2 USING(id_usuario)

  • But why can’t it be by name? That username would be like an id, it’s unique

  • Because every time you relate or change the user name, it will not be able to authenticate, and also for security and still for issues just like this one that you posted, of relationship tables :)

  • There is no way to change the username, after registering, cannot be changed

  • beauty then...

  • Anyway, how to do? I tried to replace the ON for USING just like you said, but it didn’t work... You could give an example of the query and how it would look?

Show 6 more comments

1 answer

4


Here is a query that should answer your problem.

SELECT  DISTINCT postagem.id, 
        usuario, 
        conteudo, 
        data,
        hora
FROM postagens postagem
LEFT JOIN 
(
    SELECT usuario1, usuario2
     FROM amizades
    WHERE status = 2
      AND ( usuario1 = 'Igor' or usuario2 = 'Igor')
) amizades
  ON amizades.usuario1 = postagem.usuario
  OR amizades.usuario2 = postagem.usuario
WHERE amizades.usuario1 IS NOT NULL
   OR postagem.usuario = 'Igor'
ORDER BY postagem.id DESC
LIMIT 10

If the last one doesn’t work, do it this way:

SELECT  postagem.id, 
        usuario, 
        conteudo, 
        dt
FROM postagens postagem
LEFT JOIN 
(
    SELECT usuario1    id_amigo
     FROM amizades
    WHERE status = 2
      AND usuario2 = 'Igor'
    UNION
    SELECT usuario2
     FROM amizades 
    WHERE status = 2
      AND usuario1 = 'Igor'
) amizades
  ON amizades.id_amigo = postagem.usuario
WHERE amizades.id_amigo IS NOT NULL 
   OR postagem.usuario = 'Igor'
ORDER BY postagem.id DESC
LIMIT 10

Stay here the Fiddle

Another way, conceptually simpler to solve the problem even more extensive, would be to divide the results in two: Your posts and your friends' posts. This could be done

SELECT postagem.id, 
       usuario, 
       conteudo, 
       data, 
       hora, 
       'Minha postagem' AS Descricao
FROM postagens postagem
WHERE usuario = 'Igor'

UNION ALL

SELECT postagem.id,
       usuario, 
       conteudo, 
       data, 
       hora, 
       'Postagem de um amigo'
FROM postagens postagem
INNER JOIN 
(
    SELECT usuario1 id_amigo
    FROM   amizades
    WHERE status = 2
      AND usuario2 = 'Igor'

    UNION

    SELECT usuario2
    FROM   amizades
    WHERE status = 2
      AND usuario1 = 'Igor'

) amizades
   ON id_amigo = postagens.usuario
ORDER BY postagem.id DESC
LIMIT 10

In relation to your questions:

  • Quotes descriptions are just a visual help to understand the origin of the posts. It can be removed from the query.
  • The idea is to combine your posts with your friends' posts through the use of UNION ALL - Combine the result of two queries
  • The way you wrote your query, there is no difference between LEFT JOIN and INNER JOIN.

SELECT 
    postagem.id, usuario, conteudo, data, hora, usuario1, usuario2, status
FROM 
    postagens postagem
LEFT JOIN
    amizades ON usuario = usuario1 OR usuario = usuario2
WHERE
status = 2   <- Esta condição obriga a que exista um registo na tabela amizades sendo desta forma equivalente ao INNER JOIN
  • Wow, I thought I could use the query I was trying, because it seemed simpler... But when I saw yours, I had some doubts: What is the function of the quotation marks (My post / Post of a friend)? All this is a query divided into two SELECT's in it? Why, instead of LEFT JOIN use the INNER JOIN?

  • This second solution really looks better, and it works. However, if the user has no friendship their own posts are not shown

  • Still not showing his own posts if there are no friends :(

  • 1

    Oh yes, perfect! I hadn’t noticed the change from INNER to LEFT, thank you very much!

  • I just checked here, but now it keeps doubling. If I have, for example, 1 friendship, it does not duplicate as before, but if I have 2 friendships it starts to duplicate... Remembering that the user can be both user1 and user2, since the user1 sends the invitation...

  • 1

    You can use DISTINCT?

  • I’m sorry, but what is this?

  • 1

    I changed it again. I added a DISTINCT in the first alternative that should remove duplicates. If this does not work, I have put another alternative.

  • 1

    Now yes, perfect!

Show 4 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.