Been reading up on this (for ages!) and can't get clear picture.
First, if I have two tables (e.g. recipes and ingredients) with a many-to-many relationship and I create a intermediary/relational table, how do I write an SQL query to to find all recipes with, say, bananas in them?
Second, why would I have this third relational table if I can find the same information using JOIN queries without the third tables creation??
Really appreciate a clear, helpful explanation, thanks.
how do I write an SQL query to to find all recipes with, say, bananas in them?
You can do:
select distinct r.id, r.name
from recipe r
join recipe_ingredient ri on ri.id_recipe = r.id
join ingredient i on i.id = ri.id_ingredient
where i.name = 'banana'
Second, why would I have this third relational table if I can find the same information using JOIN queries without the third tables creation?
Since a recipe can have many ingredients, and an ingredient can be related to many recipies the relationship between those two tables is not 1:n but n:m. Therefore, you need an intermediate table, as shown below:
create table recipe (
id int primary key not null,
name varchar(20)
);
create table ingredient (
id int primary key not null,
name varchar(20)
);
create table recipe_ingredient (
id int primary key not null,
id_recipe int not null,
id_ingredient int not null,
quantity double not null,
foreign key fk1 (id_recipe) references recipe (id),
foreign key fk2 (id_ingredient) references ingredient (id)
);
If an ingredient showed up in a single recipe always, the structure would be simpler, as you seem to think. This simpler structure would probably look like:
create table recipe (
id int primary key not null,
name varchar(20)
);
create table ingredient (
id int primary key not null,
name varchar(20),
id_recipe int not null,
foreign key fk3 (id_recipe) references recipe (id)
);
A model like this one is not really practical in this case. You would end up having the same ingredient multiple times. For example, if a cake uses "flour" and bread uses "flour", then "flour" would end up twice in the ingredients table. Not a great idea.
Related
Scenario:
I've been trying to solve a problem where I wanted to make a natural join between two tables Artists and Albums, where Artists cointains a column ar_id that is the primary key and Albums contains a column also named ar_id where the foreign key in the Album table is ar_id and refers to Artists ar_id. There's a 1 to many relationship between the tables (one artist can have multiple albums).
Problem:
When I want to make a NATURAL JOIN between the two tables Artists and Albums it returns 0 rows, but when I use are normal JOIN with WHERE function it returns 18 rows as it should. So I guess the problem is the foreign key setup but I can't find the problem
The select code with natural join (doesn't work):
SELECT * FROM
Artists NATURAL JOIN Albums;
The select code with normal join where (does work):
SELECT * FROM
Artists JOIN Albums
WHERE CDReg.Artists.ar_id = CDReg.Albums.ar_id;
DLL for the two tables
CREATE TABLE Artists (
ar_id int PRIMARY KEY,
ge_id int(11) DEFAULT NULL,
country_code varchar(2) DEFAULT NULL,
name varchar(45) NOT NULL,
start_year year(4) DEFAULT NULL,
end_year year(4) DEFAULT NULL,
FOREIGN KEY (ge_id) REFERENCES Genres (ge_id),
FOREIGN KEY (country_code) REFERENCES Countries (code)
);
-- --------------------------------------------------------
CREATE TABLE Albums (
al_id int PRIMARY KEY,
ar_id int,
name varchar(45) NOT NULL,
release_year year(4) DEFAULT NULL,
FOREIGN KEY (ar_id) REFERENCES Artists(ar_id)
);
Thanks for any help in advance :)
[SOLVED]:
I thought that natural join used the foreign key to join the tables but instead it uses all matching column names including the columns "name" (exsists in both tables), since there isn't any artists with an eponymous album title in the database the result was 0 rows. The solution was to use
SELECT * FROM
Artists JOIN albums USING(ar_id);
From the documentation:
The NATURAL [LEFT] JOIN of two tables is defined to be semantically
equivalent to an INNER JOIN or a LEFT JOIN with a USING clause that
names all columns that exist in both tables.
Both tables have a column name which is included in the natural join, making it fail for all combinations except for those where the artist and album names are the same (which I guess could happen).
You could use a join FROM Artists JOIN albums USING (ar_id) instead.
The natural join probably use namein the join which leads to 0 results unless an artist has an eponymous album!
Anyhow you should avoid natural join because, as you can see, they are less obvious. Stick to the normal join version.
If this question is a little vague just let me know and I will provide more info.
I have written a query that gets data from multiple tables but it isn't working how I expected it too and I am completely stumped.
Here is my code:
SELECT students.student_fname, students.student_lname
FROM students, enrolments
WHERE enrolments.courseID = 'C001';
but this just returns all of the students first and last names in the students table and these names are displayed twice.
Here is the code for the two tables:
CREATE TABLE students
(
studentID CHAR(10) NOT NULL,
student_fname VARCHAR(15) NOT NULL,
student_lname VARCHAR(15) NOT NULL,
DOB VARCHAR(10) NOT NULL,
CONSTRAINT pk_students PRIMARY KEY (studentID)
);
CREATE TABLE enrolments
(
enrolmentNo int NOT NULL AUTO_INCREMENT,
studentID CHAR(10) NOT NULL,
courseID CHAR(4) NOT NULL,
CONSTRAINT pk_enrolments PRIMARY KEY (enrolmentno),
FOREIGN KEY (studentID) REFERENCES students (studentID),
FOREIGN KEY (courseID) REFERENCES courses (courseID)
)ENGINE = INNODB;
This is because you've not defined how students relate to enrolments.
You either need to use an inner join or add a where clause that show how they relate.
For example:
FROM Students
INNER JOIN enrolments on Students.ID = enrolments.studentID
Or
FROM students, enrolements
WHERE enrolments.studentID = students.ID
The first method is newer and preferred by many; but the legacy method is also supported.
To gain a better understanding of table joins take a look at this most excellent article: http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html
Otherwise you get what is called a Cartesian product of the two tables all data relates to all data.
If you do not have a unique index on enrolements (a student can only have 1 class enrolment) then a select Distinct field names or where... group by fields will also limit your results to close to what you're looking for as well.
============================ in response to follow-up comment===================
SELECT S.student_fname, S.student_lname
FROM students S
INNER JOIN enrolments E
ON S.StudentID = E.StudentID
WHERE e.courseID = 'C001'
GROUP BY S.Student_Fname, S.Student_lname
ORDER BY S.Student_LName, S.Student_FName
The group by eliminates duplicates for the same course.
The "ON statement tells the database how students relates to enrolments.
The order by is just to provide a reasonable order to results.
You cannot fetch data from two tables in this way. You have to join tables.
Imagine we have a phpadmin database and we have a table for students and student_music and student_friend student_friend_music. Does this make sense performance wise to have four tables? or do you think we need to have three tables for students students_friends student_music and table for music. How does facebook stores friends relationship in it's database?
Other thing is that I designed the data base for both. I don't know if I see any difference but I think once the users increase one would beat other performance wise?
So my question concerns the performances of querying:
Is this better to have more tables or
Can we have duplicates.
Do you know any good book, tutorial or reference I can study to know about relational data bases in Php my admin and mysql.
Update:
Table Student can have many to many relation with itself.
Table Music has Many to Many with the student as well.
Student id Student_friend Music id STUDENT_MUSIC
A 1 1-3 YT 1 1 3
B 2 2-3 RU 2 2 3
C 3 PI 3 3 1
3 2
So I am using something called Data Mapper in code igniter which causing me a headache but this sounds like a structure I am thinking now.
table suggestions:
table_students (contains students info, etc)
table_music (music pref of studs with student id from table_students)
table_friends (contains student id's from table students and flag value either friend or not friend)
Check out this link for a mysql introduction and the wikipedia article on relational databases. Read about tables, primary and foreign keys. Before worrying about performance you need to address the structure of your database.
Try (One-to-Many:A student can own many pieces of music and have one friend):
CREATE TABLE Student(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
first_name VARCHAR(30),
last_name VARCHAR(30),
friend_id INT)
CREATE TABLE Music(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
music_title VARCHAR(30),
music_student_id INT)
FOREIGN KEY (music_student_id) REFERENCES Student(id)
ON DELETE CASCADE
Or Try (Many-to-Many:Many students can own many pieces of music and have many friends):
CREATE TABLE Student(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
first_name VARCHAR(30),
last_name VARCHAR(30))
FOREIGN KEY (id)
REFERENCES StudentMusic (Student_id)
ON DELETE CASCADE
CREATE TABLE Music(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
music_title VARCHAR(30),
FOREIGN KEY (id)
REFERENCES StudentMusic (Music_id)
ON DELETE CASCADE
CREATE TABLE StudentMusic (
Student_id INT NOT NULL AUTO_INCREMENT,
Music_id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (Student_id,Music_id)
CREATE TABLE Friendships(
student_A_id INT,
student_B_id INT)
PRIMARY KEY (student_A_id,student_B_id)
Handling the data views of the relationships can be shown using a Select statement. In the One-to-Many design finding a Student's music uses the following query:
Select Student.first_name,Student.last_name,Music.music_title
FROM Student
LEFT JOIN Music on (Student.ID=Music.music_student_id)
Part of designing a database is figuring out what relationships you will need to query.
Also look into normalizing databases.
I want to make a link between a table customer and a table product by an IdProduct.
Example:
Create table customer(
idcustomer INT not null,
name Varchar(20),
idproduct INT,
);
create table Product(
idproduct INT not null,
nameProduct varchar(40)
);
How can I link the two together like the foreign key system for, when I select a customer, I can get all his products? It's a question about the structure of the database.
You want to introduce a 3rd table to resolve the many-to-many relationship between customers and products. It should consist of idcustomer and idproduct.
Then, to get all the products for a given customer:
SELECT c.name, p.nameProduct
FROM Customer c
INNER JOIN CustomerProductXref cpx
ON c.idcustomer = cpx.idcustomer
INNER JOIN product p
ON cpx.idproduct = p.idproduct
WHERE c.idcustomer = 12345
In mysql a foreign key is a special type of constraint. It is preferably created with the table, but can also be added afterwards. In this case, you might define the constraint as:
ALTER TABLE customer
ADD FOREIGN KEY (idproduct)
REFERENCES Product (idproduct);
(Note that you have to use the InnoDB engine to take advantage of FK's in mysql. More here
However FK's aren't required to make a JOIN, which is how you would link the tables in a SELECT -
select c.idcustomer, c.name, p.nameproduct
from customer c
join Product p on p.idproduct=c.idproduct;
Here's how you'd make a foreign key constraint (ignoring the cardinality issues that Joe rightly suggests):
CREATE table Product(
idproduct INT not null,
nameProduct varchar(40),
PRIMARY KEY (idproduct )
);
CREATE table customer(
idcustomer INT not null,
name Varchar(20),
idproduct INT,
FOREIGN KEY (idproduct) REFERENCES Product(idproduct )
);
Get your data like this:
SELECT * FROM Product AS P
INNER JOIN Customer AS C ON C.idproduct = P.idproduct
WHERE C.idcustomer = 1
I am having a little bit of a problem with setting up a mysql table that will hold a list of categories and subcategories. I am not to sure how to setup the table. Does it need to be 2 separate tables? 1 for the main categories and 1 for the subcategories or can it be all in 1 table? Would something like this work?
Create Table categories (
category_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
sub_id INT UNSIGNED NOT NULL,
name VARCHAR(100) NOT NULL,
PRIMARY KEY (category_id)
)
CREATE TABLE items (
item_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
description VARCHAR(100) NOT NULL,
PRIMARY KEY (item_id),
FOREIGN KEY (category_id) REFERENCES categories (category_id),
FOREIGN KEY (sub_id) REFERENCES categories (sub_id)
)
Will this work or is this completely wrong? Thanks in advance for the help!
If you are 100% sure that you'll only have two levels of categories (main and sub), you can do a few different things. None of them are your proposed solution:
CREATE TABLE categories (
id int not null primary key,
main varchar(64)
sub varchar(64)
);
CREATE TABLE objects (
id int not null primary key,
category_id int,
name varchar(64),
FOREIGN KEY (category_id) REFERENCES categories (id)
);
Want all vehicles?
SELECT *
FROM objects AS o
INNER JOIN categories AS c ON o.category_id = c.id
WHERE c.main = 'vehicles';
Want all roflcopters?
SELECT *
FROM objects AS o
INNER JOIN categories AS c ON o.category_id = c.id
WHERE c.main = 'vehicles' and c.sub='Roflcopters';
If you want something in the "vehicle" category, but not in any of the subcategories of verhicles, just have a category record where main='vehicles' with sub NULL.
Of course, this is not particularly flexible. You're stuck with just two levels of categorization, and there's not a lot of business logic embedded in your category model. But it might be sufficient for your needs.
Two other good, proven, models are the adjacency list model, and the nested set model, both of which are described, with lots of nice example mysql code, over here
It depends.
Are categories and subcategories really two different things? This means categories have no parent, while subcategories are always in a parent category and have no further subs of themselves. Then two tables is ok.
If it's like a tree though, where there are just categories, which can both be children and have children, you should use one table (Google "nested set").
(Or maybe you don't mean category/subcategory but primary category/secondary category, where the secondary category is not fixed to a certain primary category. Electronics + Cycling instead of Cycling->Speedometers. Then you could use one table if it could also be Cycling + Electronics)
It is obviously work. But you may use separate tables.
create table categories
(
categoryId int not null,
categoryName varchar(20) not null,
primary key(categoryId)
);
create table subcategories
(
subcategoryId int not null,
subcategoryName varchar(20) not null,
parentId int not null,
primary key(subcategoryId),
foreign key(categoryId) references categories(categoryId)
);
create tables items
(
item_id int unsigned not null auto_increment,
name varchar(255) not null,
description varchar(100) not null,
primary key(item_id),
foreign key(categoryId) references categories(categoryId),
foreign key(subcategoryId) references subcategories(subcategoryId)
)