Join 3 tables only if they have information on 1 table - mysql

I have 3 table
| Likes | | Friends | | Item |
----------------------------- ------------------ -------------------
| UserID | LikeID | itemID | | user1 | user2| | itemID |
I would like to join the 3 tables based on if the user has liked an item in the Likes table and the user must also be friends with the user in the Friends table and both of them have liked the item.
The way the Friends table is set up is if a user adds a friend the userID will be placed in either user1 or user2 depending who requested the friend first, so the userID of say 1 can be in either user1 column or user2 column and users friend will be in the opposing column.
Then I would do an inner join on itemID table to get the item both of the users liked.
I am no SQL expert so I am finding this very difficult and any code examples I give you would be rubbish. I have tried all types of left joins inner joins right joins I've joined the crap out of these tables but I do not get the correct data.
Any steer in the right direction on how to accomplish this would be great I am out of ideas. Please let me know of any further information I can give you.

I believe this does what you are after:
select a.user1, b.likeid, a.user2, c.likeid, d.itemid
from friends a
inner join likes b on a.user1 = b.userid
inner join likes c on a.user2 = c.userid
inner join item d on (b.itemid = d.itemid and c.itemid = d.itemid);
Here's an sqlfiddle demonstrating it: http://sqlfiddle.com/#!4/ff9d59/11

Related

Mysql select between two table without limiting if record appear on the joined table

I have been trying to figure out how to select data related to one id between to tables without limit it to the joined table. I tried using UNION, Inner join, JOIN, but it limit me to show records that are only in both tables. By example:
Table 1 (users)
id | name | register
1 | John | 2014-03-01
2 | Kate | 2014-03-02
etc..
Table 2 (birthdays by example)
id | user | birthday
1 | 1 | 1989-09-09
Note that kate dont have a record on the birthdays table, if i do:
SELECT U.id, name, register, B.birthday FROM users as U INNER JOIN birthday as B ON B.user = U.id
it will only shows JOHN data, i would like to select all my users and if the record do not exist on the joined table, still be able to select all my users, sort of:
id | name | register | birthday
1 | John | 2014-03-01 | 1989-09-09
2 | kate | 2014-03-02 | null or ''
3
4
etc.
Sorry if its a stupid question but i dont find the light on this one. I would appreciate the help.
Regards
You need a LEFT OUTER JOIN instead of the plain JOIN (also known as INNER JOIN), like this:
SELECT U.id, name, register, B.birthday
FROM users as U
LEFT JOIN birthday as B
ON B.user = U.id
A LEFT JOIN between users and birthday tables will contain all records of the "left" table (users), even if the join-condition does not find any matching record in the "right" table (birthday).
This excellent article on The Code Project will help you a lot: Visual Representation of SQL Joins.
Summary of all JOIN types:
Note: Mysql does not support FULL OUTER JOIN but it can be emulated. Useful articles:
https://stackoverflow.com/a/4796911
http://www.sql-tutorial.ru/en/book_full_join_and_mysql.html
http://www.xaprb.com/blog/2006/05/26/how-to-write-full-outer-join-in-mysql/
Use left outer join instead of inner join..
SELECT U.id, name, register, B.birthday
FROM users as U left join birthday as B ON B.user = U.id

SQL - can I do repeated inner joins again on the same column?

