MariaDB Query with multiple joins - mysql

In this database
https://www.databasestar.com/sample-database-movies/
I would like to make the following query: "List the name and genre of all the actors in the film Brazil".
I make this query:
USE movies;
SELECT DISTINCT p.person_name AS 'Nombre', g.gender AS 'Sexo' FROM movie m
JOIN movie_crew mc ON m.movie_id = mc.movie_id
JOIN department d ON mc.department_id = d.department_id
JOIN movie_cast mc2 ON m.movie_id = mc2.movie_id
JOIN person p ON mc2.person_id = p.person_id
JOIN gender g ON mc2.gender_id = g.gender_id
WHERE m.title = 'Brazil' AND d.department_name = 'Actors';
But no results appear and I don't understand where is my mistake.
Thanks.

I recommend you simplify the schema somewhat.
Just use simple strings for genre, language_role, keyword, gender, person_name
Use iso_code in place of country_id
Perhaps simple abbreviations for department and company
These do need normalizing (as you have don): person, movie, company, but mostly because there is other stuff in those entities.
That is, get rid of most of the tables in the leftmost and rightmost columns.
Once you have made that change, the error may mysteriously go away. (And, when you get more data, the queries will run faster. This does assume you have suitable indexes.)

Related

Getting an Odd Syntax Error With SQL Query

I'm writing what should be a fairly straightforward SQL query for a modification of the IMDB database; it's supposed to return a list of all films that are categorized as BOTH horror and comedy, which I've done by creating a list of horror, a list of comedy, and then deleting everything from one that's not in the other. The query is as follows:
WITH
table_left AS (SELECT primary_names.name AS name, year, genre, title_id
FROM titles NATURAL JOIN primary_names NATURAL JOIN title_genres WHERE genre = 'Horror'),
table_right AS (SELECT primary_names.name AS name, year, genre, title_id
FROM titles NATURAL JOIN primary_names NATURAL JOIN title_genres WHERE genre = 'Comedy')
DELETE FROM table_right WHERE (title_id NOT IN (SELECT table_left.title_id))
SELECT name, year FROM table_right;
However, this generates an "ERROR: syntax error at or near 'SELECT'" on the last line of the query. I'm fairly new to SQL, but have gone over the syntax multiple times and checked some guides and I just can't understand what's going wrong. There shouldn't be a comma after the DELETE FROM statement, I don't think I've got a comma in any inappropriate places...it may be staring me in the face, but I'm at a loss, and would love to hear any suggestions.
When you use the WITH syntax, you can declare multiple table expressions, but then you can follow it with just one SQL query.
But you have two — a DELETE followed by a SELECT, with no statement terminator between the two statements. This doesn't match any syntax rule of SQL.
I could comment that another way to achieve what you want, listing films that are in two categories, is to do a self-join.
SELECT p.name, t.year, t.title_id
FROM titles AS t
INNER JOIN title_genres AS g1 ON t.title_id = g1.title_id AND g1.genre = 'Horror'
INNER JOIN title_genres AS g2 ON t.title_id = g2.title_id AND g2.genre = 'Comedy'
INNER JOIN primary_names AS p ON t.title_id = p.title_id
You really need to learn how to use JOIN if you're coding with SQL. Not doing so is like using another language like Java or Ruby without understanding loops. I don't mean joins are like loops, just that joins are a foundational part of the SQL language, and you need to know how to use them.

MySQL - More Join operators, more aliases and Error 1066

