Database Count & Group By error - mysql

I am quite new on SQL and I am trying to practice to improve myself.
I have a database which has a
Table : Players, Teams, Plays, and Wins
Players : pid, pname, age, country
Plays : pid, season, tid, value ( pid -> pid in Players, tid -> tid in Teams )
Teams : tid, tname, tcolor, tbudget
Wins : wtid, ltid, season, wscore, lscore ( wtid,ltid -> tid in Teams )
The question is Find the name of the players whose played in atleast 2 dif. teams with same color
What I did is
SELECT DISTINCT P.pname
FROM Players P
,Teams T1
GROUP BY T1.tcolor
HAVING 1 < (
SELECT COUNT (10)
FROM Teams T2
WHERE T1.tcolor=T2.tcolor)
When I try to query this , I get an error which is ;
Error Code: 1630
FUNCTION PRALATEST.COUNT does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual
In which part am I doing wrong?

Try this:
select pname
from Players
join Plays on Plays.pid = Players.pid
join Teams on Teams.tid = Plays.tid
group by pname, tcolor
having count(Teams.tname) > 1
The condition count(Teams.tname) > 1 is in a having clause instead of a where clause becuase it needs to operate on the results AFTER the group by is performed.

Couple things. Your error message is because you put a numeric constant in the COUNT function. You should just use an asterisk.
Also, you have not specified a join condition for your Players and Teams tables. As a result, you are doing a product join (probably not what you want). I'm guessing you need to join to your Plays table.
You should change your coding practice to use "explicit" join syntax to avoid errors like this in the future.

Related

Subquery has been wrong all this time what do I do?

So I have the following table structure for a Sports Event system
TEAMS TABLE
team_id
game_id
team_name
team_logo
PLAYERS TABLE
player_id
team_id
player_name
player_mobile
player_email
So whenever a player submits a team registration details get saved on both tables. Events could be something like Cricket, Basketball, Netball, etc. Sometimes they dont fill in players details and sometimes they resubmit their team again which means same team name is submitted.
So whenever I need to check the accurate details of the team list I have been using this:
SELECT team_id FROM `teams` WHERE `game_id`= 35 GROUP BY `team_name
To get a list of the people in these teams that are the same name I was using this:
SELECT team_id, player_name FROM `player` WHERE team_id IN (SELECT team_id FROM `teams` WHERE `game_id`= 35 GROUP BY `team_name`) AND player_name IS NOT NULL AND player_name <> ''
The problem is the query on top gives me different results to what I am getting on the bottom. What I need to do is to get a list of current teams whenever i need. Duplicates of teams should be not there. Then I need a list of the players of these teams.
Currently stumped :( Help me pls.
TL;DR
You can get the desired results with a JOIN and DISTINCT
SELECT DISTINCT t.team_name, P.player_name
FROM teams AS t
INNER JOIN Players AS p
ON p.team_id = t.team_id;
FULL EXPLANATION
The following query is not deterministic, that is to say, you could run the same query on the same data multiple times and get different results:
SELECT team_id
FROM `teams`
WHERE `game_id`= 35
GROUP BY `team_name`;
Many DBMS would not even allow this query to run. You have stated that some teams are duplicated, so consider the following dummy data:
team_id team_name game_id
------------------------------------
1 The A-Team 35
2 The A-Team 35
3 The A-Team 35
When you group by team_name you are end up with one group, so if we start with a valid query:
SELECT team_name
FROM `teams`
WHERE `game_id`= 35
GROUP BY `team_name`;
We would expect one result:
team_name
--------------
The A-Team
When you add team_id in to the select, with no aggregate function, you need to pick one value for team_id, but the query engine has 3 different values to chose from, and none of them are more correct than any other. This is why anything in the select statement, must be contained within the group by (or functionally dependent on something that is), or part of an aggregate function.
The MySQL Docs state:
In standard SQL, a query that includes a GROUP BY clause cannot refer to nonaggregated columns in the select list that are not named in the GROUP BY clause. For example, this query is illegal in standard SQL because the name column in the select list does not appear in the GROUP BY:
SELECT o.custid, c.name, MAX(o.payment)
FROM orders AS o, customers AS c
WHERE o.custid = c.custid
GROUP BY o.custid;
For the query to be legal, the name column must be omitted from the select list or named in the GROUP BY clause.
MySQL extends the use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause. This means that the preceding query is legal in MySQL. You can use this feature to get better performance by avoiding unnecessary column sorting and grouping. However, this is useful primarily when all values in each nonaggregated column not named in the GROUP BY are the same for each group.
The reason this clause exists is valid, and can save some time, consider the following query:
SELECT t.team_id, t.team_name, COUNT(*) AS Players
FROM teams AS t
LEFT JOIN Players AS p
ON p.team_id = t.team_id
GROUP BY t.team_id;
Here, we can include team_name in the select list even though it is not in the group by, but we can do this safely since team_id is the primary key, therefore it would be impossible to have two different values of team_name for a single team_id.
Anyway, I digress, the problem you are most likely having is that the value returned for team_id in each of your queries will likely be different depending on the context of the query and the execution plan chosen.
You can get a distinct list of players and teams using DISTINCT:
SELECT DISTINCT t.team_name, P.player_name
FROM teams AS t
INNER JOIN Players AS p
ON p.team_id = t.team_id;
This is essentially a hack, and while it does remove duplicate records it does not resolve the underlying issue, of duplicate records, and potentially a sub-optimal data structure.
If it is not too late, I would reconsider your design and make a few changes. If team names are supposed to be unique, then make them unique with a unique constraint, so instead of working around duplicate entries, you prevent them completely.
You should probably be using junction tables for players and games, i.e. have your main tables
Team (team_id, team_name, team_logo etc)
Game (game_id, game_name, etc)
Player (player_id, player_name, player_email, player_mobile etc)
Then tables to link them
Team_Game (team_id, game_id)
Team_Player (team_id, player_id)
This then allows one player to play for multiple teams, or one team to enter multiple events.
Select t.team_id , p.player_name from player p
JOIN teams t
ON t.team_id = p.team_id
Where t.game_id = 35 AND p.player_name IS NOT NULL AND p.player_name <> ''
GROUP BY(t.team_name)
```
You should do a unique constraint on the team_name column, this way you are not allowing duplicate teams
Ps. I did not test the query but it should work

