2

说,User有很多Devices。即,Device.user_id references User.id

我们需要将“活动”状态添加到用户设备关系。这意味着 aUser只能有一个 'active' Device

-- 附录

看来我必须澄清一些事情。

  • AUser实际上可以没有Devices、一个Device或几个Devices;
  • Device可以拥有User,但不能;
  • 那个“所有权”就是那个参考Device.user_id references User.id
  • User.id并且Device.id是唯一的 PK 并且不得更改(因此,没有复合 PK);
  • AUser只能有一个'active' Device,但它可以是NULL,但如果不是NULL,它必须引用DevicethisUser拥有的 s 之一。

--结束附录

我可以想象两种方法,都非常简单:

  1. 向 中添加一个active_device_id字段User,即User.active_device_id references Device.id;
  2. 向实体添加Boolean-type 标志。Device

最常见的查询是:

  • Device为给定选择一个活动User
  • 检查当前Device是否对拥有者有效User

很少使用,但非常重要的是:

  • 使给定的设备Device激活User(并使其他User设备处于非活动状态);
  • 更改给定的所有权Device

第一种方法有两个警告:

  • user1将活动设备设置为device1,但device1user2;
  • 两者都user1具有user2相同的活动device(尽管可以通过对 field 的唯一约束轻松修复它User.active_device_id

2d 方法可能会导致几个 active Devices for one User

这两种方法的其他缺点是什么?

...我应该选择什么,为什么?:)

4

4 回答 4

1

I would definitely go for the first approach -- the active_device_id.

It enables a guarantee of only one active device per user, and as you say allows an optional guarantee of uniqueness of active device id across all users.

All of the queries are very straightforward with this method.

于 2013-10-03T13:28:49.530 回答
1

您没有提到您的 DBMS,但大多数 DBMS 都有可能创建部分索引,这对它们来说似乎是一个完美的用例。

我有点困惑您的设备表的外观,但假设结构:

create table device
(
   device_id           integer not null,
   user_id             integer not null,
   is_active_for_user  boolean, 
   primary key (device_id, user_id)
);

您可以使用以下索引确保每个用户只有一个活动设备(Postgres 语法,其他 DBMS 有其他语法来定义部分索引):

create unique index idx_unique_active_device 
    on device (user_id)
    where is_active_for_user;
于 2013-10-03T14:09:04.520 回答
0

如果您试图强制每个用户使用一个唯一设备,那么您所需要的只是对表中引用的deviceId列的唯一约束user。不需要旗帜。

设备
------
ID

用户
------
ID
deviceId(fk,唯一约束)

如果您遵循David 的方法,则无法保证每个用户只有一台活动设备。您必须有一个UNIQUE约束来保证每个用户一台设备:

这将允许重复设备:

CREATE TABLE device (
  `id` int,
  PRIMARY KEY(`id`)
) ENGINE=INNODB;

INSERT INTO device (`id`)
VALUES (1), (2);

CREATE TABLE user (
  `id` int, 
  `active_device_id` int,
  INDEX (`active_device_id`),
    FOREIGN KEY (`active_device_id`)
    REFERENCES `device`(`id`)
) ENGINE=INNODB;

INSERT INTO user (`id`, `active_device_id`)
VALUES (1, 1), (2, 1);

添加UNIQUE保证每个用户都将拥有唯一的设备。

CREATE TABLE device (
  `id` int,
  PRIMARY KEY(`id`)
) ENGINE=INNODB;

INSERT INTO device (`id`)
VALUES (1), (2);

CREATE TABLE user (
  `id` int, 
  `active_device_id` int,
  INDEX (`active_device_id`),
  UNIQUE (`active_device_id`),
    FOREIGN KEY (`active_device_id`)
    REFERENCES `device`(`id`)
) ENGINE=INNODB;

INSERT INTO user (`id`, `active_device_id`)
VALUES (1, 1), (2, 1);
-- Duplicate entry '1' for key 'active_device_id_2': 
于 2013-10-03T13:33:16.437 回答
0

您可以使用以下模型...

在此处输入图像描述

...为了保证:

  • 每个用户有多个设备,但每个用户最多有一个活动设备,因为ActiveDeviceNo与用户在同一行。
  • 在支持延迟外键的 DBMS 上,此模型还可以强制每个用户只使用一个活动设备(通过设置ActiveDeviceNoNOT NULL)。
  • 保证为用户活动的设备属于同一用户。这是通过使用从用户到设备的识别关系来实现的UserId,然后将其返回给用户并与原始设备“合并” UserId

这个模型非常适合集群。如果将 PK 用作集群键,则获取给定用户的所有设备将产生最少的 I/O,因为同一用户的设备在物理上靠近存储在数据库中。它自然也代表了设备的每个用户顺序(如果这很重要的话)。

这是直接且节省空间的方法,但在关键设计中强制进行了某些权衡:

  • 当设备移动到另一个用户时需要修改密钥,这可能需要级联到引用它的其他表。此外,键“更胖”,使引用表中的外键更胖。如果避免此类问题很重要,您始终可以向设备添加代理键( ),然后从其他表中DeviceId引用它。
  • 虽然您可以只对 使用自动增量DeviceNo,但这并不是完全最优的,并且会产生比所需更大的数字(如果 DBMS 支持数字的可变长度表示,则会浪费空间)。OTOH,在并发环境中找到更优化的值DeviceNo可能会有些麻烦。

除此之外,您可以使用带有“活动”标志的过滤/部分唯一索引(正如其他人所建议的那样),或者如果您的 DBMS 不支持,您可以执行以下操作:

CREATE TABLE Device (
    DeviceId INT PRIMARY KEY,
    UserId INT REFERENCES User (UserId),
    ActiveUserId INT UNIQUE,
    CHECK (ActiveUserId IS NULL OR ActiveUserId = UserId)
);

这是因为 UNIQUE 约束忽略 NULL 值(假设您的 DBMS 正确处理 NULL)。

于 2013-10-03T17:51:27.433 回答