Left join has me stumped - mysql

I have 2 tables, person and movie
The person table has ID and gender, and the Movie table has ID, movie_type, and movie_attended. genderID and movie_attended can be null.
I want to create a query that will return 3 columns: gender, movie_attended (but it must be a romance movie), and a count of the number of people who have that gender and movie_attended.
Right now I have
select person.gender, movie.movie_attended, count(person.gender)
from person left join movie
on person.ID = movie.ID
where movie.movie_type = "romance"
order by person.gender DESC
Unfortunately this just gives me one row of information. If I remove the count(person.gender) I get too many rows. Lets say there are only 3 romance movies to choose from. I want my first row to read Male - The Notebook - 12. Second row: Male - Casablanca - 8. Third row: Male - Romeo & Juliet - 2. Fourth row: Male - Null - 2
Then start over with Female
Thank you

select gender, count(person.gender), movie.movie_attended
from person left join movie
on person.movie_id = movie.movie_id # or whatever it is called.
where movie.movie_type = "romance"
group by person.gender

I'm surprised that you managed to run that query as you are missing a 'group by' statement which is always required when you use an aggregate function. The 'group by' statement should contain the same fields as the 'select' statement, without the aggregate function.
select person.gender, movie.movie_attended, count(person.gender)
from person left join movie
on person.ID = movie.ID
where movie.movie_type = "romance"
group by person.gender, movie.movie_attended
order by count (person.gender) DESC
I've changed your 'order by' statement as the data which you gave suggest that the query is ordered by the number of results and not by the gender.
It seems strange to me that the person.id should be equal to the movie.id. In terms of database structure, it seems more practical to have a table of people, a table of movies and a joining table built of people.id and movie.id, in which is stored a list of which people who have seen which movies.

Related

How can i get a specific data from my dataset using SQL and PHPmyadmin?

I'm new to sql.
I have 3 datasets ,
patient (columns : id age Zip_Code, size, weight, sex)
blood_tests (columns : test_ID, test_date, blood_sugar, laboratory_ID, patient_ID)
laboratory (columns : id, name, Zip_code, departments)
how can i get the number of patients per center ?
i did this code but it doesnt give the number per
select DISTINCT patient_ID, laboratory_ID from patient,blood_tests where patient.id = blood_tests.patient_ID AND blood_tests.laboratory_ID = laboratory.id;
but i don't know how to get the total number of patients per center, because some of them did more than one exam in the same center and they have done tests in many labs?
For the second question. he aks us to get the 4 tests that a specific patient carried out in a laboratory called 'NWB'.
and i did this and noticed that he is patient with and ID = 25 but how can i get that without specifying that the id is 25.
select patient_ID, laboratory.name from patient, blood_tests,laboratory where patient_ID = blood_tests.patient_ID AND blood_tests.laboratory_ID = laboratory.id HAVING laboratory.name = "NWB";
Thank You in advance.
This query will give you the number of distinct patients that had a blood test done for each laboratory. The query GROUPs all the records with the same laboratory_ID and then counts the number of DISTINCT patients per GROUP. Distinct in this case means that each patient is only counted once per GROUP even if there are multiple records with the same patient_ID and laboratory_ID. However the patient can still be counted in other GROUPs.
SELECT laboratory_ID, COUNT(DISTINCT patient_ID)
FROM blood_tests
GROUP BY laboratory_ID;
You could join the laboratory table to show the name of the laboratory instead of the id.
SELECT laboratory.nom, COUNT(DISTINCT blood_tests.patient_ID)
FROM blood_tests
JOIN laboratory ON laboratory.id = blood_tests.laboratory_ID
GROUP BY blood_tests.laboratory_ID;
Your other question is confusing because there is no 'the' test for a patient having carried out 4 tests. There would be 4 tests - which one of them is 'the' test.
Here is a query that will list the ids of all patients that have had 4 test carried out at the laboratory with the id 42.
SELECT patient_ID
FROM blood_tests
WHERE laboratory_ID = 42
GROUP BY patient_ID
HAVING COUNT(patient_ID) = 4;
EDIT based on comment from OP:
I hope this is useful for anyone trying to learn how to query a database.
When you query a database your asking it for information based on what is found in the database not based on what you already know to be true. You first need to figure out what it is that you are asking.
If you say you know that there is some (some means at least one) specific patient that did exactly 4 tests at laboratory NWB, you could ask the database to list all patients that carried out exactly 4 tests at laboratory NWB.
If you know the specific patient and want to get the tests then you would ask the database for all the tests carried out at at laboratory NWB for this specific patient. In this case the fact that you know there are 4 of them is not a criteria for selection, so it wouldn't appear in your query.
If you know there is some patient that did exactly 4 tests and you want to get their tests. What are you asking the database? You could ask: Find me all tests for any patient that has done exactly 4 tests at laboratory NWB. But if there are other patients that also had exactly 4 tests at that laboratory you would get their tests as well.
Above you saw how to group records together by a criteria and how to count the members of each group.
The HAVING clause allows you to limit results based on criteria that applies to a group.
The WHERE clause allows you to limit results based on criteria that applies to a record.
You should be able to construct a query for any of those scenarios using what was shown above.
You need to use COUNT(DISTINCT ~ ) and GROUP BY. We don't need to use the table patients because we have patient_ID in blood_tests.
SELECT
COUNT( DISTINCT bt.patient_ID) number_patients ,
bt.laboratory_ID
FROM
blood_tests bt
JOIN
laboratory l
ON bt.laboratory_ID = l.id
GROUP BY
laboratory_ID;
For the second request:
SELECT
GROUP_CONCAT(
bt.test_ID
SEPARATOR CHAR(10)
) test_IDs,
bt.patient_ID patientID,
bt.laboratory_ID. labo_ID,
l.name lab_name,
l.Zip_code. lab_zip,
l.departments. lab_dept
FROM
blood_tests bt
JOIN
laboratory l
ON
bt.laboratory_ID = l.id
WHERE
l.name = 'NWB'
GROUP BY
bt.patient_ID ,
bt.laboratory_ID,
l.name lab_name,
l.Zip_code,
l.departments
HAVING
COUNT(bt.test_ID) = 4;
Assuming you're using Mysql with PhpMyAdmin (from now i'll call it PMA)
You get the COUNT of patients for each laboratory using GROUP BY keyword.
You can read the (unofficial) documentation here.
Using GROUP BY you can get the count of tests for each laboratory with
SELECT laboratory_ID, COUNT(*) As num_tests FROM blood_tests WHERE 1 GROUP BY laboratory_ID;
So the query returns 2 columns one for the laboratory ID and one for the number of tests for that one.
You can see the zip code of laboratory using JOINS statements, in this case you should use INNER JOIN click here for (unofficial) documentation

