Чи є назва цієї схеми бази даних ключових значень?


68

Ми обробляємо рутинний канал даних від клієнта, який щойно переробив свою базу даних із форми, яка здається звичною (один рядок на сутність, один стовпець на атрибут) до тієї, яка мені здається незнайомою (один рядок на сутність за атрибутом):

Перед: по одному стовпцю на атрибут

ID   Ht_cm   wt_kg   Age_yr  ... 
1      190      82     43    ...
2      170      60     22    ...
3      205      90     51    ...

Після: один стовпець для всіх атрибутів

ID    Metric   Value
 1     Ht_cm     190
 1     Wt_kg     82
 1     Age_yr    43
 1      ...
 2     Ht_cm     170
 2     Wt_kg     60
 2     Age_yr    22
 2     ...
 3     Ht_cm     205
 3     Wt_kg     90
 3     Age_yr    51
 3     ...

Чи є назва цієї структури бази даних? Які відносні переваги? Старий спосіб здається легшим розмістити обмеження дійсності на конкретних атрибутах (ненульові, негативні тощо) та легше обчислити середні значення. Але я бачу, як може бути простіше додавати нові атрибути без рефакторингу бази даних. Це стандартний / бажаний спосіб структурування даних?

Відповіді:


91

Це називається Entity-Attribute-Value (також іноді 'пари іменних значень'), і це класичний випадок "круглої шпильки в квадратному отворі", коли люди використовують шаблон EAV у реляційній базі даних.

Ось список, чому не слід використовувати EAV:

  • Ви не можете використовувати типи даних. Не має значення, чи є значення датою, цифрою чи грошима (десятковий). Це завжди буде примусово до варчару. Це може бути що завгодно, від незначної проблеми з продуктивністю до масивної кишки (коли-небудь доводилося виганяти на один відсоток варіації щомісячного звіту про підсумки?).
  • Ви не можете (легко) застосовувати обмеження. Він вимагає смішної кількості коду, щоб виконувати "Кожен повинен мати висоту між 0 і 3 метрами" або "Вік повинен бути не нульовим і> = 0", на відміну від 1-2 рядків, якими буде кожне з цих обмежень в правильно спроектованій системі.
  • Згадане вище, ви не можете легко гарантувати, що ви отримаєте необхідну інформацію для кожного клієнта (вік може бути відсутній у одного, тоді для наступного може бути відсутній їхній зріст тощо). Ви можете це зробити, але це пекло набагато складніше, ніж SELECT height, weight, age FROM Client where height is null or weight is null.
  • Знову ж таки, повторювані дані набагато складніше виявити (що відбувається, якщо вони дають вам два віки для одного клієнта? Знищення даних, як показано нижче, дасть вам два ряди результатів, якщо у вас є один атрибут подвоєний. Якщо один клієнт має два окремі записи для двох атрибутів, ви отримаєте чотири рядки із запиту нижче).
  • Ви навіть не можете гарантувати відповідність імен атрибутів. "Age_yr" може стати "AGE_IN_YEARS" або "віком". (Правда, це менше проблем, коли ви отримуєте виписку проти того, коли люди вставляють дані, але все ж.)
  • Будь-який нетривіальний запит - це повна катастрофа. Для релянізації системи EAV з трьома атрибутами, щоб ви могли раціонально запитувати її, потрібно три приєднання таблиці EAV.

Порівняйте:

SELECT cID.ID AS [ID], cH.Value AS [Height], cW.Value AS [Weight], cA.Value AS [Age]
FROM (SELECT DISTINCT ID FROM Client) cID 
      LEFT OUTER JOIN 
    Client cW ON cID.ID = cW.ID AND cW.Metric = "Wt_kg" 
      LEFT OUTER JOIN 
    Client cH ON cID.ID = cH.ID AND cW.Metric = "Ht_cm" 
      LEFT OUTER JOIN 
    Client cA ON cID.ID = cA.ID AND cW.Metric = "Age_yr"

До:

SELECT c.ID, c.Ht_cm, c.Wt_kg, c.Age_yr
FROM Client c

