我们知道数据库中有以下几个概念:
- 主键:一般情况下,满足第一范式的表都有一个主键Primary key,用于唯一标示数据库中的一个字段
- 外键: 外键是相对于数据库设计中的参考完整性而言,它与主键之间是彼此依赖的关系。
- 主表: 在数据库中建立的表格即Table,其中存在主键(primary key)用于与其它表相关联,并且作为在主表中的唯一性标识
- 从表: 以主表的主键(primary key)值为外键 (Foreign Key)的表,可以通过外键与主表进行关联查询。从表与主表通过外键进行关联查询
有如下两个表,主表是fansArr,是粉丝的信息记录。主键:fans_id,格式为 xxx-xxx-xx 表示一个32位的字符,避免重复。
const fansArr = [
{
fans_id: 'xxx-xxx-xx1',
name: 'xx'
},
{
fans_id: 'xxx-xxx-xx2',
name: 'xy'
}
];
从表是imgArr, 是所有粉丝图片的记录。外键是 fansId,
const imgArr = [
{
fansId: 'xxx-xxx-xx1',
imgUrl: 'xxxx.jpg'
},
{
fansId: 'xxx-xxx-xx1',
imgUrl: 'xxxx.jpg'
},
{
fansId: 'xxx-xxx-xx2',
imgUrl: 'xxxx.jpg'
},
{
fansId: 'xxx-xxx-xx1',
imgUrl: 'xxxx.jpg'
}
];
不难发现,粉丝和粉丝个人图片是 一对多 的关系。
问题
现在有个需求,每个粉丝至少一张图片,怎么校验?
解法一: 外键去重
弊端:性能低,因为一对多的关系,当外表记录的信息太多时,遍历次数太多。
/**
* 遍历 imgArr 数组,得到所有 fansId
*/
function emptyFn1() {
let arr = [];
imgArr.forEach( img => {
if(!arr.includes(img.fansId)) {
arr.push(img.fansId)
}
})
return arr.length !== fansArr.length
}
哈哈哈,方法一并不总是有效的!!!仔细看看问题出在哪里?
理想情况下外表是根据主表的主键作为外键来记录信息的,但是如果主表的主键改了,而外表的外键没有更改,方法一只是保证了主键和外键的数量一致,但内容不一定一样。不信把 fansArr[0].fans_id
改成 xxx-xxx-xx3
试试
解法二: 遍历主表,获取所有在从表中出现的fans_id
function emptyFn2() {
let arr = [];
let str = JSON.stringify(imgArr);
fansArr.forEach(fans => {
if(str.includes(fans.fans_id)) {
arr.push(fans.fans_id)
}
})
return arr.length !== fansArr.length
}
对解法二的优化:forEach 会遍历所有,实际可能并不需要遍历全部
function emptyFn3() {
let str = JSON.stringify(imgArr);
if(fansArr.length > 0) {
return !fansArr.every(fans => {
return str.includes(fans.fans_id)
})
}
throw new Error('主表是空的');
}
方法 二、三的弊端
fans_id 如果不是唯一的字符串,则存在不确定性
例如 fans_id 是普通的 1, 2, 3, 则字符串中可能存在干扰项