Derived joins and aggregate functions throwing errors

I am stuck in a query. i have two tables player and player_attributes with player_api_id as primary key.
I need to find the youngest,oldest player and the average overall rating of the oldest and youngest player.
Query to write the youngest and oldest player:
select player_name, birthday,YEAR(CURDATE()) - YEAR(birthday) as age from player where
birthday=(select max(birthday) from player)
or
birthday=(select min(birthday) from player)
Query for the average overall rating of all players:
SELECT player_api_id, avg(overall_rating) as avg_score,
FROM (
SELECT player_api_id, overall_rating FROM player_attributes
) as p
GROUP BY player_api_id;
Error while joining:
select player_api_id, avg(overall_rating),min(birthday),max(birthday) as avg_score
FROM (
SELECT player_api_id, overall_rating FROM player_attributes
) as p
join
(select birthday from player) as p1
on p.player_api_id=p1.player_api_id
GROUP BY player_api_id;
I am confused now??
There is no reason to use subqueries just to select columns. In fact, in MySQL, it is a really, really bad idea -- because MySQL materializes the subqueries.
So, just do:
select pa.player_api_id, avg(overall_rating) as avg_score,
min(p.birthday), max(p.birthday)
from player_attributes pa join
player p
on pa.player_api_id = p.player_api_id
group by pa.player_api_id;
I'm not sure if the rest of the logic is okay. But this should at least fix the syntax error.

Connecting multiple tables and using self join

