Ще приема, че 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
s са доста ясни. Трябва само да вмъкнете нов обект, куп атрибути и техните релации. Например, за да вмъкнете нов обект с 3 атрибута, ще трябва да изпълните 7 вмъквания (едно за обекта, още три за атрибутите и още три за връзките.
Когато искате да извършите UPDATE
, ще трябва да знаете кой е обектът, който искате да актуализирате, и да актуализирате съединяването на желания атрибут с връзката между обекта и неговите атрибути.
Когато искате да изпълните DELETE
, вие също ще трябва да знаете кое е образуванието, което искате да изтриете, да изтриете неговите атрибути, да изтриете връзката между вашия обект и неговите атрибути и след това да изтриете обекта.
Но когато искате да изпълните SELECT
, нещата стават гадни (трябва да пишете наистина трудни заявки) и производителността пада ужасно.
Представете си модел на данни за съхраняване на автомобилни обекти и неговите свойства, както във вашия пример (да кажем, че искаме да съхраняваме марка и модел). Ще бъде SELECT
за запитване до всички ваши записи
SELECT brand, model FROM cars;
Ако проектирате общ модел на данни, както в примера, SELECT
за запитване до всички ваши съхранени автомобили ще бъде наистина трудно за писане и ще включва свързване на 3 таблици. Заявката ще се представи много зле.
Също така помислете за определението на вашите атрибути. Всички ваши атрибути се съхраняват като TEXT
и това може да е проблем. Ами ако някой сгреши и запише "червено" като цена?
Индексите са друго нещо, от което не можете да се възползвате (или поне не толкова, колкото би било желателно), и те са много необходими, тъй като съхраняваните данни нарастват.
Както казвате, основното притеснение като разработчик е, че заявките са наистина трудни за писане, трудни за тестване и трудни за поддръжка (колко трябва да плати клиент, за да купи всички червени, 1980, Pontiac Firebirds, които имате?) , и ще работи много слабо, когато обемът на данните се увеличи.
Единственото предимство на използването на 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
Динамични колони в таблици на база данни
От друга страна, можете, както казвате, да генерирате таблиците динамично, добавяйки (и премахвайки) колони, когато е необходимо. В този случай можете например да създадете таблица кола с основните атрибути, които знаете, че ще използвате, и след това да добавяте колони динамично, когато имате нужда от тях (например броя на ауспусите).
Недостатъкът е, че ще трябва да добавите колони към съществуваща таблица и (може би) да изградите нови индекси.
Този модел, както казвате, също има друг проблем при работа с 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;
Както и да е, наистина не мисля, че ще трябва да изтривате колони (или поне това ще бъде много рядък сценарий). Може би някой добавя броя на вратите като колона и съхранява кола с това свойство. Ще трябва да се уверите, че някоя от вашите коли има това свойство, за да предотвратите загуба на данни, преди да изтриете колоната. Но това, разбира се, зависи от вашия конкретен сценарий.
Друг недостатък на това решение е, че ще ви е необходима таблица за всеки обект, който искате да съхранявате (една маса за съхраняване на коли, друга за съхраняване на къщи и т.н.).
Друга опция (псевдогенеричен модел)
Трета опция може да бъде да имате псевдогенеричен модел, с таблица с колони за съхраняване на id, име и тип на обекта и даден (достатъчен) брой общи колони за съхраняване на атрибутите на вашите обекти.
Да приемем, че създавате таблица като тази:
CREATE TABLE entities
(entity_id INTEGER PRIMARY KEY,
name TEXT,
type TEXT,
attribute1 TEXT,
attribute1 TEXT,
...
attributeN TEXT
);
В тази таблица можете да съхранявате всеки субект (коли, къщи, кучета), защото имате поле за тип и можете да съхранявате колкото се може повече атрибути за всеки субект, както искате (N в този случай).
Ако трябва да знаете какво означава attribute37, когато type е "червен", ще трябва да добавите друга таблица, която свързва типовете и атрибутите с описанието на атрибутите .
И какво ще стане, ако установите, че един от вашите обекти се нуждае от повече атрибути? След това просто добавете нови колони към таблицата entities (атрибутN+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