Relational Algebra on 3 Tables - sql

I am having trouble forming a relational algebra query for a question in an assignment. I have to find the name of all the teams that won a game on a specific date.
The database now has the following three schemas:
Team(teamid,teamname,stadium)
Player(playerid,name,teamid, height)
Game (gameid, hometeamid, guestteamid, date, home-score, guest-score)
I am a little confused on how to do this since the tables that I seem to need do not have anything in common (Game and Team). I see that Game has an id for both the home and away teams, but how can you find out who which team won?
The exact question that I have to answer is:
Find the name of all the teams that won on 6/1/15. (Assume a team plays only one game a day and that a tie is not possible)

Try This
(select teamname from Team t, Game g
where t.teamid = g.hometeamid
and home-score > guest-score and date = '6/1/15')
UNION
(select teamname from Team t, Game g
where t.teamid = g.guestteamid
and guest-score > home-score and date = '6/1/15')
The first query represents games which home teams have won while the second query represents games which guest teams have won. The union of the two will be the required answer

Related

Find entities sorted by aggregated property of their relations

Question is quite similar to Find entity with most relations filtered by criteria, but slightly different.
model Player {
id String #id
name String #unique
game Game[]
}
model Game {
id String #id
isWin Boolean
playerId String
player Player #relation(fields: [playerId], references: [id])
}
I would like to find 10 players with best win rate (games with isWin=true divided by total amount of games by that player).
Direct and slow way to do that is to find all the players who won at least once and count their wins (first query). Then for each of them count their total amount of games (second query). Then do the math and sorting on the application side while holding results in memory.
Is there simpler way to do that? How would I do that with prisma? If there is no prisma "native" way to do it, what is the most efficient way to do this with raw SQL?
This is simple aggregation:
SELECT p.id, p.name
, COUNT(CASE WHEN isWin THEN 1 END) AS wins
, COUNT(g.playerId) AS played
, 100 * COUNT(CASE WHEN isWin THEN 1 END)
/ COUNT(g.playerId) AS rate
FROM player AS p
JOIN game AS g
ON g.playerId = p.id
GROUP BY p.name -- since p.name is unique, not the id.
ORDER BY rate DESC
LIMIT 10
;
Adjust as needed for your database. I adjusted in case the join becomes a LEFT JOIN, to handle players with no games played.
Executable example with PG, as indicated by the comments below.
Working test case - no data

How can I select the highest counts attributes from different groups?

So I have a table with players data(name, team, etc..) and a table with goals (player who scored it, local team, etc...). What I need to do is, get from each team the highest scorer. So the result I'm getting is something like:
germany - whatever name - 1
germany - another dude - 5
spain - another name - 8
italy - one more name - 6
As you can see teams repeat, and I want them not to, just get the highest scorer of each team.
Right now I have this:
SELECT P.TEAM_PLAYER, G.PLAYER_GOAL, COUNT(*) AS "TOTAL GOALS" FROM PLAYER P, GOAL G
WHERE TO_CHAR(G.DATE_GOAL, 'YYYY')=2002
AND P.NAME = G.PLAYER_GOAL
GROUP BY G.PLAYER_GOAL, P.TEAM_PLAYER
HAVING COUNT(*)>=ALL (SELECT COUNT(*) FROM PLAYER P2 where P.TEAM_PLAYER = P2.TEAM_PLAYER GROUP BY P2.TEAM_PLAYER)
ORDER BY COUNT(*) DESC;
I am 100% sure I'm close, and I'm pretty sure I have to do this with the HAVING feature, but I can't get it right.
Without the HAVING it returns a list of all the players, their teams and how many goals have they scored, now I want to cut it down to only one player for each team.
PD: the teams in the table GOAL are local and visiting team, so I have to use the Player table to get the team. Also the Goal table is not a list of the players and how many goals they have scored, but a list of every individual goal and the player who scored it.
If I understand correctly you can try this query.
just get MAX of PLAYER_GOAL column,SUM(G.PLAYER_GOAL) instead of COUNT(*)
SELECT P.TEAM_PLAYER,
MAX(G.PLAYER_GOAL) "PLAYER_GOAL",
SUM(G.PLAYER_GOAL) AS "TOTAL GOALS"
FROM PLAYER P
INNER JOIN GOAL G
ON P.NAME = G.PLAYER_NAME
WHERE TO_CHAR(G.DATE_GOAL, 'YYYY')=2002
GROUP BY P.TEAM_PLAYER
ORDER BY SUM(G.PLAYER_GOAL) DESC;
NOTE :
Avoid using commas to join tables it's a old join style, You can use inner-join instead.
Edit
I don't know your table schema, but this query might be work.
use a subquery to contain your current result set. then get MAX function and GROUP BY
SELECT T.TEAM_PLAYER,
T.PLAYER_GOAL,
MAX(TOTAL_GOALS) AS "TOTAL GOALS"
FROM
(
SELECT P.TEAM_PLAYER, G.PLAYER_GOAL, COUNT(*) AS "TOTAL_GOALS" FROM
PLAYER P, GOAL G
WHERE TO_CHAR(G.DATE_GOAL, 'YYYY')=2002
AND P.NAME = G.PLAYER_GOAL
GROUP BY G.PLAYER_GOAL, P.TEAM_PLAYER
HAVING COUNT(*)>=ALL (SELECT COUNT(*) FROM PLAYER P2 where P.TEAM_PLAYER = P2.TEAM_PLAYER GROUP BY P2.TEAM_PLAYER)
) T
GROUP BY T.TEAM_PLAYER,
T.PLAYER_GOAL
ORDER BY MAX(TOTAL_GOALS) DESC

