2008-10-14 8 views
30

Я пытаюсь разработать приложение для хранения академической справочной информации. Проблема в том, что каждый тип ссылки (например, журнальные статьи, книги, газетные статьи и т. Д.) Требует различной информации. Например, ссылка на журнал требует как заголовка журнала, так и названия статьи, а также номера страницы, в то время как книга требует издателя и даты публикации, в которых статьи журналов не требуются.Один стол или много?

Поэтому, если у меня есть все ссылки, хранящиеся в одной таблице в моей базе данных, и просто оставляйте поля пустыми, когда они не применяются, или мне нужно иметь различные таблицы, такие как BookReferences, JournalReferences, NewspaperReferences и помещать соответствующие ссылки в каждый. Тогда проблема заключалась бы в том, что это сделало бы поиск по всем ссылкам более сложным, а также редактирование должно было бы выполняться скорее по отдельности.

(я планирую использовать Ruby On Rails для этого проекта, кстати, но я сомневаюсь, что делает никакой разницы в этом вопросе дизайна)

Update:

Все больше просмотров на этом ? Я надеялся получить простой ответ, сказав, что конкретный метод определенно считается «лучшим», но, как обычно, все не так просто. Опция Single-Table Inheritance выглядит довольно интересно, но на нее мало информации, которую я могу найти очень легко - я могу опубликовать на этом сайте еще один вопрос.

Я отделен между Olvak's answer и Corey's answer. Ответ Кори дает вескую причину, почему Олвак не самый лучший, но ответ Ольвака дает веские причины, почему Кори не самый лучший! Я никогда не понимал, что это может быть так сложно ...

Любые дальнейшие советы очень ценятся!

+0

Мне очень нравится этот вопрос, спасибо. Я рассматривал аналогичную проблему в отношении таблицы продуктов в настройке электронной торговли, и ответы здесь могут быть легко применимы к этому. Приветствия. – jammus 2008-10-14 09:45:07

+0

Рад я мог бы помочь :-) – robintw 2008-10-14 09:48:10

+0

Просто интересно: сколько записей вы ожидаете? Очевидно, просто фигурка. Я думаю, что это также должно быть фактором окончательного решения. – nickf 2008-10-14 14:14:27

ответ

0

одна таблица и поле «тип» было бы мое предложение

34

Я бы за то, что одну таблицу для всех ссылок, но дополнительные таблицы, как BookReferences и так далее для метаданных не применимо для всех ссылочных типов.

Поиск и запрос не будут сложнее - ведь вы могли бы просто создать представление, которое агрегирует всю информацию, как в решении с одной таблицей, а затем запросит этот просмотр дальше.

Наличие всего в одной таблице с большим количеством нулей может показаться более простым решением, но на самом деле это приведет к большим проблемам. Например: С отдельными таблицами вы можете определить, какие поля требуется для каждой BookReference, но если все находится в одной таблице, каждое поле должно быть нулевым и, следовательно, необязательным. Также было бы легче вставить недопустимые данные, например, ссылку на книгу, которая также ошибочно содержит имя нулевого журнала.

Редактировать: Некоторые люди, похоже, боятся присоединиться. Не бойтесь присоединиться! Если вы используете то же самое соединение в нескольких запросах, которое действительно было бы утомительным, но в этом случае соединение должно быть определено в виде , и ваши запросы должны запрашивать это представление. Представления на самом деле являются основной абстракцией в реляционных базах данных, и вы должны использовать их по тем же причинам, по которым вы используете функции в коде: во избежание повторения, а также для инкапсуляции и создания абстракций.

Редактировать: Есть некоторые комментарии относительно производительности. Очень сложно заранее догадаться о производительности схем БД, потому что это часто неинтуитивно.Например, объединение нескольких таблиц может быть быстрее, чем полное сканирование таблицы одной таблицы - все зависит от типа запроса, характера данных, доступных индексов и т. Д. Кроме того, во многих системах баз данных вы можете использовать такие функции, как материализованные представления, для оптимизации производительности для разных запросов без ущерба для логической модели. «Денормализация для исполнения» в основном является грузовым культом в эти дни ИМХО, если вы не Google или Flickr.

