JOIN 2 tables with where condition, but show first row from table2 - mysql

I have 2 tables. One is movie table, where all movie data is stored and another one is relation table, where I can see which movie is attached to which category. If I want to show movies from category 6 I join them like this:
SELECT t1.title, t2.category FROM movies t1
JOIN movie_categories t2 ON t1.movieid = t2.movieid
WHERE t2.category = 6
And it works just fine. However, movie SEF url are generated based on the first row from table2 (actually it should connect to table3, a category table, where i will find sef alias name, but for now I dont need that, I just want to know how to get first category). So lets say, movie have 3 categories, which will be looking like this:
Table 2 [relations table]
id movieid category
1 1 3
2 1 6
3 1 2
Table 1 [movie table]
movieid
1
2
The query I showed before returns results like this:
title category
bla1 6
bla2 6
However to create sef url, I need to get the first category id from relations table which is 3 in this case.
I'd like to know how to construct query to achieve the result I described.

ASSUMPTION: by MIN you mean the lowest ID in movie_Categories and then display it's category value.
Generate a set of data which is the lowest ID for each movie (subquery)
Join it back to the movie list
Then join your movie categories back to the generated set with the min value
Join the movie categories again back to the base movie set to get the limiting set for category.
Then limit the data base the category desired.
.
drop table movie;
drop table movie_categories;
create table movie (movieid int, Title varchar(10));
create table movie_categories (Id int, movieID int, category int);
insert into movie values (1, 'The');
insert into movie values (2, 'End');
insert into movie_categories values (1,1,3);
insert into movie_categories values (2,1,6);
insert into movie_categories values (3,1,2);
--To aid in understanding you may want to select * and see why this works and remove the where clause.
--Basically the two joins to Movie_Categories is once for the LOWEST ID and once again to get the limiting category.
SELECT M.Title, MC1.Category
FROM Movie M
INNER JOIN (SELECT min(ID) ID, MovieID
from movie_categories mc
GROUP BY MovieID) L
on L.MovieID = M.MovieID
INNER JOIN Movie_Categories MC1
on L.MovieID = MC1.MovieID
INNER JOIN Movie_Categories MC2
on l.movieid = mc2.movieid
and L.ID = MC1.ID
where mc2.category = 6
Results in:
title category
The 3
Note: the 2nd title isn't listed because there are no records in movie_category which match a category of 6.

Related

How to write SELECT query from 2 different tables based on result from 3rd table, while keeping order?

