join with where condition - mysql

i read many join questions here but unable to understand and create my own to get the right result i want.
i have three tables for now that is status,members,friends friends table have two columns friend_id and member_id
all three tables have member_id common primary id of members table
now i want to get all the status created by members and member's friends
if i have three members with id's 1,2,3
friends table have id's 1,2 so these two becomes friends of each other
2 have 5 status updates and 1 have 2 status and 3 have 1 updates in status table
if i query against member 2 it should return 7 record...( 5 for 2 and 2 for 1 ) and should not return record of member 3.
if i query against member 1 it should return same record as for point 5.
do i need change in my tables structure ? please help how to get the record the way i want

How about a pre-query to the friends table for any qualifying member PLUS the member itself, then back-join to the rest of the tables...
select STRAIGHT_JOIN
PeopleList.Member_id,
members.last_name,
members.first_name, (etc with any other fields)
ms.status_id,
ms.description (etc with any other fields from member_status table)
from
( Select DISTINCT m.member_id
from Members m
where m.member_id = MemberDesiredVariable
union select f.friend_id AS member_id
from Friends f
where f.member_id = MemberDesiredVariable
union select f2.member_id
from Friends f2
where f2.friend_id = MemberDesiredVariable ) PeopleList
join members
on PeopleList.member_id = members.member_id
join member_status ms
on PeopleList.member_id = ms.member_id
This should get the primary person in question regardless of the person having ANY records in the "friends" table, such as a new person with no entries yet... they would at least qualify themselves and join to the members and member_status tables.
Then, in your scenario where member 1 is the criteria, it will query against the friends for any "Friend_IDs", and thus DISTINCT will have the 1 (direct from members) and the 2 where the member_id = 1, finds the Friend_id = 2. So now, this pre-query has two IDs and proceeds to get whatever the rest of your details you want.
The THIRD scenario is you want member 2... So, direct query to the members table guarantees their ID in the list to process, yet since their ID is NOT as a "MEMBER_ID" in the friends table, it has to look for itself as a "FRIEND_ID" from someone else and grab THAT Member's ID. So now, member 2 will also find member 1 and proceed to get details out.
As for member 3, if you queried against the Friends table, you'd get NO records at all, even IF the member 3 had some status records... It must be qualified against itself to be inclusive of the rest for processing... Yet will not find itself as a "member_id" nor "friend_id" in the friends table.
I couldn't actually test this at my current location, but logically should go no problem.
Finally, if you want the friends names REGARDLESS of having any "status" changes, change the last join to member_status to a LEFT JOIN.
--- Comment feedback
I can't suggest any books specifically, it just comes from years of experience...
1. UNDERSTAND THE RELATIONSHIP OF YOUR DATA...
2. Find out the inner-most "what do I want to get".
3. Throw all other elements out until you get the CRITERIA, not the CONTENT.
4. Keep your primary "get the criteria" up front... THEN Join in your other tables.
5. Then tack on all the other fields you want in the output result set
Trying to solve a complex query can very often be cluttered by all the OTHER elements of data a person is trying to get. Like so many other programming tasks... I like to make it work, then make it pretty. So too goes with querying. If your baseline query doesn't get the WHAT you want, it doesn't matter how many other tables you are joining together (left, outer, or normal join), your output will be wrong.
I've also added the clause "STRAIGHT_JOIN" to the sql at the top. This tells MySql to do the query in the order I've instructed it and don't have the optimizer try to think for me. This one clause has come in so frequently when joining a main table (such as millions of records) to "lookup" secondary tables that the query engine has falsely interpretted the lookup table as primary for querying which killed the performance...
Try to do some timed tests between the versions that work. If they are equally comparable, I would typically go with the one that I could understand in case I had to modify / change something in the future.

-- own records
SELECT member_id, friend_id, user_name, description
FROM
(SELECT M.member_id,
M.member_id friend_id,
M.user_name,
MS.description
FROM members M
LEFT JOIN member_status MS on MS.member_id = M.member_id
UNION ALL
-- friends records
SELECT M.member_id,
F.friend_id,
MF.user_name,
MS.description
FROM members M
JOIN ( SELECT friend_id member_id, member_id friend_id from friends
UNION SELECT member_id, friend_id from friends) F
ON F.member_id = M.member_id
LEFT JOIN member_status MS on MS.member_id = F.friend_id
LEFT JOIN members MF on MF.member_id = F.friend_id) R
WHERE R.member_id = 1