+2

You взял слова из моих рук, вор! :) – 2008-10-14 08:46:03

0

Вы спрашиваете о нормализации базы данных. Джефф Этвуд написал об этом в своем посте Maybe Normalizing Isn't Normal. Это хорошо читать.

4

Наличие отдельной таблицы с полем «тип» будет проблематичным при добавлении нового типа ссылки, требующего дополнительных полей. Расширение значений полей типа не является проблемой, но вам нужно будет добавлять столбцы в таблицу, заполнять значения по умолчанию для всех текущих строк и т. Д.

Наличие отдельных таблиц позволит легко добавить новый ссылочный тип (и автоматически сгенерировать форма для него!) и поиск не был бы сложнее.

3

Rails поддерживает однонаправленное наследование и полиморфные типы ActiveRecord. Я бы предложил изучить их - у ActiveRecord есть некоторые мнения о том, как должна быть структурирована база данных.

3

Я думаю, вам нужно взглянуть на то, как SQL будет выглядеть для каждого из решений. Если вы пройдете это упражнение, то вы обнаружите, что все в одной таблице проще всего кодировать и, вероятно, приведет к лучшей производительности. Легче отделить вещи, которые вы хотите от одной таблицы, а затем собирать вещи из нескольких таблиц.

Допустим, мой один-большой стол выглядит следующим образом:

1 Идентификатор
2 типа
3 поля общего к книге-и-журнал
4 поля конкретных-to книга
5 поле конкретного к журналу

Если я просто заинтересован в книгах, я могу создать представление, или просто SQL, как это:

create view book as 
select id, field_common-to-book-and-journal, field-specific-to-book 
from my-one-big-table 
where type = 'book' 

Итак, легко смоделировать, что данные находятся в отдельных таблицах, когда я захочу.

Но, если я начинаю, помещая данные в таблицах, то отдельными я в конечном итоге писать SQL так:

select id, field-common-to-book-and-journal from books 
union 
select id, field-common-to-book-and-journal from journal-articles 
union 
.... etc, for each type 

Я не знаю о других базах данных, но делает союзов в SQL Server может быть дорогостоящим и существуют ограничения при работе с типами данных типа ntext.

Если вы будете следовать советам olavk в то ваш SQL для объединения типов в одном запросе бы в конечном итоге выглядит так:

select 
    common.id, 
    common.field-common-to-book-and-journal, 
    book.field-specific-to-book 
    journal.field-specific-to-journal 
from common-table common 
left outer join book-specific-table book on 
left outer join journal-specific-table journal on 
... etc, for each type 

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

1

Там еще один вариант: не один я полностью поддерживаю, но это еще один вариант:

Используйте три таблицы:

refs (id, title, refType) 
-- title of the reference, and what type of reference it is 

fieldDef (id, fieldName, refType, dataType) 
-- name of the field, which reference types it applies to, and 
-- what type of data is stored in these fields (ISDN number, date, etc) 

fields (refId, fieldId, value) 
-- where you actually add data to the references. 

refType может быть тип ссылки, и если вы сделаете это целое число со значениями возрастает по степеням двух (1, 2, 4, 8 ...), тогда их можно добавить вместе, чтобы создать битмаску в таблице fieldDef.

Достоинства: очень простой и расширяемый. Если вы используете другой тип ссылки или новый тип поля для существующего ссылочного типа, его можно добавить очень быстро. Формы могут автоматически генерироваться для каждого ссылочного типа. Все данные хранятся в одном месте, то есть вам не нужно отслеживать несколько схем (схемы?) для CRUD operations.

Против: Это материал, на котором работает The Daily WTF. Выборочные заявления могут стать очень запутанными и сложными. База данных не может выполнять проверку типов (например, для дат и т. Д.), А общее поле «значение» не будет оптимизировано для данных, хранящихся в нем.

9

«Жизнь проще с одной большой таблицей»: я видел естественное следствие этого, будучи столбом в 100+ столбцов, и я могу сказать вам, что я не вижу в этом радости работать.

