create pivot table using sql - sql

I have a database called player.db
These database has two tables.
The tables called person and the other is called match.
person table is
Player_ID
Player
Country
1
Lionel Messi
Argentina
2
Luis Suarez
Uruguay
3
Neymar
Brazil
match table is
Match _ID
Game
Player_ID
Date
Season
1
Uruguay-Paraguay
2
5/3/2019
1
2
Uruguay-Chile
2
19/3/2019
1
3
Argentina-Chile
1
22/3/2019
1
4
Brazil-Guyana
3
3/4/2019
1
5
Brazil-USA
3
1/6/2020
2
6
Brazil-Belize
3
3/7/2020
2
7
Brazil-Suriname
3
5/7/2020
2
8
Argentina-USA
1
8/8/2020
2
9
Argentina-Canada
1
3/3/2021
3
10
Argentina-Grenada
1
8/3/2021
3
11
Uruguay-Suriname
2
7/4/2021
3
12
Uruguay-Mexico
2
2/2/2022
4
13
Uruguay-Jamaica
2
4/2/2022
4
14
Brazil-Ecuador
3
5/2/2022
4
My pivot table should look like these:
Season
Player
1
Luis Suarez
2
Neymar
3
Lionel Messi
4
Luis Suarez
I want a sql code which create a pivot table which shows which player played most with topscore in which season year. For example Luis Suarez occured most in season 1.
I started coding in sql, but got not the desired solution
SELECT Player_ID, COUNT(*)FROM match GROUP BY Player_ID HAVING COUNT(*) max
The problem is I got an error and it doesn't create a pivot table which show which player played most in which season.

Join the tables, group by season and player to get the number of matches for each player and use FIRST_VALUE() window function to pick the top player of each season:
SELECT DISTINCT m.Season,
FIRST_VALUE(p.Player) OVER (PARTITION BY m.Season ORDER BY COUNT(*) DESC) Player
FROM match m INNER JOIN person p
ON p.Player_ID = m.Player_ID
GROUP BY m.Season, m.Player_ID;
See the demo.

Your clause HAVING count(*) max may cause the error.
The solution may depend on the relational data base you use. Here is a solution with postgresql :
SELECT DISTINCT ON (Season)
m.season, p.player, count(*) AS count
FROM match AS m
INNER JOIN player AS p ON p.player_ID = m.Player_ID
GROUP BY m.Season, p.Player_ID, p.player
ORDER BY m.Season ASC, count DESC
see dbfiddle

Related

How to add a condition to count function in PostgreSQL

I have these tables Course, subscription,subscription_Course(A table that creates a relation between Course and subscription), and another with Student. I want to Select all the id_courses that have a subscription count higher than 1 but only want to count the subscriptions from different students. Example: If a Student Subscribes two times the same course I want to have a condition that enables the count function to not count more than one time in these cases
These are my tables:
Student:
idStudent(pk)
cc
nif
1
30348507
232928185
2
30338507
231428185
3
30438507
233528185
4
30323231
3232132
Subscription
idsubscription(pk)
Student(fk)
value_subscription
vouchercurso
date
1
1
100
null
2021-11-01
2
2
150
null
2021-12-11
3
3
160
null
2021-01-03
4
4
500
null
1996-11-07
5
1
900
null
2001-07-05
6
2
432
null
2021-05-09
Subscription_Course
idsubscription(PK/fk)
id_Course(pk/fk)
Grade
1
3
9
2
4
15
3
5
12
6
3
9
5
4
16
2
6
20
6
5
4
For example, when counting within my table Subscription_Course only the id_course:5 would have a count higher than 1 because 3 and 4 have a subscription from the same student.
I have this query for now:
Select id_Course
From Subscription_Course
Group by id_Course
Having Count (id_Course)>1
I don't know what to do to add this condition to the count.
seems like you need to join to Subscription and count unique Student id's:
select id_Course
from Subscription_Course sc
join Subscription s
on s.idsubscription = sc.idsubscription
group by id_Course
having Count(distinct Studentid)>1
You can join the Subscription_Course table with the Subscription table in order to access the id_Student column. Then just count the distinct id_Student values for each id_Course value.
SELECT
Subscription_Course.id_Course,
COUNT(DISTINCT Subscription.id_Student) AS student_count
FROM Subscription_Course
INNER JOIN Subscription
ON Subscription_Course.id_Subscription = Subscription.id_Subscription
GROUP BY Subscription_Course.id_Course
HAVING COUNT(DISTINCT Subscription.id_Student) > 1
ORDER BY student_count DESC;
With result:
id_course | student_count
-----------+---------------
3 | 2
4 | 2
5 | 2

