complex sql-query for movie listing - mysql

I have a database with all movies I've seen in the cinema. it consists of a couple of tables:
screenings (main table with, date, cinema, movie, etc.)
movies (all information about the movies: title, director, length...)
cinemas (information about the cinema I watched it in, not used in my example)
tags (tags connected to movies fields: id, tag)
tag_scr (link table links tags to screenings, fields: scr_id, tag_id)
Most screenings have one tag connected (can be stuff like, film festival, 3d, premiere, live music) but screenings can also have zero tags or more than one, so what I'm trying to do is to create a query where I list all screenings, and all of the tags connected to that screening concatenated to a string with comas between. My query looks like this now:
SELECT screenings.id, movies.title, tags.tag FROM screenings
JOIN movies ON screenings.ft = movies.id
LEFT JOIN tag_scr ON screenings.id = tag_scr.scr_id
LEFT JOIN tags ON tag_scr.tag_id = tags.id
And that does what I need except the screenings with multiple tags are listed multiple times in the result. And if I group by screenings.id I only get the first tag on each screening. Is it possible to do what I want, using the concat function, a sub-query or any other method? My database is MySQL 5.5.24.

Yep, you need to use GROUP_CONCAT:
SELECT screenings.id, movies.title, GROUP_CONCAT(tags.tag SEPARATOR ',') tags
FROM screenings
JOIN movies ON screenings.ft = movies.id
LEFT JOIN tag_scr ON screenings.id = tag_scr.scr_id
LEFT JOIN tags ON tag_scr.tag_id = tags.id
GROUP BY screenings.id, movies.title
You don't really need the SEPARATOR syntax as by default, it's a comma.
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
Good luck.

Related

How to include a full column using a join

I'm doing an homework assignment and I have most of it but I can't get the last part. I don't know how to "Include artists without albums in your listing"?
SELECT
artist.name, Title
FROM
artist
INNER JOIN
album ON artist.artistID = album.artistid
ORDER BY
name, title;
This is what I get (top) and what I'm supposed to get (bottom):
You need to use LEFT JOIN instead of INNER JOIN in this case.
Different joins explanation with examples could be found in many places, for example in wikipedia

How to list only reviewed movies? [duplicate]

This question already has answers here:
SQL query return data from multiple tables
(6 answers)
Closed 6 years ago.
I have a problem here. I created a database with different tables. I have a table named "movie" with 108 movies. This table includes columns
"id, name, director, year, country",
table named "reviews" which includes columns
"movie_id" (movie_id links to the id of movie which was reviewed),
"review_score" (1 to 5 points),
"review (with review text)",
"critics_id" (which links to the critics from critics-table),
and also table named "critics" which icludes columns
"critics_name" and "critics_id".
The problem is that there is over 100 movies but only 10 of them are reviewed and I have to list only reviewed movies but reviews and movies are in different tables. When I try simple command SELECT name, review FROM movie, reviews - I get a huge list with repeating movies (see on picture) my table
Is there any command which could list only reviewed movies and so that every reviewed movie would be in the list only once?
Thanks beforehand!
This is where you use a join. You'd select from the movie table and join the reviews table, then filter by movies that have a joined review.
For example:
SELECT movie.*, review.review_score, review.review
FROM movie
LEFT JOIN reviews AS review
ON review.movie_id = movie.movie_id -- assuming this column name is correct...
WHERE review.movie_id IS NOT NULL
You could also add another join to your critics table:
SELECT movie.*, review.review_score, review.review, critic.critics_name
FROM ....
LEFT JOIN critics AS critic
ON critic.critics_id = review.critics_id
WHERE critic.critics_id IS NOT NULL
Please note that this example uses left join to highlight the way that the join works, in that if it doesn't find a match then the joined result will be null.
If you used an inner join instead, you wouldn't get any results that don't match the on clause, and as such you wouldn't need the WHERE id IS NOT NULL parts at all.
An inner joined example of the above would return movies that are reviewed, as well as the critic's name:
SELECT movie.*, review.review_score, review.review, critic.critics_name
FROM movie
INNER JOIN reviews AS review
ON review.movie_id = movie.id
INNER JOIN critics AS critic
ON critic.critics_id = review.critics_id
Also - I'm using AS to alias the plural table names to a singular name purely for semantic reasons. You could continue to use reviews and critics if you wanted to without the AS aliases.
Hope below query helps you:
SELECT
m.name,
c.critics_name,
r.review_score,
r.review_text
FROM movies m
JOIN reviews r ON r.movie_id = m.id
JOIN critics c ON c.critics_id = r.critics_id
for distinct movies change query as below:
SELECT DISTINCT m.name
FROM movies m
JOIN reviews r ON r.movie_id = m.id
JOIN critics c ON c.critics_id = r.critics_id

Duplicate values from database when joining

