質問

I am trying to learn about MYSQL Joins but am having trouble figuring this one out. I want to get all rows in table1 where the userID is in a relationship with the given user.

table2 contains user relationships so the userID can either be in iduser1 field or iduser2 field I have the following query that gives me the correct results:

set @userID = 91;
SELECT * FROM table1
    WHERE (iduser IN (SELECT iduser1 FROM table2 WHERE iduser2 = @userID)
        OR (iduser IN (SELECT iduser2 FROM table2 WHERE iduser1 = @userID)))

table1: 
    iduser FK

table2:
    iduser1 FK
    iduser2 FK

I have been told that nested queries in MySQL have a bad reputation when it comes to performance and I am sure I could probably do this same query using JOINS somehow but I just cant figure it out especially as there is an OR statement because the table1.iduser can be in either table2.iduser1 OR table2.iduser2

How can I join the same table twice?

役に立ちましたか?

解決

Given this expression:

set @userID = 91;
SELECT * FROM table1
    WHERE (iduser IN (SELECT iduser1 FROM table2 WHERE iduser2 = @userID)
        OR (iduser IN (SELECT iduser2 FROM table2 WHERE iduser1 = @userID)))

Using EXISTS, you can reduce that two subqueries into following:

set @userID = 91;
SELECT * FROM table1 x
WHERE EXISTS(
    select * from table2 z
    where (z.user2 = @userID and iduser1 = x.iduser)
       OR (z.user1 = @userID and iduser2 = x.iduser)
)

The OR might slowdown your query, it doesn't necessarily short-circuit in RDBMS. In that case, you should try to short-circuit it by using CASE WHEN. I had a query that took 5 seconds to execute when using OR, but took sub-zero second when I translated it to CASE WHEN:

set @userID = 91;
SELECT * FROM table1 x
WHERE EXISTS(
    select * from table2 z
    where 
       CASE WHEN z.user2 = @userID THEN
           IF(z.iduser1 = x.iduser, 1, 0)
       CASE WHEN z.user1 = @userID THEN 
           IF(z.iduser2 = x.iduser, 1, 0)
       END = 1
)

Boolean expression automatically cast to integer(either 0(false) or 1(true)), so you can also do it like this:

set @userID = 91;
SELECT * FROM table1 x
WHERE EXISTS(
    select * from table2 z
    where 
       CASE WHEN z.user2 = @userID THEN
           z.iduser1 = x.iduser
       CASE WHEN z.user1 = @userID THEN 
           z.iduser2 = x.iduser
       END = 1
)

Or this:

set @userID = 91;
SELECT * FROM table1 x
WHERE EXISTS(
    select * from table2 z
    where 
       CASE WHEN z.user2 = @userID THEN
           z.iduser1 = x.iduser
       CASE WHEN z.user1 = @userID THEN 
           z.iduser2 = x.iduser
       END 
)

If you are not using MySQL, you shall do it like this:

set @userID = 91;
SELECT * FROM table1 x
WHERE EXISTS(
    select * from table2 z
    where 
       CASE WHEN z.user2 = @userID THEN
           CASE WHEN z.iduser1 = x.iduser THEN 1 END
       CASE WHEN z.user1 = @userID THEN 
           CASE WHEN z.iduser2 = x.iduser THEN 1 END
       END = 1
)

他のヒント

Let me object that MySQL handles poorly not only subqueries but EXISTS, and OR, too. Therefore I would suggest a different approach to the problem.

Since the parameter in the queries is known thanks to a user variable, we can prepare joinable queries.

set @userID = 91;

SELECT DISTINCT T1.* FROM table1 T1
LEFT JOIN (SELECT iduser1 FROM table2 WHERE iduser2 = @userID) T21 
ON (T1.iduser = T21.iduser1)
LEFT JOIN (SELECT iduser2 FROM table2 WHERE iduser1 = @userID) T22 
ON (T1.iduser = T22.iduser2)
;

As you can see, EXISTS and OR are perfectly implemented by LEFT JOIN that searches for each match, if any, with other users in the second table. Obviously the same user in table1 could find several matches, hence a DISTINCT might do the trick and remove duplicates.

I suggest you to use JOIN wherever you can in order to exploit MySQL.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top