MySQL SUM(column) + (SUBQUERY WITH SUM) - mysql

I want to make a query that adds up points for a team. The points are added up by doing SUM on a column + a SUM from another table with the same team-id. I try to write it like this:
SELECT
k.id,
s.fylke,
s.Kommune,
s.Skolenavn,
k.schoolid,
k.number,
k.letter,
SUM(e.amount) + (SELECT SUM(poeng) FROM oppdrag WHERE klasseid=k.id ) AS poeng
FROM skoler AS s, klasser AS k, etappe AS e
WHERE s.id=k.schoolid AND k.id=e.klasseid AND e.year='2016'
GROUP BY k.id
ORDER BY poeng
The problem is that, when I write it in this fashion, it gives correct amount of points if the team has an entry in table "oppdrag" otherways it just returns NULL as points (poeng).

If your subquery returns NULL, this will result in trying to sum a number with NULL, which gives NULL back.
To fix this, you can try using the IFNULL function, that will replace it with a 0 if the query doesn't return any values:
SELECT
k.id,
s.fylke,
s.Kommune,
s.Skolenavn,
k.schoolid,
k.number,
k.letter,
SUM(e.amount) + IFNULL((SELECT SUM(poeng) FROM oppdrag WHERE klasseid=k.id ), 0) AS poeng
FROM skoler AS s, klasser AS k, etappe AS e
WHERE s.id=k.schoolid AND k.id=e.klasseid AND e.year='2016'
GROUP BY k.id
ORDER BY poeng

I'd avoid that subquery from your select statement, it's going to kill your performance. Also, you should really be using the correct join syntax (the style you're using is ancient). Try something like this;
SELECT
k.id,
s.fylke,
s.Kommune,
s.Skolenavn,
k.schoolid,
k.number,
k.letter,
SUM(e.amount) + ISNULL(SUM(od.poeng),0) AS poeng
FROM skoler AS s
INNER JOIN klasser AS k
ON s.id = k.schoolid
INNER JOIN etappe AS e
ON k.id = e.klasseid
LEFT JOIN oppdrag od
ON od.klasseid = k.id
WHERE e.year='2016'
GROUP BY k.id
ORDER BY poeng

First, learn to use explicit JOIN syntax. Simple rule: Never use commas in the FROM clause. Always use explicit JOIN with ON.
Then, you can solve your problem by including the subquery in the FROM clause:
SELECT ske.*, ske.amount + COALESCE(od.amount, 0) as poenb
FROM (SELECT k.id, s.fylke, s.Kommune, s.Skolenavn, k.schoolid,
k.number, k.letter, SUM(e.amount) as amount
FROM skoler s JOIN
klasser k
ON s.id = k.schoolid JOIN
etappe e
ON k.id = e.klasseid AND e.year = '2016'
GROUP BY k.id
) ske LEFT JOIN
(SELECT od.klasseid, SUM(od.poeng) as amount
FROM oppdrag od
GROUP BY od.klasseid
) od
ON od.klasseid = k.id
ORDER BY poeng;

Use explicit JOIN syntax. Also, include all columns that aren't aggregated in GROUP BY clause. Just because MySQL doesn't yield an error it doesn't mean that it's advisable to do it the way you did it. Not including all columns that aren't aggregated in a group by means picking random values.
SELECT
k.id,
s.fylke,
s.Kommune,
s.Skolenavn,
k.schoolid,
k.number,
k.letter,
COALESCE(SUM(e.amount), 0) + COALESCE(SUM(o.poeng), 0) AS poeng
FROM skoles AS s
INNER JOIN klasser AS k ON s.id = k.schoolid
INNER JOIN etappe AS e ON k.id = e.klasseid
LEFT JOIN ( SELECT klasseid, SUM(poeng) AS poeng FROM oppdrag ) AS o ON o.klasseid = k.id
WHERE e.year = '2016'
GROUP BY k.id, s.fylke, s.Kommune, s.Skolenavn, k.schoolid, k.number, k.letter
ORDER BY poeng

Related

SQL Query Aggregate Function

This is my query:
select a.id, a.title,count(x.id) as Orders
-- from x_vendordeliveriesareas vda
-- left join x_vendordeliveries vd
-- on vd.id = vda.vendordelivery_id
from x_orders x
left join x_areas a
on x.delivery_area_id = a.id
-- on vda.area_id = a.id
-- left join x_orders x
left join x_vendors v
on v.id = x.vendor_id
where v.title like 'golden dragon%' and (date_format(x.date,'%Y-%m-%d') BETWEEN '2015-01-01' AND '2015-06-30')
and x.status_id=11
and x.country_id =11
and v.city_id=3
group by 1;
This works perfectly fine, but I want to return those areas to which have 0 orders.
I have tried IFNULL and coalesce functions
Your driving table should be x_areas:
select
a.id,
a.title,
coalesce(count(x.id), 0) as Orders
from x_areas a
left join x_orders x
on x.delivery_area_id = a.id
and x.status_id = 11
and x.country_id = 11
and (date_format(x.date,'%Y-%m-%d') BETWEEN '2015-01-01' AND '2015-06-30')
left join x_vendors v
on v.id = x.vendor_id
where
v.title like 'golden dragon%'
and v.city_id = 3
group by 1;
Note that I moved some of your WHERE conditions in the ON clause to prevent the LEFT JOIN from turning into an INNER JOIN, thus giving the same result, with the added x_areas with 0 Orders.

Left Join to another select statement

I have an INNER JOIN query that runs perfectly fine by itself, which I want to LEFT JOIN to a another query/SELECT statement which contains WHERE clause.
I'm not able to join both the queries. It should link wt.tkinit = t.tkinit
Can you please suggest what I'm missing.
SELECT c.clnum, m.mmatter, ot.tkinit AS 'otkinit', wt.tkinit AS 'wtikint', t.tkrt01,
SUM(mt.mthrwkdb) AS 'whrs2010',
FROM client c, matter m, timekeep ot, timekeep wt, mattimhs mt, periodt p, timerate t
WHERE c.clnum = m.mclient
AND m.mmatter = mt.mtmatter
GROUP BY c.clnum, m.mmatter, ot.tkinit, wt.tkinit
SELECT t.tkinit, t.tkrt01
FROM timerate t
INNER JOIN (
SELECT tkinit, max(tkeffdate) as max_effdate
FROM timerate WHERE DATEPART(year, tkeffdate) = '2012'
GROUP BY tkinit) mt ON mt.tkinit = t.tkinit AND mt.max_effdate = t.tkeffdate
Did you try the following:
SELECT fields from (SELECT1) s1
LEFT JOIN
(SELECT2) s2
on s1.wt.tkinit = s2.t.tkinit.
Don't forget to get normal name for wt.tkinit and t.tkinit in their selects like wt_tkinit and t_tkinit because you can't do double aliases.
SELECT temp2.tkinit, temp2.tkrt01, temp.clnum, temp.mmatter, temp.otkinit, temp.tkrt01, temp.whrs2010
FROM
( SELECT c.clnum, m.mmatter, ot.tkinit AS 'otkinit', wt.tkinit AS 'wtikint', t.tkrt01,
SUM(mt.mthrwkdb) AS 'whrs2010',
FROM client c, matter m, timekeep ot, timekeep wt, mattimhs mt, periodt p, timerate t
WHERE c.clnum = m.mclient
AND m.mmatter = mt.mtmatter
GROUP BY c.clnum, m.mmatter, ot.tkinit, wt.tkinit ) temp
LEFT JOIN
SELECT t.tkinit, t.tkrt01
FROM timerate t
INNER JOIN (
SELECT tkinit, max(tkeffdate) as max_effdate
FROM timerate WHERE DATEPART(year, tkeffdate) = '2012'
GROUP BY tkinit) mt ON mt.tkinit = t.tkinit AND mt.max_effdate = t.tkeffdate ) temp2
ON temp.wtikint = temp2.tkinit
Be careful of your field names, but this may be a start