Основная проблема заключается в том, что разработчики таких таблиц имеют тенденцию опускать ограничения, необходимые для обеспечения целостности данных. Например, OP говорит:

ссылка

журнал требует как название журнала и название статьи, а также номер страницы, в то время как книга требует издатель и дата публикации которых журнальные статьи не требуют

... что подразумевает следующие ограничения:

CONSTRAINT a_journal_must_have_a_journal_title 
    CHECK (type <> 'journal' OR journal_title IS NOT NULL); 

CONSTRAINT a_journal_must_have_an_article_title 
    CHECK (type <> 'journal' OR article_title IS NOT NULL); 

CONSTRAINT a_journal_must_have_a_page_number 
    CHECK (type <> 'journal' OR page_number IS NOT NULL); 

CONSTRAINT a_journal_cannot_have_a_publisher 
    CHECK (type <> 'journal' OR publisher IS NULL); 

CONSTRAINT a_journal_cannot_have_a_publication_date 
    CHECK (type <> 'journal' OR publication_date IS NULL); 

CONSTRAINT a_book_cannot_have_a_journal_title 
    CHECK (type <> 'book' OR journal_title IS NULL); 

CONSTRAINT a_book_cannot_have_a_article_title 
    CHECK (type <> 'book' OR article_title IS NULL); 

CONSTRAINT a_book_cannot_have_a_page_number 
    CHECK (type <> 'book' OR page_number IS NULL); 

CONSTRAINT a_book_must_have_a_publisher 
    CHECK (type <> 'book' OR publisher IS NOT NULL); 

CONSTRAINT a_jbook_must_have_a_publication_date 
    CHECK (type <> 'book' OR publication_date IS NOT NULL); 

... и я подозреваю, что это только верхушка айсберга!

Это моя надежда, что после написания нескольких сот таких ограничений дизайнера может иметь вторую мысль о всех этих обнуляемых колоннах :)

1

Я не найти необходимость объединения таблиц особенно утомительные; Я бы взял здесь более нормализованный подход.

0

То, что я делал в прошлом, это использование подкатегорий: наличие одной таблицы со всеми общими полями внутри нее, а затем несколькими таблицами, которые могут иметь отношение «нуль-один» к « ядро ".

Приведенный ниже пример похож на то, что мы используем «в дикой природе»; она в основном строит иерархическую структуру данных, в которой каждый узел может быть папка или документ:

 
CREATE TABLE Node (
    Id int identity primary key, 
    ParentId int null references Node.ParentId, 
    Name varchar(50) not null, 
    Description varchar(max) null 
) 

CREATE TABLE Doc (
    Id int primary key references Node.Id, 
    FileExtension char(3) not null, 
    MimeType varchar(50) not null, 
    ContentLength bigint not null, 
    FilePathOnDisk varchar(255) 
) 

CREATE TABLE Folder (
    Id int primary key references Node.Id, 
    ReadOnly bit not null 
) 

Так что ваш GetFolder sproc будет делать:

 
SELECT n.Id, n.ParentId, n.Name, n.Description, f.ReadOnly 
FROM Node n 
JOIN Folder f ON n.Id = f.Id 
WHERE f.Id = @Id 

Это переводит довольно красиво в наследство на основе классов:

 
public class Folder : Node 
{ 
    public bool IsReadOnly { get; set; } 
    ...etc 
} 
7

Советуем, чтобы начать с разработки базы данных правильно, то есть с использованием нормализации, чтобы таблицы содержали только данные об одной вещи (книге, журнале, et c.) и что атрибуты хранятся в правой таблице.

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

Создайте одну таблицу, которая будет содержать общие атрибуты для всех ссылок.

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

Другая проблема заключается в том, будет ли у вас много ссылок на одно произведение, например. сотни ссылок на конкретный журнал. Тогда нормализация предложит вам таблицу, в которой хранятся журналы (название, автор, журнал), таблица, содержащая справочную информацию, которая относится к журналам (статья, страница), и другая таблица, которая содержит данные, общие для всех ссылок (дата ссылки, тип ссылки).

