sql creating view irrelevant result error - mysql

I'd like to make a view of romance movies only if it's on Netflix.
Here's the code I used and extracted pics of the result.
Sadly, I got a redundant result including not related movie lists.
CREATE VIEW DB2020_romance_on_netflix AS
SELECT
distinct(DB2020_MOVIEINFO.MOVIE_ID), DB2020_MOVIEINFO.title, DB2020_MOVIEINFO.plot, DB2020_genre.genre, DB2020_ON_NETFLIX.on_netflix
FROM
DB2020_genre, DB2020_MOVIEINFO, DB2020_ON_NETFLIX
WHERE
DB2020_genre.genre = 'romance' and
DB2020_genre.mov_id in (SELECT mov_id
FROM DB2020_ON_NETFLIX
WHERE on_netflix = 'yes');
select * from DB2020_romance_on_netflix;
Here's more code needed for checking.
CREATE TABLE DB2020_MOVIEINFO (
MOVIE_ID Int primary key,
title VARCHAR(30) NOT NULL,
plot VARCHAR(500) NOT NULL,
main_target VARCHAR(10) NOT NULL,
country char(6) NOT NULL,
INDEX (MOVIE_ID)
);
DESCRIBE DB2020_MOVIEINFO;
INSERT INTO DB2020_MOVIEINFO VALUES (1, 'Parasite', 'Greed and class discrimination threaten the newly formed symbiotic relationship between the wealthy Park family and the destitute Kim clan.','Adult','Korea');
INSERT INTO DB2020_MOVIEINFO VALUES (2, 'Before Sunset', 'Celine and Jesse, who met nine years ago in Vienna, cross paths again for a single day in Paris. Together, they try to find out what might have happened if they had acted on their feelings back then.','Adult','US');
INSERT INTO DB2020_MOVIEINFO VALUES (3,'Before Sunrise', 'While travelling on a train in Europe, Jesse, an American man, meets Celine, a French woman. On his last day in Europe before returning to the US, he decides to spend his remaining hours with her.','All', 'US');
CREATE TABLE DB2020_genre(
mov_id INT NOT NULL,
genre VARCHAR(15) NOT NULL,
release_date DATE NOT NULL,
INDEX (genre),
PRIMARY KEY (mov_id, genre),
FOREIGN KEY (mov_id) REFERENCES DB2020_MOVIEINFO(MOVIE_ID)
ON DELETE CASCADE ON UPDATE CASCADE);
DESCRIBE DB2020_genre;
INSERT INTO DB2020_genre VALUES (1, 'thriller',str_to_date('20190530','%Y%m%d') );
INSERT INTO DB2020_genre VALUES (2, 'romance',str_to_date('20041024','%Y%m%d'));
INSERT INTO DB2020_genre VALUES (3, 'romance',str_to_date('19960316','%Y%m%d'));
CREATE TABLE DB2020_ON_NETFLIX(
mov_id INT NOT NULL,
on_netflix VARCHAR(10) NOT NULL,
service_start_year VARCHAR(10),
PRIMARY KEY (mov_id, on_netflix),
INDEX(mov_id),
FOREIGN KEY (mov_id) REFERENCES DB2020_MOVIEINFO(MOVIE_ID)
ON DELETE CASCADE ON UPDATE CASCADE);
DESCRIBE DB2020_ON_NETFLIX;
INSERT INTO DB2020_ON_NETFLIX VALUES (1, 'no', NULL);
INSERT INTO DB2020_ON_NETFLIX VALUES (2, 'yes', '2018');
INSERT INTO DB2020_ON_NETFLIX VALUES (3, 'yes', '2018');
I wonder what made this problem. Big thanks in advance for helping me.

This worked. natural join made some redundancies. applying left outer join helped.
CREATE VIEW DB2020_romance_on_netflix AS
SELECT
DB2020_MOVIEINFO.title, DB2020_MOVIEINFO.plot, DB2020_genre.genre
FROM
DB2020_genre left outer join DB2020_MOVIEINFO on DB2020_genre.MOVIE_ID = DB2020_MOVIEINFO.MOVIE_ID
WHERE
DB2020_genre.genre = 'romance' and
DB2020_genre.MOVIE_ID in (SELECT MOVIE_ID
FROM DB2020_ON_NETFLIX
WHERE on_netflix = 'yes');