Ось (дуже короткий) список, коли слід використовувати EAV:

  • Коли цього абсолютно немає, і вам доведеться підтримувати дані без схем у вашій базі даних.
  • Коли вам просто потрібно зберігати "речі", і не сподівайтеся, що вони знадобляться у більш структурованому вигляді. Остерігайтеся, однак, монстр називає "мінливими вимогами".

Я знаю, що я щойно провів всю цю публікацію, детально пояснивши, чому EAV - це страшна ідея у більшості випадків - але є кілька випадків, коли це потрібно / неминуче. проте більшу частину часу (включаючи приклад вище) це буде набагато більше клопоту, ніж варто. Якщо у вас є потреба в широкій підтримці введення даних типу EAV, слід поглянути на їх зберігання в системі ключових значень, наприклад Hadoop / HBase, CouchDB, MongoDB, Cassandra, BerkeleyDB.


7
+1 з незначним повідомленням: ви можете використовувати типи даних, якщо розміщувати значення різних типів у різних таблицях (ну не класичний EAV, а такий спосіб вдосконалення). (Тоді виникає додаткове запитання: як ви знаєте тип нового атрибута?)
dezso

4
Погодився, але хочу додати, що EAV - це також хороший підхід для використання, коли ви ведете список речей, які семантично не мають значення для вашої системи (не лише схеми, що не мають схеми). Наприклад, онлайн-каталог товарів, де функції продукту потрібно зберігати та перелічувати. У вас є список пар ключів / значень, які потрібно відривати, але система насправді не знає і не піклується про те, про що ці ключі або значення. У цій ситуації небезпека EAV не має значення.
Джоель Браун

10
@JoelBrown Вас зараз не хвилює ЗАРАЗ, але якщо в дорозі представник ВПС попросить дізнатися, скільки сорочок у каталозі мають як коричневі ґудзики, так і нашийники на пуговиці, це буде суком запиту, який потрібно написати. Сама EAV зазвичай вказує на брак планування чи передбачення.
JNK

2
@JoelBrown Я не погоджуюся з тим, що він (дуже маленький і дуже вузький) використання. Але якщо інформація, ймовірно, буде запитуватися будь-яким структурованим способом, вона, ймовірно, не повинна бути в EAV
JNK

4
@JoelBrown Якщо ваші бізнес-вимоги або дані, які ви зберігаєте, зміни, то так само і ваша модель даних . Ваша модель даних не повинна бути вирізана з каменю. Крім того, для реляційної бази даних 99% часу, коли люди використовують EAV, їх міркування зводиться до "я не хочу витрачати час на роздуми про збереження моїх даних", а не на "З огляду на всі відомості моделей та моделей баз даних, EAV найкраще працює для цього набору даних ". Повторюся - є випадки, коли EAV є корисним (а може бути, навіть "правильним" відповіддю), але їх небагато і між ними.
Simon Righarts

18

Значення атрибутів особи (EAV)

Багато хто, в тому числі і я, вважають його анти-зразком.

Ось ваші альтернативи:

  1. використовувати успадкування таблиці баз даних

  2. використовувати дані XML та функції SQLXML

  3. використовувати базу даних nosql, як HBase


3
Безумовно, анти-модель для більшості випадків використання. Якщо у вас дійсно невеликий набір даних і продуктивність не має значення, це може працювати для вас.
JNK

16

У PostgreSQL одним з дуже хороших способів поводження зі структурами EAV є додатковий модуль hstore, доступний для версії 8.4 або новішої версії. Я цитую посібник:

Цей модуль реалізує hstoreтип даних для зберігання наборів пар ключів / значень у межах одного значення PostgreSQL. Це може бути корисно в різних сценаріях, таких як рядки з багатьма атрибутами, які рідко вивчаються, або напівструктуровані дані. Клавіші та значення - це просто текстові рядки.

Оскільки Postgres 9.2 є також jsonтип та безліч функціональних можливостей, які можна використати з ним ( більшість додається з 9.3 ).

Postgres 9.4 додає (значно покращений!) Тип даних "двійкових JSON" jsonbдо списку параметрів. З розширеними параметрами індексу.


10

Якщо у вас є база даних, яка використовує структуру EAV, можна запитувати дані різними способами.

