How to group in fixed count ranges? - sql

Let's say I have one table with two columns (student_id,grade).
student_id is an integer and grade is a decimal number.
In case I have 1000 students and want to group them ordered by grade in groups of 10 students each.
Just to be clear, this should produce 100 groups. The first group cointains the 10 highest grades and the last group contains the 10 lowest grades.
How should I do that ?
Optimization is always welcome.
Thank you very much.
Joao

ntile will give a ranking by an amount of buckets.
select student_id, ntile(100) over (order by grade desc) from student

Related

Calculate number of people over average

I am trying to calculate how many people have a grade higher than average.
What I have currently instead returns the number of students and when I delete "=" from ">=" it returns 0.
SELECT count(*)
FROM data.students
WHERE grade IN (SELECT grade
FROM data.students
GROUP BY grade HAVING grade >= AVG(grade));
If I put an integer instead of avg() function I get good results.
What am I doing wrong?
Computing the avg in a subquery is probably fastest:
SELECT count(*)
FROM data.students
WHERE grade > (SELECT avg(grade) FROM data.students);
> instead of >=, since you said "a grade higher than average".
What am I doing wrong?
In your subquery, GROUP BY grade aggregates to one row per distinct value of grade. avg(grade) is bound to be exactly the same as grade for every row (except grade IS NULL). Explains what you saw.
Your query was needlessly complex to begin with.
Try this :
SELECT count(*)
FROM (
SELECT grade >= AVG(grade) OVER () AS tst
FROM data.students
) AS a
WHERE a.tst= True

How can I select max value and second max value from table and order by city id?

I have this employees table.
I need to write an SQL query that will bring me the max salary of an employee and the second highest salary of an employee by city id (id is linked to a table with cities. there are 5 cities.)
My query looks like this:
select MAX([dbo.Employees].Salary) as Salary from [dbo.Employees]
where [dbo.Employees].Salary not in(select MAX([dbo.Employees].Salary) from [dbo.Employees])
UNION select MAX([dbo.Employees].Salary) from [dbo.Employees] group by [dbo.Employees].Id
I try to bring the highest and exclude the highest but it suppose to bring overall 7 values but it brings only 5. (because there are 5 cities but the 5th is not in use so there are 4 cities and 2 employees in each city except 1 that has only 1 employee so the query suppose to bring me 2 pairs of employees per city = 6, and one of the cities has only 1 employee so it will bring the only possible value. overall 7. )
another problem is that I don't know how to make in bring 2 columns - one for the id of the cities and the second for the salaries themselves because it tells me that something about the group by doesn't work.
You can use ROW_NUMBER() to create a sequence for each city, with the highest salary getting value 1, second highest getting value 2, etc. Then you just need a WHERE clause.
WITH
ranked AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY Salary DESC) AS city_salary_id
FROM
dbo.Employees
)
SELECT
*
FROM
ranked
WHERE
city_salary_id IN (1, 2)
If multiple people are tied with the same salary, it will arbitrarily pick the order for you, and always return (at most) 2 employees per city.
Adding a column to the ORDER BY lets you be more specific about how to deal with ties, such as ORDER BY Salary DESC, id ASC will prioritise the highest id in the even of a tie.
Changing to RANK() will give tied Salaries the same rank, and so will return more than two employees if there are ties.

Average grade in sql server manager studio (SSMS)

I have to calculate students with average grades bigger than 8 .
select students FROM table1
GROUP BY students
HAVING AVG(grade)>8;
When i run the code it doesn't get anything in return , no error , nothing , just column students with no values. I checked table1 to see if there're enough values to calculate and the values are there.
What is wrong with that , can you help me ?
You need to include the grade in your select statement. You can also provide a more accurate column name with as:
SELECT students, AVG(grade) as 'average'
FROM table1
GROUP BY Students
HAVING AVG(grade) > 8
The SELECT includes the values you want to see, while the HAVING filters the results down.
SELECT students, AVG(grade) "Average Grade" FROM table1
GROUP BY students
HAVING AVG(grade)>8;

Find multiple maximum values form a table

I have read answers to similar questions but I cannot find a solution to my particular problem.
I will use a simple example to demonstrate my question.
I have a table called 'Prizes' with two columns: Employees and Awards
The employee column lists the employee's ID and award shows a single award won by the employee. If an employee has won multiple awards their ID will be listed in multiple rows of the table along with each unique award.
The table would look as follows:
Employee AWARD
1 Best dressed
1 Most attractive
2 Biggest time waster
1 Most talkative
3 Hardest worker
4 Most shady
3 Most positive
3 Heaviest drinker
2 Most facebook friends
Using this table, how would I select the ID's of the employees who won the most awards?
The output should be:
Employee
1
3
For the example as both these employees won 3 awards
Currently, the query below outputs the employee ID along with the number of awards they have won in descending order:
SELECT employee,COUNT(*) AS num_awards
FROM prizes
GROUP BY employee
ORDER BY num_awards DESC;
Would output:
employee num_awards
1 3
3 3
2 2
4 1
How could I change my query to select the employee(s) with the most awards?
A simple way to express this is using rank() or dense_rank():
SELECT p.*
FROM (SELECT employee, COUNT(*) AS num_awards,
RANK() OVER (ORDER BY COUNT(*) DESC) as seqnum
FROM prizes
GROUP BY employee
) p
WHERE seqnum = 1;
Being able to combine aggregation functions and analytic functions can make these queries much more concise.
You can use dense_rank to get all the rows with highest counts.
with cnts as (
SELECT employee, count(*) cnt
FROM prizes
GROUP BY employee)
, ranks as (select employee, cnt, dense_rank() over(order by cnt desc) rnk
from cnts)
select employee, cnt
from ranks where rnk = 1

How to get Total of counts?

I'm trying to get Total number of count per column. Here is example of what I need:
Grade Count Name
9 1 Jon
10 3 Ash
I would like to get Total under my Count column what will give the sum of 1 and 3.
Here is my query:
select grade, count(*) as count, name
from students
group by grade, name
order by grade, name;
Thanks in advance.
Many databases support the ROLLUP clause. If yours does, you can just do something like this:
select grade, count(*) as count, name
from students
group by grade, name with rollup
order by grade, name;
Technically, you would want the columns where the grade and rollup are both NULL, but this will give you partial totals as well.