Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Базы";
Текущий архив: 2007.04.08;
Скачать: [xml.tar.bz2];

Вниз

Иерархия объектов в БД   Найти похожие ветки 

 
jack128 ©   (2007-01-15 18:01) [0]

Есть некая иерархия объектов.

TParent = class
 property Prop1;
 property Prop2;
 property Prop3;
end;

TChild1 = class(TParent)
 property C1Prop1;
 property C1Prop2;
end;

TChild2 = class(TParent)
 property C2Prop1;
 property C2Prop2;
end;


Вот всё это дело должно хранится в БД. Как?
A)
в одну таблицу запихнуть все поля в одну таблицу:
Prop1 Prop2 Prop3 C1Prop1 C1Prop2 C2Prop1 C2Prop2
соответственно часть полей будет всегду null"евыми. Неприятно это выглядит. С другой строны - все запросы - просты как валенок.
B)
Все свойства из TParent хранить в одной таблице, а для каждого наследника нарисовать доп. таблицу, в которой хранить специфические свойства + ссылка на базовую таблицу. Получаем тучу left join"ов если нужно получить разом все объекты(а такая необходимость есть) + для усложняется сохранение объектов в БД. Вобщем грустно это..
С)
На каждый Child - класс своя таблица. Типа
Child1Tlb (Prop1, Prop2, Prop3, C1Prop1, C1Prop2)
Child2Tlb (Prop1, Prop2, Prop3, C2Prop1, C2Prop2).
Ну это вообще не вариант - на самом деле, так как нужны FK на TParent.

Собственно как это обычно реализуется?


 
jack128 ©   (2007-01-15 18:01) [1]

FB1.5, если это что то даст :-)


 
Johnmen   (2007-01-15 18:08) [2]

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


 
jack128 ©   (2007-01-15 18:30) [3]

Johnmen   (15.01.07 18:08) [2]
Если у чилдов есть свои чилды, то м.б. стОит посмотреть на деревянную структуру, впихнутую в одну таблицу.

В смысле??  Это иерархия классов, а не объектов. И потом как разные классы в одну таблицу запихать?  Либо вариант A, либо общие свойства в отдельные поля таблицы, а остально в блоб пихнуть.. А внуков у TParent нету.

Johnmen   (15.01.07 18:08) [2]
Если нет, то однозначно "нарисовать доп. таблицу".

Хм.  "По хорошему" наверно так и надо.. Но геморойно оно как то...
А есть по первому варианту возражения - если учесть, что кол-во полей <20 и расширение маловероятно?


 
Johnmen   (2007-01-15 18:37) [4]


> jack128 ©   (15.01.07 18:30) [3]
> В смысле??  Это иерархия классов, а не объектов.


Ну для БД то какая разница, как это называть? Или от названия что-то принципиально различно с т.з. БД?

По первому нет возражений. Разве что поля для каждого класса будут обозначать что-то именно ему присущее. Тогда их кол-во = макс. количеству св-в к.-л. класса. Соот-но разрежение минимально...


 
Ega23 ©   (2007-01-15 18:38) [5]

Мы с Alkid"ом следующую реализацию сделали:
1. Есть "деревянная" таблица Classes (ClassID, ParClassID, ClassName, Тип абстрактный/нет, ClassTableName, ClassSPName (только для реальных классов), остальные свойства)
2. К ней дочерними таблицами идут: ClassFields (ID, ClassID (для какого класса), OrgClassID (от какого класса унаследовано),  FieldTypeID, FieldName); ClassEvents, ClassMethods.
3. Каждый неабстрактный класс имеет собственную таблицу с именем, указанном в Classes и с полями, перечисленными в ClassFields для данного ClassID.

Фактически, каждая запись в таблице реального класса - суть экземпляр объекта, а значения его полей - это его свойства.

Ну и всё это дело обёрнуто системой SP.


 
PEAKTOP ©   (2007-01-15 18:49) [6]

На FireBird v1.5

CREATE TABLE OBJECTS(
 ID VARCHAR(50) NOT NULL PRIMARY KEY, --Имя объекта
 PARENT_ID VARCHAR(50) -- Объект родитель, NULL соответственно для TObject
);

CREATE TABLE PROPERTIES(
 OBJECT_ID VARCHAR(50) NOT NULL FOREIGN KEY REFERENCES OBJECTS(ID), -- Объект, к которому относится свойство
 ID  VARCHAR(50) NOT NULL PRIMARY KEY, -- Имя свойства
 PROP_VALUE VARCHAR(255)  -- Значение свойства
);

