樹形結構之父子節點查詢

2022年10月27日 20:49:25

1. SQL父子節點查詢

  1. 這裡可以參照我之前關於CTE實現遞歸的用法:T-SQL——公用表表達式(CTE)

  2. 準備測試數據

--建立樹形結構表
CREATE TABLE [dbo].[Region]
(
	[Id] [bigint] IDENTITY(1,1) NOT NULL,
	[PId] [bigint] NOT NULL,
	[Name] [nvarchar](50) NOT NULL,
)

--關閉主鍵自增
SET IDENTITY_INSERT Region ON

INSERT INTO [dbo].[Region] ([Id], [PId], [Name])
VALUES
( 1, 0, N'中國' ), 
( 2, 1, N'江蘇省' ), 
( 3, 2, N'蘇州市' ), 
( 4, 3, N'吳中區' ), 
( 5, 1, N'山東省' ), 
( 6, 5, N'濟南市' ), 
( 7, 5, N'青島市' ), 
( 8, 5, N'煙台市' ), 
( 9, 2, N'南京市' ), 
( 11, 9, N'玄武區' )

--批量插入完成 打開主鍵自增
SET IDENTITY_INSERT [dbo].[Region] OFF
  1. 父查子
    查詢某個區域的下級區域
    比如說,這裡查詢表中所有江蘇省以下的行政區域
WITH temp AS (SELECT * FROM Company WHERE Id=2 --江蘇省的Id是2,所以遞歸初始值就是2
              UNION ALL
              SELECT c.* FROM temp, Company AS c WHERE temp.Id=c.PId)
SELECT * FROM temp;

結果:

Id                   PId                  Name
-------------------- -------------------- --------------------------------------------------
2                    1                    江蘇省
3                    2                    蘇州市
9                    2                    南京市
11                   9                    玄武區
4                    3                    吳中區

  1. 子查父
    查詢某個區域的上級區域
    比如說,這裡查詢吳中區的上級行政區域
WITH temp AS (SELECT * FROM Region WHERE Id=4 --吳中區Id
              UNION ALL
              SELECT c.* FROM temp, dbo.Region AS c WHERE temp.PId=c.Id)
SELECT * FROM temp;

結果

Id                   PId                  Name
-------------------- -------------------- --------------------------------------------------
4                    3                    吳中區
3                    2                    蘇州市
2                    1                    江蘇省
1                    0                    中國


2. DataTable對象中查找父子節點

  1. 準備測試數據
//模擬數據
private  DataTable GetDtRegion()
{
    //建表
    DataTable dtRegion = new DataTable("Region");
    //建列
    DataColumn dcId = new DataColumn("Id", typeof(int));
    DataColumn dcPId = new DataColumn("PId", typeof(int));
    DataColumn dcName = new DataColumn("Name", typeof(string));
    DataColumn[] aryDc = { dcId, dcPId, dcName };
    dtRegion.Columns.AddRange(aryDc);
    //設置主鍵
    //dcId.AllowDBNull = false;
    //dtRegion.PrimaryKey = new DataColumn[] { dcId };
    dtRegion.Rows.Add(new object[] { "1", "0", "中國" });
    dtRegion.Rows.Add(new object[] { "2", "1", "江蘇省" });
    dtRegion.Rows.Add(new object[] { "3", "2", "蘇州市" });
    dtRegion.Rows.Add(new object[] { "4", "3", "吳中區" });
    dtRegion.Rows.Add(new object[] { "5", "3", "山東省" });
    dtRegion.Rows.Add(new object[] { "6", "5", "濟南市" });
    dtRegion.Rows.Add(new object[] { "7", "5", "青島市" });
    dtRegion.Rows.Add(new object[] { "8", "3", "煙台市" });
    dtRegion.Rows.Add(new object[] { "9", "2", "南京市" });
    dtRegion.Rows.Add(new object[] { "11", "9", "玄武區" });
    return dtRegion;
}
  1. 測試

