calculating the first three HIGHEST GPA of each school - sql

I have calculated the HIGHEST GPA scored by student from each school. But how can I get the list of top 3 GPA, I mean RANK 1, 2, and 3 scored by students from each school ?
SELECT A.*
FROM (
SELECT B.SCHOOL_NAME
,C.STUDENT_NAME
,A.SYMBOL_NO
,A.AVG_GPA
,MAX(A.AVG_GPA) OVER (PARTITION BY B.SCHOOL_NAME) AS MAX_GPA
FROM TBL_STUDENT_MARKS A
INNER JOIN TBL_SCHOOL B ON A.SCHOOL_ID = B.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO C ON A.SYMBOL_NO = C.SYMBOL_NO
AND B.SCHOOL_ID = C.SCHOOL_ID
) A
WHERE A.AVG_GPA = MAX_GPA
ORDER BY A.AVG_GPA DESC;

You can use either RANK() or DENSE_RANK() or ROW_NUMBER() to achieve the result.
However, RANK() and DENSE_RANK() are preferable for this scenario.
RANK():
SELECT A.*
FROM (
SELECT B.SCHOOL_NAME
,C.STUDENT_NAME
,A.SYMBOL_NO
,A.AVG_GPA
,RANK() OVER (PARTITION BY B.SCHOOL_NAME ORDER BY A.AVG_GPA DESC) AS SCHOOL_RANK
FROM TBL_STUDENT_MARKS A
INNER JOIN TBL_SCHOOL B ON A.SCHOOL_ID = B.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO C ON A.SYMBOL_NO = C.SYMBOL_NO AND B.SCHOOL_ID = C.SCHOOL_ID
) A
WHERE SCHOOL_RANK <= 3
ORDER BY SCHOOL_NAME, SCHOOL_RANK
DENSE_RANK():
SELECT A.*
FROM (
SELECT B.SCHOOL_NAME
,C.STUDENT_NAME
,A.SYMBOL_NO
,A.AVG_GPA
,DENSE_RANK() OVER (PARTITION BY B.SCHOOL_NAME ORDER BY A.AVG_GPA DESC) AS SCHOOL_DENSE_RANK
FROM TBL_STUDENT_MARKS A
INNER JOIN TBL_SCHOOL B ON A.SCHOOL_ID = B.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO C ON A.SYMBOL_NO = C.SYMBOL_NO AND B.SCHOOL_ID = C.SCHOOL_ID
) A
WHERE SCHOOL_DENSE_RANK<= 3
ORDER BY SCHOOL_NAME, SCHOOL_DENSE_RANK
ROW_NUMBER():
SELECT A.*
FROM (
SELECT B.SCHOOL_NAME
,C.STUDENT_NAME
,A.SYMBOL_NO
,A.AVG_GPA
,ROW_NUMBER() OVER (PARTITION BY B.SCHOOL_NAME ORDER BY A.AVG_GPA DESC) AS SCHOOL_ROW_NUMBER
FROM TBL_STUDENT_MARKS A
INNER JOIN TBL_SCHOOL B ON A.SCHOOL_ID = B.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO C ON A.SYMBOL_NO = C.SYMBOL_NO AND B.SCHOOL_ID = C.SCHOOL_ID
) A
WHERE SCHOOL_ROW_NUMBER <= 3
ORDER BY SCHOOL_NAME, SCHOOL_ROW_NUMBER
Please refer here for the difference between Rank, DenseRank and RowNumber