Hopefully you are going to help me again :)
Well, the problem that I have is similar to the one which I've posted yesterday, but it is extended.
We are given three tables:
Pfleger
Station
Mitarbeiter
As states above, this problem is very similar to this problem.
What was the result? Well, I get back a table with the ID's and names of the workers who are living in Frankfurt.
Now I should additionally get back the ID'S and names of the workers who are living in Frankfurt AND working in the station called Onkologie.
How should I do this?
My code so far:
SELECT pfleger.PNR, Name
from mitarbeiter, ...
JOIN pfleger on (mitarbeiter.PNR=pfleger.PNR)
JOIN ...
where Ort='Frankfurt' and Name='Onkologie'
I don't know how to make 2nd JOIN.
You could try something like this
select m.PNR, m.Name
from Mitarbeiter m
inner join Station s on s.PNR = m.PNR
inner join Pfleger p on p.StationID = s.StationID
where
m.Ort = 'Frankfurt'
and p.Name = 'Onkologie'
Updated based on provided table names and column names for tables.
Notice: we eliminated the , notation after mitarbeiter, in your base query.
I don't see PNR in mitabeiter so I'm assuming stationID is how they join.
SELECT *
FROM pfleger P
INNER JOIN Station S
on S.StationId = P.StationID
INNER JOIN mitarbeiter M
on M.pnr = S.pnr
WHERE M.ORT='Frankfurt' and P.name = 'Onkologie'
Assumptions I made:
pfleger.stationID has a foreign key relationship to station.stationID
mitarbeiter.PNR has a foreign key relationship to station.PNR
We used inner joins here as we only want Mitarbeiter who exist in all 3 tables.
Otherwise we could use outer joins and return those who don't have records as well. (meaning it is unknown where they work it MIGHT be 'Onkologie' but we don't know as there is no record.)

MySQL Subquery returns more than 1 row on 3 tables

SELECT b.bill_no, b.case_no, b.patient_id,
(Select (lastname) from myhospital.patient p where p.patient_id = b.patient_id) as l_name,
(Select (givenname) from myhospital.patient p where p.patient_id = b.patient_id) as f_name,
(Select (middle) from myhospital.patient p where p.patient_id = b.patient_id) as m_name,
(select (address_street) from myhospital.patient p where
p.patient_id = b.patient_id) as adress, m. item_name,
(select cast(m.unit_price as Char(8))) as unit_price,
(select cast(m.qty as Char(8))) as quantity,
(select cast(m.charges as Char(8))) as charges,
m.date_rec, m.service_code,
(select (descript) from myhospital.hosp_services s where m.service_code = s.service_code) as Section,
(Select (fullname) from myhospital.users u where u.user_id = m.edit_by) as Encode_by,
(Select (descript) from myhospital.hosp_bill_etc c where b.bill_no = c.bill_no) as misc_edit
FROM myhospital.hosp_bill b join myhospital.hosp_bill_meds m
where b.bill_no = m.bill_no
I have join 2 tables from 1 database and i want to add another table which is "myhospital.hosp_bill_etc" and i am getting an error
subquery returns more than 1 row,
please someone tell me how to solve this.
As you stated, you are obviously new to querying, and it does take practice. Start by learning the relationships between the tables, and to direct joins (or left joins) without doing repeated queries. So, the patient information should be a single record for the given "patient_id". The joins between tables needs to identify HOW they are related or you will get Cartesian results. Notice how I am showing the relationship between respective tables via the "ON" command. And for readability, notice how I am visually nesting the table relations such as from billing to bill meds to hosp services, and users etc.
Now, you can get any column from the respective table(s) in the field selection list by the simple alias... Anyhow, hopefully a little help for you... Also, I don't know why you are casting the charges, qty, price as character. Typically output to what ever would formatted there and leave original value(s) as-is.
SELECT
b.bill_no,
b.case_no,
b.patient_id,
p.lastname as l_name,
p.givenname as f_name,
p.middle as m_name,
p.address_street as address,
m. item_name,
m.unit_price,
m.qty as quantity,
m.charges,
m.date_rec,
m.service_code,
s.descript as Section,
u.fullname as Encode_by,
c.descript as misc_edit
FROM
myhospital.hosp_bill b
JOIN myhospital.patient p
ON b.patient_id = p.patient_id
JOIN myhospital.hosp_bill_meds m
ON b.bill_no = m.bill_no
JOIN myhospital.hosp_services s
ON m.service_code = s.service_code
JOIN myhospital.users u
ON m.edit_by = u.user_id
JOIN myhospital.hosp_bill_etc c
ON b.bill_no = c.bill_no
So this is the relationships for the tables, but it will now return ALL entries for ALL patients. If you want something for a specific bill, or patient, you would add a WHERE clause for that specific component.
Now, it appears a bill is always to a single patient.
A bill has many meds.
Each med I would think has a single service, but if one med can have more than one, you will get duplicates.
Also, for each med, I would expect a single person associated with who recorded/distributed the meds.
Finally your "bill_etc". If this has multiple rows, that too could cause a Cartesian result.
Hopefully a good start based on YOUR data environment vs so many generics that you might have to wrap your head around, but PLEASE do some more reading on SQL practices.

