mysql query joining tables to create newsfeed - mysql

I want to retrieve rows from 3 tables - LikeTable, ClosetTable, FollowTable to display the activities of all the users a person is following. This is for a website. ClosetTable is the table for the user's products. LikeTable is the table for the likes of the products and Followtable for the following and followers.
Currently, I have
SELECT c.user, c.productName
FROM ClosetTable AS c
LEFT JOIN FollowTable AS f ON c.user = f.isFollowed
WHERE f.follows = 'tony'
This returns the rows of the person, 'tony' is following together with the productname. However, I want rows from the LikeTable and FollowTable in the same manner altogether. So can you suggest a way to do only 1 query to get rows from all 3 tables?

You can try something that look like this :
SELECT c.user, c.productName
FROM FollowTable AS f
INNER JOIN ClosetTable AS c ON c.user = f.isFollowed
INNER JOIN LikeTable AS l ON c.user = l.userliked
WHERE f.follows = 'tony'
I do some assumption for fields name since you didn't provide the structures of your tables.
Also, I suggested that you put the FollowTable in the FROM clause and then join it to ClosetTable since you put f.follows = 'tony' in your WHERE clause.
Otherwise, remove the WHERE clause and put the condition in the INNER JOIN.
Something like :
LEFT JOIN FollowTable AS f ON c.user = f.isFollowed AND f.follows = 'tony'

More JOINs:
SELECT c.user, c.productName
FROM ClosetTable AS c
LEFT JOIN FollowTable AS f ON c.user = f.isFollowed
JOIN LikeTable as l on c.user = l.liked
WHERE f.follows = 'tony'
or something like that. You aren't restricted to one JOIN.

Related

Get results when where value is 'grade' or where it doesn't exist

I have a wordpress site that is used to store student grades on various lessons (from the quizzes on the site). I am trying to create a query that will pull out all of the lessons for all students in a certain group (using buddypress groups) and the students grades in each lesson.
I have created this query:
SELECT u.display_name, p.post_title, cm.meta_value
FROM wp_users u
JOIN wp_bp_groups_members gm
ON u.ID = gm.user_id
JOIN wp_comments c
ON u.ID = c.user_id
JOIN wp_commentmeta cm
ON c.comment_ID = cm.comment_id
JOIN (SELECT * FROM wp_posts WHERE post_type LIKE 'lesson') p
ON c.comment_post_id = p.ID
WHERE gm.group_id = 4 AND cm.meta_key LIKE 'grade'
This currently returns all the grades for all students in a group in the lessons they have attempted the test. However it does not return any lessons they have not attempted the test in, which I need still.
Just to be clear: lessons are posts, grades are meta_values in a record with a meta_key of 'grades'. These are stored as comments, and comment_meta.
I hope this is all clear and you can help. Thanks.
After Ollie Jones help I made this:
SELECT u.display_name, p.post_title, IFNULL(cm.meta_value,'--nothing--') grade
FROM wp_users u
JOIN wp_comments c
LEFT JOIN wp_commentmeta cm
ON c.comment_ID = cm.comment_id AND cm.meta_key = 'grade'
JOIN wp_bp_groups_members gm
ON u.ID = gm.user_id
JOIN wp_posts p
ON c.comment_post_id = p.ID
WHERE gm.group_id = 4 AND p.post_type LIKE 'lesson'
Which almost works but returns all student grades, not just the ones in the group (though it only gives the one name of the student in the group).
This is the familiar key/value store problem, that comes up with commentmeta, postsmeta, and usermeta. When you JOIN two tables and the left one might not have a corresponding row, you need to use LEFT JOIN. When the left one is a key/value table, you need to adjust the ON condition accordingly. Finally, when you LEFT JOIN two tables and there's no matching row in it it, you get back NULLs for those columns, so you must allow for that.
So this kind of SQL pattern will do the trick for you
SELECT whatever, IFNULL(cm.meta_value,'--nothing--') grade
FROM whatever
LEFT JOIN wp_comments c ON whatever.id = c.user_id
LEFT JOIN wp_commentmeta cm ON c.comment_id = cm.comment_id
AND cm.meta_key = 'grade'
JOIN whatever
Is there a user_id in your comments table? I dont see where you joined which user posted which comment. So when you join the group_members to the users table, you are specifying which users to show, but since you are not joining on user id when you join to your comments table, it will show all the comments for all the users. Im not sure if this will work for your table, but try:
SELECT u.display_name, p.post_title, IFNULL(cm.meta_value,'--nothing--') grade
FROM wp_users u
LEFT JOIN wp_comments c
JOIN wp_commentmeta cm
ON c.comment_ID = cm.comment_id AND cm.meta_key = 'grade'
JOIN wp_bp_groups_members gm
ON c.user_ID = gm.user_id
ON u.user_id = c.user_id
JOIN wp_posts p
ON c.comment_post_id = p.ID
WHERE gm.group_id = 4 AND p.post_type LIKE 'lesson'
Hope this helps!

