Joining two queries with id - sql

So I have two tables: match and player
winner_id and loser_id is from match table are connected with player_id in player table
and I want to achieve this

This is a very basic form of a JOIN:
select w.first_name as winner_first_name,
w.last_name as winner_last_name,
l.first_name as loser_first_name,
l.last_name as loser_last_name
from match m
join player w on m.winner_id = w.player_id
join player l on m.loser_id = l.player_id

Related

SQL - Selecting highest scores for different categories

Lets say i've got a db with 3 tables:
Players (PK id_player, name...),
Tournaments (PK id_tournament, name...),
Game (PK id_turn, FK id_tournament, FK id_player and score)
Players participate in tournaments. Table called Game keeps track of each player's score for different tournaments)
I want to create a view that looks like this:
torunament_name Winner highest_score
Tournament_1 Jones 300
Tournament_2 White 250
I tried different aproaches but I'm fairly new to sql (and alsoto this forum)
I tried using union all clause like:
select * from (select "Id_player", avg("score") as "Score" from
"Game" where "Id_tournament" = '1' group by "Id_player" order by
"Score" desc) where rownum <= 1
union all
select * from (select "Id_player", avg("score") as "Score" from
"Game" where "Id_tournament" = '2' group by "Id_player" order by
"Score" desc) where rownum <= 1;
and ofc it works but whenever a tournament happens, i would have to manually add a select statement to this with Id_torunament = nextvalue
EDIT:
So lets say that player with id 1 scored 50 points in tournament a, player 2 scored 40 points, player 1 wins, so the table should show only player 1 as the winner (or if its possible 2or more players if its a tie) of this tournament. Next row shows the winner of second tournament. I dont think Im going to put multiple games for one player in the same tournament, but if i would, it would probably count avg from all his scores.
EDIT2:
Create table scripts:
create table players
(id_player numeric(5) constraint pk_id_player primary key, name
varchar2(50));
create table tournaments
(id_tournament numeric(5) constraint pk_id_tournament primary key,
name varchar2(50));
create table game
(id_game numeric(5) constraint pk_game primary key, id_player
numeric(5) constraint fk_id_player references players(id_player),
id_tournament numeric(5) constraint fk_id_tournament references
tournaments(id_tournament), score numeric(3));
RDBM screenshot
FINAL EDIT:
Ok, in case anyone is wondering I used Jorge Campos script, changed it a bit and it works. Thank you all for helping. Unfortunately I cannot upvote comments yet, so I can only thank by posting. Heres the final script:
select
t.name,
p.name as winner,
g.score
from
game g inner join tournaments t
on g.id_tournament = t.id_tournament
inner join players p
on g.id_player = p.id_player
inner join
(select g.id_tournament, g.id_player,
row_number() over (partition by t.name order by
score desc) as rd from game g join tournaments t on
g.id_tournament = t.id_tournament
) a
on g.id_player = a.id_player
and g.id_tournament = a.id_tournament
and a.rd=1
order by t.name, g.score desc;
This query could be simplified depending on the RDBMs you are using.
select
t.name,
p.name as winner,
g.score
from
game g inner join tournaments t
on g.id_tournament = t.id_tournament
inner join players p
on g.id_player = p.id_player
inner join
(select id_tournament,
id_player,
row_number() over (partition by t.name order by score desc) as rd
from game
) a
on g.id_player = a.id_player
and g.id_tournament = a.id_tournament
and a.rd=1
order by t.name, g.score desc
Assuming what you want as "Display high score of each player in each tournament"
your query would be like below in MS Sql server
select
t.name as tournament_name,
p.name as Winner,
Max(g.score) as [Highest_Score]
from Tournmanents t
Inner join Game g on t.id_tournament=g.id_tournament
inner join Players p on p.id_player=g.id_player
group by
g.id_tournament,
g.id_player,
t.name,
p.name
Please check this if this works for you
SELECT tournemntData.id_tournament ,
tournemntData.name ,
dbo.Players.name ,
tournemntData.Score
FROM dbo.Game
INNER JOIN ( SELECT dbo.Tournaments.id_tournament ,
dbo.Tournaments.name ,
MAX(dbo.Game.score) AS Score
FROM dbo.Game
INNER JOIN dbo.Tournaments ONTournaments.id_tournament = Game.id_tournament
INNER JOIN dbo.Players ON Players.id_player = Game.id_player
GROUP BY dbo.Tournaments.id_tournament ,
dbo.Tournaments.name
) tournemntData ON tournemntData.id_tournament =Game.id_tournament
INNER JOIN dbo.Players ON Players.id_player = Game.id_player
WHERE tournemntData.Score = dbo.Game.score

SQL: Counting number of games for team from results page

