MySQL Outer Left Join with Criteria for Second Table - mysql

Table 1 - Golfers
golferID
golferName
Table 2 - Picks
golferID
userID
I want to select all Golfers who have not been picked yet by a certain userID. The Picks table could have 20 entries for one golferID.

Assume you want all Golfers who have not been picked yet by userID 3, you can use
SELECT * FROM Golfers g
WHERE g.golferID NOT IN (
SELECT p.golferID FROM Picks p
WHERE p.userID = 3
)

You can use this:
select G.golferId, G.golferName
from Golfers G left join Picks P
on G.golferId = P.golferId
where P.userId != 'certain_user_id' or P.userId is NULL

Related

MySQL: join where not equal to

I want to write a query that displays photos that user 5 did not like. Photos and likes are 2 tables joined by Photo_Id / Image_Id.
To display photos that user 5 did like I write:
Select
wp_photos.Photo_Id
From
wp_photos inner Join
wp_photos_likes On wp_photos.Photo_Id = wp_photos_likes.Image_Id
Where
wp_photos_likes.From_Id = 5
Group By
wp_photos.Photo_Id
I would like a query that displays all photos that user 5 (From_Id) did not like.
Trying
Where
wp_photos_likes.From_Id <> 5
Does not work as other users liked photos that user 5 liked and so returns these as well.
I tried some left joins but with no success.
What's the correct query?
You can use a left join antipattern:
select p.photo_id
from wp_photos p
left join wp_photos_likes l
on p.photo_id = l.image_id and l.from_id = 5
where l.image_id is null
This phrases as: check if each photo was liked by user 5, and filter on thoses who were not.
Another way to solve this is to use a not exists condition with a correlated subquery:
select p.photo_id
from wp_photos p
where not exists (
select 1
from wp_photos_likes l
where p.photo_id = l.image_id and l.from_id = 5
)

How to Join three tables properly

The main table has 4 columns:
User Activity Table
userActivityId userId therapistId activityId
1 1 1 1
Each of these columns is a table and these values are all foreign keys.
Basically im trying to run a query that will join to the users table and pull their first and last name based off the user Id.Same thing with therapist - join to the therapist table, pull first + last name.And finally Join to the Activity table and pull the activity name and path from the activity Id
The other tables look like this:
User Table
userId fName lName
Therapist Table
therapistId therapistFirstName therapistLastName
Activity Table
activityId activityTitle activityPath
So far my query looks like
SELECT
User_Activities.userId,
User_Activities.therapistId,
User_Activities.activityId,
Activities.activityTitle,
Activities.activityPath,
Users.fName,
users.lName,
Therapists.therapistFirstName,
Therapists.therapistLastName
FROM
User_Activities
INNER JOIN Users
ON User_Activities.userId = Users.userId
INNER JOIN Therapists ON
User_Activities.therapistId = Therapists.therapistId
INNER JOIN Activities ON
Activities.activityId = User_Activities.userActivityId
WHERE
User_Activities.userId = 1;
When I run this query It only returns 1 row as a result. However there are two activities in the User_Activites table assigned to userId 1.
If I change : INNER JOIN Activities ON
Activities.activityId = User_Activities.userActivityId
from an INNER JOIN to the LEFT JOIN it will display the second row, however the activityTitle and activityPath will be displayed as NULL in the second row.
userActivityId userId therapistId activityId activityId activityTitle activityPath fName lName therapistFirstName therapistLastName
1 1 1 1 1 Brain GZZ0zpUQ S C M D
11 1 1 1 NULL NULL NULL S C M D
You have pretty much answered your question. The second activity does not have a valid ActivityId.
If you want all activities for a user, then you should phrase the query as:
SELECT . . .
FROM Users u LEFT JOIN
User_Activities ua
ON ua.userId = u.userId LEFT JOIN
Therapists t
ON ua.therapistId = t.therapistId LEFT JOIN
Activities a
ON a.activityId = ua.userActivityId
WHERE u.userId = 1;
You want to start with the table where you want to keep all the rows. Then use LEFT JOIN to bring in other tables.
Two other changes of note:
Table aliases are used to simplify reading and writing the query. The SELECT needs to change to use the aliases.
The WHERE clause refers to the Users table rather than UserActivities.

How to select users without specific one to many rows in MySQL