Related

Struggling to SELECT all elements from a table

MySQL is a new language to me and I struggle with selecting more data from my loans table when I do this query:
My objective is to print all elements of the Loans table that match the Bank IDs, all I get is outputs 1-10 where I have over 13 elements in my loans table.
EDIT 1: Bank Table serves as a link between all tables, I know the problem resides in my DML query however cluelessly not sure what to do.
When running my query, only matching primary key to foreign key appears. That is if Bank ID is 1 and Loans ID is 1 it shows, but when Bank ID is 1 and Loans ID is 13 it does not appear in the query.
Please save your criticism, as mentioned above, my experience is green.
My DML:
SELECT bank.bankID, bankcustomer.FirstName, bankcustomer.LastName, loans.FirstPaymentDate
FROM bank
JOIN bankcustomer ON bank.bankID = bankcustomer.customerID
JOIN loans ON loans.LoansID = bank.bankID;
Tables DDL:
CREATE TABLE bankCustomer(
CustomerID int(11) NOT NULL AUTO_INCREMENT,
FirstName varchar(20) DEFAULT NULL,
MiddleName varchar(20) DEFAULT NULL,
LastName varchar(20) DEFAULT NULL,
Address_Line1 varchar(50) DEFAULT NULL,
Address_Line2 varchar(50) DEFAULT NULL,
City varchar(20) DEFAULT NULL,
Region varchar(20) DEFAULT NULL,
PostCode varchar(20) DEFAULT NULL,
Country varchar(30) DEFAULT NULL,
DateOfBirth DATE DEFAULT NULL,
telephoneNumber int(13) DEFAULT 0,
openingAccount int CHECK (openingAccount >= 50),
PRIMARY KEY (CustomerID),
KEY CustomerID (CustomerID)
) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE bank(
BankID int(11) NOT NULL AUTO_INCREMENT,
customerID int,
PRIMARY KEY (BankID),
FOREIGN KEY (CustomerID) REFERENCES bankCustomer(CustomerID) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE loans(
LoansID int(11) NOT NULL AUTO_INCREMENT,
BankID int,
PaymentRate int(100) DEFAULT 300,
NumOfMonthlyPayments int(12) DEFAULT NULL,
FirstPaymentDate DATE DEFAULT NULL,
MonthlyDueDate DATE DEFAULT NULL,
PRIMARY KEY (LoansID),
FOREIGN KEY (BankID) REFERENCES bank(BankID) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
INSERT DML's:
INSERT INTO bank (BankID, CustomerID) VALUES (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10);
INSERT INTO loans (LoansID, BankID, PaymentRate, NumOfMonthlyPayments, FirstPaymentDate, MonthlyDueDate) VALUES (1, 1, 400, 12, '2008-02-03', '2008-03-25'),
(11, 1, 150, 10, '2008-02-04', '2008-04-25'),
(12, 1, 150, 10, '2008-02-07', '2008-04-25'),
(2, 2, 100, 20, '2011-04-01', '2011-04-25'),
(3, 3, 85, 5, '2015-07-03', '2015-08-25')...
Thank you all for your dear help, I managed to resolve my issue. The problem was the order of JOINing clauses.
SELECT loans.LoansID, bankcustomer.FirstName, customerbankcard.AccountNumber, loans.FirstPaymentDate
FROM bank
JOIN loans ON loans.BankID = bank.bankID
JOIN bankcustomer ON bankcustomer.customerID = bank.customerID
JOIN customerbankcard ON customerbankcard.bankID = bank.bankID
GROUP BY loans.LoansID ASC;
The outcome was to avoid loop, repeating wrongly assigned account numbers with customers whose IDs did not match.
If you want to select all tables use *. Also, you are joining tables incorrectly.
SELECT bank.*, bankcustomer.*, loans.*
FROM bank
JOIN bankcustomer ON bank.customerID = bankcustomer.customerID --Since you want to join data on customer ID you select custemerID in both tables
JOIN loans ON loans.BankID = bank.bankID; --The same problem here when joining tables
Goodluck and happy coding!
Firstly, joins could only be used between the tables when the columns are common between them (though they might have different names in different tables). That is why the concept of foreign key is of paramount importance as it references the column to the referenced table as you have duly done in your DDL commands.
Secondly, try using semicolon (;) as it denotes the end of any command and might get you out of the looping.

How to make a certain set of values available in just one row

I would like to set up a database for a example project I have. I have chosen a driving school and work with tables including teachers, pupils, cars, time_lessons and bookings. The code can be seen in the code section. The four first tables i.e. teachers, pupils, cars and time_lessons are all foreign keys in bookings.
My issue is that I need certian combinations of these foreign keys to be available once. Let me clarify:
- A teacher cannot teach a same time_lessons twice
- A pupil cannot drive in a driving time_lessons twice
- A car cannot be used during the same time_lessons twice
- However a time in time_lessons can be used multiple times providing you have a pupil, a teacher and a car that haven't already been booked in that time.
How do I set this up?
See code below to see what I've done so far...
DROP DATABASE IF EXISTS c4trafik;
CREATE DATABASE c4trafik;
USE c4trafik;
CREATE TABLE teachers(
id INT PRIMARY KEY AUTO_INCREMENT,
t_fname VARCHAR(20) NOT NULL,
t_lname VARCHAR(20) NOT NULL,
auth_lvl INT DEFAULT 3
);
CREATE TABLE pupils(
id INT PRIMARY KEY AUTO_INCREMENT,
p_fname VARCHAR(20) NOT NULL,
p_lname VARCHAR(20) NOT NULL,
persnr CHAR(10) NOT NULL,
email VARCHAR(50) DEFAULT NULL,
telnr VARCHAR(10) DEFAULT NULL,
to_pay INT DEFAULT 0,
has_pay INT DEFAULT 0
);
CREATE TABLE cars(
id INT PRIMARY KEY AUTO_INCREMENT,
regnr VARCHAR(6) NOT NULL,
auto_gear BOOLEAN DEFAULT false
);
CREATE TABLE time_lessons(
id INT PRIMARY KEY AUTO_INCREMENT,
t_start TIMESTAMP NOT NULL,
t_end TIMESTAMP NOT NULL,
les_type VARCHAR(20) DEFAULT 'Driving Lesson'
);
CREATE TABLE bookings(
teachers_id INTEGER NOT NULL,
pupils_id INTEGER NOT NULL,
cars_id INTEGER NOT NULL,
time_lessons_id INTEGER NOT NULL,
FOREIGN KEY(teachers_id) REFERENCES teachers(id),
FOREIGN KEY(pupils_id) REFERENCES pupils(id) ON DELETE CASCADE,
FOREIGN KEY(cars_id) REFERENCES cars(id),
FOREIGN KEY(time_lessons_id) REFERENCES time_lessons(id) ON DELETE CASCADE,
PRIMARY KEY(teachers_id, pupils_id, cars_id, time_lessons_id)
);
INSERT INTO teachers (t_fname, t_lname, auth_lvl) VALUES
('Ali', 'Alisson', 1),
('Adam', 'Adamsson', 2),
('Noah', 'Noahsson', 3);
INSERT INTO pupils(p_fname, p_lname, persnr, email, telnr, to_pay, has_pay) VALUES
('Jakob', 'Jaboksson', '8702111254', 'pupil1#gmail.com','0704585962', 2800, 2800),
('Hassan','Hassansson', '9504234858' ,NULL,'0704125463',5000,1000),
('Mona','Monasson', '9410118547',NULL, NULL, 10200, NULL);
INSERT INTO cars (regnr, auto_gear) VALUES
('MNS111', false),
('OJS111', true),
('MNF111', false);
INSERT INTO time_lessons (t_start, t_end ) VALUES
('2019-06-10 08:00:00', '2019-06-10 08:40:00'),
('2019-06-10 08:50:00', '2019-06-10 09:30:00'),
('2019-06-10 09:40:00', '2019-06-10 10:20:00'),
('2019-06-10 10:30:00', '2019-06-10 11:10:00'),
('2019-06-10 11:20:00', '2019-06-10 12:00:00'),
('2019-06-10 13:10:00', '2019-06-10 13:50:00'),
('2019-06-10 14:00:00', '2019-06-10 14:40:00'),
('2019-06-10 14:50:00', '2019-06-10 15:30:00'),
('2019-06-10 15:40:00', '2019-06-10 16:20:00');
INSERT INTO bookings(teachers_id ,pupils_id, cars_id, time_lessons_id) VALUES
(1,2,1,1),(2,1,1,1),
(1,2,1,2),(2,1,2,2),
(1,1,1,3),(2,2,1,3);
-- Ska ge error om man duplicerar!!
-- Find main admin
SELECT t_fname, t_lname FROM teachers WHERE auth_lvl = 1;
-- How many manual geared cars?
SELECT COUNT(*) AS manually_geared FROM cars
WHERE auto_gear = false;
-- all booked student
SELECT p_fname, p_lname FROM bookings
INNER JOIN pupils ON pupils.id = bookings.pupils_id
GROUP BY p_lname, p_fname;
-- What time will each student drive?
SELECT p_fname, p_lname, t_start, t_end FROM bookings
INNER JOIN pupils ON pupils.id = bookings.pupils_id
INNER JOIN time_lessons ON time_lessons.id = bookings.time_lessons_id
ORDER BY p_lname, p_fname, t_start;
-- All time_lessons for teacher number 1
SELECT t_fname, p_fname, p_lname, t_start, t_end FROM bookings
INNER JOIN teachers ON teachers.id = bookings.teachers_id
INNER JOIN pupils ON pupils.id = bookings.pupils_id
INNER JOIN time_lessons ON time_lessons.id = bookings.time_lessons_id
WHERE teachers.id = 1;
So what I'm wondering is do I change my structure? Am I missing some code to make what I mentioned above work.
A teacher cannot teach a same time_lessons twice
Add a UNIQUE constraint on the (teacher_id,time_lesson_id) tuple in the booking, e.g.
CREATE UNIQUE INDEX booking_UX1 ON booking (teacher_id,time_lesson_id)
This will disallow rows with duplicates of a combination of values. If an attempt to INSERT a new row (or UPDATE and existing row) would cause two rows to have the same values, the statement will fail with error 1062 "Duplicate entry '' for key ".
A pupil cannot drive in a driving time_lessons twice
A UNIQUE index will prevent duplicates:
CREATE UNIQUE INDEX booking_UX2 ON booking (pupil_id,time_lesson_id)
(My preference is for entity tables be named in the singular, to name what one row represents.)

SQL error: 1364 Field 'movie_id' doesn't have a default value

I've been trying to sort out this error code for the past day or so and can't seem to find any solution. I've reviewed other post on this topic and I'm still lost. If anyone could take a look and see why I'm getting error,it would be greatly appreciated:
1364 Field 'movie_id' doesn't have a default value, .
Thank you,
David
DROP DATABASE movie_tracker;
/*task 1*/
CREATE DATABASE IF NOT EXISTS movie_tracker;
USE movie_tracker;
/*task 2*/
CREATE TABLE movies(
movie_id INT(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(200)NOT NULL,
release_date DATETIME NOT NULL,
plot_description VARCHAR(4000)NOT NULL
)ENGINE = InnoDB;
CREATE TABLE actors(
actor_id INT (11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR (100) NOT NULL,
last_name VARCHAR (100) NOT NULL,
birth_date DATETIME NOT NULL,
biography VARCHAR (1000) NOT NULL
)ENGINE = InnoDB;
CREATE TABLE locations(
location_id INT (11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
location_name VARCHAR (100) NOT NULL,
street_address VARCHAR (150) NOT NULL,
city VARCHAR(100) NOT NULL,
state CHAR(2) NOT NULL,
zip VARCHAR (5) NOT NUll
)ENGINE = InnoDB;
/*task 3*/
CREATE TABLE movies_actors(
movie_id INT NOT NULL,
actor_id INT NOT NULL
)ENGINE = InnoDB;
CREATE TABLE movies_locations (
movie_id INT (11) NOT NULL,
location_id INT(11) NOT NULL
)ENGINE = InnoDB;
ALTER TABLE movies_actors
ADD COLUMN fk_movie_id INT(11) NOT NULL,
ADD CONSTRAINT fk_movies
FOREIGN KEY (fk_movie_id)
REFERENCES movies (movie_id)
ON UPDATE CASCADE
ON DELETE CASCADE;
ALTER TABLE movies_actors
ADD COLUMN fk_actor_id INT NOT NULL,
ADD CONSTRAINT fk_actors
FOREIGN KEY(fk_actor_id)
REFERENCES actors (actor_id)
ON UPDATE CASCADE
ON DELETE CASCADE;
ALTER TABLE movies_locations
ADD COLUMN fk_movie_loc_id INT NOT NULL,
ADD CONSTRAINT fk_movies_loc
FOREIGN KEY (fk_movie_loc_id)
REFERENCES movies(movie_id)
ON UPDATE CASCADE
ON DELETE CASCADE;
ALTER TABLE movies_locations
ADD COLUMN fk_location_id INT NOT NULL,
ADD CONSTRAINT fk_locations
FOREIGN KEY (fk_location_id)
REFERENCES locations(location_id)
ON UPDATE CASCADE
ON DELETE CASCADE;
/*tast 4*/
INSERT INTO movies(title, release_date, plot_description)
VALUES ('Resident Evil: The Final Chapter','2017-01-27 18:00:00', 'Picking up
immediately after the events in Resident Evil: Retribution, Alice is the only survivor of what was meant to be humanity
s final stand against the undead. Now, she must return to where the nightmare began - The Hive in Raccoon City, where the Umbrella Corporation is gathering its forces for a final strike against
the only remaining survivors of the apocalypse.'),
('Gold','2017-01-22 18:00:00', 'Kenny Wells, a prospector desperate for
a lucky break, teams up with a similarly eager geologist and sets off on an amazing journey to find gold in the uncharted
jungle of Indonesia. Getting the gold was hard but keeping it is even more difficult, sparking an adventure through the most powerful boardrooms of Wall Street'),
('A Dog''s Purpose','2017-02-03 18:00:00', 'A devoted dog (Josh Gad)
discovers the meaning of its own existence through the lives of the humans it teaches to laugh and love. Reincarnated as
multiple canines over the course of five decades, the lovable pooch develops an unbreakable bond with a kindred spirit
named Ethan (Bryce Gheisar). As the boy grows older and comes to a crossroad, the dog once again comes back into his life to remind him of his true self.');
INSERT INTO actors(first_name, last_name, birth_date, biography)
VALUES ('Milla', 'Jovovich', '1975-12-17 01:00:00', 'Milla Jovovich is an Ukrainian-born
actress, supermodel, fashion designer, singer and public figure, who was on the cover
of more than a hundred magazines, and starred in such films as The Fifth Element (1997),
Ultraviolet (2006), and the Resident Evil (2002) franchise. '),
('Matthew', 'McConaughey', '1969-11-04 01:00:00', 'American actor and producer
Matthew David McConaughey was born in Uvalde, Texas. His mother, Mary Kathleen (McCabe), is a substitute school teacher originally
from New Jersey. His father, James Donald McConaughey, was a Mississippi-born gas station owner who ran an oil pipe supply business. '),
('Josh', 'Gad', '1981-02-23 18:00:00', 'Josh Gad was born on February 23, 1981 in
Hollywood, Florida, USA. He is an actor and writer, known for Frozen (2013), Love & Other Drugs (2010) and The
Wedding Ringer (2015). ');
INSERT INTO locations (location_name, street_address, city, state, zip)
VALUES ('AMC Loews Waterfront 22', '300 W Waterfront Dr.', 'West Homestead', 'PA', '15120'),
('Cinemark North Hills and XD', 'McCandless Crossing, 851 Providence Blvd', 'Pittsburgh', 'PA', '15237'),
('Century Square Luxury Cinemas', '2001 Mountain View Dr.', 'West Mifflin', 'PA', '15122');
/*task 5*/
ALTER TABLE movies_locations
ADD COLUMN show_time TIME NOT NULL,
ADD COLUMN view_format VARCHAR (50) NOT NULL;
INSERT INTO movies_locations(show_time, view_format)
VALUES (183000,'Standard'),
(190000,'IMAX'),
(214500,'3D');
ALTER TABLE movies_actors
ADD COLUMN character_name VARCHAR (100) NOT NULL,
ADD COLUMN lead_role VARCHAR (5) NULL,
ADD COLUMN support_role VARCHAR (5) NULL;
INSERT INTO movies_actors(character_name, lead_role, support_role)
VALUES ('Alicia Marcus', 'YES', 'NO'),
('Kenny Wells', 'YES', 'NO'),
('Bailey / Buddy / Tino / Ellie (voice)', 'YES', 'NO');
/*task 6*/
This line is causing error
INSERT INTO movies_locations(show_time, view_format)
VALUES (183000,'Standard'),
(190000,'IMAX'),
(214500,'3D');
You must add movie_id and location_id in the insert query because they are foreign keys and marked as NOT NULL.
I am not getting why u have added extra two columns
ALTER TABLE movies_locations
ADD COLUMN fk_movie_loc_id INT NOT NULL,
ALTER TABLE movies_locations
ADD COLUMN fk_location_id INT NOT NULL,
I think you should delete these two columns and if u correct your insert query like this problem might solve.
INSERT INTO movies_locations(movie_id,location_id,show_time, view_format)
VALUES (movieid,locationid,183000,'Standard');

SQL Querying: How to Find the Maxima of Certain Columns within Different Sets of Similar Records?

How do I find all the secret ingredients that were used in my pizzas which have won a prize? I’m missing the SQL condition in the comment below:
SELECT r.name, p.secret_ingredient FROM restaurants AS r
JOIN restaurant_has_pizzas AS rhp ON rhp.restaurant_id = r.id
JOIN pizzas AS p ON p.id = rhp.pizza_id
JOIN awarded_prizes AS a ON a.id = r.latest_prize_id
WHERE r.owner = 'me!'
AND p.created_at < a.won_at
-- AND p is as young/new as possible, i.e., p.created_at is as close to
-- a.won_at as possible; I’m only interested in the winning pizzas which
-- have been made right before the taster awarded us a prize!
;
The query so far returns all kinds of ingredients that were used in restaurants which have won a prize. However, I’m only interested in those secret ingredients that were used in winning pizzas. Note that my chefs have all created newer pizzas since we have won the last prizes.
Here’s the DDL:
CREATE TABLE pizzas
(`id` INT NOT NULL AUTO_INCREMENT,
`created_at` DATETIME NOT NULL,
`secret_ingredient` VARCHAR(42) NOT NULL,
PRIMARY KEY (`id`))
;
INSERT INTO pizzas
(`created_at`, `secret_ingredient`)
VALUES
('2012-11-01', 'peas'),
('2012-12-01', 'pepper'),
('2012-12-11', 'pork'),
('2012-12-21', 'peanuts'),
('2012-12-31', 'oranges'),
('2013-01-02', 'ham'),
('2013-01-20', 'oranges'),
('2013-01-21', 'root beer'),
('2013-03-22', 'mushrooms')
;
CREATE TABLE awarded_prizes
(`id` INT NOT NULL AUTO_INCREMENT,
`won_at` DATETIME NOT NULL,
PRIMARY KEY (`id`))
;
INSERT INTO awarded_prizes
(`won_at`)
VALUES
('2012-12-23'),
('2013-02-02')
;
CREATE TABLE restaurants
(`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(42) NOT NULL,
`owner` VARCHAR(42) NOT NULL,
`latest_prize_id` INT,
PRIMARY KEY (`id`),
CONSTRAINT `fk_restaurants_awarded_prizes1`
FOREIGN KEY (`latest_prize_id`)
REFERENCES `awarded_prizes` (`id`))
;
INSERT INTO restaurants
(`name`, `owner`, `latest_prize_id`)
VALUES
('Don Camillo', 'me!', 1),
('Tasty Pizzas', 'me!', 2),
('B. R.', 'Don Alphonso', NULL)
;
CREATE TABLE restaurant_has_pizzas
(`restaurant_id` INT NOT NULL,
`pizza_id` INT NOT NULL,
PRIMARY KEY (`restaurant_id`, `pizza_id`),
CONSTRAINT `fk_restaurant_has_pizzas_restaurants1`
FOREIGN KEY (`restaurant_id`)
REFERENCES `restaurants` (`id`),
CONSTRAINT `fk_restaurant_has_pizzas_pizzas1`
FOREIGN KEY (`pizza_id`)
REFERENCES `pizzas` (`id`))
;
INSERT INTO restaurant_has_pizzas
(`restaurant_id`, `pizza_id`)
VALUES
(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(2, 6),
(1, 7),
(2, 8),
(2, 9)
;
See also this SQL Fiddle. Award-winning ingredients are only “peanuts” for “Don Camillo” and “root beer” for “Tasty Pizzas”. I don’t want to see the other ingredients which are currently returned by the SQL query. This is the expected result table:
name | secret_ingredient
-------------|------------------
Don Camillo | peanuts
Tasty Pizzas | root beer
Background
Ok, this is admittedly just a crafted example based on a more complex real life DB schema … but the latter was just too boring compared with my restaurant empire to show it here :-)
SELECT a.name
, b.secret_ingredient
FROM
( SELECT r.*
, MAX(p.created_at) max_created_at
FROM restaurants r
JOIN awarded_prizes z
ON z.id = r.latest_prize_id
JOIN pizzas p
ON p.created_at <= z.won_at
GROUP
BY r.id
) a
JOIN pizzas b
ON b.created_at = a.max_created_at;
+--------------+-------------------+
| name | secret_ingredient |
+--------------+-------------------+
| Don Camillo | peanuts |
| Tasty Pizzas | root beer |
+--------------+-------------------+
I'm not convinced that either of these ingredients belong anywhere near a pizza.

MySQL using IN/FIND_IN_SET to read multiple rows in sub query

I have two tables, locations and location groups
CREATE TABLE locations (
location_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(63) UNIQUE NOT NULL
);
INSERT INTO locations (name)
VALUES
('london'),
('bristol'),
('exeter');
CREATE TABLE location_groups (
location_group_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
location_ids VARCHAR(255) NOT NULL,
user_ids VARCHAR(255) NOT NULL,
name VARCHAR(63) NOT NULL,
);
INSERT INTO location_groups (location_ids, user_ids, name)
VALUES
('1', '1,2,4', 'south east'),
('2,3', '2', 'south west');
What I am trying to do is return all location_ids for all of the location_groups where the given user_id exists. I'm using CSV to store the location_ids and user_ids in the location_groups table. I know this isn't normalised, but this is how the database is and it's out of my control.
My current query is:
SELECT location_id
FROM locations
WHERE FIND_IN_SET(location_id,
(SELECT location_ids
FROM location_groups
WHERE FIND_IN_SET(2,location_groups.user_ids)) )
Now this works fine if the user_id = 1 for example (as only 1 location_group row is returned), but if i search for user_id = 2, i get an error saying the sub query returns more than 1 row, which is expected as user 2 is in 2 location_groups. I understand why the error is being thrown, i'm trying to work out how to solve it.
To clarify when searching for user_id 1 in location_groups.user_ids the location_id 1 should be returned. When searching for user_id 2 the location_ids 1,2,3 should be returned.
I know this is a complicated query so if anything isn't clear just let me know. Any help would be appreciated! Thank you.
You could use GROUP_CONCAT to combine the location_ids in the subquery.
SELECT location_id
FROM locations
WHERE FIND_IN_SET(location_id,
(SELECT GROUP_CONCAT(location_ids)
FROM location_groups
WHERE FIND_IN_SET(2,location_groups.user_ids)) )
Alternatively, use the problems with writing the query as an example of why normalization is good. Heck, even if you do use this query, it will run more slowly than a query on properly normalized tables; you could use that to show why the tables should be restructured.
For reference (and for other readers), here's what a normalized schema would look like (some additional alterations to the base tables are included).
The compound fields in the location_groups table could simply be separated into additional rows to achieve 1NF, but this wouldn't be in 2NF, as the name column would be dependent on only the location part of the (location, user) candidate key. (Another way of thinking of this is the name is an attribute of the regions, not the relations between regions/groups, locations and users.)
Instead, these columns will be split off into two additional tables for 1NF: one to connect locations and regions, and one to connect users and regions. It may be that the latter should be a relation between users and locations (rather than regions), but that's not the case with the current schema (which could be another problem of the current, non-normalized schema). The region-location relation is one-to-many (since each location is in one region). From the sample data, we see the region-user relation is many-many. The location_groups table then becomes the region table.
-- normalized from `location_groups`
CREATE TABLE regions (
`id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(63) UNIQUE NOT NULL
);
-- slightly altered from original
CREATE TABLE locations (
`id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(63) UNIQUE NOT NULL
);
-- missing from original sample
CREATE TABLE users (
`id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(63) UNIQUE NOT NULL
);
-- normalized from `location_groups`
CREATE TABLE location_regions (
`region` INT UNSIGNED,
`location` INT UNSIGNED UNIQUE NOT NULL,
PRIMARY KEY (`region`, `location`),
FOREIGN KEY (`region`)
REFERENCES regions (id)
ON DELETE restrict ON UPDATE cascade,
FOREIGN KEY (`location`)
REFERENCES locations (id)
ON DELETE cascade ON UPDATE cascade
);
-- normalized from `location_groups`
CREATE TABLE user_regions (
`region` INT UNSIGNED NOT NULL,
`user` INT UNSIGNED NOT NULL,
PRIMARY KEY (`region`, `user`),
FOREIGN KEY (`region`)
REFERENCES regions (id)
ON DELETE restrict ON UPDATE cascade,
FOREIGN KEY (`user`)
REFERENCES users (id)
ON DELETE cascade ON UPDATE cascade
);
Sample data:
INSERT INTO regions
VALUES
('South East'),
('South West'),
('North East'),
('North West');
INSERT INTO locations (`name`)
VALUES
('London'),
('Bristol'),
('Exeter'),
('Hull');
INSERT INTO users (`name`)
VALUES
('Alice'),
('Bob'),
('Carol'),
('Dave'),
('Eve');
------ Location-Region relation ------
-- temporary table used to map natural keys to surrogate keys
CREATE TEMPORARY TABLE loc_rgns (
`location` VARCHAR(63) UNIQUE NOT NULL
`region` VARCHAR(63) NOT NULL,
);
-- Hull added to demonstrate correctness of desired query
INSERT INTO loc_rgns (region, location)
VALUES
('South East', 'London'),
('South West', 'Bristol'),
('South West', 'Exeter'),
('North East', 'Hull');
-- map natural keys to surrogate keys for final relationship
INSERT INTO location_regions (`location`, `region`)
SELECT loc.id, rgn.id
FROM locations AS loc
JOIN loc_rgns AS lr ON loc.name = lr.location
JOIN regions AS rgn ON rgn.name = lr.region;
------ User-Region relation ------
-- temporary table used to map natural keys to surrogate keys
CREATE TEMPORARY TABLE usr_rgns (
`user` INT UNSIGNED NOT NULL,
`region` VARCHAR(63) NOT NULL,
UNIQUE (`user`, `region`)
);
-- user 3 added in order to demonstrate correctness of desired query
INSERT INTO usr_rgns (`user`, `region`)
VALUES
(1, 'South East'),
(2, 'South East'),
(2, 'South West'),
(3, 'North West'),
(4, 'South East');
-- map natural keys to surrogate keys for final relationship
INSERT INTO user_regions (`user`, `region`)
SELECT user, rgn.id
FROM usr_rgns AS ur
JOIN regions AS rgn ON rgn.name = ur.region;
Now, the desired query for the normalized schema:
SELECT DISTINCT loc.id
FROM locations AS loc
JOIN location_regions AS lr ON loc.id = lr.location
JOIN user_regions AS ur ON lr.region = ur.region
;
Result:
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+