SQL query for join on different values and aggregation with join - sql

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)

Related

How can I connect two tables in SQL depending on the data?

It's quite simple problem but I am begginer and i have problems with it.
I have 2 tables in database
matches id, team1id, team2id, score, date
teams id, name
If I want to select team1 name, team2 name, score and date what command should I use?
You should use a double "join" on teams table. I wrote a sheet of how you should use it :
select
matches.id,
teamA.name,
teamB.name,
score,
date
from matches
inner join teams teamA
on teamA.id = matches.team1id
inner join teams teamB
on teamB.id = matches.team2id

How to count the difference between two values in SQL?

Sorry if my title is not detailed
I have two tables
Game table:
homeTeam int
awayTeam int
homePoints int
awayPoints int
Team
tid int
name varchar(20)
I am trying to find the number of games won at home by a specific team, lets say 'cops', with Team.tid = Game.homeTeam and wins are counted if homePoints > awayPoints
I want to end up with
Team HomeWins
-----------------
Cops 20
How do I go about that?
EDIT: #
I Managed to get my answer using
SELECT t.name, count(CASE WHEN homePoints > awayPoints then 1 ELSE NULL END) as "Home Wins"
from Team t
JOIN Game g
ON t.tid = g.homeTeam
where t.name = 'Patriots'
GROUP BY t.name
some of the other answers were giving me the following errors
Column 'team.name' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
You should join the tables to be able to get both name and homePoints and use COUNT by homePoints and group by using team id to see a result for each team.
SELECT
T.name, COUNT(G.homePoints)
FROM
team T
INNER JOIN
game G ON G.homeTeam = T.tid
WHERE
G.homePoints > G.awayPoints
GROUP BY T.tid, T.name;
OR if you need the result for a specific team by providing it's id, you may drop the GROUP BY and add condition in the WHERE, e.g.
SELECT
T.name, COUNT(T.tid)
FROM
team T
INNER JOIN
game G ON G.homeTeam = T.tid
WHERE
G.homePoints > G.awayPoints and T.tid = :request_team_id;
First SELECT the columns that we want to display and use AS to specify they headings.
SELECT
team.name as 'Team',
COUNT(game.homePoints) AS 'HomeWins'
FROM dbo.team
Then we use INNER JOIN to only include the entries in game table, which match the Team ID in the homeTeam column vs what we selected from the team table.
INNER JOIN game on team.tid = game.homeTeam
Then we add a WHERE clause to limit it to only the team we ask for team.name = 'cops' and only include wins by that team
WHERE
team.name = 'cops'
AND game.homePoints > game.awayPoints
So all together your script should look like this;
SELECT
team.name as 'Team',
COUNT(game.homePoints) AS 'HomeWins'
FROM dbo.team
INNER JOIN game on team.tid = game.homeTeam
WHERE
team.name = 'cops'
AND game.homePoints > game.awayPoints
GROUP BY team.name

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 )

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)

Return first element in array_agg()

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