Consider the following data set:
users table:
id (int) email (string)
1 first#example.com
2 second#example.com
order_items table:
id (int) user_id (int) generation (string)
1 1 '11'
2 1 '12'
2 1 '12.50'
3 1 '16.00'
4 2 '11'
5 2 '12'
UPDATED question
How can I select users which doesn't have order_items with generation 16.00 and have at least one order_item?
So:
email
second#example.com
1) Returning Users who don't have order item with generation 16 included users with no orders at all.
Assuming you have some kind of id column in order_items table:
select u.* from users u
left outer join order_items oi on (u.id = oi.user_id and oi.generation = 16)
where oi.id is null;
Otherwise use whatever primary key you have in order_items in the where condition to be NULL.
Updated to include answer for the question in comment
2) Returning users who don't have order item with generation 16 but have least one order.
select distinct u.* from users u
left outer join order_items oi16 on (u.id = oi.user_id and oi.generation = 16)
join order_items oiother on (u.id = oiother.user_id and oiother.generation != 16)
where oi16.id is null;
We do the filtering by using a second (normal) join which only returns users where it finds matching rows from the order_items table.
Here we need the distinct because the second join will multiply your rows depending on how many other orders the user have.
Alternatively you can also do a count or sum like this:
select u.*, count(distinct oiother.id) from users u
left outer join order_items oi16 on (u.id = oi.user_id and oi.generation = 16)
join order_items oiother on (u.id = oiother.user_id and oiother.generation != 16)
where oi16.id is null
group by u.id;
This will give you also how many other order items each returned user have. Or omit the count completely and using group by just to return distinct items.
You can use NOT EXISTS() like this:
SELECT * FROM Users u
WHERE NOT EXISTS(SELECT 1 FROM order_items o
WHERE o.userid = u.id
AND o.generation = 16)
That checks if there is a record for this user with order.generation = 16, and if there isn't it selects him.
Or not in()
SELECT * FROM Users u
WHERE u.id NOT IN(SELECT userid FROM order_items o
WHERE o.generation = 16)
That selects the list of users who have order.generation = 16, and select every id except them.
Following query should give you the desired output:
*update*
changed query as per the new result format in the question
As we want the data only from generation table, join with user table is not needed anymore. Here's the updated query:
select id, generation
from mytable where id not in (
select id from mytable
where generation = 16
group by id
);
Here is the SQL fiddle for it.

MySql select Not Work

I have two table .
one meal table
id type
1 lunch
2 lunch
3 dinner
two user_history table
ID Meal_id User_id create
1 2 4 1404638939
Now I want to select all meal from table one but have condition
if table two meal_id match with Meal table id and that create date same as current date then skip that row from table one
I use this code but not work correctly
SELECT m.* FROM `meal` AS m LEFT JOIN user_history
ON user_history.meal_id != m.id and date(FROM_UNIXTIME(user_history.create))!=CURRENT_DATE() where m.meal_type = 'Lunch'
SELECT m.* FROM `meal` AS m
LEFT JOIN user_history
ON user_history.Meal_id != m.id and date(FROM_UNIXTIME(user_history.create))=CURRENT_DATE()
where m.type LIKE "%lunch%"
If I correctly understood the question, I'd say:
SELECT m.* FROM `meal` AS m
WHERE m.meal_type = 'Lunch'
AND m.id NOT IN (
SELECT meal_id FROM user_history
WHERE date(FROM_UNIXTIME(user_history.create))=CURRENT_DATE()
)

SQL SUM Help - Only returning 1 field

My SQL is only returning one field when it should be returning one for each user.
Any idea where I'm going wrong? If you need additional information I can provide, but I'm just not sure where to go with this at the moment.
Here is my SQL:
SELECT uId, uForename, SUM(biProductPrice * biQuantity) AS uTotalSpent
FROM users
LEFT JOIN orders ON uId = ordUserId
LEFT JOIN basket ON ordUserId = bUserId
LEFT JOIN basketitems ON bId = biBasketId
WHERE ordStatus BETWEEN 4 AND 50
GROUP BY uId, uForename
any columns starting with u belong to the users table.
any columns starting with ord belong to the orders table.
any columns starting with b belong to the basket table.
any columns starting with bi belong to the basketitems table.
EDIT:
Everything now works fine except for my SUM, there are only 2 fields with an ordStatus between 4 and 50, so they are the only ones that apply, the biQuantity for one is 8 and the biProductPrice is 100, the other field has a biQuantity of 1 and a biProductPrice of 100, why is it returning a value of 400?
Group by the user and the sum will be returned for each one
SELECT users.id, users.name, SUM(biProductPrice) AS uTotalSpent
FROM users
LEFT JOIN orders ON uId = ordUserId
LEFT JOIN basket ON ordUserId = bUserId
LEFT JOIN basketitems ON bId = biBasketId
WHERE ordStatus BETWEEN 4 AND 50
group by users.uId, users.name
SELECT users.id, users.name, SUM(biProductPrice) AS uTotalSpent
FROM users
LEFT JOIN orders ON uId = ordUserId
LEFT JOIN basket ON ordUserId = bUserId
LEFT JOIN basketitems ON bId = biBasketId
WHERE ordStatus BETWEEN 4 AND 50
group by users.uId, users.name