tags:

views:

422

answers:

6

I have three tables:

Table User( userid username)

Table Key( userid keyid)

Table Laptop( userid laptopid)

i want all users who have either a key or a laptop, or both. How do i write the query so that it uses a join between table User and table Key, as well as a join between table User and table Laptop?

The main problem is that in the actual scenario, there are twelve or so table joins, sth like:

" select .. From a left join b on (...), c join d on (..),e,f,g where ...",

and i see that a could be joined to b, and a could also be joined to f. So assuming i can't make the tables a,b, and f appear side-by-side, how do i write the sql query?

+1  A: 
select distinct u.userid, u.username
from User u 
    left outer join Key  /* k on u.userid = k.userid */
    left outer join Laptop /* l on u.userid = l.userid */
where k.userid is not null or l.userid is not null

EDIT "The main problem is that in the actual scenario, there are twelve or so table joins, sth like: " select .. From a left join b on (...), c join d on (..),e,f,g where ...", and i see that a could be joined to b, and a could also be joined to f. So assuming i can't make the tables a,b, and f appear side-by-side, how do i write the sql query?"

You can have as many left outer joins as required. Join the table with the primary key to the rest of the tables or on any other field where field values of one table should match field values of other table.

eg will explain better than words

select * 
from a
 left outer join b on a.pk = b.fk -- a pk should match b fk
 left outer join c on a.pk = c.fk -- a pk should match c fk
 left outer join d on c.pk = d.fk -- c pk should match d fk

and so on

Rashmi Pandit
i completed the question to show my main difficulty is not being able to make the three tables appear side by side, with something like full table joins (i mean: "from c,d" etc) appearing mixed in between
Edited ans as per ur edited Q
Rashmi Pandit
You still miss a where-clause like "where k.userid is not null or l.userid is not null" in your first answer to filter out the users without anything.
Matt, you are right :)
Rashmi Pandit
+6  A: 

You can use multiple joins to combine multiple tables:

select *
from user u
left join key k on u.userid = k.userid
left join laptop l on l.userid = u.userid

A "left join" also finds users which do not have a key or a laptop. If you replace both with "inner join", it would find only users with a laptop and a key.

When a "left join" does not find a row, it will return NULL in its fields. So you can select all users that have either a laptop or a key like this:

select *
from user u
left join key k on u.userid = k.userid
left join laptop l on l.userid = u.userid
where k.userid is not null or l.userid is not null

NULL is special, in that you compare it like "field is not null" instead of "field <> null".

Added after your comment: say you have a table Mouse, that is related to Laptop, but not to User. You can join that like:

select *
from user u
left join laptop l on l.userid = u.userid
left join mouse m on m.laptopid = l.laptopid

If this does not answer your question, you gotta clarify it some more.

Andomar
I think the last u.userid should be k.userid
Matthew Flaschen
yeah! It's correct answer like my answer. +1
Soul_Master
sorry for not clarifying the question before: i have added more details to the question so that your answer seemingly does not solve my problem
+1  A: 

As you described the case, you only wanted to know if someone has a laptop or a key. I would write the query with a subquery rather than a join:

select * 
from user 
where userid in (select userid from key union select userid from laptop)

The reason for this is that by a join a person with multiple laptops or multiple keys will be listed several times (unless you use distinct). And even you use distinct you end up with a less efficient query (at least on Oracle the query optimizer doesn't appear to be able to create an efficient plan).

[Edited to correct what Rashmi Pandit pointed out.]

waxwing
It wont be a cross join if you specify left outer join. Left outer join is faster than subquery.
Rashmi Pandit
I tried it out quickly in an Oracle database: you are right, it does not cause a cross join. But it was also not faster than the subquery method - actually more than twice as slow.
waxwing
i want to do very little modification to the query except adding more JOIN and ON clauses, so for now i am looking for a way to do it without using subqueries. also i have mysql database whose behavior might be different from oracles behavior on nested queries
I am not sure abt Oracle but I have heard in general that joins are faster than sub queries ... maybe for large amount of data
Rashmi Pandit
Also differs from DB to DB
Rashmi Pandit
This was very helpful, as a SQL n00b this did exactly what I wanted and it is very readable to boot.
Nick
A: 
 SELECT * 
 FROM User
 LEFT JOIN Key ON User.id = Key.user_id
 LEFT JOIN Laptop ON User.id = Laptop.user_id
 WHERE Key.id IS NOT NULL OR Laptop.id IS NOT NULL
Soul_Master
A: 

Solution one:

SELECT * FROM [User] u
INNER JOIN [Key] k
ON u.userid = k.userid

UNION

SELECT * FROM [User] u
INNER JOIN Laptop l
ON u.userid = l.userid

[...]

Solution two:

SELECT * FROM [User] u
LEFT JOIN [Key] k
ON u.userid = k.userid
LEFT JOIN Laptop l
ON u.userid = l.userid
LEFT JOIN [...]
WHERE k.userid IS NOT NULL
OR l.userid IS NOT NULL
OR [...]

Just a guess, also you could check the execution plan for theses two to see if the UNION one is heavier or vice versa.

Maxime
+2  A: 
-- // Assuming that a user can have at max 1 items of each type
SELECT      u.*
-- // Assuming that a user can have more then 1 items of each type, just add DISTINCT:
-- // SELECT      DISTINCT u.*
FROM        "User" u
LEFT JOIN   "Key"    u1 ON u.UserID = u1.UserID
LEFT JOIN   "Laptop" u2 ON u.UserID = u2.UserID
LEFT JOIN   "Server" u3 ON u.UserID = u3.UserID
-- // ...
WHERE       COALESCE(u1.UserID, u2.UserID, u3.UserID /*,...*/) IS NOT NULL
van
+1 interesting use of coalesce :)
Andomar