CREATE PROCEDURE GET_OBJECT_PROP( -- Получает список свойств по имени объекта по иерархии
 Q_OBJECT_ID VARCHAR(50) -- Имя объекта
)RETURNS(
 OBJECT_ID VARCHAR(50), -- Объект, от которого унаследовано свойство
 ID  VARCHAR(50), -- имя свойства
 PROP_VALUE VARCHAR(255) -- занчение
)AS
BEGIN

 OBJECT_ID = :Q_OBJECT_ID;
 WHILE(:OBJECT_ID IS NOT NULL)DO
   BEGIN
   FOR
     SELECT
P.ID, P.PROP_VALUE
     FROM   PROPERTIES P
     WHERE (P.OBJECT_ID = :OBJECT_ID)
     INTO    :ID, :PROP_VALUE
   DO
     SUSPEND
;
   SELECT FIRST 1 O.ID FROM OBJECTS O WHERE(O.PARENT_ID = :OBJECT_ID)INTO :OBJECT_ID;
   END
END



Соответсвенно, вызов

 SELECT * FROM GET_OBJECT_PROP("TMyObject") ORDER BY ID;


 
PEAKTOP ©   (2007-01-15 18:55) [7]

Сорри, ошибка вышла. Надо:

CREATE PROCEDURE GET_OBJECT_PROP( -- Получает список свойств по имени объекта по иерархии
Q_OBJECT_ID VARCHAR(50) -- Имя объекта
)RETURNS(
OBJECT_ID VARCHAR(50), -- Объект, от которого унаследовано свойство
ID  VARCHAR(50), -- имя свойства
PROP_VALUE VARCHAR(255) -- значение
)AS
BEGIN

OBJECT_ID = :Q_OBJECT_ID;
WHILE(:OBJECT_ID IS NOT NULL)DO
  BEGIN
  FOR
    SELECT
P.ID, P.PROP_VALUE
    FROM  PROPERTIES P
    WHERE (P.OBJECT_ID = :OBJECT_ID)
    INTO    :ID, :PROP_VALUE
  DO
    SUSPEND;
  SELECT FIRST 1 O.PARENT_ID FROM OBJECTS O WHERE(O.ID = :OBJECT_ID)INTO :OBJECT_ID;
  END
END



 
Ega23 ©   (2007-01-15 18:59) [8]


> PEAKTOP ©   (15.01.07 18:49) [6]


Коль по строкам сравниваешь, нехило к Upper всё привести... ИМХО...


 
jack128 ©   (2007-01-15 19:04) [9]

Johnmen   (15.01.07 18:37) [4]
Ну для БД то какая разница, как это называть?

Хм. Можно пример? Что то я вообще не понимаю о чем ты говоришь.

Johnmen   (15.01.07 18:37) [4]
Тогда их кол-во = макс. количеству св-в к.-л. класса. Соот-но разрежение минимально...

Хм, ты с вариантом С не спутал?  В первом варианте как раз много разреженных данных. В третьем их нет вообще, но это бред. Все FK нужно будет руками поддерживать.

Ega23 ©   (15.01.07 18:38) [5]
Фактически, каждая запись в таблице реального класса - суть экземпляр объекта, а значения его полей - это его свойства

так я не понял, у вас значения унаследованных свойств в этой же таблице хранятся?  А FK как поддерживаются?  Руками через SP?  Ну ты же понимаешь, что это совсем грусно. Если таки есть таблицы для предков, то фактически B + свои метаданные.

PEAKTOP ©   (15.01.07 18:49) [6]
Это ты Тенцера почитал?
Спасибо, но я уже переболел этой универсальной структурой БД ;-) Это структура безумно избыточна.  Ты хотя бы понятие классов ввёл...


 
Ega23 ©   (2007-01-15 19:09) [10]


> так я не понял, у вас значения унаследованных свойств в
> этой же таблице хранятся?  А FK как поддерживаются?  Руками
> через SP?  Ну ты же понимаешь, что это совсем грусно. Если
> таки есть таблицы для предков, то фактически B + свои метаданные.
>


Блин... Так долго всё объяснять надо будет...
Я, вполне вероятно, в пятницу в Москве появлюсь, на собеседование поеду...  :)
Есть маза вечером пересечься. Заодно все кишки и расскажу...   :о)


 
atruhin ©   (2007-01-15 19:10) [11]

> [5] Ega23 ©   (15.01.07 18:38)

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


 
atruhin ©   (2007-01-15 19:14) [12]

> [9] jack128 ©   (15.01.07 19:04)
> А FK как поддерживаются?