Get DISTINCT show.name and COUNT of associated seasons

I'm trying to write a SQL statement that outputs a table with the two columns show name and number of seasons. The show column must contain no duplocates and the number of seasons column counts the number of seasons associated with the show entity.
Here are my two tables
Shows Table
id | name
Seasons Table
id | show_id | season_number
Here's what I've tried so far
SELECT DISTINCT shows.name
FROM shows
INNER JOIN seasons on show.id = seasons.show_id;
The above code works for grabbing distinct names but whenever I try adding COUNT(season.id) it breaks.
Any suggestions?
Thanks!
Use group by to aggregate multiple rows with the same name into a group. With count(distinct id) you calculate the number of distinct values of the id column in that group.
SELECT name
, COUNT(DISTINCT seasons.id)
FROM shows
JOIN seasons
ON shows.show_id = seasons.show_id
GROUP BY
name
By the way, I'd expect a season to have a one to many relation to show, not the other way around.

How to show a data using JOIN in SQL (PHP)

Select maintable.name FROM maintable
JOIN genres genre1 USING (tmdb_id)
JOIN genres genre2 USING (tmdb_id)
WHERE genre1.genres_name = 'Action'
AND genre2.genres_name = 'Drama'
group by maintable.name
Here genres is table name. genres_name is column name. genres1 and genres2 are just nor a table name, nor a column name, they are just random name in the code.
This is my code, now How do i display all genres_name?
The genres is like:
tmdb_id genres_name
1 Action
1 Crime
1 Drama
2 Horror
2 Comedy
2 Drama
The main table isl ike
tmdb_id movie_title
1 The Dark Knight
2 Logan
3 Wonder Woman
Let me know, if you need more information. (Please do not ask to show, what i tried. Trust me, it will make the question more confusing)
I want to echo the genres like:
The Dark Knight - Drama, Action, Crime
Of course, you need to use group_concat:
Select maintable.movie_title, group_concat(genres.genres_name) AS genres_name
FROM maintable
JOIN genres USING (tmdb_id)
GROUP BY maintable.tmdb_id
HAVING find_in_set('Action', genres_name) AND find_in_set('Drama', genres_name)
See demo here.
Note: How does find_in_set works, please see official doc.
I would try something like this. But this is the best I can do guessing at it in my head... ( sorry for any mistakes )
$Sql = "SELECT
m.name,
GROUP_CONCAT( g.genres_name ) as genres_list
FROM
maintable AS m
JOIN
genres AS g USING (tmdb_id)
WHERE
g.genres_name IN('Drama', 'Action')
GROUP BY m.tmdb_id";
MySQL GROUP_CONCAT() function returns a string with concatenated non-NULL value from a group.
http://www.w3resource.com/mysql/aggregate-functions-and-grouping/aggregate-functions-and-grouping-group_concat.php
Also note GROUP_CONCAT has a setting for the length, I don't recall what that is or how to change it, but it bit me in the butt one time. Basically it will truncate the list after a certain size, so be cautious of that.
See here: MySQL and GROUP_CONCAT() maximum length
AS I said I haven't tested this, but it seems you have a many to one relationship. Records in the maintable can have many related records in the genres table. Therefor, you should be able to group them on that relationship. Normally this would return 1 record for each pair ( same record in main table different in genre ) Without the group. The Group Concat allows you to compress that into a comma separated list.

