How to manage one to one relationship in mySql? - mysql

I've two tables one about Customers and the second one is about their Accounts as 'Customer_Account_Information'. we know that one customer can have only one account, so I'm trying to enforce one to one relationship, but i don't know the procedure/syntax in mySql or mySqlyog.
is there any one who can help me?

You should create a Foreign Key contraint on table Accounts to table Customers using CustomerID.
Have a look at FOREIGN KEY Constraints
Also then make this a Unique Column in table Accounts
Have a look at MySQL foreign keys - how to enforce one-to-one across tables?
You could also use the same CustomerID from table Customers as PRIMARY KEY column in table Accounts as PRIMARY KEY.

A simple implementation
This is not a perfect solution but it is easy to understand with a little bit of experience with mysql and referential integrity.
You need a FOREIGN KEY in customers to refer to customer_account_inforamtion. Since the account_id in customers is the only way to join the tables, no customer can have more than one record in customer_account_information.
In the sample definitions below, I've included a FOREIGN KEY CONSTRAINT which causes the customer to be deleted when their account information is deleted. You may or may not want/need this.
CREATE TABLE customer_account_information (
id int(11) NOT NULL auto_increment,
some_attribute varchar(20) NOT NULL,
another_attribute varchar(30) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE customers (
id int(11) NOT NULL auto_increment,
account_id int(11) NOT NULL,
firstname varchar(30) NOT NULL,
surname varchar(30) NOT NULL,
PRIMARY KEY (id),
INDEX account_id (account_id),
CONSTRAINT account_id FOREIGN KEY (account_id) REFERENCES customer_account_information (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The account_id in customers is the foreign key which refers to a record in customer_account_information
Alternative implementation
It might be an idea to revisit the design of the two tables. If each customer really can have only one account then perhaps all of the attributes of each can be merged into one table. Yes this business rule may change in the future but it would be a better idea to defer this work until really necessary - don't over engineer the solution today.

Related

MySQL - How to create a composite table?

I am new to MySQL, I am about creating a database that manages information about diabetes patients. I have 6 tables, what important for here is 3 tables, diabetic table, diabetic_profile table and medication_profile table.
The last table (medication_profile) should be a composite table, which will have two primary foreign keys. These foreign keys are the diabetic_id(diabetic) and the profile_id (diabetic_profile). Translating that into MySQL code is the difficult part for me. I tried many ways but couldn't yet achieve that. below is the sql command I used:
CREATE TABLE Medication_Profile (
medication_type VARCHAR(10) NOT NULL,
profile_id INT NOT NULL,
diabetic int NOT NULL,
times_a_day NUMERIC(1) NOT NULL,
dose VARCHAR(20) NOT NULL,
PRIMARY KEY (medication_type, profile_id, diabetic),
FOREIGN KEY (profile_id, diabetic)
REFERENCES Diabetic_Profile (profile_id, diabetic_id)
);
please notice that the diabetic_id in this table is already a foreign key exists in Diabetic_profile table.
So any suggestions you may provide, please?
thanks in advance
You need a separate FOREIGN KEY clause for each foreign key, since they refer to different tables.
FOREIGN KEY (diabetic_id) REFERENCES diabetic (diabetic_id),
FOREIGN KEY (profile_id) REFERENCES diabetic_profile (profile_id)

Setting up MySQL foreign keys and indexes - Migrating to InnoDB

I need help setting up foreign keys for migrating to InnoDB. The situation is that I have 3 tables: jobs, customers and contacts.
A job belongs to a Customer and a job belongs to a Contact. A Customer has many jobs and has many contacts. A contact belongs to a Customer and has many Jobs. I need help setting up foreign keys in the jobs table so that only Contacts that belong to a selected Customer can be inserted. (i.e. If Customer A has contacts A1 and A2 and Customer B has contact B1, once you select Customer A as the customer for a job, it will reject entries for Contact other than A1 or A2). Is this possible to do with foreign keys or will I have to do the verification in the programming language of my choice?
Here's my schema:
CREATE TABLE jobs(
job_id INT(11) NOT NULL AUTO_INCREMENT,
customer_id INT(11) DEFAULT NULL,
contact_id INT(11) DEFAULT NULL,
job_number INT(11) UNSIGNED NOT NULL DEFAULT 0,
status_void TINYINT(1) DEFAULT 0,
PRIMARY KEY (job_id),
INDEX active_jobs (job_number, status_void),
INDEX customer_id (customer_id),
UNIQUE INDEX job_number (job_number),
CONSTRAINT FK_jobs_contacts_contact_id FOREIGN KEY (contact_id)
REFERENCES contacts (contact_id) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT FK_jobs_customers_customer_id FOREIGN KEY (customer_id)
REFERENCES customers (customer_id) ON DELETE SET NULL ON UPDATE CASCADE
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;`
CREATE TABLE customers(
customer_id INT(11) NOT NULL AUTO_INCREMENT,
inactive TINYINT(1) DEFAULT 0,
customer_name VARCHAR(50) NOT NULL DEFAULT '',
PRIMARY KEY (customer_id),
UNIQUE INDEX customer_name (customer_name),
INDEX inactive (inactive)
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;
CREATE TABLE contacts(
contact_id INT(11) NOT NULL AUTO_INCREMENT,
customer_id INT(11) DEFAULT NULL,
inactive TINYINT(1) DEFAULT 0,
first_name VARCHAR(50) DEFAULT NULL,
last_name VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (contact_id),
UNIQUE INDEX contact_key (customer_id, last_name, first_name),
INDEX customer_id (customer_id),
INDEX inactive (inactive),
INDEX name (last_name, first_name),
CONSTRAINT fk_contacts_customers_customer_id FOREIGN KEY (customer_id)
REFERENCES customers (customer_id) ON DELETE RESTRICT ON UPDATE CASCADE
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;
Another goal I have is to setup indexes to make listing all jobs that are not void (status_void=0) and all customers and contacts that are not inactive(inactive=0). I don't know if a multi-column index (jobs) or two separate indexes (customers/contacts) would help in this case.
Please take it easy on me as I'm still learning. Thank you for your time.
Is this possible to do with foreign keys or will I have to do the verification in the programming language of my choice?
Yes and yes.
This is the referential integrity that FKs add to your data. However if you do not also account for it in your application, you would present a horrible user experience that would allow a user to enter contacts that don't belong to a customer and after submitting that to the DB getting an error. It would be better to account for this in you application to prevent that situation from ever being sent to the DB, and the FK is kind of a back-up in case that somehow gets through.
Another goal I have is to setup indexes to make listing all jobs that are not void (status_void=0) and all customers and contacts that are not inactive(inactive=0). I don't know if a multi-column index (jobs) or two separate indexes (customers/contacts) would help in this case.
Which indexes to build tends to be query specific. So you would want to show an example query to best get advice in indexing, not just what your end goal is. It's important to know that only one index can be used for each table reference in a query. So if a table has multiple filters in the where clause you tend to get the best performance by having a multi-column index.
When planning your indexes it's a good idea to think about which columns have fewer unique values that would filter out a larger number of rows and list those first in the index.
All of that being said, without a representative query and the results of an explain statement on it, an outsider can't do much to advise you on indexes as their effectiveness is dependent on the data they are being used against.

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

How do I refer from another table for SQL?

Situation is this:
I have a table for staff and another one for course.
In course I will have a column called coordinator which will refer to the staff_id.
This is because some staff are coordinators and some are not, etc etc.
Staff_id is an IDENTITY hence it is auto-incrementing and I have to make sure the in the course table, it won't follow this auto-increment.
I'm just unsure how do I implement the check constraint for this case.
I have to make sure the in the course table, it won't follow this auto-increment.
Do you just mean that you don't want the coordinator field in the course table to auto-increment? It won't unless you tell it to.
For example these two tables would work fine:
CREATE TABLE Staff
(
`staff_id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(64),
PRIMARY KEY (`staff_id`)
)
CREATE TABLE Courses
(
`course_id` INT NOT NULL AUTO_INCREMENT,
`coordinator_id` INT NOT NULL,
`coursename` VARCHAR(64),
PRIMARY KEY (`course_id`),
INDEX `coordid` (`coordinator_id`)
)
Of course, as GarethD says, you can then additionally add constraints to guarantee that the coordinator_id is correctly filled in and the corresponding record exists in staff.
If I'm understanding your requirements properly you can add a foreign key constraint to the table so that it references itself:
ALTER TABLE Staff
ADD CONSTRAINT FK_Staff_Coordinator FOREIGN KEY (Coordinator)
REFERENCES Staff (StaffID)
ADENDUM
I think I had misunderstood your requirements, I missed the part about a course table. So now I am thinking that the course table needs to link back to the staff table, in which case the command would be:
ALTER TABLE Course
ADD CONSTRAINT FK_Course_Coordinator FOREIGN KEY (Coordinator)
REFERENCES Staff (StaffID)
You are of course free to implement your own naming convention for constraints.

How do I create a MySQL table when it has two foreign keys?

What would be the code for creating a table with two foreign keys?
I have a USER table and a PICTURE table. Since a USER can be in many PICTURE and many PICTURE can be from a USER, I need a third table with both primary keys.
Thank you SO, as usual you are invaluable for a learning novice. :)
I can't speak specifically for mySQL but in most databases I have worked with you can put as many foreign keys as you need on a table. But you can only have one primary key. A third table with both keys is the right choice. Make a foreign key to each of the other two tables and a primary key consisting of both ids in the table.
If I understood correctly, you may need to do something like the following:
CREATE TABLE users (
user_id INT NOT NULL PRIMARY KEY,
name VARCHAR(50) NOT NULL
) ENGINE=INNODB;
CREATE TABLE pictures (
picture_id INT NOT NULL PRIMARY KEY,
filename VARCHAR(255) NOT NULL,
posted_by INT NOT NULL,
FOREIGN KEY (posted_by) REFERENCES users(user_id)
) ENGINE=INNODB;
CREATE TABLE users_in_pictures (
user_id INT NOT NULL,
picture_id INT NOT NULL,
PRIMARY KEY (user_id, picture_id),
FOREIGN KEY (user_id) REFERENCES users(user_id),
FOREIGN KEY (picture_id) REFERENCES pictures(picture_id)
) ENGINE=INNODB;
Note that each picture can be posted by a user. In fact the posted_by field is constrained by a foreign key that references the users table.
In addition, I assume that you want to tag pictures ala-facebook. In this case, you can use the third table, which is using a composite primary key on (user_id, picture_id) and both fields are also constrained to the appropriate table.