如何从SQL中读取父级和子级数据?

4
感谢您们提供的所有精彩答案!
我在SQL中有两个表。第一个定义了父级,具有名为ParentId的主键列。我还有一个子表,它具有一个主键和一个作为“ParentId”的外键。因此,这两个表形成一个一对多的关系。
问题是什么是以最有效的方式从C#代码中获取父项+子项数据?数据必须读入以下对象:
public class Parent
{
    public int ParentId { get; set; }
    public List<Child> Children { get; set; }
    //  ... many more properties ... //
}


public class Child
{
    public int ChildId { get; set; }
    public string Description { get; set; }
    //  ... many more properties ... //
}

如果我使用以下查询,我将一次性获取父级和子级,其中每个父级将重复多少次就有多少个子级:

SELECT
    p.ParentId as 'ParentId',
    c.ChildId as 'ChildId',
    -- other relevant fields --
FROM
    Parents p
INNER JOIN
    Children c
ON 
    p.ParentId = c.ParentId

使用这种方法,我需要找到所有唯一的父行,然后读取所有子行。优点是只需要进行1次数据库访问。
第二种方法是分别读取所有父行:
SELECT * FROM Parents

然后单独读取所有子元素:

SELECT * FROM Children

使用LINQ将所有父项与子项合并。这种方法需要2次访问数据库。
第三种方法是最低效的,它获取所有父项,并在构建每个父对象时,访问数据库以获取其所有子项。这种方法需要n+1个连接:1个用于所有父项,n个用于获取每个父项的所有子项。
有没有更简单的方法?我不能避免使用存储过程,也不能使用LINQ2SQL或EF。您更喜欢Data Tables还是DataReaders?如果是这样,如何在第1或第2种方法中使用它们?
谢谢, 马丁
4个回答

3
我更喜欢在一个查询中检索所有结果,然后在一个循环中构建树结构。
    SELECT p.ParentId as 'ParentId', null as 'ChildId'
    FROM Parents p
    UNION ALL
    SELECT c.ParentId as 'ParentId', c.ChildId as 'ChildId'
    FROM Children c

    List<Parent> result = new List<Parent>();
    Parent current;
    while (dr.Read())
    {
      if (string.isNullOrEmpty(dr['ChildId']))
      {
        //create and initialize your parent object here and set to current
      }
      else if (!string.isNullOrEmpty(dr['ChildId']) 
                && dr['ParentId'].ToString().Equals(current.ParentId.ToString())
      {
        //create and initialize child
        //add child to parents child collection
      }
    }

1
使用这种方法,我必须找到所有唯一的父行,然后读取所有子行。
你可以只包含一个order by p.ParentId。这确保了来自同一父级的所有子项都在连续的行中。因此,您可以读取下一行,如果父级已更改,则创建新的父对象,否则将子项添加到先前的父级。无需搜索唯一的父行。

0

我猜选项#2在带宽方面比选项#1更有效率(因为你不会重复任何数据)。

你可以将这两个查询放在一个存储过程中,并通过代码使用sqldataadapter执行该过程(例如(new SqlDataAdapter(command)).Fill(myDataSet),其中myDataSet将包含这两个表)。

然后,您可以读取第一个表格,通过ParentId创建父级的字典(在Dictionary<int, Parent>中),然后简单地读取第二个表格中的每一行以添加子项:

parents[(int)myDataSet.Tables[1]["ParentId"]].Children.Add(new Child() { etc } );

虚拟代码可能有点不准确,但希望您能理解大致思路。


0

我通常在表级别做出这个决定。有些表我经常需要子项,所以我立即获取它们。在其他情况下,访问子项是罕见的,因此我会进行延迟加载。


我显然需要孩子,这就是我第一次提出问题的原因。 - bleepzter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接