SQL - 2nd highest record in table - sql

SELECT MAX(Score)
FROM Students
WHERE Score < (SELECT MAX(Score) FROM Students);
the above query works perfectly and fetches the record that has 2nd highest score, whereas the query mentioned below does not fetch anything
SELECT *
FROM Students
WHERE Score < (SELECT MAX(Score) FROM Students);
here Students is the table from which I want to fetch all the details of that record which has 2nd highest score in the entire table.
I want that 2nd query should get executed, thanks in advance for helping me out.
I have not used any database, I'm simply trying out these queries in w3schools.

With standard SQL, this is typically solved using window functions:
select *
from (
select *, dense_rank() over (order by score desc) as rnk
from students
) t
where rnk = 2;
The above is ANSI standard SQL and works on all modern DBMS.

How about ordering and getting the first record returned using LIMIT:
SELECT *
FROM Students
WHERE Score < (SELECT MAX(Score) FROM Students)
ORDER BY Score DESC
LIMIT 1;

Related

BIGQUERY - LEFT OUTER JOIN cannot be used without a condition that is an equality of fields from both sides of the join

I am trying to find 5th highest salary in bigquery using this query but it gives me error
LEFT OUTER JOIN cannot be used without a condition that is an equality of fields from both sides of the join.
I believe this is the right query for sql for this question but something is not working out in bigquery. Can anybody help me with this? :)
select concat(first_name, ' ', last_name) as Name, salary
from `table` w1
where 4 = (select count(distinct(salary))
from `table` w2
where w2.salary > w1.salary)
Your query appears to be returning rows that have four larger salaries. That would be the fifth largest salary. So, just use dense_rank():
select w.*
from (select w.*,
dense_rank() over (order by salary desc) as seqnum
from `table` w
) w
where seqnum = 5;
Below is for BigQuery Standard SQL
Using functions like DENSE_RANK(), ROW_NUMBER() and such for big volumes of data usually ends up with some thing like Resource Limits Exceeded error.
Depends on your real use case - you can consider below alternatives:
#standardSQL
SELECT *
FROM `project.dataset.table`
ORDER BY salary DESC
LIMIT 1 OFFSET 4
OR
#standardSQL
SELECT AS VALUE ARRAY_AGG(t ORDER BY salary DESC LIMIT 5)[SAFE_OFFSET(4)]
FROM `project.dataset.table` t
Both above versions should give you a record with 5th highest salary

How do I grab each student’s 3rd max assignment mark in each subject

I am trying to write an sql that will allow me select each student’s 3rd best assignment mark in each subject. I have tried with the query below but it isn't working for me. I will be grateful to get some answers. I am getting an error [Code: 0, SQL State: 21000] ERROR: more than one row returned by a subquery used as an expression.
This is the table structure Students , Courses(Id) , bridging table called StudentsCourses(ID, StudentID,CourseID) and then assignment table which has StudentsCourse(FK) and Grade
select max(Assignments.Grade)
from Assignments
where grade < (select max(Assignments.Grade)
from Assignments
where grade < (select max(Assignments.Grade)
from Assignments
group by Assignments.StudentCourseID))
You can use window functions:
select *
from (
select a.*, row_number() over(partition by student_id, subject_id order by grade desc)
from assignments a
) a
where rn = 3
Your question is a bit unclear about the structure of table assignments. This assumes that a student is identified by student_id and a subject by subject_id - you many need to ajust that to your actual column names.
Use row_number():
select a.*
from (select a.*,
row_number() over (partition by student_id, StudentCourseID order by grade desc) as seqnum
from assignments a
) a
where seqnum = 3;
Note: If all the assignments have the same value, this will return the highest value.
If you want the third highest distinct score, then use dense_rank() instead of row_number().

Unable to find Max Age of a Player

i am a newbie to SQL.
I wanna find out what which player is oldest by age.
So here is my table..
Somehow my Query give error.
Can you please tell me where i am doing it wrong.
Thanks.
select * from players
where age = (select max(age) as Oldest_Player from players);
limit 1
SQL has a SELECT TOP command, which allows you to retrieve a set number of rows. You can do SELECT TOP 1 name AS 'Oldest Person' FROM players ORDER BY age DESC
What this will do is: first retrieve all the players, sort them by age descending (oldest first), then take the first one.
You can use row_number as below:
Select * from (
Select *, RowN = Row_Number() over(order by age desc) from Players
) a Where a.RowN = 1

sql query finding most often level appear

