LINUX.ORG.RU

Как это запихнуть в реляционную базу данных

 


1

1

Есть классы, например дом, автомобиль.

У классов есть атрибуты, например площадь, количество дверей у класса дом, вес, количество дверей у класса автомобиль.

Классы и их атрибуты могут добавляться/удаляться/изменяться в зависимости от фазы луны.

Есть объекты, например дом-1 класса дом, площадь 100 м*м, количество дверей 5, автомобиль-1 класса автомобиль, вес 2 т, количество дверей 5. Объекты могут добавляться/удаляться/изменяться в зависимости от фазы луны.

Нужно искать объекты по атрибутам. Запрос «площадь 100 м*м» должен возвращать дом-1. Запрос «вес 2 т» должен возвращать автомобиль-1. Запрос «количество дверей 5» должен возвращать дом-1 и автомобиль-1.

Как это по умному запихнуть в mysql?

Думал как-то так:

classes
------------------
cid|cname

attributetypes
------------------
atid|atname|cid

objects
------------------
oid|oname|cid

attributes
------------------
aid|avalue|oid|atid

Но тут например можно добавить объекту одного класса атрибут от другого. Вообще не покидает ощущение, что я ректально лечу кариес.



Последнее исправление: suuaq (всего исправлений: 3)
Ответ на: комментарий от dmitry_vk

давно не смотрел что там нового в постгре. оно с json умеет работать как с xml полями? т.е. xpath, разворачивание xml в табличное представление и т.п.?

exception13 ★★★★★
()

http://en.wikipedia.org/wiki/Entity–attribute–value_model например. Практически то же самое, можно еще запулить отдельную таблицу cid|atid. Но вообще да, я бы такое юзал только если очень безальтернативно это надо в таблицах реляционной базы хранить. Постгрес с жсоном - одобряю.

arkhnchul ★★★
()

Запрос «количество дверей 5» должен возвращать дом-1 и автомобиль-1.

Но тут например можно добавить объекту одного класса атрибут от другого.

Непонятно, как ты собираешься выбирать объекты разных классов одним запросом, если у разных классов не будет общих атрибутов. Тупо матчить по имени атрибута?

Apple-ch ★★
()

Ну сделай таблицу класс, и таблицу атрибуты. Во второй таблице будут поля id класса, ключ и значения атрибута.

А потом обычным join-ом хреначить

xorik ★★★★★
()
Последнее исправление: xorik (всего исправлений: 1)

В слоне кроме json есть просто поддержка массивов и в более ранних версиях, можно их использовать. MYSQL это как бы среднее между postgresql и sqlite когда-то было, но без массивов и других фич.

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

массивами не оче удобно будет. Для ранних версий лучше уж hstore для такого.

arkhnchul ★★★
()

Если тебе по каким-то причинам нельзя просто сделать по таблице на класс + немного служебных и потом объединять результаты селектов из разных таблиц (неважно, каким образом) — да, ты ректально лечишь кариес. Особенно если изменение атрибутов достаточно часто по сравнению с поиском. Ты уверен, что нужен mysql?

По каким запросам тебе нужно быстро выбирать?

x3al ★★★★★
()

Идея правильная. Выхода два:
1) не путать разные сущности в одной таблице (дома - своей таблице, машины - в своей)
2) рулить доступностью свойств для типов объектов на прикладном уровне:
а) добавлять записи не прямыми запросами к таблицам, а через хранимые процедуры (в мускуле они есть?) - хранить в отдельной таблице «настройки» доступности свойств для типов объектов
б) делать те же проверки уже на прикладном уровне, в самой программе

bvn13 ★★★★★
()

Но тут например можно добавить объекту одного класса атрибут от другого.

Поставь проверяющий триггер.

Вообще не покидает ощущение, что я ректально лечу кариес.

Реляционные БД хорошо себя показывают в жёстких схемах. Гибкие схемы в них действительно получаются неудобными.

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

Насколько я понимаю по json полям нужно будет все равно создавать индексы. Еще могу посоветовать solr, особенно если будет поиск по тексту.

goingUp ★★★★★
()

Задачу можно решить следующим способом.

Создаем сущности «class», «attribute» и «object»:

CREATE TABLE `class`(
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `title` VARCHAR(128),
  PRIMARY KEY(`id`)
);

CREATE TABLE `attribute`(
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `title` VARCHAR(128),
  PRIMARY KEY(`id`)
);

CREATE TABLE `object`(
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `class_id` INT UNSIGNED NOT NULL,
  `title` VARCHAR(128) NOT NULL,
  PRIMARY KEY(`id`),
  FOREIGN KEY(`class_id`) REFERENCES `class`(`id`)
);

Здесь «attribute» подразумевается обычной строкой (т.е., в нашем случае, «title» может быть равен «площадь 100 м*м» или «вес 2 т»). Для приведенной задачи не критично и легко расширяемо.

Далее создаем сущности «class_attribute» (сопоставление атрибутов классам) и «object_attribute» (сопоставление атрибутов объектам):

CREATE TABLE `class_attribute`(
  `class_id` INT UNSIGNED NOT NULL,
  `attribute_id` INT UNSIGNED NOT NULL,
  FOREIGN KEY(`class_id`) REFERENCES `class`(`id`),
  FOREIGN KEY(`attribute_id`) REFERENCES `attribute`(`id`)
);

CREATE TABLE `object_attribute`(
  `object_id` INT UNSIGNED NOT NULL,
  `attribute_id` INT UNSIGNED NOT NULL,
  FOREIGN KEY(`object_id`) REFERENCES `object`(`id`),
  FOREIGN KEY(`attribute_id`) REFERENCES `attribute`(`id`)
);