Delete Group of Records based on Group Statistic

I have the following two example tables
Teams
Team ID
Team Name
1
Bears
2
Tigers
3
Lions
4
Sharks
Players
Player ID
Name
Team ID
Playtime
1
John
1
5
2
Adam
1
4
3
Smith
1
5
4
Michelle
2
5
5
Stephanie
2
10
6
David
2
10
7
Courtney
2
2
8
Frank
2
7
9
Teresa
2
1
10
Michael
3
3
11
May
4
1
12
Daniel
4
1
13
Lisa
4
4
I need a select statement with the following criteria
Select all teams with less than 4 players
Figure out the total playtime for all players on those teams and sort the resulting table based on this in descending order
Based on the example given, I would want a table that looks like this:
Teams
Team Name
Num Players
Total Playtime
Bears
3
14
Sharks
3
6
Lions
1
3
Finally, I want to cut all even rows from the previous table, so the final table would look like:
Team Name
Num Players
Total Playtime
Bears
3
14
Lions
1
3
You may try the following:
Query #1
SELECT
t."Team Name",
COUNT(p."Player ID") as "Num Players",
SUM(p."Playtime") as "Total Playtime"
FROM
teams t
LEFT JOIN
players p ON t."Team ID"=p."Team ID"
GROUP BY
t."Team Name"
HAVING
COUNT(p."Player ID") < 4
ORDER BY
SUM(p."Playtime") DESC;
Team Name
Num Players
Total Playtime
Bears
3
14
Sharks
3
6
Lions
1
3
Query #2
SELECT
t1."Team Name",
t1."Num Players",
t1."Total Playtime"
FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY SUM(p."Playtime") DESC) as rn,
t."Team Name",
COUNT(p."Player ID") as "Num Players",
SUM(p."Playtime") as "Total Playtime"
FROM
teams t
LEFT JOIN
players p ON t."Team ID"=p."Team ID"
GROUP BY
t."Team Name"
HAVING
COUNT(p."Player ID") < 4
) t1
WHERE MOD(rn,2)=1
ORDER BY rn;
Team Name
Num Players
Total Playtime
Bears
3
14
Lions
1
3
View on DB Fiddle
Let me know if this works for you.

How to count distinct a field cumulatively using recursive cte or other method in SQL?

Using example below, Day 1 will have 1,3,3 distinct name(s) for A,B,C respectively.
When calculating distinct name(s) for each house on Day 2, data up to Day 2 is used.
When calculating distinct name(s) for each house on Day 3, data up to Day 3 is used.
Can recursive cte be used?
Data:
Day
House
Name
1
A
Jack
1
B
Pop
1
C
Anna
1
C
Dew
1
C
Franco
2
A
Jon
2
B
May
2
C
Anna
3
A
Jon
3
B
Ken
3
C
Dew
3
C
Dew
Result:
Day
House
Distinct names
1
A
1
1
B
1
1
C
3
2
A
2 (jack and jon)
2
B
2
2
C
3
3
A
2 (jack and jon)
3
B
3
3
C
3
Without knowing the need and size of data it'll be hard to give an ideal/optimal solution. Assuming a small dataset needing a quick and dirty way to calculate, just use sub query like this...
SELECT p.[Day]
, p.House
, (SELECT COUNT(DISTINCT([Name]))
FROM #Bing
WHERE [Day]<= p.[Day] AND House = p.House) DistinctNames
FROM #Bing p
GROUP BY [Day], House
ORDER BY 1
There is no need for a recursive CTE. Just mark the first time a name is seen in a house and use a cumulative sum:
select day, house,
sum(sum(case when seqnum = 1 then 1 else 0 end)) over (partition by house order by day) as num_unique_names
from (select t.*,
row_number() over (partition by house, name order by day) as seqnum
from t
) t
group by day, house

