mysql full join not working correctly - mysql

I am using the following mysql to look at user subscriptions. I need to get all the users and in one column show if they have an active subscription or not
This is what I have tried:
SELECT `general`.`exchange`.`email`,substring_index(`general`.`exchange`.`name`, " ", 1) as name, `courses`.`courseSubscriptions`.`expiryTimestamp`
FROM `general`.`exchange`
LEFT JOIN `courses`.`courseSubscriptions`
ON `courses`.`courseSubscriptions`.`memberID` = `general`.`exchange`.`id`
WHERE (`courses`.`courseSubscriptions`.memberID = `general`.`exchange`.`id`)
AND (`courses`.`courseSubscriptions`.expiryTimestamp > 1443975741)
The problem is that it is only returning users who have a subscription. I need it to return all users, and show in a column who has a subscription or not
How can I do this?

Your where clause is turning the left join into an inner join. Also, table aliases would make the query easier to write and to read:
SELECT e.`email`, substring_index(e.`name`, ' ', 1) as name, cs.`expiryTimestamp`
FROM `general`.`exchange` e LEFT JOIN
`courses`.`courseSubscriptions` cs
ON cs.`memberID` = e.`id` AND (cs.expiryTimestamp > 1443975741) ;
The solution to the LEFT JOIN problem is to move the condition on the second table into the ON clause. Also, you don't need to repeat the join conditions.
If you want a flag, you can add that:
SELECT e.`email`, substring_index(e.`name`, ' ', 1) as name, cs.`expiryTimestamp`,
(cs.`expiryTimestamp` is not null) as isActiveFlag
FROM `general`.`exchange` e LEFT JOIN
`courses`.`courseSubscriptions` cs
ON cs.`memberID` = e.`id` AND (cs.expiryTimestamp > 1443975741) ;

You've repeated the join condition in the where clause. Since null will never return true in an equality check, this effectively removes the users without any courses and turns your outer join to an inner join. Similarly, the condition on the course's time should also be moved to the join clause:
SELECT `general`.`exchange`.`email`,
SUBSTRING_INDEX(`general`.`exchange`.`name`, " ", 1) AS name,
`courses`.`courseSubscriptions`.`expiryTimestamp`
FROM `general`.`exchange`
LEFT JOIN `courses`.`courseSubscriptions` ON
`courses`.`courseSubscriptions`.`memberID` =
`general`.`exchange`.`id` AND
`courses`.`courseSubscriptions`.expiryTimestamp > 1443975741

Related

Use LEFT OUTER JOIN to return count even if 0

I am trying to return all of the specified event_abr's even if the count is 0.
Here is my SQL Query:
SELECT
basketball_event_types.event_abr,
count(*) value
FROM
basketball_event_types
LEFT OUTER JOIN basketball_game_events on basketball_event_types.id = basketball_game_events.event_type_id
LEFT JOIN users on basketball_game_events.player_id = users.id
WHERE
users.id = 198
AND
basketball_game_events.game_id = 213
AND
(basketball_event_types.event_abr = "TO" OR basketball_event_types.event_abr = "AST")
GROUP BY basketball_event_types.event_abr
However, when I run this, the specified user (198) has 0 AST's for the specified game (213). I want AST -> 0 to pop up, but it just doesn't. The two big things I tried were adding OUTER ot the LEFT JOIN, and I also tried using ifnull(count(*), 0). Neither gave me the result I was looking for.
The filtering needs to be in the on clause. And you need to fix the count():
SELECT bet.event_abr, COUNT(u.id) as value
FROM basketball_event_types bet LEFT JOIN
basketball_game_events bge
ON bet.id = bge.event_type_id AND bge.game_id = 213 LEFT JOIN
users u
ON bge.player_id = u.id AND u.id = 198
WHERE bet.event_abr IN ('TO', 'AST')
GROUP BY bet.event_abr;
Note the changes:
The filtering is moved to the ON clauses -- both filters to the appropriate clauses.
The COUNT() counts the values in the last LEFT JOINed table.
Table aliases make the query easier to write and to read.
IN is simpler than a chain of OR conditions.

MySQL LEFT JOIN a different table based on a field value

