fetching a row from table - sql

Having this data:
I am running this code:
SELECT student_info.student_name,student_info.department,student_info.marks,
rank() over (order by student_info.marks desc) score from student_info ;
and getting:
I want first two records alone with rank with out using rownum

Can you try this please, I think this is what you need
SELECT student_name, department, marks, score
FROM (SELECT s.*, RANK() OVER (ORDER BY marks DESC) AS score
FROM student_info s)
WHERE score = 1

Related

SQL display MIN() column with other columns displayed

I am working on a report where I need to display a student id, the score and the lowest date/time that the score was filed. The table will have multiple scores per student across multiple days.
The problem I am having (using the sample code below) is that I am getting a new row for every score, instead of just one line per student showing the student id, score and date.
sample source table columns
student_id |
score |
score_date
select tests.student_id
,tests.score
,min(score_date)
from tests
group by tests.student_id
,tests.score
You use row_number() for this one.
select * from (
select t1.student_id
,t1.score
,row_number() over (partition by t1.student_id order by t1.score_date) rn
,t1.score_date
from tests t1) t
where t.rn = 1
There is also one trick by using correlated subquery.
select student_id
, min(score_date) score_date
, (select top 1 score from tests where score_date = min(t.score_date) and student_id = t.student_id) as score
from tests t
group by student_id
or if you are using sql server 2012 and higher versions. You can maximize the use of first_value() function.
select distinct first_value(student_id) over (partition by student_id order by score_date)
, first_value(score_date) over (partition by student_id order by score_date)
, first_value(score) over (partition by student_id order by score_date)
from tests t
group by student_id
Try this.
SELECT student_id, score, score_date
FROM tests T
INNER JOIN (SELECT student_id, Min(score_date) AS score_date FROM tests) X
ON T.student_id = X.student_id AND T.score_date = X.score_date
If you want all the dates per student when they had their lowest score?
Then the window function DENSE_RANK comes to mind. Sorted by ascending score.
WITH CTE_STUDENT_SCORES AS
(
SELECT student_id, score, score_date
, DENSE_RANK() OVER (PARTITION BY student_id ORDER BY score ASC) AS StudentAscendingScoreRank
FROM tests
)
SELECT student_id, score, score_date
FROM CTE_STUDENT_SCORES
WHERE StudentAscendingScoreRank = 1
ORDER BY student_id, score_date
If only the latest score_date with the student lowest score is needed?
Then one could limit on ROW_NUMBER instead.
Sorted by ascending score and descending score_date.
WITH CTE_STUDENT_SCORES AS
(
SELECT student_id, score, score_date
, ROW_NUMBER() OVER (PARTITION BY student_id ORDER BY score ASC, score_date DESC) AS StudentAscendingScoreRownum
FROM tests
)
SELECT student_id, score, score_date
FROM CTE_STUDENT_SCORES
WHERE StudentAscendingScoreRownum = 1
ORDER BY score_date DESC, student_id;

Group by two columns, take sum, then max