Trying to add one last SUM() column to my query in SQL Server 2008

I have the first query which is producing correct results. What I need is I need to add the sum of values as a last column grouped by surveyid. I can't insert Sum(c.value) into the first query because it is an aggregate function. I have the correct query as my second query below. I know there's pivot functionality but not sure if it can be used here. I do realize that there will be repetition but that's okay.
'first query
SELECT
A.PATIENTID, B.STUDENTNUMBER, c.surveyid,
convert(varchar, A.CreatedDate, 107),
C.QuestionID, C.Value, D.Question
FROM
dbo.Survey A, dbo.Patient B, [dbo].[SurveyQuestionAnswer] C, [dbo].[LookupQuestions] D
WHERE
A.PATIENTID = B.ID
and c.SurveyID = A.ID
and c.QuestionID = d.ID
and c.questionid <> 10
ORDER BY
A.PATIENTID
'second query
select
c.surveyid,SUM(c.value) as scores
from
dbo.SurveyQuestionAnswer c
group by
c.SurveyID
order by
SurveyID '---not important
You can use SUM if you add the OVER clause. In this case:
SELECT
A.PATIENTID, B.STUDENTNUMBER, c.surveyid,
convert(varchar, A.CreatedDate, 107),
C.QuestionID, C.Value, D.Question,
SUM(c.Value) OVER(PARTITION BY c.surveyid) scores
FROM
dbo.Survey A
INNER JOIN dbo.Patient B
ON A.PATIENTID = B.ID
INNER JOIN [dbo].[SurveyQuestionAnswer] C
ON c.SurveyID = A.ID
INNER JOIN [dbo].[LookupQuestions] D
ON c.QuestionID = d.ID
WHERE
c.questionid <> 10
ORDER BY
A.PATIENTID
You could use something like this:
SELECT
s.PATIENTID, p.STUDENTNUMBER, sqa.surveyid,
CONVERT(varchar, s.CreatedDate, 107),
sqa.QuestionID, sqa.Value, lq.Question,
Scores = (SELECT SUM(Value) FROM dbo.SurveyQuestionAnswer s2 WHERE s2.SurveyID = s.ID)
FROM
dbo.Survey s
INNER JOIN
dbo.Patient p ON s.PatientID = p.ID
INNER JOIN
[dbo].[SurveyQuestionAnswer] sqa ON sqa.SurveyID = s.ID
INNER JOIN
[dbo].[LookupQuestions] lq ON sqa.QuestionID = lq.ID
WHERE
sqa.questionid <> 10
ORDER BY
s.PATIENTID
By having a subquery with the SUM(...) you should be able to get that sum as a single value and you don't need to use any grouping function

