LINUX.ORG.RU

Проектирую схему БД

 , ,


0

0

tl;dr как лучше делать подчинение одной таблицы многим таблицам (но для одной записи — один владелец). Много опциональных FK или один INTEGER с ручной проверкой?

Вот к примеру, есть у меня таблицы с разными «сущностями», и надо ко всем дать возможность «прикреплять документы». Делаю так:

CREATE TABLE objects (object INTEGER PRIMARY KEY);
CREATE TABLE tasks (task INTEGER PRIMARY KEY);
CREATE TABLE records (record INTEGER PRIMARY KEY);

CREATE TABLE documents (
    object          INTEGER, -- возможный владелец
    task            INTEGER, -- возможный владелец
    record          INTEGER, -- возможный владелец, но...
    side            INTEGER NOT NULL DEFAULT 0, -- ... у него несколько "сторон"

    -- тут собственно описание документа
    name            TEXT NOT NULL, -- неуникальное
    originalName    TEXT NOT NULL, -- неуникальное
    relativePath    TEXT NOT NULL, -- неуникальное
    position        INTEGER NOT NULL, -- (!) уникальное, для сортировки

    -- гарантируем уникальность по владельцу и позиции
    UNIQUE (object, task, record, side, position),

    FOREIGN KEY (object)
        REFERENCES objects (object)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (task)
        REFERENCES tasks (task)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (record)
        REFERENCES records (record)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);
Когда один из владельцев NOT NULL, остальные по-любому NULL (можно добавить CHECK, пока не стал заморачиваться).

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

У всех трех владельцев сквозная нумерация, так что PRIMARY KEY уникален внутри всех трех таблиц. Выборка документов планируется только внутри владельца, то есть выяснять к какой таблице относится ключ владельца — ненужно. Т.о. можно было бы сделать только одно поле владельца «owner INTEGER NOT NULL» без FOREIGN KEY (оставив костыль side для record) и самому реализовать каскады. Какой подход более правильный?

Теперь второй вопрос, чем-то схожий. У objects есть значения атрибутов в отдельной таблице (схема типизации в таблицах objectTypes и objectAttributes, здесь не привожу):

CREATE TABLE objectValues (
    object          INTEGER NOT NULL, -- FK
    attribute       INTEGER NOT NULL, -- FK

    objectValue     INTEGER, -- или это
    scalarValue     BLOB, -- или это

    PRIMARY KEY (object, attribute),
    FOREIGN KEY (object)
        REFERENCES objects (object)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (attribute)
        REFERENCES objectAttributes (attribute)
        ON DELETE CASCADE
        ON UPDATE CASCADE,
    FOREIGN KEY (objectValue)
        REFERENCES objects (object)
        ON DELETE RESTRICT
        ON UPDATE CASCADE
);
Поскольку значения атрибутов могут быть как простого, так и объектного типа, приходится делить их на два поля, чтобы сделать RESTRICT CONSTRAINT для objectValue. Есть еще пара похожих мест в схеме, вопрос вот в чем: так ли это нужно, не проще ли оставить единое поле value, а в нужных местах прослойки самому реализовать referential integrity? Насколько эффективнее SQL выполняет эту задачу, он также просто селект делает в parent key, или там шустрый magic?

И побочный вопрос: если запись однозначно идентифицируется по полю TEXT, нужно ли создавать синтетический первичный ключ, или можно спокойно использовать TEXT как FK?

Тогда уж не «INTEGER с ручной проверкой», а constraint который сам проверит, что установлен один и только один опциональный FK

например, для SQLServer. Допустим, File может принадлежать или Group или User

CREATE TABLE dbo.[Group]
(
    ID int NOT NULL CONSTRAINT PK_Group PRIMARY KEY,
    Name varchar(50) NOT NULL
);

CREATE TABLE dbo.[User]
(
    ID int NOT NULL CONSTRAINT PK_User PRIMARY KEY,
    Name varchar(50) NOT NULL
);