MySQL: how to get result from 2 tables without repeating results?

I've got 3 tables: book, publisher, book_category
For a particular book category (fantasy) I have to display list of publisher names supplying that genre.
publisher_name and category_name are linked through book table, so my query is:
SELECT publisher.publisher_name
FROM publisher, book, book_category
WHERE publisher.publisher_id = book.publisher_id
AND book.category_id = book_category.category_id
AND category_name = 'fantasy';
But the result I'm getting is repeating the name of publisher if there's more than one fantasy book supplied by that publisher.
Let's say I've got The Hobbit and The Lord of the Rings,both are fantasy and are supplied by the same PublisherA.
In that case the result of my query is:
PublisherA
PublisherA
Is it possible to get that result just once? Even if there's much more than 2 fantasy books
published by the same publisher?
Just use distinct if you only need publisher_name
SELECT distinct publisher.publisher_name
by the way, try to use JOIN syntax... to join tables
SELECT distinct p.publisher_name
FROM publisher p
join book b on b.publisher_id = p.publisher_id
join book_Category bc on bc.category_id = b.category_id
where bc.category_name = 'fantasy'
Use DISTINCT
SELECT DISTINCT publisher.publisher_name
FROM publisher, book, book_category
WHERE publisher.publisher_id = book.publisher_id
AND book.category_id = book_category.category_id
AND category_name = 'fantasy';
Try adding this to the end of the query: GROUP BY publisher.publisher_name
Everyone is mentioning DISTINCT, which is correct (better than GROUP BY in MySQL, because of the way the optimizer is set up), but I figured I would also add a modification for performance enhancements.
Currently you have implicit cross joins to get to the other tables, and making these explicit INNER JOINs will increase efficiency because of the order of filtering. Example:
SELECT DISTINCT Publisher.publisher_name
FROM publisher Publisher
INNER JOIN book Book ON Publisher.publisher_id = Book.publisher_id
INNER JOIN book_category Book_Category ON Book.category_id = Book_Category.category_id
WHERE Book_Category.category_name = 'fantasy';
In the original query, you bring in the complete record set of all three tables (publisher, book, book_category), and then from that set you join on the respective keys, and then return the result set. In this new query, your join to Book_Category happens based only upon the record set returned from the join between Publisher and Book. If there is filtering that happens based on this join, you will see a performance increase.
You also have the added benefit of being ANSI-compliant, as well as explicit coding to improve ease of maintenance.

How to approach this SQL query

