Select by greatest sum, but without the sum in the result - sql

I need to select the top score of all combined attempts by a player and I need to use a WITH clause.
create table scorecard(
id integer primary key,
player_name varchar(20));
create table scores(
id integer references scorecard,
attempt integer,
score numeric
primary key(id, attempt));
Sample Data for scorecard:
id player_name
1 Bob
2 Steve
3 Joe
4 Rob
Sample data for scores:
id attempt score
1 1 50
1 2 45
2 1 10
2 2 20
3 1 40
3 2 35
4 1 0
4 2 95
The results would simply look like this:
player_name
Bob
Rob
But would only be Bob if Rob had scored less than 95 total. I've gotten so far as to have the name and the total scores that they got in two columns using this:
select scorecard.player_name, sum(scores.score)
from scorecard
left join scores
on scorecard.id= scores.id
group by scorecard.name
order by sum(scores.score) desc;
But how do I just get the names of the highest score (or scores if tied).
And remember, it should be using a WITH clause.

Who ever told you to "use a WITH clause" was missing a more efficient solution. To just get the (possibly multiple) winners:
SELECT c.player_name
FROM scorecard c
JOIN (
SELECT id, rank() OVER (ORDER BY sum(score) DESC) AS rnk
FROM scores
GROUP BY 1
) s USING (id)
WHERE s.rnk = 1;
A plain subquery is typically faster than a CTE. If you must use a WITH clause:
WITH top_score AS (
SELECT id, rank() OVER (ORDER BY sum(score) DESC) AS rnk
FROM scores
GROUP BY 1
)
SELECT c.player_name
FROM scorecard c
JOIN top_score s USING (id)
WHERE s.rnk = 1;
SQL Fiddle.
You could add a final ORDER BY c.player_name to get a stable sort order, but that's not requested.
The key feature of the query is that you can run a window function like rank() over the result of an aggregate function. Related:
Postgres window function and group by exception
Get the distinct sum of a joined table column