Self inner join to get single record

I have an SQL table data as follow
I want to display single record for product
example
90792 Amlaan-Hi-Power .............. Show only 1 record when there are 2 record
90793 Amlaan-Neutral .............. show only 1 record when there are 2 record
90794 Amlaan-Phosphate free .........show only 1 record when there are 2 record
90801 Acetone .......................show only 1 record when there are 2 record
90901 Acetanilide ...................show only 1 record when there is 1 record
Can I do this using Inner join
I know
select distinct product from product ORDER BY `product`.`product` DESC
will select distinct (unique) product code and that to only one field i.e. product but confused how to get other information using SQL statement
but results in duplicate records or same table...........................
It looks like your duplicate rows vary by the quantity of product in the package.
You can display just the product and name with
SELECT DISTINCT product, name
FROM product
If you want to deal with the quantity as well, that's a little trickier. This might work: it will put all product codes on one line.
SELECT product,
GROUP_CONCAT(product_code ORDER BY product_code) product_codes,
name
FROM product
GROUP BY product, name
Self join doesn't make a whole lot of sense for this application.
Use group by option for such purposes.
SELECT product,GROUP_CONCAT(product_code SEPERATOR '|') AS product_code,name FROM Table GROUP BY NAME
It will show only one record for duplicate names.
The multiple enteries of product code will seperated by | .

Correct use of the HAVING clause, to return the unique row

