Sorry if the title is unclear.
As of right now, I just have a spreadsheet of a bunch of customers and orders. For example, a line in the spreadsheet might look like:
A Customer with an ID of 1 with name Sally and address 291 North Street bought item id 2.
The actual spreadsheet looks something like this table:
Customer Id
Customer Name
Customer Address
Item Id
Name
Cost
Order Id
Ordered Date
1
Sally
291 North Street
2
Long Sleeves
$20
1
1/1/2022
1
Sally
291 North Street
1
Shirt
$15
1
1/1/2022
2
George
892 Lakers Ave
3
Backpack
$30
5
4/9/2022
My goal is to properly normalize this data so it's not as redundant. I've already separated the data into 3 tables, Items, Orders, and OrderInfo.
Items follows a structure like so:
Item Id (PK)
Name
Cost
1
XL Shirt
$15
2
Long sleeves shirt
$20
3
Backpack
$30
Orders:
Order ID (PK/FK?)
Customer ID
Ordered Date
1
1
1/1/2022
5
2
4/9/2022
OrderInfo:
Order ID (PK/FK?)
Item ID (PK/FK?)
1
2
1
1
5
3
As you can see from the orders table, I tried to combine all redundant orders where say user Sally ordered a long sleeves shirt and a regular shirt in the same order. However, this leaves redundant data in the OrdersInfo table, where the OrderId is the same for multiple fields because the customer bought multiple items in one order.
Is this correct? I am trying to define relationships on the tables in LibreOffice Base, and I can define the correct one-to-many relationships for all of them except for OrderInfo and Orders.
Here's a screenshot of the relations and the error when I try to link the OrderID field.
Error code:
SQL Status: S0011
Error code: -170
Primary or unique constraint required on main table: "Orders" in statement [ALTER TABLE "Order_Info" ADD FOREIGN KEY ("order_id") REFERENCES "Orders" ("order_id")]
A foreign key must reference the primary key (or unique key) of the referenced table. You will get the error you show if no such primary/unique key is defined.
Example:
CREATE TABLE Orders (
order_id INT NOT NULL,
customer_id INT NOT NULL,
PRIMARY KEY(order_id), <-- this is probably not defined
FOREIGN KEY (customer_id) REFERENCES Customers(customer_id)
);
CREATE TABLE Order_info (
order_id INT NOT NULL,
item_id INT NOT NULL,
quantity INT NOT NULL,
PRIMARY KEY (order_id, item_id),
FOREIGN KEY (order_id) REFERENCES Orders(order_id),
FOREIGN KEY (item_id) REFERENCES Items(item_id)
);
Related
I have two tables say, Hotel with Primary Key Hotel_ID and Staff with Primary Key Staff_ID.
The Hotel table also has another column, Manager_ID referencing Staff_ID of Staff.
The Staff table references Hotel_ID using its column H_ID to indicate which hotel the staff is working at.
I have been trying to figure out if it is possible to restrict allowed values for Manager_ID so that only the staff of that same hotel can be made the manager.
I understand that there is a cross-reference here but my understanding is that it shouldn't be a problem. Could someone tell me how this could be incorporated into the create statement for the Hotel table?
You can add a multicolumn foreign key to select not just the id of the staff, but also the assigned hotel_id inside your hotel table. This would result in a weird looking table structure like this:
Hotel
- Id
- Name
- Manager_Id
- Hotel_Id
Staff
- Id
- Hotel_Id
- IsManager
- Firstname
- Lastname
This is specially looking weird as you have the same id in the Hotel table twice. And for that to work correctly, you have to add triggers to verify that the Id and Hotel_Id value are the same, when the Hotel_Id column is used. And since you have some kind of circle references (Staff references the Hotel table via Hotel_Id, Hotel references the Staff table via Manager_Id) you have to run ALTER TABLE statements to add the additional columns Manager_Id and Hotel_Id. The queries might look like this:
CREATE TABLE Hotel
(
Id INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(30)
);
CREATE TABLE Staff
(
Id INT AUTO_INCREMENT PRIMARY KEY,
Hotel_Id INT NOT NULL,
FirstName VARCHAR(30),
LastName VARCHAR(30),
FOREIGN KEY (Hotel_Id) REFERENCES Hotel(Id),
INDEX (Id, Hotel_Id)
);
The second index is used for the foreign key in the Hotel table:
ALTER TABLE Hotel ADD Manager_ID INT NULL;
ALTER TABLE Hotel ADD Hotel_ID INT NULL;
ALTER TABLE Hotel ADD FOREIGN KEY (Manager_ID, Hotel_ID) REFERENCES Staff(Id, Hotel_Id);
Unfortunately, you cannot use a CHECK CONSTRAINT on the value of the Id column. If you could, it would be possible to write something like:
ALTER TABLE Hotel ADD CONSTRAINT CHK_Hotel CHECK (Hotel_ID = Id OR Hotel_Id IS NULL);
This would verify that the Hotel_Id column, which value come from the Staff table, must be the same as the Id column. However, you will get the following error message:
Check constraint 'CHK_Hotel' cannot refer to an auto-increment column.
You have to add a trigger for INSERT and UPDATE queries (see questions like Constant column value in MySQL table) to make that check.
The following example queries shows how the foreign keys are working:
INSERT INTO Hotel (Name) VALUES ('Some Hotel Name');
SELECT * FROM Hotel;
+----+-----------------+------------+----------+
| Id | Name | Manager_ID | Hotel_ID |
+----+-----------------+------------+----------+
| 1 | Some Hotel Name | NULL | NULL |
+----+-----------------+------------+----------+
INSERT INTO Staff (Hotel_Id, FirstName, LastName) VALUES (1, 'John', 'Doe');
SELECT * FROM Staff;
+----+----------+-----------+----------+
| Id | Hotel_Id | FirstName | LastName |
+----+----------+-----------+----------+
| 1 | 1 | John | Doe |
+----+----------+-----------+----------+
UPDATE Hotel SET Manager_Id = 1, Hotel_Id = 1 WHERE Id = 1;
SELECT * FROM Hotel;
+----+-----------------+------------+----------+
| Id | Name | Manager_ID | Hotel_ID |
+----+-----------------+------------+----------+
| 1 | Some Hotel Name | 1 | 1 |
+----+-----------------+------------+----------+
UPDATE Hotel SET Manager_Id = 1, Hotel_Id = 2 WHERE Id = 1;
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test`.`Hotel`, CONSTRAINT `Hotel_ibfk_1` FOREIGN KEY (`Manager_ID`, `Hotel_ID`) REFERENCES `Staff` (`Id`, `Hotel_Id`))
You can create a flag or role column in the staff table to indicate if the person is a manager or has a specific role in the hotel. You won't need the Manager_Id column in the hotel table anymore. The tables might look like this:
Hotel
- Id
- Name
Staff
- Id
- Hotel_Id
- IsManager
- Firstname
- Lastname
When you add a new stuff you can set the IsManager flag to indicate that this person is the manager of the hotel, identified by the id in the Hotel_Id column.
I'm new to mysql and am trying to link two tables and am not sure how, i have a products table with a list of products in a restaurant, the column in this table are
ID(primary key)
name
price
The other table is called extras, this table contains extra things that you can add to your order but are optional, for when buying chicken you have an option of spicy and non spicy,
Some products have more than one extras, for instance a product can have the option of choosing three extras.
The extras table at the moment only has
ID(primary key)
name
not sure how to link the two or where to put foreign constraints.
UPDATE
Same extra may also belong to numerous products
A joining/linking table usually uses many-to-many relationships by joining the 2 parent tables/primary keys to allow many products to have many extras or no extras at all.
so for example:
eg:
Product IDs (primary keys) are: 1, 2, 3
Product names are: chicken wings, chicken breast, chicken fillet
Extras IDs (primary keys) are: 1, 2, 3
Extras names are: mild, medium, hot
Wings, breasts and fillet have the option of all three of the extras, so the product_extras table would end up looking something like this:
product_extras_id | product_id | extras_id
------------------------------------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 2
6 | 2 | 3
7 | 3 | 1
8 | 3 | 2
9 | 3 | 3
With products having many extras and extras applying to many products this is actually a many to many relationship.
create table products(ID int auto_increment Primary Key,
Name varchar(50),
Price decimal(6,2));
create table extras(ID int auto_increment Primary Key,
Name varchar(50));
create table product_extras(Product int Not Null,
Extra int Not Null,
FOREIGN KEY(Product) References products(ID) ON DELETE CASCADE,
FOREIGN KEY(Extra) References extras(ID) ON DELETE CASCADE);
Something akin to this should work for you, though you may want to change the datatypes based off preference and what data actually needs to go in there.
In your case one product may have many extras(1 to many) and many products may be having same extra thing(many to 1). Thus, this is a many to many relationship and for such relations we need 3 tables.
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
price DECIMAL(10.2)
);
CREATE TABLE extras (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50)
);
CREATE TABLE products_extras (
id INT PRIMARY KEY AUTO_INCREMENT,
pro_id INT,
FOREIGN KEY(pro_id) REFERENCES products(id),
ext_id INT,
FOREIGN KEY(ext_id) REFERENCES extras(id)
);
I want to create a table that contain 2 foreign key that references on the same field of the same table:
I've the table "nation" and the table "relation"
2 nations form a relation like "USA and Russia are Neutral"
i made with
create table relation(
id int(3) primary key,
nation1 int(3),
foreign key (nation1) references nation(id),
nation2 int(3),
foreign key (nation2) references nation(id),
type int(1)
);
thats works but
i can create ambiguous records simply inverting nation1 and nation2 values like:
1 USA RUSSIA 0
2 RUSSIA USA 1
i can't make effective queries like
SELECT nation1.name,nation2.name,relation.type
FROM nation.relation
WHERE relation.type = "0"
AND nation.name = "USA"
AND nation.id = relation.nation1
because "USA" can be under "nation2"
How can i resolve those 2 problems?
So I understand how to create foreign keys and I know what is the purpose of the FK. But I have a problem in understanding How to use them. I asked a question regarding Foreign keys HERE(Click link)
Here is what I made:
CREATE TABLE user(
id INT(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(20) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE items(
i_id INT(11) NOT NULL AUTO_INCREMENT,
name TINYTEXT NOT NULL,
price DECIMAL(8,2) NOT NULL,
PRIMARY KEY (i_id)
);
CREATE TABLE user_purchase(
i_id INT(11) NOT NULL,
name TINYTEXT NOT NULL,
id INT(11) NOT NULL,
FOREIGN KEY (i_id) REFERENCES items(i_id),
FOREIGN KEY (name) REFERENCES items(name),
FOREIGN KEY (id) REFERENCES user(id)
);
Now my question is how do I make the most out of this using PHP? From the link above, people have suggested that it's good to use only one foreign key in the user_purchase table, but what if I want several columns? Why don't we use several foreign keys for different columns of the same table?
I am using mysql and php. I would appreciate it if you could show some examples of how you use PHP with the tables which have foreign keys to get get information using MYSQL commands. I really need a thorough explanation.
I also need to understand the terms Normalization and Denormalization. I would appreciate if you could give some links which explain these terms in great detail with examples or if you have any suggestion for some great books for beginners in database design, implementation, etc, I would really appreciate.
Thanks a lot.
Foreign key columns/constraints disambiguation
So I understand how to create foreign keys and I know what is the
purpose of the FK. But I have a problem in understanding How to use
them.
Assuming you are referring to the foreign key constraints, the short answer would be you just don't use them.
And here comes the long one:
We are accustomed to refer to columns being foreign keys to other tables. Especially during the normalization process, phrases like "user_purchase.i_id is a foreign key to the items table" would be very common. While that's a perfectly valid way to describe the relationship, it can get a little fuzzy when we reach the implementation phase.
Suppose you have created your tables without the FOREIGN KEY clauses:
CREATE TABLE user(
id INT(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(20) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE items(
i_id INT(11) NOT NULL AUTO_INCREMENT,
name TINYTEXT NOT NULL,
price DECIMAL(8,2) NOT NULL,
PRIMARY KEY (i_id)
);
CREATE TABLE user_purchase(
i_id INT(11) NOT NULL,
name TINYTEXT NOT NULL,
id INT(11) NOT NULL,
);
Notice that, relation-wise, the foreign key columns are still implemented. There's a column that references the user table (id) and another one that references the items table (i_id) -- let's put the name column aside for a moment. Consider the following data:
user user_purchase items
| id username | | id i_id | | i_id name price |
| 23 john | | 55 10 | | 10 chocolate bar 3.42 |
| 55 mary | | 70 10 | | 33 mobile phone 82.11 |
| 70 fred | | 70 33 | | 54 toothpaste 8.67 |
| 55 10 | | 26 toy car 6.00 |
| 70 26 |
The relation is there. It is implemented by means of the user_purchase table, which holds information as to who bought what. If we were to query the database for a relevant report, we would do:
select * from user_purchase p
join user u on (p.id=u.id)
join items i on (p.i_id=i.i_id)
And that's how we use the relation and the foreign key columns involved.
Now, what if we do:
insert into user_purchase (id,i_id) values (23,99)
Apparently, this is an invalid entry. Although there is a user with id=23, there's no item with i_id=99. The RDBMS would allow that to happen, because it doesn't know any better. Yet.
That's where foreign key constraints come into play. By specifying FOREIGN KEY (i_id) REFERENCES items(i_id) in the user_purchase table definition, we essentially give the RDBMS a rule to follow: entries with i_id values that are not contained in the items.i_id column are not acceptable. In other words, while a foreign key column implements the reference, a foreign key constraint enforces the referential integrity.
Note, however, that the above select wouldn't change, just because you defined a FK constraint. Thus, you don't use FK constraints, the RDBMS does, in order to protect your data.
Redundancies
...what if I want several columns? Why don't we use several foreign
keys for different columns of the same table?
Ask yourself: Why would you want that? If the two foreign keys are to serve the same purpose, the redundancy will eventually get you in trouble. Consider the following data:
user_purchase items
| id i_id name | | i_id name price |
| 55 10 chocolate bar | | 10 chocolate bar 3.42 |
| 70 10 chocolate bar | | 33 mobile phone 82.11 |
| 70 33 mobile phone | | 54 toothpaste 8.67 |
| 55 10 toothpaste | | 26 toy car 6.00 |
| 70 26 toy car |
What's wrong with this picture? Did user 55 buy two chocolate bars, or a chocolate bar and a toothpaste? This kind of ambiguity can lead to a lot of effort to keep data in-sync, which would be unnecessary if we just kept one of the foreign keys. In fact, why not drop the name column altogether, since it is implied by the relation.
Of course, we could resolve this by implementing a composite foreign key, by setting PRIMARY KEY(i_id,name) for the items table (or defining an extra UNIQUE(i_id,name) index, it doesn't realy matter) and then setting a FOREIGN KEY(i_id,name) REFERENCES items(i_id,name). This way, only (i_id,name) couples that exist in the items table would be valid for user_purchases. Apart from the fact that you would still have one foreign key, this approach is totally unnecessary, provided that the i_id column is already enough to identify an item (can't say the same for the name column...).
However, there's no rule against using multiple foreign keys to a table. In fact, there are circumstances that demand such an approach. Consider a person(id,name) table and a parent(person,father,mother) one, with the following data:
person parent
| id name | | person father mother |
| 14 John | | 21 14 59 |
| 43 Jane | | 14 76 43 |
| 21 Mike |
| 76 Frank |
| 59 Mary |
Obviously, all three columns of the parent table are foreign keys to person. Not for the same relation, though, but for three different ones: Since a person's parents are persons too, the two corresponding columns must reference the same table person does. Note, however, that the three fields not only can but also have to refer different persons in the same parent row, since noone is his own parent and noone's father is his mother as well.
Foreign keys are used in joins. For instance, if you want to know the usernames that purchased a particular item, you would write:
select u.username
from items i
join user_purchase up on i.i_id = up.i_id
join user u on u.id = up.id
where i.name = "Some product name"
They may also be used by the database engine itself. It can detect if you create a row in user_purchase whose id or i_id column doesn't match anything in the referenced column in the other table.
You should not replicate the name column in the user_purchase table. The name is just an attribute of the item, it's not specific to any particular purchase. If you need to get the name of the item that was purchased, join with the items table.
Instead of reading so many links, just try to implement this in any simple project. I'm just explaining how we gonna use the above tables.
Suppose you 3 users in user table and 5 items in items table.
user table
id | username | password
1 abc 123
2 def 456
3 qwe 987
items table
i_id | name | price
1 item 1 6
2 item 2 8
3 item 3 11
4 item 4 3
5 item 5 14
your user_purchase table look like this
CREATE TABLE user_purchase(
i_id INT(11) NOT NULL,
id INT(11) NOT NULL,
FOREIGN KEY (i_id) REFERENCES items(i_id),
FOREIGN KEY (id) REFERENCES user(id)
);
There is no need of item name again in this table. So I have removed.
i_id | id
1 1
1 2
2 2
3 3
In the above table we will get, user 1 has purchased item 1, user 2 has purchased item 1,item 2 and user 3 has purchased item 3.
This is how normalization works. You can use MySQL JOIN for getting user name and item details
SELECT B.user_name,C.name AS item_name,C.price
FROM user_purchase A
JOIN user B ON A.id = B.id
JOIN items C ON A.i_id = C.i_id
Here is foreign key use to join
A.id = B.id
A.i_id = C.i_id
You treet tables with foreign keys in php the same way, as if they had no foreign keys. Foreign keys are defined in the database and have (almost) nothing to do with php. The only thing you have to do in php is reacting to potential errors that can be returned by sql queries, which brake the foreign key constraint (typically DELETE queries).
And for your database schema, you should drop column "name" from "table user_purchase". It is redundat.
Just looking at the normalization/de-normalization point:
Normalization is a process of trying to remove redundancy in your database - in your example the name field in user_purchase is redundant - I can find out the name of the item by looking it up in the items table using i_id.
So if we were to look at normalizing user_purchase we'd probably remove the name field and use a JOIN to retrieve that when we needed it. This would, of course, also mean we don't need the second FOREIGN KEY reference to items.
De-normalization is basically going the opposite way - adding redundancy - usually done for performance reasons.
However, in your example you might also consider de-normalization for business reasons too. For example you might decide it is important to store the product name as it was when the user actually purchased it (rather than what it's called now) - just in case you need to be able to re-print an invoice for example. However even in this case you wouldn't want the FOREIGN KEY back to items (as it would "break" if the product was re-named).
I have the following tables category, Restaurant, and menu.
A restaurant can have more than one menu, and a restaurant can also have more than one category. Each menu obviously has some number of catagories, and each catagory can be a part of a number of menus.
Can I create a table that looks like this, and has 3 primary keys?
Category ID | Restaurant ID | Menu ID
1 (Appetizer) | 1 Chile's | 2 Dinner Menu
2 (Entree) | 1 Chile's | 2 Dinner Menu
3 (Dessert) | 1Chile's | 2 Dinner Menu
1 | 67 McDonalds | 3 Lunch Menu
1
Yes, you can make a primary key of the tuple (category_id, restaurant_id, menu_type_id). Better even, make a normal, integral primary key, and add another index on that tuple with uniqueness constraint. It's always good to have a fast, simple primary key, but you can certainly enforce uniqueness of the triple:
CREATE UNIQUE INDEX menu_index
ON menus (category_id, restaurant_id, menu_type_id)
Or you can define the index right in the table creation:
CREATE TABLE menus (menu_id INT AUTO_INCREMENT PRIMARY KEY,
category_id INT,
restaurant_id INT,
menu_type_id INT,
FOREIGN KEY (category_id) REFERENCES categories(category_id),
FOREIGN KEY (restaurant_id) REFERENCES restaurants(restaurant_id),
FOREIGN KEY (menu_type_id) REFERENCES menu_types(menu_type_id),
UNIQUE INDEX (category_id, restaurant_id, menu_type_id)
) ENGINE=InnoDB;