Cross Join for Missing table (Select all and Select or Insert the Missing Row Only)

I have two table and have to fill in a list of missing values in one of the table based on the other one. First table has student's information and the second table has Grade related info, Grade and Grade description.
Table One
ID Name yearWithUs Grade Course Level
1 Jim 2004 4 4
2 Jim 2004 4 1
2 Jim 2003 3 3
4 Jim 2002 2 3
4 Jim 2002 2 1
3 Jim 2001 1 2
3 Jim 2001 1 1
Table two -- logic is.. A Student in a higher Course Level can change to a lower Course Level at anytime during the semester. And It can only go downward 1 level at a time. Example: Jim in his second grade first was assigned to attend course in level 3. He need to attend course in level 2 first before he can attend course in level 1. Means. Row for course level 2 at jim's first grade is missing.
Table Two
ID Grade Grade_Desc Course Level Course Desc
1 1 First Grade 1 Basic
2 1 First Grade 2 Normal
3 1 First Grade 3 Hard
4 1 First Grade 4 Expert
5 2 Second Grade 1
6 2 Second Grade 2
7 2 Second Grade 3
8 2 Second Grade 4
. . .
. . .
. . .
Logic of Table Two
ID Grade Grade_Desc Course Level Possible Move
1 1 First Grade 1 Null
2 1 First Grade 2 1
3 1 First Grade 3 2
4 1 First Grade 4 3
Ouptput one ... how to use select statement to return Jim's Grade?
ID Name Grade_Desc Grade yerWithUs Course Level
1 Jim Fourth Grade 4 2004 4
2 Jim Fourth Grade 4 2004 3
3 Jim Fourth Grade 4 2004 2
4 Jim Fourth Grade 4 2004 1
5 Jim Third Grade 3 2003 3
6 Jim Second Grade 2 2002 3
7 Jim Second Grade 2 2002 2
8 Jim Second Grade 2 2002 1
9 Jim First Grade 2 2001 2
10 Jim First Grade 2 2001 1
Output Two..How to retrieve only the missing row into a new temp table?
ID Name Grade_Desc Grade yearWithUs Course Level
2 Jim Fourth Grade 4 2004 3
3 Jim Fourth Grade 4 2004 2
7 Jim Second Grade 2 2002 2
I am currently is using a messy Cursor Statement to do it. The structure looks really messy and hard to debug return errors. I did a lot of research, and saw people use Cross Join to fill the missing portion which looks really clean (See example below)... I have tried the script it myself in many different way by using the cross join example below...obviously, I failed. I found a similar question in stackoverflow..but I am not able to understand how does it work and why without looking at the data....I need help to understand how to use cross join to rerun missing row? and I am open to any other possible solution.
"SELECT calendar.Date,
Category.Cat,
Score = ISNULL(Scores.Score, 0)
FROM Calendar
CROSS JOIN Catogory
LEFT JOIN Scores
ON Scores.Cat = Category.Cat
AND Scores.Date = Calendar.Date
WHERE Calendar.DayOfMonth = 1;"
Inserting missing rows with a join
Thank You
This will produce that output:
select distinct name, grade, Grade_Desc
from one
cross join two
If select is all you want then:
Select row_number() over(order by (select 1)) as id, * from
(Select distinct name from t1)t1
cross join t2
Here is fiddle http://sqlfiddle.com/#!6/a8a42/3
Try this out:
Create #Temp
DECLARE #Name VARCHAR(100) = 'Jim'
SELECT ROW_NUMBER() OVER (ORDER BY B.Grade DESC,B.CourseLevel DESC) ID,
A.Name,
B.Grade_Desc,
B.Grade,
A.YearWithUs,
B.[Course Level]
INTO #temp
FROM
(
SELECT DISTINCT Name,YearWithUs,Grade
FROM TableOne
WHERE Name = #Name
) A
INNER JOIN TableTwo B
ON A.Grade = B.Grade
Output One
SELECT *
FROM #temp
Output Two into #OutputTwo(temp table)
SELECT A.* INTO #OutputTwo
FROM #temp A
LEFT JOIN TableOne B
ON A.Grade = B.Grade
AND A.[Course Level] = B.[Course Level]
WHERE A.Grade IS NULL AND A.[Course Level] IS NULL