Нужно проверять, чтобы в таблицу «object_attribute» не попадали атрибуты, которые не имеет класс объекта. Для этого добавляем триггеры «object_attribute_before_insert» (проверяет при добавлении атрибута объекта, имеет ли класс объекта этот атрибут) и «class_attribute_after_delete» (удаляет атрибуты объекта, если класс объекта более не имеет этот атрибут):

DELIMITER ;;

CREATE TRIGGER `object_attribute_before_insert`
  BEFORE INSERT
  ON `object_attribute`
  FOR EACH ROW BEGIN
    IF (NOT EXISTS (
        SELECT TRUE
          FROM `class_attribute`
            INNER JOIN `object`
              ON `class_attribute`.`class_id` = `object`.`class_id`
                AND `object`.`id` = NEW.`object_id`
                AND `class_attribute`.`attribute_id` = NEW.`attribute_id`
        )) THEN
      SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Object''s class doesn''t have attribute with this ID.';
    END IF;
  END;;

CREATE TRIGGER `class_attribute_after_delete`
  AFTER DELETE
  ON `class_attribute`
  FOR EACH ROW BEGIN
    DELETE FROM `object_attribute`
      WHERE `attribute_id` = OLD.`attribute_id`
        AND `object_id` IN (
            SELECT `id`
              FROM `object`
              WHERE `class_id` = OLD.`class_id`
          );
  END;;

DELIMITER ;

Для проверки заполняем таблицы исходными данными:

INSERT INTO `class` SET `title` = 'дом';
INSERT INTO `class` SET `title` = 'автомобиль';
INSERT INTO `attribute` SET `title` = 'площадь 100 м*м';
INSERT INTO `attribute` SET `title` = 'количество дверей 5';
INSERT INTO `attribute` SET `title` = 'вес 2 т';
INSERT INTO `class_attribute` SET `class_id` = 1, `attribute_id` = 1;
INSERT INTO `class_attribute` SET `class_id` = 1, `attribute_id` = 2;
INSERT INTO `class_attribute` SET `class_id` = 2, `attribute_id` = 2;
INSERT INTO `class_attribute` SET `class_id` = 2, `attribute_id` = 3;
INSERT INTO `object` SET `class_id` = 1, `title` = 'дом-1';
INSERT INTO `object` SET `class_id` = 2, `title` = 'автомобиль-2';
INSERT INTO `object_attribute` SET `object_id` = 1, `attribute_id` = 1;
INSERT INTO `object_attribute` SET `object_id` = 1, `attribute_id` = 2;
INSERT INTO `object_attribute` SET `object_id` = 2, `attribute_id` = 2;
INSERT INTO `object_attribute` SET `object_id` = 2, `attribute_id` = 3;

Выполняем запрос:

SELECT `object`.`title`
  FROM `object`
    INNER JOIN `object_attribute`
      ON `object`.`id` = `object_attribute`.`object_id`
    INNER JOIN `attribute`
      ON `object_attribute`.`attribute_id` = `attribute`.`id`
  WHERE `attribute`.`title` = ?;

с параметрами «площадь 100 м*м», «вес 2 т» и «количество дверей 5» и получаем требуемые результаты.

p97
()

Такого кажется не предлагали

сущность   | количество дверей |  площадь  | вес  | 
---------------------------------------------------
дом        |         2         | 100 кв.м. | null |
---------------------------------------------------
автомобиль |         5         |   null    | 2 т  |
---------------------------------------------------

у вас многое будет зависеть от задач, можно упор делать на производительность или на удобство работы в определённом языке/фреймворке

ya-betmen ★★★★★
()

в постгресе есть hstore, ну и json на подходе :) (как там в мускулах дела обстоят - не знаю..)

EAV - ну... в целом, решение, да.. Но столько всяких «но»..

aol ★★★★★
()

key=value

/thread

anonymous
()

Я чего-то могу не знать или ошибаться, но по-моему сюда mongodb прямо напрашивается.

BattleCoder ★★★★★
()

тебе действительно sql нужен? не пробовал в сторону монги или кассандры посмотреть?

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

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

Вообще думаю тут можно найти нормальное реляционное решение (тред не осилил, может уже нашли).

MrClon ★★★★★
()

Думал как-то так

Да, я именно так делал в аналогичной задаче.

Но тут например можно добавить объекту одного класса атрибут от другого

Ну так можно же и не добавлять :) Просто в админке при выборе атрибута показывай только допустимые.

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

А как там с производительностью? Как я понимаю, никаких индексов нет?

Без понятия. Никогда её не пробовал.

static_lab ★★★★★
()

Но тут например можно добавить объекту одного класса атрибут от другого.

Не разрешай работать с таблицами напрямую. Используй pl/sql функции. Джейсон уже посоветовали.

anonymous
()
Ответ на: комментарий от ya-betmen

сущность | количество дверей | площадь | вес |

Mysql уже научился в sparse columns? Если нет, то это решение хуже, чем EAV.

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

1. Я и не говорил что это лучшее решение
2. Почему оно хуже чем EAV, учитывая, что известно только то что ТС хочет запихнуть данные в мускуль?
3. Кажется innoDB такое умел

ya-betmen ★★★★★
()
Ответ на: комментарий от x3al

Где ТС написал, что ему критичны скорость и вес? В моём варианте инсерты писать гораздо приятнее.

ya-betmen ★★★★★
()

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

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