ФК в данной структуре поддерживаются на уровне БД, тем более, что для IB/FB сделать поддержку FK на
процедурах/тригерах невозможно (если я чего нового не упустил).
Например: общая для всех объектов таблица связанна 1:1 с доп таблицей (таблица полей класса),
а FK при необходимости создается на эту доп. таблицу.


 
Ega23 ©   (2007-01-15 19:16) [13]


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


Совершенно верно. Более того, при таком подходе ещё и as работает.
Т.е. можно получить данные всех объектов-потомков Custom-класса.
Только триггеры мы не используем: во-первых, предполагается, что прямых вставок в таблицы не бывает, всё только через клиентское приложение (а там нормально и хп оборачивается), а во-вторых, у шефа откуда-то предубеждение насчёт триггеров...   :о)


 
Ega23 ©   (2007-01-15 19:19) [14]


> Например: общая для всех объектов таблица связанна 1:1 с
> доп таблицей (таблица полей класса),
> а FK при необходимости создается на эту доп. таблицу.


Так точно. На уровне базового класса есть поле ObjID - суть уникальный идентификатор объекта в рамках всех объектов всех классов. Далее, есть таблица ObjectClasses (ClassID FK на Classes и ObjID)/ Каждая запись в классовой таблице ссылается на ObjectClasses.ObjID.
Т.е. ObjectClasses фактически список всех объектов по всем классам.


 
Johnmen   (2007-01-15 19:25) [15]


> atruhin ©   (15.01.07 19:14) [12]
> ...тем более, что для IB/FB сделать поддержку FK на
> процедурах/тригерах невозможно (если я чего нового не упустил).


Именно на триггерах ФК и реализованы!


 
Ega23 ©   (2007-01-15 19:29) [16]


> Именно на триггерах ФК и реализованы!


Здесь другое, здесь за ссылочной целостностью самому придётся следить. Особенно, если в свойствах твоего класса есть ссылка на другой класс. Там если городить FK средствами СУБД - просто умереть можно. Да и расширяемости не получишь простой.


 
atruhin ©   (2007-01-15 19:30) [17]

Нда, до боли похоже, хоть и шли разными путями! :)
Мы в прикладном ПО не использем интерпретаторов, пока нет времени, но есть идеи,
но все равно так как описания классов храняться в БД, иконки класса, форматы ввода полей, ограничения,
рускоязычное название и т.п. Активно используем универсальные редакторы для простых справочников,
универсальный эксплорер БД, он же обеспечиват выбор любых объектов для ссылочных полей,
плюс еще ряд специализированных визуальных и невизуальных компонентов.
В итоге примерно полгода затраченные на доводку структуры БД, написание компонентов и
каркаса программы, окупаются с лихвой.


 
Ega23 ©   (2007-01-15 19:31) [18]


> В итоге примерно полгода затраченные на доводку структуры
> БД, написание компонентов и
> каркаса программы, окупаются с лихвой.
>


Это точно. Вот только как сложно это было с самого начала шефу объяснить...   :о)


 
atruhin ©   (2007-01-15 19:33) [19]

> [15] Johnmen   (15.01.07 19:25)
> Именно на триггерах ФК и реализованы!

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


 
Johnmen   (2007-01-15 20:31) [20]


> atruhin ©   (15.01.07 19:33) [19]


> На тригерах то на тригерах, только эти внутренние тригера
> работают ВНЕ контекста транзакций.

ВНЕ контекста только генераторы.
ВСЁ остальное в контексте. Да по-другому и быть не может.

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


В данном случае "внутренние" НИЧЕМ не отличаются от "пользовательских".


> Ega23 ©   (15.01.07 19:29) [16]
> > Именно на триггерах ФК и реализованы!
> Здесь другое, здесь за ссылочной целостностью самому придётся
> следить.


Ес-но. И это слежение ничем не хуже, если не самому.


 
atruhin ©   (2007-01-15 20:42) [21]

> [20] Johnmen   (15.01.07 20:31)
> ВНЕ контекста только генераторы.
> ВСЁ остальное в контексте. Да по-другому и быть не может.

Вы неправы. Простой пример:
1. Есть таблицы Master, Detail
2. Пользователь_1 открыл Транзакцию_1
3. Пользователь_2 открыл Транзакцию_2
4. Пользователь_1 проверяет, что для записи X Master таблицы, нет записей в таблице Detail, и удаляет ее
5. Пользователь_2 вставляет в таблицу Detail запись ссылающуюся на запись X таблицы Master, происходит проверка в тригере,
тригер видит версию записи на начало транзакции, запись вставляется.
6. Транзакции подтверждаются, целостность нарушена.


 
atruhin ©   (2007-01-15 20:43) [22]

