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

时间:2011-03-14 20:56:40

标签: c# asp.net sql sql-server-2008 ado.net

再次感谢您发布的所有精彩答案!

我在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

使用这种方法我必须找到所有唯一的父行,然后读取所有的子行。优点是我只进行了一次db。

第二个版本是分别阅读所有家长:

SELECT * FROM Parents

然后分别阅读所有孩子:

SELECT * FROM Children

并使用LINQ将所有父母与孩子合并。这种方法可以两次前往数据库。

第三种也是最后一种(也是最低效的)方法是抓住所有父母,并在构建每个父对象时,前往数据库抓住所有的孩子。这种方法需要n + 1个连接:所有父母都有1个连接,每个父母都有n次旅行以获得所有孩子。

有关如何更轻松地提出建议吗?当然,我无法摆脱使用存储过程,我不能使用LINQ2SQL或EF。您更喜欢数据表与数据读取器,如果是这样,如何使用方法1或2?

谢谢, 马丁

4 个答案:

答案 0 :(得分:2)

我更喜欢在一个查询中提取所有结果,只需在一个循环中构建树

    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 :(得分:1)

  

使用这种方法我必须找到   所有唯一的父行,然后   读了所有的孩子。

您可以添加order by p.ParentId。这可确保来自同一父级的所有子级都在连续的行中。因此,您可以读取下一行,如果父项已更改,则创建新的父对象,否则将子项添加到上一个父项。无需搜索唯一的父行。

答案 2 :(得分:0)

我通常在表级做出这个决定。有些桌子我经常需要孩子,所以我马上抓住它们。在其他情况下,访问孩子是罕见的,所以我懒惰他们。

答案 3 :(得分: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 } );

伪代码可能有点偏差,但希望你能得到一般的想法