WHERE clause if match - mysql

What is the simplest way to make part of a WHERE clause dependent on the result of a JOIN? I realize that is an extremely ambiguous and confusing question, so allow me to simply show you what I am trying to achieve:
SELECT m.member_id, m.first_name, m.last_name
FROM cal_form_answers a
INNER JOIN cal_form_elements e
USING(element_id)
INNER JOIN cal_forms f
USING(form_id)
LEFT JOIN members m
USING(member_id)
WHERE f.org_id = ?
AND m.org_id = ?
AND e.form_id = ?
GROUP BY a.member_id
ORDER BY a.member_id
First, note that the question marks are not invalid syntax—I am using Codeigniter, which uses them as bound parameters.
Line 10 (AND m.org_id = ?) is dependent on whether or not the LEFT JOIN actually finds something. If there is no match in the members table, then Line 10 becomes unnecessary. In fact, it becomes a problem. I would like to limit results by Line 10 unless there is no match in the members table. In that event, I would simply like to ignore that part of the WHERE clause.
I believe this can be achieved using subqueries, though I am admittedly unsure how to work out the syntax. Is there any other way? If yes, how else can this result be achieved? In the event there is no other way, can somebody demonstrate an implementation of a subquery for this situation, and explain how it works?
Thank you!

If a LEFT JOIN does not match a record, then those LEFT JOINed fields are null. Why not just check if that field IS NULL, and when it is not then check it.)
SELECT m.member_id, m.first_name, m.last_name
FROM cal_form_answers a
INNER JOIN cal_form_elements e
USING(element_id)
INNER JOIN cal_forms f
USING(form_id)
LEFT JOIN members m
USING(member_id)
WHERE f.org_id = ?
AND (m.org_id = ? OR m.org_id IS NULL)
AND e.form_id = ?
GROUP BY a.member_id
ORDER BY a.member_id

Related

How can I perform this MySQL update command [duplicate]

This question already has answers here:
How to update from select with a Join
(2 answers)
Closed 8 years ago.
My SQL (no pun intended) is rather rusty. I need to update user's group_id in Table A based on a combination of data from Tables B & C. Can someone give me some pointers on how I should do it.
Here is how the SELECT statement looks:
SELECT group_id
FROM exp_channel_data d, exp_channel_titles t, exp_members m
WHERE d.field_id_19 LIKE '%[362]%'
AND t.entry_id = d.entry_id
AND t.author_id = m.member_id
At the moment, I'm more familiar with MSSQL, but I believe this will also work in MySQL. You haven't indicated what you want to update group_id to, so I just inserted a random string in there. If you want to set it to the value from another table in the query, you can also do that by doing something like m.group_id = d.group_id:
UPDATE m SET
m.group_id = 'newValue'
FROM
exp_members m
INNER JOIN exp_channel_titles t
ON m.member_id = t.author_id
INNER JOIN exp_channel-data d
ON t.entry_id = d.entry_id
AND d.field_id_19 LIKE '%[362]%'
As you can see, I've changed your implicit joins to explicit ones (eg INNER JOIN). I really recommend this syntax, as it's easier to see what's going on by separating your WHERE conditions from your JOIN conditions.
Update:
It looks like MySQL doesn't support the UPDATE...FROM syntax used above. Try this instead:
UPDATE
exp_members m,
exp_channel_titles t,
exp_channel_data d
SET
m.group_id = 'newValue'
WHERE
m.member_id = t.author_id
AND t.entry_id = d.entry_id
AND d.field_id_19 LIKE '%[362]%'
Again, this isn't tested, but I think it will work. If not, it might at least help you get you closer to your answer.
Maybe UPDATE from SELECT solve you problem...
UPDATE TABLE
SET group_id = (SELECT group_id FROM ...)
WHERE group_id = <your_criteria>

Mysql building query using same column multiple times with different parameters. Use subquery?