SQL Server: how to get this result from this table (example inside)

I would like to ask you guys how would you do a query to show the data of this table:
week name total
==== ====== =====
1 jon 15.2
1 jon 10
1 susan 10
1 howard 9
1 ben 10
3 ben 30
3 susan 10
3 mary 10
5 jon 10
6 howard 12
7 tony 25.1
8 tony 7
8 howard 10
9 susan 6.2
9 howard 9
9 ben 10
11 howard 10
11 howard 10
like this:
week name total
==== ====== =====
1 ben 10
1 howard 9
1 jon 25.2
1 mary 0
1 susan 10
1 tony 0
3 ben 30
3 howard 0
3 jon 0
3 mary 10
3 susan 10
3 tony 0
5 ben 0
5 howard 0
5 jon 10
5 mary 0
5 susan 0
5 tony 0
6 ben 0
6 howard 12
6 jon 0
6 mary 0
6 susan 0
6 tony 0
7 ben 0
7 howard 0
7 jon 0
7 mary 0
7 susan 0
7 tony 25.1
8 ben 0
8 howard 10
8 jon 0
8 mary 0
8 susan 0
8 tony 7
9 ben 10
9 howard 9
9 jon 0
9 mary 0
9 susan 6.2
9 tony 0
11 ben 0
11 howard 20
11 jon 0
11 mary 0
11 susan 0
11 tony 0
I tried something like:
select t1.week_id ,
t2.name ,
sum(t1.total)
from xpto as t1 ,
xpto as t2
where t1.week_id = t2.week_id
group by t1.week_id, t2.name
order by t1.week_id, t2.name
But I'm failing to understand the "sum" part and I can't figure out why...
Any help would be very appreciated. Thanks in advance, and sorry for my english.
you might try something like the following:
select week = w.week ,
name = n.name ,
sum_total = coalesce( sum( d.total ) , 0 )
from ( select distinct week from my_table ) w
cross join ( select distinct name from my_table ) n
left join my_table d on d.week = w.week
and d.name = n.name
group by w.week ,
n.name
order by 1,2
The cross join of first two derived tables constructs their cartesian product: all week and all names from the table, regardless of whether or not a particular week/name combination exists.
We then take that, join it against the actual data rows and summarize it, using coalesce() to collapse any null results from the aggregate function sum() to 0.
As I understood you you want to show all weeks and all names across all table regardless whether they were entered for the week or not. To do so you will need to first build a list of all names and week, cross join them and then join them to the list of totals, like this:
;with names as (select distinct name from xpto),
weeks as (select distinct week from xpto),
totals as (select week, name, sum(total) as total
from xpto group by week, name)
select w.week, n.name, coalesce(total, 0) as total
from names n cross join weeks w
left join totals t on t.name=n.name and w.week = t.week
order by 1,2
SQL Fiddle
I've edited my answer because I now understand what you want to do a bit better.
I prefer doing things in several steps rather than trying to do several transformations of data with a single join or subquery. So I would approach this like this:
;
with Weeks as (
select distinct Week_id
from xpto
)
, Names as (
select distinct Name
from xpto
)
, Scores as (
select week_id
, name
, score = sum(t1.score)
from xpto t1
group by
t1.week_id
, t1.name
)
, WeeksAndNames as (
select week_id
, name
from Weeks
cross join Names
)
-- The final query!
select wan.week_id
, wan.name
, total = COALESCE(s.total,0)
from WeeksAndNames wan
left join Scores s
on wan.week_id = s.week_id
and wan.name = s.name
order by
wan.week_id
, wan.name
Lengthy, I'll grant you, and you can probably condense it. But this shows each step you need to go through to transform your data into the list you want.