我已经为一个客户编写了一个会员网站,其中的会员在其他用户的下方加入。例如
userid | name | subof
1 | John | 0
2 | Joe | 1
3 | Jill | 0
4 | Janet | 2
5 | Juan | 1
6 | George| 2
约翰和吉尔在顶部,乔和胡安在约翰之下,珍妮特和乔治在乔之下。分层用于传递佣金。我的客户希望能够查看在任何给定用户之下有多少用户,(至少它被限制为 8 层)
现在我已经将附加字段“num_below”添加到用户表中,每当有人加入或离开用户下方时,该字段就会递增或递减。
这样做的第一个问题是感觉它违反了良好的数据库规范化实践~因为它存储了已经在数据库中的数据
第二个是当我的客户过来说“哦,乔治打算加入胡安的手下,请移动他”时,它变得毛茸茸的
我考虑只是在每次被要求时动态计算下面的数字,但数据库查询似乎呈指数增长。
我写了一个rectifySubs()
函数,可以遍历并修复所有的`num_below`字段,但是随着成员越来越多,运行起来会越来越密集~
function rectifySubs(){
$NumBelow=array();//UID=>NUM_BELOW
$SubOf=array();//UID=>IS_A_SUB_OF_UID
$Uids=array();//UID
$r=mysql_query("SELECT uid,subof FROM user");
if(!$r || mysql_num_rows($r)==0){return 'Invalid';}
while(list($uid,$subof)=mysql_fetch_row($r)){
$NumBelow[$uid]=0;
$SubOf[$uid]=$subof;
$Uids[]=$uid;
}
mysql_free_result($r);
$RungsUp=8;
foreach($Uids as $uid){
$r=1;
$parent=$SubOf[$uid];
while($parent>0 && $r<=$RungsUp){
$NumBelow[$parent]+=1;
$parent=$SubOf[$parent];
$r++;
}
}
$QueryByNum=array();
foreach($NumBelow as $uid=>$num){
if(!isset($QueryByNum[$num])){$QueryByNum[$num]=array();}
$QueryByNum[$num][]=$uid;
}
unset($QueryByNum[0]);
mysql_query("UPDATE user SET below=0");
foreach($QueryByNum as $num=>$uids){
$where=$or='';
foreach($uids as $uid){
$where.=$or."`uid`=".$uid;
$or=" OR ";
}
mysql_query("UPDATE user SET below=".$num." WHERE ".$where);
}
}
有什么建议吗?我不想在数据库中放置过多的冗余数据,但每次执行 8 层似乎太占用处理器资源了。
- 编辑 -
我不太清楚这些层是如何工作的,所以我把桌子做得更大了。我在编辑中解决的关键问题是,任何人都可以在其正下方的一层中拥有多个人。希望这是有道理的。
-- SOLUTION -- (将 Kakao 的解决方案作为 'Member' 类的方法实现)
protected function getNumBelowAtLevel($i=1,$force=false){
$i=abs((int)$i);
if($i<=1){return 0;}//Level 1 is just the member themselves
if($force || !isset($this->numBelow[$i])){
$Us='';
$Sels='';
$Lefts='';
$Groups='';
$comma='';
$nl='';
for($k=1;$k<=$i-1;$k++){
$j=$k==1?'0':$k-1;
$Us.=$comma.'u'.$k;
$Sels.=$comma.$nl.'m'.$k.'.mid as u'.$k;
$Lefts.=$nl.'left join members as m'.$k.' on m'.$k.'.subof = m'.$j.'.mid';
$Groups.=$comma.'u'.$k;
$nl="\n\t\t\t\t\t";
$comma=', ';
}
$sql="select count(*) - 1 as users_below
from (
select distinct {$Us}
from (
select
{$Sels}
from members as m0
{$Lefts}
where m0.mid = {$this->id}
group by {$Groups} with rollup
) d
) a";
if(DEBUG){var_dump($sql);}
$r=mysql_query($sql);
list($this->numBelow[$i])=mysql_fetch_row($r);
}
return $this->numBelow[$i];
}