понял, значит тут подойдет алгоритм пересчета границ примерно как в Битрикс
function ReSort($IBLOCK_ID, $ID=0, $cnt=0, $depth=0, $ACTIVE="Y")
{
global $DB;
$IBLOCK_ID = IntVal($IBLOCK_ID);
if($ID > 0)
{
$DB->Query("
UPDATE
b_iblock_section
SET
TIMESTAMP_X=".($DB->type=="ORACLE"?"NULL":"TIMESTAMP_X")."
,RIGHT_MARGIN=".IntVal($cnt)."
,LEFT_MARGIN=".IntVal($cnt)."
WHERE
ID=".IntVal($ID)
);
}
$strSql = "
SELECT BS.ID, BS.ACTIVE
FROM b_iblock_section BS
WHERE BS.IBLOCK_ID = ".$IBLOCK_ID."
AND ".($ID>0? "BS.IBLOCK_SECTION_ID=".IntVal($ID): "BS.IBLOCK_SECTION_ID IS NULL")."
ORDER BY BS.SORT, BS.NAME
";
$cnt++;
$res = $DB->Query($strSql);
while($arr = $res->Fetch())
$cnt = CIBlockSection::ReSort($IBLOCK_ID, $arr["ID"], $cnt, $depth+1, ($ACTIVE=="Y" && $arr["ACTIVE"]=="Y" ? "Y" : "N"));
if($ID==0)
{
$obIBlockRights = new CIBlockRights($IBLOCK_ID);
$obIBlockRights->Recalculate();
return true;
}
$DB->Query("
UPDATE
b_iblock_section
SET
TIMESTAMP_X=".($DB->type=="ORACLE"?"NULL":"TIMESTAMP_X")."
,RIGHT_MARGIN=".IntVal($cnt)."
,DEPTH_LEVEL=".IntVal($depth)."
,GLOBAL_ACTIVE='".$ACTIVE."'
WHERE
ID=".IntVal($ID)
);
return $cnt+1;
}
т.е. проходимся рекурсивно вглубь категорий и присваиваем левую границу = счетчику, увеличивая его, а выходя, присваиваем правую границу