I have the following query which returns the number of appointments that a particular subject has had:
select s.last_name, count(c.length)
from data.appointments a, data.subjects s, data.clinics c, research.sublog t
where s.id = a.subject_id and c.id = a.clinic_id and
s.ssn = t.ssn and a.status = '1' and
a.appt_date between '2012-10-01' and '2013-09-30' and a.appt_time not like '%01'
group by t.id
I would like to have counts for multiple time periods in the same query (add different years or quarters). I believe I would need to use subqueries for this but am having trouble deciphering what conditions to put in each subquery and what needs to remain outside of the subqueries (I have little experience in this area). Is this correct or is there a different method that would be better to use to return such a result? Thanks in advance for any help you can offer!
First, you want proper join syntax. Second, the solution to your problem is conditional aggregation functions. Here is an example:
select s.last_name,
sum(a.appt_date between '2012-10-01' and '2013-09-30') as cnt_2012,
sum(a.appt_date between '2013-10-01' and '2014-09-30') as cnt_2013
from data.appointments a join
data.subjects s
on s.id = a.subject_id join
data.clinics c
on c.id = a.clinic_id join
research.sublog t
on s.ssn = t.ssn
where a.status = '1' and
a.appt_time not like '%01'
group by t.id;
I didn't make the change, but you should probably have group by s.last_name because you have last_name in the select clause. And, the filter on appt_time doesn't make sense to me. You shouldn't use like on a date/time field.

Write SQL other optimized way

I have below query which I have written like below. Actually, I want to get two diff. colors from colors table. Please look into it and can you tell me It is optimized way? Can I write below query other optimized way?
SELECT d.*,
(SELECT c.clr_title FROM colors AS c WHERE c.id = d.base_color_id) AS base_color,
(SELECT c.clr_title FROM colors AS c WHERE c.id = d.overlay_color_id) AS overlay_color
FROM indira.dress AS d
WHERE id=669;
Thanks for your help.
Here's another way to get an equivalent result:
SELECT d.*
, b.clr_title AS base_color
, o.clr_title AS overlay_color
FROM indira.dress d
LEFT
JOIN colors b ON b.id = d.base_color_id
LEFT
JOIN colors o ON b.id = d.overlay_color_id
WHERE d.id=669
The correlated subqueries in the SELECT list can be expensive for large sets. But for returning a single row, that's not going to be a performance issue, since those subqueries will get executed only once.
In a more general case, for returning lots of rows, using a JOIN is usually more efficient.
You likely already have suitable indexes. For optimum performance, you'd want an index ON indira.dress(id) (likely already the primary key) and ON colors (id) (again, likely already the primary key). There's likely no performance benefit of adding a covering index.
Here is another option. I don't know what columns you do have on dress table so you will likely have to call the ones you out in select and group but this should work.
Not sure if it would be any faster/slower but wanted to give you more options ;-)
Here it is in sql fiddle where I also show what would happen if null was given for overlay. -> http://sqlfiddle.com/#!2/ebc82/3
SELECT
d.name
,MAX(CASE WHEN d.base_color_id = c.id THEN c.clr_title ELSE NULL END) base_color
,MAX(CASE WHEN d.overlay_color_id = c.id THEN c.clr_title ELSE NULL END) overlay_color
FROM
dress d
INNER JOIN colors c ON
c.Id IN (d.base_color_id, d.overlay_color_id)
WHERE
d.id = 669
GROUP BY
d.name
Since you're restricting to a single record, it's probably just fine. But you could always join against the color table twice, like:
select
d.*
,base_color.clr_title base_color
,overlay_color.clr_title overlay_color
from
indira.dress d
left join
colors base_color on d.base_color_id = base_color.id
left join
colors overlay_color on d.overlay_color_id = overlay_color.id
where
d.id = 669

"Optional" WHERE clause on (My)SQL query