Completely noob to SQL.
I have created the following table, which stores data on matches between two opponents and the points the winner got.
CREATE TABLE matches ( winner INT references players,
loser INT references players,
gamepoints INT);
I created the below VIEW to show standings:
CREATE VIEW standings as
select
players.id,
players.name,
count(matches.winner) as number_of_wins,
coalesce(sum(matches.gamepoints),0) as points
from players left join matches
on players.id = matches.winner
group by players.name, players.id
order by number_of_wins desc, points desc;
I wish to add a column that will show how many games a player played. My problem is that games appear in both matches.winner and matches.loser columns, and I'm not sure how to aggregate them in the standings view.
Also, would you say that the matches table is normalized?
Any help would be greatly appreciated.
EDIT: changed matches content.
With the help of #Jorge Campos, this is the solution:
CREATE VIEW games_won as
select p.id, p.name, coalesce(sum(m.gamepoints),0) gp, count(m.winner) ng
from players p left join matches m
on p.id=m.winner
group by p.id, p.name;
CREATE VIEW games_lost as
select p.id, p.name, count(m.loser) as ng
from players p left join matches m
on p.id=m.loser
group by p.id, p.name;
CREATE VIEW standings as
select w.id, w.name, w.ng as wins, w.ng+l.ng as matches, w.gp as gamepoints
from games_won w INNER JOIN games_lost l
on w.id=l.id
order by wins desc, gamepoints;
For the simple case you show there are only a few things that you should fix to be ok. Again for the problem you show.
First: Change the columns types of the table matches it shouldn't be SERIAL as it is an autoincrement type column (not a real type). Both columns are foreign keys and it should be integer, int or bigint
as
create table matches (
winner bigint,
loser bigint,
gamepoints int,
constraint fk_player_winner foreign key (winner)
references players(id),
constraint fk_player_loser foreign key (loser)
references players(id)
);
Second: to know how many games a player did with the number of points you can create two subqueries one with the winners and one with the losers and join the two summing the values. The catch is that you have to decrease the gamepoints from the two:
select w.id, w.name, w.gp-l.gp as gamepoints, w.ng+l.ng
from (select p.id, p.name, sum(m.gamepoints) gp, count(m.winner) as ng
from players p inner join matches m
on p.id=m.winner
group by p.id, p.name ) w
INNER JOIN
(select p.id, p.name, sum(m.gamepoints) gp, count(m.loser) as ng
from players p inner join matches m
on p.id=m.loser
group by p.id, p.name) l on w.id=l.id;
From it you create your view.
Note: maybe I'm being overkill with this two subqueries. It is possible to work out with a join between two players tables and a matches
See how it goes here on fiddle: http://sqlfiddle.com/#!15/5b6a4/4

Complicated SQL Query involving multiple tables

In this Query, I have to list pair of players with their playerID and playerName who play for the exact same teams.If a player plays for 3 teams, the other has to play for exact same 3 teams. No less, no more. If two players currently do not play for any team, they should also be included. The query should return (playerID1, playername1, playerID2, playerName2) with no repetition such as if player 1 info comes before player 2, there should not be another tuple with player 2 info coming before player 1.
For example if player A plays for yankees and redsox, and player b plays for Yankees, Red Sox, and Dodgers I should not get them. They both have to play for Yankees, and Red Sox and no one else. Right now this query finds answer if players play for any same team.
player(playerID: integer, playerName: string)
team(teamID: integer, teamName: string, sport: string)
plays(playerID: integer, teamID: integer)
Right now the Query I have is
SELECT p1.playerID, p1.playerName, p2.playerID, p2.playerName
FROM player p1, player p2, plays
WHERE p1.teamID = p2.teamID AND teamID in.....
I am stuck on how to approach it after this. Any hints on how to approach this problem. Thanks for your time.
I think the easiest approach is to concatenate the teams together and just join on the results. Postgres provides the function string_agg() to aggregate strings:
select p1.playerId, p1.playerName, p2.playerId, p2.playerName
from (select p.playerId, string_agg(cast(p.TeamId as varchar(255)), ',' order by TeamId) as teams,
pp.PlayerName
from plays p join
players pp
on p.playerId = pp.playerId
group by p.playerId
) p1 join
(select p.playerId, string_agg(cast(p.TeamId as varchar(255)), ',' order by TeamId) as teams,
pp.PlayerName
from plays p join
players pp
on p.playerId = pp.playerId
group by p.playerId
) p2
on p1.playerid < p2.playerid and p1.teams = p2.teams;
EDIT:
You can do this without string_agg. The idea is to start with a list of all possible player combinations.
Then, join in the teams for the first player using left outer join. And join in the teams for the second by using full outer join and matching on the team and driver name. The reason you need the driver table is to be sure that the id/name does not get lost in the full outer join:
select driver.playerid1, driver.playerid2
from (select p1.playerId as playerId1, p1.playerName as playerName1,
p2.playerId as playerId2, p1.playerName as playerName2
from players p1 cross join
players p2
where p1.playerId < p2.playerId
) driver left outer join
plays p1
on p1.playerId = driver.playerId full outer join
plays p2
on p2.playerId = driver.playerId and
p2.teamid = p1.teamid
group by driver.playerid1, driver.playerid2
having count(p1.playerid) = count(*) and
count(p2.playerid) = count(*);
This joins two players on the team id (with ordering so a pair only gets considered once). It then says there is a match when all the rows for the two players have non-NULL team values. This is perhaps more clear with the equivalent having clause:
having sum(case when p1.playerid is null then 1 else 0 end) = 0 and
sum(case when p2.playerid is null then 1 else 0 end) = 0;
The full outer join will produce NULL values when two players have teams that don't match. So, no NULL values mean that all the teams match.
This is an adaptation of my answer to a previous question of yours.
Get all unique combinations of players using a triangular join:
SELECT p1.playerID, p1.playerName, p2.playerID, p2.playerName
FROM player p1
INNER JOIN player p2 ON p1.playerID < p2.playerID
Subtract the second player's team set from that of the first player and check if there are no rows in the result:
NOT EXISTS (
SELECT teamID
FROM plays
WHERE playerID = p1.playerID
EXCEPT
SELECT teamID
FROM plays
WHERE playerID = p2.playerID
)
Swap the sets, subtract and check again:
NOT EXISTS (
SELECT teamID
FROM plays
WHERE playerID = p2.playerID
EXCEPT
SELECT teamID
FROM plays
WHERE playerID = p1.playerID
)
Finally, apply both conditions to the result of the triangular join in Step 1.
SELECT p1.playerID, p1.playerName, p2.playerID, p2.playerName
FROM player p1
INNER JOIN player p2 ON p1.playerID < p2.playerID
WHERE
NOT EXISTS (
SELECT teamID
FROM plays
WHERE playerID = p1.playerID
EXCEPT
SELECT teamID
FROM plays
WHERE playerID = p2.playerID
)
AND
NOT EXISTS (
SELECT teamID
FROM plays
WHERE playerID = p2.playerID
EXCEPT
SELECT teamID
FROM plays
WHERE playerID = p1.playerID
)
;

Finding records where sum of count() from two different relations is atleast 2

I'm trying to create an SQL query that will return the names of non-captain players that have at least 2 fans.
My difficulty is that the fans are split into favourite teams and favourite players, so I have to count the players that are favourited as a team and individually.
Here is what i've come up with, but it doesn't return the correct records:
select players.name
from players
-- join relevant relations
join teams
on teams.name = players.team
join favplayers
on players.name = favplayers.player
join favteams
on players.team = favteams.team
-- conditions
where players.team <> teams.captain
group by players.name
having (count(favplayers.player) + count(favteams.team)) > 1;
Here is the relational model for reference:
Any ideas?
Output:
NAME COUNT(DISTINCTFAVPLAYERS.FAN) COUNT(DISTINCTFAVTEAMS.FAN)
-------------------- ----------------------------- ---------------------------
Arthurs 3 1
Becker 1 1
Bryan 0 3
Greul 0 2
Since it's possible to have favourite players who are members of a team that is nobody's favourite (and vice versa), the joins to the "favourite" tables need to be outer joins. Also, the non-captain condition should be on the name of the player, not the name of the team:
select players.name
from players
-- join relevant relations
join teams
on teams.name = players.team
left join favplayers
on players.name = favplayers.player
left join favteams
on players.team = favteams.team
-- conditions
where players.name <> teams.captain -- not players.team
group by players.name
having (count(favplayers.player) + count(favteams.team)) > 1;
This should correctly return players that are "favourited" more than once, although if you wanted to include a count of the number of times a player was "favourited", the having expression would overcount where there was more than one fan for both the player and the player's team - a better expression would be count(distinct favplayers.fan) + count(distinct favteams.fan). Note also that it would include players where a single fan had "favourited" both the player and their team.

SQL get rows containing two rows from bridge table

That title is presumably awfully worded. I have some PostgreSQL tables. There is a bridge table, that also holds extra data, so not strictly a bridge, but acts that way also. It goes something like so:
player_game
===========
player_game_id PK
player_id FK -> player
game_id FK -> game
other stuff.
I want to compile a list of all such game_ids that contain two players of my choice.
So I could find for example, games in which player 1234 played with player 9876.
There can be between 2 and 10 players in a game.
select pg1.game_id from player_game pg1
inner join player_game pg2 on pg1.game_id = pg2.game_id
where pg1.player_id = 1234
and pg2.player_id = 9876
group by pg1.game_id -- or : AND pg1.player_game_id < pg2.player_game_id
You need to join the table to itself using an alias. (for both tables) and then specify the two ids as needed
select *
from player_game as A
inner join player_game as B on A.game_id = B.game_id
where A.player_id = '' and B.player_id = ''