I have a problem regarding a mysql query. What I'm supposed to do is find the pair of actors that acted in most of films together.
I have 3 tables:
actor - has id and name
casting - has movieid, actorid and ord
movie - has id (own id, not the one from actor), title, year, score, votes and director.
So, I did the following query:
SELECT *
FROM ( SELECT a.actorid,
b.actorid,
c.name,
d.name,
COUNT(*) AS zajednicka_pojavljivanja
FROM casting a,
casting b,
actor c,
actor d,
WHERE a.movieid=b.movieid AND a.actorid<>b.actorid
GROUP BY a.actorid,b.actorid,c.name,d.name) tablica_temp
ORDER BY zajednicka_pojavljivanja DESC;
What I get is an error:
ERROR
1064 (42000): You have an error in your SQL syntax; check the manual
that corresponds to your MySQL server version for the right syntax to
use near 'WHERE a.movieid=b.movieid AND a.actorid<>b.actorid GROUP BY
a.actorid) tablica_t' at line 1
I don't know where the problem is, can anyone help?
You're pretty close. The <> on your original query simply needed to be set to < or > but not <> and then you needed to limit your results or use a select max(count) approach to get all of the same counts.
SELECT A.ActorID, B.ActorID, Count(*) MostFilms
FROM Casting A
INNER JOIN Casting B
ON A.MovieID = B.MovieID and A.ActorID < B.ActorID
Group by A.ActorID, B.ActorID
order by MostFilms Desc
Limit 1
Basically what this says is join casting to itself getting the paired actors by movie. the < on A.ActorID to B.ActorID eliminates actor to him/herself and the opposites so if Actor A and B exist, B to A will not be included.
ordering by the MostFilms descending will return only that pair with the most.
The downfall to this is if two pairs have the same count only one of them would be returned.
If you need the names of the actors you can add two joins one for each casting table.
You have an extra comma in the FROM clause. Change:
actor d,
to
actor d

Many-to-many relationship: select players that are not on a specific team

I tried researching this but wasn't sure what to search for. I have a players table and a teams table and a linker table. A player can be on multiple teams.
I need to select all players that are NOT on team XYZ.
I've tried:
SELECT * FROM players LEFT JOIN linker ON players.playerID = linker.playerID
WHERE NOT linker.teamID = 'XYZ'
But then a player is returned multiple times and I get false positives (say player is on team XYZ, ABC, and LMNOP - the player is returned twice).
I've tried using GROUP BY players.playerID which takes care of duplicates. But then I get false positives, players that are on XYZ show up in the list.
I'm sure there is something I'm missing.
(P.S. The reason I need this is to build a list of players that can be added to a team, i.e. players that are not already on the team.)
Using NOT IN:
SELECT *
FROM players
WHERE playerID NOT IN
( SELECT playerID
FROM linker
WHERE teamID = 'XYZ'
)
Using LEFT JOIN - IS NULL, your try corrected:
SELECT players.*
FROM players
LEFT JOIN linker
ON linker.playerID = players.playerID
AND linker.teamID = 'XYZ'
WHERE linker.playerID IS NULL
Using NOT EXISTS:
SELECT *
FROM players
WHERE NOT EXISTS
( SELECT *
FROM linker
WHERE linker.playerID = players.playerID
AND linker.teamID = 'XYZ'
)

MYSQL View and summing fields

I need some help I have been scouring the web and haven't been able to find something too similar. I have a MYSQL database for my Golf League. I need to display standings by creating a view from this database. There are 2 people per team, my primary key in 'players' is 'id' there is also a teamID (numerical value 1 - 20, 20 teams) for each player which corresponds to their teammates. Basically what I need is a view that contains 'teamID', both players 'LName' (maybe an 'LNameA','LNameB'), and a sum of the two players 'points' field. I have never summed a field from one person and another or created a view in MYSQL.
EDIT:
I was trying something like
CREATE
VIEW standings1
AS SELECT teamID, LName, points
FROM players
but need teamID to be the primaryKey of the view which will contain each players last name, and their points summed together.
Try this:
create view standings as
select teamId, group_concat(lname separator ', ') as TeamMembers,
sum(points) TotalPoints from players
group by teamId
Oh, one more thing. If you want to have the names of the players in different fields (group_concat just separate them by commas, but it is still a single field) you can use this query:
create view standings as
select a.teamId, a.lname as player1, b.lname as player2,
a.points + b.points TotalPoints
from players a
join players b ON a.teamId = b.teamId AND a.id >= b.id
group by a.teamId, a.id
having count(*) = 2
That way you can play better with the names in PHP without having to parse the ", "
If I understand your table structure, you will need a JOIN against the table's own teamID. I'm assuming the teamID refers to a team, and is not the id of the player. The trick here is to join two copies of the table on the same teamID, but where the player ids are non-equal. That should produce the pair of players per team.
CREATE VIEW standings AS
(
SELECT
p1.teamID AS teamID,
p1.id AS p1id,
p2.id AS p2id,
p1.LName AS p1LName,
p2.LName AS p2LName,
p1.score + p2.score AS totalScore
FROM
/* JOIN on matching teamID and non-matching player-id (so you don't get the same player twice) */
players p1 JOIN players p2 ON p1.teamID = p2.teamID and p1.id <> p2.id
);