2

Все зависит от того, сколько разных полей и размеров полей у вас есть ограничение на общий размер строки (это можно игнорировать до некоторой степени, зная, что все поля никогда не будут заполнены, но один раз вы попадаете туда, где страницы слишком широкие, фактическое хранилище в datbase заканчивается расщеплением информации, требующей поиска, дольше. Поэтому, если информация небольшая и (это важно) вряд ли сильно изменится (это будет редкое событие необходимо добавить новый тип информации, который еще не рассмотрен), тогда одна таблица является лучшим маршрутом. Если таблица будет слишком широкой или если она будет подвержена многим возможным изменениям в типе данных, которые необходимо сохранить, то таблица spearate будет лучшим подходом, хотя всегда будет сложнее запросить правильно. Если вы часто хотите запрашивать несколько типов ссылок одновременно, t он большой стол является более эффективным подходом. Если вам обычно нужно только захватить один за раз, вы теряете очень мало с точки зрения эффективности при объединении.

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

Одной из проблем с наличием отдельных таблиц является то, что вы не знаете до запуска времени, к какой из таблиц вам нужно присоединиться. Это ставит вас в сферу динамического SQl, который я не поклонник (для обеспечения безопасности, эффективности и обслуживания) или заставляет вас делать левые соединения с таблицами, которые вам могут понадобиться или не понадобиться, что неэффективно.

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

-1

Как насчет обоих? Имейте торт и съешьте его тоже!

Есть еще один вариант где-то между «одной большой таблицей» и «полностью нормализованной» БД, которая действительно сочетает в себе лучшее из обоих миров: вы можете использовать что-то, называемое materialized views, которые похожи на виды, поскольку они столь же гибкие и вы запрашиваете столько таблиц, сколько необходимо, настраиваете все соединения и т. д., но они также похожи на таблицы, в которых результаты фактически хранятся в таблице.

Приятная вещь в этом заключается в том, что после того, как вы настроите это и решите, когда оно будет обновлено (каждый раз, когда одна из основных таблиц меняется или, может быть, только один раз в сутки), вы больше не беспокоитесь об этом , Вы можете запросить материализованное представление, как если бы это была одна большая таблица (потому что это так), и производительность будет быстрой (быстрее, чем использование оператора select, который стоит за ним). Самое главное, что у вас нет головной боли при сохранении целостности данных. Это то, что DB обрабатывает.

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

0

Olavk делает хорошие очки, а Кори дает подробное объяснение. Чтение информации Кори, тем не менее, дает мне заключение Олавка. Имейте в виду, что в зависимости от того, что вы делаете с информацией, вы можете завершить 2-этап вашего запроса. Найдите элемент, затем для каждой ссылки сделайте прямой выбор того, что представляет интерес.

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

хранилищ данных :)

0

У меня было обсуждение этих вопросов, некоторое время назад с моим начальником. Конечно, я не мог доказать, что «иерархический многотабличный подход» (см. olavk's answer) лучше, но я это почувствовал! Я всегда выбирал бы этот метод. Одна корневая таблица со всеми полями, которые имеют сущности, и 1-1 дочерних таблиц с полями, которые у них отсутствуют. Если этот подход может быть расширен до большего числа дочерних таблиц, пока бизнес-логика и другие объекты будут иметь что-то из этого. То есть, я не думаю, что нужно переходить за борт с этим.

Я также против создания отдельных «дочерних» таблиц без корневой таблицы, где каждая таблица имеет копию тех же полей. Я думаю, что Corey's answer предлагает такой подход, как пример плохой модели с несколькими таблицами, и он тоже критикует его. Я хотел бы добавить, что необходимость писать объединения не является основной проблемой. Это не проблема, так как большинство запросов к базе данных имеют много соединений, и это нормально. Трудно создавать отношения с другими таблицами - вам всегда нужен Id и TypeId, чтобы узнать, к какой таблице привязаны к нему. В случае корневой таблицы вам нужен только идентификатор.

Смежные вопросы