Эффективный способ запроса вложенных данных

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

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

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;
    }
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
2 921
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

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

выберите <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);

Есть несколько граней, которые можно сбить с толку, но они должны дать вам общее представление.

Это зависит от ширины MasterTable ... если у нее много столбцов (или несколько очень толстых столбцов, например nvarchar (max) и т. д.), Тогда вы не захотите дублировать основные данные снова и снова. Если в нем всего несколько узких столбцов, вы, вероятно, можете рискнуть.

Marc Gravell 25.11.2008 15:11

Да, это нормально, но задан вопрос, как это сделать без нескольких запросов, так что код достигает

Garry Shutler 25.11.2008 15:20

Это альтернатива, которую вы можете рассмотреть. Это действительно стоит 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()

Другие вопросы по теме