Trying to figure out why this constraint doesn't work as expected and attempting to find solution.
A description of tables and attributes below:
Table Hotel
hotel_id - primary key - INT(5)
Table Room
room_id - primary key - INT(5)
hotel_id - primary key - INT(5)
room_type - VARCHAR(25)
Table RoomType
room_type - primary key - VARCHAR(25)
hotel_id - primary key - INT(5)
type_description - VARCHAR(100)
price - INT(5)
So the constriants:
room.room_type references roomtype.room_type
room.hotel_id references hotel.hotel_id
roomtype.hotel_id references hotel.hotel_id
So the desired result of this would be that for all the hotels will have room types that are unique to them. Then hotels will also have rooms that are linked to a roomtype. So example data for these would be:
Hotel:
hotel_id
1
2
Room:
room_id hotel_id room_type
1 1 Single
2 2 Double
RoomType:
room_type hotel_id type_description price
Single 1 Random 500
Double 2 Random 500
So if I tried adding this row to the Rooms I would expect it not to work due to constraints. As for hotel_id 1 there is no roomtype Double in the roomtypes table. This still works and doesn't get stopped by the constraint.
Room
room_id hotel_id room_type
3 1 Double
So how would you do this?
Related
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)
);
I want to restrict one column in `orders` MySQL table to primary key from **2 different tables**.
Example:
I have one table orders
|order_id|item_1 |item_2 |price|
|1 |service_id1|product_id1|10.00|
|2 |product_id1|service_id1|10.00|
Columns item_1 and item_2 are basic indexes and I want to restrict each of them to following tables:
services
|service_id |name |price|
|service_id1|service1|5.00 |
|service_id2|service2|5.00 |
products
|product_id |name |price|
|product_id1|product1|5.00 |
|product_id2|product2|5.00 |
Basically i just want to insert data from both services and products into one column: orders.item_1 or orders.item_2. Is it possible? I added both name restrictions in phpmyadmin but it doesn't work like it suppose to work, generating error 1452. Maybe I'm overthinking and the solution is easier than I think or I just want too much from mysql.
This is only a foreign key constraint
Joining both in a single column makes no sense, but is doable
what you essentially have is a bridge taböe, to join different ids from different tables. As the ids are indexed this is ver fast, especially when you are looking for few items .
in the sample you need to have in both tables a 1 to add to orders a 1 as reference, and so you always must have products and services in sync with each other
CREATE tABLE products (product_id bigint Primary KEY auto_increment, name VARCHAR(255),price DECIMAL (10,2))
CREATE tABLE services (sercvice_id bigint Primary KEY auto_increment, name VARCHAR(255),price DECIMAL (10,2))
CREATE TABLe orders (order_id BIGINT AUTO_INCREMENT PRIMARY KEY
,item_1 BIGINT
, item_2 BIGINT
, price DECIMAL(10,2)
,
CONSTRAINT FK_OrdersProducts FOREIGN KEY (item_1)
REFERENCES products(product_id),
CONSTRAINT FK_OrdersServices FOREIGN KEY (item_2)
REFERENCES services(sercvice_id)
, INDEX (item_1,item_2)
)
db<>fiddle here
CREATE tABLE products (product_id bigint Primary KEY auto_increment, name VARCHAR(255),price DECIMAL (10,2))
CREATE tABLE services (sercvice_id bigint Primary KEY auto_increment, name VARCHAR(255),price DECIMAL (10,2))
CREATE TABLe orders (order_id BIGINT AUTO_INCREMENT PRIMARY KEY
,item_1 BIGINT
, price DECIMAL(10,2)
,
CONSTRAINT FK_OrdersProducts FOREIGN KEY (item_1)
REFERENCES products(product_id),
CONSTRAINT FK_OrdersServices FOREIGN KEY (item_1)
REFERENCES services(sercvice_id)
)
db<>fiddle here
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 trying to create a table that has a composite PK constraint using Rep_ID, Store_ID, and Quarter and I'm trying to create a FK constraint on Rep_ID and Store_ID
This is my statement:
CREATE TABLE REP_CONTRACTS(
Store_ID INT(8),
Name INT(5),
Quarter CHAR(3),
Rep_ID INT(5),
PRIMARY KEY (Rep_ID, Store_ID, Quarter),
Rep_ID INT REFERENCES BOOK_STORES(Rep_ID),
Store_ID INT REFERENCES BOOK_STORES(Store_ID)
);
These are my tables:
Book Stores:
Column Name Datatype Constraint Comments
Store_ID INT(8) PRIMARY KEY column
Name VARCHAR(30) Should be UNIQUE and NOT NULL
Contact VARCHAR(20)
Rep_ID INT(5)
Rep Contracts
Column Name DataType
Store_ID INT(8)
Name INT(5)
Quarter CHAR(3)
Rep_ID INT(5)
I have already created the book store table, I'm trying to create the rep contracts table
I also get the error Duplicate column name 'Rep_ID'. Add a differentiating column alias. when running this query
you are declaring REPID twice in the table, which is why you are getting the duplication error. You may also want to create the column "Store ID" before using it in the Primary Key Statement.
CREATE TABLE REP_CONTRACTS(
Store_ID INT(8),
Name INT(5),
Quarter CHAR(3),
Rep_ID INT(5) REFERENCES BOOK_STORES(Rep_ID),
Store_ID INT REFERENCES BOOK_STORES(Store_ID),
PRIMARY KEY (Rep_ID, Store_ID, Quarter)
);
Each member has 0 or more orders. Each order contains at least 1 item.
memberid - varchar, not integer - that's OK (please do not mention that's not very good, I can't change it).
So, thera 3 tables: members, orders and order_items. Orders and order_items are below:
CREATE TABLE `orders` (
`orderid` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`memberid` VARCHAR( 20 ),
`Time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
`info` VARCHAR( 3200 ) NULL ,
PRIMARY KEY (orderid) ,
FOREIGN KEY (memberid) REFERENCES members(memberid)
) ENGINE = InnoDB;
CREATE TABLE `order_items` (
`orderid` INT(11) UNSIGNED NOT NULL,
`item_number_in_cart` tinyint(1) NOT NULL , --- 5 items in cart= 5 rows
`price` DECIMAL (6,2) NOT NULL,
FOREIGN KEY (orderid) REFERENCES orders(orderid)
) ENGINE = InnoDB;
So, order_items table looks like:
orderid - item_number_in_cart - price:
...
1000456 - 1 - 24.99
1000456 - 2 - 39.99
1000456 - 3 - 4.99
1000456 - 4 - 17.97
1000457 - 1 - 20.00
1000458 - 1 - 99.99
1000459 - 1 - 2.99
1000459 - 2 - 69.99
1000460 - 1 - 4.99
...
As you see, order_items table has no primary keys (and I think there is no sense to create an auto_increment id for this table, because once we want to extract data, we always extract it as WHERE orderid='1000456' order by item_number_in_card asc - the whole block, id woudn't be helpful in queries).
Once data is inserted into order_items, it's not UPDATEd, just SELECTed.
The questions are:
I think it's a good idea to put index on item_number_in_cart. Could anybody please confirm that?
Is there anything else I have to do with order_items to increase the performance, or that looks pretty good? I could miss something because I'm a newbie.
Thank you in advance.
Primary keys can span multiple columns. You can't use the PRIMARY attribute of columns to do this, but you can define a separate primary key with multiple columns:
CREATE TABLE `order_items` (
`orderid` INT(11) UNSIGNED NOT NULL,
`item_number_in_cart` tinyint(1) NOT NULL , --- 5 items in cart= 5 rows
`price` DECIMAL (6,2) NOT NULL,
PRIMARY KEY (orderid, item_number_in_cart),
FOREIGN KEY (orderid) REFERENCES orders(orderid)
) ENGINE = InnoDB;
Moreover, a primary key is simply a unique key where every column is not null with a certain name; you can create your own unique keys on non-nullable columns to get the same effects.
You'll not likely get much of a performance improvement by indexing item_number_in_cart; as the number of line items for a given order will tend to be small, sorting by item_number_in_cart won't take much time or memory. However, including the column in a primary key will help with data consistency.
Index on item_number_in_cart won't be used. It's tiny int, not selective enough, and won't even considered by the engine once you have 2 records. You can add it as a second column to the existing index on orderid (since you created FK constraint on orderid, mysql automatically adds an index on this field).
You say that data in order_items never updated, but I think it can be deleted; doing so without primary key will be problematic.
Well I'd be having an autoinc anyway, as I'm a big believer in surrogate keys, but as suggested by alex07 an index, or even primary key of orderid,item_number_in_cart should sort things out. Note the order by item_number will be using a two pass sort, (get the data and then sort it in the number order) so an index / key will chop that out straight off so you'd want that index even with a surrogate key.