MSSQL - Grouping Results using MAX() - sql

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

Related

SQL Row containing max value per another variable

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.

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

Window function in SQL

I'm trying to get the column 'Desired Result' from the data presented. I'm not sure of the correct syntax for the windows function to do this. I've tried row_number and versions of RANK but just can seem to get it correct. Any help would be appreciated. thanks
ID TransactionID Desired Result
33 2681998458900 1
44 2682232081872 1
44 2682232081872 1
44 2682232081872 1
44 2683269923140 2
44 2683269923140 2
44 2683269923140 2
60 2345620067722 1
Select ID, TransactionID, Dense_rank() over (partition by ID order by transactionID)
Rank
from table1
This would give your expected results. Dense rank will not skip ranking and will give the same rank if the ordering column has the same value.

Delete rows, which are duplicated and follow each other consequently

It's hard to formulate, so i'll just show an example and you are welcome to edit my question and title.
Suppose, i have a table
flag id value datetime
0 b 1 343 13
1 a 1 23 12
2 b 1 21 11
3 b 1 32 10
4 c 2 43 11
5 d 2 43 10
6 d 2 32 9
7 c 2 1 8
For each id i want to squeze the table by flag columns such that all duplicate flag values that follow each other collapse to one row with sum aggregation. Desired result:
flag id value
0 b 1 343
1 a 1 23
2 b 1 53
3 c 2 75
4 d 2 32
5 c 2 1
P.S: I found functions like CONDITIONAL_CHANGE_EVENT, which seem to be able to do that, but the examples of them in docs dont work for me
Use the differnece of row number approach to assign groups based on consecutive row flags being the same. Thereafter use a running sum.
select distinct id,flag,sum(value) over(partition by id,grp) as finalvalue
from (
select t.*,row_number() over(partition by id order by datetime)-row_number() over(partition by id,flag order by datetime) as grp
from tbl t
) t
Here's an approach which uses CONDITIONAL_CHANGE_EVENT:
select
flag,
id,
sum(value) value
from (
select
conditional_change_event(flag) over (order by datetime desc) part,
flag,
id,
value
from so
) t
group by part, flag, id
order by part;
The result is different from your desired result stated in the question because of order by datetime. Adding a separate column for the row number and sorting on that gives the correct result.

Re-Organize Access Table by converting Rows to Columns

I'm pretty new to access and SQL and need some help re-organizing a table. I have the following table (sorry for the table below - having trouble posting):
ID GroupID Distance Code Start_Finish
1 44 7 A S1
2 44 14 A F1
3 45 12 B S1
4 45 16 B F1
5 45 31 C S2
6 45 36 C F2
7 45 81 B S3
8 45 88 B F3
And need for the table to be transformed into:
GroupID Code Start_Distance Finish_Distance
44 A 7 14
45 B 12 16
45 C 31 36
45 B 81 88
try something like this
Select GroupID, Code, min(distance) as Start_distance, max(distance) as Finish_distance
from Table
group by GroupID, Code
If the min and max functions don't give you what you need, try it with First() and Last() instead.
Oops - just noticed you have 2 different entries in the output for GroupID 45 Code B - is that a requirement? With that data structure and requirement, the problem gets much more difficult.
Now I see the final column in the 1st table - I think that can be used to get the output you want:
Select GroupID, Code, mid(start_finish,2) as T, min(distance) as Start_distance, max(distance) as Finish_distance
from Table
group by GroupID, Code, T
You can use conditional aggregation for this.
select GroupID
, CODE
, max(case when Left(Start_Finish, 1) = 'S' then Distance end) as Start_Distance
, max(case when Left(Start_Finish, 1) = 'F' then Distance end) as Finish_Distance
from SomeTable
group by GroupID
, CODE