Finding Standings Table in a tournament - sql

I have 2 tables
create table players
(name text,
id serial primary key);
create table matches
(winner integer references players(id),
loser integer references players(id),
id serial primary key);
I have to make a table called "standings" containing:
player_id,player_name,total_wins,total_matches
How to proceed?

Something like this:
select p.id, p.name,
count(w.id) as total_wins,
count(l.id) + count(w.id) as total_matches
from players p
left join matches w on w.winner = p.id
left join matches l on l.loser = p.id
group by p.id, p.name;
Online example: http://rextester.com/EHDCG19917

Related

SQL select the actor and movie name from every actor that has worked with a particular actor

This is a HW assignment, so please no exact answers if you can help it; I want to learn, not have it done for me.
(The create table statments are at the end of this post)
My task is to find all of the actors who have been in a movie with Tom Hanks, ordered by movie title, using 2 queries.
So far I have been able to create the following query; I know that my join is wrong, but I'm not sure why. How can I think about this differently? I feel like I'm close to the answer, but not quite there.
SELECT actor.name, movie.title FROM actor
LEFT OUTER JOIN character.movie_id ON movie.id IN
(
-- Get the ID of every movie Tom Hanks was in
SELECT movie_id FROM actor
INNER JOIN character ON character.actor_id = actor.id
WHERE actor.name = 'Tom Hanks'
)
WHERE actor.name != 'Tom Hanks'
ORDER BY movie.title;
Here are the create table statments for the schema:
create table actor (
id varchar(100),name varchar(100),
constraint pk_actor_id primary key (id));
create table movie(
id varchar(100),
title varchar(100),
year smallint unsigned,
mpaa_rating varchar(10),
audience_score smallint unsigned,
critics_score smallint unsigned,
constraint pk_id primary key(id));
create table character(
actor_id varchar(100),
movie_id varchar(100),
character varchar(100),
constraint pk_character_id primary key(movie_id, actor_id, character),
constraint fk_actor_id foreign key (actor_id) references actor (id),
constraint fk_movie_id foreign key (movie_id) references movie (id));
A left outer join will give you every entry in the left table (actor). If it has a corresponding value in the right table, it will give you that value, otherwise, you will get a null value returned.
Additionally, you join a table. In your query, you are trying to join a column
Something like this, perhaps? FWTH is films with Tom Hanks.
select mo.title, ac.name
from character ch join
(select m.movie_id
from character c join movie m on c.movie_id = m.id
join actor a on a.id = c.actor_id
where a.name = 'Tom Hanks'
) fwth on ch.movie_id = fwth.movie_id
join actor ac on ac.id = ch.actor_id
join movie mo on mo.id = fwth.movie_id
order by mo.title;

SQL issue - type of fkey

I'm using PostgreSQL
What I need
In SELECT query I need to select owner_type (client or domain). If solution does not exist please help me to rework this schema.
Schema (tables)
Albums - id | client_id (fkey) | domain_id (fkey) | name
Clients - id | first_name | last_name
Domains - id | name
Description: Albums owner can be Client or Domain or future other Nodes...
1. CREATE TABLE QUERY
CREATE TABLE albums
(
id BIGSERIAL PRIMARY KEY,
client_id BIGINT,
domain_id BIGINT,
name VARCHAR(255) NOT NULL,
FOREIGN KEY (client_id) REFERENCES clients(id),
FOREIGN KEY (domain_id) REFERENCES domains(id),
CHECK ((client_id IS NULL) <> (domain_id IS NULL))
);
2. SELECT QUERY
SELECT albums.id,
albums.name,
COALESCE(c.id, d.id) AS owner_id
FROM albums
LEFT JOIN clients c
ON albums.client_id = c.id
LEFT JOIN domains d
ON albums.domain_id = d.id
Need something like -> if c.id === null -> owner_type = 'Domain'
You would seem to want:
SELECT a.id, a.name,
COALESCE(c.id, d.id) AS owner_id,
(CASE WHEN c.id IS NOT NULL THEN 'client' ELSE 'domain' END) as owner_type
FROM albums a LEFT JOIN
clients c
ON a.client_id = c.id LEFT JOIN
domains d
ON a.domain_id = d.id ;
Do you need two separate columns representing client_id and domain_id for the type of owners? It seems that if you were to add more nodes, you would have to add additional columns.
Could you have an owners table representing all types of owners, and have an owner_id foreign key on the albums table?
I was thinking something like this:
CREATE TABLE albums (
id BIGSERIAL PRIMARY KEY,
owner_id BIGINT,
name VARCHAR(255) NOT NULL,
FOREIGN KEY (owner_id) REFERENCES owners(id)
);
CREATE TABLE owners (
id BIGSERIAL PRIMARY KEY,
type VARCHAR(100) NOT NULL,
name VARCHAR(255) NOT NULL
);
You could then query for albums belonging to clients:
SELECT a.id, a.name, o.name AS owner_name
FROM albums a
JOIN owners o ON o.id = a.owner_id
WHERE o.type = 'Client';
As new nodes (types of owners) are added, you simply need to add them to the owners table without modifying the schema of the albums table.
Hope this helps.