I have 3 tables where one table has 3 columns with foreign keys to the other two tables.
table album_posters_albums-
+---------+---------+---------+
| album_id|poster_id|albums_id|
+---------+---------+---------+
| 49 | 167 | NULL |
| 49 | NULL | 45 |
+---------+---------+---------+
album_id and albums_id references the album table and poster_id represents the poster table.
I need to
SELECT * FROM poster
WHERE poster_id IN (
SELECT poster_id
FROM album_poster_albums
WHERE album_id=49);
IF the poster_id IS NULL:
SELECT * FROM album
WHERE album_id IN (
SELECT poster_id
FROM album_poster_albums
WHERE album_id=49).
The problem is I need to keep the posters and albums in the same order as they occur in the album_posters_albums table.
I was sending a query to get the list of ids, then looping through each result and querying the db to get either the poster or album but that is obviously very inefficient when I should be able to do it in one query.
It sounds like you want to use INNER JOINS
SELECT album.*, poster.*
FROM album_poster_albums
INNER JOIN album ON album_poster_albums.albums_id = album.album_id
INNER JOIN poster ON album_poster_albums.poster_id = poster.poster_id
WHERE album_poster_albums.album_id = 49
Based on your comment about one row with a poster and one row with an album, UNION ALL might be what you're looking for. (We'd need to see more details about the tables and a few more rows to understand the ordering part.) This should give you an album row then a poster row for each album id.
Caveats: The number and the orders of columns in the album and poster tables must be the same. Also, the data types of those columns must be the same or compatible. (I haven't used a UNION, or UNION ALL, in a very long time.)
SELECT * FROM (
SELECT album.*
FROM album_poster_albums
INNER JOIN album ON album_poster_albums.albums_id = album.album_id
WHERE album_poster_albums.album_id = 49
UNION ALL
SELECT poster.*
FROM album_poster_albums
INNER JOIN poster ON album_poster_albums.poster_id = poster.poster_id
WHERE album_poster_albums.album_id = 49
)
ORDER BY album_id
DECLARE #rowId INT(11);
SET #rowId :=0;
SELECT * FROM(SELECT #rowId:=#rowId+1,t.album_id,album.*
FROM album_poster_albums t
INNER JOIN album ON album.albums_id = t.albums_id
WHERE t.album_id = 49
UNION
SELECT #rowId:=#rowId + 2,t.album_id,poster.*
FROM album_poster_albums s
INNER JOIN poster ON poster.poster_id = t.poster_id
WHERE t.album_id = 49) T
ORDER BY #rowId,t.album_id
I decided to create a new table with an auto increment field based on #beltouche comment. My Mysql is pretty rusty and I thought there may be a way using case or if null. I didn't need the unique id previously with how I wrote the queries.
In hindsight the solution is obvious.
SELECT * FROM (SELECT albums.*, 1 AS type, t.id
FROM album_poster_album t
INNER JOIN albums ON albums.album_id = t.albums_id
WHERE t.album_id = 49
UNION ALL
SELECT poster.*, 2 AS type, s.id
FROM album_poster_album s
INNER JOIN poster ON poster.posterID = s.poster_id
WHERE s.album_id = 49) T
ORDER BY t.id

Combine data from 2 "strange" tables

Having the following Schema
(ER of DataBase)
I am trying to create a query that
will show the title(Movies.Title) along with each movie's genre(movie_Genres.movie_genre) for each movie that..
that have been seen in (a) a specific time period (lets say 2-3 days for ex. We can take those days from the Tickets.ticket_date) (b) by Male Customers(Customer.customer_sec) and (c) got rated more than 4 (rated_customerRation) by those customers.
I can get as close as the following query:
SELECT
`movie_title`, `movie_Genres`.`movie_genre`
FROM
`Movies`
INNER JOIN
`movie_Genres` ON `mg_movie` = `movie_ID`
INNER JOIN
`Rated` ON `rated_movie_ID` = `movie_ID`
WHERE `rated_customerRatio` > 4 AND
UNION
(SELECT
`customer_sex`, `rated_customerRatio`
FROM
`Customer`
/* INNER JOIN
`cinema`.`Rated` ON `Rated`.`rated_customer_tabID` = `Customer`.`customer_tabID`
*/
INNER JOIN
`cinema`.`Tickets` ON `Tickets`.`ticket_customer_tabID`=`Customer`.`customer_tabID`
WHERE
`customer_sex` LIKE 'Male'
/* AND rated_customerRatio > 4 */
AND `Tickets`.`ticket_date` > '2016-02-16')
GROUP BY `movie_title`;
Also I am thinking that I will have trouble, cause one movie can have more than one Genre, and I don't want double lines, for the same movie, in my outcome.
Any help will be taken into serious regards!
use a WHERE movie.ID IN (Select movie.id...) query to have unusual query relationships. i.e. if movie.id can be related to tickets
WHERE MovieID IN
(
SELECT
MovieID
FROM
MovieTable
LEFT JOIN TICKETS ON MovieID = TICKETS.movieID
LEFT JOIN CUSTOMER ON TICKETS.ID = CUSTOMER.ID
WHERE
CUSTOMER.customer_sex LIKE 'Male'
AND Tickets.ticket_date > '2016-02-16')
)
I dont know your DB schema but that should give you a rough idea on one way of sorting based on viewership of moves from tickets? 1 ticket should have 1 movie ID and 1 customer, so the relationship is pretty easy to figure out

build query to select data based on another table

tables
sample data
walls
wall_id wall_name
1 wall_1
2 wall_2
6 wall_6
wall_categories
wall_id category_id
1 2
2 1
6 1
6 2
categories
category_id category_name
1 Wallpaper
2 Photography
html
Wallpaper
Photography
What I need to do is when the user click the Wallpaper link, I want the images with wallpaper as category to be displayed same with photography. I have built an initial query but it generates duplicate records as I'm using join, there must be something else that need to be added to make it work.
SELECT DISTINCT walls.wall_id, walls.wall_name, walls.wall_views, walls.upload_date,
categories.category_name FROM categories INNER JOIN wall_categories ON
wall_categories.category_id=categories.category_id INNER JOIN walls ON
walls.wall_id=wall_categories.wall_id;
or since the category_id is fixed, we can use walls and wall_categories table only. Then lets' say we can use the following html.
Wallpaper
Photography
You're not limiting your query by category_id, so it's returning all wall records that have an associated category.
SELECT DISTINCT walls.wall_id, walls.wall_name, walls.wall_views, walls.upload_date,
categories.category_name FROM categories INNER JOIN wall_categories ON
wall_categories.category_id=categories.category_id INNER JOIN walls ON
walls.wall_id=wall_categories.wall_id
WHERE category.category_id = ?
;
And then bind ? to the appropriate category ID from your user's selection.

