SQL Row containing max value per another variable - sql

I have a basic SQL query but laptop is about to go out the window lol
I have a table
ID, StudentID, Mark, DateAdded
1 2 78 19/02/2020
2 4 43 19/02/2020
3 2 23 19/02/2020
4 5 91 20/03/2020
5 7 56 20/03/2020
6 9 24 20/03/2020
7 10 56 12/05/2020
8 10 23 12/05/2020
9 10 78 12/05/2020
10 9 23 12/05/2020
What I want to pull out is the entire row which has max score for each unique studentID, so for example
ID. StudentID. Mark. DateAdded
1 2 78 19/02/2020
2 4 43 19/02/2020
4 5 91 20/03/2020
5 7 56 20/03/2020
6 9 24 20/03/2020
9 10 78 12/05/2020
Thanks

You can use analytical function row_number as follows:
Select * from
(select t.*,
Row_number() over (partition by studentid order by mark desc) as rn
From t)
Where rn= 1

You can achieve your result with a group by, but you can't get the ID and DateAdded column too in this way.
SELECT StudentID, MAX(Mark)
FROM YourTable
GROUP BY StudentID

Simply use function MAX() and then a GROUP BY
SELECT StudentID, MAX(Mark) from tableOne GROUP BY studentID
Here is a fiddle link:
http://sqlfiddle.com/#!9/dd8e77/3

I am going to recommend a correlated subquery:
select t.*
from mytable t
where t.mark = (select max(t1.mark) from mytable t1 where t1.studentid = t.studentid)
Why:
Portability: this works in all versions of MySQL/MariaDB, whereas window functions were introduced in MySQL 8.0 / MariaDB 10.2.2
Efficiency: you want an index on (studentid, mark) (or better yet, studentid, mark desc), so the subquery can take advantage of it. In many situations, the correlated subquery is the most efficient approach at such top-1-per group problem.

Related

MSSQL choose top 2 ranked for each record

Hi I have a table that looks like this
StudentID
ParentID
Rank
1
11
1
1
15
5
1
16
6
2
21
1
2
22
2
3
31
1
3
37
7
3
38
8
4
41
1
4
42
2
So I want to pull only the top 2 ranks per student the out come will look like:
StudentID
ParentID
Rank
1
11
1
1
15
5
2
21
1
2
22
2
3
31
1
3
37
7
4
41
1
4
42
2
What would be the best way to do this? What makes it complicated is that every student has a parent ranked 1 but not every student has a parent ranked 2. What sql statement would I use to pull just the next ranked parent after 1?
The canonical solution is to use row_number():
select t.*
from (select t.*,
row_number() over (partition by studentid order by rank) as seqnum
from t
) t
where seqnum <= 2;
Under some circumstances, it might be faster to use:
select t.*
from t
where t.rank in (select top (2) t2.rank
from t t2
where t2.studentid = t.studentid
order by rank asc
) ;
I think this might have better performance if there were few students that had lots of ranks per student and you had an index on studentid, rank.

How to grouping with distinct or group max sql

i have date like this Data
id name period difference
6172 A 6 10
6172 A 3 10
10099 AB 12 24
10099 AB 6 24
10099 AB 3 24
10052 ABC 12 26
10052 ABC 6 26
10052 ABC 3 26
9014 ABCD 12 21
9014 ABCD 6 21
9014 ABCD 3 21
how to get result like this
id name period difference
6172 A 6 10
10099 AB 12 24
10052 ABC 12 26
9014 ABCD 12 4
i try with distinct on (id), but the result like this
id name period difference
6172 A 6 10
10099 AB 6 24
10052 ABC 6 26
9014 ABCD 6 4
The query you want looks something like:
SELECT DISTINCT ON (id) *
FROM Data
ORDER BY id, period DESC;
Demo
This is probably the most efficient way to write your query on Postgres. Note that DISTINCT ON syntax does not support more than one column in the ON clause. The above logic happens to work here assuming that id would uniquely identify each group (that is, that id would always be unique). If not, then we might have to resort to using ROW_NUMBER with a partition over id and name.
using max()
select id, name, t2.period, difference from tableA t1
inner join
(select id, max(period) as period from tableA
group by id) t2 on t2.id = t1.id
using distinct()
select distinct id, name, t2.period, difference from tableA
it seems you need just max()
select id,name,max(period),max(difference)
from table group by id,name
Though i have not found difference=4 in your sample data but you used that on output,so i guessed its your typo
Use max()
select id, name, max(period), difference from tablename
group by id, name,difference
You can try my code:
SELECT
id, name, max(period), difference
FROM
data_table
group by id, name,difference
order by name
This is a demo link http://sqlfiddle.com/#!17/9ab8d/2

Sql getting MAX and MIN values based on two columns for the ids from two others