how to simplify my sql query

I have this query, but it takes about 15 seconds to finish.. how can i simplyfy it to get same result in less time? my problem is that i need all of this data at ones.
SELECT * FROM (
SELECT c.client_id, c.client_name, c.client_bpm,
c.client_su_name, c.client_maxbpm, s.bpm,
s.timestamp, m.mesure_id, ms.currentT
FROM tbl_clients c, tbl_meting m, tbl_sensor_meting s,
tbl_magsens_meting ms
WHERE c.client_id = m.client_id
AND (m.mesure_id = s.id_mesure
OR m.mesure_id = ms.id_mesure)
AND m.live =1
ORDER BY s.timestamp DESC
) AS mesure
GROUP BY mesure.client_id
I think the problem may be the OR condition from your WHERE clause? You seem to be trying to join to one table or another, which you can't do. So I've replaced it with a LEFT JOIN, so in the event no related records exist nothing will be returned.
I also took out your GROUP BY, as I don't think it was required.
SELECT c.client_id, c.client_name, c.client_bpm,
c.client_su_name, c.client_maxbpm, s.bpm,
s.timestamp, m.mesure_id, ms.currentT
FROM tbl_clients c
JOIN tbl_meting m ON m.client_id = c.client_id
LEFT JOIN tbl_sensor_meting s ON s.id_mesure = m.mesure_id
LEFT JOIN tbl_magsens_meting ms ON ms.id_mesure = m.mesure_id
WHERE m.live = 1
ORDER BY s.timestamp DESC

MySQL: "Ignore" if a table row is missing during JOIN

I'm doing a LEFT JOIN on three tables, where the table "time" doesn't necessarily contain any matching rows. But if no matching rows is found in that table, the linked data disappears.
SELECT
w.date AS worker_date,
w.name AS worker_name,
w.address AS worker_address,
w.zip AS worker_zip,
w.place AS worker_place,
w.phone AS worker_phone,
w.email AS worker_email,
w.company AS worker_company,
w.accessibility AS worker_accessibility,
c.date AS client_date,
c.name AS client_name,
c.address AS client_address,
c.zip AS client_zip,
c.place AS client_place,
c.phone AS client_phone,
c.email AS client_email,
c.web AS client_web,
c.contact AS client_contact,
j.date AS job_date,
j.client_id,
j.worker_id,
j.name AS job_name,
j.description AS job_description,
j.type AS job_type,
j.status AS job_status,
j.proof AS job_proof,
j.deadline AS job_deadline,
j.price AS job_price,
j.final_client AS job_final_client,
SUM(t.hours) AS time_hours
FROM
jobs AS j
LEFT JOIN (
workers AS w,
clients AS c,
time AS t
) ON (
w.id = j.worker_id AND
c.id = j.client_id AND
j.id = t.job_id
) GROUP BY
j.id;
How can I make this work?
Thank you in advance.
add
WHERE t.job_id IS NOT NULL before GROUP BY
Try Replace
SUM(t.hours) AS time_hours
to
(SELECT IFNULL(SUM(t.hours),0) FROM time WHERE time.job_id=j.job_id) AS time_hours
And remove the time from the join
I think your basic query is correct (with the join under braces)
Just replace
SUM(t.hours) AS time_hours
with
SUM(if(t.hours is NULL,0,t.hours)) AS time_hours
I am not sure if this is the problem here, but the behavior of commas vs JOINs changed after a certain MySQL version. Try this
...
FROM jobs AS j LEFT JOIN workers AS w ON w.id = j.worker_id
LEFT JOIN clients AS c c.id = j.client_id
LEFT JOIN `time` AS t ON j.id = t.job_id
...
Also modify the SUM with IFNULL as #ajreal suggests.