You can use DENSE_RANK() and PARTITION BY in combination:
DENSE_RANK() OVER (
PARTITION BY <expr1>[{,<expr2>...}]
ORDER BY <expr1> [ASC|DESC], [{,<expr2>...}]
)
In this case something like
DENSE_RANK() OVER (PARTITION BY B.SCHOOL_NAME ORDER BY AVG_GPA desc) AS SCHOOL_RANK
and then in the outer query you add
WHERE SCHOOL_RANK in (1, 2, 3)
Also
Give Common Table Expressions a look! I find it often nicer than nested SELECT.
WITH ranked as (
SELECT
SCHOOL_ID
, SYMBOL_NO
, AVG_GPA
, DENSE_RANK() OVER (PARTITION BY SCHOOL_ID ORDER BY AVG_GPA DESC) AS SCHOOL_RANK
FROM TBL_STUDENT_MARKS
)
SELECT
SC.SCHOOL_NAME
, INFO.STUDENT_NAME
, ST.SYMBOL_NO
, ST.AVG_GPA
, ST.SCHOOL_RANK
FROM ranked ST
INNER JOIN TBL_SCHOOL SC ON R.SCHOOL_ID = SC.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO INFO ON ST.SYMBOL_NO = INFO.SYMBOL_NO AND SC.SCHOOL_ID = INFO.SCHOOL_ID
WHERE SCHOOL_RANK <= 3
ORDER BY SCHOOL_NAME, SCHOOL_RANK;

You can use ROW_NUMBER() OVER (PARTITION BY B.SCHOOL_NAME ORDER BY A.AVG_GPA DESC)
SELECT A.*
FROM (
SELECT B.SCHOOL_NAME
,C.STUDENT_NAME
,A.SYMBOL_NO
,A.AVG_GPA
,ROW_NUMBER() OVER (PARTITION BY B.SCHOOL_NAME ORDER BY A.AVG_GPA DESC)
AS HIGHEST_GPA
FROM TBL_STUDENT_MARKS A
INNER JOIN TBL_SCHOOL B ON A.SCHOOL_ID = B.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO C ON A.SYMBOL_NO = C.SYMBOL_NO
AND B.SCHOOL_ID = C.SCHOOL_ID
) A
WHERE HIGHEST_GPA <= 3
ORDER BY A.AVG_GPA DESC;

Use Dense_rank window function:
select *
from(SELECT A.*,DENSE_RANK()over(partition by school_name order by MAX_GPA
desc)as rankno
FROM (
SELECT B.SCHOOL_NAME
,C.STUDENT_NAME
,A.SYMBOL_NO
,A.AVG_GPA
,MAX(A.AVG_GPA) OVER (PARTITION BY B.SCHOOL_NAME) AS MAX_GPA
FROM TBL_STUDENT_MARKS A
INNER JOIN TBL_SCHOOL B ON A.SCHOOL_ID = B.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO C ON A.SYMBOL_NO = C.SYMBOL_NO
AND B.SCHOOL_ID = C.SCHOOL_ID
) A
WHERE A.AVG_GPA = MAX_GPA )B
WHERE rankno IN(1,2,3);

Related

SQL - Finding entries that are the max of a count?

I have a table like the above image shown, how can I display the id and names of the customers and the category of the food that have the customer ordered the most?
SELECT Customer_ID, COUNT(F_Catg)
FROM ORDER_RECORD ORD
INNER JOIN FOOD_MENU FM
ON ORD.Item_ID = FM.Item_ID
GROUP BY Customer_ID
HAVING COUNT(F_Catg) =
(SELECT MAX(c) FROM
(SELECT COUNT(F_Catg) AS c
FROM ORDER_RECORD ORD
INNER JOIN FOOD_MENU FM
ON ORD.Item_ID = FM.Item_ID
GROUP BY Customer_ID))
I tried this but it doesn't work.
Your derived table is missing an alias. Fix that, and your query should work:
SELECT Customer_ID, COUNT(F_Catg)
FROM ORDER_RECORD ORD
INNER JOIN FOOD_MENU FM ON ORD.Item_ID = FM.Item_ID
GROUP BY Customer_ID
HAVING COUNT(F_Catg) = (SELECT MAX(c) FROM (
SELECT COUNT(F_Catg) AS c
FROM ORDER_RECORD ORD
INNER JOIN FOOD_MENU FM ON ORD.Item_ID = FM.Item_ID
GROUP BY Customer_ID) t -- fix is here
);
On MySQL 8+, you could simplify a bit by using the RANK analytic function:
WITH cte AS (
SELECT Customer_ID, COUNT(F_Catg) AS cnt,
RANK() OVER (ORDER BY COUNT(F_Catg) DESC) rnk
FROM ORDER_RECORD ORD
INNER JOIN FOOD_MENU FM ON ORD.Item_ID = FM.Item_ID
GROUP BY Customer_ID
)
SELECT Customer_ID, cnt
FROM cte
WHERE rnk = 1;