I have 3 tables in this scenario: Teams, Players and PlayersInTeams.
A Player is just a registered user. No W/L data is associated with a Player.
A Team maintains win/loss records. If a player is playing by himself, then he plays with his "solo" team (a Team with only that Player in it). Every time Bob and Jan win together, their Team entry gets wins++. Every time Jason and Tommy lose together, their Team entry gets losses++.
The PlayersInTeams table only has 2 columns, and it's an intersection table between Players and Teams:
> desc PlayersInTeams ;
+------------+---------+
| Field | Type |
+------------+---------+
| fkPlayerId | int(11) |
| fkTeamId | int(11) |
+------------+---------+
So here is the tough part:
Because a Player can be part of multiple Teams, it is important to fetch the right TeamId from the Teams table at the beginning of a match.
A Player's SOLO team is given by
select fkTeamId from PlayersInTeams where
fkPlayerId=1 HAVING count(fkTeamId)=1;
NO IT'S NOT!! But I don't understand why.
I'm trying to say:
Get the fkTeamId from PlayersInTeams where
the fkPlayerId=1, but also, the count of rows
that have this particular fkTeamId is exactly 1.
The query returns (empty set), and actually if I change the HAVING clause to being incorrect (HAVING count(fkTeamId)<>1;), it returns the row I want.
To fix your query, add a group by. To compute the count per team, you'll need to change the where clause to return all teams that player 1 is on:
select fkTeamId
from PlayersInTeams
where fkTeamId in
(
select fkTeamId
from PlayersInTeams
where fkPlayerId = 1
)
group by
fkTeamId
having count(*) = 1;
Example at SQL Fiddle.
Below a detailed explanation of why your count(*) = 1 condition works in a surprising way. When a query contains an aggregate like count, but there is no group by clause, the database will treat the entire result set as a single group.
In databases other than MySQL, you could not select a column that is not in a group by without an aggregate. In MySQL, all those columns are returned with the first value encountered by the database (essentially a random value from the group.)
For example:
create table YourTable (player int, team int);
insert YourTable values (1,1), (1,2), (2,2);
select player
, team
, count(team)
from YourTable
where player = 2
-->
player team count(team)
1 1 1
The first two columns come from a random row with player = 1. The count(team) value is 2, because there are two rows with player = 1 and a non-null team. The count says nothing about the number of players in the team.
The most natural thing to do is to count the rows to see what is going on:
select fkTeamId, count(*)
from PlayersInTeams
where fkPlayerId=1
group by fkTeamId;
The group by clause is a more natural way to write the query:
select fkTeamId
from PlayersInTeams
where fkPlayerId=1
having count(fkteamid) = 1
However, if there is only one row for a player, then your original version should work -- the filtering would take it to one row, the fkTeamId would be the team on the row and the having would be satisfied. One possibility is that you have duplicate rows in the data.
If duplicates are a problem, you can do this:
select fkTeamId
from PlayersInTeams
where fkPlayerId=1
having count(distinct fkteamid) = 1
EDIT for "solo team":
As pointed out by Andomar, the definition of solo team is not quite what I expected. It is a player being the only player on the team. So, to get the list of teams where a given player is the team:
select fkTeamId
from PlayersInTeams
group by fkTeamId
having sum(fkPlayerId <> 1) = 0
That is, you cannot filter out the other players and expect to get this information. You specifically need them, to be sure they are not on the team.
If you wanted to get all solo teams:
select fkTeamId
from PlayersInTeams
group by fkTeamId
having count(*) = 1
HAVING is usually used with a GROUP BY statement - it's like a WHERE which gets applied to the grouped data.
SELECT fkTeamId
FROM PlayersInTeams
WHERE fkPlayerId = 1
GROUP BY fkTeamId
HAVING COUNT(fkPlayerId) = 1
SqlFiddle here: http://sqlfiddle.com/#!2/36530/21
Try finding teams with one player first, then using that to find the player id if they are in any of those teams:
select DISTINCT PlayersInTeams.fkTeamId
from (
select fkTeamId
from PlayersInTeams
GROUP BY fkTeamId
HAVING count(fkPlayerId)=1
) AS Sub
INNER JOIN PlayersInTeams
ON PlayersInTeams.fkTeamId = Sub.fkTeamId
WHERE PlayersInTeams.fkPlayerId = 1;
Everybody's answers here were very helpful. Besides missing GROUP BY, I found that my problem was mainly that my WHERE clause was "too early".
Say we had
insert into PlayersInTeams values
(1, 1), -- player 1 is in solo team id=1
(1, 2),(2,2) -- player 1&2 are on duo team id=2
;
Now we try:
select fkTeamId from PlayersInTeams
where fkPlayerId=1 -- X kills off data we need
group by fkTeamId
HAVING count(fkTeamId)=1;
In particular the WHERE was filtering the temporary resultset so that any row that didn't have fkPlayerId=1 was being cut out of the result set. Since each fkPlayerId is associated with each fkTeamId only once, of course the COUNT of the number of occurrences by fkTeamId is always 1. So you always get back just a list of the teams player 1 is part of, but you still don't know his SOLO team.
Gordon's answer with my addendum
select fkPlayerId,fkTeamId
from PlayersInTeams
group by fkTeamId
having count(fkTeamId) = 1
and fkPlayerId=1 ;
Was particularly good because it's a cheap query and does what we want. It chooses all the SOLO teams first, (chooses all fkTeamIds that only occur ONCE in the table (via HAVING count(*)=1)), then from there we go and say, "ok, now just give me the one that has this fkPlayerId=1". And voila, the solo team.
DUO teams were harder, but similar. The difficulty was similar to the problem above only with ambiguity from teams of 3 with teams of 2. Details in an SQL Fiddle.