Select MAX or SUM - sql

Simply, I have exam note for many Student for many exam,
see the picture below (MATH = 0, BIOLOGY = 2, ALGEBRA = 1)
I just want to give the student the Max notes = The Student have 2 = on BIOLOGY so ALGEBRA AND MATH must be at 2
Try to have this :
I try this :
SELECT First_Name, EXAM, MAX(NOTE)
FROM My_Table
Group by First_Name, EXAM
Not working, still give me this (MATH = 0, BIOLOGY = 2, ALGEBRA = 1)
Try also :
SELECT First_Name, EXAM,
CASE
WHEN SUM(NOTE) <> 0 THEN MAX(NOTE)
Else 0
END AS MAX_NOTE
FROM My_Table
Not working
Please do you have any idea ? or solution ? Click to check see the picture (screenshot)

Remove the group by, and use a window function to take care of per-student logic:
SELECT First_Name, EXAM, MAX(NOTE) over (partition by First_Name)
FROM My_Table

SELECT
First_Name,
EXAM,
MAX(NOTE) OVER(partition by First_Name) as MAX_NOTE
FROM
My_Table
Reference for using Max and OVER together in SQL Server:
https://learn.microsoft.com/en-us/sql/t-sql/functions/max-transact-sql?view=sql-server-ver15

Related

How to find every combination of features shared across multiple rows?

I am pretty new to using SQL (using StandardSQL via Big Query currently) and unfortunately my Google-fu could not find me a solution to this issue.
I'm working with a dataset where each row is a different person and each column is an attribute (name, age, gender, weight, ethnicity, height, bmi, education level, GPA, etc.). I am tying to 'cluster' these people into all of the feature combinations that match 5 or more people.
Originally I did this manually with 3 feature columns where I would essentially concatenate a 'cluster name' column and then have 7 select queries for each grouping with a >5 where clause, which I then UNIONed together:
gender
age
ethnicity
gender + age
gender + ethnicity
age + ethnicity
gender + age + ethnicity
^ unfortunately doing it this way just balloons the number of combinations and with my anticipated ~15 total features doing it this way seems really unfeasible. I'd also like to do this through a less manual approach so that if a new feature is added in the future it does not require major edits to include it in my cluster identification.
Is there a function or existing process that could accomplish something like this? I'd ideally like to be able to identify ALL combinations that meet my combination user count minimum (so it's expected the same rows would match multiple different clusters here. Any advice or help here would be appreciated! Thanks.
If only BQ supported grouping sets or cube, this would be simple. One method that is pretty generalizable enumerates the 7 groups and then uses bits to figure out what to aggregate:
select (case when n & 1 > 0 then gender end) as gender,
(case when n & 2 > 0 then age end) as age,
(case when n & 4 > 0 then ethnicity end) as ethnicity,
count(*)
from t cross join
unnest(generate_array(1, 7)) n
group by n, 1, 2, 3;
Another method which is trickier is to reconstruct the groups using rollup(). Something like this:
select gender, age, ethnicity, count(*)
from t
group by rollup(gender, age, ethnicity);
Produces three of the groups you want. So:
select gender, age, ethnicity, count(*)
from t
group by rollup(gender, age, ethnicity)
union all
select gender, null, ethnicity, count(*)
from t
group by gender, ethnicity
union all
select null, age, ethnicity, count(*)
from t
group by rollup (ethnicity, age);
The above reconstructs all your groups using rollup().

SQL,Nested Queries (MS ACCESS)

I am trying to tackle a problem but seem to be getting nowhere. I want to display Grade 12 students who scored below average for Maths then instead of displaying their average display their maths marks instead.
I am using msAccess and suspect the use of nested queries are necessary.The fields I am working with are first_name, last_name, grade (from 1 to 12) and Maths (containing maths marks)
I have this:
Select first_name,last_name,maths
FROM students
WHERE grade = 12
HAVING ROUND(AVG(maths),1)< maths;
Output:
Error:
You tried to execute a query that does not include the specified expression 'first_name' as part of an aggregate function
However, I do not know why it is throwing this error and it repeats like this even after removing the field from select which I don't want to do in the first place because I need to display it
To get the users who scored below the average, you can do a query similar to yours, but with a group by:
select s.student_id, avg(maths) as avg_maths
from students as s
where s.grade = 12
group by s.student_id
having avg(maths) < (select avg(maths) from students where grade = 12);
(Note: This assumes that you have an id for each student, rather than using the name.)
Next, you can get the original maths scores in various ways. One simple way uses in:
select first_name, last_name, maths
from students
where grade = 12 and
student_id in (select s.student_id, avg(s.maths) as avg_maths
from students as s
where s.grade = 12
group by s.student_id
having avg(maths) < (select avg(maths) from students where grade = 12)
);

STDDEV function and with clause