CREATE TABLE dbo.File
(
    ID int NOT NULL CONSTRAINT PK_File PRIMARY KEY,
    OwnerGroup int NULL
      CONSTRAINT FK_File_Group FOREIGN KEY REFERENCES dbo.[Group] (ID),
    OwnerUser int NULL
      CONSTRAINT FK_File_User  FOREIGN KEY REFERENCES dbo.[User]  (ID),
    Subject varchar(50) NULL,
    CONSTRAINT CK_File_GroupUser CHECK (
      CASE WHEN [Group] IS NULL THEN 0 ELSE 1 END +
      CASE WHEN [User]  IS NULL THEN 0 ELSE 1 END = 1
    )
);

но если система ничего не знает об sql и не можешь поставить constraint, то можно заюзать «эмуляцию наследования»:

если table1 extends table2,table3, то

table1(id, table1-specific stuff)
table1_table2(id, table2-specific stuff)
table1_table3(id, table3-specific stuff)

если select по table1_table2 выдал пустой ответ, то table1 не extends table2

а в нужных местах прослойки самому реализовать referential integrity

чем больше дашь информации движку БД, тем быстрее он будет работать :3

stevejobs ★★★★☆
()
Последнее исправление: stevejobs (всего исправлений: 1)
Ответ на: комментарий от stevejobs

А это нормально будет, что у меня несколько nullable полей заполненных всегда на 25-30%? Оверхед оправдан, или он не оверхед вовсе?

Прослойка само собой знает об SQL, схема статичная, у меня как бы схема на схеме получается. Я для типов объектов задаю типизированные атрибуты и на уровне прослойки проверяю, что значение, записываемое в атрибут объекта, соответствует типу, указанному в дексрипторе атрибута для этого типа объекта, как-то так. Прослойка также позволяет добавить/удалить тип объекта, его атрибут и пр., выполняя нужные преобразования для уже имеющихся объектов. Получается как бы прототипная модель для пользователя, плюс «еще документы можно прикреплять» :3

Про чек 1+0+0=1 я тоже подумал, значит не все потеряно :)
Спасибо!

arturpub ★★
() автор топика
Ответ на: комментарий от stevejobs

Я пробовал через ALTER TABLE делать, но блин, там столько мудежа и ограничений со стороны движка, что проще оверхед огрести. Вот пытаюсь как-то сгладить. В принципе, нагрузка на объекты и атрибуты будет небольшая, я больше за финансовую часть переживаю. Думаю держать все в памяти и не парить себе мозг, щас такие компы, что редкая база 3 раза в память не влезет.

arturpub ★★
() автор топика
Ответ на: комментарий от arturpub

что редкая база 3 раза в память не влезет

щито? эээ. У меня самая частая проблема была в том, что джойн не влезал в RAM и начинался ад.

stevejobs ★★★★☆
()
Ответ на: комментарий от stevejobs

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

arturpub ★★
() автор топика
Ответ на: комментарий от arturpub

что реляционная модель, особенно моя, думаю не катит

может тогда nosql в бэкенде, а целостность и прочие ништяки напишешь сам в прослойке? :)

stevejobs ★★★★☆
()
Ответ на: комментарий от stevejobs

А вообще интересно, что за джойны такие, типа кросс или как его там? Что это за данные на выходе и кто их смотрит? Это особенность реляционной модели или объективная необходимость столько памяти сожрать при вычислениях?

arturpub ★★
() автор топика
Ответ на: комментарий от stevejobs

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

Может тогда sqlite как (id int primary key, data blob) чисто использовать?

arturpub ★★
() автор топика

У нас в проде вообще нет FK, скрипты, накатывающие связи, лежит отдельно в liquibase и накатываются только в dev. Количество рестрикшенов, чеков и внешних ключей минимально, проверки реализованы не на стороне БД. Пичаль, но как выяснилось, жить можно.

Insomnium ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.