There are three columns: Id (char), Name (char), and Score (int).
First, we group by Id and Name and add Score for each group. Let us call the added score total_score.
Then, we group by Name and take only the maximum of total_score and its corresponding Id and Name. I've got everything else but I'm having a hard time figuring out how to get the Id. The error I get is
Column 'Id' is invalid in the select list because
it is not contained in either an aggregate function or the GROUP BY
clause.
WITH Tmp AS
(SELECT Id,
Name,
SUM(Score) AS total_score
FROM Mytable
GROUP BY Id,
Name)
SELECT Name, -- Id,
MAX(total_score) AS max_score
FROM Tmp
GROUP BY Name
ORDER BY max_score DESC
just add row_number() partition by Name to your query and get the 1st row (order by total_score descending)
select *
from
(
-- your existing `total_score` query
SELECT Id, Name,
SUM(Score) AS total_score,
r = row_number() over (partition by Name order by SUM(Score) desc)
FROM Mytable
GROUP BY Id, Name
) d
where r = 1
WITH Tmp AS
(SELECT Id,
Name,
SUM(Score) AS total_score
FROM Mytable
GROUP BY Id,
Name)
SELECT Name, Id,
MAX(total_score) AS max_score
FROM Tmp
GROUP BY Name,id
ORDER BY max_score DESC
Try this. Hope this will help.
WITH Tmp AS
(
SELECT Id,
Name,
SUM(Score) AS total_score
FROM Mytable
GROUP BY Id,
NAME
)
SELECT Name, Id,
MAX(total_score) AS max_score
FROM Tmp
GROUP BY Name,id
ORDER BY max_score DESC
Note:- If we are using aggregate function then we have to use other column as Group By....
In your case you are using SUM(Score) as aggregate function then we to use other column as Group by ...
I am not sure about performance of below query but we can use window functions to get maximum value from data partition.
SELECT
Id,
Name,
SUM(Score) AS total_score,
MAX(SUM(Score)) OVER(Partition by Name) AS max_score
FROM Mytable
GROUP BY Id, Name;
Tested -
declare #Mytable table (id int, name varchar(10), score int);
insert into #Mytable values
(1,'abc', 100),
(2,'abc', 200),
(3,'def', 300),
(3,'def', 400),
(4,'pqr', 500);
Output -
Id Name total_score max_score
1 abc 100 200
2 abc 200 200
3 def 700 700
4 pqr 500 500
You can select DENSE_RANK() with total_score column and then select records with Rank = 1. This will work for those also when there are multiple Name which are having same total_score.
WITH Tmp AS
(SELECT Id,
Name,
SUM(Score) AS total_score
FROM Mytable
GROUP BY Id, Name)
SELECT Id,
Name,
total_score AS max_score
FROM (SELECT Id,
Name,
total_score,
DENSE_RANK() OVER (PARTITION BY Name ORDER BY total_score DESC) AS Rank
FROM Tmp) AS Tmp2
WHERE Rank = 1
You can try this as well:
select id,name,max(total_score) over (partition by name) max_score from (
select id,name,sum(score) as total_score from YOURTABLE
group by id,name
) t

Sum column but for each row

How to do like
and I want to have result like
and what I already try is like this
select no, student, sum(point) from student group by no, student
But the result is not like what I expected.
You appear to want a "running sum" e.g.
select
no
, student
, sum(point) over(partition by student order by no) run_sum
from student
and it seems that the extra point might a subtraction of the 2nd last running sum from the final like this.
WITH cte
AS (
SELECT
no
, student
, SUM(point) OVER (PARTITION BY student ORDER BY no) run_sum
, ROW_NUMBER() OVER (PARTITION BY student ORDER BY no DESC) rn
FROM student)
SELECT
t.*
, coalesce(t.rum_sum,0) - coalesce(ex.run_sum,0) AS extra_point
FROM cte t
LEFT JOIN (
SELECT
*
FROM cte
WHERE rn = 2) ex ON t.student = ex.student
AND t.rn = 1
AND ex.rn = 2
;
but without more guidance on the required logic for extra_point that is just a guess.

Dense_Rank with Case statement is not giving Rank with Date

