Displaying the top two of summed values by group - database

I have a query named "QueryTotalGrades" which has three fields (Group, StudentID and Mark). each studentID has more than one mark. What I want to do is to create another query that conduct the following :
1- Sum mark for each studentID as a sumOfMark (Descending order)
2- Display the top 2 of sumOfMarks per group.
Example: let say that the "QueryTotalGrades" has the following values.
I'm using Microsoft access 2013
Group StudentID Mark
1 1 8
1 1 7
1 1 8
1 2 7
1 2 7
1 2 7
1 3 9
1 3 9
1 3 9
2 4 5
2 4 7
2 4 5
2 5 7
2 5 7
2 5 7
2 6 6
2 6 6
2 6 6
3 7 8
3 7 7
3 7 8
3 8 7
3 8 7
3 8 7
3 9 10
3 9 10
3 9 10
,so the output that I want should be as following
Group StudentID SumOfMark
1 3 27
1 1 23
2 5 21
2 6 18
3 9 30
3 7 23
I have tried many solutions, but no avail. HELP

A little longwinded but:
select
t1.[Group], t1.StudentID, t1.SumOfMark
from
(select [Group], StudentID, sum(Mark) as SumOfMark
from QueryTotalGrades
group by [Group], StudentID) as t1
where
(select count(*) from
(select [Group], StudentID, sum(Mark) as SumOfMark
from QueryTotalGrades
group by [Group], StudentID) as t2
where
t2.[Group] = t1.[Group] and
t2.SumOfMark >= t1.SumOfMark) <= 2
order by
t1.[Group], t1.SumOfMark desc
You can play with it here: SQL Fiddle

Query
;with cte as
(
select rn=row_number() over
(
partition by [Group]
order by sum(Mark) desc
),
[Group],StudentID,
sum(Mark) as SumOfMark
from student
group by [Group],StudentID
)
select [Group],StudentId,SumOfMark from cte where rn in (1,2);
fiddle demo

Related

Get the rank of each string extracted from split_string