Select both row number and count from oracle

I'm trying to select records with pagination, and I need the total number of records so that I can display the number of records and pages on the UI.
The query I'm using is as below but it always returning the totalcount as 1.
WITH cteEmp AS
(SELECT e.empid, e.empname, d.deptid, d.deptname
FROM hr.Emp e
INNER JOIN hr.dept d ON e.deptid = d.deptid)
Select * from (SELECT row_number() over (order by hr.empid desc) rn, Count(*) totalcount,
C.empName FROM CTEPO C
LEFT JOIN hr.emphistory ON C.empid=hr.empid
GROUP BY c.empid,hr.empid) where rn>0 and rn<= 100
You can try this maybe it'll work for you:
(SELECT e.empid, e.empname, d.deptid, d.deptname
FROM hr.Emp e
INNER JOIN hr.dept d ON e.deptid = d.deptid)
Select * from (SELECT row_number() over (order by hr.empid desc) rn,
count(*) OVER (ORDER BY hr.empid desc ) AS totalcount
C.empName FROM CTEPO C
LEFT JOIN hr.emphistory ON C.empid=hr.empid
GROUP BY c.empid,hr.empid) where rn>0 and rn<= 100

Left join tables in sqlite ordered by desc limit

I want to left join two tables in sqlite. Short summary:
I have a table named "_Menu", one of the fields in this table is named "menu_id" with unique numbers.
Another table is called "_Approvals". This table has a history of which items has been "approved" or "unapproved". This table also has a field named "menu_id".
I want to get the lowest row (if there is any) from "_Approvals" for a given menu_id and join the two tables.
What I have so far is:
SELECT m.menu_id, m.p_id AS parent_id, m.name, m.url, a.status, a.auth
FROM _Menu AS m
LEFT JOIN (
SELECT * FROM _Approvals ORDER BY _Approvals.approval_id DESC LIMIT 1) as a
ON a.menu_id = m.menu_id
GROUP BY m.menu_id
ORDER BY m.menu_id
My problem is only the absolute last row in "_Approvals" gets joined. I.e I only know the status of the last item approved/unapproved.
Any help is greatly appreciated!
You want the lowest row (if there is any) from "_Approvals" for a given menu_id and not the the lowest row from "_Approvals" which is what your code does.
One way to do what you need is by using NOT EXISTS in the subquery that you join:
SELECT m.menu_id, m.p_id AS parent_id, m.name, m.url, a.status, a.auth
FROM _Menu AS m
LEFT JOIN (
SELECT * FROM _Approvals t
WHERE NOT EXISTS (
SELECT 1 FROM _Approvals
WHERE menu_id = t.menu_id AND approval_id > t.approval_id
)
) AS a
ON a.menu_id = m.menu_id
GROUP BY m.menu_id
ORDER BY m.menu_id
Another way with a CTE:
WITH cte AS (
SELECT t.* FROM _Approvals t
INNER JOIN (
SELECT menu_id, MAX(approval_id)
FROM _Approvals
GROUP BY menu_id
) g
ON g.menu_id = t.menu_id
)
SELECT m.menu_id, m.p_id AS parent_id, m.name, m.url, a.status, a.auth
FROM _Menu AS m LEFT JOIN cte AS a
ON a.menu_id = m.menu_id
GROUP BY m.menu_id
ORDER BY m.menu_id
Or with window function ROW_NUMBER() if your version of SQLite is 3.25.0+:
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY menu_id ORDER BY approval_id DESC) rn
FROM _Approvals
)
SELECT m.menu_id, m.p_id AS parent_id, m.name, m.url, a.status, a.auth
FROM _Menu AS m LEFT JOIN (
SELECT * FROM cte
WHERE rn = 1
) AS a
ON a.menu_id = m.menu_id
GROUP BY m.menu_id
ORDER BY m.menu_id
I would recommend writing this using row_number():
SELECT m.menu_id, m.p_id AS parent_id, m.name, m.url,
a.status, a.auth
FROM _Menu m LEFT JOIN
(SELECT a.*,
ROW_NUMBER() OVER (PARTITION BY a.menu_id ORDER BY a.approval_id DESC) as seqnum
FROM _Approvals a
) a
ON a.menu_id = m.menu_id AND a.seqnum = 1
ORDER BY m.menu_id;
No aggregation should be needed for the query, assuming that menu_id is the primary key in _Menu.
If you are using an old version of SQLite that doesn't support window functions, then there are several options. Probably the simplest is:
SELECT m.menu_id, m.p_id AS parent_id, m.name, m.url,
a.status, a.auth
FROM _Menu m LEFT JOIN
_Approvals a
ON a.menu_id = m.menu_id LEFT JOIN
(SELECT a.menu_id, MAX(a.approval_id) as max_approval_id
FROM _Approvals a
GROUP BY a.menu_id
) aa
ON aa.menu_id = a.menu_id AND
aa.max_approval_id = a.approval_id
ORDER BY m.menu_id;

