4

我需要从一个表中选择一些“主”行,同时为每个结果返回另一个表中的一些详细信息行。什么是在没有多个查询的情况下实现此目的的好方法(一个用于主行,一个用于获取详细行的结果)。

例如,具有如下数据库结构:

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;
    }
}
4

6 回答 6

3

通常,我会选择两个网格方法 - 但是,您可能还想查看 FOR XML - 将父/子数据塑造为 xml 并从那里。

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

此外 - LINQ-to-SQL 支持这种类型的模型,但您需要提前告诉它您想要哪些数据。通过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 个查询 - 每个主行一个主列表和一个子列表。

于 2008-11-25T11:36:15.903 回答
3

可以通过这样的单个查询来完成:

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

然后在伪代码中

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);

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

于 2008-11-25T11:41:06.847 回答
0

从 master 中选择 <columns>

select < columns > from master M join Child C on M.Id = C.MasterID

于 2008-11-25T11:11:58.170 回答
0

您可以通过两个查询和一个传递每个结果集来做到这一点:

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

于 2008-11-25T11:16:01.297 回答
0

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

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只返回您感兴趣的主项目和详细项目。另请注意,为了有效,您需要将 MasterId 添加到详细信息类

于 2008-11-25T11:17:36.863 回答
0

这是您可以考虑的替代方案。每个开发人员确实要花费 150 美元,但时间就是金钱……

我们使用一个名为实体空间的对象持久层,它会为您生成代码以完全按照您的意愿行事,并且您可以在架构更改时重新生成。用数据填充对象是透明的。使用上面描述的对象看起来像这样(请原谅我的 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()
于 2008-11-29T01:18:17.260 回答