I have a table Student in SQL Server with these columns:
[ID], [Age], [Level]
I want the query that returns each age value that appears in Students, and finds the level value that appears most often. For example, if there are more 'a' level students aged 18 than 'b' or 'c' it should print the pair (18, a).
I am new to SQL Server and I want a simple answer with nested query.
You can do this using window functions:
select t.*
from (select age, level, count(*) as cnt,
row_number() over (partition by age order by count(*) desc) as seqnum
from student s
group by age, level
) t
where seqnum = 1;
The inner query aggregates the data to count the number of levels for each age. The row_number() enumerates these for each age (the partition by with the largest first). The where clause then chooses the highest values.
In the case of ties, this returns just one of the values. If you want all of them, use rank() instead of row_number().
One more option with ROW_NUMBER ranking function in the ORDER BY clause. WITH TIES used when you want to return two or more rows that tie for last place in the limited results set.
SELECT TOP 1 WITH TIES age, level
FROM dbo.Student
GROUP BY age, level
ORDER BY ROW_NUMBER() OVER(PARTITION BY age ORDER BY COUNT(*) DESC)
Or the second version of the query using amount each pair of age and level, and max values of count pair age and level per age.
SELECT *
FROM (
SELECT age, level, COUNT(*) AS cnt,
MAX(COUNT(*)) OVER(PARTITION BY age) AS mCnt
FROM dbo.Student
GROUP BY age, level
)x
WHERE x.cnt = x.mCnt
Demo on SQLFiddle
Another option but will require later version of sql-server:
;WITH x AS
(
SELECT age,
level,
occurrences = COUNT(*)
FROM Student
GROUP BY age,
level
)
SELECT *
FROM x x
WHERE EXISTS (
SELECT *
FROM x y
WHERE x.occurrences > y.occurrences
)
I realise it doesn't quite answer the question as it only returns the age/level combinations where there are more than one level for the age.
Maybe someone can help to amend it so it includes the single level ages aswell in the result set: http://sqlfiddle.com/#!3/d597b/9
with combinations as (
select age, level, count(*) occurrences
from Student
group by age, level
)
select age, level
from combinations c
where occurrences = (select max(occurrences)
from combinations
where age = c.age)
This finds every age and level combination in the Students table and counts the number of occurrences of each level.
Then, for each age/level combination, find the one whose occurrences are the highest for that age/level combination. Return the age and level for that row.
This has the advantage of not being tied to SQL Server - it's vanilla SQL. However, a window function like Gordon pointed out may perform better on SQL Server.

Fetch one row per account id from list

I have a table with game scores, allowing multiple rows per account id: scores (id, score, accountid). I want a list of the top 10 scorer ids and their scores.
Can you provide an sql statement to select the top 10 scores, but only one score per account id?
Thanks!
select username, max(score) from usertable group by username order by max(score) desc limit 10;
First limit the selection to the highest score for each account id.
Then take the top ten scores.
SELECT TOP 10 AccountId, Score
FROM Scores s1
WHERE AccountId NOT IN
(SELECT AccountId s2 FROM Scores
WHERE s1.AccountId = s2.AccountId and s1.Score > s2.Score)
ORDER BY Score DESC
Try this:
select top 10 username,
max(score)
from usertable
group by username
order by max(score) desc
PostgreSQL has the DISTINCT ON clause, that works this way:
SELECT DISTINCT ON (accountid) id, score, accountid
FROM scoretable
ORDER BY score DESC
LIMIT 10;
I don't think it's standard SQL though, so expect other databases to do it differently.
SELECT accountid, MAX(score) as top_score
FROM Scores
GROUP BY accountid,
ORDER BY top_score DESC
LIMIT 0, 10
That should work fine in mysql. It's possible you may need to use 'ORDER BY MAX(score) DESC' instead of that order by - I don't have my SQL reference on hand.
I believe that PostgreSQL (at least 8.3) will require that the DISTINCT ON expressions must match initial ORDER BY expressions. I.E. you can't use DISTINCT ON (accountid) when you have ORDER BY score DESC. To fix this, add it into the ORDER BY:
SELECT DISTINCT ON (accountid) *
FROM scoretable
ORDER BY accountid, score DESC
LIMIT 10;
Using this method allows you to select all the columns in a table. It will only return 1 row per accountid even if there are duplicate 'max' values for score.
This was useful for me, as I was not finding the maximum score (which is easy to do with the max() function) but for the most recent time a score was entered for an accountid.