I know similar questions have been presented a few times, but it seems these all related to the field value changing based on a specific field value. I actually want to join a completely different table based on the the field value of each record.
I think this is possible with a single query, but since the join table could change every record I'm not sure.
To clarify I have four tables; I am trying to join three of them in a single query. However the third table could be one of two tables depending on the value in each records field.
I would imagine it would look something like this, but its not working.
SELECT crm_deal . * , crm_deal_step.sort_order, COALESCE( CONCAT_WS(' ',crm_lead.first_name, crm_lead.last_name),CONCAT_WS(' ',customer.firstname, customer.lastname) ) AS contact_full_name
FROM `crm_deal`
IF( crm_deal.deal_for = 'lead', LEFT JOIN crm_lead ON crm_deal.deal_for_id = crm_lead.lead_id, LEFT JOIN customer ON crm_deal.deal_for_id = customer.customer_id)
LEFT JOIN crm_deal_step ON crm_deal.deal_step_id = crm_deal_step.deal_step_id
Am I close? Is it possible or do I need to do a php loop and run a second query for every record?
You cant use if stat ment in a select statment.
But you can try this:
SELECT crm_deal . * , crm_deal_step.sort_order,
CASE WHEN crm_lead.first_name is not null AND
crm_lead.last_name is not null
THEN crm_lead.first_name+ ' '+crm_lead.last_name
ELSE customer.firstname +' '+ customer.lastname
END as contact_full_name
FROM `crm_deal`
LEFT JOIN crm_lead ON crm_deal.deal_for_id = crm_lead.lead_id AND crm_deal.deal_for = 'lead'
LEFT JOIN customer ON crm_deal.deal_for_id = customer.customer_id AND crm_deal.deal_for <> 'lead'
LEFT JOIN crm_deal_step ON crm_deal.deal_step_id = crm_deal_step.deal_step_id

IF/CASE in an outer join