Retrieve parent category name from table in MYSQL results

I have a MYSQL table called 'categories' from a project I inherited from someone else.
id parent_id name
1 NULL Travel
2 NULL Sleep
3 NULL Eat
4 NULL Bath
5 1 Prams
6 1 Travel Systems
7 2 Cots
8 3 High Chairs
The table is obviously a lot bigger than that, but you get the general idea. I have a MYSQL statement which brings together this table with other category, brand and product tables, but basically I want to list the parent category name from the above table with the sub-category in the statement. How do I do this?
My current statement is something like:
SELECT brands.name, products.name, categories.id, categories.name, brands.id,
FROM `products` , `brands` , `categories`
WHERE products.brand_id = brands.id
AND products.category_id = categories.id
AND brands.name = '$brand'
ORDER BY categories.name, products.name
How do I retrieve the parent category names in the results?
For example if the product is a Pram, how can I output "Travel". I could do seperate MYSQL statements in the loop but I want to avoid this. This is either a stupidly simple question (in which case I apologise for being brain dead) or a little more complicated! Thanks.
First you need to know the parent id of the current category and then get the name for that id, you could use a subquery in this way:
SELECT name FROM categories WHERE id = (SELECT pid FROM categories WHERE name = $brand)
EDIT: Since you need to get the category and subcategory names in the same row for a given subcategory id, try this:
SELECT sc.name AS subcategory, c.name AS category
FROM categories sc
LEFT JOIN categories c ON c.id = sc.parent
WHERE sc.id = $subcategory_id

What is the most efficient way to search rows in a table based on metadata stored in another table?

Consider these two tables stored in a MySQL database. The first stores a list of movies and the second stores metadata about each movie. Rather than adding 'category' and 'actor' fields to the movies table, they are stored as key/value pairs in the movie_attributes table, as each movie may have more than one category or actor attributed to it.
movies
id name
-----------
0 hot rod
1 star wars
movie_attributes
id movie_id key value
----------------------------------------
0 0 genre comedy
1 0 genre stupid
2 0 actor andy samberg
3 0 actor harrison ford
4 0 actor chester tam
5 1 genre adventure
6 1 actor harrison ford
What would be the most efficient way to query the movies based on a search criteria that allows the user to select multiple genres and multiple actors? If the user adds more than one category to their search, I would like the results to include movies from both categories (but only those videos that also match the actors selected.)
For example, this pseudo-query:
select * from movies where (genre = 'comedy' OR genre = 'adventure') AND (actor = 'harrison ford')
would return this:
id name
-----------
0 hot rod
1 star wars
Currently, my solution uses a mess of subqueries and it's awfuly slow, as my database contains more than two attribute key types per movie (genres, actors, actresses, tags, etc..) and it's terribly slow. Here's the real query that's equivalent to the example pseudo-query above:
select * from movies
WHERE (select count(*) from movie_attributes
WHERE key='genre'
AND (value='comedy'
OR value='adventure'))>0
AND
(select count(*) from movie_attributes
WHERE key='actor'
AND (value='harrison ford'))>0
Is there a more efficient way to achieve the same results using joins or some other SQL voodoo magic I'm unfamiliar with? Thanks!
SELECT *
FROM movies
WHERE EXISTS(
SELECT NULL
FROM movie_attributes
WHERE movies.id = movie_attributes.movie_id AND
key = 'genre' AND value in ('comedy', 'adventure')
) AND
EXISTS(
SELECT NULL
FROM movie_attributes
WHERE movies.id = movie_attributes.movie_id AND
key = 'actor' AND value = 'harrison ford'
)
I would however suggest against this kind of design, see #n8wrl answer.
See if this one helps:
SELECT m.* FROM `movie_attributes` ma
JOIN `movies` m ON (ma.movie_id=m.id)
WHERE
(`key` = 'genre' and `value` in ('comedy', 'adventure'))
OR (`key` = 'actor' and `value` in ('harrison ford'))
Secondly, make sure, there is an index on the fields key, value in movie_attributes