Вообще это общеизвестный факт работы версионников.


 
Johnmen ©   (2007-01-15 20:56) [23]


> atruhin ©   (15.01.07 20:42) [21]

Данный пример никакого отношения к "ВНЕ контекста" не имеет. А лишь иллюстрирует один из способов изоляции транзакций.

> atruhin ©   (15.01.07 20:43) [22]
> Вообще это общеизвестный факт работы версионников.

Просьба указать источник сего факта. М.б. я что-то пропустил в своё время...


 
Petr V. Abramov ©   (2007-01-15 22:37) [24]

> jack128 ©  
Женя, ты хочешь сделать хранение dfm-ников в БД?
ну так и скажи и переведи в потрепаться, там идей ьрльше будет, потому что идея хорошая, и не ты первый придумал и  не последний


 
jack128 ©   (2007-01-15 23:01) [25]

Petr V. Abramov ©   (15.01.07 22:37) [24]
Не, нафиг мне эти dfm/скрипты.  До скриптов в базе я еще не дорос. Нет необходимости.

Ну вобщем выводы сделаны. Всем спасибо.

ЗЫ а на счет встретится - это можно.


 
atruhin ©   (2007-01-16 13:45) [26]

> [23] Johnmen ©   (15.01.07 20:56)
> Данный пример никакого отношения к "ВНЕ контекста" не имеет.
> А лишь иллюстрирует один из способов изоляции транзакций.

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

Источник не укажу, данный вопрос неоднократно поднимался на разных форумах, и обсуждался,
в том числе с разработчиками FB. Например SQL.RU


 
Sergey13 ©   (2007-01-16 13:57) [27]

> [21] atruhin ©   (15.01.07 20:42)
> 6. Транзакции подтверждаются, целостность нарушена.
ИМХО, если бы ОБЕ транзакции в этом случае подтвердились (закоммитились) то транзакции были бы вообще не нужны, как ненужный рудимент.


 
evvcom ©   (2007-01-16 15:36) [28]

> [13] Ega23 ©   (15.01.07 19:16)
> а во-вторых, у шефа откуда-то предубеждение насчёт триггеров...

У моего шефа тоже были предубеждения. Я их поломал. :)

> [5] Ega23 ©   (15.01.07 18:38)
> 3. Каждый неабстрактный класс имеет собственную таблицу с именем

Зачем? Это для хранения значений? По-моему вполне достаточно одной таблицы.

> [22] atruhin ©   (15.01.07 20:43)
> Вообще это общеизвестный факт работы версионников.

Я бы это назвал общеизвестным фактом работы кривых рук. Я, конечно, не знаю FB, а в Оракле для этого есть инструменты.

> [25] jack128 ©   (15.01.07 23:01)
> ЗЫ а на счет встретится - это можно.

Ах вот что послужило причиной пятничной MMP :)

> Если вы не согласны приведите пример корректно работающего
> тригера т.е. возможность обеспечить
> ссылочную целостность обычными тригерами

Для Оракла пойдет?


 
atruhin ©   (2007-01-16 15:46) [29]

> [27] Sergey13 ©   (16.01.07 13:57)
> ИМХО, если бы ОБЕ транзакции в этом случае подтвердились
> (закоммитились) то транзакции были бы вообще не нужны, как
> ненужный рудимент.

Не понял, можно пояснить? Естественно они подтвердяться, тригера то без ошибок отработали!

> [28] evvcom ©   (16.01.07 15:36)
> Для Оракла пойдет?

Я не знаю Оракл, но наверное нет, насколько слышал ни Оракл, ни MSSQL не являются чистыми версионниками,
а описанная мною ситуация возникает только для версионников. Для блокировочников реализация ссылочной целостности
"вручную на тригерах" работает нормально.


 
evvcom ©   (2007-01-16 15:56) [30]

> [29] atruhin ©   (16.01.07 15:46)
> насколько слышал ни Оракл, ни ... не являются чистыми
> версионниками

Сейчас начнется :)
Что есть версионник, и что есть блокировочник?
Так как ты написал в [21] и Оракл поведет себя аналогично. А как же ты хочешь изменять запись не блокируя ее? В винде тоже есть объекты синхронизации, они блокируют (приостанавливают) работу потока, винда тоже блокировочник?


 
Sergey13 ©   (2007-01-16 15:57) [31]

> [29] atruhin ©   (16.01.07 15:46)

Я просто не въехал, что вы про тригерную СЦ уже обсуждаете. 8-)
Я думал про простую форинкейную. Сори.


 
atruhin ©   (2007-01-16 16:22) [32]

