Need help solving this SQL query to understand - mysql

write an sql to generate the report for employee dataset with give condition if average age >35 then states value is ok else notok dataset
id name age dept salary
1 tt 51 it 4000
2 kk 56 it 6000
3 mm 45 sales 7000
4 kk 25 sales 9000
5 op 24 hr 4000
6 op 24 hr 8000
output
dept avgage states
it 53.5 ok
sales 35 ok
hr 24 notok

Use this query.
SELECT a.dept,
a.avgage,
CASE
WHEN a.avgage >= 35 THEN 'ok'
ELSE 'notok'
END states
FROM (SELECT dept,
Avg (age) avgage
FROM employee
GROUP BY dept) a
ORDER BY avgage DESC;
Note: Please show some effort to understand and write a query on your own.

Related

Calculating moving average for different values in a column MySQL

I have a dataset like this:
team date score
A 2011-05-01 50
A 2011-05-02 54
A 2011-05-03 51
A 2011-05-04 49
A 2011-05-05 59
B 2011-05-03 30
B 2011-05-04 35
B 2011-05-05 39
B 2011-05-06 47
B 2011-05-07 50
I want to add another column called MA3 where I can calculate the moving average of scores for the last 3 days. The point that made it tricky is to calculate the MA for each team. The end result should be like this:
team date score MA3
A 2011-05-01 50 null
A 2011-05-02 54 null
A 2011-05-03 51 null
A 2011-05-04 49 51.66
A 2011-05-05 59 51.33
B 2011-05-03 30 null
B 2011-05-04 35 null
B 2011-05-05 39 null
B 2011-05-06 47 34.66
B 2011-05-07 50 40.33
If that would be a single team, I would go on and do:
SELECT team,
year,
AVG(score) OVER (ORDER BY date ASC ROWS 3 PRECEDING) AS MA3
FROM table
You're missing the PARTITION BY clause:
SELECT team,
date,
AVG(score) OVER (
PARTITION BY team
ORDER BY date ASC ROWS 3 PRECEDING
) AS MA3
FROM table
Note that there will always be an average calculation, regardless of the window size. If you want the average to be null if your window size is smaller than 3, you could do it like this:
SELECT team,
date,
CASE
WHEN count(*) OVER w <= 3 THEN null
ELSE AVG(score) OVER w
END AS MA3
FROM table
WINDOW w AS (PARTITION BY team ORDER BY date ASC ROWS 3 PRECEDING)
dbfiddle
Side note
Your next question might be about logical windowing, because often, you don't actually want to calculate the average over 3 rows, but over some interval,
like e.g. 3 days. Luckily, MySQL implements this. You could then write:
WINDOW w AS (PARTITION BY team ORDER BY date ASC RANGE INTERVAL 3 DAY PRECEDING)

mysql group by day and count then filter only the highest value for each day

