项目需求用到了邻接模型,但是是采用开源的Mysql,而Mysql没有这个功能,Oracle数据库提供了现在的分析方法 connect by 处理邻接模型,不过mysql支持存储过程,可以建立存储过程实现Oracle的分析功能.
数据库对层次结构的处理模型有好多种,可以根据自己的需求来设计模型,当然最简单的也是最容易设计的模型就是所谓的邻接模型,在这方面,其他数据库比如Oracle 提供了现成的分析方法 connect by,而MySQL在这方面就显得有些薄弱了,不过可以用MySQL的存储过程实现ORACLE类似的分析功能.
这样,先来创建一个简单的数表,代码如下:
- createtablecountry(idnumber(2)notnull,namevarchar(60)notnull);
- createtablecountry_relation(idnumber(2),parentidnumber(2));
- createtablecountry_relation(idnumber(2),parentidnumber(2));
插入一些数据,代码如下:
- --Tablecountry.
- insertintocountry(id,name)values(0,'Earth');
- insertintocountry(id,name)values(2,'NorthAmerica');
- insertintocountry(id,name)values(3,'SouthAmerica');
- insertintocountry(id,name)values(4,'Europe');
- insertintocountry(id,name)values(5,'Asia');
- insertintocountry(id,name)values(6,'Africa');
- insertintocountry(id,name)values(7,'Australia');
- insertintocountry(id,name)values(8,'Canada');
- insertintocountry(id,name)values(9,'CentralAmerica');
- insertintocountry(id,name)values(10,'IslandNations');
- insertintocountry(id,name)values(11,'UnitedStates');
- insertintocountry(id,name)values(12,'Alabama');
- insertintocountry(id,name)values(13,'Alaska');
- insertintocountry(id,name)values(14,'Arizona');
- insertintocountry(id,name)values(15,'Arkansas');
- insertintocountry(id,name)values(16,'California');
- --Tablecountry_relation.
- insertintocountry_relation(id,parentid)values(0,NULL);
- insertintocountry_relation(id,parentid)values(2,0);
- insertintocountry_relation(id,parentid)values(3,0);
- insertintocountry_relation(id,parentid)values(4,0);
- insertintocountry_relation(id,parentid)values(5,0);
- insertintocountry_relation(id,parentid)values(6,0);
- insertintocountry_relation(id,parentid)values(7,0);
- insertintocountry_relation(id,parentid)values(8,2);
- insertintocountry_relation(id,parentid)values(9,2);
- insertintocountry_relation(id,parentid)values(10,2);
- insertintocountry_relation(id,parentid)values(11,2);
- insertintocountry_relation(id,parentid)values(12,11);
- insertintocountry_relation(id,parentid)values(13,11);
- insertintocountry_relation(id,parentid)values(14,11);
- insertintocountry_relation(id,parentid)values(15,11);
- insertintocountry_relation(id,parentid)values(16,11);
在Oracle 里面,对这些操作就比较简单了,都是系统提供的,比如下面四种情形.
1).查看深度,代码如下:
- selectmax(level)"level"fromCOUNTRY_RELATIONastartwitha.parentidisNULL
- connectbyPRIORa.id=a.PARENTID
- orderbylevel;
- level
- ----------
- 4
- --已用时间:00:00:00.03
2).查看叶子节点,代码如下:
- selectnamefrom
- (
- selectb.name,connect_by_isleaf"isleaf"
- fromCOUNTRY_RELATIONainnerjoincountrybon(a.id=b.id)
- startwitha.parentidisNULLconnectbypriora.id=a.PARENTID
- )TwhereT."isleaf"=1;
- NAME
- --------------------------------------------------
- Canada
- CentralAmerica
- IslandNations
- Alabama
- Alaska
- Arizona
- Arkansas
- California
- SouthAmerica
- Europe
- Asia
- Africa
- Australia
- --已选择13行。
- --已用时间:00:00:00.01
3).查看ROOT节点,代码如下:
- selectconnect_by_rootb.name
- fromCOUNTRY_RELATIONainnerjoincountrybon(a.id=b.id)
- startwitha.parentidisNULLconnectbya.id=a.PARENTID
- --phpfensi.com
- CONNECT_BY_ROOTB.NAME
- --------------------------------------------------
- Earth
- --已用时间:00:00:00.01
4).查看路径,代码如下:
- selectsys_connect_by_path(b.name,'/')"path"
- fromCOUNTRY_RELATIONainnerjoincountrybon(a.id=b.id)
- startwitha.parentidisNULLconnectbypriora.id=a.PARENTID
- orderbylevel,a.id;
- path
- --------------------------------------------------
- /Earth
- /Earth/NorthAmerica
- /Earth/SouthAmerica
- /Earth/Europe
- /Earth/Asia
- /Earth/Africa
- /Earth/Australia
- /Earth/NorthAmerica/Canada
- /Earth/NorthAmerica/CentralAmerica
- /Earth/NorthAmerica/IslandNations
- /Earth/NorthAmerica/UnitedStates
- /Earth/NorthAmerica/UnitedStates/Alabama
- /Earth/NorthAmerica/UnitedStates/Alaska
- /Earth/NorthAmerica/UnitedStates/Arizona
- /Earth/NorthAmerica/UnitedStates/Arkansas
- /Earth/NorthAmerica/UnitedStates/California
- --已选择16行。
- --已用时间:00:00:00.01
接下来我们看看在MySQL 里面如何实现上面四种情形,前三种都比较简单,可以很容易写出SQL.
1)查看深度,代码如下:
- mysql>SELECTCOUNT(DISTINCTIFNULL(parentid,-1))ASLEVELFROMcountry_relation
- ;
- +-------+
- |LEVEL|
- +-------+
- |4|
- +-------+
- 1rowinset(0.00sec)
2)查看ROOT节点,代码如下:
- mysql>SELECTb.`name`ASroot_nodeFROM
- ->(
- ->SELECTidFROMcountry_relationWHEREparentidISNULL
- ->)ASa,countryASbWHEREa.id=b.id;
- +-----------+
- |root_node|
- +-----------+
- |Earth|
- +-----------+
- 1rowinset(0.00sec)
3).查看叶子节点,代码如下:
- mysql>SELECTb.`name`ASleaf_nodeFROM
- ->(
- ->SELECTidFROMcountry_relationWHEREidNOTIN(SELECTIFNULL(parentid,
- -1)FROMcountry_relation)
- ->)ASa,countryASbWHEREa.id=b.id;
- +-----------------+
- |leaf_node|
- +-----------------+
- |SouthAmerica|
- |Europe|
- |Asia|
- |Africa|
- |Australia|
- |Canada|
- |CentralAmerica|
- |IslandNations|
- |Alabama|
- |Alaska|
- |Arizona|
- |Arkansas|
- |California|
- +-----------------+
- 13rowsinset(0.00sec)
- mysql>
4)查看路径
这一块没有简单的SQL实现,不过可以用MySQL的存储过程来实现同样的功能,存储过程代码如下:
- DELIMITER$$
- USE`t_girl`$$
- DROPPROCEDUREIFEXISTS`sp_show_list`$$
- CREATEDEFINER=`root`@`localhost`PROCEDURE`sp_show_list`()
- BEGIN
- --Createdbyytt2014/11/04.
- --Isequaltooracle'sconnectbysyntax.
- --Body.
- DROPTABLEIFEXISTStmp_country_list;
- CREATETEMPORARYTABLEtmp_country_list(node_levelINTUNSIGNEDNOTNULL,node_pathVARCHAR(1000)NOTNULL);
- --Gettherootnode.
- INSERTINTOtmp_country_listSELECT1,CONCAT('/',id)FROMcountry_relationWHEREparentidISNULL;
- --Loopwithinallparentnode.
- cursor1:BEGIN
- DECLAREdone1INTDEFAULT0;
- DECLAREi1INTDEFAULT1;
- DECLAREv_parentidINTDEFAULT-1;
- DECLAREv_node_pathVARCHAR(1000)DEFAULT'';
- DECLAREcr1CURSORFORSELECTparentidFROMcountry_relationWHEREparentidISNOTNULLGROUPBYparentidORDERBYparentidASC;
- DECLARECONTINUEHANDLERFORNOTFOUNDSETdone1=1;
- OPENcr1;
- loop1:LOOP
- FETCHcr1INTOv_parentid;
- IFdone1=1THEN
- LEAVEloop1;
- ENDIF;
- SETi1=i1+1;
- label_path:BEGIN
- DECLAREdone2INTDEFAULT0;
- DECLARECONTINUEHANDLERFORNOTFOUNDSETdone2=1;
- --Gettheupperpath.
- SELECTnode_pathFROMtmp_country_listWHEREnode_level=i1-1ANDLOCATE(v_parentid,node_path)>0INTOv_node_path;
- --Escapetheouternotfoundexception.
- IFdone2=1THEN
- SETdone2=0;
- ENDIF;
- INSERTINTOtmp_country_list
- SELECTi1,CONCAT(IFNULL(v_node_path,''),'/',id)FROMcountry_relationWHEREparentid=v_parentid;
- END;
- ENDLOOP;
- CLOSEcr1;
- END;
- --Updatenode'sidtoitsrealname.
- update_name_label:BEGIN
- DECLAREcntINTDEFAULT0;
- DECLAREi2INTDEFAULT0;
- SELECTMAX(node_level)FROMtmp_country_listINTOcnt;
- WHILEi2<cnt
- DO
- UPDATEtmp_country_listASa,countryASb
- SETa.node_path=REPLACE(a.node_path,CONCAT('/',b.id),CONCAT('/',b.name))
- WHERELOCATE(CONCAT('/',b.id),a.node_path)>0;
- SETi2=i2+1;
- ENDWHILE;
- END;
- SELECTnode_pathFROMtmp_country_list;
- END$$
- DELIMITER;
调用结果,代码如下:
- mysql>CALLsp_show_list();
- +-----------------------------------------------+
- |node_path|
- +-----------------------------------------------+
- |/Earth|
- |/Earth/NorthAmerica|
- |/Earth/SouthAmerica|
- |/Earth/Europe|
- |/Earth/Asia|
- |/Earth/Africa|
- |/Earth/Australia|
- |/Earth/NorthAmerica/Canada|
- |/Earth/NorthAmerica/CentralAmerica|
- |/Earth/NorthAmerica/IslandNations|
- |/Earth/NorthAmerica/UnitedStates|
- |/Earth/NorthAmerica/UnitedStates/Alabama|
- |/Earth/NorthAmerica/UnitedStates/Alaska|
- |/Earth/NorthAmerica/UnitedStates/Arizona|
- |/Earth/NorthAmerica/UnitedStates/Arkansas|
- |/Earth/NorthAmerica/UnitedStates/California|
- +-----------------------------------------------+
- 16rowsinset(0.04sec)
- QueryOK,0rowsaffected(0.08sec)
- mysql>