Return first element in array_agg() - sql

I am writing a query to get all players for all teams. Instead of looping in the application, I decided to get the players of all teams in a single query using array_agg(). I have written the query as follows:
SELECT team_id, array_agg(team_name) AS teamname,
array_agg(player_id||'##'||player_name) AS playerdetails
FROM team
INNER JOIN players ON team_id = player_team
GROUP BY team_id
This query gives me the result as below, in the result set the teamname is being repeated (exactly to the no. of players)
team_id team_name playerdetails
1 {Australia,Australia,Australia,Australia} {"5##Glenn Donald McGrath","6##Shane Warne","2##Steve Waugh","1##Adam Gilchrist"}
2 {India,India,India,India} {"8##Kapil Dev","11##Saurav Ganguly","3##Rahul Dravid","9##Sachin Tendulkar"}
3 {"South Africa","South Africa","South Africa","South Africa"} {"12##Gary Kristen","4##Shaun Pollock","7##Jacques Kallis","10##Alan Donald"}
Is there any way to return the result like this
team_id team_name playerdetails
1 Australia {"5##Glenn Donald McGrath","6##Shane Warne","2##Steve Waugh","1##Adam Gilchrist"}
I have achieved it using a subquery, but want to know if its possible to write it without the subquery
SELECT team_id, teamname[1], playerdetails
FROM (
SELECT team_id, array_agg(team_name) AS teamname,
array_agg(player_id||'##'||player_name) AS playerdetails
FROM team
INNER JOIN players ON team_id = player_team
GROUP BY team_id) AS tempresult
The sqfiddle is here. And I am using Postgresql 8.4
[EDIT]
I was actually thinking of hack to the GROUP BY limitation column "team.team_status" must appear in the GROUP BY clause or be used in an aggregate function when try to retrieve the a column which was not been specified in group by
SELECT team_id, array_agg(team_name) AS teamname,
array_agg(player_id||'##'||player_name) AS playerdetails,
team_status -- could be replaced by something like array_agg(team_status)[0] or customfunction(team_status)
FROM team
INNER JOIN players ON team_id = player_team
GROUP BY team_id

It was actually a mistake from my part... the answer to my first question lies in that query itself. I just have to enclose the (array_agg(team_name))[1], earlier I tried it without the brackets.
SELECT team_id, (array_agg(team_name))[1] AS teamname,
array_agg(player_id||'##'||player_name) AS playerdetails
FROM team
INNER JOIN players ON team_id = player_team
GROUP BY team_id

Very simply, do not aggregate the team_name but GROUP BY it:
SELECT team_id, team_name, array_agg(player_id||'##'||player_name) AS playerdetails
FROM team
JOIN players ON team_id = player_team
GROUP BY team_id, team_name;

SELECT team_id, array_agg(distinct team_name) AS teamname,
array_agg(player_id||'##'||player_name) AS playerdetails
FROM team
INNER JOIN players ON team_id = player_team
GROUP BY team_id

Related

SQL UNION syntax

