SQL 数据层次结构 [英] SQL Data Hierarchy

查看:55
本文介绍了SQL 数据层次结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我浏览了一些 SQL 层次结构教程,但没有一个对我的应用程序有意义.也许我只是没有正确理解它们.我正在编写一个 C# ASP.NET 应用程序,我想从 SQL 数据创建一个树视图层次结构.

这是层次结构的工作方式:

<前>SQL 表身份证 |位置 ID |名称_______|__________ |_____________第1331章第1331章房子第1321章第1331章房间第2141章第1321章床第1251章2231 |健身房

如果 ID 和位置 ID 相同,这将确定顶级父级.该父级的任何子级都将具有与父级相同的位置 ID.该子项的任何孙子项的位置 ID 都等于子项的 ID,依此类推.

对于上面的例子:

<前>- 房子- 房间- - 床

对于易于遵循的教程的任何帮助或指导将不胜感激.

到目前为止我拥有的代码,但它只获取父和子,没有孙子.我似乎无法弄清楚如何让它递归获取所有节点.

使用系统;使用 System.Data;使用 System.Collections.Generic;使用 System.Web;使用 System.Web.UI;使用 System.Web.UI.WebControls;使用 System.Configuration;使用 System.Data.SqlClient;命名空间 TreeViewProject{公共部分类 _Default : System.Web.UI.Page{protected void Page_Load(object sender, EventArgs e){PopulateTree(SampleTreeView);}公共无效人口树(控制ctl){//数据连接SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["AssetWhereConnectionString1"].ConnectionString);连接.打开();//SQL 命令string getLocations = "SELECT ID, LocationID, Name FROM dbo.Locations";SqlDataAdapter 适配器 = 新的 SqlDataAdapter(getLocations, connection);数据表位置 = 新数据表();//用 SQL Locations 表填充数据表适配器.填充(位置);//设置行索引DataRow[] myRows;myRows = location.Select();//创建树的实例TreeView t1 = new TreeView();//将树分配给控件t1 = (TreeView)ctl;//清除所有存在的节点t1.Nodes.Clear();//建树!for (int p = 0; p < myRows.Length; p++){//获取父节点if ((Guid)myRows[p]["ID"] == (Guid)myRows[p]["LocationID"]){//创建父节点TreeNode parentNode = new TreeNode();parentNode.Text = (string)myRows[p]["Name"];t1.Nodes.Add(parentNode);//获取子节点for (int c = 0; c 

这是实际 SQL 表中的一个片段:Locations

<前>ID LocationID 姓名____________________________________ ____________________________________ ______________DEAF3FFF-FD33-4ECF-910B-1B07DF192074 48700BC6-D422-4B26-B123-31A7CB704B97降F48700BC6-D422-4B26-B123-31A7CB704B97 7EBDF61C-3425-46DB-A4D5-686E91FD0832 奥尔威06B49351-6D18-4595-8228-356253CF45FF 6E8C65AC-CB22-42DA-89EB-D81C5ED0BBD0降E 5E98BC1F6-4BAE-4022-86A5-43BBEE2BA6CD DEAF3FFF-FD33-4ECF-910B-1B07DF192074 降 F 6F6A2CF99-F708-4C61-8154-4C04A38ADDC6 7EBDF61C-3425-46DB-A4D5-686E91FD0832 普雷0EC89A67-D74A-4A3B-8E03-4E7AAAFEBE51 6E8C65AC-CB22-42DA-89EB-D81C5ED0BBD0降E 435540B7A-62F9-487F-B65B-4EA5F42AD88A 48700BC6-D422-4B26-B123-31A7CB704B97 Olway 故障5000AB9D-EB95-48E3-B5C0-547F5DA06FC6 6E8C65AC-CB22-42DA-89EB-D81C5ED0BBD0 输出 153CDD540-19BC-4BC2-8612-5C0663B7FDA5 6E8C65AC-CB22-42DA-89EB-D81C5ED0BBD0降E 37EBDF61C-3425-46DB-A4D5-686E91FD0821 B46C7305-18B1-4499-9E1C-7B6FDE786CD6 测试17EBDF61C-3425-46DB-A4D5-686E91FD0832 7EBDF61C-3425-46DB-A4D5-686E91FD0832 HMN

谢谢.

解决方案

您正在寻找使用公用表表达式(简称 CTE)的递归查询.SQL Server 2008 中对此的详细说明可以在 在 MSDN 上找到.

一般来说,它们的结构类似于以下内容:

WITH cte_name ( column_name [,...n] )作为 (-  锚CTE_query_definition联合所有--- 递归部分CTE_query_definition)-- 使用 CTE 的语句SELECT * FROM cte_name

执行此操作时,SQL Server 将执行类似于以下操作(从 MSDN 转述为更简单的语言):

  1. 将 CTE 表达式拆分为锚和递归成员.
  2. 运行锚点,创建第一个结果集.
  3. 运行递归部分,将前一步骤作为输入.
  4. 重复第 3 步,直到返回一个空集.
  5. 返回结果集.这是锚点和所有递归步骤的 UNION ALL.

对于此特定示例,请尝试以下操作:

带层级(id, [location id], name, depth)作为 (-- 选择根";级别的项目.选择 ID、[LocationID]、名称、1 作为深度来自 dbo.Locations其中 ID = [位置 ID]联合所有-- 选择后代项目.选择 child.id、child.[LocationID]、child.name、parent.depth + 1 作为深度来自 dbo.Locations 作为孩子内部连接层次结构作为父级在 child.[LocationID] = parent.ID其中 child.ID != parent.[位置 ID])-- 调用上面的表达式.选择 *从层次结构

根据您的示例数据,您应该得到如下信息:

ID |位置 ID |姓名 |深度_______|__________ |______ |_____第1331章第1331章房子 |1第1321章第1331章房间 |2第2141章第1321章床 |3

注意健身房"被排除在外.根据您的示例数据,它的 ID 与其 [位置 ID] 不匹配,因此它不会是根级别的项目.它的位置 ID 2231 未出现在有效父 ID 列表中.


编辑 1:

您已经询问了将其放入 C# 数据结构的问题.在 C# 中有很多很多不同的方式来表示层次结构.这是一个例子,选择它是为了它的简单性.一个真正的代码示例无疑会更广泛.

第一步是定义层次结构中每个节点的样子.除了包含节点中每个数据的属性外,我还包含了 ParentChildren 属性,以及用于 Add 子项和 的方法>得到一个孩子.Get 方法将搜索节点的整个后代轴,而不仅仅是节点自己的子节点.

public class LocationNode {公共位置节点父{获取;放;}公共列表Children = new List();公共 int ID { 获取;放;}公共 int LocationID { 获取;放;}公共字符串名称 { 获取;放;}公共无效添加(位置节点子){child.Parent = 这个;this.Children.Add(child);}公共位置节点获取(int id){LocationNode 结果;foreach (LocationNode child in this.Children) {如果(child.ID == id){返回孩子;}结果 = child.Get(id);如果(结果!= null){返回结果;}}返回空;}}

现在您需要填充树.这里有一个问题:很难以错误的顺序填充树.在添加子节点之前,您确实需要对父节点的引用.如果您必须乱序执行,则可以通过两次传递来缓解问题(一次创建所有节点,然后另一次创建树).然而,在这种情况下,这是不必要的.

如果您采用我上面提供的 SQL 查询并按 depth 列排序,您可以在数学上确定在遇到父节点之前永远不会遇到子节点.因此,您可以一次性完成.

您仍然需要一个节点作为根"节点.你的树.你可以决定这是否是房子"?(来自您的示例),或者它是否是您为此目的而创建的虚构占位符节点.我建议后者.

所以,代码!同样,这是为了简单性和可读性而优化的.您可能希望在生产代码中解决一些性能问题(例如,实际上没有必要经常查找父"节点).我在这里避免了这些优化,因为它们会增加复杂性.

//创建树的根.LocationNode root = new LocationNode();使用 (SqlCommand cmd = new SqlCommand()) {cmd.Connection = conn;//您的连接对象,此处未显示.cmd.CommandText = "以上查询,按 [Depth] 升序排序";cmd.CommandType = CommandType.Text;使用 (SqlDataReader rs = cmd.ExecuteReader()) {而(rs.Read()){int id = rs.GetInt32(0);//ID 列var parent = root.Get(id) ??根;parent.Add(新位置节点{身份证=身份证,LocationID = rs.GetInt32(1),名称 = rs.GetString(2)});}}}

达达!root LocationNode 现在包含您的整个层次结构.顺便说一下,我还没有真正执行过这段代码,所以如果你发现任何明显的问题,请告诉我.


编辑 2

要修复您的示例代码,请进行以下更改:

删除这一行:

//创建树的实例TreeView t1 = new TreeView();

这一行实际上不是问题,但应该将其删除.你在这里的评论是不准确的;您并没有真正为控件分配一棵树.相反,您正在创建一个新的 TreeView,将它分配给 t1,然后立即将一个不同的对象分配给 t1.下一行执行后,您创建的 TreeView 就会丢失.

修正你的 SQL 语句

//SQL 命令string getLocations = "SELECT ID, LocationID, Name FROM dbo.Locations";

用我之前建议的带有 ORDER BY 子句的 SQL 语句替换此 SQL 语句.阅读我之前的编辑,它解释了为什么深度"不可用.很重要:您确实希望按特定顺序添加节点.在拥有父节点之前,您无法添加子节点.

可选地,我认为您不需要 SqlDataAdapter 和 DataTable 的开销.我最初建议的 DataReader 解决方案更简单、更易于使用,并且在资源方面更高效.

此外,大多数 C# SQL 对象都实现了 IDisposable,因此您需要确保正确使用它们.如果某些东西实现了 IDisposable,请确保将它包装在 using 语句中(请参阅我之前的 C# 代码示例).

修复您的造树循环

您只能获得父节点和子节点,因为您有一个用于父节点的循环和一个用于子节点的内部循环.您一定已经知道,您没有获得孙子,因为您没有添加它们的代码.

您可以添加一个内部-内部循环来获取孙子,但显然您是在寻求帮助,因为您已经意识到这样做只会导致疯狂.如果那时你想要曾孙,会发生什么?内-内-内循环?这种技术不可行.

您可能已经想到了这里的递归.这是一个完美的地方,如果你正在处理树状结构,它最终会出现.现在您已经编辑了您的问题,很明显您的问题与 SQL 几乎没有关系.你真正的问题是递归.最终可能会有人提出并为此设计递归解决方案.那将是一个完全有效的,并且可能是更可取的方法.

然而,我的回答已经涵盖了递归部分——它只是将它移到了 SQL 层.因此,我将保留我以前的代码,因为我觉得这是对这个问题的合适的通用答案.根据您的具体情况,您需要再做一些修改.

首先,您不需要我建议的 LocationNode 类.您正在使用 TreeNode 代替,这将正常工作.

其次,TreeView.FindNode 类似于我建议的 LocationNode.Get 方法,除了 FindNode 需要完整路径节点.要使用 FindNode,您必须修改 SQL 以提供此信息.

因此,您的整个 PopulateTree 函数应如下所示:

public void PopulateTree(TreeView t1) {//清除所有存在的节点t1.Nodes.Clear();使用 (SqlConnection 连接 = 新 SqlConnection()) {connection.ConnectionString = "((替换这个字符串))";连接.打开();字符串 getLocations = @"具有层次结构(id,[位置 id],名称,深度,[路径])作为 (选择 ID,[LocationID],名称,1 作为深度,Cast(Null as varChar(max)) As [path]来自 dbo.Locations其中 ID = [位置 ID]联合所有选择 child.id、child.[LocationID]、child.name、parent.depth + 1 作为深度,一片空白(parent.[path] + '/' + Cast(parent.id As varChar(max)),演员 (parent.id As varChar(max))) 作为 [路径]来自 dbo.Locations 作为孩子内部连接层次结构作为父级在 child.[LocationID] = parent.ID其中 child.ID != parent.[位置 ID])选择 *从层次结构Order By [depth] Asc";使用 (SqlCommand cmd = new SqlCommand(getLocations, connection)) {cmd.CommandType = CommandType.Text;使用 (SqlDataReader rs = cmd.ExecuteReader()) {而(rs.Read()){//我猜你这里确实有 GUID,是吧?int id = rs.GetInt32(0);int locationID = rs.GetInt32(1);TreeNode 节点 = new TreeNode();node.Text = rs.GetString(2);node.Value = id.ToString();if (id == locationID) {t1.Nodes.Add(节点);} 别的 {t1.FindNode(rs.GetString(4)).ChildNodes.Add(node);}}}}}}

如果您发现任何其他错误,请告诉我!

I have looked through a few SQL hierarchy tutorials, but none of them made much sense for my application. Perhaps I am just not understanding them correctly. I'm writing a C# ASP.NET application and I would like to create a tree view hierarchy from SQL data.

This is how the hierarchy would work:

SQL TABLE

ID     | Location ID | Name
_______| __________  |_____________
1331   | 1331        | House
1321   | 1331        | Room
2141   | 1321        | Bed
1251   | 2231        | Gym

If the ID and Location ID are the same, this would determine the top Parent. Any Children of that Parent would have the same Location ID as the Parent. Any Grandchildren of that Child would have a Location ID equal to the ID of the Child, and so on.

For the above example:

- House
   -- Room
       --- Bed

Any help or direction to easy to follow tutorials would be greatly appreciated.

EDIT:

Code I have so far, but it only gets the Parent and Children, no GrandChildren. I can't seem to figure out how to get it to recursively get all of the nodes.

using System;
using System.Data;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using System.Data.SqlClient;

namespace TreeViewProject
{
public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        PopulateTree(SampleTreeView);

    }



    public void PopulateTree(Control ctl)
    {

        // Data Connection
        SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["AssetWhereConnectionString1"].ConnectionString);
        connection.Open();

        // SQL Commands
        string getLocations = "SELECT ID, LocationID, Name FROM dbo.Locations";
        SqlDataAdapter adapter = new SqlDataAdapter(getLocations, connection);
        DataTable locations = new DataTable();
        // Fill Data Table with SQL Locations Table
        adapter.Fill(locations);
        // Setup a row index
        DataRow[] myRows;
        myRows = locations.Select();

        // Create an instance of the tree
        TreeView t1 = new TreeView();
        // Assign the tree to the control
        t1 = (TreeView)ctl;
        // Clear any exisiting nodes
        t1.Nodes.Clear();

        // BUILD THE TREE!
        for (int p = 0; p < myRows.Length; p++)
        {
            // Get Parent Node
            if ((Guid)myRows[p]["ID"] == (Guid)myRows[p]["LocationID"])
            {
                // Create Parent Node
                TreeNode parentNode = new TreeNode();
                parentNode.Text = (string)myRows[p]["Name"];
                t1.Nodes.Add(parentNode);

                // Get Child Node
                for (int c = 0; c < myRows.Length; c++)
                {
                    if ((Guid)myRows[p]["LocationID"] == (Guid)myRows[c]["LocationID"] 
                        && (Guid)myRows[p]["LocationID"] != (Guid)myRows[c]["ID"] /* Exclude Parent */)
                    {
                        // Create Child Node
                        TreeNode childNode = new TreeNode();
                        childNode.Text = (string)myRows[c]["Name"];
                        parentNode.ChildNodes.Add(childNode);
                    }
                }
            }
        }
        // ALL DONE BUILDING!

        // Close the Data Connection
        connection.Close();
    }

}
}

Here is a snippit from the actual SQL table: Locations

ID                                      LocationID                              Name
____________________________________    ____________________________________    ______________
DEAF3FFF-FD33-4ECF-910B-1B07DF192074    48700BC6-D422-4B26-B123-31A7CB704B97    Drop F
48700BC6-D422-4B26-B123-31A7CB704B97    7EBDF61C-3425-46DB-A4D5-686E91FD0832    Olway
06B49351-6D18-4595-8228-356253CF45FF    6E8C65AC-CB22-42DA-89EB-D81C5ED0BBD0    Drop E 5
E98BC1F6-4BAE-4022-86A5-43BBEE2BA6CD    DEAF3FFF-FD33-4ECF-910B-1B07DF192074    Drop F 6
F6A2CF99-F708-4C61-8154-4C04A38ADDC6    7EBDF61C-3425-46DB-A4D5-686E91FD0832    Pree
0EC89A67-D74A-4A3B-8E03-4E7AAAFEBE51    6E8C65AC-CB22-42DA-89EB-D81C5ED0BBD0    Drop E 4
35540B7A-62F9-487F-B65B-4EA5F42AD88A    48700BC6-D422-4B26-B123-31A7CB704B97    Olway Breakdown
5000AB9D-EB95-48E3-B5C0-547F5DA06FC6    6E8C65AC-CB22-42DA-89EB-D81C5ED0BBD0    Out 1
53CDD540-19BC-4BC2-8612-5C0663B7FDA5    6E8C65AC-CB22-42DA-89EB-D81C5ED0BBD0    Drop E 3
7EBDF61C-3425-46DB-A4D5-686E91FD0821    B46C7305-18B1-4499-9E1C-7B6FDE786CD6    TEST 1
7EBDF61C-3425-46DB-A4D5-686E91FD0832    7EBDF61C-3425-46DB-A4D5-686E91FD0832    HMN

Thanks.

解决方案

You are looking for a recursive query using a common table expression, or CTE for short. A detailed write-up for this in SQL Server 2008 can be found on MSDN.

In general, they have a structure similar to the following:

WITH cte_name ( column_name [,...n] )
AS (
    –- Anchor
    CTE_query_definition

    UNION ALL

    –- Recursive portion
    CTE_query_definition
)
-- Statement using the CTE
SELECT * FROM cte_name

When this executes, SQL Server will do something similar to the following (paraphrased into simpler language from the MSDN):

  1. Split the CTE expression into anchor and recursive members.
  2. Run the anchor, creating the first result set.
  3. Run the recursive portion, with the prior step as an input.
  4. Repeat step 3 until an empty set is returned.
  5. Return the result set. This is a UNION ALL of the anchor and all recursive steps.

For this specific example, try something like this:

With hierarchy (id, [location id], name, depth)
As (
    -- selects the "root" level items.
    Select ID, [LocationID], Name, 1 As depth
    From dbo.Locations
    Where ID = [LocationID]

    Union All

    -- selects the descendant items.
    Select child.id, child.[LocationID], child.name,
        parent.depth + 1 As depth
    From dbo.Locations As child
    Inner Join hierarchy As parent
        On child.[LocationID] = parent.ID
    Where child.ID != parent.[Location ID])
-- invokes the above expression.
Select *
From hierarchy

Given your example data, you should get something like this:

ID     | Location ID | Name  | Depth
_______| __________  |______ | _____
1331   | 1331        | House |     1
1321   | 1331        | Room  |     2
2141   | 1321        | Bed   |     3

Note that "Gym" is excluded. Based on your sample data, it's ID does not match its [Location ID], so it would not be a root-level item. It's location ID, 2231, does not appear in the list of valid parent IDs.


Edit 1:

You've asked about getting this into a C# data structure. There are many, many different ways to represent a hierarchy in C#. Here is one example, chosen for its simplicity. A real code sample would no doubt be more extensive.

The first step is to define what each node in the hierarchy looks like. Besides containing properties for each datum in the node, I've included Parent and Children properties, plus methods to Add a child and to Get a child. The Get method will search the node's entire descendant axis, not just the node's own children.

public class LocationNode {
    public LocationNode Parent { get; set; }
    public List<LocationNode> Children = new List<LocationNode>();
    
    public int ID { get; set; }
    public int LocationID { get; set; }
    public string Name { get; set; }
    
    public void Add(LocationNode child) {
        child.Parent = this;
        this.Children.Add(child);
    }
    
    public LocationNode Get(int id) {
        LocationNode result;
        foreach (LocationNode child in this.Children) {
            if (child.ID == id) {
                return child;
            }
            result = child.Get(id);
            if (result != null) {
                return result;
            }
        }
        return null;
    }
}

Now you'll want to populate your tree. You have a problem here: it's difficult to populate a tree in the wrong order. Before you add a child node, you really need a reference to the parent node. If you have to do it out of order, you can mitigate the problem by making two passes (one to create all the nodes, then another to create the tree). However, in this case, that is unnecessay.

If you take the SQL query I provided above and order by the depth column, you can be mathematically certain that you will never encounter a child node before you encounter its parent node. Therefore, you can do this in one pass.

You will still need a node to serve as the "root" of your tree. You get to decide whether this will be "House" (from your example), or whether it is a fictional placeholder node that you create just for this purpose. I suggest the later.

So, to the code! Again, this is optimized for simplicity and readability. There are some performance issues that you may want to address in production code (for instance, it is not really necessary to constantly look up the "parent" node). I've avoided these optimizations here because they increase complexity.

// Create the root of the tree.
LocationNode root = new LocationNode();

using (SqlCommand cmd = new SqlCommand()) {
    cmd.Connection = conn; // your connection object, not shown here.
    cmd.CommandText = "The above query, ordered by [Depth] ascending";
    cmd.CommandType = CommandType.Text;
    using (SqlDataReader rs = cmd.ExecuteReader()) {
        while (rs.Read()) {
            int id = rs.GetInt32(0); // ID column
            var parent = root.Get(id) ?? root;
            parent.Add(new LocationNode {
                ID = id,
                LocationID = rs.GetInt32(1),
                Name = rs.GetString(2)
            });
        }
    }
}

Ta-da! The root LocationNode now contains your entire hierarchy. By the way, I haven't actually executed this code, so please let me know if you spot any glaring issues.


Edit 2

To fix your sample code, make these changes:

Remove this line:

// Create an instance of the tree
TreeView t1 = new TreeView();

This line is not actually an issue, but it should be removed. Your comments here are inaccurate; you are not really assigning a tree to the control. Instead, you are creating a new TreeView, assigning it to t1, then immediately assigning a different object to t1. The TreeView that you create is lost as soon as the next line executes.

Fix your SQL statement

// SQL Commands
string getLocations = "SELECT ID, LocationID, Name FROM dbo.Locations";

Replace this SQL statement with the SQL statement that I suggested earlier, with an ORDER BY clause. Read my previous edit that explains why the "depth" is important: you really do want to add the nodes in a particular order. You cannot add a child node until you have the parent node.

Optionally, I think you don't need the overhead of an SqlDataAdapter and DataTable here. The DataReader solution I originally suggested is simpler, easier to work with, and more effecient in terms of resources.

Also, most C# SQL objects implement IDisposable, so you will want to make sure you are using them correctly. If something implements IDisposable, be sure you wrap it in using statements (see my prior C# code sample).

Fix your tree-building loop

You're only getting the parent and child nodes because you have a loop for the parents and an inner loop for the children. As you must already know, you aren't getting the grandchildren because you have no code that adds them.

You could add an inner-inner loop to get the grandchildren, but clearly you're asking for help because you've realized that doing so will only lead to madness. What would happen if you then wanted the great-grandchildren? An inner-inner-inner loop? This technique is not viable.

You have probably thought of recursion here. This is a perfect place for it, and if you are dealing with tree-like structures, it's going to come up eventually. Now that you've edited your question, it's clear that your problem has little, if anything, to do with SQL. Your real problem is with recursion. Somebody may eventually come along and devise a recursive solution for this. That would be a perfectly valid, and possibly preferable approach.

However, my answer has already covered the recursive part--it has simply moved it into the SQL layer. Therefore, I'll keep my previous code around, as I feel it is a suitable generic answer to the question. For your specific situation, you'll need to make a few more modifications.

First, you won't need the LocationNode class that I suggested. You are using TreeNode instead, and that will work fine.

Secondly, the TreeView.FindNode is similar to the LocationNode.Get method that I suggested, except that FindNode requires the complete path to the node. To use FindNode, you must modify the SQL to give you this information.

Therefore, your entire PopulateTree function should look like this:

public void PopulateTree(TreeView t1) {

    // Clear any exisiting nodes
    t1.Nodes.Clear();

    using (SqlConnection connection = new SqlConnection()) {
        connection.ConnectionString = "((replace this string))";
        connection.Open();

        string getLocations = @"
            With hierarchy (id, [location id], name, depth, [path])
            As (

                Select ID, [LocationID], Name, 1 As depth,
                    Cast(Null as varChar(max)) As [path]
                From dbo.Locations
                Where ID = [LocationID]

                Union All

                Select child.id, child.[LocationID], child.name,
                    parent.depth + 1 As depth,
                    IsNull(
                        parent.[path] + '/' + Cast(parent.id As varChar(max)),
                        Cast(parent.id As varChar(max))
                    ) As [path]
                From dbo.Locations As child
                Inner Join hierarchy As parent
                    On child.[LocationID] = parent.ID
                Where child.ID != parent.[Location ID])

            Select *
            From hierarchy
            Order By [depth] Asc";

        using (SqlCommand cmd = new SqlCommand(getLocations, connection)) {
            cmd.CommandType = CommandType.Text;
            using (SqlDataReader rs = cmd.ExecuteReader()) {
                while (rs.Read()) {
                    // I guess you actually have GUIDs here, huh?
                    int id = rs.GetInt32(0);
                    int locationID = rs.GetInt32(1);
                    TreeNode node = new TreeNode();
                    node.Text = rs.GetString(2);
                    node.Value = id.ToString();

                    if (id == locationID) {
                        t1.Nodes.Add(node);
                    } else {
                        t1.FindNode(rs.GetString(4)).ChildNodes.Add(node);
                    }
                }
            }
        }
    }
}

Please let me know if you find any additional errors!

这篇关于SQL 数据层次结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