Join and nullable values - mysql

I have three tables in my MySql database :
Vehicle
Id_vehicle_pk( int,auto-increment,pk)
id_driver_fk(varchar,fk, nullable)
User
Id_user_pk(varchar,pk)
mail(varchar)
Collaborator
Id_coll_pk(int,pk)
Id_user_fk(varchar,fk)
First_Name(varchar)
Last_Name(varchar)
I'd like to create a view which join these three tables, so i try this
select Id_vehicle_pk, id_driver_fk,mail, Id_coll_pk, First_Name,Last_Name
from
Vehicle join User join
Collaborator
where
`Vehicle`.`id_driver_fk` = `User`.`Id_user_pk`
and `Collaborator`.`Id_user_fk` = `User`.`Id_user_pk`
group by Id_vehicle_pk
Having Id_vehicle_pk> 0
But I have wrong result caused by null values of id_driver_fk. So I need to know :
How can I fix my query?

I think you are looking for the LEFT JOIN clause which allows to get all records from the main table even if there are no foreign key matches to other tables:
SELECT
v.Id_vehicle_pk,
v.id_driver_fk,
u.mail,
c.Id_coll_pk,
c.First_Name,
c.Last_Name
FROM Vehicle v LEFT JOIN
User u ON v.id_driver_fk = u.Id_user_pk LEFT JOIN
Collaborator c ON c.Id_user_fk = u.Id_user_pk
WHERE Id_vehicle_pk> 0
Also it is a best practice to put join condition inside the join rather than WHERE clause.

You are missing the on clause in both of your joins.
select Id_vehicle_pk,
id_driver_fk,
mail, Id_coll_pk,
First_Name,
Last_Name
from Vehicle
join User on id_driver_fk = Id_user_pk
join Collaborator on Id_user_pk = Id_user_fk
group by Id_vehicle_pk
Having Id_vehicle_pk> 0

Related

Avoid Duplicate in Mysql Left Join Query

i have to take data from three tables like Userdetails,tasks,timedetails so i am getting data but it is duplicating based on the timedetails table. for example person id is - 1. it is presented two times in timedetails table then i'm getting duplicate rows.
my query is
SELECT GROUP_CONCAT(B.task_status) as task_status,
GROUP_CONCAT(B.task_type) as task_type,
GROUP_CONCAT(B.task_id) as task_id,
GROUP_CONCAT(B.task_name) as task_name,
A.us_id,
A.us_name,
C.out_time
FROM ts_userdetails A
LEFT JOIN
cms_task B
ON B.emp_id = A.us_id
LEFT JOIN
ts_timedetails C
ON C.user_id=A.us_id
WHERE C.entry_date='2017-05-09' AND
A.us_id!='1'
GROUP BY C.user_id
I am getting results like
I don't want duplicated things in displayed fields.
If I have 2 timedetails for one particular person id means 2 times duplication occurs. I just want one time.
use distinct keyword
SELECT GROUP_CONCAT(distinct B.task_status) as task_status,GROUP_CONCAT(distinct B.task_type) as task_type,GROUP_CONCAT(distinct B.task_id) as task_id,GROUP_CONCAT(distinct B.task_name) as task_name,A.us_id,A.us_name,C.out_time FROM ts_userdetails A LEFT JOIN cms_task B ON B.emp_id = A.us_id LEFT JOIN ts_timedetails C ON C.user_id=A.us_id WHERE C.entry_date='2017-05-09' AND A.us_id!='1' GROUP BY C.user_id

MySQL query to filter only expired users from table

