MySQL与主要选择一对多

时间:2018-10-25 05:00:12

标签: mysql sql database-design

说我有一堆人有多个电话号码。在MySQL数据库中,我将具有一个人与人表和一个具有多对一关系的电话号码表。 现在,我想将其中一个号码设为主要电话号码,并且每人只允许使用一个主要号码。我将如何建模?

6 个答案:

答案 0 :(得分:1)

“仅一个主要电话号码”就比较棘手。一种方式使用触发器。其他数据库提供基于表达式的索引。这很棘手,因为:

  • 该约束跨越两个表。
  • 保证更新过程中准确的“一体性”非常棘手。

但是MySQL中一种接近且不使用触发器的方法:

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);

很容易将primary_personPhonesId声明为not null。但是,这使得很难在两个表中插入行。

另一种方法使用计算列:

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)
);

类似于先前的解决方案,这不能保证始终设置isPrimary

答案 1 :(得分:0)

您可以尝试以下提到的设计:

Person (Id (PK),name,....)
TelephoneNumber (Id(PK), telNo, PersonId(FK))
PrimaryTelNo (PersonId(FK), TelId(FK))

您可以创建一个表,显示TelIdPersonId的映射,并将TelIdPersonId的组合声明为composite primary key

答案 2 :(得分:0)

尝试以下模式。这样可以防止试图为每个人分配多个主要号码的条目。

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);

这将使DB可以为每个人管理一个单一的主要电话号码。例如,如果您尝试为Michael Jones分配另一个主要电话号码:

将电话号码(电话号码,person_id,is_primary)插入VALUES('0123211234',1,1);

您将收到“键'idx_person_primary'的重复条目'1-1'”错误。

http://sqlfiddle.com/#!9/dbb3c7/1

答案 3 :(得分:0)

您应该创建仅包含两个字段的第三个表person_primary_number

person_id
phone_number_id

在此表中,您应该插入此人的ID及其主要电话号码。该表的主键在这两列上。

另一种方法是将primary_number_id直接添加到person表中。这可能是最简单的解决方案。

那么您应该拥有:

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

此解决方案的唯一问题是您可以将其他人的号码分配为主电话。

答案 4 :(得分:0)

最简单的方法是将“第一个”作为主要对象,但这在您要更改哪个是主要对象时变得很棘手。在那种情况下,我相信你可以做到这一点...

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);

因此,该枚举允许值为1和null,但是虽然可以有多个null,但每个人只能有一个“ 1”。但是,此解决方案并不排除所有数字都不是主要数字的可能性!

答案 5 :(得分:0)

这违反了架构设计的强大原则-不要将列表打包到单元格中。但是...

  • 如果您只需要向要打个电话的人显示电话号码,
  • 如果该人可能需要查看非主要数字,那么

请考虑使用一个awk -v q="'" '{for(i=2;i<=NF;i++)if($i~/^[^-]/)$i=q $i q}7' file 列,该列的逗号以“主要”电话号码开头,并以备用号码开头。

请注意,应用程序将负责将列表放在一起,并处理更新。或者,您也可以通过提供一个要求输入“电话号码(从首选电话与您联系的电话号码;请用逗号分隔)”的UI,将其推回给用户。