Can try something like follows.
With (SELECT id, sum(score) as sum_scores
FROM scores
group by id) as sumScoresTable,
With (SELECT max(score) as max_scores
FROM scores
group by id) as maxScoresTable
select player_name
FROM scorecard
WHERE scorecard.id in (SELECT sumScoresTable.id
from sumScoresTable
where sumScoresTable.score = (select maxScoresTable.score from maxScoresTable)

Try this code:
WITH CTE AS (
SELECT ID, RANK() OVER(ORDER BY SumScore DESC) As R
FROM (
SELECT ID, SUM(score) AS SumScore
FROM scores
GROUP BY ID )
)
SELECT player_name
FROM scorecard
WHERE ID IN (SELECT ID FROM CTE WHERE R = 1)

Related

How to select nth row of each group in SQL

I have a table like this
member_id
book_title
1
one-one
1
one-two
1
one-three
2
two-one
2
two-two
2
two-three
I want to group by member_id and 3rd book title, so if I run the query, the result must be:
member_id
book_title
1
one-three
2
two-three
Assuming with "3rd book title" you mean "the third book if sorted alphabetically by title" this can be achieved using window functions:
select member_id, book_title
from (
select member_id, book_title,
row_number() over (partition by member_id order by book_title) as rn
from the_table
) t
where rn = 3;
Note that this won't return members that have less than 3 books assigned.

Select row with max value from each group in Oracle SQL [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Closed 1 year ago.
I have table people containing people, their city and their money balance:
id city_id money
1 1 25
2 1 13
3 2 97
4 2 102
5 2 37
Now, I would like to select richest person from each city. How can I do that using Oracle SQL? Desired result is:
id city_id money
1 1 25
4 2 102
Something like that would be useful:
SELECT * as tmp FROM people GROUP BY city_id HAVING money = MAX(money)
You should be thinking "filtering", not "aggregation", because you want the entire row. You can use a subquery:
select p.*
from people p
where p.money = (select max(p2.money) from people p2 where p2.city_id = p.city_id);
You can use DENSE_RANK() analytic function through grouping by city_id(by using partition by clause) and descendingly ordering by money within the subquery to pick the returned values equal to 1 within the main query in order to determine the richest person including ties(the people having the same amount of money in each city) such as
SELECT id, city_id, money
FROM( SELECT p.*,
DENSE_RANK() OVER ( PARTITION BY city_id ORDER BY money DESC ) AS dr
FROM people p )
WHERE dr = 1
You can use RANK() as its flexible as you can get richest or top N richest
SELECT
id, city_id, money
FROM (
SELECT
p.* ,RANK() OVER (PARTITION BY city_id ORDER BY money DESC ) as rank_per_city
FROM
people p )
WHERE
rank_per_city = 1

SQL MIN(value) matching row in PostgreSQL

I have a following tables:
TABLE A:
ID ID NAME PRICE CODE
00001 B 1000 1
00002 A 2000 1
00003 C 3000 1
Here is the SQL I use:
Select Min (ID),
Min (ID NAME),
Sum(PRICE)
From A
GROUP BY CODE
Here is what I get:
ID ID NAME PRICE
00001 A 6000
As you can see, ID NAME don't match up with the min row value. I need them to match up.
I would like the query to return the following
ID ID NAME PRICE
00001 B 6000
What SQL can I use to get that result?
If you want one row, use limit or fetch first 1 row only:
select a.*
from a
order by a.price asc
fetch first 1 row only;
If, for some reason, you want the sum() of all prices, then you can use window functions:
select a.*, sum(a.price) over () as sum_prices
from a
order by a.price asc
fetch first 1 row only;
You can use row_number() function :
select min(id), max(case when seq = 1 then id_name end) as id_name, sum(price) as price, code
from (select t.*, row_number() over (partition by code order by id) seq
from table t
) t
group by code;
you can also use sub-query
select t1.*,t2.* from
(select ID,Name from t where ID= (select min(ID) from t)
) as t1
cross join (select sum(Price) as total from t) as t2
https://dbfiddle.uk/?rdbms=postgres_10&fiddle=a496232b552390a641c0e5c0fae791d1
id name total
1 B 6000

SQL to get unique rows in Netezza DB

I have a table with rows like:
id group_name_code
1 999
2 16
3 789
4 999
5 231
6 999
7 349
8 16
9 819
10 999
11 654
But I want output rows like this:
id group_name_code
1 999
2 16
3 789
4 231
5 349
6 819
7 654
Will this query help?
select id, distinct(group_name_code) from group_table;
You seem to want:
Distinct values for group_name_code and a sequential id ordered by minimum id per set of group_name_code.
Netezza has the DISTINCT key word, but not DISTINCT ON () (Postgres feature):
https://www.ibm.com/support/knowledgecenter/en/SSULQD_7.2.1/com.ibm.nz.dbu.doc/r_dbuser_select.html
You could:
SELECT DISTINCT group_name_code FROM group_table;
No parentheses, the DISTINCT key word does not require parentheses.
But you would not get the sequential id you show with this.
There are "analytic functions" a.k.a. window functions:
https://www.ibm.com/support/knowledgecenter/en/SSULQD_7.2.1/com.ibm.nz.dbu.doc/c_dbuser_overview_analytic_funcs.html
And there is also row_number():
https://www.ibm.com/support/knowledgecenter/en/SSULQD_7.2.1/com.ibm.nz.dbu.doc/r_dbuser_functions.html
So this should work:
SELECT row_number() OVER (ORDER BY min(id)) AS new_id, group_name_code
FROM group_table
GROUP BY group_name_code
ORDER BY min(id);
Or use a subquery if Netezza should not allow to nest aggregate and window functions:
SELECT row_number() OVER (ORDER BY id) AS new_id, group_name_code
FROM (
SELECT min(id) AS id, group_name_code
FROM group_table
GROUP BY group_name_code
) sub
ORDER BY id;
If you do not mind losing data on id you can use an aggregate function on that column and group by group_name_code:
select min(id) as id, group_name_code
from group_table
group by group_name_code
order by id;
This way you pull unique values for group_name_code and the lowest id for each code.
If you don't need id in your output (it seems like this doesn't correspond to input table) and just want the unique codes, try this:
select group_name_code
from p
group by group_name_code
order by id;
This gets the codes you want. If you want id to be the rownumber that will depend on which RDBMS you are using
you can get that result using CTE, replace #t with you table name and value with group_name_code
; WITH tbl AS (
SELECT DISTINCT value FROM #t
)
SELECT ROW_NUMBER() OVER (ORDER BY value) AS id,* FROM tbl

SQL - Find Differences Between Columns

Let's say I have the following table
Sku | Number | Name
11 1 hat
12 1 hat
13 1 hats
22 2 car
33 3 truck
44 4 boat
45 4 boat
Is there an easy way to figure out how to find the differences within each Number. For example, with the table above, I would want the query to output:
13 | 1 | hats
The reason for this is because our program processes the rows as long as the number matches the name. If there is an instance where the name doesn't match but the rest of the names do, it will fail.
You can find the most common value (the "mode") using window functions and aggregation:
select t.*
from (select number, name, count(*) as cnt,
row_number() over (partition by number order by count(*) desc) as seqnum
from t
group by number, name
) t
where seqnum = 1;
You could then find everything that is not the mode using a join. The easier way is just to change the where condition:
select t.*
from (select number, name, count(*) as cnt,
row_number() over (partition by number order by count(*) desc) as seqnum
from t
group by number, name
) t
where seqnum > 1;
Note: If there are ties in frequency for the most common value, then an arbitrary most common value is chosen.
EDIT:
Actually, if you want the original skus, you might as well do the join:
with modes as (
select t.*
from (select number, name, count(*) as cnt,
row_number() over (partition by number order by count(*) desc) as seqnum
from t
group by number, name
) t
where seqnum = 1
)
select t.*
from t join
modes
on t.number = modes.number and t.name <> modes.name;
This will ignore NULL values (but the logic can easily be fixed to accommodate them).