I have this table contains student id and their score for each
|student id |score|
| aac | 3 |
| aaa | 6 |
| aac | 5 |
| aaa | 7 |
| aad | 3 |
I want to find the highest score for each student. How do I do it?
I tried going through every student ID on the list but it is not efficient.
For the exact table you gave, a simple group by query should work:
SELECT student_id, MAX(score) AS max_score
FROM yourTable
GROUP BY student_id;
You can use window function row_number
select
student_id,
score
from
(
select
*,
row_number() over (partition by student_id order by score desc) as rn
from yourTable
) subq
where rn = 1
Related
I working on a query for SQL Server 2016. I have order by serial_no and group by pay_type and I would like to add row number same example below
row_no | pay_type | serial_no
1 | A | 4000118445
2 | A | 4000118458
3 | A | 4000118461
4 | A | 4000118473
5 | A | 4000118486
1 | B | 4000118499
2 | B | 4000118506
3 | B | 4000118519
4 | B | 4000118521
1 | A | 4000118534
2 | A | 4000118547
3 | A | 4000118550
1 | B | 4000118562
2 | B | 4000118565
3 | B | 4000118570
4 | B | 4000118572
Help me please..
SELECT
ROW_NUMBER() OVER(PARTITION BY paytype ORDER BY serial_no) as row_no,
paytype, serial_no
FROM table
ORDER BY serial_no
You can assign groups to adjacent pay types that are the same and then use row_number(). For this purpose, the difference of row numbers is a good way to determine the groups:
select row_number() over (partition by pay_type, seqnum - seqnum_2 order by serial_no) as row_no,
t.*
from (select t.*,
row_number() over (order by serial_no) as seqnum,
row_number() over (partition by pay_type order by serial_no) as seqnum_2
from t
) t;
This type of problem is one example of a gaps-and-islands problem. Why does the difference of row numbers work? I find that the simplest way to understand is to look at the results of the subquery.
Here is a db<>fiddle.
add this to your select list
ROW_NUMBER() OVER ( ORDER BY (SELECT 1) )
since you already sorting by your stuff, so you don't need to sorting in your windowing function so consuming less CPU,
Given the following table P_PROV
+----+-----------+-----------+
| id | date | person_id |
+----+-----------+-----------+
| 1 |19/06/2019 | 1 |
| 2 |18/07/2010 | 2 |
| 3 |19/06/2020 | 1 |
| 4 |17/06/2020 | 2 |
| 5 |28/06/2020 | 3 |
+----+-----------+-----------+
I want this output
+----+-----------+-----------+
| id | date | person_id |
+----+-----------+-----------+
| 3 |19/06/2020 | 1 |
| 4 |17/06/2020 | 2 |
| 5 |28/06/2020 | 3 |
+----+-----------+-----------+
Putting this in words, I want to return per person the maximum date. I tried something like this
SELECT DISTINCT pp.date, pp.id FROM P_PROV pp
WHERE (SELECT MAX(aa.date)
FROM P_PROV aa) = pp.date;
This one is only returning one row (of course, because the MAX will return the maximum date only), but I really don't know how to approach this issue, any kind of help would be appreciated
ROW_NUMBER provides one way to handle this:
SELECT id, date, person_id
FROM
(
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY person_id ORDER BY date DESC) rn
FROM yourTable t
) t
WHERE rn = 1;
Oracle has a fun way to do this using aggregation:
select max(id) keep (dense_rank first order by date desc) as id,
max(date) as date, person_id
from P_PROV
group by person_id;
Given that your ids are increasing, this probably also does what you want:
select max(id) as id, max(date) as date, person_id
from P_PROV
group by person_id;
My table looks like this, what I'm trying to achieve is to pull out all the records for one user for the product that have the earliest date
product |type_id| user | Date |Desired ROW_NUMBER as output |
-------+--------+------+-------+---------------------
1 | 1 | A | 0101 | 1
1 | 1 | A | 0102 | 1
2 | 3 | A | 0105 | 2
2 | 5 | A | 0105 | 2
3 | 7 | B | 0101 | 1
3 | 8 | B | 0104 | 1
So I want to pull all the records with "1" in the desired row_num column, but I haven't figured out hot to get this without doing another group by. Any helps would be appreciated.
You can use window functions:
select t.*
from (select t.*,
rank() over (partition by user order by min_date) as seqnum
from (select t.*,
min(date) over (partition by user, product) as min_date
from t
) t
) t
where seqnum = 1;
Or, with only one subquery:
select t.*
from (select t.*,
min(date) over (partition by user, product) as min_date_up,
min(date) over (partition by user) as min_date_u
from t
) t
where min_date_u = min_date_up;
You can interpret this as "return all rows where the product has the minimum date for the user".
Here is a db<>fiddle.
SELECT * FROM [tableName] WHERE Desired ROW_NUMBER = 1 ORDER BY Date[DESC, ASC]
Pass the Desired ROW_NUMBER value dynamically as a parameter.
A little trickier than just getting the top # per category. I want the top 2 videos per artist, per day per country.
My code, which didn't give me the right results is:
Select *
From
(
Select t2.*, dense_rank() over(partition by artist order by views desc)
From
(select country, day, artist, song, sum(view) as views
From t1
Group by 1,2,3,4
) t2
)
Where rn >=5
Sample data results
| Country | Date | artist | video | views | rn |
|---------|------|----------|-------|-------|----|
| US | Jan1 | Beyonce | ab | 100 | 1 |
| US | Jan1 | Beyonce | ac | 99 | 2 |
| US | Jan2 | C. Brown | ad | 89 | 1 |
| US | Jan2 | C. Brown | ai | 103 | 2 |
| AU | Jan1 | Beyonce | bf | 99 | 1 |
| AU | Jan1 | Beyonce | bb | 89 | 2 |
I want all artists per day, per country but only 10 videos per artist..
I am kind confused as to how to achieve this..
I generally struggle when it comes to window functions, so I would appreciate any help.
I am using Amazon Redshift
Thanks
You need to partition by all of the columns you mentioned since you are ranking views within each combination of these elements.
Because you've renamed the aggregate column as "views", you need to call it by that name.
Finally, if you want the top 2 videos/songs, use this condition: where rn <= 2
Select *
From
(
Select t2.*, dense_rank() over(partition by country, day, artist order by views desc)
From
(select country, day, artist, song, sum(views) as views
From t1
Group by 1,2,3,4
) t2
)
Where rn <= 2
This will rank per artist per day the views and show the two two for each artist per day
Select *
From
(
Select t2.*, ROW_NUMBER() over(partition by artist, day, country order by views desc) as rn
From t1 t2
)
Where rn <= 2
I have a question about writing a sub-query in Microsoft T-SQL. From the original table I need to return the name of the person with the second most pets. I am able to write a query that returns the number of perts per person, but I'm not sure how to write a subquery to return rank #2.
Original table:
+—————————-——+———-————-+
| Name | Pet |
+————————————+————-————+
| Kathy | dog |
| Kathy | cat |
| Nick | gerbil |
| Bob | turtle |
| Bob | cat |
| Bob | snake |
+—————————-——+—————-———+
I have the following query:
SELECT Name, COUNT(Pet) AS NumPets
FROM PetTable
GROUP BY Name
ORDER BY NumPets DESC
Which returns:
+—————————-——+———-————-+
| Name | NumPets |
+————————————+————-————+
| Bob | 3 |
| Kathy | 2 |
| Nick | 1 |
+—————————-——+—————-———+
You are using TSQL So:
WITH C AS (
SELECT COUNT(Pet) OVER (PARTITION BY Name) cnt
,Name
FROM PetTable
)
SELECT TOP 1 Name, cnt AS NumPets
FROM C
WHERE cnt = 2
The ANSI standard method is:
OFFSET 1 FETCH FIRST 1 ROW ONLY
However, most databases have their own syntax for this, using limit, top or rownum. You don't specify the database, so I'm sticking with the standard.
This is how you could use ROW_NUMBER to get the result.
SELECT *
FROM(
SELECT ROW_NUMBER() OVER (ORDER BY COUNT(name) DESC) as RN, Name, COUNT(NAME) AS COUNT
FROM PetTable
GROUP BY Name
) T
WHERE T.RN = 2
In MSSQL you can do this:
SELECT PetCounts.Name, PetCounts.NumPets FROM (
SELECT
RANK() OVER (ORDER BY COUNT(Pet) DESC) AS rank,
Name, COUNT(Pet)as NumPets
FROM PetTable
GROUP BY Name
) AS PetCounts
WHERE rank = 2
This will return multiple rows if they have the same rank. If you want to return just one row you can replace RANK() with ROW_NUMBER()