I'm having a bit of a problem with a SELECT query in MySQL and I'd be thankful for some pointers. Please feel free to point me towards an existing answer (if there is one and I missed it).
The query is currently as follows:
SELECT e.*, ie.aaa, ue.bbb, ue.ccc
FROM ie
LEFT JOIN e ON ie.e_id = e.e_id
LEFT JOIN ue ON ie.e_id = ue.e_id
WHERE ie.other_id = ? AND ue.unrelated_id = ?
ORDER BY ...
There are three tables: ie, e and ue.
Tables ie and ue are relationships of e, and therefore contain foreign keys to it (e_id). ? represents an input parameter.
The problem is the ue.unrelated_id = ? part. What I'm really trying to do here is:
To return ue.ccc if and only if there is a ue relationship for unrelated_id = ?. If it doesn't exist, I want this field to be null.
Even if the ue relationship for unrelated_id = ? doesn't exist, this query should always return the remaining fields (ie is guaranteed to exist for other_id = ?).
Unfortunately, if I remove this where clause, I get ue.ccc for a "random" unrelated_id. But if I keep it, the query won't return any results at all if ue doesn't exist for this unrelated_id! I also tried adding OR ue.unrelated_id IS NOT NULL, but this makes the query return no results if the ue table is empty.
Any ideas? Please drop a comment if you need further clarification. I should answer quickly in the next few hours.
You could do one of two things:
SELECT e.*, ie.aaa, ue.bbb, ue.ccc
FROM ie
LEFT JOIN e ON ie.e_id = e.e_id
LEFT JOIN ue ON ie.e_id = ue.e_id AND ue.unrelated_id = ?
WHERE ie.other_id = ?
ORDER BY ...
Or
SELECT e.*, ie.aaa, ue.bbb, ue.ccc
FROM ie
LEFT JOIN e ON ie.e_id = e.e_id
LEFT JOIN ue ON ie.e_id = ue.e_id
WHERE ie.other_id = ? AND (ue.unrelated_id IS NULL OR ue.unrelated_id = ?)
ORDER BY ...
I would go with the first query, however.
EDIT: Please note that the 2nd query is only approperiate if ue.unrelated_id is not a nullable column.

MySQL - LEFT JOIN failing when WHERE clause added

I have 4 tables as follows; SCHEDULES, SCHEDULE_OVERRIDE, SCHEDULE_LOCATION_OVERRIDES and LOCATION
I need to return ALL rows from all tables so running this query works fine, adding NULL values for any values that are not present:
SELECT.....
FROM (schedule s LEFT JOIN schedule_override so ON so.schedule_id = s.id)
LEFT JOIN schedule_location_override slo ON slo.schedule_override_id = so.id
LEFT JOIN location l ON slo.location_id = l.id
ORDER BY s.id, so.id, slo.id, l.id
I then need to restict results on the schedule_override end_date field. My problem is, as soon as I do this, no results for the SCHEDULE table are returned at all. I need all schedules to be returned, even if the overrides end_date criteria is not met.
Heres what I am using:
SELECT.....
FROM (schedule s LEFT JOIN schedule_override so ON so.schedule_id = s.id)
LEFT JOIN schedule_location_override slo ON slo.schedule_override_id = so.id
LEFT JOIN location l ON slo.location_id = l.id
WHERE so.end_date > '2011-01-30' OR so.end_date IS NULL
ORDER BY s.id, so.id, slo.id, l.id
Appreciate any thoughts/comments.
Best regards, Ben.
Have you tried putting it in the ON clause?
SELECT.....
FROM (schedule s LEFT JOIN schedule_override so ON so.schedule_id = s.id AND (so.end_date > '2011-01-30' OR so.end_date IS NULL))
LEFT JOIN schedule_location_override slo ON slo.schedule_override_id = so.id
LEFT JOIN location l ON slo.location_id = l.id
ORDER BY s.id, so.id, slo.id, l.id
That's a quite common mistake with outer Joins.
You need to put everything that limits the Join into the "ON" part for that table, otherwise you are effectively transforming the join to an inner one.
So move the WHERE clause in this case into the ON-part of the schedule_override and you should be fine.
Yes, when you left join, it could be that a row is not found, and the field is NULL in the result. When you add a condition in the WHERE clause, the value must match that condition, which it won't if it's NULL.
That shouldn't be a problem, because you explicitly check for NULL, so I don't really know why this condition fails, unless it does return a date, but that date is befor 2011-01-30.
Anyway, you could try to move the condition to the join. It will eliminate the need to check for NULL, although it shouldn't make a difference really.
SELECT.....
FROM
schedule s
LEFT JOIN schedule_override so
ON so.schedule_id = s.id
AND so.end_date > '2011-01-30'
...