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>
Related
I'm really struggling with this query and I hope somebody can help.
I am querying across multiple tables to get the dataset that I require. The following query is an anonymised version:
SELECT main_table.id,
sub_table_1.field_1,
main_table.field_1,
main_table.field_2,
main_table.field_3,
main_table.field_4,
main_table.field_5,
main_table.field_6,
main_table.field_7,
sub_table_2.field_1,
sub_table_2.field_2,
sub_table_2.field_3,
sub_table_3.field_1,
sub_table_4.field_1,
sub_table_4.field_2
FROM main_table
INNER JOIN sub_table_4 ON sub_table_4.id = main_table.id
INNER JOIN sub_table_2 ON sub_table_2.id = main_table.id
INNER JOIN sub_table_3 ON sub_table_3.id = main_table.id
INNER JOIN sub_table_1 ON sub_table_1.id = main_table.id
WHERE sub_table_4.field_1 = '' AND sub_table_4.field_2 = '0' AND sub_table_2.field_1 != ''
The query works, the problem I have is sub_table_1 has a revision number (int 11). Currently I get duplicate records with different revision numbers and different versions of sub_table_1.field_1 which is to be expected, but I want to limit the result set to only include results limited by the latest revision number, giving me only the latest sub_table_1_field_1 and I really can not figure it out!
Can anybody lend me a hand?
Many Thanks.
It's always important to remember that a JOIN can be on a subquery as well as a table. You could build a subquery that returns the results you want to see then, once you've got the data you want, join it in the parent query.
It's hard to 'tailor' an answer that's specific to you problem, as it's too obfuscated (as you admit) to know what the data and tables really look like, but as an example:
Say table1 has four fields: id, revision_no, name and stuff. You want to return a distinct list of name values, with their latest version of stuff (which, we'll pretend varies by revision). You could do this in isolation as:
select t.* from table1 t
inner join
(SELECT name, max(revision_no) maxr
FROM table1
GROUP BY name) mx
on mx.name = t.name
and mx.maxr = t.revision_no;
(Note: see fiddle at the end)
That would return each individual name with the latest revision of stuff.
Once you've got that nailed down, you could then swap out
INNER JOIN sub_table_1 ON sub_table_1.id = main_table.id
....with....
INNER JOIN (select t.* from table1 t
inner join
(SELECT name, max(revision_no) maxr
FROM table1
GROUP BY name) mx
on mx.name = t.name
and mx.maxr = t.revision_no) sub_table_1
ON sub_table_1.id = main_table.id
...which would allow a join with a recordset that is more tailored to that which you want to join (again, don't get hung up on the actual query I've used, it's just there to demonstrate the method).
There may well be more elegant ways to achieve this, but it's sometimes good to start with a simple solution that's easier to replicate, then simplify it once you've got the general understanding of the what and why nailed down.
Hope that helps - as I say, it's as specific as I could offer without having an idea of the real data you're using.
(for the sake of reference, here is a fiddle with a working version of the above example query)
In your case where you only need one column from the table, make this a subquery in your select clause instead of than a join. You get the latest revision by ordering by revision number descending and limiting the result to one row.
SELECT
main_table.id,
(
select sub_table_1.field_1
from sub_table_1
where sub_table_1.id = main_table.id
order by revision_number desc
limit 1
) as sub_table_1_field_1,
main_table.field_1,
...
FROM main_table
INNER JOIN sub_table_4 ON sub_table_4.id = main_table.id
INNER JOIN sub_table_2 ON sub_table_2.id = main_table.id
INNER JOIN sub_table_3 ON sub_table_3.id = main_table.id
WHERE sub_table_4.field_1 = ''
AND sub_table_4.field_2 = '0'
AND sub_table_2.field_1 != '';
There is a query that to count rows from table question_category:
SELECT
idSpecialization,
thematicspecialization.SpecializationName AS SpecializationName,
SubscrubeToUsersIdNote,
COUNT(question_categoryId) AS CNT,
COUNT(idSubscrubeToUsers) AS SUBS
FROM thematicspecialization
LEFT JOIN question_category ON question_category.question_categoryIdCategory = thematicspecialization.idSpecialization
LEFT JOIN question a ON a.idQuestion = question_category.question_categoryIdQuestion
LEFT JOIN subscrubetousers ON (subscrubetousers.SubscrubeToUsersIdNote = idSpecialization AND subscrubetousers.SubscrubeToUsersType = 4 AND SubscrubeToUsersStatus = 1)
WHERE a.country = 1
GROUP BY idSpecialization
Need display the category names of thematicspecialization with the condition WHERE a.country = 1 for joining tables question a ON a.idQuestion = question_category.question_categoryIdQuestion
In my case - the categories are not displayed.
This is too long for a ocmment.
This is your query, fixed up a bit to use table aliases so it is easier to read:
SELECT ts.idSpecialization, ts.SpecializationName, sts.SubscrubeToUsersIdNote,
COUNT(question_categoryId) AS CNT,
COUNT(idSubscrubeToUsers) AS SUBS
FROM thematicspecialization ts LEFT JOIn
question_category qc
ON qc.question_categoryIdCategory = ts.idSpecialization LEFT JOIN
question q
ON q.idQuestion = qc.question_categoryIdQuestion LEFT JOIN
subscrubetousers sts
ON (sts.SubscrubeToUsersIdNote = ts.idSpecialization AND
sts.SubscrubeToUsersType = 4 AND
sts.SubscrubeToUsersStatus = 1)
WHERE q.country = 1
GROUP BY idSpecialization;
Here are things that I notice:
The join between ts and qc is on IdCategory and idSpecialization. That seems like a strange combination of names.
The join between sts and ts is partly on SubscrubeToUsersIdNote and idSpecialization. That seems like a strange combination of names.
The where condition is turning some left joins into inner joins -- calling into question why you want left join anyway.
The two count columns are going to return very similar numbers, because they are counting non-NULL values on join keys`.
Your question is about "categories" and their "names". However, based on your query, it is entirely unclear what fields these refer to.
Based on this query, I have no idea what you are really trying to do. You should probably write another question, with sample data and desired output to express what you are trying to do. This question already has two answers. Clarifying it so it would be understood would (probably) invalidate the answers, which is impolite.
Not clear.
But I guess. You have a join with condition like
subscrubetousers.SubscrubeToUsersIdNote = idSpecialization AND subscrubetousers.SubscrubeToUsersType = 4 AND SubscrubeToUsersStatus = 1be this
May be this condition conflict thematicspecialization field
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
I have the following query:
SELECT PKID, QuestionText, Type
FROM Questions
WHERE PKID IN (
SELECT FirstQuestion
FROM Batch
WHERE BatchNumber IN (
SELECT BatchNumber
FROM User
WHERE RandomString = '$key'
)
)
I've heard that sub-queries are inefficient and that joins are preferred. I can't find anything explaining how to convert a 3+ tier sub-query to join notation, however, and can't get my head around it.
Can anyone explain how to do it?
SELECT DISTINCT a.*
FROM Questions a
INNER JOIN Batch b
ON a.PKID = b.FirstQuestion
INNER JOIN User c
ON b.BatchNumber = c.BatchNumber
WHERE c.RandomString = '$key'
The reason why DISTINCT was specified is because there might be rows that matches to multiple rows on the other tables causing duplicate record on the result. But since you are only interested on records on table Questions, a DISTINCT keyword will suffice.
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
Try :
SELECT q.PKID, q.QuestionText, q.Type
FROM Questions q
INNER JOIN Batch b ON q.PKID = b.FirstQuestion
INNER JOIN User u ON u.BatchNumber = q.BatchNumber
WHERE u.RandomString = '$key'
select
q.pkid,
q.questiontext,
q.type
from user u
join batch b
on u.batchnumber = b.batchnumber
join questions q
on b.firstquestion = q.pkid
where u.randomstring = '$key'
Since your WHERE clause filters on the USER table, start with that in the FROM clause. Next, apply your joins backwards.
In order to do this correctly, you need distinct in the subquery. Otherwise, you might multiply rows in the join version:
SELECT q.PKID, q.QuestionText, q.Type
FROM Questions q join
(select distinct FirstQuestion
from Batch b join user u
on b.batchnumber = u.batchnumber and
u.RandomString = '$key'
) fq
on q.pkid = fq.FirstQuestion
As to whether the in or join version is better . . . that depends. In some cases, particularly if the fields are indexed, the in version might be fine.
I have two tables that look like this:
Table: cases
id
name
status
case_no
Table: notes
id
case_id
note_date
notes
I'd like to be able to create a query that grabs the data from the cases table and only the most recent entry from the notes table for each row in the cases table. So far I'm having no luck at all.
Any pointers would be greatly appreciated
This will return only the cases with notes attached:
SELECT c.*,
x.*
FROM CASES c
JOIN NOTES x ON x.case_id = c.case_id
JOIN (SELECT n.case_id,
MAX(n.note_date) AS max_note_date
FROM NOTES n
GROUP BY n.case_id) y ON y.case_id = x.case_id
AND y.max_note_date = x.note_date
If you want all cases, regardless if they have a note attached:
SELECT c.*,
x.*
FROM CASES c
LEFT JOIN NOTES x ON x.case_id = c.case_id
JOIN (SELECT n.case_id,
MAX(n.note_date) AS max_note_date
FROM NOTES n
GROUP BY n.case_id) y ON y.case_id = x.case_id
AND y.max_note_date = x.note_date
SELECT *
FROM cases c
INNER JOIN notes n ON n.case_id = c.id
AND n.id = (SELECT MAX(id)
FROM notes
WHERE case_id = c.id)
Also it is a common practice to keep the pointer to the last note id directly in cases table and support it with trigger
I have been having same problem recently and this forum has helped me a lot but I found the OMG Ponies answer isn't complete. It works for those cases with notes but it doesn't for cases without notes which is my issue.
My answer is similar but instead joining the group query with notes, I join it with cases.
It would be:
SELECT c.*, x.*
FROM CASES c
LEFT JOIN (SELECT n.case_id, MAX(n.note_date) AS max_note_date
FROM NOTES n
GROUP BY n.case_id) y ON y.case_id = c.case_id
LEFT JOIN NOTES x ON x.case_id = c.case_id AND x.note_date=y.max_note_date
It's also valid to get just cases with notes removing one left keyword or both