I am using Oracle. I have table like:
Company Employee salary
A1 Jim 122000
...
I want to return the company with the highest number of employee whose salary is above 2 standard deviations (~>95%). Here is my code
With Com_2Std as (
Select company-name, AVG(salary)+2*STDDEV(salary) as AboveS
From works
Group By company-name)
Select company-name, count(employee-name) as ENumber
From works
Where ENumber=MAX(
Select count(a.employee-name)
From works a, Com_2Std b
Where a.company-name=b.company-name
And a.salary>b.AboveS;
Group by a.company-name)
Group by company-name;
I have two quesions:
(1) I can't access to oracle today and can't test it. Is my code correct please?
(2) It looks quite complicated, any way to simplify it please?
with Com_2Std as (
select company-name, AVG(salary)+2*STDDEV(salary) as AboveS
from works
group by company-name
),
CompanyCount as (
select a.company-name, count(*) as CountAboveS
from
works a
inner join Com_2Std b on a.company-name=b.company-name
where
a.salary > b.AboveS
group by a.company-name
)
select company-name
from CompanyCount
where CountAboveS = (select max(CountAboveS) from CompanyCount)
This ought to be close. It will produce ties as well.

How to use Order By clause on a column containing string values separated by comma?

I have a table with a column named Skills which contains comma separated values for different employees like
EmpID Skills
1 C,C++,Oracle
2 Java,JavaScript,PHP
3 C,C++,Oracle
4 JavaScript,C++,ASP
5 C,C++,JavaScript
So I want to write a query which will order all the employees first who knows JavaScript, how can I get this result?
You should not use one attribute to store multiple values. That goes against relation DB principles.
Instead of that you should create additional table to store skills and refer to employee in it. Then, your query will looks like:
SELECT
*
FROM
employees
LEFT JOIN employees_skills
ON employee.id=employees_skills.employee_id
WHERE
employees_skills='JavaScript'
Try this
SELECT *
FROM
(
SELECT *
,CASE WHEN Skills LIKE '%JavaScript%' THEN 0 ELSE 1 END AS Rnk
FROM MyTable
) T
ORDER BY rnk,EmpID
DEMO
OR
SELECT * FROM #MyTable
ORDER BY CASE WHEN Skills LIKE '%JavaScript%' THEN 0 ELSE 1 END,EmpID
select EmpID, Skills
from Table1
order by case when Skills like '%JavaScript%' then 0 else 1 end
Try this:
SELECT *
FROM YourTable
ORDER BY PATINDEX('%JavaScript%', Skills) DESC
But this is a bad way. You should really normalize your table.
For MySQL
select Skills from myTable
order by case Skills
when "Javascript" then 0
when "Java" then 1 when "C++" then 2
end
and so on
For SQL Server
select Skills from myTable
order by case
when Skills="Javascript" then 1
when Skill="Java" then 2
else 3
end
Make sure to start SQL server from 1 (That I'm not sure).
Include an else before end that will show all remaining results.
For more details about SQL Server see this or see this
This works for DB2/400:
with s (id, skill, rest) as
(select id, '', sk from skills
union all
select id, substr(rest, 1, locate(',',rest)-1),
substr(rest,locate(',',rest)+1)
from s
where locate(',',rest) > 0)
select id, skill from s
where skill = 'JavaScript'
order by id

SQL help: select the last 3 comments for EACH student?

I have two tables to store student data for a grade-school classroom:
Behavior_Log has the columns student_id, comments, date
Student_Roster has the columns student_id, firstname, lastname
The database is used to store daily comments about student behavior, and sometimes the teacher makes multiple comments about a student in a given day.
Now let's say the teacher wants to be able to pull up a list of the last 3 comments made for EACH student, like this:
Jessica 7/1/09 talking
Jessica 7/1/09 passing notes
Jessica 5/3/09 absent
Ciboney 7/2/09 great participation
Ciboney 4/30/09 absent
Ciboney 2/22/09 great participation
...and so on for the whole class
The single SQL query must return a set of comments for each student to eliminate the human-time-intensive need for the teacher to run separate queries for each student in the class.
I know that this sounds similar to
SQL Statement Help - Select latest Order for each Customer but I need to display the last 3 entries for each person, I can't figure out how to get from here to there.
Thanks for your suggestions!
A slightly modified solution from this article in my blog:
Analytic functions: SUM, AVG, ROW_NUMBER
SELECT student_id, date, comment
FROM (
SELECT student_id, date, comment, (#r := #r + 1) AS rn
FROM (
SELECT #_student_id:= -1
) vars,
(
SELECT *
FROM
behavior_log a
ORDER BY
student_id, date DESC
) ao
WHERE CASE WHEN #_student_id <> student_id THEN #r := 0 ELSE 0 END IS NOT NULL
AND (#_student_id := student_id) IS NOT NULL
) sc
JOIN Student_Roster sr
ON sr.student_id = sc.student_id
WHERE rn <= 3
A different approach would be to use the group_concat function and a single sub select and a limit on that subselect.
select (
select group_concat( concat( student, ', ', date,', ', comment ) separator '\n' )
from Behavior_Log
where student_id = s.student_id
group by student_id
limit 3 )
from Student_Roster s