@ Відповідь Саймона вже показує, як виконати запит, використовуючи декілька приєднань.

Використовувані зразки даних:

CREATE TABLE yourtable ([ID] int, [Metric] varchar(6), [Value] int);

INSERT INTO yourtable ([ID], [Metric], [Value])
VALUES (1, 'Ht_cm', 190),
    (1, 'Wt_kg', 82),
    (1, 'Age_yr', 43),
    (2, 'Ht_cm', 170),
    (2, 'Wt_kg', 60),
    (2, 'Age_yr', 22),
    (3, 'Ht_cm', 205),
    (3, 'Wt_kg', 90),
    (3, 'Age_yr', 51);

Якщо ви використовуєте RDBMS з PIVOTфункцією ( SQL Server 2005+ / Oracle 11g + ), ви можете запитувати дані наступним чином:

select id, Ht_cm, Wt_kg, Age_yr
from
(
  select id, metric, value
  from yourtable
) src
pivot
(
  max(value)
  for metric in (Ht_cm, Wt_kg, Age_yr)
) piv;

Див. SQL Fiddle with Demo

Якщо у вас немає доступу до PIVOTфункції, ви можете використовувати сукупну функцію із CASEзаявою для повернення даних:

select id,
  max(case when metric ='Ht_cm' then value else null end) Ht_cm,
  max(case when metric ='Wt_kg' then value else null end) Wt_kg,
  max(case when metric ='Age_yr' then value else null end) Age_yr
from yourtable
group by id

Див. SQL Fiddle with Demo

Обидва ці запити повернуть дані в результаті:

| ID | HT_CM | WT_KG | AGE_YR |
-------------------------------
|  1 |   190 |    82 |     43 |
|  2 |   170 |    60 |     22 |
|  3 |   205 |    90 |     51 |

10

Смішно бачити, як модель EAV db піддається критиці і навіть розглядається як "анти-модель".

Що стосується мене, то основними недоліками є:

  • Крива навчання крутіша, якщо ви потрапляєте на проект, який вже почав використовувати EAV деякий час тому. Дійсно, запити жорсткі, оскільки ви значно збільшуєте кількість приєднань (і таблиць), і тому вам потрібно більше часу для розуміння. Просто погляньте на проект Magento і подивіться, як зовнішній розробник проекту непростий час працює над БД, але документація добре підтримується.
  • Не підходить для звітності , якщо вам потрібно отримати кількість людей, котрі з прізвищем почали з "М" тощо ...

Однак вам точно не слід відмовлятися від цього рішення, і ось чому:

  • Саймон розповів про чудовисько, яке називалося "мінливі вимоги". Мені подобається цей вираз :). І ІМХО саме тому EAV може бути хорошим кандидатом, оскільки це добре підходить для "змін" , оскільки ви можете легко додавати стільки атрибутів, скільки бажаєте. Звичайно, це залежить від вимог, які ми змінюємо. Якщо ми говоримо про зовсім новий бізнес, звичайно, вам доведеться переглянути свої даніModel, але EAV пропонує велику гнучкість. Тільки тому, що вона вимагає більшої жорсткості, не означає, що це менш цікаво.
  • Також було сказано, що "типи даних не можна використовувати". : Це неправильно . У вас може бути кілька таблиць значень , по одній для кожного типу даних. Потім у таблиці атрибутів потрібно вказати, який тип даних є вашим атрибутом. Насправді, поєднання класичного відношення / EAV з класовими стосунками пропонує багато цікавого потенціалу в дизайні баз даних.

2
Крива навчання крутіша для першої зустрічі дизайну EAV. Після цього всі виглядають однаково.
ypercubeᵀᴹ

1
Коментар щодо темпу: я не розумію, чому претензія "не підходить для звітування". EAV здається чудовим для звітності. Виберіть ObjectId з eav.values, де propertyId = ім'я та значення на зразок 'm%'. Зміни віртуальної схеми (наприклад, додавання властивостей) можуть бути включені в будь-які динамічні інтерфейси звітування (наприклад, спадні меню) без необхідності перекомпілювати.
крокусек
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.