I have an issue while using Dense_Rank with CASE Statement. Below is the
sample table screenshot
So my requirement is two provide a Rank to every employee based on Emp_Dep_id
Req 1-->If Emp_Dep_id is same give same rank
Req 2-->If Emp_Dep_id is null then give same rank only when Emp_Joining_Date and Emp_Country is same
Below is the code to give rank
Select case
when Emp.Emp_Dep_Id IS NULL
then
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc,
Emp.Emp_Joining_Date desc,Emp.Emp_Country)
else
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc)
end as
rnk ,*
from Employee Emp with (nolock)
Below is Output-->
So,I am facing two issues-
Why Rank is skipping if same ranks is there ex- after second rank why sixth rank is coming next
I want to give rank basis of Emp_Joining_Date Currently it is behaving like firstly it is assigning rank if Emp_Dep_Id is not null after that it is continuing for Emp_Dep_Id is null.
I want to get the rank based on latest Emp_Joining_Date means joining date with 2016 with null should come first
Thanks Guys for your valuable response,I fixed my issue by doing this way
1. Step 1
Select case
when Emp.Emp_Dep_Id IS NULL
then
DENSE_RANK() over (order by Emp.Emp_Joining_Date,Emp.Emp_Country)
else
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc)
end as
rnk ,*
into #Emp_Output_Tbl
from Employee Emp
Select * from #Emp_Output_Tbl order by Emp_Joining_date desc
--Step 2
Select distinct rnk,Emp_Joining_date into #Emp_New_Tbl from #Emp_Output_Tbl order by Emp_Joining_date desc
Select * from #Emp_New_Tbl order by Emp_Joining_Date desc
Select * from #Emp_Output_Tbl order by Emp_Joining_Date desc
--Step 3
Select * from #Emp_Output_Tbl where rnk in(
Select TOP 5 rnk from #Emp_New_Tbl
)
order by Emp_Joining_Date desc
**Output as per expectation**
I hope this is going to help
I think you want this logic:
You want a single dense_rank(). The trick is to get the logic into the order by clause.
I think this is what you want:
Select dense_rank() over (order by Emp.Emp_Dep_Id,
(case when Emp.Emp_Dep_Id IS NULL then Emp.Emp_Joining_Date end) desc,
(case when Emp.Emp_Dep_Id IS NULL then Emp.Emp_Country end) desc
)
The Dense rank is working as per the result set & the number of records in it. Please refer the link given
https://msdn.microsoft.com/en-IN/library/ms173825.aspx?
use the below query to know the result better. Also try to use order by after the SQL statement to order it properly.
Select
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc,
Emp.Emp_Joining_Date desc,Emp.Emp_Country),
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc),
case
when Emp.Emp_Dep_Id IS NULL
then
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc,
Emp.Emp_Joining_Date desc,Emp.Emp_Country)
else
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc)
end as
rnk ,*
from Employee Emp with (nolock)
Could you please use the below query whether your requirement is met or not. I believe the dense range will operate as your requirement. You don't need to add a extra case statement. Only thing you need to specify is the order of the column. It automatically handle as you required.
Select
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc,Emp.Emp_Joining_Date desc,Emp.Emp_Country) as rnk ,*
from Employee Emp with (nolock)
Req 1-->If Emp_Dep_id is same give same rank
Req 2-->If Emp_Dep_id is null then give same rank only when
Emp_Joining_Date and Emp_Country is same
Thanks Guys for your valuable response,I fixed my issue by doing this way
1. Step 1
Select case
when Emp.Emp_Dep_Id IS NULL
then
DENSE_RANK() over (order by Emp.Emp_Joining_Date,Emp.Emp_Country)
else
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc)
end as
rnk ,*
into #Emp_Output_Tbl
from Employee Emp
Select * from #Emp_Output_Tbl order by Emp_Joining_date desc
--Step 2
Select distinct rnk,Emp_Joining_date into #Emp_New_Tbl from #Emp_Output_Tbl order by Emp_Joining_date desc
Select * from #Emp_New_Tbl order by Emp_Joining_Date desc
Select * from #Emp_Output_Tbl order by Emp_Joining_Date desc
--Step 3
Select * from #Emp_Output_Tbl where rnk in(
Select TOP 5 rnk from #Emp_New_Tbl
)
order by Emp_Joining_Date desc
**Output as per expectation**

To get the only 5th record from a table in ORACLE

I have a table named Stud in which I have a column to store the total. Now I need to find the 5th largest total in total column. How to perform this operation?
Try this:
SELECT total FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY total DESC) as RN, total FROM Stud
) T
WHERE RN=5
Select total
From
(SELECT total,
row_number() over(order by total desc) as rn
From totalTable
)Z
Where rn=5
Also can be done using ROWNUM pseudocolumn
Select total
From
(SELECT total
FROM totalTable
ORDER BY total desc
)Z
Where ROWNUM=5