メンバーが他のユーザーの下に参加するクライアントのメンバーシップサイトをプログラムしました。例えば
userid | name | subof
1 | John | 0
2 | Joe | 1
3 | Jill | 0
4 | Janet | 2
5 | Juan | 1
6 | George| 2
ジョンとジルが一番上にあり、ジョーとファンがジョンの下にあり、ジャネットとジョージがジョーの下にあります。階層化は、手数料を渡すために使用されます。私のクライアントは、特定のユーザーの下にいるユーザーの数を確認できるようにしたいと考えています(少なくとも、8層に制限されています) 。
今のところ、ユーザーテーブルに追加のフィールド `num_below`を追加しました。このフィールドは、誰かがユーザーに参加またはユーザーの下を離れるたびにインクリメントまたはデクリメントされます。
これに関する最初の問題は、データベースの正規化の適切な慣行に違反しているように感じることです。これは、すでにDBにあるデータを保存しているためです。
2つ目は、クライアントが来て「ジョージはフアンの下に参加するつもりだったので、彼を動かしてください」と言うと、毛むくじゃらになるということです。
要求されるたびに以下の数値を動的に計算することを検討しましたが、dbクエリは指数関数的に増加するように見えます。
すべての`num_below`フィールドを調べて修正できる関数を作成しましたrectifySubs()
が、メンバーが増えるにつれて、実行するのがますます集中的になります〜
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);
}
}
何かお勧めはありますか?DBに冗長なデータを入れすぎたくないのですが、毎回8層にするのは、プロセッサに負担がかかりすぎるようです。
- 編集 -
ティアがどのように機能するかについて十分に明確ではなかったので、テーブルを大きくしました。私が編集で取り組んでいる重要な問題は、誰でもそのすぐ下の層に複数の人がいる可能性があるということです。それが理にかなっていることを願っています。
-ソリューション-(「メンバー」クラスのメソッドとしてのカカオのソリューションの実装)
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];
}