SQL Query - One to Many relationship where all "Many" have a specific attribute

I am a bit rusty in Queries and I can't seem to find the solution for a problem that I have.
I'll make an analogy with sports.
Let's say we have a One to Many relationship between Teams (1-*) Games so each team has multiple games.
The Table Games has an attribute called Result which is an Enum and can have Victory or Loss.
I want to list all the teams where all Results are Victories.
Thanks in advance!
Something like this will do the job :)
select t.name
from teams t
where
(select COUNT(*) from games g where g.teamid = t.teamid and g.result = 1 --victory) > 0
AND (select COUNT(*) from games g where g.teamid = t.teamid and g.result = 0 --Loss) = 0

Match All Records

I am having difficulty with writing an SQL query for my application. Below shows a screenshot of some of the tables in my database.
Please let me explain how a section of my application works, and then I can show you the problem I am having with my SQL query.
A User can create an application form on my system (dbo.Form). An application form consists of several sections, one of those sections is Employment History (dbo.FormEmployment). An employment record includes details like employer name, start date etc, but also a gradeID. A User can add one or more employment records to the table dbo.FormEmployment.
A system administrator can add a Shift (dbo.Shift) to the system, and also then assign Grades to a Shift. These Grades are recorded in the dbo.ShiftGrade table. A Shift can be assigned 1 or more Grades, i.e. it could be assigned 1,2,3,4 or 5 Grades.
When the system administrator has added the Shift and Shift Grades, I then want to perform an SQL query to return a list of Users whose Grades match that of the Grades assigned to a Shift (remember the User adds their Grade when they add an employment record).
I have written the following SQL query which works, however, the issue with this query is that it returns a list of Users, that have any Grade that matches that of the Grades assigned to a Shift.
SELECT u.userID, u.userTypeID, u.firstName, u.lastName, u.email, u.password,
u.contactNumber, u.organisationID, u.emailVerificationCode, u.mobileVerificationCode,
u.userStatusID, u.AddedBy, u.AddedDate, u.ModifiedBy, u.ModifiedDate
FROM [User] u
--Check Grades Match
WHERE u.userID IN
(
SELECT f.locumID
FROM dbo.Form f, dbo.FormEmployment emp
WHERE f.formID = emp.formID
AND emp.statusID = 101
AND emp.workYN = 1
AND emp.gradeID IN (
select gradeID from dbo.ShiftGrade where shiftID = #p0
)
)
You can see I am using the IN statement to do this. However, I only want to return a list of Users who can match exactly the same Grades that have been assigned to a Shift. Therefore, if a Shift has been assigned 3 Grades, then I want a List of Users who also have the exact same three Grades to be returned.
I apologise for the lengthy post, I just thought it would be better explained this way.
Any feedback or help with this would be greatly appreciated.
select u.*
from dbo.Users u
join dbo.Form f on u.? = f.formId
join dbo.FormEmployment fe on fe.formId = f.formId
join dbo.Grade g on g.gradeId = fe.gradeId
join dbo.ShiftGrade shg on shg.gradeId =g.gradeId
join dbo.Shift sh on sh.shiftId = shg.shiftId
where
sh.shiftId = -- recently added shift id
and g.gradeId == -- recently added grade id

How to improve the speed of this SQL update query?

Sorry, this is my first time using this forum. Apparently people can edit my post which although helpful, has taken some information out.
I will try to make it more understandable.
I am using SQL Compact 3.5 as a local database.
The program is written in VB.NET.
The problem is with querying one of my tables that is taking too long.
The player table has, among other things, id, skill, school, weight, starter.
id is the player's id
skill is the player's skill level
school is a foreign key pointing to the id of the school table
weight is one of 14 different numbers
What I am trying to do is set the starter value = 'true' for the player with the highest skill at a given weight for a given school.
So if there are 100 players at a school, there will be 14 starters, one for each weight.
The player table has 170,000 players, each having 1 of 14 different weights, and each belongs to 1 of 4500 schools.
Someone commented below and showed this statement which appears to be on the right track. I am a novice and have not gotten it implemented quite yet.
"UPDATE p " &
"SET starter = 'TRUE' " &
"FROM player p" &
"JOIN (" &
"SELECT DISTINCT school, weight, MAX(skill) AS MaxSkill " &
"FROM player " &
"GROUP BY school, weight" &
") q ON q.school = p.school AND q.weight = p.weight AND q.MaxSkill =
p.skill"
Instead of doing a group-by-group, row-by-row approach, this update query does it all at once:
First, it gathers the highest skill for each school / weight combination.
It then joins that to the player that has the matching school / weight / skill combination, and then sets that player to the starter.
UPDATE p
SET starter = 'TRUE'
FROM player p
JOIN (
SELECT school, weight, MAX(skill) AS MaxSkill
FROM player
GROUP BY school, weight
) maxResults
ON maxResults.school = p.school
AND maxResults.weight = p.weight
AND maxResults.MaxSkill = p.skill
However, in the case of a tie in skill, all players with the highest skill would be set to a starter...
There's some minor confusion over the use of weight, as I'm assuming you you're not doing this on a per-unit basis. You may want to extract out ranges to another table, then use the id's there instead of a numeric weight.
In any case, here's a query that should work for all RDBMSs
UPDATE player a SET starter = TRUE
WHERE NOT EXISTS (SELECT '1'
FROM player b
WHERE b.school = a.school
AND b.weight = a.weight
AND b.skill > a.skill)
The inner query should return null (thus setting starter true) if:
There are no other players at the school
There are no players at the same school, in the same weight class
There are no players with a higher skill level, for the same school and weight class