I'm stuck on this query. I need to do a group by date, card_id and only show the highest hits. I have this data:
date card_name card_id hits
29/02/2016 Paul Stanley 1345 12
29/02/2016 Phil Anselmo 1347 16
25/02/2016 Dave Mustaine 1349 10
25/02/2016 Ozzy 1351 17
23/02/2016 Jhonny Cash 1353 13
23/02/2016 Elvis 1355 15
20/02/2016 James Hethfield 1357 9
20/02/2016 Max Cavalera 1359 12
My query at the moment
SELECT DATE(card.create_date) `day`, `name`,card_model_id, count(1) hits
FROM card
Join card_model ON card.card_model_id = card_model.id
WHERE DATE(card.create_date) >= DATE(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AND card_model.preview = 0
GROUP BY `day`, card_model_id
;
I want to group by date, card_id and filter the higher hits result showing only one row per date. As if I run a max(hits) with group by but I won't work
Like:
date card_name card_id hits
29/02/2016 Phil Anselmo 1347 16
25/02/2016 Ozzy 1351 17
23/02/2016 Elvis 1355 15
20/02/2016 Max Cavalera 1359 12
Any light on that will be appreciated. Thanks for reading.
Here is one way to do this. Based on your sample data (not the query):
select s.*
from sample s
where s.hits = (select max(s2.hits)
from sample s2
where date(s2.date) = date(s.date)
);
Your attempted query seems to have no relationship to the sample data, so it is unclear how to incorporate those tables (the attempted query has different columns and two tables).

Mysql best students in every class in a school

In MySql I need to select top student in every class in a school in termid=10 to get discount for next term enrollment .
Please notice that total is not in table(I put in below for clearing problem)
I have this workbook table for all students workbook:
id studentid classid exam1 exam2 total termid
1 2 11 20 40 60 10
2 1 22 40 20 60 10
3 4 11 40 20 60 10
4 5 33 10 60 70 10
5 7 22 10 40 50 10
6 8 11 10 30 40 10
7 9 33 20 45 65 10
8 11 11 null null null 10
9 12 54 null null null 02
10 13 58 null null null 02
1st challenge is : exam1 and exam2 are VARCHAR and total is not in table (as i explained).
2nd challenge is : as you can see in id=8 std #11 has not numbers
3rd challenge is : may be two students have top level so they must be in result.
I need result as :
id studentid classid exam1 exam2 total termid
1 2 11 20 40 60 10
3 4 11 40 20 60 10
4 5 33 10 60 70 10
2 1 22 40 20 60 10
i have this query but not work good as i mention.
SELECT DISTINCT id,studentid,classid,exam1,exam2,total,termid ,(CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2))) FROM workbook WHERE ClassId = '10';
You can get the total for the students by just adding the values (MySQL will convert the values to numbers). The following gets the max total for each class:
select w.classid, max(coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) as maxtotal
from workbook w
group by w.classid;
You can then join this back to the original data to get information about the best students:
select w.*, coalesce(w.exam1, 0) + coalesce(w.exam2, 0) as total
from workbook w join
(select w.classid, max(coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) as maxtotal
from workbook w
group by w.classid
) ww
on w.classid = ww.classid and (coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) = ww.maxtotal;
Another approach is to join the table with itself. You find out the max for each class and then join all students of this class which match the class max:
max for each class (included in the final statement already):
SELECT classid, MAX(CAST(exam1 AS UNSIGNED) + CAST(exam2 AS UNSIGNED)) as 'maxtotal'
FROM students
WHERE NOT ISNULL(exam1)
AND NOT ISNULL(exam2)
GROUP BY classid
The complete statement:
SELECT s2.*, s1.maxtotal
FROM (SELECT classid, MAX(CAST(exam1 AS UNSIGNED) + CAST(exam2 AS UNSIGNED)) as 'maxtotal'
FROM students
WHERE NOT ISNULL(exam1)
AND NOT ISNULL(exam2)
GROUP BY classid) s1
JOIN students s2 ON s1.classid = s2.classid
WHERE s1.maxtotal = (CAST(s2.exam1 AS UNSIGNED) + CAST(s2.exam2 AS UNSIGNED));
SQL Fiddle: http://sqlfiddle.com/#!2/9f117/1
Use a simple Group by Statement:
SELECT
studentid,
classid,
max(coalesce(exam1,0)) as max_exam_1,
max(coalesce(exam2,0)) as max_exam_2,
sum(coalesce(exam1,0) + coalesce(exam2,0)) as sum_exam_total,
termid
FROM
workbook
WHERE
termid=10
GROUP BY
1,2
ORDER BY
5
Try something like this:
SELECT id,studentid,classid,exam1,exam2,(CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2))) AS total,termid FROM `workbook` WHERE ((CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2)))) > 50
Thanks all my friends
I think combine between 2 answer in above is best :
SELECT s2.*, s1.maxtotal
FROM (SELECT ClassId, MAX(
coalesce(exam1,0)+
coalesce(exam2,0)
) as 'maxtotal'
FROM workbook
WHERE
(
termid = '11'
)
GROUP BY ClassId) s1
JOIN workbook s2 ON s1.ClassId = s2.ClassId
WHERE s1.maxtotal = (
coalesce(exam1,0)+
coalesce(exam2,0)
) AND (s1.maxtotal >'75');
last line is good for s1.maxtotal=0 (some times student scores have not be entered and all equals 0 so all will shown as best students) or some times we need minimum score (to enroll in next term).
So thanks all

SQL query to sum and group hours and wages by day

Sorry it seems like a simple query but I am stuck on it. I am trying to get daily hours and wages from Totals table grouped by employee ID. The table has TimeinSeconds, WageAmount, EmpID, location and other information.
I am using this:
Select EmpID, Loc, Sum(timeinseconds/3600.0) as Hours, Paydate, wageamt
from Totals
where loc = 'locID'
and paycode IN (paycodenames)
and date > (getdate()-21)
Group by EmpID, Loc, date, wageamt
But I am getting breakdown and not daily Totals by Emp. It works if I don't use wageamt in the query. So, how can I Sum on both hours and wages?
EmpID Loc Hours Paydate wageamt
112 7 0.733333 08/06/14 14.666667
112 7 2.533333 08/06/14 50.666667
112 7 4 08/06/14 80
112 7 1.25 08/07/14 25
112 7 4 08/07/14 80
200 81 3.983333 08/06/14 31.866667
450 703 3.733333 08/06/14 108.789333
I am using SQL 2008. Thanks for any suggestions!
Use a GROUP BY and SUM like this:
select
EmpID
,Loc
,sum(Hours) as Hours
,PayDate
,sum(WageAmt) as WageAmt
from DataTable
group by
EmpID
,Loc
,PayDate

