How to setup must exist in database - mysql

i need help. As you can see in my tables i have to setup primary and forign keys and i need help on this cascade updates.
For example i have a task like this:
If the value for Category_Name is changed in CATEGORY, the change should be reflected here as well.
But this doesent work :
If an OrderID in ORDERED is changed, then the change should be reflected here as well.
If an ORDERED is deleted, then any LINE_ITEM(s)associated with that order should also be deleted.
If the Item_Number of an ITEM is changed, then the change should be reflected here as well.
I dont know how to setup this correctly and i need help to better understand this.
CREATE DATABASE TEST1;
USE TEST1;
CREATE TABLE CATEGORY (
CategoryName VARCHAR(35) NOT NULL,
ShippingPerPound CHAR(4),
OffersAlowed ENUM('y', 'n'),
CONSTRAINT CATEGORY_PK PRIMARY KEY (CategoryName)
) ENGINE=INNODB;
CREATE TABLE ITEM(
ItemNumber INT UNSIGNED AUTO_INCREMENT,
Item_Name VARCHAR(35) NOT NULL,
Description VARCHAR(255),
Model VARCHAR(50) NOT NULL,
Price CHAR(8) NOT NULL,
parent_ItemNumber INT,
CategoryName VARCHAR(35),
CONSTRAINT ITEM_PK PRIMARY KEY (ItemNumber),
CONSTRAINT ITEM_CategoryName_fk FOREIGN KEY (CategoryName) REFERENCES
CATEGORY(CategoryName) ON UPDATE CASCADE)
ENGINE=INNODB;
CREATE table LINE_ITEM(
Quantitiy INT(255),
Shipping_amounth DECIMAL(4,2),
ItemNumber INT UNSIGNED,
OrderID INT UNSIGNED,
CONSTRAINT LINE_ITEM_PK PRIMARY KEY(ItemNumber),
CONSTRAINT LINE_ITEM_PK PRIMARY KEY(OrderID),
CONSTRAINT LINE_ITEM_OrderID FOREIGN KEY (OrderID) REFERENCES ORDERED(OrderID) ON UPDATE CASCADE),
CONSTRAINT LINE_ITEM_OrderID FOREIGN KEY (OrderID) REFERENCES ORDERED(OrderID) ON UPDATE DELETE),
CONSTRAINT LINE_ITEM_ItemNumber_fk FOREIGN KEY (ItemNumber) REFERENCES ITEM(ItemNumber) ON UPDATE CASCADE)
ENGINE=INNODB;
CREATE TABLE OFFER(
OfferCode varchar(15),
Discount_Amt varchar(35) NOT NULL,
MinAmount DECIMAL(2,2) NOT NULL,
ExpirationDate DATE NOT NULL,
CONSTRAINT OFFER_OfferCode PRIMARY KEY(OfferCode)
)
ENGINE=INNODB;
CREATE TABLE ORDERED(
OrderID INT UNSIGNED AUTO_INCREMENT,
total_cost DECIMAL(8,2),
CONSTRAINT ORDERED_PK PRIMARY KEY (OrderID),
CONSTRAINT OFFER_OfferCode FOREIGN KEY (OrderID) REFERENCES OFFER(OfferCode) ON UPDATE CASCADE),
CONSTRAINT CUSTOMER_CustomerID FOREIGN KEY (CustomerID) REFERENCES CUSTOMER(CustomerID) ON UPDATE CASCADE)
)
ENGINE=INNODB;

If the value for Category_Name is changed in CATEGORY, the change should be reflected [in the tables which reference Category] as well.
You have an XY Problem. You've already chosen a solution to your problem (using cascading updates) and asked about that. It's not a good solution, it just papers over the real. Instead, you should ask about the real problem.
The real problem is you have duplicate data, CategoryName is stored in two places. The question is how one deals with that? The answer is to redesign the schema to eliminate the duplication.
Looking at the Category table reveals the real real problem, CategoryName is the primary key, so it will be referenced in other tables. But if CategoryName can change it's a poor choice for a primary key. Instead, use a simple auto incrementing integer as the primary key and the problem goes away.
create table Category (
ID integer auto_increment primary key,
Name varchar(255) not null,
ShippingPerPound CHAR(4),
OffersAlowed ENUM('y', 'n'),
);
Now categories can be referenced using CategoryID integer references category(id). Category names can change as much as they like. Any query needing to know the category name will have to do a join Category on Category.id = CategoryID; simple joins like that are cheap.
If the Item_Number of an ITEM is changed, then the change should be reflected [in the tables which reference Item] as well.
Same problem: if the primary key can change, it's not a good primary key. Since Item_Number is auto incremented it should never change, so you might be worrying about a problem that doesn't exist.
If Item_Number can change, then you need two columns. One for the immutable primary key, just call it id, and one for the mutable Item_Number. They can be the same thing for most columns, that's fine, it just adds 4 bytes to each column.
Note that I increased the name constraint to 255. It's a bad practice to put business rules, like size limits on names, into the database schema. The database should not be limiting design choices, and there's no technical reason to limit it: 30 characters in a varchar(255) takes up as much space as 30 characters in varchar(30).
I'd also question why ShippingPerPound is a char(4). It seems it should be a number, probably a numeric(9,2) for money. numeric will store exact values and does not suffer from floating point error, it's a good choice for money.
Finally, I'd caution against using an unsigned integer as a primary key. Yes, it doubles your keyspace, but I can guarantee people referencing that key will forget and use a plain, signed integer. If you hit 2 billion rows it's likely you're growing so rapidly that you'll blow through the next 2 billion much faster than the first, so it isn't worth it. If you're really concerned about keyspace, use a bigint or UUID. But this isn't something you need to worry about now, tables can be altered later if it becomes an issue. But it's unlikely to be an issue for Items as it's unlikely you'll have 2 billion items. Keyspace exhaustion is something that happens to tables that are logging things, or tracking sales, things which grow exponentially as you get more users; not manually entered warehouse data.
Old database guides sometimes encourage bad practices like trying to use data as a primary key, or putting unnecessary limits on storage sizes. That might have made sense when disk and CPU was extremely limited, or when column sizes were fixed, but it makes little sense now. An auto incremented integer primary key is a good default choice.

Related

When to use an auto-increment key?

I've recently started to work on MySQL and while I've read some documentation on database structure, I cannot get my head around auto-increment keys and why using them.
I have been told:
it's best to use a number instead of text as a primary key,
it's best to use a key that doesn't have any business signification
Let's look at the situation below:
tStores tSales tCustomers
---------- ----------- --------------
store_id sale_id customer_id
storeCode store_id
customer_id
First, I load some data in tStores for all the stores products can be sold. In our business, all stores have a 4 letters code to identify them. I could use this as a primary key, but based on the recommendations above I should use a store_id field that auto-increments?
The problem is, each time I insert something in tSales, I have to go back to tStores and do something like:
SELECT store_id from tStores WHERE storeCode = #myStoreCode;
Assuming I am loading hundreds of thousands rows in tSales for each store, would it not be more efficient to use the storeCode as primary key?
What would be the most efficient way to deal with this?
Yes you can use storeCode as the primary key, it will work if you can ensure it is unique. Then you will add a foreign key on your other tables to establish the relationship.
The benefit of auto increment index are:
It is usually faster than any index on other column type
It is usually recommended by some framework (such as Laravel in PHP)
Related to you structure I would comment on some points:
You have mixed casing columns/tables. When working on MySQL, especially when used on different OS (Windows/Linux), I would always recommend to use lowercase names for both schemas, tables and columns.
You added a prefix in front of store_id and store_code. This prefix is not necessary. Why not simply naming the columns id and code.
The relationship on tSales should be named tStores_id instead to clearly indicate from which table and which column you are referring to.
Here the SQL code for this example:
CREATE SCHEMA `myshop` ;
CREATE TABLE `store`.`stores` (
`code` VARCHAR(10) NOT NULL,
PRIMARY KEY (`code`));
CREATE TABLE `store`.`sales` (
`id` INT NOT NULL AUTO_INCREMENT,
`store_code` VARCHAR(10) NOT NULL,
`customer_id` INT NOT NULL,
PRIMARY KEY (`id`));
CREATE TABLE `store`.`customers` (
`id` INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`));
ALTER TABLE `store`.`sales`
ADD INDEX `fk_sales_customers_id_idx` (`customer_id` ASC) VISIBLE;
ALTER TABLE `store`.`sales`
ADD CONSTRAINT `fk_sales_customers_id`
FOREIGN KEY (`customer_id`)
REFERENCES `store`.`customers` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE;
ALTER TABLE `store`.`sales`
ADD INDEX `fk_sales_stores_code_idx` (`store_code` ASC) VISIBLE;
ALTER TABLE `store`.`sales`
ADD CONSTRAINT `fk_sales_stores_code_id`
FOREIGN KEY (`store_code`)
REFERENCES `store`.`stores` (`code`)
ON DELETE CASCADE
ON UPDATE CASCADE;

Primary Key in DB in SQL

guys i was asked to make the design for product table like this
product_code (PK)----------varchar2(5)
product_name---------------varchar2(15)
product_type---------------varchar2(1)
but i want to make an ID auto increment column to be primary key as well to be accessed within any CRUD operations .. can i do this with the existence of primary key product_code ?... or the ID column is useless here ??
Make the product_id the primary key as an auto_increment column.
You can then define product_code as unique and not null.
Foreign key relationships should use the product_id column.
If you've been given requirements that state your primary key should be a varchar2(5) called product_code. I would recommend following the requirements, I see no practical reason to object.
Do you know how many records will exist in this table, do you have reason to believe the requirements provided will introduce an issue? If so document your concerns and ask for clarification, but you appear to be a contractor and I would defer to the customer.
Only the primary key can be auto-generated in MySQL.
Create the table with a new ID column that is auto-generated and make your existing PRODUCT_CODE column unique, as in:
create table product (
id int primary key not null auto_increment,
product_code varchar(5) not null,
product_name varchar(15) not null,
product_type varchar(1),
constraint uq_code unique (product_code)
);

MySql: Composite Unique Key

I want to make composite key of 2 column id & code,the both columns altogether should act like Unique key for the table. while I have browsed and tried to create a table as follows,
Create table test (
`test_no` int not null AUTO_INCREMENT,
`code` varchar(5) NOT NULL,
`name` varchar(255),
`UPDBy` varchar(255),
PRIMARY KEY (`test_no`),
FOREIGN KEY (code) REFERENCES other_table(code)
// CONSTRAINT `K_test_1` Unique KEY (`test_no`,`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Just a second thought, can i make both the column as PK ? I think it will serve my purpose, RIght?
CONSTRAINT `K_test_1` Primary KEY (`test_no`,`code`) OR Primary KEY (`test_no`,`code`)
You seem to be on the wrong track somehow. Your table has an ID which is auto incremented. This is not supposed to be the primary key? Why do you call it ID then?
There are two ways to build a database: Either use the natural values a user is used to, such as an employee number a department number and so on. Or use IDs (which are usually hidden from the user). Than you would have an employee table with primary key "id" or "employee_id" or whatever, and the employee number just as a field. But as it must be unique, you would have an additional unique index on that field.
Having said that; you have a table "other_table" with primary key "code" it seems. So you are not using an ID concept here. Then why do you use it on table test? If this is a detail table on other_table, then I'd expect the composite key to be something like code + test_no (thus showing numbered tests per code) for isntance.
So the essence is: 1. Think about what your table contains. 2. Think about wether to use IDs or natural keys. The answer to these questions should help you find the correct key for your table. (And sometimes a table even doesn't have a primary key and needs none.)
You sure can make them both as PRIMARY KEY. If you don't want to, just use UNIQUE instead of UNIQUE KEY.
To set both as PRIMARY KEY, do as it follows:
...
PRIMARY KEY (`id`, `code`);
...
To set a UNIQUE CONSTRAINT, do as it follows:
...
CONSTRAINT `K_test_1` UNIQUE (`id`,`code`);
...

MYSQL Error # 1005

I have been trying to create a foregin key with nbrseats but I i get the error 1005 all the time.
CAn someone help me!?
create table theater (
name varchar(30) primary key,
nbrseats int not null
) ENGINE=INNODB;
create table reservation (
nbr integer auto_increment,
users_username varchar(30),
cinemashow_showdate date,
movies varchar(30),
nbrseats int not null,
primary key (nbr),
foreign key (nbrseats) references theater(nbrseats),
foreign key (users_username) REFERENCES users(username)
on delete cascade,
foreign key (cinemashow_showdate, movies) references cinemashow(showdate, movie_title)
on delete cascade
) ENGINE=INNODB;
In order to be a FOREIGN KEY in another table, you must have an index created on theater.nbrseats. And in order to be able to reference a specific row reliably, it should therefore be a UNIQUE index. Otherwise, if you have duplicate values, the referencing table won't be able to discern which row it references. Even though InnoDB will allow you to create the relationship on a non-unique index, it is likely not the behavior you are looking for.
See this question for more info on that bit.
create table theater (
name varchar(30) primary key,
nbrseats int not null,
UNIQUE INDEX `idx_nbrseats` (nbrseats)
) ENGINE=INNODB;
The same will be true of the other FOREIGN KEY definitions in your table reservation, though we do not see their referenced tables posted here. The rules are:
The referenced column must be indexed (independently of any other compound indexes on it)
The referencing column must have exactly the same data type.
This kind of calls into question your design, however. If you are attaching a number of seats to a reservation, will the reservation number of seats exactly match the number available in the theater? Also this means that you could not have 2 theaters with the same number of seats.
You may need to reconsider your design here, and perhaps create a FOREIGN KEY that references theater.name instead of theater.nbrseats.

In SQL, is it OK for two tables to refer to each other?

In this system, we store products, images of products (there can be many image for a product), and a default image for a product. The database:
CREATE TABLE `products` (
`ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`NAME` varchar(255) NOT NULL,
`DESCRIPTION` text NOT NULL,
`ENABLED` tinyint(1) NOT NULL DEFAULT '1',
`DATEADDED` datetime NOT NULL,
`DEFAULT_PICTURE_ID` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `Index_2` (`DATEADDED`),
KEY `FK_products_1` (`DEFAULT_PICTURE_ID`),
CONSTRAINT `FK_products_1` FOREIGN KEY (`DEFAULT_PICTURE_ID`) REFERENCES `products_pictures` (`ID`) ON DELETE SET NULL ON UPDATE SET NULL
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8;
CREATE TABLE `products_pictures` (
`ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`IMG_PATH` varchar(255) NOT NULL,
`PRODUCT_ID` int(10) unsigned NOT NULL,
PRIMARY KEY (`ID`),
KEY `FK_products_pictures_1` (`PRODUCT_ID`),
CONSTRAINT `FK_products_pictures_1` FOREIGN KEY (`PRODUCT_ID`) REFERENCES `products` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
as you can see, products_pictures.PRODUCT_ID -> products.ID and products.DEFAULT_PICTURE_ID -> products_pictures.ID, so a cycle reference. Is it OK?
No, it's not OK. Circular references between tables are messy. See this (decade old) article: SQL By Design: The Circular Reference
Some DBMS can handle these, and with special care, but MySQL will have issues.
Option 1
As your design, to make one of the two FKs nullable. This allows you to solve the chicken-and-egg problem (which table should I first Insert into?).
There is a problem though with your code. It will allow a product to have a default picture where that picture will be referencing another product!
To disallow such an error, your FK constraint should be:
CONSTRAINT FK_products_1
FOREIGN KEY (id, default_picture_id)
REFERENCES products_pictures (product_id, id)
ON DELETE RESTRICT --- the SET NULL options would
ON UPDATE RESTRICT --- lead to other issues
This will require a UNIQUE constraint/index in table products_pictures on (product_id, id) for the above FK to be defined and work properly.
Option 2
Another approach is to remove the Default_Picture_ID column form the product table and add an IsDefault BIT column in the picture table. The problem with this solution is how to allow only one picture per product to have that bit on and all others to have it off. In SQL-Server (and I think in Postgres) this can be done with a partial index:
CREATE UNIQUE INDEX is_DefaultPicture
ON products_pictures (Product_ID)
WHERE IsDefault = 1 ;
But MySQL has no such feature.
Option 3
This approach, allows you to even have both FK columns defined as NOT NULL is to use deferrable constraints. This works in PostgreSQL and I think in Oracle. Check this question and the answer by #Erwin: Complex foreign key constraint in SQLAlchemy (the All key columns NOT NULL Part).
Constraints in MySQL cannot be deferrable.
Option 4
The approach (which I find cleanest) is to remove the Default_Picture_ID column and add another table. No circular path in the FK constraints and all FK columns will be NOT NULL with this solution:
product_default_picture
----------------------
product_id NOT NULL
default_picture_id NOT NULL
PRIMARY KEY (product_id)
FOREIGN KEY (product_id, default_picture_id)
REFERENCES products_pictures (product_id, id)
This will also require a UNIQUE constraint/index in table products_pictures on (product_id, id) as in solution 1.
To summarize, with MySQL you have two options:
option 1 (a nullable FK column) with the correction above to enforce integrity correctly
option 4 (no nullable FK columns)
The only issue you're going to encounter is when you do inserts.
Which one do you insert first?
With this, you will have to do something like:
Insert product with null default picture
Insert picture(s) with the newly created product ID
Update the product to set the default picture to one that you just inserted.
Again, deleting will not be fun.
this is just suggestion but if possible create one join table between this table might be helpfull to tracking
product_productcat_join
------------------------
ID(PK)
ProductID(FK)- product table primary key
PictureID(FK) - category table primary key
In the other table you can just hold that field without the foreign key constraint.
it is useful in some cases where you want to process with the smaller table but connect to the bigger table with the result of the process.
For example if you add a product_location table which holds the country, district, city, address and longitude and latitude information. There might be a case that you want to show the product within a circle on the map.
John what your doing isnt anything bad but using PK-FK actually helps with normalizing your data by removing redundant repeating data. Which has some fantastic advantages from
Improved data integrity owing to the elimination of duplicate storage locations for the same data
Reduced locking contention and improved multiple-user concurrency
Smaller files
that is not a cyclic ref, that is pk-fk