数据库树型结构的存储和展示

数据库擅长操作的数据是无层次的数据,根据不同的sql语句提取特定的行运算。对于树型结构的存储第一次应用是做省市县三级列表。当时仅仅使用了三个字段id,pid,name。pid表示name的所属的父id。然后可以在服务器端组织好数据结构给客户端,也可以让客户端自行处理。但是现在需要做一个背单词的联想功能,层数是不确定的。根据一个单词需要把它的所有的子节点全部查出来,并且以树的形式展示出来,这就不能够用上面的存储方式,首先是因为上面的存储会导致数据库递归查找,效率不高。第二就是想要获取关于某个节点的子树的任何信息,不进行遍历是不能达到效果的,也是查询次数过多,效率问题。

https://www.sitepoint.com/hierarchical-data-database/,这篇文章详细介绍了数据库存储树形结构的两种方式,第一种就是上面介绍的哪种,另一种是一种新的存储方式。这种新的存储方式极大的简化了数据库的查询次数。

树.png
数据库数据.png

查询food的所有的节点

SELECT * FROM tree WHERE lft BETWEEN 1 AND 18;

更改Food的名字,直接更改就行

UPDATE tree set name = 'Foods' where name = 'Food'

在Red下加入新的子节点

UPDATE tree SET rgt=rgt+2 WHERE rgt>5;   
UPDATE tree SET lft=lft+2 WHERE lft>5;
INSERT INTO tree SET lft=6, rgt=7, name='Strawberry';

删除草莓

DELETE FROM tree where name = 'Strawberry'
UPDATE tree SET rgt=rgt-2 WHERE rgt>7;   
UPDATE tree SET lft=lft-2 WHERE lft>7;
image.png

操作很简单了,接下来是上面前端的展示,最简单的就是按照层次打印,如图

image.png

但是这样显示效果不好,所以网上搜索了下树的展示。参考这里https://jsfiddle.net/Limitlessisa/5Lhb0ron/

发现这里是根据css设置的,而且每一个节点都是独立的。所以可以参考这里把树按照这样的方式展示出来。

单词树编辑.gif

实现代码基于TP

 public function treeEdit(Request $request)
    {
        $action_type = $request->param('action_type', '');
        $root_name = $request->param('root_name', '');
        if (empty($root_name) && $action_type != 'add_root_node'){
            return json(['status' => -1,'msg' => '没有根节点名称']);
        }
        if ($action_type == 'add_root_node') { //添加根节点
            $new_name = $request->param('root_name');
            Db::table('h_dict_tree')->insert(['name' => $new_name, 'root' => $new_name, 'parent' => '','lft' => 1,'rgt' => 2]);
            return json(['status' => 0,'msg' => '添加根节点成功']);
        } elseif ($action_type == 'add_child_node' || $action_type == 'edit_node_name' || $action_type == 'add_brother_node') { //添加子节点
            $origin_name = $request->param('origin_name', '');
            if (empty($origin_name)) {
                return json(['status' => -1,'msg' => '请选择节点']);
            }
            $new_name = $request->param('new_name','');
            if (empty($new_name)) {
                return json(['status' => -1, '请输入新名称']);
            }
            $exist_nodes = Db::table('h_dict_tree')->where('root', $root_name)->column('name');
            if (in_array($new_name, $exist_nodes)) {
                return json(['status' => -1,'msg' => '节点已存在']);
            }
            if ($action_type == 'add_child_node'){
                $parent_node = Db::table('h_dict_tree')->where(['root' => $root_name, 'name' => $origin_name])->find();
                Db::table('h_dict_tree')->where(['rgt' => ['gt', $parent_node['rgt']-1],'root' => $root_name])->update(['rgt' => array('exp', 'rgt+2')]);
                Db::table('h_dict_tree')->where(['lft' => ['gt', $parent_node['rgt']-1],'root' => $root_name])->update(['lft' => array('exp', 'lft+2')]);
                Db::table('h_dict_tree')->insert(['name' => $new_name, 'lft' => $parent_node['rgt'],'rgt' => $parent_node['rgt']+1, 'root' => $root_name, 'parent' => $origin_name]);
                return json(['status' => 0,'msg' => '添加子节点成功']);
            }else if($action_type == 'edit_node_name'){
                Db::table('h_dict_tree')->where(['name' => $origin_name, 'root' => $root_name])->update(['name' => $new_name]);
                Db::table('h_dict_tree')->where(['parent' => $origin_name, 'root' => $root_name])->update(['parent' => $new_name]);
                if ($root_name == $origin_name){
                    Db::table('h_dict_tree')->where(['root' => $root_name])->update(['root' => $new_name]);
                }
                return json(['status' => 0,'msg' => '更改节点名成功']);
            }else {
                if ($origin_name == $root_name) {
                    return json(['status' =>-1,'msg' => '根节点不能添加兄弟节点']);
                }
                $add_pos = $request->param('add_pos','left');
                $brother_node = Db::table('h_dict_tree')->where(['root' => $root_name, 'name' => $origin_name])->find();
                if ($add_pos == 'right') {
                    Db::table('h_dict_tree')->where(['lft' => ['gt', $brother_node['rgt']],'root' => $root_name])->update(['lft' => array('exp', 'lft+2')]);
                    Db::table('h_dict_tree')->where(['rgt' => ['gt', $brother_node['rgt']],'root' => $root_name])->update(['rgt' => array('exp', 'rgt+2')]);
                    Db::table('h_dict_tree')->insert(['name' => $new_name, 'lft' => $brother_node['rgt']+1,'rgt' => $brother_node['rgt']+2, 'root' => $root_name, 'parent' => $brother_node['parent']]);
                }elseif ($add_pos == 'left') {
                    Db::table('h_dict_tree')->where(['lft' => ['gt', $brother_node['lft']-1],'root' => $root_name])->update(['lft' => array('exp', 'lft+2')]);
                    Db::table('h_dict_tree')->where(['rgt' => ['gt', $brother_node['lft']-1],'root' => $root_name])->update(['rgt' => array('exp', 'rgt+2')]);
                    Db::table('h_dict_tree')->insert(['name' => $new_name, 'lft' => $brother_node['lft'],'rgt' => $brother_node['lft']+1, 'root' => $root_name, 'parent' => $brother_node['parent']]);
                }
                return json(['status' => 0, '加入成功']);
            }

        } elseif ($action_type == 'delete_node') { //删除节点
            $del_name = $request->param('origin_name','');
            $count = Db::table('h_dict_tree')->where(['parent' => $del_name, 'root' => $root_name])->count('*');
            if ($count == 0){
                $data = Db::table('h_dict_tree')->where(['name'=> $del_name,'root' => $root_name])->find();
                Db::table('h_dict_tree')->where(['lft' => ['gt',$data['rgt']],'root' => $root_name])->update(['lft' => array('exp', 'lft-2')]);
                Db::table('h_dict_tree')->where(['rgt' => ['gt',$data['rgt']],'root' => $root_name])->update(['rgt' => array('exp', 'rgt-2')]);
                Db::table('h_dict_tree')->where(['name' => $data['name'], 'root' => $root_name])->delete();
                return json(['status' => 0,'msg' => '删除节点成功']);
            }else{
                return json(['status' => -1,'msg' => '不能删除非叶子节点']);
            }
        }
    }

{extend name='include/head'/}
{block name='title'}单词树{/block}
{block name="menu"}添加单词{/block}

{block name='content'}

  


{$tree_div}
{if $has_root == 1}
已选择
节点内容
操作
{else/}
根节点名
{/if} {/block}

你可能感兴趣的:(数据库树型结构的存储和展示)