id rootid deep ordernum  title
 1    0     1     0       根
 2    1     2     32      电脑
 4    1     3     48     硬件 ******************
 3    1     3     64      软件
 5    1     4     96      系统应用
 6    1     5     112     系统优化      修改id=4 的ordernum为ordernum(id=2)和ordernum(id=3)的中值
注意,若ordernum为ordernum(id=2)和ordernum(id=3)的差小于2时,应先调整ordernum(id=3)及以后的ordernum,以便能容纳ordernum(id=4)的新值记得我在春节前发过一个思考题,但是响应者廖廖。所以也就没有再贴出我的代码了

解决方案 »

  1.   

    不知道这个能否对你有帮助。我以前写的 $pieces = explode("-", $_POST['list_order']);
    $list_order=$pieces[0];
    $showid=$pieces[1];
    $id_zhu=$list_order-1;

    //print $list_order;exit;
    if($id_zhu==0){$public_class->msg('已是顶层,不能在上移动!');exit;}

    //修改被移动-往下移动
    $B_sql = "update SW_web_list set list_order='$list_order' where SW_userid='".$_SESSION['SW_userid']."' and list_order='$id_zhu'";

    //修改主移动-往上移动
    $Z_sql = "update SW_web_list set list_order='$id_zhu' where SW_userid='".$_SESSION['SW_userid']."' and id='$showid'";

    if(mysql_query($B_sql) and mysql_query($Z_sql))
    {
        print "<meta http-equiv=Refresh content=0;url=list_order.php?order=$id_zhu>";
    }else{
    $public_class->msg('发生未知错误,请关闭重试!');
    }
      

  2.   

    不好意思,楼上的,我没看明白你写的.
    to xuzuning:
    是的,当时我根据我的分析写过一些代码,不知道你看了没有.
    现在的问题是,硬件下面没有子分类,如果有子分类,只移动该结点结果是不对的,
    难道需要将所有子结点也都进行相应的ordernum的修改吗?
      

  3.   

    稻草人你可以看看我的心空:ckong.cn。
    就是中值排序。你看看我后台的版面处理怎么管理的你就明白了。用我那种方法更简单。
      

  4.   

    to  zairwolf(君子兰) 
    我安装完你的论坛,index.php显示一片空白,怎么回事?
    想看看header.php中的内容,可惜加密了。
      

  5.   

    引用
    ==================================
    现在的问题是,硬件下面没有子分类,如果有子分类,只移动该结点结果是不对的,
    难道需要将所有子结点也都进行相应的ordernum的修改吗?
    =============================================================查询的时候得到 表(id rootid deep ordernum  title)中的 ordernum ,根据这个值来排序。
    这样做可能会慢一点,不过也是一个解决办法啊。
      

  6.   

    现在的问题是,硬件下面没有子分类,如果有子分类,只移动该结点结果是不对的,
    难道需要将所有子结点也都进行相应的ordernum的修改吗?是的!这是必然的
      

  7.   

    调整分类(包括移动节点)的操作是有限的,所以所谓“效率”是不值得一提的。
    况且在数据库中执行一条有过滤条件的update语句时,操作的是一条记录还是多条记录所需的时间悬殊不是很大,尤其是对有索引的字段
      

  8.   

    <?php
    require_once 'adodb/tohtml.inc.php'; 
    require_once 'adodb/adodb.inc.php';
    $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;/**
     * 一个简单的数据库类
     * 你需要根据你所使用的数据库来修改这个类
     * 你也可以扩展这个类,或者从你正在使用的数据库类中派生出这个类
     * 但必须约定提供query和next方法,并正确设置属性eof和insert_id属性
     * 这里是一个从ADODB构造出来的类
     **/
    class DB extends Object {
      var $conn; //数据库连字
      var $result; //查询连接字
      var $insert_id = 0; //最后插入的id
      var $eof = true; //查询结果的指针状态,无记录或到达记录尾时为true
      var $fields; //对象,保存查询结果。之所以用对象是为了统一书写格式
      /**
       * 构造函数
       * 可以接受一个传入的参数,用于重建数据表
       **/
      function DB($param='') {
    $this->conn = &ADONewConnection('mssql');
    // 侦错
    $this->conn->debug = false; // DSN 四项基本资料设定
    $mch = "localhost";
    $user = "sa";
    $pwd = "";
    $database = "test";
    $this->conn->Connect($mch, $user, $pwd, $database); if($param != '') $this->_init($param);
      }
      /**
       * 执行一个查询
       **/ 
      function query($sql) {
    $this->result = &$this->conn->Execute($sql); // 若 $rs 为 false,则输出错误讯息
    if (!$this->result)
    exit($this->conn->ErrorMsg()); 
    if(eregi("^select ",$sql)) {
    if(!($this->eof = $this->result->EOF)) {
    $this->fields = $this->fields = $this->result->fields;
    settype($this->fields,'object');
    }
    }else {
    if(eregi("^insert ",$sql))
    $this->insert_id = $this->conn->Insert_ID();
    }
      }
      /**
       * 从查询结果中取下一个记录到fields
       **/
      function next() {
    if(!($this->eof = $this->result->EOF)) {
    $this->fields = $this->result->FetchRow();
    settype($this->fields,'object');
    }
      }
      /**
       * 执行一个重建数据表的sql指令集
       * sql指令集可以是保存在文件中的,也可以保存在变量中
       **/
      function _init($sql='') {
    if($sql == '') return;
    if(glob($sql))
    $sql = file_get_contents($sql);
    $ar = split(';',preg_replace("/[\r\n]+/","",$sql));
    foreach($ar as $v)
    if(! preg_match("/^#|^$/",$v)) $this->query($v);
      }
      function insert($ar=array()) {
    // 产生一笔空记录
    $sql = "select * from $this->tbl_name  where 1=0";
    $rs = $this->conn->Execute($sql); // 用一个空阵列来装要更新的资料
    $r = $this->fields;
    settype($r,'array');
    foreach($ar as $k=>$v)
    $r[$k] = $v;
    unset($r['id']);
    // 用 GetInsertSQL 方法来制作一个完整的 sql 命令
    $insertSQL = $this->conn->GetInsertSQL($rs, $r); // 执行插入
    $this->conn->Execute($insertSQL);
    $this->insert_id = $this->conn->Insert_ID();
      }
      function update($ar=array()) {
    $r = $this->fields;
    settype($r,'array');
    unset($r['id']);
    foreach($ar as $k=>$v)
    $r[$k] = $v;
    // 用 GetInsertSQL 方法来制作一个完整的 sql 命令
    $insertSQL = $this->conn->GetUpdateSQL($this->result, $r); // 执行修改
    $this->conn->Execute($insertSQL);
      }
      function html() {
        echo rs2html($this->result);
      }
    }
    ?>
      

  9.   

    <?php
    /**
     * 树形数据结构操作类
     * 与一般的树形类不同,也与我以前发布的树形类不同。
     * 这个类将精力全部放到建树的过程中,取出时只需简单的查询即可,并无任何计算量。
     * 数据组织采用“中值排序法”的数据结构,其原理可在网上找到。
     * 采用动态修改排序键值的算法,避免了“中值排序法”的有限深度的约束
     *
     * 这个类提供以下主要方法:
     * Add 向指定的节点(用id指示,下同)添加一个同级节点
     * AddChild 向指定的节点添加一个子节点
     * AddFirst 向指定的节点所在组的开始处添加一个节点
     * AddLast 向指定的节点所在组的结束处添加一个节点
     * delete 删除指定的节点及其子节点
     * move 移动以指定节点开始的分支(以下简称分支)到目标节点处
     *      1、当目标节点为空时,分支从根开始
     *      2、当目标节点与分支同属于一个根节点时,分支插入到目标前(即挤占目标节点的原位置)
     *      3、当目标节点与分支不属于同一根节点时,分支做为目标节点的第一子节点
     * 这个类同样适合用于bbs,类中已预制置查询语句。只需设置属性mode等于BBS即可。
     * 当用于bbs是,就只需要用到Add 加贴、AddChild 回复、delete 删贴,这几个方法。
     *
     * 可容纳的记录数和树的深度完全取决于排序字段(ordernum)的数据类型和操作系统的最大文件尺寸的约束。理论上是无限级别的。
     *
     **/
    class Tree_cortrol extends DB {
      var $mode = 'TREE'; //工作模式,当做为论坛控制时请改为 BBS
      var $increment = 4; //排序基数的2的幂指数,计算在构造函数中进行
      var $db_name = 'test'; //**** 数据库名,请填写真实库名。或在$_CONFIG数组中定义,下同
      var $tbl_name = 'tree1'; //**** 数据表名,请填写真实表名
      var $default_sql = array(
    'BBS' => 'select * from $this->tbl_name order by root_id+id-sign(root_id)*id desc,ordernum',
    'TREE' => 'select * from $this->tbl_name order by ordernum'
    );
      /**
       * 构造函数
       **/
      function Tree_cortrol($param='') {
    $this->DB($param);
    $this->increment = pow(2,$this->increment);
    global $_CONFIG;
    if(is_array($_CONFIG))
    foreach($_CONFIG as $k=>$v)
    $this->$k = $v;
      }
      function get_default_sql() {
    $v = $this->default_sql[$this->mode];
    return eval("return \"$v\";");
      }
      /**
       * 内部方法
       * 计算插值,并实现插入时的动态调整排序键
       **/
      function _epenthesis($a,$b) {
    if($a < $b) list($a,$b) = array($b,$a);
    if($a - $b < 2) {
    $this->_push($this->increment,$a);
    $a += $this->increment;
    }
    return floor(($a+$b)/2);
      }
      /**
       * 内部方法
       * 向后调整排序键
       **/
      function _push($dx,$expr) {
        $this->query("update $this->tbl_name set ordernum=ordernum+$dx where ordernum>=$expr");
      }
      /**
       * 内部方法
       * 完成所有的插入算法
       **/
      function _additem($id,$deep,$data) {
    if($id == 0) {
    $this->query("select max(ordernum) as maxnum from $this->tbl_name");
    if($this->fields->maxnum == '') $this->ordernum = 0;
    else $this->ordernum = $this->fields->maxnum + $this->increment;
    $this->root_id = 0;
    $this->deep = 0;
    return $this->_insert($data);
    }
    $this->_locate("id=$id") or die("没有此节点:$id");
    switch($deep) {
    case 0:
    $this->_group();
    break;
    case 1:
    if($this->root_id == 0) $this->root_id = $this->id;
    $this->deep += $deep;
    $this->_locate("ordernum>$this->ordernum");
    break;
    case 2:
    $this->_locate("root_id=$this->root_id and deep=$this->deep and ordernum<=$this->ordernum");
    $this->ordernum = $this->fields->ordernum;
    $this->_locate("ordernum<=$this->ordernum",'desc');
    break;
    case 3:
    $this->_group(1);
    break;
    default:
    return 0;
    }
    //计算插值
    $this->ordernum = $this->_epenthesis($this->fields->ordernum,$this->ordernum);
    return $this->_insert($data);
      }
      /**
       * 内部方法
       * 获取组信息:
       * $type = 1 取得当前节点的子节点,定位到下一个组节点处
       * $type = 2 定位到组结束处
       **/
      function _group($type=0) {
    $id = array();
    if($this->root_id == 0)
    $this->_locate("root_id=$this->id and ordernum>$this->ordernum");
    else
    $this->_locate("root_id=$this->root_id and ordernum>$this->ordernum");
    while(! $this->eof) {
    if($type == 0 && $this->fields->deep <= $this->deep) return $id;
    if($type == 1 && $this->fields->deep < $this->deep) return $id;
    $id[] = $this->fields->id;
    $this->ordernum = $this->fields->ordernum;
    $this->next();
    }
    $this->_locate("ordernum>$this->ordernum");
    return $id;
      }
      /**
       * 内部方法
       * 执行特定条件查询
       **/
      function _locate($expr,$ext='') {
    $this->query("select id,root_id,deep,ordernum from $this->tbl_name where $expr order by ordernum $ext");
    //如果是id定位,则设置公共信息
    if(! $this->eof && preg_match("/^id=\d+$/",$expr)) {
    $this->id = $this->fields->id;
    $this->root_id = $this->fields->root_id;
    $this->deep = $this->fields->deep;
    $this->ordernum = $this->fields->ordernum;
    }
    if($this->eof && preg_match("/^ordernum>\d+$/",$expr))
    $this->fields->ordernum = $this->ordernum + $this->increment;
    return ! $this->eof;
      }
      /**
       * 内部方法
       * 插入数据到数据表,返回插入点的id
       * 根据参数执行用户扩展的插入动作
       **/
      function _insert($data) {
    $data['root_id'] = $this->root_id;
    $data['deep'] = $this->deep;
    $data['ordernum'] = $this->ordernum;
    $this->insert($data);
    return $this->insert_id;
      }
      

  10.   


      /**
       * 公共方法
       * 修改置顶的节点内容
       * 参数$data的格式为用逗号分开的赋值表达式
       **/
      function update($id,$data) {
    if(! empty($data))
    $this->query("update $this->tbl_name set $data where id=$id");
      }
      /**
       * 公共方法
       * 在指定填加节点的后面插入节点
       **/
      function Add($id,$data='') {
    return $this->_additem($id,0,$data);
      }
      /**
       * 公共方法
       * 向指定节点插入子节点
       **/
      function AddChild($id,$data='') {
    return $this->_additem($id,1,$data);
      }
      /**
       * 公共方法
       * 插入节点到组开始处
       */
      function AddFirst($id,$data='') {
    return $this->_additem($id,2,$data);
      }
      /**
       * 公共方法
       * 插入节点到组结束处
       **/
      function AddLast($id,$data='') {
    return $this->_additem($id,3,$data);
      }
      /**
       * 公共方法
       * 删除分支
       * 删除节点$id及其子节点
       **/
      function delete($id) {
    $this->_locate("id=$id");
    $ar = $this->_group();
    array_unshift($ar,$id);
    $this->query("delete from $this->tbl_name where id in (".join(',',$ar).")");
      }
      /**
       * 公共方法
       * 移动分支
       * 将节点$ids及其子节点移动到节点$idt处
       * 1、当节点$ids和节点$idt具有同一根节点时
       *    节点$ids插入到节点$idt之前
       * 2、当节点$ids和节点$idt不具有同一根节点时
       *    节点$ids作为节点$idt的第一子节点
       * 3、当$idt为0时,表示将节点$ids作为顶级节点,并放在最后
       **/
      function move($ids,$idt=0) {
    //读取节点$ids的信息
    $this->_locate("id=$ids");
    //缓存源节点参数
    $root_id = $this->root_id;
    $deep = $this->deep;
    $ordernum_s = $this->ordernum; //起始的排序键值
    //获得子节点id列表
    $ar = $this->_group();
    $idlist = join(',',$ar);
    $ordernum_e = $this->ordernum; //结束的排序键值 if($idt == 0) {
    $this->query("select max(ordernum) as maxnum from $this->tbl_name");
    $maxnum = $this->fields->maxnum;
    $this->query("update $this->tbl_name set root_id=0, deep=0, ordernum=ordernum+$maxnum where id=$ids");
    $this->query("update $this->tbl_name set root_id=$ids, deep=deep-$this->deep, ordernum=ordernum+$maxnum where id in ($idlist)");
    return;
    } //读取节点$idt的信息
    array_unshift($ar,$ids);
    $idlist = join(',',$ar);
    $this->_locate("id=$idt");
    if($ordernum_s <= $this->ordernum && $this->ordernum <= $ordernum_e)
    die("不能移动到分支本身 $ordernum_s $this->ordernum $ordernum_e");
    $dx = $ordernum_e - $ordernum_s + $this->increment;
    if($this->root_id == $root_id) {
    $this->_push($dx,$this->ordernum); //下推目标节点的后续节点
    $deep = $this->deep - $deep;
    if($this->ordernum > $ordernum_e)
    $dx = $this->ordernum - $ordernum_s; //目的地在源分支之后
    else
    $dx = $this->ordernum - $ordernum_s - $dx; //目的地在源分支之前 //安置源分支
    $this->query("update $this->tbl_name set ordernum=$dx+ordernum, deep=$deep+deep where id in ($idlist)");
    }else {
    $this->_locate("ordernum>$this->ordernum");
    $num = $this->fields->ordernum;
    $this->_push($dx,$num); //下推目标节点的后续节点
    $deep = $this->deep + 1 - $deep;
    if($this->ordernum > $ordernum_e)
    $dx = $num - $ordernum_s; //目的地在源分支之后
    else
    $dx = $num - $ordernum_s - $dx; //目的地在源分支之前
    $root_id = $this->root_id==0?$this->id:$this->root_id;
    //安置源分支
    $this->query("update $this->tbl_name set root_id=$root_id, ordernum=$dx+ordernum, deep=$deep+deep where id in ($idlist)");
    }
      }
      /**
       * 调试用方法
       * 列表展示数据分布
       **/
      function debug($sql='') {
    if($sql == '') $sql = $this->get_default_sql();
    $this->query($sql);
    if($this->eof) return; echo '<table border>';
    echo '<tr>';
    foreach($this->fields as $key=>$value)
    echo "<th>$key</th>";
    echo "</tr>\n";
    while(!$this->eof) {
    echo '<tr>';
    foreach($this->fields as $key=>$value)
    if($key == 'id')
    echo '<td>'.$this->set_deep($this->fields->id,$this->fields->deep).'</td>';
    else
    echo "<td>$value</td>";
    echo "</tr>\n";
    $this->next();
    }
    echo '</table>';
      }
      /**
       * 格式化输出
       **/
      function view() {
    $sql = $this->get_default_sql();
    $this->query($sql);
    if($this->eof) return;
    echo '<table>';
    while(!$this->eof) {
    echo '<tr><td>'.$this->set_deep($this->fields->id,$this->fields->deep).'</td></tr>';
    $this->next();
    }
    echo '</table>';
      }
      /**
       * 辅助方法
       **/
      function set_deep($text,$level) {
    if($level == 0) return $text;
    return str_repeat('&'.'nbsp;',$level*2).$text;
      }
    }
    ?>
      

  11.   


      /**
       * 公共方法
       * 修改置顶的节点内容
       * 参数$data的格式为用逗号分开的赋值表达式
       **/
      function update($id,$data) {
    if(! empty($data))
    $this->query("update $this->tbl_name set $data where id=$id");
      }
      /**
       * 公共方法
       * 在指定填加节点的后面插入节点
       **/
      function Add($id,$data='') {
    return $this->_additem($id,0,$data);
      }
      /**
       * 公共方法
       * 向指定节点插入子节点
       **/
      function AddChild($id,$data='') {
    return $this->_additem($id,1,$data);
      }
      /**
       * 公共方法
       * 插入节点到组开始处
       */
      function AddFirst($id,$data='') {
    return $this->_additem($id,2,$data);
      }
      /**
       * 公共方法
       * 插入节点到组结束处
       **/
      function AddLast($id,$data='') {
    return $this->_additem($id,3,$data);
      }
      /**
       * 公共方法
       * 删除分支
       * 删除节点$id及其子节点
       **/
      function delete($id) {
    $this->_locate("id=$id");
    $ar = $this->_group();
    array_unshift($ar,$id);
    $this->query("delete from $this->tbl_name where id in (".join(',',$ar).")");
      }
      /**
       * 公共方法
       * 移动分支
       * 将节点$ids及其子节点移动到节点$idt处
       * 1、当节点$ids和节点$idt具有同一根节点时
       *    节点$ids插入到节点$idt之前
       * 2、当节点$ids和节点$idt不具有同一根节点时
       *    节点$ids作为节点$idt的第一子节点
       * 3、当$idt为0时,表示将节点$ids作为顶级节点,并放在最后
       **/
      function move($ids,$idt=0) {
    //读取节点$ids的信息
    $this->_locate("id=$ids");
    //缓存源节点参数
    $root_id = $this->root_id;
    $deep = $this->deep;
    $ordernum_s = $this->ordernum; //起始的排序键值
    //获得子节点id列表
    $ar = $this->_group();
    $idlist = join(',',$ar);
    $ordernum_e = $this->ordernum; //结束的排序键值 if($idt == 0) {
    $this->query("select max(ordernum) as maxnum from $this->tbl_name");
    $maxnum = $this->fields->maxnum;
    $this->query("update $this->tbl_name set root_id=0, deep=0, ordernum=ordernum+$maxnum where id=$ids");
    $this->query("update $this->tbl_name set root_id=$ids, deep=deep-$this->deep, ordernum=ordernum+$maxnum where id in ($idlist)");
    return;
    } //读取节点$idt的信息
    array_unshift($ar,$ids);
    $idlist = join(',',$ar);
    $this->_locate("id=$idt");
    if($ordernum_s <= $this->ordernum && $this->ordernum <= $ordernum_e)
    die("不能移动到分支本身 $ordernum_s $this->ordernum $ordernum_e");
    $dx = $ordernum_e - $ordernum_s + $this->increment;
    if($this->root_id == $root_id) {
    $this->_push($dx,$this->ordernum); //下推目标节点的后续节点
    $deep = $this->deep - $deep;
    if($this->ordernum > $ordernum_e)
    $dx = $this->ordernum - $ordernum_s; //目的地在源分支之后
    else
    $dx = $this->ordernum - $ordernum_s - $dx; //目的地在源分支之前 //安置源分支
    $this->query("update $this->tbl_name set ordernum=$dx+ordernum, deep=$deep+deep where id in ($idlist)");
    }else {
    $this->_locate("ordernum>$this->ordernum");
    $num = $this->fields->ordernum;
    $this->_push($dx,$num); //下推目标节点的后续节点
    $deep = $this->deep + 1 - $deep;
    if($this->ordernum > $ordernum_e)
    $dx = $num - $ordernum_s; //目的地在源分支之后
    else
    $dx = $num - $ordernum_s - $dx; //目的地在源分支之前
    $root_id = $this->root_id==0?$this->id:$this->root_id;
    //安置源分支
    $this->query("update $this->tbl_name set root_id=$root_id, ordernum=$dx+ordernum, deep=$deep+deep where id in ($idlist)");
    }
      }
      /**
       * 调试用方法
       * 列表展示数据分布
       **/
      function debug($sql='') {
    if($sql == '') $sql = $this->get_default_sql();
    $this->query($sql);
    if($this->eof) return; echo '<table border>';
    echo '<tr>';
    foreach($this->fields as $key=>$value)
    echo "<th>$key</th>";
    echo "</tr>\n";
    while(!$this->eof) {
    echo '<tr>';
    foreach($this->fields as $key=>$value)
    if($key == 'id')
    echo '<td>'.$this->set_deep($this->fields->id,$this->fields->deep).'</td>';
    else
    echo "<td>$value</td>";
    echo "</tr>\n";
    $this->next();
    }
    echo '</table>';
      }
      /**
       * 格式化输出
       **/
      function view() {
    $sql = $this->get_default_sql();
    $this->query($sql);
    if($this->eof) return;
    echo '<table>';
    while(!$this->eof) {
    echo '<tr><td>'.$this->set_deep($this->fields->id,$this->fields->deep).'</td></tr>';
    $this->next();
    }
    echo '</table>';
      }
      /**
       * 辅助方法
       **/
      function set_deep($text,$level) {
    if($level == 0) return $text;
    return str_repeat('&'.'nbsp;',$level*2).$text;
      }
    }
    ?>
      

  12.   

    iceberg,ckong已经解密了。你看看admin下面的setforum.php就知道了。