I want to get the tags and genres that are connected to the items using two relationtables, though I'm getting duplicate values.
This is my query, I'm grouping the values by the items id so I don't understand why it is giving me duplicate values.
SELECT
name,
GROUP_CONCAT(tag) AS tags,
GROUP_CONCAT(genre) AS genres
FROM items
LEFT JOIN tagsItemsRelation ON
tagsItemsRelation.itemId = items.id
LEFT JOIN tags ON
tags.id = tagsItemsRelation.tagId
LEFT JOIN genresItemsRelation ON
genresItemsRelation.itemId = items.id
LEFT JOIN genres ON
genres.id = genresItemsRelation.genreId
GROUP BY items.id
Here is a SQLFiddle
As you can see it gives me duplicate values:
NAME TAGS GENRES
item1 tag2,tag1 genre1,genre1
You are aggregating along two different dimensions at the same time. That is why you are getting duplicates. So, if a name has tags, t1, t2, and t3 along with genres g1 and g2, then your joins are producing six rows for the name, with all combinations of the tags and genres.
If you have just a handful of multiple values for tags and genres, then the easiest solution is to use distinct:
SELECT name, GROUP_CONCAT(DISTINCT tag) AS tags, GROUP_CONCAT(DISTINCT genre) AS genres
FROM items LEFT JOIN
tagsItemsRelation
ON tagsItemsRelation.itemId = items.id LEFT JOIN
tags
ON tags.id = tagsItemsRelation.tagId LEFT JOIN
genresItemsRelation
ON genresItemsRelation.itemId = items.id LEFT JOIN
genres
ON genres.id = genresItemsRelation.genreId
GROUP BY items.name;
If you have lots of duplicates (dozens or hundreds per name), then the generation and handling of the duplicates can be a real performance issue. In that case, you would want to pre-aggregate the values along each dimension and then do the join.
Note that I changed the group by condition to be on name rather than id. It is good form for the group by columns to match the select columns.

SQL Multiple Joins multiple where

I have three tables is question. categories, vocabulary & tex. I am trying to figure out how to have multiple joins in my query, i thought you can just add as many joins as you wanted, as long as you reference them properly.
So, the following two work perfectly on there own:
1.
SELECT
categories.ID AS ID,
categories.ParentID AS ID,
vocabulary.value AS Name
FROM categories
INNER JOIN vocabulary
ON categories.sid=vocabulary.sid
WHERE vocabulary.langid=1
2.
SELECT
categories.ID AS ID,
categories.ParentID AS ID,
tex.value AS Description
FROM categories
INNER JOIN tex
ON categories.tid=tex.tid
WHERE tex.langid=1
However, if i try to combine them as follows, it does not work.
categories.ID AS ID,
categories.ParentID AS ID,
vocabulary.value AS Name
tex.value AS Description
FROM categories
INNER JOIN tex
ON categories.tid=tex.tid
WHERE tex.langid=1
INNER JOIN vocabulary
ON categories.sid=vocabulary.sid
WHERE vocabulary.langid=1
Any ideas?
Thanks in advance
John
In MySQL, when you have columns with the same name, one of them will only be shown. You need to identify them uniquely by supplying ALIAS. And you can either put the condition on the ON clause or WHERE clause which could yield the same result since it uses INNER JOIN.
SELECT categories.ID AS CategoryID,
categories.ParentID AS CategoryParentID,
vocabulary.value AS Name
tex.value AS Description
FROM categories
INNER JOIN tex
ON categories.tid = tex.tid
INNER JOIN vocabulary
ON categories.sid = vocabulary.sid
WHERE vocabulary.langid = 1 AND
tex.langid = 1

How do you fix this mysql query without the use of a subquery?

I have 3 tables. For the purposes of this example I will simplify it as much as I can.
First table contains movie ids and names, second table contains genre ids and genre names (action, drama, etc). 3rd table stores the genre id associated with each movie, since some movies have more than one. It has 2 columns, genre_id and movie_id. Fairy simple stuff. Im trying to output the list of movies, along with a list of genres associated with each movie.
SELECT *
FROM movies
LEFT JOIN gen_pairs
ON movies.mov_id = gen_pairs.gen_movieid
LEFT JOIN categories
ON gen_pairs.gen_catid = categories.cat_id
GROUP BY mov_id
This will obviously output a single genre of each film, even if it has multiple ones in the gen_pairs table. How would I get it to display a comma separated list of genres for each movie, without running a sub-query for each item?
Your select should build the Cartesian product, so you'll get output like
MovieA GenreA
MovieA GenreB
MovieB GenreA
...
But it sounds like you want this instead:
MovieA GenreA, GenreB
MovieB GenreA
...
MySQL has a GROUP_CONCAT function to do what you want:
SELECT m.mov_id, GROUP_CONCAT(c.name)
FROM movies m
LEFT JOIN gen_pairs gp ON (m.mov_id = gp.gen_movieid)
LEFT JOIN categories c ON (gp.gen_catid = c.cat_id)
GROUP BY m.mov_id
Note the GROUP_CONCAT and GROUP BY.