I have created a query but it is not working

WITH rows AS
(
SELECT
*, ROW_NUMBER() OVER (ORDER BY dtDeliveryDate) AS rn
FROM
TCheckRecipient
)
SELECT
AVG(avarage)
FROM
(SELECT
TCheck.iCheckId,
AVG(DATEDIFF(minute, mc.dtDeliveryDate, mp.dtDeliveryDate)) AS average
FROM
rows mc
LEFT JOIN
TCheck ON TCheck.iCheckId = mc.iCheckId
JOIN
rows mp ON mc.rn = mp.rn - 1
GROUP BY
TCheck.iCheckId)
You have Typo error in AVG(avarage) , it should be average.
And also, remember to assign alias on your subquery.
WITH rows AS
(
SELECT
*, ROW_NUMBER() OVER (ORDER BY dtDeliveryDate) AS rn
FROM
TCheckRecipient
)
SELECT
AVG(avarage)
FROM
(SELECT
TCheck.iCheckId,
AVG(DATEDIFF(minute, mc.dtDeliveryDate, mp.dtDeliveryDate)) AS average
FROM
rows mc
LEFT JOIN
TCheck ON TCheck.iCheckId = mc.iCheckId
JOIN
rows mp ON mc.rn = mp.rn - 1
GROUP BY
TCheck.iCheckId) as A

Fetch DISTINCT Record with JOIN

I want to take a distinct patient records and show it in a grid
Patient Table
PatientId, FirstName, LastName, City
Booking Table
BookingId, PatientId, CategoryId, BookingDate
Running below query gives duplicate patient records. This is happening because I have 3 bookings for same patient for different date. This query makes join and brings 3 patient records instead of 1 patient record.
SELECT DISTINCT PAT.PatientId
,PAT.FirstName
,PAT.LastName
,ROW_NUMBER() OVER (ORDER BY PAT.PatientId DESC) AS RowNumber
INTO #itemSearch
FROM dbo.Patient AS PAT
INNER JOIN dbo.Booking AS B
ON PAT.PatientId = B.PatientId WHERE B.CategoryId = 1
If I remove this line I get only 1. But I need this temp table for other paging process.
ROW_NUMBER() OVER (ORDER BY PAT.PatientId DESC) AS RowNumber INTO #itemSearch
How can I get only 1 patient even though they have 3 bookings?
Use GROUP BY clause
SELECT PAT.PatientId
,PAT.FirstName
,PAT.LastName
,ROW_NUMBER() OVER (ORDER BY PAT.PatientId DESC) AS RowNumber
INTO #itemSearch
FROM dbo.Patient AS PAT
INNER JOIN dbo.Booking AS B
ON PAT.PatientId = B.PatientId
WHERE B.CategoryId = 1
GROUP BY PAT.PatientId, PAT.FirstName, PAT.LastName
OR use DISTINCT with DENSE_RANK() ranking function
SELECT DISTINCT PAT.PatientId
,PAT.FirstName
,PAT.LastName
,DENSE_RANK() OVER (ORDER BY PAT.PatientId DESC) AS RowNumber
INTO #itemSearch
FROM dbo.Patient AS PAT
INNER JOIN dbo.Booking AS B
ON PAT.PatientId = B.PatientId
WHERE B.CategoryId = 1