Подтвердить что ты не робот

Один стол или много?

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

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

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

Update:

Любые другие взгляды на это? Я надеялся получить простой ответ, сказав, что конкретный метод определенно считается "лучшим", но, как обычно, все не так просто. Параметр "Наследование на одном столе" выглядит довольно интересным, но на нем мало информации, что я могу найти очень легко - я могу опубликовать еще один вопрос на этом сайте об этом.

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

"Жизнь проще с одной большой таблицей": я видел естественное следствие этого, став столбом более 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 );

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

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

Давайте скажем, что my-one-big-table выглядит так:

1 id
2 типа
3-полевые общие книги и журналы
4 полевые книги - 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

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

Ответ 7

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

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

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

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

Ответ 8

Есть еще один вариант: не один, который я бы полностью одобрил, но это еще один вариант:

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

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.

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

Ответ 9

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

Ответ 10

одна таблица и поле типа будут моим предложением

Ответ 12

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

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

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
}

Ответ 13

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

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

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

Ответ 14

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

Я также против создания отдельных "дочерних" таблиц без корневой таблицы, где каждая таблица имеет копию тех же полей. Я думаю, что ответ Кори предлагает такой подход, как пример плохой модели с несколькими таблицами, и он тоже его критикует. Я хотел бы добавить, что необходимость писать объединения не является основной проблемой. Это не проблема, так как большинство запросов к базе данных имеют много соединений, и это нормальная вещь. Это затрудняет создание отношений с другими таблицами - вам всегда нужен Id и TypeId, чтобы узнать, какая таблица ссылается на него. В случае с корневой таблицей вам нужен только идентификатор.

Ответ 15

Как обоим? Иметь торт и съесть его!

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

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

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