MySQL one to many with primary choice - mysql

Say I have a bunch of people with multiple phone numbers. In a MySQL database I'd have a Person table and a Phone Number table with a many to one relationship.
Now I want to make one of those numbers the primary phone number and only allow one primary number per person. How would I model this?

Try the schema below. It will prevent entries that try to assign more than one primary number per person.
CREATE TABLE person (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`first_name` VARCHAR(50) NOT NULL,
`last_name` VARCHAR(50) NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE phonenumber (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`phonenumber` VARCHAR(10) NOT NULL,
`person_id` INT(11) UNSIGNED NOT NULL,
`is_primary` ENUM('1'),
PRIMARY KEY(`id`),
UNIQUE KEY idx_person_primary (`person_id`, `is_primary`),
UNIQUE KEY idx_person_phone (`phonenumber`, `person_id`)
);
INSERT INTO person (first_name, last_name) VALUES ('Michael', 'Jones');
INSERT INTO phonenumber (phonenumber, person_id, is_primary) VALUES ('9876543210', 1, 1);
INSERT INTO phonenumber (phonenumber, person_id, is_primary) VALUES ('1234567890', 1, NULL);
INSERT INTO phonenumber (phonenumber, person_id, is_primary) VALUES ('1234567891', 1, NULL);
This will allow the DB to police a single primary phone number for each person. For example if you try to assign another primary phone number to Michael Jones:
INSERT INTO phonenumber (phonenumber, person_id, is_primary) VALUES ('0123211234', 1, 1);
You will get a "Duplicate entry '1-1' for key 'idx_person_primary'" error.
http://sqlfiddle.com/#!9/dbb3c7/1

The "exactly one primary phone number" is tricky. One way uses triggers. Other databases offer expression-based indexes. This is tricky because:
The constraint spans two tables.
Guaranteeing exact "one-ness" across updates is tricky.
But one method in MySQL that comes close and doesn't use triggers:
create table persons (
personId int auto_increment primary key,
primary_personPhonesId int,
. . .
);
create table personPhones (
personPhonesId int auto_increment primary key,
personId int,
. . .
foreign key (personId) references persons (personId),
unique (personId, personPhonesId) -- seems redundant but needed
);
alter table persons
add foreign key (personId, primary_personPhonesId) on personPhones(personId, personPhonesId);
It is tempting to declare primary_personPhonesId as not null. However, that makes it difficult to insert rows into the two tables.
An alternative method uses computed columns:
create table persons (
personId int auto_increment primary key,
. . .
);
create table personPhones (
personPhonesId int auto_increment primary key,
personId int,
isPrimary boolean,
. . .
foreign key (personId) references persons (personId),
primaryId as (case when isPrimary then personPhonesId end),
unique(primaryId)
);
Similar to the previous solution, this does not guarantee that isPrimary is always set.

You can try the below mentioned design:
Person (Id (PK),name,....)
TelephoneNumber (Id(PK), telNo, PersonId(FK))
PrimaryTelNo (PersonId(FK), TelId(FK))
You can create a table showing mapping of TelId and PersonId and declare the combination of TelId and PersonId as composite primary key

The simplest way is to make the 'first' one primary, but this becomes tricky when you want to change which one is primary. In that case, I believe you can do this...
CREATE TABLE my_table
(person_id INT NOT NULL
,phone VARCHAR(12) not null
,is_primary enum('1') null
,primary key(person_id,phone)
, unique (person_id,is_primary)
);
INSERT INTO my_table VALUES
(1,'123',1),
(1,'234',null),
(1,'345',null),
(2,'456',null),
(2,'567',1),
(2,'678',null);
So, the enum allows values of 1 and null, but while there can be several nulls, there can only be one '1' per person. However, this solution doesn't preclude the possibility that none of the numbers are primary!

You should create a third table person_primary_number with only two fields:
person_id
phone_number_id
In this table you should insert the ids of the person and his primary number. The primary key of this table is on these two columns.
Another way is to add primary_number_id directly to the person table. This is probably the simplest solution.
Then you should have:
person
—————-
id (primary key int autoincrement)
primary_number_id (foreign key for phone_number.id)
name
...
phone_number
———————————-
id (primary key int autoincrement)
person_id (foreign key for person.id)
phone_number
The only problem with this solution is that you can assign as primary phone the number of somebody else.

This violates a strong principle of schema design -- don't pack a list into a cell. But...
If you only need to display the phone number to some human who will be doing a call, and
If that human possibly needs to see non-primary numbers, then
Consider having a VARCHAR(100) column that has a commalist that starts with the 'primary' phone number and continues with alternative numbers.
Note that the application would be responsible for putting the list together, and dealing with updates. Or you could push this back onto the user by providing a UI that asks for "phone number(s), starting with the preferred one to call you with; please separate with commas."

Related

MySQL create table one to many, what is the best option below?

I have below data and using mysql. Person_name is unique and TelephoneNumbers are unique per person.
Person_name1=TelephoneNumber1, TelephoneNumber2, TelephoneNumber3...
Person_name2=TelephoneNumber4, TelephoneNumber5, TelephoneNumber6...
Option 1. Create 1:Many master and child table.
CREATE TABLE Person (
personName varchar(50) NOT NULL,
id int NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
UNIQUE KEY personName (personName)
);
CREATE TABLE Telephone (
telephoneNumber int,
mappingId int,
PRIMARY KEY (telephoneNumber),
foreign key(mappingId) references Person(id)
);
Option 2. Create one table with personName, telephoneNumber as Composite Key.
CREATE TABLE
Person_Telephone (
personName varchar(50) NOT NULL,
telephoneNumber int NOT NULL,
PRIMARY KEY(personName, telephoneNumber)
);
Option 1 is it over complicating creating two tables for just two fields?
Option 2 looks perfect and will there be any issues if Option 2 chosen over Option 1?
The option 2 gives you duplicate persons that you must control in every query.
The best is have the entities separate, it's a classic 1-N relation
Since users can have multiple phone numbers, I think 2 tables would be the best solution.
CREATE TABLE person (
PRIMARY KEY (id) AUTO_INCREMENT,
person_name VARCHAR(45) NOT NULL,
);
CREATE TABLE phone_number (
PRIMARY KEY (id) AUTO_INCREMENT,
phone_number VARCHAR(11) NOT NULL,
FOREIGN KEY (person_id) REFERENCES person(id)
)
Now you can simply JOIN the tables like this:
SELECT
t1.id,
t1.person_name,
t2.phone_number
FROM person t1
LEFT JOIN phone_number t2
ON (t1.id = t2.person_id);

Issue in mysql table creation

I need to create a table called benificiaries where I have three columns
customerid
accountno
bank
The condition should be one customerid can have only one unique accountno. But another customerid can have the same accountno and same unique (only once). So I cant give primary key to accountno. Even for customerid I can't give primary key, since one customerid can have multiple records with unique accountno.
How can we create table in this case? Any ideas?
You can use multiple-column unique index.
CREATE TABLE YOUR_TABLE (
id INT NOT NULL AUTO_INCREMENT,
customerid INT NOT NULL,
accountno INT NOT NULL,
bank INT NOT NULL,
PRIMARY KEY (id),
UNIQUE INDEX name (customerid,accountno)
);
Documentation here.
https://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html
If one customerid can have only 1 unique account number then how can you expect duplicates in terms of customer id in that table?
You can simply set a primary key to another column and make the customerid unique. I think this is what you want to have. Now every customerid is unique, but many costomerids can have the same accountno.
CREATE TABLE benificiaries(
id INT PRIMARY KEY,
customerid INT NOT NULL UNIQUE,
accountno INT NOT NULL,
bank INT NOT NULL
);
database can't manage all the business constraints within the data model. For the case, you might address elementary constraints with indexes (multiple column index for customerid, accountno and simple column index for accountno to perform search on the other way), add an auto-increment id and deal the business constraints in your code.
Just set your customer_id as a primary key then regarding the concept that a only two customer_id can have same account number once, will depend on the process of your App or System.
CREATE TABLE `tmpr_map`.`tbl_example`
(`customer_id` INT(11) NOT NULL AUTO_INCREMENT,
`account_number` VARCHAR NOT NULL , `bank_amount` DECIMAL(11,2) NOT NULL ,
PRIMARY KEY (`customer_id`)) ENGINE = InnoDB;

Can't add foreign key to mysql

I have multiple tables and they all seem to be fine but there is this one table which I'm trying to create but it wont work because I am keep on getting Error1005 "Foreign key constraint is incorrectly formed".
These are the two tables. I don't know what seems to be the problem.
CREATE TABLE Patient(
ID INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
Name VARCHAR(255) NOT NULL,
Age TINYINT UNSIGNED,
Sex VARCHAR(10),
Contact INT(11),
Email TEXT(2083),
PRIMARY KEY(ID)
);
CREATE TABLE Appointments (
Appointment_No INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
Name VARCHAR(255) NOT NULL,
Contact INT(11),
Date DATE NOT NULL,
Time TIME NOT NULL,
Reason TEXT(2083),
PRIMARY KEY(Appointment_No),
FOREIGN KEY (Name, Contact) REFERENCES Patient (Name, Contact)
);
As per me, table structure should be like below:
CREATE TABLE Patient(
ID INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
Name VARCHAR(255) NOT NULL,
Age TINYINT UNSIGNED,
Sex VARCHAR(10),
Contact INT(11),
Email TEXT(2083),
PRIMARY KEY(ID)
);
CREATE TABLE Appointments (
Appointment_No INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
Patient_ID INT UNSIGNED NOT NULL,
Date DATE NOT NULL,
Time TIME NOT NULL,
Reason TEXT(2083),
PRIMARY KEY(Appointment_No),
FOREIGN KEY (Patient_ID) REFERENCES Patient (ID)
);
FOREIGN KEY (Name, Contact) REFERENCES Patient (Name, Contact)
The referenced columns in the Patient table must be part of a key. Ideally you would want Patient (Name, Contact) to be a unique key, because then the foreign key is guaranteed to reference exactly one row in the Patient table.
But in your table definition, the Name and Contact columns are not part of a key. That explains why you got the error given your table design.
But your table design is not good. Name and Contact are not good choices as a unique key, because two people can share a name, and in theory you could even have two people with the same name with the same contact details (for example, the former boxer George Foreman named his five sons George).
#Shadow is correct that it's a better idea is to reference Patient(id) instead, because it's guaranteed to be unique already.
Define Engine so sql statement would end with
ENGINE = MyISAM;
It should fix the issue.
Try to split the two foreign keys in two lines like this:
FOREIGN KEY (Contact) REFERENCES Patient (Contact)
FOREIGN KEY (Name) REFERENCES Patient (Name)

mysql opposite of foreign key

I have 2 tables, customers and affiliates. I need to make sure that customers.email and affiliates.email are exclusive. In other words, a person cannot be both a customer and an affiliate. It's basically the opposite of a foreign key. Is there a way to do this?
You can use a table that stores emails and have unique constrain on the email, and reference that table from the customer and affiliate. (still need to ensure that there are no 2 records referencing the same key)
You can use trigger before insert and before update to check if the email is not present.
Or you can leave this validation to the application logic - not in the database, but in the applicationc ode.
There is no key you can do this with, but it sounds like you shouldn't be using two tables. Instead, you can have one table with either customer/affiliate data (that needs to be unique in this table) and another table that has the type (customer/affiliate).
CREATE TABLE People (
pplid,
pplEmail,
ptid,
UNIQUE KEY (pplEmail)
)
CREATE TABLE PeopleType (
ptid,
ptType
)
INSERT INTO PeopleType VALUES (1, 'affiliates'), (2, 'customers');
You can try the following.
Create a new table, which will be a master for customers and affiliates:
CREATE TABLE party
(
id int not null auto_increment primary key ,
party_type enum('customer','affiliate') not null,
email varchar(100),
UNIQUE (id,party_type)
);
--Then
CREATE TABLE customer
(
....
party_id INT NOT NULL,
party_type enum('customer') NOT NULL DEFAULT 'customer',
PRIMARY KEY (party_id,party_type)
FOREIGN KEY (party_id,party_type) REFERENCES party(id,party_type)
);
CREATE TABLE affiliates
(
....
party_id INT NOT NULL,
party_type enum('affiliate') NOT NULL DEFAULT 'affiliate',
PRIMARY KEY (party_id,party_type)
FOREIGN KEY (party_id,party_type) REFERENCES party(id,party_type)
)
-- enum is used because mysql still doesn't have CHECK constraints
This way each party can be only of one type

Design database table to check id in another table before allowing insert

I've got what I'm sure is a really simple database question, but I don't even know what to google, so hopefully someone can help me here.
I'm trying to create 2 tables that are interconnected. For example, lets say I have an employee table with employee name and employee id, then I'm creating another table, employee_bonus, with employee id and bonus amount. What I want is for the employee_bonus table to automatically check with the employee table every time a row is inserted to make sure that the employee id exists in the employee table and reject the insert if not. The thing is, there could be multiple bonuses for a single employee, so I just want it to check that it exists, not make it one to one. Does that make sense? Is that possible? How would I do that and WHAT is that called?
Also, I'm doing this in MySql.
From wikipedia: Foreign Key
In the context of relational databases, a foreign key is a referential constraint between two tables.
http://en.wikipedia.org/wiki/Foreign_key
The following is a basic example of what you are looking for:
CREATE TABLE employees (
employee_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
family_name VARCHAR(100) NOT NULL,
first_name VARCHAR(100) NOT NULL,
birth_date DATETIME NULL DEFAULT NULL,
PRIMARY KEY(employee_id)
) ENGINE=INNODB;
CREATE TABLE employee_bonus (
bonus_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
employee_id INT UNSIGNED NOT NULL,
bonus_value DECIMAL(10,2) DEFAULT 0.00,
PRIMARY KEY(bonus_id),
KEY fk_employee_id (employee_id),
CONSTRAINT fk_employee_constraint FOREIGN KEY fk_employee_id(employee_id)
REFERENCES employees(employee_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=INNODB;
Since it's not a 1-to-1 relationship give the employee_bonus table its own primary key and establish a foreign key relationship between the employee table's id column and the employee_id in the employee_bonus table.
If you want to avoid an error on insertion when attempting to add a missing employee, try this:
insert into employee_bonus
select ?, ?, ? -- put your insert values in here
from employee
where employee_id = ?; -- put the employee_id in here
This will insert one row if the employee exists and will do nothing if it doesn't.