我有一张记录许可证使用情况的表格.每个许可证使用都需要与用户和主机相关联.表定义如下所示.
create table if not exists per_user_fact ( per_user_fact_id int unsigned not null auto_increment, time_of_day char(16) not null, license_served_id smallint unsigned not null, license_hours numeric(10,2) not null, role_name varchar(64) null, user varchar(128) not null, host varchar(128) not null, primary key (per_user_fact_id), foreign key (license_served_id) references served_license(served_license_id), foreign key (user, host) references user_host(username, hostname) );
我想规范化这个表,以便将重复的用户/主机值移动到这样的新表.
create table if not exists user_host ( username varchar(64) not null, hostname varchar(128) not null, primary key (username, hostname) );
对于user_host表,我应该选择哪种主键 - 自然或代理?我可以想到以下控制因素.
如果主键是自然的,即用户名和主机名的组合,则父表per_user_fact将不需要额外的连接来查找用户名和主机名.
如果主键是自然的,则会浪费存储空间,因为两个表中的用户名和主机名值都将重复.
如果主键是代理项,则父表需要额外的连接来获取用户名和主机名的值.
如果主键是代理,则user_host表上的索引将更快.
请指教.
即使在这种情况下,我也非常喜欢使用代理主键.加入群集主键时,附加连接的开销可以忽略不计.
此外,假设username
并且hostname
(一起)长于四个左右的字符,代理键可以节省空间.实际上,您可能会发现代理键导致查询速度更快,因为数据输入per_user_fact
更小.较小的表占用较少的数据页,从而减少了I/O.
代理键的另一个优点是可以在不修改任何其他表的情况下更改用户名和主机名.如果要使用数据字段进行连接,则修改这些值需要更新多个表 - 这是一种更麻烦的操作.
我也喜欢代理身份/串行/自动增量键,因为它们也捕获表中的插入顺序.当然,还有其他方法(我的表通常有一个CreatedAt
默认为插入时间的列).但是,代理键也可以扮演这个角色.
这些原因并不构成问题的"正确"答案.没有使用代理人的正当理由.但对我来说,几乎所有桌子都有这样的主键.