MySQL: how to combine three tables

I have a table with persons (personid,name), another table with camps (campid,title) and a third table with camp participations (id,personid,campid).
Now, for a given camp, I need a list of all other camp participations by all persons participating in the current camp.
But I have no idea how to join these tables. I have looked at a lot of other examples, but I can't really seem to get any inspiration from those...
This should work:
SELECT *
FROM PERSONS AS D INNER JOIN CAMP_PARTICIPATIONS AS E ON D.PERSONID = E.PERSONID
INNER JOIN CAMPS AS F ON F.CAMPID = E.CAMPID
WHERE F.CAMPID <> [your_camp] AND A.PERSONID IN (
SELECT A.PERSONID
FROM PERSONS AS A INNER JOIN CAMP_PARTICIPATIONS AS B ON A.PERSONID = B.PERSONID
INNER JOIN CAMPS AS C ON C.CAMPID = B.CAMPID
WHERE C.CAMPID = [your_camp] )
select persons.*,participations.*,camps.* from persons left join participations
on participations.personid=persons.personid
left join camps on camps.campid=participations.campid
where camp.campid=1;
now u can change the campid in where clause and place column name which u want in select clause

How can I use OR in a left join in MySQL?

Maybe there's a better way to do this... I have a table of member friend requests. The columns are request_id, author_id, recipient_id, status(accepted or denied). I also have a members table whose id is linked to the author or recipient. I want to get a list of a member's friends by selecting from the members table and then joining the requests table. Since the member can be either the author or the recipient of any, some, or none of the requests, a simple LEFT JOIN member_requests AS r ON member_id = r.author_id wouldn't work. How can I write a query that will do this?
SELECT
m.member_id, m.display_name
r.author_id, r.recipient_id, r.status
FROM members AS m
LEFT JOIN member_requests AS r ON m.member_id = r.recipient_id
WHERE r.status = 1 --Accepted
ORDER BY m.display_name
You can use an OR in your left join, like so:
LEFT JOIN member_requests AS r
ON m.member_id = r.recipient_id
OR m.member_id = r.author_id
However, your where clause also needs to be altered:
SELECT
m.member_id, m.display_name
r.author_id, r.recipient_id, r.status
FROM members AS m
LEFT JOIN member_requests AS r
ON (m.member_id = r.recipient_id
OR m.member_id = r.author_id)
AND r.status = 1 //Accepted
ORDER BY m.display_name
When you left join table A to table B, and then specify a restriction in your where clause on table B, you convert your left join into an inner join. Typically, a left join to table B would yield some null values, since table A might have records that don't join to table B. But if you say 'where table B.value = x', you restrict your join to only rows in which table A joins to table B, and furthermore to rows in which 'B.value = x'. The join is then evaluated as an inner join, rather than a left outer.

Complex MySQL query with multiple select statements