Multiple selects on joined tables with group by?

I have three tables with the structures outlined below:
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(255) UNIQUE
);
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(id) NOT NULL,
category BIGINT REFERENCES categories(id) NOT NULL,
text TEXT NOT NULL
);
CREATE TABLE posts_votes (
user_id BIGINT REFERENCES users(id) NOT NULL,
post_id BIGINT REFERENCES posts(id) NOT NULL
value SMALLINT NOT NULL,
PRIMARY KEY(user_id, post_id)
);
I was able to compose a query that gets each post with its user and its total value using the below query:
SELECT p.id, p.text, u.username, COALESCE(SUM(v.value), 0) AS vote_value
FROM posts p
LEFT JOIN posts_votes v ON p.id=t.post_id
JOIN users u ON p.user_id=u.id
WHERE posts.category=1337
GROUP BY p.id, p.text, u.username
But now I want to also return a column that returns the result of SELECT COALESCE((SELECT value FROM posts_votes WHERE user_id=1234 AND post_id=n), 0) for each post_id n in the above query. What would be the best way to do this?
I think an additional LEFT JOIN is a reasonable approach:
SELECT p.id, p.text, u.username, COALESCE(SUM(v.value), 0) AS vote_value,
COALESCE(pv.value, 0)
FROM posts p JOIN
users u
ON p.user_id=u.id LEFT JOIN
topics_votes v
ON p.id = t.post_id LEFT JOIN
post_votes pv
ON pv.user_id = 1234 AND pv.post_id = p.id
WHERE p.category = 1337
GROUP BY p.id, p.text, u.username, pv.value;

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

Conditional query of the tables

ALL,
Consider the following database schema:
CREATE TABLE players(playerid integer primary key, playertype integer, ....);
CREATE TABLE scoretype1(scoreid integer primary key, scorename varchar);
CREATE TABLE scoretype2(scoreid integer primary key, scorename varchar);
CREATE TABLE scoreforplayerstype1(playerid integer, scoreid integer, value double, foreign key(playerid) references players(playerid), foreign key(scoreid) references scoretype1(scoreid));
CREATE TAble scoreforplayerstype2(playerid integer, scoreid integer, value double, foreign key(playerid) references players(playerid), foreign key(scoreid) references scoretype2(scoreid));
Now the problem:
Is it possible to get the score values for all players in one query or I have to do 2 queries for type 1 and type 2? I'm looking to have a score name and score value for a player.
Thank you.
SELECT P1.PLAYERID, S1.SCORENAME, J1.VALUE
FROM PLAYERS AS P1
INNER JOIN SCOREFORPLAYERSTYPE1 AS J1
ON J1.PLAYERID = P1.PLAYERID
INNER JOIN SCORETYPE1 AS S1
ON S1.SCOREID = J1.SCOREID
UNION ALL
SELECT P2.PLAYERID, S2.SCORENAME, J2.VALUE
FROM PLAYERS AS P2
INNER JOIN SCOREFORPLAYERSTYPE2 AS J2
ON J1.PLAYERID = P1.PLAYERID
INNER JOIN SCORETYPE2 AS S2
ON S1.SCOREID = J1.SCOREID
SELECT p.playerid
,p.playertype
,IFNULL(sfp1.value, sfp2.value) AS value
,IFNULL(st1.scorename, st2.scorename) AS scorename
FROM players p
LEFT JOIN scoreforplayerstype1 sfp1 ON p.playerid = sfp1.playerid
LEFT JOIN scoreforplayerstype2 sfp2 ON p.playerid = sfp1.playerid
LEFT JOIN scoretype1 st1 ON sfp1.scoreid = st1.scoreid
LEFT JOIN scoretype2 st2 ON sfp2.scoreid = st2.scoreid
That should do it, but as the comments suggest, you should rethink your table structure.