I have a TABLE with STR DNA DATA the following Table [DYS]
Id
DYS385
3
10-19
4
13-16
5
13-18
6
13-19
7
13-17
8
13-18
9
13-18
10
14-19
11
13-19
12
13-18
I am USING the following script to split the values of [DYS385]
select top 10 id,[DYS385],t.Value
from dys
OUTER APPLY(select * from string_split([DYS385],'-')) t
where dys385 is not null
Output
Id
DYS385
VALUE
3
10-19
10
3
10-19
19
4
13-16
13
4
13-16
16
5
13-18
13
5
13-18
18
6
13-19
13
6
13-19
19
7
13-17
13
7
13-17
17
I want to get for each Value, the Rank
example
10-19 => 10: Rank 1, 19: Rank 2
Desired results:
Id
DYS385
VALUE
RANK
3
10-19
10
1
3
10-19
19
2
4
13-16
13
1
4
13-16
16
2
5
13-18
13
1
5
13-18
18
2
6
13-19
13
1
6
13-19
19
2
7
13-17
13
1
7
13-17
17
2
Use an alternative string-split method, such as XML or Json that can return an ordinal position, such as:
create function dbo.SplitString(#string varchar(1000), #Delimiter varchar(10))
returns table
as
return(
select j.[value], 1 + Convert(tinyint,j.[key]) Seq
from OpenJson(Concat('["',replace(#string,#delimiter, '","'),'"]')) j
);
select value, Seq as [Rank]
from dbo.SplitString('10-19','-')
order by [Rank];
Example fiddle
Given this data:
CREATE TABLE dbo.Something(Id int, DYS385 varchar(10));
INSERT dbo.Something(Id, DYS385) VALUES (3 , '10-19'),
(4 , '13-16'), (5 , '13-18'), (6 , '13-19'),
(7 , '13-17'), (8 , '13-18'), (9 , '13-18'),
(10, '14-19'), (11, '13-19'), (12, '13-18');
Another way to make sure you rank the broken-up strings in the right order could be to use tricks like PARSENAME(), though this can be sensitive to strings lengths and whether a dot is valid within the data:
SELECT s.Id, s.DYS385, value = v.v, [rank] = ROW_NUMBER() OVER
(PARTITION BY s.Id ORDER BY c.c DESC)
FROM dbo.Something AS s
CROSS APPLY (VALUES(1),(2)) AS c(c)
CROSS APPLY (VALUES(PARSENAME(REPLACE(s.DYS385,'-','.'),c.c))) AS v(v)
ORDER BY s.Id, [rank];
Output:
Id
DYS385
value
rank
3
10-19
10
1
3
10-19
19
2
4
13-16
13
1
4
13-16
16
2
5
13-18
13
1
5
13-18
18
2
6
13-19
13
1
6
13-19
19
2
7
13-17
13
1
7
13-17
17
2
8
13-18
13
1
8
13-18
18
2
9
13-18
13
1
9
13-18
18
2
10
14-19
14
1
10
14-19
19
2
11
13-19
13
1
11
13-19
19
2
12
13-18
13
1
12
13-18
18
2
Example db<>fiddle
Split_string has an option to output this rank, called ordinal. Just add an extra parameter with the value of 1:
SELECT *FROM STRING_SPLIT('Lorem ipsum dolor sit amet.', ' ', 1);
That returns:
value ordinal
Lorem 1
ipsum 2
dolor 3
sit 4
amet. 5
In your case the query would be:
select top 10 id,[DYS385],t.* from dys OUTER APPLY(select * from string_split([DYS385],'-',1)) t where dys385 is not null
We can also make recursive split_string function which returns the rank of each item as follows
Link dbfiddle
Create function Split_Recursive(#string nvarchar(max),#delimiter as varchar(1)) returns Table
as
return(
with cte as(
select 1 N,
case when charindex(#delimiter,#string,1) =0 then #string else
substring(#string,1,-1+charindex(#delimiter,#string,1)) end [Found],
case when charindex(#delimiter,#string,1)=0 then #string else substring(#string,1+charindex(#delimiter,#string,1) ,len(#string))
end [MYSTRING]
union all
select 1+n,
case when charindex(#delimiter,[MYSTRING],1)=0 then [MYSTRING] else
substring([MYSTRING],1,-1+charindex(#delimiter,[MYSTRING],1)) end,substring([MYSTRING],1+charindex(#delimiter,[MYSTRING]),len([MYSTRING])) from cte
where charindex(#delimiter,[MYSTRING],1)>0),
cte2 as (select N,Found from cte
union select 1+t.N,Mystring from cte OUTER APPLY(select top 1 N from cte order by n desc)t where t.n=cte.n
)
select N Rank,Found from cte2
)
go
select * from dbo.Split_Recursive('12-16','-')
Output
Rank
Found
1
12
2
16

SQL Server Rank Function without taking into account some flagged values

Here is my problem: I have a list of flagged values, I want to see where those values would be in the case they weren't flagged. But I don't want the other flagged values to influence the order.
Note: Flagged values are the ones with CurrentPlace 10000
ID Value CurrentPlace
------------------------
1 2 1
2 8 3
3 3 2
4 4 10000
5 5 10000
6 10 10000
Using:
select *
from
(select
id, value,
rank() over (order by Value asc) as Rank
from
tbl1) r
where
r.ID in (select id from tbl1 where CurrentPlace = 10000)
Desired output:
ID Value Rank
------------------
4 4 3
5 5 3
6 10 4
But I'm getting this instead:
ID Value Rank
------------------
4 4 3
5 5 4
6 10 6
Any help will be appreciated
Thank you guys
I've solved with
SELECT ID, Value, Rank
FROM tbl1 a
CROSS APPLY
(SELECT isnull(max(currentPlace),0) + 1 AS Rank FROM tbl1 WHERE value < a.value and currentPlace <> 10000) b
WHERE a.CurrentPlace = 10000
Please feel free to comment this out.

Top 9 Distinct records after using sub query for cumulative sum

I have SQL data table with following data structure
ID Hrly Hshed Dust_removal_to_done Dust_removal_done Update_datetime
2 ER MGS 4 4 2009-05-05
3 ER AQ 4 2 2009-05-05
4 SR ANDA 4 4 2009-05-05
5 ECR HOME 5 5 2009-05-05
6 NR GZB 5 5 2009-05-05
7 NR LDH 5 5 2009-05-05
8 NCR JHS 5 5 2009-05-05
9 NCR CNB 5 5 2009-06-05
10 SCR LGD 5 5 2009-06-05
11 SCR LGD 5 5 2009-05-05
the data is fed by users on daily basis.
Further I am using a stored procedure for cumulative sum of 'Dust_removal_done' as
ALTER PROCEDURE [dbo].[RAP_regular] as
SELECT Hshed, HRly, Dust_removal_to_done, Dust_removal_done, (SELECT SUM(Dust_removal_done) FROM TPHRAP_regular t2
where t2.Hshed = TPHRAP_regular.Hshed and t2.Update_datetime <= TPHRAP_regular.Update_datetime) as cumulative_dust_removal
FROM TPHRAP_regular
This stored procedure is giving me result as under
Hshed Hrly Dust_removal_to_done Dust_removal_done cumulative_dust_removal
MGS ER 4 4 4
AQ ER 4 2 2
ANDA SR 4 4 4
HOME ECR 5 5 5
GZB NR 5 5 5
LDH NR 5 5 5
JHS NCR 5 5 5
CNB NCR 5 5 5
LGD SCR 5 5 10
LGD SCR 5 5 5
This is working fine. Now the issue is that there are only 9 Hsheds and therefore I want to display only 9 latest records (unique Hshed along with cumulative column) in my grid view as final result so that no Hshed will repeate in the table. How to achieve this? please help.
You need to change your stored procedure(Has to be in it since you are discarding the date field in it).
You can use ROW_NUMBER() window function to filter only the latest records, like this:
SELECT Hshed,HRly,Dust_removal_to_done,Dust_removal_done,cumulative_dust_removal
FROM(
SELECT Hshed, HRly, Dust_removal_to_done, Dust_removal_done,
(SELECT SUM(Dust_removal_done) FROM TPHRAP_regular t2
where t2.Hshed = TPHRAP_regular.Hshed
and t2.Update_datetime <= TPHRAP_regular.Update_datetime) as cumulative_dust_removal,
ROW_NUMBER() OVER (PARTITION BY Hshed ORDER BY Update_datetime DESC) as rnk
FROM TPHRAP_regular)
WHERE rnk = 1
EDIT: You should also use SUM() OVER(..) for cumulative sum , no need to select from the table twice:
SELECT t.Hshed,
t.HRly,
t.Dust_removal_to_done,
t.Dust_removal_done,
t.cumulative_dust_removal
FROM (SELECT Hshed,
HRly,
Dust_removal_to_done,
Dust_removal_done,
SUM(Dust_removal_done) OVER(PARTITION BY Hshed ORDER BY Update_datetime) as cumulative_dust_removal,
ROW_NUMBER() OVER(PARTITION BY Hshed ORDER BY Update_datetime DESC) as rnk
FROM TPHRAP_regular) t
WHERE t.rnk = 1

Cursor to update values in certain fashion

In SQL Server 2008 R2, I have a table like this:
ID Dates Count
1 03-02-2014 2
2 04-02-2014 1
3 05-02-2014 NULL
4 06-02-2014 1
5 07-02-2014 3
6 08-02-2014 NULL
7 09-02-2014 2
8 10-02-2014 NULL
9 11-02-2014 1
10 12-02-2014 3
11 13-02-2014 NULL
12 14-02-2014 1
I have an INT variable having some value such as #XCount = 15.
My requirement is to update the count column with (#XCount - Count) such as the result of previous record will be subtracted by the Count value in the next record.
Result:
ID Dates Count
1 03-02-2014 13 (15-2)
2 04-02-2014 12 (13-1)
3 05-02-2014 12 (12-0)
4 06-02-2014 11 (12-1)
5 07-02-2014 8 (11-3)
6 08-02-2014 8 (8-0)
7 09-02-2014 6 (8-2)
8 10-02-2014 6 (6-0)
9 11-02-2014 5 (6-1)
10 12-02-2014 2 (5-3)
11 13-02-2014 2 (2-0)
12 14-02-2014 1 (2-1)
I'm reluctant to use cursors as a solution. Can somebody help me?
How about something like
DECLARE #XCount INT = 15
;WITH Vals AS(
SELECT ID, Dates, [Count] OriginalCount, #XCount - ISNULL([COUNT],0) NewCount
FROM Table1
WHERE ID = 1
UNION ALL
SELECT t.ID, t.Dates, t.[Count], v.NewCount - ISNULL(t.[Count],0)
FROM Table1 t INNER JOIN Vals v ON t.ID = v.ID + 1
)
SELECT *
FROM Vals
SQL Fiddle DEMO
Do note thought that this is a recursive query, and that sometimes (until the tech allows for it, such as SQL SERVER 2012 LAG or Running totals) old does work.

How to extract ids of the rows with minimum value in sql

I have the following table
RecordID Group Date Value
----------------------------------------
1 Test 1 1/01/2012 10
2 Test 1 1/01/2012 10
3 Test 1 1/01/2012 20
4 Test 2 1/01/2012 20
5 Test 1 2/01/2012 10
6 Test 2 2/01/2012 30
7 Test 1 2/01/2012 20
8 Test 1 2/01/2012 20
9 Test 2 2/01/2012 20
10 Test 1 3/01/2012 10
11 Test 2 3/01/2012 10
12 Test 2 3/01/2012 20
13 Test 3 3/01/2012 10
14 Test 1 4/01/2012 10
I need to get all RecordIds, where for the same date and same group it has the lowest value and disregard all records for the same date and group that have greater value. So my query needs to group by "date" and "group" and find records with lowest value, that is result should be:
RecordIds: 1, 2, 4, 5, 9, 10, 11, 13, 14
You can use rank in a sub query.
select T.RecordID,
T.[Group],
T.Date,
T.Value
from
(
select RecordID,
[Group],
Date,
Value,
rank() over(partition by [Group], Date order by Value) as rn
from YourTable
) as T
where T.rn = 1
order by T.RecordID
SQL Fiddle

Resources