I have data related as follows:
A table of Houses
A table of Boxes (with an FK back into Houses)
A table of Things_in_boxes (with an FK back to Boxes)
A table of Owners (with an FK back into Houses)
In a nutshell, a House has many Boxes, and each Box has many Things in it. In addition, each House has many Owners.
If I know two Owners (say Peter and Paul), how can I list all the Things that are in the Boxes that are in the Houses owned by these guys?
Also, I'd like to master this SQL stuff. Can anyone recommend a good book/resource? (I'm using MySQL).
Thanks!
Peter and Paul are gay couple ?
Then you should go for many-to-many relationship instead of having ownerID inside of Houses Table
ie. Houses2Owners with two columns ownerID and houseID
then the query would be
select item from houses as h
left join Boxes as b on h.houseID=b.houseID
left join Things as t on b.boxID=t.boxID
left join Houses2Owners as h2o on h.houseID=h2o.houseID
left join Owners as o on h2o.ownerID=o.ownerID
Main question you should ask yourself while designing that would be whether each object will appear once ie. if there are two similar boxes with similar things in them or ie. two boxes with ski masks in them.
Then you should create tables with no relationship to parent object and also to create a table that connects two tables. This way you will avoid ski mask to appear twice for two boxes which contain that mask.
SELECT
Things_in_boxes.*
FROM
Houses
JOIN Boxes ON Houses.HouseID = Boxes.House
JOIN Things_in_boxes ON Boxes.BoxID = Things_in_boxes.Box
WHERE
Houses.Owner = 'Peter' OR Houses.Owner = 'Paul'
As for resources to learn from... I can't really suggest anything specific. I learnt how to use (My)SQL gradually and from a number of sources, and can't single any of them out as having been of primary importance. w3schools has OK coverage of the very basic stuff, and MySQL's own documentation (available on the web, google for it) does an OK job and is a reasonable reference for when you want to know the nitty gritty of some topic or other.
EDIT: The above answer is wrong. I had missed the stipulation that a House can have multiple Owners.
New approach: I'll assume that there is a cross-referencing table, HouseOwners, with House and Owner as foreign keys.
My first thought was this:
SELECT
Things_in_boxes.*
FROM
Houses
JOIN Boxes ON Houses.HouseID = Boxes.House
JOIN Things_in_boxes ON Boxes.BoxID = Things_in_boxes.Box
JOIN HouseOwners ON Houses.HouseID = HouseOwners.House
WHERE
HouseOwners.Owner = 'Peter' OR HouseOwners.Owner = 'Paul'
However, this is not quite right. If both Peter and Paul are Owners of a given house, then the things in the boxes in that house would show up twice. I think a subquery is needed.
SELECT
Things_in_boxes.*
FROM
Houses
JOIN Boxes ON Houses.HouseID = Boxes.House
JOIN Things_in_boxes ON Boxes.BoxID = Things_in_boxes.Box
WHERE
Houses.HouseID IN (
SELECT DISTINCT House
FROM HouseOwners
WHERE Owner = 'Peter' OR Owner = 'Paul'
) AS MySubquery
SELECT t.name
FROM Houses h
INNER JOIN Boxes b ON b.houseId = h.id
INNER JOIN Things t ON t.boxId = b.id
INNER JOIN Owners o ON o.houseId = h.id
WHERE o.name = 'Peter' OR o.name = 'Paul'
By using inner joins you can combine these 4 tables with all the linked information. There is also an other way using inner select queries:
SELECT t.name
FROM Houses h
INNER JOIN Boxes b ON b.houseId = h.id
INNER JOIN Things t ON t.boxId = b.id
INNER JOIN Owners o ON o.houseId = h.id
WHERE h.id IN (SELECT o.housId
FROM Owners o
WHERE o.name = 'Peter' OR o.name = 'Paul')
This query works differently (by first finding the two house ID's of Peter and Paul and then performing the join), but it has the same effect.
Hopefully these examples will help you understand SQL :)
This isn't tested and written on the spot:
SELECT *
FROM
`things_in_boxes` AS a
LEFT JOIN `houses` AS b
on ( a.`house_id` = b.`house_id` )
LEFT JOIN `owners` AS c
on ( b.`house_id` = c.`house_id` )
WHERE c.`owner_id` IN( 0, 1 )
That is the general structure I would use, where the "0, 1" in the last IN statement are the owner ids for Peter and Paul. If you wanted to do it by name, you could simply make it something like
c.`name` IN( 'Peter', 'Paul' )
As far as books, I can't really tell you, I've learned through tutorials and references.
Here's one approach:
SELECT * FROM Things_in_boxes t
WHERE box_id IN (
SELECT b.id
FROM Boxes b
INNER JOIN Owners o
ON (o.house_id = b.house_id)
WHERE o.name LIKE 'Peter'
OR o.name LIKE 'Paul'
)
Note that you don't need to join on the House table, since both the Boxes and Owners have a house id.
Without knowing the full structure, I will assume a structure to build a query, step by step
Get the IDs of the houses belonging to the owners
select id from House where owner in ('peter', 'paul')
Get the boxes in those houses
select boxid from boxes where homeid in (select id from House where owner in ('peter', 'paul'))
Get the things in those boxes
select * from things where boxid in (select boxid from boxes where homeid in (select id from House where owner in ('peter', 'paul')))
This should get you what you want, but is very inefficient.
In the above method, The final query in step 3 gets the ids in each step, and stores them in temporary storage while it consumes them. This is a very slow operation in most DBMS.
The better alternative is a join. Combine all the tables and select the desired data.
select * from things join boxes on things.boxid =boxes.boxid join houses on boxes.houseid=house.id join owners on houses.owner=owner.ownerid where owner.name in ('peter',''paul)