我們知道數(shù)據(jù)庫(kù)中有以下幾個(gè)概念:
- 主鍵:一般情況下,滿足第一范式的表都有一個(gè)主鍵Primary key,用于唯一標(biāo)示數(shù)據(jù)庫(kù)中的一個(gè)字段
- 外鍵: 外鍵是相對(duì)于數(shù)據(jù)庫(kù)設(shè)計(jì)中的參考完整性而言,它與主鍵之間是彼此依賴的關(guān)系。
- 主表: 在數(shù)據(jù)庫(kù)中建立的表格即Table,其中存在主鍵(primary key)用于與其它表相關(guān)聯(lián),并且作為在主表中的唯一性標(biāo)識(shí)
- 從表: 以主表的主鍵(primary key)值為外鍵 (Foreign Key)的表,可以通過(guò)外鍵與主表進(jìn)行關(guān)聯(lián)查詢。從表與主表通過(guò)外鍵進(jìn)行關(guān)聯(lián)查詢
有如下兩個(gè)表,主表是fansArr,是粉絲的信息記錄。主鍵:fans_id,格式為 xxx-xxx-xx 表示一個(gè)32位的字符,避免重復(fù)。
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'
}
];
不難發(fā)現(xiàn),粉絲和粉絲個(gè)人圖片是 一對(duì)多 的關(guān)系。
問(wèn)題
現(xiàn)在有個(gè)需求,每個(gè)粉絲至少一張圖片,怎么校驗(yàn)?
解法一: 外鍵去重
弊端:性能低,因?yàn)橐粚?duì)多的關(guān)系,當(dāng)外表記錄的信息太多時(shí),遍歷次數(shù)太多。
/**
* 遍歷 imgArr 數(shù)組,得到所有 fansId
*/
function emptyFn1() {
let arr = [];
imgArr.forEach( img => {
if(!arr.includes(img.fansId)) {
arr.push(img.fansId)
}
})
return arr.length !== fansArr.length
}
哈哈哈,方法一并不總是有效的?。?!仔細(xì)看看問(wèn)題出在哪里?
理想情況下外表是根據(jù)主表的主鍵作為外鍵來(lái)記錄信息的,但是如果主表的主鍵改了,而外表的外鍵沒(méi)有更改,方法一只是保證了主鍵和外鍵的數(shù)量一致,但內(nèi)容不一定一樣。不信把 fansArr[0].fans_id 改成 xxx-xxx-xx3 試試
解法二: 遍歷主表,獲取所有在從表中出現(xiàn)的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
}
對(duì)解法二的優(yōu)化:forEach 會(huì)遍歷所有,實(shí)際可能并不需要遍歷全部
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, 則字符串中可能存在干擾項(xiàng)