I'm having difficulties figuring a query out, would someone be able to assist me with this?
Problem: 4 columns that represent results for the 2 separate tests. One of them taken in UK and another in US. Both of them are the same test and I need to find the highest and lowest score for the test taken in both countries. I also need to avoid using subqueries and temporary tables. Would appreciate theoretical ideas and actual solutions for the problem.
The table looks like this:
ResultID Test_UK Test_US Test_UK_Score Test_US_Score
1 1 2 48 11
2 4 1 21 24
3 3 1 55 71
4 5 6 18 78
5 7 4 19 49
6 1 3 23 69
7 5 2 98 35
8 6 7 41 47
The desired results I'm looking for:
TestID HighestScore LowestScore
1 71 23
2 35 11
3 69 55
4 49 21
5 98 18
6 78 41
7 47 19
I tried implementing a case of comparison, but I still ended up with subquery to pull out the final results. Also tried union, but it ends up in a sub query again. As far as I can think it shoul be a case when then query, but can't really come up with the logic for it, as it requires to match the ID's of the tests.
Thank you!
What I've tried and got the best results (still wrong)
select v.TestID,
max(case when Test_US_Score > Test_UK_Score then Test_UK_Score else null end) MaxS,
min(case when Test_UK_Score > Test_US_Score then Test_US_Score else null end) MinS
FROM ResultsDB rDB CROSS APPLY
(VALUES (Test_UK, 1), (Test_US, 0)
) V(testID, amount)
GROUP BY v.TestID
Extra
The answer provided by M. Kanarkowski is a perfect solution. I'm no expert on CTE, and a bit confused, how would it be possible to adapt this query to return the result ID of the row that min and max were found.
something like this:
TestID Result_ID_Max Result_ID_Min
1 3 6
2 7 1
3 6 3
Extra 2
The desired results of the query would me something like this.
The two last columns represent the IDs of the rows from the original table where the max and min values were found.
TestID HighestScore LowestScore Result_ID_Of_Max Result_ID_Of_Min
1 71 23 3 6
2 35 11 7 1
3 69 55 6 3
For example you can use union to have results from both countries togehter and then just pick the maximum and the minimum for your data.
with cte as (
select Test_UK as TestID, Test_UK_Score as score from yourTable
union all
select Test_US as TestID, Test_US_Score as score from yourTable
)
select
TestID
,max(score) as HighestScore
,min(score) as LowestScore
from cte
group by TestID
order by TestID
Extra:
I assumed that you want to have the additional column with the previous result. If not just take the above select and replace Test_UK_Score and Test_US_Score with ResultID.
with cte as (
select Test_UK as TestID, Test_UK_Score as score, ResultID from yourTable
union all
select Test_US as TestID, Test_US_Score as score, ResultID from yourTable
)
select
TestID
,max(score) as HighestScore
,min(score) as LowestScore
,max(ResultID) as Result_ID_Max
,min(ResultID) as Result_ID_Min
from cte
group by TestID
order by TestID

MSSQL - Grouping Results using MAX()

I have this dataset;
dID Num
11 3
11 4
11 5
13 9
13 11
45 3
45 8
99 44
99 78
99 53
I want it to look like this.
dID Num
11 5
13 11
45 8
99 78
List all ID's and only show those ID's where the 'Num' is the Largest number for that group of ID's
my attempt here doesnt quite work out.
http://sqlfiddle.com/#!9/1a47f/1
You seem to just want an aggregation query:
select dId, max(num) as num
from data t
group by dId;
You need to aggregate by the first column, not the argument to the aggregation function.
If you have oversimplified the problem, and want other columns as well, then use row_number():
select t.*
from (select t.*, row_number() over (order by num desc) as seqnum
from data t
) t
where seqnum = 1;
You almost get it right, you just grouped by the wrong column:
select dID,
MAX(num) from data
group by dID
See it here: http://sqlfiddle.com/#!9/1a47f/3

Query to return all results except for the first record

I have a archive table that has records of transactions per locationID.
A location will have 0, 1 or many rows in this table.
I need a SELECT query that will return rows for any location that has more than 1 row, and to skip the first entry.
e.g.
Transactions table
transactionId locationId amount
1 11 2343
2 11 23434
3 25 342
4 32 234
5 77 234
6 11 38938
7 43 234
8 43 1235
So given the above, since the locationID has multiple rows, I will get back all rows except for the first one (lowest transacton_id):
2 11 23434
6 11 38938
8 43 1235
You can use row_number to do this. This assumes there would be no duplicate transactionid's.
select transactionid,locationid,amount
from
(select t.*, row_number() over(partition by locationid order by transactionid) as rn
from transactions t) t
where rn > 1
The other answer is fine. You could also write it this way, it might give you a little insight into grouping practices:
SELECT Transactions.TransactionID, Transactions.locationID, Transactions.amount
FROM Transactions INNER JOIN
(SELECT locationID, MIN(TransactionID) AS MinTransaction,
COUNT(TransactionID) AS CountTransaction
FROM Transactions
GROUP BY locationID) TableSum ON Transactions.locationID = TableSum.locationID
WHERE (Transactions.TransactionID <> TableSum.MinTransaction) AND
(TableSum.CountTransaction > 1)