Here is the solution using UNION clauses. If the result if each SELECT is short (let's say less than 1000 rows) then it is faster than LEFT JOIN combined with a OR.
If by "friends of each other" you mean that you want :
(a) the status of the members marked as friend
+
(b) the status of the members which the considered member is marked as friend
then you should use the tree UNION below.
If you want only (a) then delete the last UNION.
SELECT s.status_id
FROM member_status AS s
WHERE (s.member_id=#id)
UNION ALL
SELECT s.status_id
FROM member_status AS s
INNER JOIN friends AS f ON (s.member_id=f.friend_id)
WHERE (f.member_id=#id)
UNION ALL
SELECT s.status_id
FROM member_status AS s
INNER JOIN friends AS f ON (s.member_id=f.member_id)
WHERE (f.friend_id=#id)

Related

Retrieving data from 3 Mysql tables

Suppose I have 3 different tables relationships as following
1st is tbl_users(id,gender,name)
2nd is tbl_feeds(id,user_id,feed_value)
3rd is tbl_favs(id,user_id,feed_id)
where id is primary key for every table.
Now suppose I want to get data where those feeds should come which is uploaded by Gender=Male users with one field in every row that should say either the user who is calling this query marked that particular feed as favourite or not.
So final data of result should be like following :
where lets say the person who is calling this query have user_id=2 then is_favourite column should contain 1 if that user marked favourite that particular feed otherwise is_favourite should contain 0.
user_id feed_id feed_value is_favourite gender
1 2 xyz 1 M
2 3 abc 0 M
3 4 mno 0 M
I hope you getting my question , I m able to get feeds as per gender but problem is I m facing problem to get is_favourite flag as per particular user for every feed entry.
I hope some one have these problem before and I can get help from those for sure.
I would be so thankful if some one can resolve my this issue.
Thanks
Something like this should work:
SELECT
u.id AS user_id.
fe.id AS feed_id,
fe.feed_value,
IFNULL(fa.is_favourite, 0),
u.gender
FROM
tbl_users u
JOIN
tbl_feeds fe ON (fe.user_id = u.id)
LEFT JOIN
tbl_favs fa ON (
fa.user_id = u.id
AND
fa.feed_id = fe.id
)
In order to link your tables, you need to find the most common link between them all. This link is user_id. You'll want to create a relationship between all tables with JOIN in order to make sure each and every user has data.
Now I don't know if you're planning on making sure all tables have data with the user_id. But I would use INNER JOIN as it will ONLY show records of that user_id without nulls. If the other tables could POSSIBLY (Not always guaranteed) you should use a LEFT JOIN based on the tables that is it possible with.
Here is an SQLFiddle as an example. However, I recommend you name your ID fields as appropriate to your table's name so that way, there is no confusion!
To get your isFavorite I would use a subquery in order to validate and verify if the user has it selected as a favorite.
SELECT
u.userid,
u.gender,
f.feedsid,
f.feedvalue,
(
SELECT
COUNT(*)
FROM
tbl_favs a
WHERE
a.userid = u.userid AND
a.feedsid = f.feedsid
) as isFavorite
FROM
tbl_users u
INNER JOIN
tbl_feeds f
ON
u.userid = f.userid
~~~~EDIT 1~~~~
In response to your comment, I have updated the SQLFiddle and the query. I don't believe you really need a join now based on the information given. If you were to do a join you would get unexpected results since you would be trying to make a common link between two tables that you do not want. Instead you'll want to just combine the tables together and do a subquery to determine from the favs if it is a favorite of the user's.
SQLFiddle:
SELECT
u.userid,
f.feedsid,
u.name,
u.gender,
f.feedvalue,
(
SELECT
COUNT(*)
FROM
tbl_favs a
WHERE
a.userid = u.userid AND
a.feedsid = f.feedsid
) as isFavorite
FROM
tbl_users u,
tbl_feeds f
ORDER BY
u.userid,
f.feedsid

Using a Combination of INNER and FULL OUTER Joins to Get Desired Data Set

I’ve got 5 Tables:
Patients Table 'PAT'
Patient Orders 'ORD'
Patient Comments 'Comm'
Patient Antigens 'Ant'
Patient MRNs 'MRN'
Information in the following four tables:
Pat has all of our patients information
Ord only has orders from the last four years, joined to the Pat table on Patient ID
Comm has all patient comments, joined on the Pat table on Patient ID
Ant has all antigens entered on patients, joined on the Pat table on Patient ID
MRN has all medical record numbers of patients, joined on the Pat table on Patient ID
What I’m trying to do it make sure I have all patients that appear in the Ord, Comm, Ant, and MRN tables and the join it to the Pat table to get information like first name, last name, sex, DOB...
So I’m trying to figure out how best to join these tables together to make sure I don’t include patients that don’t appear in the Ord, Comm, Ant, or MRN tables.
If I join the tables to my main patient table with left outer joins, I'll get all patients regardless if they have an order, comment, antigen, or MRN.
Select * from Pat
Left Outer Join Ord on Pat.X=Ord.X
Left Outer Join Comm on Pat.X=Comm.X
Left Outer Join Ant on Pat.X=Ant.X
Left Outer Join MRN on Pat.X=MRN.X
I've attempted to make a visual diagram of what I'm looking for:
My ideal query would return all Patients except for 4 and 10 because they do not have any orders, antigens, comments, or a MRN.
I was thinking I could perform FULL OUTER JOINS on the ORD, ANT, COMM, and MRN tables and then take those results and and join it to the PAT table.
Select *
FROM P50DATA.AGABFL1 ANT
FULL OUTER JOIN P50DATA.ORDMSTL1 ORD ON ANT.AGACCT=ORD.OPACCT
FULL OUTER JOIN P50DATA.DCMTRNL5 COMM ON ANT.AGACCT=COMM.DCACCT
FULL OUTER JOIN P50DATA.HOSPIDL1 HOS ON ANT.AGACCT=HOS.APACCT
But I don't know how to then take this data set and marry it to the Pat table so I could then get my patient information.
Thoughts?
This doesn't have to be too complicated.
ORD, ANT, COMM, and MRN don't have to join to each other, because you don't need the records in any one of them to match any other one. You just need to know whether they have records for a given patient or not.
There are many ways to construct such a query; here's one:
SELECT *
FROM PAT
WHERE EXISTS (
SELECT 1
FROM ORD
WHERE ORD.X = PAT.X
)
OR EXISTS (
SELECT 1
FROM ANT
WHERE ANT.X = PAT.X
)
OR EXISTS (
SELECT 1
FROM COMM
WHERE COMM.X = PAT.X
)
OR EXISTS (
SELECT 1
FROM MRN
WHERE MRN.X = PAT.X
)
Because you are querying the Patient table without joining it to anything, you don't have to worry about duplicate records, and because you're just checking existence for ORD, ANT, COMM, and MRN, you don't have to worry about exactly how many records each of them has for a given patient.

SQL query: What groups is a given member NOT a member?

I have three tables in MySQL,
groups (key: group_id)
members (key: member_id)
group_member_relations key: group_id, member_id
The last table has combinations of members and groups (members that have joined that group).
I've been struggling with a way to perform a single query that gives me a list of members and groups that are NOT IN the group_member_relations table. (Basically I want to eventually ask the question "What groups is a given member not a member"). I can do this the hard way in code but was wondering if a single query was possible.
Not a SQL wiz at all, but have used it a lot over the last 20 years, mostly basic stuff. This is obviously over my head. Made many attempts over the last few days but, embarrassingly don't seem to get close.
Any pointers from the sql wizards out there..
Groups that a member is not in:
select *
from group
where id not in (
select group_id
from group_member_relations
where member_id = ?)
The following query will list all groups available and the members that are not present on each group. The query will also give all the columns for each table.
SELECT a.*, b.*
FROM members a
CROSS JOIN groups b
LEFT JOIN group_member_table c
ON a.memberID = c.memberID AND
b.groupID = c.groupID
WHERE c.memberID IS NULL OR -- actually this condition is already enough
c.groupID IS NULL
SQLFiddle Demo

How to check if id exists in another table given table is 30 million records?

I know the question seems duplicate, but I don't know how to ask it differently.
I have two very simple tables in MySQL database, The first is table Users
id, user_id
1 1
2 3
4 4
The second is table Friends
id, user_id, friend_id
1 1 3
2 1 4
3 1 8
I dumped the data from CSV file that I would like to clean. I need to check if friend_id exists in table 1 as well. The first table has around 30000 rows, but the second table has around 30 million rows.
And I use this query to check
SELECT u.user_id, uf.friend_id as exists_friend_ids
FROM Users u, Friends uf
WHERE u.user_id = '1'
and uf.friend_id IN (select user_id from eventify.Users)
However, my desired output would be this but as I cannot run the above query to actually give my test results I cannot continue.
user_id, exists_friend_ids
1 3
1 4
You can see that 8 is not there, because it doesn't exist in Users table. But as the second table has over 30 million records it's just running forever on my computer. Am I doing it right or this is the only way to do it. Or should I learn Hadoop instead?
I have updated my query to use equal join.
Have you tried a LEFT JOIN query with a GROUP BY friend_id ? If a user doesn't exist, it won't add a line to the result.
If all you are doing is cleaning the table then you have some flexibility since the fact that the query runs slow will not have a great impact since you will want to run it only once. Here are a couple of different options:
use a left join to find the rows in friends without the corresponding friend id in the users table (untested):
SELECT Friends.id, Users.user_id
FROM Friends LEFT JOIN Users on Friends.friend_id = Users.user_id
WHERE Users.user_id is NULL
Then delete the records you find
use an inner join to fin the friends that exist. Then create a new table with those records (untested)
SELECT Friends.id, Users.user_id
FROM Friends INNER JOIN Users on Friends.friend_id = Users.user_id
And insert the resulting rows into a new table which will become your new "Friends" table.
Hope that helps
I don't understand why you do the CASE construct here. If you want to get a list of all friend_ids that don't exist in the users table, then what about something like:
select friends.friend_id,
count(*)
from friends
where friends.friend_id not in (select users.user_id
from users)
group by 1
You will of course have an index on users.user_id...

MySQL joins and COUNT(*) from another table

I have two tables: groups and group_members.
The groups table contains all the information for each group, such as its ID, title, description, etc.
In the group_members table, it lists all the members who are apart of each group like this:
group_id | user_id
1 | 100
2 | 23
2 | 100
9 | 601
Basically, I want to list THREE groups on a page, and I only want to list groups which have MORE than four members. Inside the <?php while ?> loop, I then want to four members who are apart of that group. I'm having no trouble listing the groups, and listing the members in another internal loop, I just cannot refine the groups so that ONLY those with more than 4 members show.
Does anybody know how to do this? I'm sure it's with MySQL joins.
MySQL use HAVING statement for this tasks.
Your query would look like this:
SELECT g.group_id, COUNT(m.member_id) AS members
FROM groups AS g
LEFT JOIN group_members AS m USING(group_id)
GROUP BY g.group_id
HAVING members > 4
example when references have different names
SELECT g.id, COUNT(m.member_id) AS members
FROM groups AS g
LEFT JOIN group_members AS m ON g.id = m.group_id
GROUP BY g.id
HAVING members > 4
Also, make sure that you set indexes inside your database schema for keys you are using in JOINS as it can affect your site performance.
SELECT DISTINCT groups.id,
(SELECT COUNT(*) FROM group_members
WHERE member_id = groups.id) AS memberCount
FROM groups
Your groups_main table has a key column named id. I believe you can only use the USING syntax for the join if the groups_fans table has a key column with the same name, which it probably does not. So instead, try this:
LEFT JOIN groups_fans AS m ON m.group_id = g.id
Or replace group_id with whatever the appropriate column name is in the groups_fans table.
Maybe I am off the mark here and not understanding the OP but why are you joining tables?
If you have a table with members and this table has a column named "group_id", you can just run a query on the members table to get a count of the members grouped by the group_id.
SELECT group_id, COUNT(*) as membercount
FROM members
GROUP BY group_id
HAVING membercount > 4
This should have the least overhead simply because you are avoiding a join but should still give you what you wanted.
If you want the group details and description etc, then add a join from the members table back to the groups table to retrieve the name would give you the quickest result.