> [30] evvcom ©   (16.01.07 15:56)
Сейчас начнется :)

Только не с моей стороны, я кроме FB ни с чем не знаком, по крайней мере настолько чтобы спорить.

> А как же ты хочешь изменять запись не блокируя ее?

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


 
evvcom ©   (2007-01-16 16:27) [33]

> [32] atruhin ©   (16.01.07 16:22)
> В этом и особенность "чистых" версионников что они ее не
> блокирут, изменяй хоть в пятером одновременно

И в результате кто последний тот и папа? Что-то мне тогда даже пробовать не хочется "чистого" версионника :)


 
atruhin ©   (2007-01-16 17:01) [34]

> И в результате кто последний тот и папа?

Обычно да, но есть средства блокировать запись.

> Что-то мне тогда даже пробовать не хочется

Зря, если бы это было так плохо, то версионность не вводили бы во все сервера.


 
evvcom ©   (2007-01-16 17:49) [35]

> [34] atruhin ©   (16.01.07 17:01)
> но есть средства блокировать запись

Так все-таки они есть!

> то версионность не вводили бы во все сервера

так "версионность" или "чистую версионность"? Ты уж определись с понятиями. И судя по тому, что все-таки

> есть средства блокировать запись

то твое

> [32] atruhin ©   (16.01.07 16:22)
> В этом и особенность "чистых" версионников что они ее не блокирут

не верно? Ты ж сам себе противоречишь.


 
atruhin ©   (2007-01-16 18:45) [36]

Нет не протеворечу. Разница в том, что FB блокирует запись только по требованию,
да и появилась возможность блокировки не так давно. На эту тему много копий сломано.
И я не хочу устраивать очередную ветку какой сервер лучше, тем более это сервера
разных категорий и для разных задач.
Спор возник о конкретной особенности сервера Sergey13 © уже сказал что не так понял
вопрос и согласился. Думаю тему можно закрыть.


 
vlad-mal ©   (2007-01-18 00:12) [37]


> jack128 ©   (15.01.07 18:01) 



> Ну это вообще не вариант
> - на самом деле, так как нужны FK на TParent. Собственно
> как это обычно реализуется?


Собственно говоря, и так делается, и эдак.

Например, когда все дочерние таблички ссылаются на родителя.
Вовсе не нужно, для получений все объекты класса Parent, делать left join ко всей цепочке деток (так и недолго Out of Memory словить) - выбирайте пары: "идентификатор объекта" + "идентификатор класса" из таблички Parent.
Идентификатор класса - это признак в Parent - табличке, означающий, что объект Parent на самом деле является объектом класса Child_NN.
А чтобы получить данные конкрентного класса, по полученному Id в нужный момент выбирайте данных простым join цепочки Parent...Child_NN.

Или, если нужны данные свойств классов сразу, а не их адентификаторы - пусть все будет в одной табличке. В Delphi, хвала аллаху, одинарное наследование. И ничего страшного, если она почти вся будет состоять из Null-ов. Гигабайты ноне дешевы. Полей в одной табличке FB можно разместить о-о-о-чень много. И опять-таки, в этой табличке заведите признак, к какому классу относится эта запись. Тогда выборка нужной коллекции будет проста и быстра.

Как пример, можете посмотреть структуру базы, генерируемую Bold for Delphi. Там можно настраивать объектно-реляционное отображение несколькими вариантами.

Или загрузите себе InstantObject с http://sourceforge.net/project/showfiles.php?group_id=94747 - бесплатно, просто и обычно сразу понятно  (по сравнению с Bold).

То, что предлагает
> PEAKTOP ©   (15.01.07 18:49) [6]
- на самом деле вещь довольно опасная. Время выборки коллекции будет линейно от числа свойств класса (т.е. числу соединений). Довольно просто провести эксперимент, создав большую коллекцию объектов с большим числом свойств, и выбрать все. Будет не весело.



Страницы: 1 вся ветка

Форум: "Базы";
Текущий архив: 2007.04.08;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.58 MB
Время: 0.037 c
3-1169389719
Дед Маздай
2007-01-21 17:28
2007.04.08
Аналог Old и New в MS SQL


2-1173797301
koha
2007-03-13 17:48
2007.04.08
Интересный вопрос


15-1173765400
SerJaNT
2007-03-13 08:56
2007.04.08
phpMySQLAdmin


15-1173770951
Java
2007-03-13 10:29
2007.04.08
Проблема с SimpleDateFormat(Java)


15-1174058986
oldman
2007-03-16 18:29
2007.04.08
Пожелай мне удачи в бою...





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский