I have this query :
SELECT AVG(legs.avg) FROM legs INNER JOIN matchs ON matchs.id = legs.match_id WHERE matchs.player_id=4 GROUP BY match_id
Which allows me to get the average of the attribute "legs.avg".
The problem is that I get several results for this query, one for each matchs.id.
I need to get the average of these different results, so only one row with the total average.
Is that possible ?
One approach is to get the "average of averages":
SELECT avg(l_avg)
FROM (SELECT AVG(l.avg) as l_avg
FROM legs l INNER JOIN
matchs m
ON m.id = l.match_id
WHERE m.player_id = 4
GROUP BY l.match_id
) lm;
There are two other approaches with no subquery:
SELECT AVG(l.avg) as l_avg
FROM legs l INNER JOIN
matchs m
ON m.id = l.match_id
WHERE m.player_id = 4;
Or:
SELECT SUM(l.avg) / COUNT(DISTINCT l.match_id) as l_avg
FROM legs l INNER JOIN
matchs m
ON m.id = l.match_id
WHERE m.player_id = 4;
These do not return the same value. The first is the overall average and the second is weighted so each match has a weight of exactly 1. This is the same as the first first query, with the subquery.
Without sample data, it is not clear which version you really want.
Use below query :-
select avg(leg_avg) from (SELECT AVG(legs.avg) leg_avg FROM legs INNER JOIN matchs ON matchs.id = legs.match_id WHERE matchs.player_id=4 GROUP BY match_id) a11
Related
I have the following piece of code:
select sum(max(p.highest_market_value_in_eur)) as value, c.name from Transfermarket.dbo.players$ p
left join Transfermarket.dbo.player_valuations v
on p.player_id = v.player_id
inner join Transfermarket.dbo.competitions$ c
on c.competition_id = v.player_club_domestic_competition_id
where c.name = 'Ligue 1' and last_season = 2022
group by c.name;
Apparently, performing aggregate functions on expressions containing an aggregate doesn't work. Is there any other way? I suppose I could use a subquery, but I am not sure how to do that.
So you're going to want to do this in two steps.
If i am understanding your question correctly, you are wanting to find the maximum p.highest_market_value_in_eur for each player in the players table for a given c.name where the last_season is 2022. Then you want sum for all players in that competition.
Lets find our max first:
select
max(p.highest_market_value_in_eur) as value,
p.player_id as player_id
from
Transfermarket.dbo.players$ p
left join Transfermarket.dbo.player_valuations v on p.player_id = v.player_id
inner join Transfermarket.dbo.competitions$ c on c.competition_id = v.player_club_domestic_competition_id
where c.name = 'Ligue 1' and last_season = 2022
group by p.player_id;
The above query will find the max value of p.highest_market_value_in_eur for each player where the competition name is 'Ligue 1' and the last season is 2022. Notice that the first step here is grouping on a different value. This is because we want the max PER PLAYER - so we ask for the max and group by player.
Now that we've found the max for every player in Ligue 1 from 2022, lets calculate our sum to find 'Total Market Value in EUR for all players competing in Ligue 1 from 2022'. We'll do this by calling the sum() aggregate function on the results from our first query. we'll do this in the form of a subquery:
select
sum(value)
from
(select
max(p.highest_market_value_in_eur) as value,
p.player_id as player_id
from
Transfermarket.dbo.players$ p
left join Transfermarket.dbo.player_valuations v on p.player_id = v.player_id
inner join Transfermarket.dbo.competitions$ c on c.competition_id = v.player_club_domestic_competition_id
where c.name = 'Ligue 1' and last_season = 2022
group by p.player_id) max_per_player;
Hope this helps.
I'm making a select in which I give a year (hardcoded as 1981 below) and I expect to get one row per qualifying band. The main problem is to get the oldest living member for each band:
SELECT b.id_band,
COUNT(DISTINCT a.id_album),
COUNT(DISTINCT s.id_song),
COUNT(DISTINCT m.id_musician),
(SELECT name FROM MUSICIAN WHERE year_death IS NULL ORDER BY(birth)LIMIT 1)
FROM BAND b
LEFT JOIN ALBUM a ON(b.id_band = a.id_band)
LEFT JOIN SONG s ON(a.id_album = s.id_album)
JOIN MEMBER m ON(b.id_band= m.id_band)
JOIN MUSICIAN mu ON(m.id_musician = mu.id_musician)
/*LEFT JOIN(SELECT name FROM MUSICIAN WHERE year_death IS NULL
ORDER BY(birth) LIMIT 1) AS alive FROM mu*/ -- ??
WHERE b.year_formed = 1981
GROUP BY b.id_band;
I would like to obtain the oldest living member from mu for each band. But I just get the oldest musician overall from the relation MUSICIAN.
Here is screenshot showing output for my current query:
Well, I think you can follow the structure that you have, but you need JOINs in in the subquery.
SELECT b.id_band,
COUNT(DISTINCT a.id_album),
COUNT(DISTINCT s.id_song),
COUNT(DISTINCT mem.id_musician),
(SELECT m.name
FROM MUSICIAN m JOIN
MEMBER mem
ON mem.id_musician = m.id_musician
WHERE m.year_death IS NULL AND mem.id_band = b.id_band
ORDER BY m.birth
LIMIT 1
) as oldest_member
FROM BAND b LEFT JOIN
ALBUM a
ON b.id_band = a.id_band LEFT JOIN
SONG s
ON a.id_album = s.id_album LEFT JOIN
MEMBER mem
ON mem.id_band = b.id_band
WHERE b.year_formed = 1981
GROUP BY b.id_band
Following query will give you oldest member of each band group. You can put filter by year_formed = 1981 if you need.
SELECT
b.id_band,
total_albums,
total_songs,
total_musicians
FROM
(
SELECT b.id_band,
COUNT(DISTINCT a.id_album) as total_albums,
COUNT(DISTINCT s.id_song) as total_songs,
COUNT(DISTINCT m.id_musician) as total_musicians,
dense_rank() over (partition by b.id_band order by mu.year_death desc) as rnk
FROM BAND b
LEFT JOIN ALBUM a ON(b.id_band = a.id_band)
LEFT JOIN SONG s ON(a.id_album = s.id_album)
JOIN MEMBER m ON(b.id_band= m.id_band)
JOIN MUSICIAN mu ON(m.id_musician = mu.id_musician)
WHERE mu.year_death is NULL
)
where rnk = 1
You can reference a table that is out of this nested select, like so
SELECT b.id_band,
COUNT(DISTINCT a.id_album),
COUNT(DISTINCT s.id_song),
COUNT(DISTINCT m.id_musician),
(SELECT name FROM MUSICIAN WHERE year_death IS NULL ORDER BY(birth) AND
MUSICIAN.id_BAND = b.id_band LIMIT 1)
FROM BAND b
LEFT JOIN ALBUM a ON(b.id_band = a.id_band)
LEFT JOIN SONG s ON(a.id_album = s.id_album)
JOIN MEMBER m ON(b.id_band= m.id_band)
JOIN MUSICIAN mu ON(m.id_musician = mu.id_musician)
/*LEFT JOIN(SELECT name FROM MUSICIAN WHERE year_death IS NULL ORDER
BY(birth)LIMIT 1) AS alive FROM mu*/
WHERE b.year_formed= 1981
GROUP BY b.id_band
For queries where you want to find the "max person by age" you can use ROW_NUMBER() grouped by the band
SELECT b.id_band,
COUNT(DISTINCT a.id_album),
COUNT(DISTINCT s.id_song),
COUNT(DISTINCT m.id_musician),
oldest_living_members.*
FROM
band b
LEFT JOIN album a ON(b.id_band = a.id_band)
LEFT JOIN song s ON(a.id_album = s.id_album)
LEFT JOIN
(
SELECT
m.id_band
mu.*,
ROW_NUMBER() OVER(PARTITION BY m.id_band ORDER BY mu.birthdate ASC) rown
FROM
MEMBER m
JOIN MUSICIAN mu ON(m.id_musician = mu.id_musician)
WHERE year_death IS NULL
) oldest_living_members
ON
b.id_band = oldest_living_members.id_band AND
oldest_living_members.rown = 1
WHERE b.year_formed= 1981
GROUP BY b.id_band
If you run just the subquery you'll see how it's working = artists are joined to member to get the band id, and this forms a partition. Rownumber will start numbering from 1 according to the order of birthdates (I didn't know what your column name for birthday was; you'll have to edit it) so the oldest person (earliest birthday) gets a 1.. Every time the band id changes the numbering will restart from 1 with the oldest person in that band. Then when we join it we just pick the 1s
I think this should be considerably faster (while also solving your problem):
SELECT b.id_band, a.*, m.*
FROM band b
LEFT JOIN LATERAL (
SELECT count(*) AS ct_albums, sum(ct_songs) AS ct_songs
FROM (
SELECT id_album, count(*) AS ct_songs
FROM album a
LEFT JOIN song s USING (id_album)
WHERE a.id_band = b.id_band
GROUP BY 1
) ab
) a ON true
LEFT JOIN LATERAL (
SELECT count(*) OVER () AS ct_musicians
, name AS senior_member -- any other columns you need?
FROM member m
JOIN musician mu USING (id_musician)
WHERE m.id_band = b.id_band
ORDER BY year_death IS NOT NULL -- sorts the living first
, birth
, name -- as tiebreaker (my optional addition)
LIMIT 1
) m ON true
WHERE b.year_formed = 1981;
Getting the senior band member is solved in the LATERAL subquery m - without multiplying the cost for the base query. It works because the window function count(*) OVER () is computed before ORDER BY and LIMIT are applied. Since bands naturally only have few members, this should be the fastest possible way. See:
Best way to get result count before LIMIT was applied
What is the difference between LATERAL and a subquery in PostgreSQL?
Prevent duplicate values in LEFT JOIN
The other optimization for counting albums and songs builds on the assumption that the same id_song is never included in multiple albums of the same band. Else, those are counted multiple times. (Easily fixed, and uncorrelated to the task of getting the senior band member.)
The point is to eliminate the need for DISTINCT at the top level after multiplying rows at the N-side repeatedly (I like to call that "proxy cross join"). That would produce a possibly huge number of rows in the derived table without need.
Plus, it's much more convenient to retrieve additional column (like more columns for the senior band member) than with some other query styles.
i'm trying to determine who is the largest scorer in a world cup group (this is a personal project)
I have the data but i'm having a hard time using count, group by and having in order to accomplish what i need.
I need to count messi's goals (top scorer) and group by each one of the groups so i get the highest scorer of each group.
For now i just have the joins:
select * from zonas
left join goles_zonas on (zonas.id = goles_zonas.Id_zona)
inner join goles on (goles.id = goles_zonas.id_gol)
inner join jugadores on (goles.id_jugador = jugadores.id)
instead displaying all columns (by using SELECT * ), in order to group the data, I find it necessary to do SELECT only certain columns which are considered to be the keys to determine the difference of each group of dataset to get the aggregation (in this case COUNT) of each dataset group
SELECT Id_zona, id_gol, id_jugador, COUNT(1) as number_of_goal
FROM zonas
left join goles_zonas on (zonas.id = goles_zonas.Id_zona)
inner join goles on (goles.id = goles_zonas.id_gol)
inner join jugadores on (goles.id_jugador = jugadores.id)
GROUP BY Id_zona, id_gol, id_jugador
It has to be grouped by all columns included the select statement that does not being aggregated.
but if you expect to display other columns as well which are not part of the grouping keys, you can do it like this
SELECT goles_zonas.* , x.* FROM (
SELECT Id_zona, id_gol, id_jugador, COUNT(1) as number_of_goal
FROM zonas
left join goles_zonas on (zonas.id = goles_zonas.Id_zona)
inner join goles on (goles.id = goles_zonas.id_gol)
inner join jugadores on (goles.id_jugador = jugadores.id)
GROUP BY Id_zona, id_gol, id_jugador ) X
LEFT JOIN goles_zonas on (x.id = goles_zonas.Id_zona)
I'm trying to Select a Record that has the Maximum Count of a character e.g 'A' and get this error "Cannot perform an aggregate function on an expression containing an aggregate or a subquery.". What is causing this, and how can I correct it.Thanks!
select
*
from GROUPS as G
inner join STUDENT_GROUP as SG
on sg.Group_ID = g.Groups_ID
inner join STUDENT as S
on s.Student_ID = sg.Student_ID
inner join STUDENT_MARKS as SM
on SM.Student_ID=s.Student_ID
inner join smark as m
on m.mark_id=sm.Mark_ID
where m.Mark_name = max(count('A'));
there is no such function to count the times a particular character appears in a string, but you can use the length of original string and the lengh of the string after removing this particular character as this:
declare #foo nvarchar(max)
set #foo='abcabc'
select len(#foo)-len(replace(#foo,'a','')) as [Total of 'a']
In your case, using TOP 1 will bring the first row in a particular order, and this order is count of character A
select top 1 *
from GROUPS as G
inner join STUDENT_GROUP as SG
on sg.Group_ID = g.Groups_ID
inner join STUDENT as S
on s.Student_ID = sg.Student_ID
inner join STUDENT_MARKS as SM
on SM.Student_ID=s.Student_ID
inner join smark as m
on m.mark_id=sm.Mark_ID
order by
len(m.Mark_name)-len(replace(m.Mark_name,'A','')) DESC
As #jyao note, In case you need to get all records with the same max amount of character use TOP 1 WITH TIES as this:
select top 1 with ties *
...
SELECT Opponent, JerseyNumber, A
FROM (SELECT * FROM Games_t INNER JOIN GameStats_t ON Games_t.GameID=GameStats_t.GameID)
WHERE A >=1 AND COUNT(Opponent) >3;
I'm trying to return games where there were at least three players who recorded one assist or more. If I don't have AND COUNT(Opponent) >3, the query returns almost what I want, but there are a few games where only three players recorded an assist.
Try this :
SELECT Opponent,
JerseyNumber,
A,
COUNT(Opponent) FROM
(
SELECT *
FROM Games_t INNER JOIN GameStats_t
ON Games_t.GameID=GameStats_t.GameID
)
WHERE A >=1
GROUP BY Opponent, JerseyNumber, A
HAVING COUNT(Opponent) >3
Use the following Query.
SELECT G_TEMP.GAME_ID, GT.OPPONENT, GT.JERSEYNUMBER, G.A FROM
(
SELECT GAME_ID, COUNT(OPPONENT) OPP_COUNT FROM GAMESTATS_T
HAVING COUNT(OPPONENT) > 3
GROUP BY GAME_ID
) G_TEMP
LEFT OUTER JOIN
GAMES_T G
ON
G.GAME_ID = G_TEMP.GAME_ID
AND G.A > 1
INNER JOIN
GAMESTATS_T GT
ON
G.GAME_ID = GT.GAME_ID
Working SQL Fiddle HERE
Note 1: When there are more than one table, it is always better to specify fields using tablename_alias.field_name syntax. This is a good practice, however it is optional.
For example, if Table TABLEA has fields FIELDA1, FIELDA2, FIELDA3 and if Table TABLEB has fields FIELDB1, FIELDB2
Then you can use query as:
SELECT A.FIELDA1, A.FIELDA3, B.FIELDB2
FROM TABLEA A JOIN TABLEB B ON A.FIELDA2 = B.FIELDB2
Use HAVING part in query to use some parameters after query completion:
SELECT Opponent, JerseyNumber, A
FROM (SELECT * FROM Games_t INNER JOIN GameStats_t ON Games_t.GameID = GameStats_t.GameID)
WHERE A >=1 HAVING COUNT(Opponent) > 3;