I am trying to figure out what the correct syntax for UNION is. My schema looks like is the following:
Players (playerNum, playerName, team, position, birthYear)
Teams = (teamID, teamName, home, leagueName)
Games = (gameID, homeTeamNum, guestTeamNum, date)
I need to print all teamIDs where the team played against the X team but not against the Y team.
So my first idea was to check for the hometeamNum and then do a check for the guesteamNum, but I am not sure home to do the proper syntax.
SELECT DISTINCT hometeamNum
FROM games
WHERE
guestteamNum IN
(SELECT teamid FROM teams WHERE teamname = 'X') AND
guestteamNum NOT IN
(SELECT teamid FROM teams WHERE teamname = 'Y')
UNION DISTINCT
If you just need the home teams, this should suffice:
SELECT DISTINCT hometeamnum
FROM games
WHERE guestteamnum NOT IN (SELECT teamid FROM teams WHERE teamname = 'Y')
If you need both home teams and guest teams:
Select all teams that are not 'y' that didn't play agains 'y' as home team and didn't play against 'y' as guest team, and played against 'x' as guest team or played against 'x' as home team.
SELECT DISTINCT teamid
FROM teams
WHERE teamname != 'y' AND teamid NOT IN
(SELECT hometeamnum
FROM games INNER JOIN teams ON games.guestteamnum = teams.teamid
WHERE teamname = 'y'
UNION
SELECT guestteamnum
FROM games INNER JOIN teams ON games.hometeamnum = teams.teamid
WHERE teamname = 'y')
AND teamid IN
(SELECT guestteamnum
FROM games INNER JOIN teams on games.hometeamnum = teams.teamid
WHERE teamname = 'x'
UNION
SELECT hometeamnum
FROM games INNER JOIN teams on games.guestteamnum = teams.teamid
WHERE teamname = 'x');
Hopefully this is what you were after. There may be a more concise query out there but it's too late in the night for me to think of one :)
SELECT City, Country FROM Customers
WHERE Country='Germany'
UNION ALL
SELECT City, Country FROM Suppliers
WHERE Country='Germany'
ORDER BY City;
Using NOT EXISTS allows you to locate rows that don't exist. That is , you want teams that have played against 'X' which are rows that do exist and these can be located by using a simple join and where clause**. Then from those rows you need to find any that do not exist against the team 'Y'.
SELECT DISTINCT
hometeamnum
FROM games
INNER JOIN teams AS guests ON games.guestTeamNum = guests.teamID
WHERE guests.teamname = 'X'
AND NOT EXISTS (
SELECT 1
FROM games AS games2
INNER JOIN teams AS guests2 ON games2.guestTeamNum = guests2.teamID
WHERE games.hometeamnum = games2.hometeamnum
AND guests2.teamname = 'Y'
)
Notes.
EXISTS/NOT EXISTS does not actually need to return any data so it is possible to use select 1 or select null or select *. I have used select 1 here simply because it may be easier to understand - however I would personally prefer `select null' which stresses that no data is being returned by the exists subquery.
EXISTS/NOT EXISTS are both reasonably efficient and can perform better than IN (...)
** for performance, and where it does not alter the result, use a join in preference to IN ( subquery )

SQL query for join on different values and aggregation with join

I need to write bunch of queries with a database with the following tables:
Match(homeId, awayId, homeScore, awayScore, date)
League(leagueId, leagueName)
Player(playerId, teamId, playerName, age, position, marketValue, position)
Team(teamId, teamName, leagueId, city)
However, I could not figure out how to write a sql query for the queries like following:
"Find the latest date when Lazio beat Milan in Milan’s home ground."
or
"Find the names of the leagues in which at least one team has at least three
Goalkeepers."
How can I join the Team table with Match table that will give me the matches with team names included. (i.e. homeId, awayId, homeName, awayName) and write these two queries.
Thanks.
For your first requirement, you can use something like this.
select date
from match
where homeid =(select teamid from team where teamname = 'Milan')
and awayId = (select teamid from team where teamname = 'Lazio')
and homeScore < awayScore
For second, use something like below.
select l.leagueName
from league l
inner join Team t
on l.leagueid=t.leagueid
where t.teamid in (
select distinct(teamid) as teamid from player p
where p.position like 'Goalkeeper'
group by teamid
having count(*) >= 3)

How to find the following SQL query?

there is a table in SQL database, called Players:
Players (ID, name, age, gender, score)
where ID is the primary key.
Now I want to write a query to find the following results:
For each age, find the name and age of the player(s) with the highest score among all players of this age.
I wrote the following query:
SELECT P.name, P.age
FROM Players P
WHERE P.score = (SELECT MAX(P2.score) FROM Players P2)
GROUP BY P.age, P.name
ORDER BY S.age
However, the result of the above query is a list of players with the highest score among ALL players across all ages, not for EACH age.
Then I changed my query to the following:
SELECT P.name, P.age, MAX(P.score)
FROM Players P
GROUP BY P.age, P.name
ORDER BY P.age
However, the second query I wrote gives a list of players with each age, but for each age, there are not only the players with the highest score, but also other players with lower scores within this age group.
How should I fix my logic/query code?
Thank you!
You can use rank to do this.
select name, age
from (
SELECT *,
rank() over(partition by age order by score desc) rnk
FROM Players) t
where rnk = 1
Your original query is quite close. You just need to change the subquery to be a correlated subquery and remove the GROUP BY clause:
SELECT P.name, P.age
FROM Players P
WHERE P.score = (SELECT MAX(P2.score) FROM Players P2 WHERE p2.age = p.age)
ORDER BY P.age;
The analytic ranking functions are another very viable method for processing this question. Both methods can take advantage of an index on Players(age, score). This also wants an index on Players(score). With that index, this should have better performance on large data sets.
You can try it also.
SELECT p.name, p.age, p.score
FROM players p
INNER JOIN
(SELECT `age`, MAX(`score`) AS Maxscore
FROM players
GROUP BY `age`) pp
ON p.`age` = pp.`age`
AND p.`score` = pp.Maxscore;
Try it this will resolve your issue :
select p1.name,p1.age,p1.score from players p1 where p1.score =
(SELECT max(score) from players where age = p1.age) group by p1.age;
If you will required all records having same maximum score :
Then you will use this. I have tested both the query on my localhost.
SELECT p1.name,p1.age,p1.score FROM players p1
WHERE p1.score IN (SELECT MAX(score) FROM players GROUP BY age)

New to SQL, having trouble with table with two IDs

I have a DB of scores over the course of a season and I am having trouble with a query to return the season results for a given team. Here is a snapshot of the table involved:
Teams (tid, team_name)
Games (home_team_id, road_team_id, game_date, home_score, road_score)
What would the SQL look like to return all games where home_team_id or road_team_id is 1 and include the team_name of the opponent?
Not clearly stated in question, but I guess your main confusion is on how to get team_name from both home_team_id and road_team_id. You can do twice INNER JOIN for that purpose, for example :
select
g.*
, home.team_name as home_team_name
, road.team_name as road_team_name
from Games g
inner join Teams home on home.tid = g.home_team_id
inner join Teams road on road.tid = g.road_team_id
where g.home_team_id = 1
or g.road_team_id = 1

I need to append data to an existing table in Access from 2 tables

I have data in two tables:
tbl_games has the following columns:
game_id, season, date, home_team, visiting_team,
home_score, visiting score, home_score_half_time, visiting score_half_time
tbl_formation has the following columns:
game_id, home_formation, home_team, visiting_team, visiting_formation
I would like to append data from these tables into a game_team table that has the following columns:
game_id, team_id, status, end_score, half_score, Formation
Instead of separating out home_team visiting_team I would like to have them all under team_id and status indicating whether it was a home team or a visiting team for a certain game.
I was experimenting with the query below but it did not work
INSERT INTO Game_Team ( game_id, Team_ID, End_Score, half_score, Formation )
SELECT G.game_id, G.home_team, G.home_score_half_time, G.home_score, GL.home_formation
FROM tbl_games AS G
INNER JOIN tbl_formation AS GL ON G.game_id = GL.game_id;
INSERT INTO Game_Team ( game_id, Team_ID, End_Score, half_score, Formation )
values(SELECT G.game_id, G.home_team, G.home_score_half_time, G.home_score, GL.home_formation
FROM tbl_games AS G
INNER JOIN tbl_formation AS GL ON G.game_id = GL.game_id);
I have added values.
See insert data from one table to another in mysql
try this
INSERT INTO Game_Team ( game_id, Team_ID, End_Score, half_score, Formation )
SELECT G.game_id, G.home_team & ' ' & G.visiting_team, G.home_score_half_time, G.home_score, GL.home_formation
FROM tbl_games AS G
INNER JOIN tbl_formation AS GL ON G.game_id = GL.game_id;