Эффективный способ запроса вложенных данных - PullRequest
4 голосов
/ 25 ноября 2008

Мне нужно выбрать количество «главных» строк из таблицы, а также вернуть для каждого результата количество строк подробностей из другой таблицы. Что является хорошим способом достижения этого без множественных запросов (один для основных строк и один для каждого результата, чтобы получить подробные строки).

Например, со структурой базы данных, как показано ниже:

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 ]

3 голосов
/ 25 ноября 2008

Это можно сделать с помощью одного запроса:

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 голосов
/ 25 ноября 2008

Как правило, я бы пошел на подход с двумя сетками - однако, вы также можете взглянуть на FOR XML - довольно просто (в SQL Server 2005 и выше) сформировать родительские / дочерние данные как 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 запросов - один мастер и один дочерний список на строку мастера.

0 голосов
/ 29 ноября 2008

Это альтернатива, которую вы могли бы рассмотреть. Это стоит 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()
0 голосов
/ 25 ноября 2008

В зависимости от размера вашего набора данных вы можете вытянуть все данные в ваше приложение в памяти с помощью двух запросов (один для всех мастеров и один для всех вложенных данных), а затем использовать его для программного создания ваших подсписков для каждого из ваших объекты, дающие что-то вроде:

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 к классу подробностей

0 голосов
/ 25 ноября 2008

Вы можете сделать это с двумя запросами и одним проходом для каждого набора результатов:

Запрос всех мастеров, заказанных MasterId, затем запрос всех деталей, также заказанных MasterId. Затем с помощью двух вложенных циклов итерируйте основные данные и создайте новую строку foreach главного объекта в главном цикле, итерируйте детали, имея тот же MasterId, что и текущий главный объект, и заполните его коллекцию _details во вложенном цикле.

0 голосов
/ 25 ноября 2008

выберите <столбцы> из мастера

выберите <столбцы> из основного M присоедините дочерний C на M.Id = C.MasterID

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...