SQL query by date

MySql 5.5.
I have a table that represents a work assignment:
empId jobNo workDate hours
4 441 10/1/2012 10
4 441 9/1/2012 22
4 441 8/1/2012 6
And one that represents salary:
empId effDate rate
4 10/1/2012 6.50
4 9/1/2012 5.85
4 6/1/2012 4.00
The salary applies to all work performed on or after the effective date. So the rate in jun, jul, and aug is 4.00; sep is 5.85, and oct is 6.50.
If I naively query for October's work:
SELECT Work.empId, Work.jobNo, Work.workDate, Work.hours, Salary.effDate, Salary.rate
FROM Work
JOIN Salary ON Work.empId = Salary.empId
WHERE Work.workDate <= '2012-10-01'
AND Salary.effDate <= Work.workDate
ORDER BY Work.jobNo ASC, Work.workDate DESC;
I do not get what I want. I get something like
4 441 10/1/2012 10 10/1/2012 6.50
4 441 10/1/2012 10 9/1/2012 5.85
4 441 10/1/2012 10 6/1/2012 4.00
4 441 9/1/2012 22 9/1/2012 5.85
4 441 9/1/2012 22 6/1/2012 4.00
4 441 8/1/2012 6 6/1/2012 4.00
When I want
4 441 10/1/2012 10 10/1/2012 6.50
4 441 9/1/2012 22 9/1/2012 5.85
4 441 8/1/2012 6 6/1/2012 4.00
I can't quite wrap my head around how to create the query I want.
The real situation has multiple employees, multiple jobs, obviously.
Thanks for your help.
Here is your actual issue: you want to be able to detect, for each record in Work, what is the corresponding effective rate, according to the work date x salary effective date. When you simply do Salary.effDate <= WORK.workDate you get ALL rates before the work date. But you only want the most recent one.
This is a slightly complicated variant of the greatest-n-per-group problem. There are many ways of doing this, here is one:
SELECT sel.*, Salary.Rate
FROM
(
SELECT Work.empId, Work.jobNo, Work.workDate,
Work.hours, Max(Salary.effDate) effDate
FROM WORK
JOIN Salary ON WORK.empId = Salary.empId
WHERE WORK.workDate <= '2012-10-01'
AND Salary.effDate <= WORK.workDate
GROUP BY WORK.empId, WORK.jobNo, WORK.workDate, WORK.hours
ORDER BY WORK.jobNo ASC, WORK.workDate DESC
) sel
INNER JOIN Salary ON sel.empId = Salary.empId
AND sel.EffDate = Salary.EffDate
First of all, the inner query detects the most recent salary effective date for each work record. Then, we join that with the Salary again to the rate.
See the working SQLFiddle.
You're using what's called a NATURAL JOIN. Try changing the word "JOIN" to "LEFT JOIN" which should group the results on the left, giving you the desired results.
Assuming the salary table has a primary or alternate key (unique index) consisting of the columns empId and effDate, I'd do something like this:
select w.empID as EMPLOYEE_ID ,
w.jobNo as JOB_NUMBER ,
w.workDate as DATE_WORKED ,
w.hours as HOURS_WORKED ,
rate.HourlyWage as HOURLY_WAGE ,
w.hours * rate.HourlyWage as WAGES_CHARGED ,
rate.effDateFrom as HOURLY_WAGE_EFFECTIVE_DATE
from work w
join ( select sfrom.EmpId as EmpID ,
sfrom.rate as HourlyWage ,
sfrom.EffDate as effDateFrom ,
( select min(Effdate)
from salary t
where t.empId = sfrom.EmpId
and t.effDate > sfrom.EffDate
) as effDateThru
from salary sfrom
) rate on rate.empID = w.empID
and rate.EffDateFrom <= w.workDate
and ( rate.effDateThru is null -- if rate has not end date, is the current period
or rate.effDateThru > w.workDate -- 'date-thru' represents the start date of the next period, so the this upper bound is EXCLUSIVE
)
we join the work table against a virtual rate table that gives us each employee's wage and the date range for which it is effective. The 'current' row for each employee will have the thru/expiry date set to null. And...since the thru/expiry date is actually the effective date for the next salary entry, the upper bound is exclusive rather than inclusive. Consequently, the range test must test for null and one can't use between.