具有两个可能的唯一键的数据库表

Posted

技术标签:

【中文标题】具有两个可能的唯一键的数据库表【英文标题】:Database table with two possible unique keys 【发布时间】:2021-10-26 22:23:08 【问题描述】:

我有一张桌子,我正在努力弄清楚如何最好地构建它。它应该包含一堆文件的元数据,对于每个文件,我们都有以下信息:

文件名 文件大小 上次修改 md5(有时)

我希望唯一键位于 md5,但如果 md5 是 NULL,我希望它回退到 filename+filesize。是的,文件名+文件大小不是完美匹配,但根据我所做的分析,它的准确率约为 99.9%,而且对于超过 24 个字符的文件名,准确率几乎为 100%。

存储此密钥的最佳方式是什么?我应该添加一个应用生成的列(或触发器?)类似于:

COALESCE(md5, CONCAT(filename, filesize))

或者这种“多键”的事情在实践中通常是如何完成的?

【问题讨论】:

md5 是从哪里来的,你能手动创建缺失值并在缺失时添加插入吗? @Stu 不,这是资产的 md5——我通常无权访问该文件,有时我可以,但文件很大,需要很长时间才能生成(~100GB 视频)。 出于性能原因,我会坚持使用一个主键。我遇到了复合键的问题,包括空值。 @RaphaelPICCOLO 你建议怎么做?触发器?在插入之前的应用程序中?等 我认为要给出有用的答案,您应该详细说明您的应用程序。当您尝试插入一条已经存在 md5 的新记录时会发生什么?这不可能发生;或者如果文件名和大小不同,则替换记录?你给个错误?忽略操作?类似地插入 md5 为空的记录。 【参考方案1】:
filesize BIGINT UNSIGNED NOT NULL,
md5 BINARY(16) NULL,
PRIMARY KEY(id?)  -- UNIQUE
INDEX(filename, filesize),  -- or UNIQUE?
UNIQUE(md5)    -- ok to have multiple NULLs

( SELECT ...
    WHERE md5 = UNHEX(?) )
UNION ALL
( SELECT ...
    WHERE filename = ?
      AND filesize = ? )
ORDER BY md5 DESC      -- give preference to non-NULL
LIMIT 1                -- Either the md5 one or some one with the desired size

是的,一个表可以有多个唯一键,但这种情况很少见。这通常是架构设计不佳的标志。

我避免使用COALESCE(),因为它不可分割,因此很慢。上面的代码将是两个快速索引查找,然后是一个简单的排序。

【讨论】:

谢谢。出于好奇,您为什么将 md5 存储为 BINARY(16) 而不是 CHAR(32) ? @David542 - 占用了一半的空间。但是,它确实需要应用程序代码来执行 UNHEX()HEX()。对于笑容,我曾经使用过 Base64 和 CHAR(22) COLLATE ascii_bin。这是SELECT * 的可读性和节省一些空间之间的折衷。【参考方案2】:

我会选择 2 个键(都是非唯一的):

你的 md5 列上的一个键 以及文件名 + 文件大小的键

加上一个正常的自增主键

当你执行 sql 查询时,mysql 会自动决定使用哪个索引。

所有 thos 3 请求都应该有效。您还可以在 select 查询前面添加 explain 关键字来检查索引使用和瓶颈。

explain select * from files where md5 = 'xxx';
explain select * from files where filename = 'xxx' and size = 'xxx'
explain select * from files where md5 = 'xxx' or (filename = 'xxx' and size = 'xxx')

【讨论】:

违背了整个目的...两个键都必须是唯一的(除非它们为空) 理论上,2个文件可以有相同的内容,那么相同的md5对吗?独特的约束会阻止这种情况。如果你愿意,你可以做一个独特的约束 我认为您是在建议如何提高查询性能(因此解释),我不关心查询,我只关心完整性和架构。 如果你想让它工作,你不应该在 md5 列中使用空值。那么你有一个 3 列的唯一键。这是对空值的解释。 ***.com/questions/3086382/… 带有OR 的那个将进行全表扫描。【参考方案3】:

你的逻辑很好, 我只会进行简单的调整,让 ID 列是自动递增的,并且对于每一行都不为空,如果可能的话,我会根据它来做唯一键

建议 -> concat(ID,"",md5,"",concat(filename, filesize))

Picture

这样你就可以确保每一行都有唯一的键,

几乎唯一对数据建模没有帮助,您说您有 99.9% 的准确率,这意味着有多个唯一键,这对 ER 模型不利,并且当通过该键连接表时,它可以使很多- 许多连接和复制数据。

希望对你有所帮助^^

【讨论】:

感谢您的建议,但是将 ID 放在合并的开头会自动使每一行的值成为该值。你的意思是放在最后吗? 另外,您将在何时/何地填充该构造键?在触发器中?在应用程序中插入之前?还是在哪里? @finally -- 为什么合并中的额外" "?合并的目的不是获取第一个非空值吗? 嘿,它可以在触发器中,通常我用 ETL 做,但是当你拥有所有符合特定条件的数据时,你可以添加新列,先形成表格,然后添加新列这将根据您的决定生成密钥:) 看更新的图片,我认为应该如何,在这个视图中它没有显示好的格式

以上是关于具有两个可能的唯一键的数据库表的主要内容,如果未能解决你的问题,请参考以下文章

比较两个没有唯一键的表

具有多个附加到列表的主键的数据库

SAS/PROC-SQL 从具有唯一键的表转换为具有相同键的多行表

具有主键和外键的事实表

具有不同级别日期维度数据作为日期维度键的事实表

在交叉引用表中创建具有两个外键的 MySQL 表