I have two simple tables, one called itineraries that holds details of holiday itineraries and one called users, that holds details of users. Other users create itineraries, and users can copy their itineraries and add travel agents, so the copied_from_id is the ID of the original creating user from users.id.
I've joined itineraries.user_id to users.id using the below query which works perfectly:
SELECT
itineraries.travel_agent_id,
itineraries.copied_from_id,
itineraries.user_id,
users.full_name,
users.username
FROM `gadabouting_gadabouting_production`.`itineraries`
INNER JOIN `gadabouting_gadabouting_production`.`users` ON itineraries.user_id=users.id
WHERE itineraries.travel_agent_id='253'
Giving me the following output:
+-----------------+------------------+---------+-------------+-------------+
| travel_agent_id | original_creator | user_id | full_name | username |
| 253 | 501 | 1465 | John Smithy | j.smithy |
| 253 | 501 | 1465 | John Smithy | j.smithy |
| 253 | 501 | 1474 | Ben Stockes | ben.stockes |
+-----------------+------------------+---------+-------------+-------------+
(The travel_agent_id and original_creator columns are the same as users.id).
What I want to do now is map the itineraries.travel_agent_id and itineraries.original creator to the users.full_name and users.username columns (so have the full_name and username columns printed next to each of the travel_agent_id and original_creator columns, but I just can't work out how to do it. I've spent hours on it now and can't get my head round it. Do I need to do more joins?
I've looked at several other SO questions about multiple joins but as far as I can see, none of them cover the process of 'going back' again and again on the same column as I want to do here.
Is this possible? Would greatly appreciate any help!
Thanks
You can join in the same table over and over, but you need to use an alias for each one so that you can specify which one you want to use. If you access the result by name, you also need alases for some of the field names.
(It's conventient to use aliases on other tables also, to make the query less verbose.)
select
i.travel_agent_id,
i.copied_from_id,
i.user_id,
u.full_name,
u.username,
ut.full_name as travel_agent_full_name,
ut.username as travel_agent_username,
uc.full_name as creator_full_name,
uc.username as creator_username
from
gadabouting_gadabouting_production.itineraries as i
inner join gadabouting_gadabouting_production.users as u on u.id = i.user_id
inner join gadabouting_gadabouting_production.users as ut on u.id = i.travel_agent_id
inner join gadabouting_gadabouting_production.users as uc on u.id = i.original_creator
where
i.travel_agent_id = '253'

Problem with one of my LEFT JOIN and SUM the result of it

So I got a question about LEFT JOIN, this code returns different values for totalPoints depending on if the user got the group or not. (if user don't got group or event it returns the correct value)
I just want to grasp how to get the LEFT JOIN flow_has_vote ON flow_has_vote.flow_id=flows.id to work every time. I did a solution before with three query's, one that gets the group and event rule, one that checks if the user got the group or event considering the security and one to get the flow...
And I guess I could solve this by having two query's, one that gets the group and event rules and also check if the user got the group and event and then one that gets the flow depending on the user should have access to it.
Right now I'm getting every information needed in ONE query and then checking with IF statements if it should be printed or not...
So, my question is, is it possible to get the SUM(flow_has_vote.points) AS totalPoints to work this way? And do you know how?
And also I'm a bit curios, is one query the best way to work with this? Would it be justified to use two when you take into account performance?
SELECT
flows.id AS flowId,
flows.security,
SUM(flow__has__vote.points) AS totalPoints,
users.id AS userId,
users.alias,
flows.event_id AS eventId,
events.group_id AS groupId,
events.membershipRules AS eMR,
groups.membershipRules AS gMR,
user__has__group.permission AS userHasGroup,
user__has__event.permission AS userHasEvent
FROM
users,
events LEFT JOIN user__has__event ON user__has__event.user_id = '.$userId.',
groups LEFT JOIN user__has__group ON user__has__group.user_id = '.$userId.',
flows LEFT JOIN flow__has__vote ON flow__has__vote.flow_id=flows.id
WHERE
flows.user_id = users.id AND
events.id = flows.event_id AND
groups.id = events.group_id AND
flows.id='.$flowId
And if you wonder what the SQL-statement is doing, getting the information for the flow(post), the information about the event and group that the flow is in, checking the user access to the group and event and also getting all the votes for the flow...
This is how the tables looks like...
FLOWS id,security,event_id,user_id
USERS id, alias
EVENTS id, name group_id, membershipRules
GROUPS id, name, membershipRules
USER__HAS__GROUP user_id,group_id,permission
USER__HAS__EVENT user_id,event_id,permission
FLOW__HAS__VOTE flow_id,user_id,points
This is the result I wish for...
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
| flowId | security | totalPoints | userId | alias | eventId | groupId | eMR | gMR | userHasGroup | userHasEvent |
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
| 1 | 2 | 1337 | 5 | Pontus | 15 | 2 | 2 | 2 | 4 | 4 |
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
and one more example...
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
| flowId | security | totalPoints | userId | alias | eventId | groupId | eMR | gMR | userHasGroup | userHasEvent |
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
| 1 | 2 | 1337 | 6 | Kezia | 15 | 2 | 2 | 2 | null | null |
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
Enjoy your life ~ Pontus
So, basically the main point (IMHO) is not to include conditions on tables you LEFT JOINed in the WHERE clause, since this makes the LEFT JOIN behave like an INNER JOIN.
Start with trying this query (although I am sure you will have to make adjustments as I am not sure exactly what you want as a result, more about this later):
SELECT
flows.id AS flowId,
flows.security,
SUM(flow__has__vote.points) AS totalPoints,
users.id AS userId,
users.alias,
flows.event_id AS eventId,
events.group_id AS groupId,
events.membershipRules AS eMR,
groups.membershipRules AS gMR,
user__has__group.permission AS userHasGroup,
user__has__event.permission AS userHasEvent
FROM users,
LEFT JOIN user__has__event
ON user__has__event.user_id = users.id,
LEFT JOIN events
ON user__has__event.event_id = events.id
LEFT JOIN user__has__group
ON user__has__group.user_id = users.id,
LEFT JOIN groups
ON user__has__group.group_id = groups.id
AND groups.id = events.group_id
LEFT JOIN flows
ON flows.user_id = users.id
AND events.id = flows.event_id
AND flows.id='.$flowId'
LEFT JOIN flow__has__vote
ON flow__has__vote.flow_id = flows.id
WHERE users.id = '.$userId.'
GROUP BY users.id
Here, I LEFT JOINed everything to the user, and also grouped by the user. I have a feeling you will want to add columns to the group by (flows.id?, events.id?)
Also, you may want to turn some of the LEFT JOINs to JOIN, so you will get only users who have a 'flow', for example.

"Friend" relationships across two mysql tables

I'm not quite sure how to construct an sql join inorder to find who a specific users "friends" are.
For example I have two table
User Table
u_ID | u_Name
-------------
1 | bob
2 | jill
3 | jack
4 | susan
Friends Table
f_ID | u_ID1 | u_ID2
--------------------
1 | 1 | 2
2 | 3 | 1
3 | 4 | 2
I need to find a way of getting all of bobs friends, or all of jills friends, for example.
Friends cannot have duplicate results
I.e. can have a row with either (u_ID1 = 1, u_ID2 = 2) or (u_ID1 = 2, u_ID = 1) but not both, as they are technically the same.
Here is my incorrect query
SELECT u.u_Name
FROM user u
INNER JOIN friends f ON (f.u_ID1 = '1' OR f.u_ID2 = '1')
Thanks in advance
Solution
Check Kris Babic reply for solution,
also thank you for everyone elses help
This uses a STRAIGHT join, but should work:
select u.u_Name
from friends f, user u
where (f.u_ID1 = '1' and u.u_ID = f.u_ID2)
or (f.u_ID2 = '1' and u.u_ID = f.u_ID1)
For all of bob's friends this should work (untested)
select u.u_Name
FROM user u
inner join friends f1 on f1.u_uID1 = u.u_ID
inner join friends f2 on f2.u_uID2 = u.u_ID
where u.u_ID = 1
Try this:
SELECT u1.u_Name as user1 , u2.u_Name as user2
FROM user as u1 INNER JOIN friends ON u1.u_ID=friends.u_ID1
INNER JOIN user as u2 ON u2.u_ID=friends.u_ID2

Select one row from this table and all related rows from other table

table user
id | name | address
3 | Jacko | 33A Herewini
table user_photo
id | userid | thumb | full
1 | 3 | 3k1j_thumb.jpg| 3k1j.jpg
1 | 3 | 3k1j_thumb.jpg| 3k1j.jpg
2 | 14 | 44r_thumb.jpg| 44r.jpg
2 | 14 | 55t_thumb.jpg| 55t.jpg
2 | 14 | 12f_thumb.jpg| 12f.jpg
I got the user id, I want to select his name and address and all his photos
PS: what tool/software you use to draw the table line (the +---+) ?
Edit: then how would you put the name in a div and all the photos in a ul
my html look like this
<div class='name'></div>
...600 elements...
<ul class='photos'></ul>
To get the address, you can just query that table:
SELECT * FROM user WHERE id = 3
To get the photos, you can query the user photo table:
SELECT * FROM user_photo WHERE userid = 3
If you want to get everything at once, you can join the two tables together on the user id:
SELECT * FROM user u
LEFT JOIN user_photo up ON up.userid = u.id
WHERE u.id = 3
Note though that the address info will of course be duplicated on every row
Learn about LEFT JOIN, see e.g. http://www.postgresql.org/docs/8.2/static/sql-select.html
SELECT * FROM user AS u LEFT JOIN user_photo AS p ON p.userid = u.id
Adjust as needed.
To combine the results of two tables linked by one column (id in this case), you would use a JOIN.
You can read up on JOINs in the MySQL Reference Manual
SELECT *
FROM user u
LEFT OUTER JOIN user_photo up ON up.userid = u.id