Мне нужно выбрать несколько «основных» строк из таблицы, также возвращая для каждого результата количество подробных строк из другой таблицы. Каков хороший способ добиться этого без нескольких запросов (один для основных строк и один для каждого результата, чтобы получить подробные строки).
Например, со структурой базы данных, как показано ниже:
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;
}
}





выберите <столбцы> из мастера
выберите <columns> from master M join Child C on M.Id = C.MasterID
Вы можете сделать это с помощью двух запросов и одного прохода для каждого набора результатов:
Запрос всех мастеров, упорядоченных по MasterId, затем запрос всех деталей, также упорядоченных по MasterId. Затем с помощью двух вложенных циклов выполните итерацию основных данных и создайте новый объект Master для каждой строки в основном цикле и повторите детали, пока они имеют тот же MasterId, что и текущий объект Master, и заполните его коллекцию _details во вложенном цикле.
В зависимости от размера вашего набора данных вы можете вытащить все данные в свое приложение в памяти с помощью двух запросов (один для всех мастеров и один для всех вложенных данных), а затем использовать это для программного создания ваших подсписок для каждого из ваших объектов, дающих что-то нравиться:
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 в класс подробностей.
Обычно я бы выбрал подход с двумя сетками - однако вы также можете посмотреть 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 запросов - один главный и один дочерний список для каждой главной строки.
Это можно сделать с помощью одного такого запроса:
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);
Есть несколько граней, которые можно сбить с толку, но они должны дать вам общее представление.
Да, это нормально, но задан вопрос, как это сделать без нескольких запросов, так что код достигает
Это альтернатива, которую вы можете рассмотреть. Это действительно стоит 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()
Это зависит от ширины MasterTable ... если у нее много столбцов (или несколько очень толстых столбцов, например nvarchar (max) и т. д.), Тогда вы не захотите дублировать основные данные снова и снова. Если в нем всего несколько узких столбцов, вы, вероятно, можете рискнуть.