We have two tables that I need to join on where a column (which, at this point, I can't change how/what values are used in that column) aren't using the same value. So depending on the value of the column in the first table, I need to join/select a particular value in the second table. Here is an example (which obviously doesn't work) of what I am trying to do:
SELECT Alert.*, Comments.Comment FROM
Alert
LEFT OUTER JOIN Comments ON Comments.ObjectId = Alert.ObjectId AND
CASE
WHEN Alert.ObjectType = 'U' THEN Comments.ObjectType = 'USER'
WHEN Alert.ObjectType = 'E' THEN Comments.ObjectType = 'EVENT'
END CASE
So I want everything from the Alert table and, if there are corresponding records in the Comments table, I want those, too. But only for the appropriate/matching ObjectType.
I've tried this using both CASE and IF but I can't seem to get it to work. Is something like this possible?
The CASE statement is made to return a value, not to perform an operation.
Also change the last END CASE to just END.
Use that returned value to compare with in the join condition.
Try:
SELECT Alert.*, Comments.Comment FROM
Alert
LEFT OUTER JOIN Comments ON Comments.ObjectId = Alert.ObjectId AND
Comments.ObjectType =
CASE Alert.ObjectType
WHEN 'U' THEN 'USER'
WHEN 'E' THEN 'EVENT'
END
I recommend you handle this with a UNION. In one union join the user comments in another the event comments:
SELECT Alert.*, userComments.Comment
FROM alert
LEFT OUTER JOIN comments usercomments ON userComments.ObjectId = Alert.ObjectId AND usercomments.objecttype='USER'
WHERE alert.objecttype = 'U'
UNION
SELECT Alert.*, eventComments.Comment
FROM alert
LEFT OUTER JOIN comments eventcomments ON eventComments.ObjectId = Alert.ObjectId AND eventcomments.objecttype='EVENT'
WHERE alert.objecttype = 'E'
You don't have to alias them they way I did - it just helps readability.
Why not just:
SELECT Alert.*, Comments.Comment FROM
Alert
LEFT OUTER JOIN Comments ON Comments.ObjectId = Alert.ObjectId AND
Alert.ObjectType = LEFT(Comments.ObjectType, 1);
It seems a lot simpler...
EDIT
Based on your comment, it seems that not all 'matching' values start with the same letter. In this case, I would recommend designing an intermediate table with columns AlertType char(1) and CommentType varchar(50). Insert each combination of TypeId, like U, User; E, Event; etc. The you can modify your SQL to read
SELECT Alert.*, Comments.Comment FROM
Alert
LEFT OUTER JOIN Intermediate i on Alert.ObjectType = i.AlertType
LEFT OUTER JOIN Comments ON Comments.ObjectId = Alert.ObjectId AND
Comments.ObjectType = i.CommentType;

Can't concat values with GROUP_CONCAT on joins

Could anybody help me?
With this query I am getting the ids, but it is not putting the separators when subscriber_data.fieldid is null. For example instead of coming 2,,12 it comes 2,12 when the value for 4 is null...
I think the problem is on the Join with subquery, but i couldn't make it with two left joins in the main query also...
This is the query im using:
SELECT
list_subscribers.emailaddress,
(SELECT
GROUP_CONCAT(IFNULL(customfields.fieldid,'') SEPARATOR '","')
FROM customfields
LEFT JOIN subscribers_data
ON subscribers_data.fieldid = customfields.fieldid
WHERE
customfields.fieldid IN (2,4,12,13,14,15,17,19,20,21,22,23,16,26,27)
AND
list_subscribers.subscriberid = subscribers_data.subscriberid
) AS data FROM list_subscribers
Thanks everyone.
The left join is useless. Because you have a condition on subscriber_data in the WHERE clause, that subquery will not return those rows for which there is no matching subscriber_data, so it effectively works as if you used INNER JOIN. You should add that condition to the left join condition, but it is impossible in this query layout. Values from the outer query are not allowed in join conditions in the inner query.
You could change it, but apparently you need to join three tables, where the middle table, subscriber_data, that links them all together, is optional. That doesn't really make sense.
Or maybe customfields is the table that is optional, but in that case, you should have reversed the table or used a RIGHT JOIN.
In conclusion, I think you meant to write this:
select
s.emailaddress,
GROUPCONCAT(IFNULL(f.fieldid, '') SEPARATOR '","')
from
list_subscribers s
inner join subscribers_data d on d.subscriberid = s.subscriberid
left join customfields f on f.fieldid = d.fieldid
where
d.fieldid in (2,4,12,13,14,15,17,19,20,21,22,23,16,26,27)
group by
s.emailaddress
Or do you want to list the id's of the fields that are filled for the subscriber(s)? In that case, it would be:
select
s.emailaddress,
GROUPCONCAT(IFNULL(d.fieldid, '') SEPARATOR '","')
from
list_subscribers s
cross join customfields f
left join subscribers_data d on
d.subscriberid = s.subscriberid and
d.fieldid = f.fieldid
where
f.fieldid in (2,4,12,13,14,15,17,19,20,21,22,23,16,26,27)
group by
s.emailaddress

Extra condition in ON clause is ignored

I have this query:
SELECT
TA.id,
T.duration,
DATE_FORMAT(TA.startTime,'%H:%i') AS startTime,
TEI.displayname,
TA.threatment_id,
TE.employeeid,
TTS.appointment_date
FROM
tblEmployee AS TE
INNER Join tblEmployeeInfo AS TEI ON TEI.employeeinfoid = TE.employeeinfoid
LEFT OUTER Join tblAppointment AS TA ON TE.employeeid = TA.employee_id
LEFT OUTER Join tblThreatment AS T ON TA.threatment_id = T.threatmentid
LEFT OUTER Join tblAppointments AS TTS ON TTS.id = TA.appointments_id
AND TTS.appointment_date = '2009-10-19'
LEFT OUTER Join tblCustomerCard AS TCC ON TCC.customercardid = TTS.customercard_id
WHERE
TE.employeeid = 1
What I try to accomplish is to select an employee, if available, all appointments at a given date. When there aren't any appointments, it should at least give the information about the employee.
But right now, it just gives all appointments related to an employee, and passes null when the date doesn't match. Whats going wrong here?
Because you are doing a left OUTER join, it will only join those records that match the On condition and will attach Null when the condition is not met.
You will still get records for which there is no Appointments on that date.
If you did an INNER join, then if the On condition is not met, no record will be output. So you will not get any records for which there are no appointments on that date.
Ok, not sure what database you are on, but this would work on SQL server :
select * from tblEmployee TA
...
left join
( select * from tblAppointments ed where ed.appointment_date = '10/01/2008' ) TTS
on ON TTS.id = TA.appointments_id
Thats the vibe anyway! You might need to tinker a bit.. Im at work and cant get the whole thing going for ya! :)