Я предполагаю, что SQLite (или другая реляционная СУБД) является обязательным требованием.
EAV
Я работал с EAV и универсальными моделями данных и могу сказать, что модель данных очень запутана и с ней трудно работать в долгосрочной перспективе.
Допустим, вы разрабатываете модель данных с тремя таблицами: entities, attributes и _entities_attributes_:
CREATE TABLE entities
(entity_id INTEGER PRIMARY KEY, name TEXT);
CREATE TABLE attributes
(attribute_id INTEGER PRIMARY KEY, name TEXT, type TEXT);
CREATE TABLE entity_attributes
(entity_id INTEGER, attribute_id INTEGER, value TEXT,
PRIMARY KEY(entity_id, attribute_id));
В этой модели таблица entities будет содержать ваши автомобили, а таблица attributes — атрибуты, которые вы можете связать с вашими автомобилями (марка, модель, цвет,... ) и его тип (текст, число, дата,...), а _entity_attributes_ будет содержать значения атрибутов для данного объекта (например, "красный").
Учтите, что с этой моделью вы можете хранить столько сущностей, сколько хотите, и это могут быть машины, дома, компьютеры, собаки или что-то еще (хорошо, может быть, вам нужно новое поле для сущностей, но для примера этого достаточно).
INSERT
довольно просты. Вам нужно только вставить новый объект, кучу атрибутов и их отношений. Например, чтобы вставить новую сущность с 3 атрибутами, вам потребуется выполнить 7 вставок (одну для сущности, еще три для атрибутов и еще три для отношений.
Когда вы хотите выполнить UPDATE
, вам нужно будет знать, что представляет собой сущность, которую вы хотите обновить, и обновить желаемый атрибут, соединяющий связь между сущностью и ее атрибутами.
Когда вы хотите выполнить DELETE
, вам также нужно будет знать, какой объект вы хотите удалить, удалить его атрибуты, удалить связь между вашим объектом и его атрибутами, а затем удалить объект.
Но когда вы хотите выполнить SELECT
, дело становится неприятным (вам нужно писать действительно сложные запросы), и производительность ужасно падает.
Представьте себе модель данных для хранения сущностей автомобиля и его свойств, как в вашем примере (скажем, мы хотим хранить марку и модель). SELECT
для запроса всех ваших записей будет
SELECT brand, model FROM cars;
Если вы разработаете универсальную модель данных, как в примере, SELECT
для запроса всех ваших сохраненных автомобилей будет очень сложно написать, и это потребует объединения трех таблиц. Запрос будет работать очень плохо.
Кроме того, подумайте об определении ваших атрибутов. Все ваши атрибуты хранятся как TEXT
, и это может быть проблемой. Что, если кто-то ошибется и сохранит «красное» в качестве цены?
Индексы — это еще одна вещь, от которой вы не можете получить пользу (или, по крайней мере, не так сильно, как хотелось бы), и они очень необходимы по мере роста хранимых данных.
Как вы сказали, основная проблема разработчика заключается в том, что запросы действительно сложно писать, трудно тестировать и трудно поддерживать (сколько клиент должен заплатить, чтобы купить все красные Pontiac Firebirds 1980 года, которые у вас есть?) , и будет работать очень плохо при увеличении объема данных.
Единственное преимущество использования EAV заключается в том, что вы можете хранить практически все с одной и той же моделью, но это все равно, что иметь коробку, полную вещей, в которой вы хотите найти один конкретный маленький предмет.
Кроме того, чтобы использовать авторитетный аргумент, я скажу, что Том Кайт решительно выступает против общих моделей данных: http://tkyte.blogspot.com.es/2009/01/this-should-be-fun-to-watch.html https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:10678084117056
Динамические столбцы в таблицах базы данных
С другой стороны, вы можете, как вы говорите, генерировать таблицы динамически, добавляя (и удаляя) столбцы по мере необходимости. В этом случае вы можете, например, создать таблицу car с основными атрибутами, которые, как вы знаете, будете использовать, а затем динамически добавлять столбцы, когда они вам нужны (например, количество выхлопных газов).
Недостатком является то, что вам нужно будет добавить столбцы в существующую таблицу и (возможно) построить новые индексы.
Эта модель, как вы говорите, также имеет другую проблему при работе с SQLite, поскольку нет прямого способа удалить столбцы, и вам нужно будет сделать это, как указано на http://www.sqlite.org/faq.html#q11
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
CREATE TABLE t1(a,b);
INSERT INTO t1 SELECT a,b FROM t1_backup;
DROP TABLE t1_backup;
COMMIT;
В любом случае, я не думаю, что вам нужно будет удалять столбцы (или, по крайней мере, это будет очень редкий сценарий). Возможно, кто-то добавит количество дверей в виде столбца и сохранит автомобиль с этим свойством. Перед удалением столбца вам необходимо убедиться, что любой из ваших автомобилей имеет это свойство, чтобы предотвратить потерю данных. Но это, конечно, зависит от вашего конкретного сценария.
Еще одним недостатком этого решения является то, что вам понадобится таблица для каждого объекта, который вы хотите сохранить (одна таблица для хранения автомобилей, другая для хранения домов и т. д.).
Другой вариант (псевдообщая модель)
Третьим вариантом может быть псевдообщая модель с таблицей, имеющей столбцы для хранения идентификатора, имени и типа объекта. и заданное (достаточное) количество общих столбцов для хранения атрибутов ваших сущностей.
Допустим, вы создаете такую таблицу:
CREATE TABLE entities
(entity_id INTEGER PRIMARY KEY,
name TEXT,
type TEXT,
attribute1 TEXT,
attribute1 TEXT,
...
attributeN TEXT
);
В этой таблице вы можете хранить любые объекты (машины, дома, собаки), потому что у вас есть поле type и вы можете хранить столько атрибутов для каждый объект по своему усмотрению (N в данном случае).
Если вам нужно знать, что означает attribute37, когда type имеет значение «красный», вам нужно будет добавить еще одну таблицу, которая связывает типы и атрибуты с описанием атрибутов. .
А что, если вы обнаружите, что одной из ваших сущностей нужно больше атрибутов? Затем просто добавьте новые столбцы в таблицу entities (attributeN+1, ...).
В этом случае атрибуты всегда хранятся в виде ТЕКСТА (как в EAV) с его недостатками.
Но вы можете использовать индексы, запросы действительно просты, модель достаточно универсальна для вашего случая, и в целом я думаю, что преимуществ у этой модели больше, чем недостатков.
Надеюсь, поможет.
Дополнение из комментариев:
При использовании псевдоуниверсальной модели ваша таблица entities будет содержать множество столбцов. Из документации (https://www.sqlite.org/limits.html) по умолчанию значение для SQLITE_MAX_COLUMN равно 2000. Я работал с таблицами SQLite с более чем 100 столбцами с высокой производительностью, поэтому 40 столбцов не должны иметь большого значения для SQLite.
Как вы сказали, большинство ваших столбцов будут пустыми для большинства ваших записей, и вам нужно будет проиндексировать все ваши столбцы для повышения производительности, но вы можете использовать частичные индексы (https://www.sqlite.org/partialindex.html). Таким образом, ваши индексы будут небольшими даже при большом количестве строк, а селективность каждого индекса будет высокой.
Если вы реализуете EAV только с двумя таблицами, количество соединений между таблицами будет меньше, чем в моем примере, но запросы все равно будет сложно писать и поддерживать, и вам нужно будет сделать несколько (внешних) соединений для извлечения данных. , что снизит производительность даже при большом индексе, когда вы храните много данных. Например, представьте, что вы хотите получить марку, модель и цвет ваших автомобилей. Ваш SELECT
будет выглядеть так:
SELECT e.name, a1.value brand, a2.value model, a3.value color
FROM entities e
LEFT JOIN entity_attributes a1 ON (e.entity_id = a1.entity_id and a1.attribute_id = 'brand')
LEFT JOIN entity_attributes a2 ON (e.entity_id = a2.entity_id and a2.attribute_id = 'model')
LEFT JOIN entity_attributes a3 ON (e.entity_id = a3.entity_id and a3.attribute_id = 'color');
Как видите, вам потребуется одно (левое) внешнее соединение для каждого атрибута, который вы хотите запросить (или отфильтровать). С псевдоуниверсальной моделью запрос будет таким:
SELECT name, attribute1 brand, attribute7 model, attribute35 color
FROM entities;
Кроме того, примите во внимание потенциальный размер вашей таблицы _entity_attributes_. Если у вас потенциально может быть 40 атрибутов для каждой сущности, скажем, у вас есть 20 ненулевых атрибутов для каждого из них. Если у вас есть 10 000 сущностей, ваша таблица _entity_attributes_ будет содержать 200 000 строк, и вы будете запрашивать ее, используя один огромный индекс. С псевдоуниверсальной моделью у вас будет 10 000 строк и один небольшой индекс для каждого столбца.
person
antonio
schedule
12.05.2015