mysql一对多,主要选择

smdncfj3  于 2021-06-18  发布在  Mysql
关注(0)|答案(6)|浏览(298)

假设我有一群人有多个电话号码。在mysql数据库中,我有一个person表和一个phone number表,它们具有多对一的关系。现在我想把这些号码中的一个作为主要电话号码,并且每人只允许一个主要号码。我该如何建模?

quhf5bfb

quhf5bfb1#

请尝试下面的模式。它将阻止试图为每个人分配多个主要号码的条目。

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:
插入phonenumber(phonenumber,person\u id,is\u primary)值('0123211234',1,1);
您将得到一个“重复条目'1-1'键'idx\u person\u primary'”错误。
http://sqlfiddle.com/#!9/dbb3c7/1号

d7v8vwbk

d7v8vwbk2#

“只有一个主要电话号码”很棘手。一种方法使用触发器。其他数据库提供基于表达式的索引。这很棘手,因为:
约束跨越两个表。
在更新中保证精确的“一性”是很棘手的。
但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 总是设定好的。

mcvgt66p

mcvgt66p3#

这违反了模式设计的一个重要原则——不要将列表打包到单元格中。但是。。。
如果你只需要把电话号码显示给某个打电话的人
如果人类可能需要看到非主要的数字,那么
考虑有一个 VARCHAR(100) 列,其中有一个逗号,以“主要”电话号码开始,并以其他号码继续。
请注意,应用程序将负责将列表放在一起,并处理更新。或者你也可以通过提供一个用户界面,要求用户输入“电话号码”,从首选号码开始;请用逗号分开。”

6za6bjd0

6za6bjd04#

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

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

您可以创建一个表来显示 TelId 以及 PersonId 并声明 TelId 以及 PersonId 作为 composite primary key

guykilcj

guykilcj5#

您应该创建第三个表 person_primary_number 只有两个字段:

person_id
phone_number_id

在此表中,您应该插入此人的ID及其主要号码。此表的主键位于这两列上。
另一种方法是添加 primary_number_id 直接到 person table。这可能是最简单的解决方案。
那么你应该有:

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

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

pdtvr36n

pdtvr36n6#

最简单的方法是将“第一个”设为主要的,但是当您想更改哪个是主要的时,这就变得很棘手了。那样的话,我相信你能做到。。。

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”。然而,这个解决方案并不排除没有一个数字是主要的可能性!

相关问题