The database is as follows:
Classes Challenges Class Challenges
id id id
title class_id
challenge_id
In order to get all the challenges of a specific class I use the following
SELECT
DISTINCT class_challenges.challenge_id,
challenges.title
FROM class_challenges
LEFT JOIN challenges
ON class_challenges.challenge_id = challenges.id
WHERE class_challenges.class_id = :class_id
ORDER BY challenge_id
How can I do the same for all the challenges that do not belong to a specific class?
So far I use:
SELECT
DISTINCT challenges.id,
challenges.title
FROM
challenges,
class_challenges
WHERE challenges.id NOT IN(
SELECT
DISTINCT class_challenges.challenge_id
FROM class_challenges
LEFT JOIN challenges
ON class_challenges.challenge_id = challenges.id
WHERE class_challenges.class_id = :class_id
ORDER BY challenge_id
);
which I think can be written better. (maybe using a double join?)
So, how can this be optimized (if it can?)
Try this query
SELECT
t.id,
t.title,
t.CCID
FROM
(
SELECT
challenges.id,
challenges.title,
class_challenges.id as CCID
FROM
challenges
LEFT JOIN class_challenges
ON class_challenges.challenge_id = challenges.id
) as t
WHERE t.CCID IS NULL
Without seeing your expected results it's a bit vague for me to answer, but anyway here is a code to try out. Call me visual ;) Please comment after you have tried out the query.
The code list down 'challenges for each class'. You may use a variable to filter out the data for a specific class id.
SQLFIDDLE DEMO
SELECT DISTINCT a.id,
group_concat(b.challenge_id) as challengeIs,
group_concat(c.title) as Titles
FROM Classes a
LEFT JOIN
class_challenges b
ON a.id = b.class_id
LEFT JOIN challenges c
ON b.challenge_id = c.id
group by a.id
ORDER BY a.id;
Results:
ID CHALLENGEIS TITLES
100 11,15 a,c
200 15 b
300 11,15 a,c
400 (null) (null)
500 15 b
Challenges that do not belong to a specific class
Just noticed that I have missed out to add this portion of the query.
Query:
-- challenge that doesn't belong to a class
SELECT DISTINCT c.id,
c.title, group_concat(a.id) as class
FROM challenges c
LEFT JOIN
class_challenges b
ON b.challenge_id = c.id
LEFT JOIN Classes a
ON a.id = b.class_id
GROUP BY c.id
HAVING class is null
ORDER BY c.id;
Results:
ID TITLE CLASS
18 c (null)
How about this, using an anti-join:
SELECT challenges.id, challenges.title
FROM challenges
LEFT JOIN class_challenges ON class_challenges.challenge_id = challenges.id
AND class_challenges.class_id = :class_id
WHERE class_challenges.id IS NULL
Related
I am guessing there is a mistake in the nested query however I cannot seem to find it myself.
Here is the query:
Select student.sid, name
from student join exam on exam.sid = student.sid
where student.sid in (select *
from course join exam on cid=courseid
group by exam.sid
having sum(credits) >= 20)
Thank you in advance!
You can use the group by as follows:
select s.sid, s.name
from student s
Join exam e on s.sid = e.sid
Join course c on c.cid = e.courseid
group by s.sid, s.name
having sum(c.credits) >= 20
Yes, there is a mistake. The nested query is returning multiple columns but the comparison is to only a single column.
This will obviously generate an error. Presumably, you don't want the sum() but without sample data, desired results, and an explanation of what the query is supposed to be doing, it is hard to make a concrete suggestion.
The query may have other errors, but presumably, you intend something like this:
select s.sid, s.name
from student s
where s.sid in (select e.sid,
from course c join
exam e
on c.cid = e.courseid
group by e.sid
having sum(c.credits) >= 20
)
We are maintaining a history of Content. We want to get the updated entry of each content, with create Time and update Time should be of the first entry of the Content. The query contains multiple selects and where clauses with so many left joins. The dataset is very huge, thereby query is taking more than 60 seconds to execute. Kindly help in improving the same. Query:
select * from (select * from (
SELECT c.*, initCMS.initcreatetime, initCMS.initupdatetime, user.name as partnerName, r.name as rightsName, r1.name as copyRightsName, a.name as agelimitName, ct.type as contenttypename, cat.name as categoryname, lang.name as languagename FROM ContentCMS c
left join ContentCategoryType ct on ct.id = c.contentType
left join User user on c.contentPartnerId = user.id
left join Category cat on cat.id = c.categoryId
left join Language lang on lang.id = c.languageCode
left join CopyRights r on c.rights = r.id
left join CopyRights r1 on c.copyrights = r1.id
left join Age a on c.ageLimit = a.id
left outer join (
SELECT contentId, createTime as initcreatetime, updateTime as initupdatetime from ContentCMS cms where cms.deleted='0'
) as initCMS on initCMS.contentId = c.contentId WHERE c.deleted='0' order by c.id DESC
) as temp group by contentId) as c where c.editedBy='0'
Any help would be highly appreciated. Thank you.
Just a partial eval and suggestion because your query seems non properly formed
This left join seems unuseful
FROM ContentCMS c
......
left join (
SELECT contentId
, createTime as initcreatetime
, updateTime as initupdatetime
from ContentCMS cms
where cms.deleted='0'
) as initCMS on initCMS.contentId = c.contentId
same table
the order by (without limit) in a subquery in join is unuseful because join ordered values or unordered value produce the same result
the group by contentId is strange beacuse there aren't aggregation function and the sue of group by without aggregation function is deprecated is sql
and in the most recente version for mysql is not allowed (by deafult) if you need distinct value or just a rows for each contentId you should use distinct or retrive the value in a not casual manner (the use of group by without aggregation function retrive casual value for not aggregated column .
for a partial eval your query should be refactored as
SELECT c.*
, c.initcreatetime
, c.initupdatetime
, user.name as partnerName
, r.name as rightsName
, r1.name as copyRightsName
, a.name as agelimitName
, ct.type as contenttypename
, cat.name as categoryname
, lang.name as languagename
FROM ContentCMS c
left join ContentCategoryType ct on ct.id = c.contentType
left join User user on c.contentPartnerId = user.id
left join Category cat on cat.id = c.categoryId
left join Language lang on lang.id = c.languageCode
left join CopyRights r on c.rights = r.id
left join CopyRights r1 on c.copyrights = r1.id
WHERE c.deleted='0'
) as temp
for the rest you should expiclitally select the column you effectively need add proper aggregation function for the others
Also the nested subquery just for improperly reduce the rows don't help performance ... you should also re-eval you data modelling and design.
I have 3 tables concerning complains. The first table consists of the complain information itself, 2nd one is the complain_review with status_id, and the 3rd is the status_id table consisting status information. I'm trying to select the complain_desc from complain and latest status_id from complain_review (sort by date desc) and couple that with complain_status information.
This is what I've tried (no success so far):
SELECT c1.complain_desc, c2.status_id, c2.name as statusDesc from complain c1
left join
(SELECT c3.status_id, c4.name, c3.complain_id FROM complain_review c3
inner join complain_status c4 on c4.id=c3.status_id ORDER by c3.date DESC) c2
on c2.complain_id=c1.id
this is the updated example provided by #maheshiv
.. I've searched through the site but I don't exactly know what keyword to search concerning this matter :(
Edit: I've build a schema at http://sqlfiddle.com/#!9/d86a7a/2 so perhaps somebody could give take a better look at the tables
Edit: Perhaps this would be the closest as I could get .. and working!
SELECT c.complain_desc, cr1.status_id, cs.name
FROM complain c
INNER JOIN complain_review cr1 ON c.id=cr1.complain_id
INNER JOIN complain_status cs ON cs.id=cr1.status_id
WHERE cr1.date = (SELECT MAX(cr2.date) FROM complain_review cr2
WHERE cr1.complain_id=cr2.complain_id)
I'm trying to select the complain_desc from complain and latest status_id from complain_review (sort by date desc) and couple that with complain_status information.
This is a very common question on Stack Overflow. You can follow the greatest-n-per-group to find many solutions.
Here's a solution using your example:
SELECT c.complain_desc, latest_cr.status_id, cs.name AS status_desc
FROM complain AS c
INNER JOIN (
SELECT complain_id, status_id
FROM (
SELECT cr.complain_id, cr.status_id,
IF(#cgroup=cr.complain_id, #rownum:=#rownum+1, 1) AS rownum,
(#cgroup:=cr.complain_id)
FROM (SELECT #cgroup:=0, #rownum:=1) AS _init
CROSS JOIN complain_review AS cr
ORDER BY cr.complain_id DESC, cr.date DESC
) AS n
WHERE n.rownum = 1
) AS latest_cr
ON c.id=latest_cr.complain_id
INNER JOIN complain_status AS cs
ON cs.id = latest_cr.status_id;
Here's a different solution using no subqueries:
SELECT c.complain_desc, cr1.status_id, cs.name AS status_desc
FROM complain AS c
INNER JOIN complain_review AS cr1
ON cr1.complain_id = c.id
LEFT OUTER JOIN complain_review AS cr2
ON cr2.complain_id = c.id AND (cr2.date > cr1.date OR cr2.date = cr1.date AND cr2.id > cr1.id)
INNER JOIN complain_status AS cs
ON cs.id = cr1.status_id
WHERE cr2.id IS NULL;
I think you may need this query,
I believe max status_id is the latest status for complaint. As per http://sqlfiddle.com/#!9/d86a7a/15
select c1.complain_desc, c2.status_id, c3.name from complain c1 inner join (select complain_id, max(status_id) from complain_review group by complain_id) c2 on c1.id=c2.complain_id inner join complain_status c3 on c3.id=c2.status_id;
I have 3 tables: activites, taks and requirements. I want to return all of the duration of all the tasks for a specific requirement. This is my query:
SELECT r.id as req_id,
r.project_id,
r.name as req_name,
r.cost,r.estimated,
p.name as project_name,
v.name AS `status` ,
t.taskid,
(SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(duration)))
FROM activities a
WHERE a.taskid = t.taskid) AS worked
FROM requirements r
INNER JOIN projects p
ON p.projectid = r.project_id
INNER JOIN `values` v
ON v.id = r.r_status_id
LEFT JOIN tasks t
on t.id_requirement = r.id
WHERE 1 = 1
ORDER BY req_id desc
And this is the result :
As you can see there are 2 same req_id (48) . I want to appear one time and get the sum of the last two rows in worked. How can I manage that ?
this is the activities structure :
this is tasks structure :
and this is the requirement structure :
Include your activities table in the JOIN, GROUP by all requirement columns you need and add a sum. Since you are aggregating tasks, you cannot have taskid in the SELECT clause.
SELECT r.id as req_id,
r.project_id,
r.name as req_name,
r.cost,r.estimated,
p.name as project_name,
v.name AS `status` ,
SEC_TO_TIME(SUM(TIME_TO_SEC(a.duration)))
FROM requirements r
INNER JOIN projects p ON p.projectid = r.project_id
INNER JOIN `values` v ON v.id = r.r_status_id
LEFT JOIN tasks t ON t.id_requirement = r.id
LEFT JOIN activities a ON a.taskid=t.taskid
WHERE 1 = 1
GROUP BY r.id, r.project_id, r.name,r.cost,r.estimated,p.name, v.name
ORDER BY req_id desc
The joins in your query appear to be creating extra rows. I'm sure there is a way to fix the logic directly, possibly by pre-aggregating some results in the from clause.
Your duplicates appear to be complete duplicates (every column is exactly the same). The easy way to fix the problem is to use select distinct. So, just start your query with:
SELECT DISTINCT r.id as req_id, r.project_id, r.name as req_name,
. . .
I suspect that one of your underlying tables has duplicated rows that you are not expecting, but that is another issue.
I have 3 tables. practices, adjustments and claims.
I need to select all practices with additional column adjustments.amount + count(claims.id)
Here is my query.
SELECT
practices.*,
(
SUM(adjustments.amount) + count(claims.id)
) as accurred_fees
INNER JOIN adjustments ON adjustments.practice_id = practices.id
INNER JOIN claims ON claims.practice_id = practices.id
GROUP BY practices.id
In my database I have 1 practice, 20 claims and 1 adjustment with amount equal to 2. SUM(eligible_fee_adjustments.amount) in query always returns 40, I guess it's 2 * count(claims.id), I don't know why it behaves like that. Could you please help me to find solution. Thanks in advance
You have a problem of joining along two different dimensions, so the rows are multiplying. You can solve this by pre-aggregating the data. The following will fix this particular problem:
SELECT p.*,
(SUM(a.amount) + c.cnt) as accurred_fees
FROM practices p INNER JOIN
adjustments a
ON a.practice_id = p.id INNER JOIN
(SELECT practice_id, count(*) as cnt
FROM claims c
GROUP BY practice_id
) c
ON c.practice_id = p.id
GROUP BY p.id;
You should probably preaggregate the adjustments as well:
SELECT p.*,
(a.amount + c.cnt) as accurred_fees
FROM practices p INNER JOIN
(SELECT practice_id, SUM(amount) as amount
FROM adjustments a
GROUP BY practice_id
) a
ON a.practice_id = p.id INNER JOIN
(SELECT practice_id, count(*) as cnt
FROM claims c
GROUP BY practice_id
) c
ON c.practice_id = p.id
GROUP BY p.id;
And you might want to consider LEFT OUTER JOIN rather than INNER JOIN so you get all practices, even those that might be missing adjustments or claims.