I'm trying to export af list of subscribers from a database, and while my sql call works, it's missing some logic. My problem is - I want to export users with an expired membership. Some users might have both an expired membership and an active one. I don't want those users on my list.
I'm thinking I need a subquery to filter users with both an expired and an active membership, but I'm not sure how.
Here's my current query:
SELECT
jos_users.name,
jos_users.email,
jos_users.username,
jos_payplans_subscription.expiration_date,
jos_payplans_subscription.status,
jos_payplans_subscription.total,
jos_payplans_subscription.subscription_id
FROM jos_payplans_subscription
LEFT JOIN jos_users ON jos_payplans_subscription.user_id = jos_users.id
LEFT JOIN jos_payplans_user ON jos_payplans_user.user_id = jos_users.id
WHERE (jos_payplans_subscription.expiration_date BETWEEN '2015-09-01 00:00:00' AND '2015-10-31 00:00:00')
I'll appreciate any help.
Thanks :-)
You could join back to the subscriptions table to find the active subs, and ignore those:
select q.name, q.email, q.username, q.expiration_date, q.status, q.total, q.subscription_id
from (
SELECT
jos_users.name,
jos_users.email,
jos_users.username,
jos_payplans_subscription.expiration_date,
jos_payplans_subscription.status,
jos_payplans_subscription.total,
jos_payplans_subscription.subscription_id
FROM jos_payplans_subscription
LEFT JOIN jos_users ON jos_payplans_subscription.user_id = jos_users.id
LEFT JOIN jos_payplans_user ON jos_payplans_user.user_id = jos_users.id
WHERE (jos_payplans_subscription.expiration_date BETWEEN '2015-09-01 00:00:00' AND '2015-10-31 00:00:00')
) q
join jos_payplans_subscription s on s.user_id = q.user_id
group by q.name, q.email, q.username, q.expiration_date, q.status, q.total, q.subscription_id
having min(s.expiration_date) is not null;
Edit: Forgot the is
having min(s.expiration_date) not null
to
having min(s.expiration_date) is not null
A similar thing I've done before was create a junction table between a users and membership table called "activeMembership." This had a composite PK of userID and membershipID and a column called "membershipSatus"
Then you could easily do:
SELECT username, email, membershipType, membershipStatus FROM
activeMembership
JOIN users on users.userID = activeMembership.userID
JOIN membership ON membership.membershipID = activeMembership.membershipID
WHERE membershipStatus='Inactive';
Maybe altering the schema and creating a many to many relationship would solve your problem?

count associate rows in multiple tables with left join

I'm trying to select Posts with the associate numbers of Comments and Likes.
This is my query
SELECT `waller_posts`.*,
COUNT(waller_comments.id) AS num_comments,
COUNT(waller_likes.id) AS num_likes
FROM `waller_posts`
LEFT JOIN `waller_comments` ON `waller_comments`.`post_id` = `waller_posts`.`id`
LEFT JOIN `waller_likes` ON `waller_likes`.`post_id` = `waller_posts`.`id`
WHERE `wall_id` = 1
AND `wall_type` = "User"
GROUP BY `waller_posts`.`id`
When I add the second left join in this case of the likes, the results of the num_comments and num_likes came wrong. How can I perform this kind of query?
The query builds up to give you every possible combination of comments and likes on a post.
Probably easiest to just use COUNT(DISTINCT...) :-
SELECT `waller_posts`.*,
COUNT(DISTINCT waller_comments.id) AS num_comments,
COUNT(DISTINCT waller_likes.id) AS num_likes
FROM `waller_posts`
LEFT JOIN `waller_comments` ON `waller_comments`.`post_id` = `waller_posts`.`id`
LEFT JOIN `waller_likes` ON `waller_likes`.`post_id` = `waller_posts`.`id`
WHERE `wall_id` = 1
AND `wall_type` = "User"
GROUP BY `waller_posts`.`id`
Note that your query is relying on a feature of MySQL but which would cause an error in most flavours of SQL. For most flavours of SQL you need to list ALL the non aggregate columns in the GROUP BY clause.
Use Distinct clause because it will display combination of like and comment table data
SELECT `waller_posts`.*,
COUNT(DISTINCT waller_comments.id) AS num_comments,
COUNT(DISTINCT waller_likes.id) AS num_likes
FROM `waller_posts`
LEFT JOIN `waller_comments` ON `waller_comments`.`post_id` = `waller_posts`.`id`
LEFT JOIN `waller_likes` ON `waller_likes`.`post_id` = `waller_posts`.`id`
WHERE `wall_id` = 1
AND `wall_type` = "User"
GROUP BY `waller_posts`.`id`

LEFT OUTER JOIN AND WHERE on joining table

To elaborate, I'm selecting fields from item and locations tables. Connection is location_id from items table and id field from locations table. After join I'm doing WHERE statement on city_text field from locations table.
Is this legal action since I'm doing WHERE on field from second table?
SELECT uc_items.* ,
uc_users_store.id AS store_id,
uc_users_store.store_name,
uc_users_store.address,
uc_users_store.work_hours,
uc_locations.city_text AS city,
uc_locations.zipcode_text AS zipcode,
uc_locations.state_text AS STATE,
uc_locations.country_text AS country
FROM uc_items
LEFT OUTER JOIN uc_users_store ON uc_items.store_id=uc_users_store.id
LEFT OUTER JOIN uc_locations ON uc_users_store.store_location_id=uc_locations.id
WHERE uc_locations.city_text LIKE "%'.$city.'%"
AND uc_items.iname LIKE "%'.$description.'%"
AND uc_items.expiration_stamp > '.time().'
ORDER BY uc_items.posting_stamp DESC,
uc_items.discount DESC
It's legal, but might lead to unexpected results if the tested value is NULL. However, you could catch these situations by including an IS NULL check.
For instance
WHERE (col = 'value' OR col IS NULL)
Completely legal. However it would be a lot more logical to use INNER JOIN instead of LEFT JOIN since your WHERE statement is concerning the table you are joining to. A pseudo example:
SELECT t1.something, t2.somethin FROM first_table t1 INNER JOIN second_table t2 ON t1.some_id = t2.some_id_from_t1 WHERE t2.some_column='something'

Mysql Query Left Join Condition Problem

I have a litte problem with a mysql query.
I use 5 tables:
user_has_data (uid, dataid); users (uid, uname); user_in_group(uid, groupid, data); groups(groupid, data, packageid); packages(packageid, name)
all ids are PK. I want to build a sql query that finds a user, which belongs to a specified dataid, by its uname and checks if the user is in a group (relation in table user_in_group) belonging to a specified package (a group is assigned to one package). if so data from users, package and group should be fetched, otherwise only the user data should be fetched. Therefore I use left joins, so I can also get the users with no group:
SELECT `uac`.`uid`, `u`.`uid`, `uig`.`groupid`, `ag`.`packageid`
FROM `user_has_data` AS `uac`
INNER JOIN `users` AS `u` ON u.uid = uac.uid
LEFT JOIN `user_in_group` AS `uig` ON uig.uid = uac.uid
LEFT JOIN `groups` AS `ag` ON (ag.groupid = uig.groupid) AND (ag.packageid = 2)
WHERE (uac.dataid = '3') AND (u.uname LIKE 'test%')
GROUP BY `u`.`uid`
Unfortunately I get wrong results: I get groups that have a different packageid than stated in the join, if the user has another group assigned to him with a different packageid.
probably this is because the first left join has no restrictions to packageid and the second is a left join and so it has no restrictions on the result (packageid is NULL for all results, but should have values). If I change the second left join to a ordinary join, the group problem would be fixed but the query cant find users without group any more.
Any ideas how to fix this or even possible?
thanks in advance!
Are you saying that you are actually seeing the value ag.packageid = 2 in your query results?
If not, I think you might try something like:
SELECT `uac`.`uid`, `u`.`uid`, `g`.`groupid`, `g`.`packageid`
FROM `user_has_data` AS `uac`
INNER JOIN `users` AS `u` ON u.uid = uac.uid
LEFT JOIN (`user_in_group` AS `uig`
INNER JOIN `groups` AS `ag` ON (ag.groupid = uig.groupid) AND (ag.packageid = 2) )
AS `g` ON uac.uid = g.uid
WHERE (uac.dataid = '3') AND (u.uname LIKE 'test%')
GROUP BY `u`.`uid`
Because you are limiting your search to a specific group packageid of '2', why not just make both of your LEFT JOIN INNER JOINS and then throw in ag.packageid = 2 in your WHERE clause?
SELECT `uac`.`uid`, `u`.`uid`, `uig`.`groupid`, `ag`.`packageid`
FROM `user_has_data` AS `uac`
INNER JOIN `users` AS `u` ON u.uid = uac.uid
LEFT OUTER JOIN `user_in_group` AS `uig` ON uig.uid = uac.uid
LEFT OUTER JOIN `groups` AS `ag` ON ag.groupid = uig.groupid
WHERE (uac.dataid = '3') AND (u.uname LIKE 'test%')
AND (ag.packageid = 2 OR uig.uid IS NULL)
GROUP BY `u`.`uid`
I know LEFT JOIN and LEFT OUTER JOIN mean the same thing, but I like to be explicit. With the condition in your join, I bet you were getting groups with different packages, but weren't getting the packages?