Иерархические данные из БД - присоединяться или не присоединяться - PullRequest
1 голос
/ 30 декабря 2010

Я пытаюсь найти чистое и эффективное решение для этой проблемы, но застрял как-то.

Общая информация:
-ASP.Net C # приложение (.Net 3.5)
-MS-SQL Server 2005

Вот так выглядят данные:
Категория -> Шаблон -> Экземпляр

Категория может содержать несколько шаблонов.
Шаблон может содержать несколько экземпляров.

Существует класс для каждого из этих 3 и соответствующая таблица базы данных с большим количеством столбцов.

Я хочу загрузить полную категорию из базы данных в объект класса C # Category, включаявсе связанные объекты шаблона и экземпляра.

Теперь у меня есть два варианта:
1) Выполнить объединение всех трех таблиц и сразу прочитать все данные.
Upside : намного быстрее на стороне базы данных, вся информация в одном запросе.
Downside : я передаю много избыточных данных, потому что в каждой строке есть одна и та же категория иданные шаблона для каждого экземпляра.

Пример (упрощенно):

CategoryID | CategoryName | TemplateID | TemplateName | InstanceID | InstanceName  
1 | FirstCategory | 1 | FirstTemplate | 1 | FirstInstance   
1 | FirstCategory | 1 | FirstTemplate | 2 | SecondInstance  
1 | FirstCategory | 1 | FirstTemplate | 3 | ThirdInstance  
1 | FirstCategory | 1 | SecondTemplate | 4 | FourthInstance  

2) Я запрашиваю каждую таблицу отдельно, сначала собирая данные категории, затем связанные данные шаблонас идентификатором категории и т. д.
Upside : Интуитивно понятный подход, легче обрабатывать на стороне кода, лишние данные не извлекаются.
Downside :Несколько запросов к серверу, возможно, медленнее.

Какой лучший путь сюда?Я пропускаю опцию?
Решение 1, кажется, имеет лучшую производительность, но для меня это выглядит "нечистым".Мне нужно было бы получить данные для категории из целого ряда строк данных.

Если я выберу решение 1, какой лучший способ получить данные категории и шаблона?
Считать их изПервая строка данных и создать новый экземпляр после изменения значения?
Есть ли какая-то группировка?

Заранее спасибо!Эта проблема вызывает у меня головную боль с незапамятных времен.

Ответы [ 5 ]

1 голос
/ 30 декабря 2010

Я использую Entity Framework для проекта, который я делаю в данный момент.Профилируя его при определенных сценариях, он действительно использует опцию 1 и возвращает таблицу с избыточными данными.Таким образом, похоже, что Microsoft выбрала этот подход, и они владеют всем стеком, поэтому, вероятно, знают, как принять правильное решение об этой конкретной проблеме.

Может быть какая-то эвристика, которая решает использовать вариант 2 при определенных сценариях, но я не видел этого в своем профилировании.Более того, я никогда не видел, чтобы EF возвращал несколько наборов результатов в одном запросе.

1 голос
/ 30 декабря 2010

Для небольшого количества данных вариант 1 звучит хорошо, однако вы должны изменить структуру класса и создать композицию для Template и Instance, то есть класс Template должен иметь коллекцию Instance, а Category будет иметь коллекцию Template, и вы можете избавиться от нее.избыточные данные.

Вариант 2 можно использовать заранее, если объем данных велик, а пропускная способность вашей сети действительно хороша для частых вызовов БД.

1 голос
/ 30 декабря 2010

Допущения: вы используете ADO / сохраненные процессы и у вас есть нормализованная структура данных.

Вы можете вернуть 3 набора результатов из одного вызова хранимой процедуры.

1) select c.* from category c where c.id = @categoryId

2) select t.* from templates t
    join category c on t.categoryid = c.id 
    where c.id = @categoryId

3) select i.* from Instance i
    join templates t on i.templateid = t.id  
    join category c on t.categoryid = c.id
    where c.id = @categoryId

И последовательно заполнятьваши объекты через sqldatareader с использованием sqldatareader.read () и sqldatareader.Nextresult ()

Это действительно зависит от размера возвращаемых вами данных и от того, как часто вы будете запрашивать эти данные относительно того, используете ли вынабор данных на основе извлечения или что-то вроде выше.

1 голос
/ 30 декабря 2010

Существует третий вариант: сделать 'select *' для каждой таблицы, а затем выполнить объединение в памяти.Вы можете использовать LINQ для некоторой ленивой оценки:

  class Category
  {
    public int CategoryId { get; set; }
    public List<Template> Templates
    {
      get
      {
        return Repository.Templates.Where(t => t.CategoryId == this.CategoryId).ToList();
      }
    }
  }

Редактировать: вы можете использовать ту же логику для отношения Шаблон / Экземпляр:

  class Template
  {
    public int CategoryId { get; set; }
    public int TemplateId { get; set; }
    public List<Instance> Instances
    {
      get
      {
        return Repository.Instances.Where(i => i.TemplateId == this.TemplateId).ToList();
      }
    }
  }
0 голосов
/ 30 декабря 2010

Если иерархия не слишком глубока, а число детей на каждом уровне достаточно мало, я обычно начинаю с варианта № 2. Интуитивный подход, как вы это описали. Это позволяет нам использовать любые методы, которые у нас уже есть (getTemplates (), getInstances (234) и т. Д.).

Но с точки зрения производительности, выполнение одного запроса с объединением из трех таблиц и обработка записей в отсортированном порядке, вероятно, будет более быстрой альтернативой (вариант № 1).

...