public void Test()
{
    DataTable dtRegion = GetDtRegion();
    DataSet ds = new DataSet();
    ds.Tables.Add(dtRegion);

    //DataSet的Relations屬性主要是用於建立主子表關係,這裡我們將一張樹形結構的表建立自連接
    ds.Relations.Add("TreeRelation", ds.Tables[0].Columns["Id"], ds.Tables[0].Columns["PId"], false);

    //獲取指定的節點的所有下一級子節點(注意根據我們的關聯關係,我們只能查找到一個節點的所有二級節點)
    DataRow[] drSons = dtRegion.Select("Name='中國'")[0].GetChildRows("TreeRelation");
    DataRow[] drParents = dtRegion.Select("Name='越溪街道'")[0].GetParentRows("TreeRelation");

    //使用遞歸獲取江蘇省節點下的所有子節點
    List<DataRow> listDr = GetSons(dtRegion.Select("Name='江蘇省'")[0]);
    List<DataRow> listDra = GetParents(dtRegion.Select("Name='越溪街道'")[0]);

    listDr.ForEach(n => Console.WriteLine($"Id:{n["Id"]},PId:{n["PId"]},Name:{n["Name"]}"));
    Console.WriteLine("---------------");
    listDra.ForEach(n => Console.WriteLine($"Id:{n["Id"]},PId:{n["PId"]},Name:{n["Name"]}"));
}


//廣度遍歷:獲取指定的父節點的所有層級的子節點
public static List<DataRow> GetSons(DataRow dr)
{
    List<DataRow> drSons = dr.GetChildRows("TreeRelation").ToList();
    List<DataRow> result = new List<DataRow>(drSons);
    foreach (DataRow row in drSons)
    {
        result.AddRange(GetSons(row));
    }
    return result;
}
//獲取指定的節點的所有上級父節點
public static List<DataRow> GetParents(DataRow dr)
{
    List<DataRow> drParents = dr.GetParentRows("TreeRelation").ToList();
    List<DataRow> result = new List<DataRow>(drParents);
    foreach (DataRow row in drParents)
    {
        result.AddRange(GetParents(row));
    }
    return result;
}


3. Linq父子節點查詢

  1. 準備測試數據
//樹形表實體對象
public class Region
{
    public int Id { get; set; }
    public int PId { get; set; }
    public string Name { get; set; }
}

//模擬數據源
public static List<Region> GetListRegion()
{
    List<Region> listRegion = new List<Region>()
    {
        new Region (){Id=1, PId=0,Name="中國"    },
        new Region (){Id=2, PId=1,Name="江蘇省"  },
        new Region (){Id=3, PId=2,Name="蘇州市"  },
        new Region (){Id=4, PId=3,Name="吳中區"  },
        new Region (){Id=5, PId=1,Name="山東省"  },
        new Region (){Id=6, PId=5,Name="濟南市"  },
        new Region (){Id=7, PId=5,Name="青島市"  },
        new Region (){Id=8, PId=5,Name="煙台市"  },
        new Region (){Id=9, PId=2,Name="南京市"  },
        new Region (){Id=11,PId=9,Name="玄武區"  },
        new Region (){Id=12,PId=4,Name="越溪街道"},
        new Region (){Id=13,PId=4,Name="橫涇街道"}
    };
    return listRegion;
}
  1. 遞歸查詢

public void Test()
{
    //使用Linq實現
    List<Region> regions = GetSons(GetListRegion(), 5).ToList();
    regions.ForEach(n => Console.WriteLine($"Id:{n.Id},PId:{n.PId},Name:{n.Name}"));
}

// 獲取指定的節點其所有的子節點(包含指定節點本身)
public static IEnumerable<Region> GetSonsWithSelf(IEnumerable<Region> list, int pId)
{
    var query = list.Where(p => p.Id == pId).ToList();
    return query.Concat(GetSons(list, pId));
}

//獲取指定的節點其所有的子節點
public static IEnumerable<Region> GetSons(IEnumerable<Region> list, int pId)
{
    var query = list.Where(p => p.PId == pId).ToList();
    return query.ToList().Concat(query.ToList().SelectMany(t => GetSons(list, t.Id)));
}

//獲取父節點
public static IEnumerable<Region> GetFatherList(IEnumerable<Region> list, int Id)
{
    var query = list.Where(p => p.Id == Id).ToList();
    return query.ToList().Concat(query.ToList().SelectMany(t => GetFatherList(list, t.PId)));
}


3. 參加及源程式碼下載