代码之家  ›  专栏  ›  技术社区  ›  Compile This

查询嵌套数据的有效方法

  •  4
  • Compile This  · 技术社区  · 16 年前

    我需要从一个表中选择一些“主”行,同时为每个结果返回另一个表中的一些详细行。不需要多个查询(一个用于主行,一个用于每个结果以获取详细行)就可以实现这一点的好方法是什么。

    例如,使用如下数据库结构:

    MasterTable:
        - MasterId BIGINT
        - Name NVARCHAR(100)
    
    DetailTable:
        - DetailId BIGINT
        - MasterId BIGINT
        - Amount MONEY
    

    data 下面的物体?

    IList<MasterDetail> data;
    
    public class Master
    {
        private readonly List<Detail> _details = new List<Detail>();
    
        public long MasterId
        {
            get; set;
        }
    
        public string Name
        {
            get; set;
        }
    
        public IList<Detail> Details
        {
            get
            {
                return _details;
            }
        }
    }
    
    public class Detail
    {
        public long DetailId
        {
            get; set;
        }
    
        public decimal Amount
        {
            get; set;
        }
    }
    
    6 回复  |  直到 16 年前
        1
  •  3
  •   Marc Gravell    16 年前

    通常情况下,我会选择双网格方法——不过,您可能还想看看XML——将父/子数据格式化为XML并从中加载是相当容易的(在SQL Server 2005及更高版本中)。

    SELECT parent.*,
           (SELECT * FROM child
           WHERE child.parentid = parent.id FOR XML PATH('child'), TYPE)
    FROM parent
    FOR XML PATH('parent')
    

    另外,LINQtoSQL支持这种类型的模型,但是您需要提前告诉它需要哪些数据。通过 DataLoadOptions.LoadWith

    // sample from MSDN
    Northwnd db = new Northwnd(@"c:\northwnd.mdf");
    DataLoadOptions dlo = new DataLoadOptions();
    dlo.LoadWith<Customer>(c => c.Orders);
    db.LoadOptions = dlo;
    
    var londonCustomers =
        from cust in db.Customers
        where cust.City == "London"
        select cust;
    
    foreach (var custObj in londonCustomers)
    {
        Console.WriteLine(custObj.CustomerID);
    }
    

    如果你不使用 LoadWith ,您将得到n+1个查询-每个主行一个主列表和一个子列表。

        2
  •  3
  •   Garry Shutler    16 年前

    select   MasterTable.MasterId,
             MasterTable.Name,
             DetailTable.DetailId,
             DetailTable.Amount
    from     MasterTable
             inner join
             DetailTable
             on MasterTable.MasterId = DetailTable.MasterId
    order by MasterTable.MasterId
    

    然后在psuedo代码中

    foreach(row in result)
    {
       if (row.MasterId != currentMaster.MasterId)
       {
           list.Add(currentMaster);
           currentMaster = new Master { MasterId = row.MasterId, Name = row.Name };
       }
       currentMaster.Details.Add(new Detail { DetailId = row.DetailId, Amount = row.Amount});
    }
    list.Add(currentMaster);
    

    有一些优势,但它应该给你一个大致的想法。

        3
  •  0
  •   Mladen Prajdic    16 年前

    选择<栏目>从主人那里

    选择<栏目>从主M在M.Id=C.MasterID上连接子C

        4
  •  0
  •   Ovidiu Pacurar    16 年前

    您可以通过两次查询和对每个结果集进行一次传递来完成此操作:

    查询按MasterId排序的所有母版,然后查询也按MasterId排序的所有详细信息。然后,使用两个嵌套循环,迭代主数据并为主循环中的每一行创建一个新的主对象,并在细节与当前主对象具有相同的主ID时迭代细节,并在嵌套循环中填充其_details集合。

        5
  •  0
  •   Wolfwyrd    16 年前

    根据数据集的大小,您可以通过两个查询(一个用于所有主数据,一个用于所有嵌套数据)将所有数据拉入内存中的应用程序,然后使用该查询以编程方式为每个对象创建子列表,如下所示:

    List<Master> allMasters = GetAllMasters();
    List<Detail> allDetail = getAllDetail();
    
    foreach (Master m in allMasters)
        m.Details.Add(allDetail.FindAll(delegate (Detail d) { return d.MasterId==m.MasterId });
    

    通过这种方法,您实际上是在用内存占用换取速度。您可以很容易地对此进行调整,以便 GetAllMasters GetAllDetail

        6
  •  0
  •   Scott Whitlock    16 年前

    这是你可以考虑的另一种选择。每个开发者花费150美元,但时间就是金钱。。。

    我们使用一个名为 Entity Spaces 这将生成代码,让您完全按照自己的意愿执行操作,并且您可以在模式发生更改时重新生成代码。用数据填充对象是透明的。使用上面描述的对象看起来是这样的(请原谅我的VB,但它也在C#中工作):

    Dim master as New BusinessObjects.Master
    master.LoadByPrimaryKey(43)
    Console.PrintLine(master.Name)
    For Each detail as BusinessObjects.Detail in master.DetailCollectionByMasterId
       Console.PrintLine(detail.Amount)
       detail.Amount *= 1.15
    End For
    With master.DetailCollectionByMasterId.AddNew
       .Amount = 13
    End With
    master.Save()