如何从加入同一个表的DataTable中进行选择?

时间:2013-09-25 14:17:39

标签: c# datatable

我有DataTable个对象,它拥有一些“树数据结构”。数据不存储在任何数据库中,我只使用DataTable来操作没有SQL服务器的数据。

我的数据看起来像这样(缩进只是为了更好地阅读):

DataTable dtCategories = GetCategoriesAsDataTable();

id    name    parentId
int   string  int
----------------------
 1    One         0
 2      OneA      1
 3      OneB      1
 4    Two         0
 5      TwoA      4
 6      TwoB      4
 7        TwoAA   5
 8        TwoAB   5

到目前为止 - 我正在考虑选择“where parentId = 0”的第一级并将其放在单独的DataTable中,如下所示:

DataTable dtFirstLevel = dtCategories.Select("[parentId] = 0");

// and after this - create DataTable for second level
// but I don't know how can I use "IN" clause here
DataTable dtSecondLevel = dtCategories.Select(?????????);
  1. 如何只选择前2级树?
  2. 如何在没有SQL服务器的情况下选择此选项(仅使用数据对象)?

5 个答案:

答案 0 :(得分:3)

也许这会有所帮助:

var rows = table.AsEnumerable();
var parents = rows.Where(r => !r.Field<int?>("parentId").HasValue);
var children = rows.Where(r => r.Field<int?>("parentId").HasValue);
var secondLevel = from parent in parents
                  join child in children
                  on parent.Field<int>("id") equals child.Field<int?>("parentId").Value
                  select child;
var both = parents.Concat(secondLevel).CopyToDataTable();

请注意,我为父母使用Nullable<int>而不是0,因为它更易读,更不容易出错。以下是您的示例数据:

var table = new DataTable();
table.Columns.Add("id", typeof(int));
table.Columns.Add("name", typeof(string));
table.Columns.Add("parentId", typeof(int));
table.Rows.Add(1, "One", (int?)null);
table.Rows.Add(2, "OneA", 1);
table.Rows.Add(3, "OneB", 1);
table.Rows.Add(4, "Two", (int?)null);
table.Rows.Add(5, "TwoA", 4);
table.Rows.Add(6, "TwoB", 4);
table.Rows.Add(7, "TwoAA", 5);
table.Rows.Add(8, "TwoAB", 5);

结果:

1   One 
4   Two 
2   OneA    1
3   OneB    1
5   TwoA    4
6   TwoB    4

由于您希望使用0代替int?

var parents = rows.Where(r =>  r.Field<int>("parentId") == 0);
var children = rows.Where(r => r.Field<int>("parentId") != 0);
var secondLevel = from parent in parents
                  join child in children
                  on parent.Field<int>("id") equals child.Field<int>("parentId")
                  select child;

答案 1 :(得分:2)

我认为此功能可以帮助您确定每个条目的树级别,以便您可以在选择中使用它:

    public int level(DataTable dt, DataRow row)
    {
        int parentid = int.Parse(row[2].ToString());
        if (parentid == 0)
            return 1;
        else
            return 1 + level(dt, GetDataRow(dt,parentid ));
    }

    public DataRow GetDataRow(DataTable dt, int id)
    {
        foreach (DataRow r in dt.Rows)
        {
            if (int.Parse(r[0].ToString()) == id) return r;
        }
        return null;
    }

答案 2 :(得分:1)

您有几个问题可供选择。正如@Ali所提出的,你可以使用这样的递归:

public int level(DataTable dt, DataRow row)
{
    int parentid = int.Parse(row[2].ToString());
    if (parentid == 0)
        return 1;
    else
        return 1 + level(dt, GetDataRow(dt,parentid ));
}

public DataRow GetDataRow(DataTable dt, int id)
{
    foreach (DataRow r in dt.Rows)
    {
        if (int.Parse(r[0].ToString()) == id) return r;
    }
    return null;
}

但问题是你最终会迭代每个元素然后在每次迭代时使用递归。如果您的列与它们在树中的级别之间绝对没有数据关系,那么除了parentId之外,这是您唯一的解决方案。

另一方面,如果你有一个关系,你有名字[树的级别],如名[A]是树级别1,名称[AB]是树级别2与右边的节点,然后迭代通过每个人都喜欢:

    foreach (DataRow r in dt.Rows)
    {
        //Pull out the element
        //Check the element's level
       //Add it to the result set if level <= 2
    }

我个人更喜欢通过实际构建树结构或使用SQL WHERE子句来解决问题,但很难证明它的时间是合理的。根据您从何处获取此数据,您还可以添加一个额外的列,该列可以告诉您节点所在的级别,具体取决于插入的位置。如果它有祖父母(即两个父节点),则不将其包含在结果集中。

答案 3 :(得分:1)

DataTable level1 = (from t in dtCategories.AsEnumerable()
                    where t.Field<int>("parentId") == 0
                    select t).CopyToDataTable();

DataTable level2 =(from t1 in dtCategories.AsEnumerable()
                        join t2 in dtCategories.AsEnumerable() 
                           on t1.Field<int>("id") equals t2.Field<int>("parentId")
                   where t1.Field<int>("parentId") == 0
                   select t2).CopyToDataTable();

答案 4 :(得分:1)

另一种方法,这将为您提供一个新对象,其中包含级别和行项本身。这将适用于n个级别......

        var nodes = table.AsEnumerable();

        //var nodes = new List<TreeNode>();

        var parentId = 0;
        var countLevel = 0;
        var allNods = new List<dynamic>();

        while (nodes.Any(p => p.Field<int>("parentId") == parentId))// && countLevel < 2) 
            // countlevel< 2 only to give you the first 2 levels only...
        {
            var nodesWithLevel = nodes.Where(p => p.Field<int>("parentId") == parentId)
                        .Select(p => new { Level = parentId, Node = p });

            allNods = allNods.Concat<dynamic>(nodesWithLevel).ToList();
            parentId++;
            countLevel++;
        }

代码当前期望根节点具有parentId = 0.也可以将原因更改为null ...