Billzabob
Billzabob

Reputation: 33

MySQL one to many with primary choice

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?

Upvotes: 0

Views: 135

Answers (6)

Rick James
Rick James

Reputation: 142278

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."

Upvotes: 0

Gordon Linoff
Gordon Linoff

Reputation: 1269633

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.

Upvotes: 1

Genome
Genome

Reputation: 1236

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

Upvotes: 1

kiks73
kiks73

Reputation: 3758

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.

Upvotes: 0

Strawberry
Strawberry

Reputation: 33935

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!

Upvotes: 0

codeLover
codeLover

Reputation: 2592

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

Upvotes: 0

Related Questions