2010-12-30 2 views
1

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

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

Это как данные выглядит следующим образом:
Категория -> Шаблон -> Instance

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

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

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

У меня есть два варианта сейчас:
1) Соедините все 3 таблицы и сразу прочитайте все данные.
Поверхность: намного быстрее на стороне базы данных, вся информация в одном запросе.
Даунсайд: Я передаю много избыточных данных, потому что в каждой строке есть одна и та же категория и данные шаблона для каждого экземпляра.

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

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) я запрос каждую таблицу по себе, во-первых сбора данных категории, то соответствующие данные шаблона с категорией ID и так далее.
Поверхностный индекс: Интуитивно понятный подход, более простой в обращении на кодовой стороне, нет избыточных данных.
Даунсайд: Несколько запросов к серверу, возможно, медленнее.

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

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

Заранее благодарен! Эта проблема дает мне головные боли с тех пор.

+0

Я смущен, если вы используете опцию №1, почему есть избыточные данные? Если вы организуете свои классы таким образом, чтобы внешняя категория класса содержала только идентификатор или первичный ключ для внутренних классов, то это не избыточные данные. С большинством ORM я использовал именно то, как это делается. –

+1

Возможно, упрощенный пример - это пропуски: в опции № 1 полная категория и данные шаблона (а не только идентификатор и имя) будут включены в каждую строку. Таким образом, его довольно избыточный, чтобы иметь информацию несколько раз для каждого экземпляра, где он не меняется. – magnattic

+0

Мне нужна информация для категорий и шаблонов, чтобы заполнить классы данными, но если я присоединяюсь как к опции № 1, у меня она есть несколько раз для каждой строки экземпляра. Пример: CategoryID | CategoryName | CategoryLocation | КатегорияXYZ | TemplateID | TemplateName | TemplateLocation | TemplateXYZ | InstanceID | InstanceName | ... – magnattic

ответ

1

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

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

+0

Большое спасибо за понимание! Я думаю, что я поменяю свой код на вариант №1 для оптимальной производительности. Я не был уверен, что избыточные данные не являются признаком плохого дизайна, но что Entity Framework идет одинаково, дает некоторую уверенность. Я буду отмечать ваш ответ как принятый, но я думаю, что большинство ответов здесь чрезвычайно полезно для понимания различных возможных путей решения проблемы и их преимуществ. – magnattic

+0

Рад помочь. Отправьте сюда и сообщите нам, если вы найдете что-нибудь интересное, как только система начнет работать. Это определенно то, что я буду следить за собой в своем приложении. –

1

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

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

Edit: Вы можете использовать ту же логику для отношений Шаблона/Instance:

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

Эй, спасибо. Я об этом не думал. Кажется, это лучший способ, чем вариант №2, потому что мне нужно только выполнить 3 запроса (1 для каждой таблицы). Но что было бы лучшим способом убедиться, что я собираю только экземпляры-объекты, принадлежащие моей категории? В SQL Select с CategoryID путем присоединения к таблице Template или путем сбора всех связанных идентификаторов шаблонов в коде, а затем используйте IN() в SQL-запросе? – magnattic

+0

Вам не нужен дополнительный SQL, поскольку все в памяти. Я сделал редактирование, чтобы также показать логику отношений Шаблон/Экземпляр. – TomBot

+0

Но если я хочу получить данные только для 1 определенной категории (что я и делаю), мне не нужно выполнять полный «SELECT *» в таблице Instance (который содержит много данных). – magnattic

0

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

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

1

Предположения: вы используете 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()

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

+0

Да, все предположения верны. Я никогда не работал с несколькими результирующими наборами из 1 Хранимой процедуры, посмотрю, спасибо, спасибо! Данные будут запрашиваться очень часто. Приложение ASP.Net и каждый раз, когда пользователь открывает страницу категории, выполняется вызов БД. – magnattic

1

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

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

+0

У меня уже есть структура класса так, как вы описали, я просто не описал классы подробно в вопросе. Например, Category.Templates - это список