I have three tables in Mysql that are link together:
Profile (ID, Name, Stuff..)
Contact(ID, ProfileID,desc,Ord)
Address(ID,ProfileID, desc, Ord)
Now I need to select all profile from the profile table, with the “desc” field from Contact and Address where Ord = 1. (this is for a search function where in a table I’ll display the name, main contact info and main Address of a client.
I can currently do this with three separate SQL request:
SELECT Name, ID FROM Profile WHERE name=”bla”
Then in a foreach loop, I’ll run the other two requests:
SELECT ProfileID, desc FROM Contact WHERE ProfileID=MyProfileID AND Ord=1
SELECT ProfileID, desc FROM Address WHERE ProfileID=MyProfileID AND Ord=1
I know you can do multiple SELECT in one query, is there a way I could group all three SELECT into one query?
You should be able to JOIN the tables on the profile.id and the profileid in the other tables.
If you are sure the profileid exists in all three tables, then you can use an INNER JOIN. The INNER JOIN returns matching rows in all of the tables:
select p.id,
p.name,
c.desc ContactDesc,
a.desc AddressDesc
from profile p
inner join contact c
on p.id = c.profileid
inner join address a
on p.id = a.profileid
where p.name = 'bla'
and c.ord = 1
and a.ord = 1
If you are not sure that you will have matching rows, then you can use a LEFT JOIN:
select p.id,
p.name,
c.desc ContactDesc,
a.desc AddressDesc
from profile p
left join contact c
on p.id = c.profileid
and c.ord = 1
left join address a
on p.id = a.profileid
and a.ord = 1
where p.name = 'bla'
If you need help learning JOIN syntax, here is a great visual explanation of joins
This query below only selects column when an ID from Profile table has atleast one match on tables: Contact and Address. If one or both of them are nullable, use LEFT JOIN instead of INNER JOIN because LEFT JOIN displays all records from the Left-hand side table regardless if it has a match on other tables or not.
SELECT a.*,
b.desc as BDESC,
c.desc as CDESC
FROM Profile a
INNER JOIN Contact b
ON a.ID = b.ProfileID
INNER JOIN Address c
ON a.ID = c.ProfileID
WHERE b.ORD = 1 AND
c.ORD = 1 AND
a.Name = 'nameHERE'
The LEFT JOIN version:
SELECT a.*,
b.desc as BDESC,
c.desc as CDESC
FROM Profile a
INNER JOIN Contact b
ON a.ID = b.ProfileID AND b.ORD = 1
INNER JOIN Address c
ON a.ID = c.ProfileID AND c.ORD = 1
WHERE a.Name = 'nameHERE'
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
i have created working demo as your requirement :
The query bellow will retrieve all matching records from the database.its retrieving profile id,name stufff and description of contact tables
select p.id,p.name,p.stauff,c.descr,a.descr from profile as p
inner join contact as c on c.profileid=p.id
inner join address as a on a.profileid=p.id
where p.name="bla" and c.ord=1 and a.ord=1

MySQL how to check if selected column is null before woking with him?

I am working on expanding user profiles for my project, adding more info. I was just adding countries, when I hit a problem. This is my query for selecting all profile data needed:
SELECT c.*, d.username, d.email, e.country_name
FROM user_profiles c, users d, country e
WHERE c.user_id = ".$id." AND d.id = ".$id."
AND e.country_name = (SELECT country_name FROM country WHERE id = c.country_id)
c.* should select all columns from user_profiles.
The $id is an id of user selecting wich profile should be returned. The problem is, keeping registration as simple as possible, I dont have a country selector there. So when this query comes in action, column country_id from user_profiles is empty, that means when I want to return the country_name, the query result is empty, so no profile data is returned.
I have tried to rewrite this with CASE or LEFT JOIN, but I think I am missing something. I want to find if country_id is not null, when it is not, select also country_name with this country_id. Any ideas?
Thank you kindly for responds.
You can use a LEFT JOIN, but it would be so much easier to do that if you started off by using the cleaner and more modern JOIN syntax:
SELECT c.*, d.username, d.email, e.country_name
FROM user_profiles c
JOIN users d ON d.id = c.id
JOIN country e ON e.country_id = c.country_id
WHERE c.user_id = 42
Now to solve your problem you can just add LEFT:
LEFT JOIN country e ON e.country_id = c.country_id
Full query:
SELECT c.*, d.username, d.email, e.country_name
FROM user_profiles c
JOIN users d ON d.id = c.id
LEFT JOIN country e ON e.country_id = c.country_id
WHERE c.user_id = 42
Related
Why isn't SQL ANSI-92 standard better adopted over ANSI-89?
Before I even start thinking about your current problem, can I just point out that your current query is a mess. Really bad. It might work, it might even work efficiently - but it's still a mess:
SELECT c.*, d.username, d.email, e.country_name
FROM user_profiles c, users d, country e
WHERE d.id = ".$id."
AND d.id = c.user_id
AND e.id = c.country_id;
I have tried to rewrite this with CASE or LEFT JOIN
But you're not going to show us your code?
One solution would be to use a sub select against each row in user_profiles/users:
SELECT c.*, d.username, d.email,
(SELECT e.country_name
FROM country e
WHERE AND e.id = c.country_id LIMIT 0,1) AS country_name
FROM user_profiles c, users d
WHERE d.id = ".$id."
AND d.id = c.user_id;
Alternatively, use a LEFT JOIN:
SELECT c.*, d.username, d.email, e.country_name
FROM user_profiles c
INNER JOIN users d
ON d.id = c.user_id
LEFT JOIN country e
ON e.id = c.country_id
WHERE d.id = ".$id.";