Пары ключ-значение в реляционной базе данных - PullRequest
67 голосов
/ 24 сентября 2008

Есть ли у кого-то опыт хранения пар ключ-значение в базе данных?

Я использовал этот тип таблицы:

CREATE TABLE key_value_pairs ( 
    itemid           varchar(32) NOT NULL,
    itemkey         varchar(32) NOT NULL,
    itemvalue       varchar(32) NOT NULL,
    CONSTRAINT ct_primarykey PRIMARY KEY(itemid,itemkey)
)

Тогда, например, могут существовать следующие строки:

 itemid            itemkey        itemvalue    
 ----------------  -------------  ------------ 
 123               Colour         Red            
 123               Size           Medium             
 123               Fabric         Cotton

Проблема этой схемы в том, что синтаксис SQL, необходимый для извлечения данных, довольно сложен. Было бы лучше просто создать серию столбцов ключ / значение?

CREATE TABLE key_value_pairs ( 
    itemid            varchar(32) NOT NULL,
    itemkey1        varchar(32) NOT NULL,
    itemvalue1      varchar(32) NOT NULL,
    itemkey2        varchar(32) NOT NULL,
    itemvalue2      varchar(32) NOT NULL,
 . . .etc . . .
)

Это будет проще и быстрее запрашивать, но не обладает расширяемостью первого подхода. Любой совет?

Ответы [ 18 ]

119 голосов
/ 24 сентября 2008

Прежде чем вы продолжите свой подход, я смиренно рекомендую вам сделать шаг назад и подумать, действительно ли вы хотите сохранить эти данные в таблице «пара ключ-значение». Я не знаю вашу заявку, но мой опыт показал, что каждый раз, когда я делал то, что вы делаете, позже мне хотелось бы создать таблицу цветов, таблицу тканей и таблицу размеров.

Подумайте об ограничениях ссылочной целостности, если вы используете подход пары ключ-значение, база данных не сможет сказать вам, когда вы пытаетесь сохранить идентификатор цвета в поле размера

Подумайте о преимуществах производительности объединения таблицы с 10 значениями по сравнению с общим значением, которое может иметь тысячи значений в нескольких доменах. Насколько полезным будет индекс по значению ключа?

Обычно причина того, что вы делаете, заключается в том, что домены должны быть «определяемыми пользователем». Если это так, то даже я не собираюсь подталкивать вас к созданию таблиц на лету (хотя это осуществимый подход).

Однако, если вы считаете, что вам будет проще управлять, чем несколькими таблицами, или если вы планируете обслуживающий пользовательский интерфейс, общий для всех доменов, остановитесь и подумайте, прежде чем продолжить. *

16 голосов
/ 24 сентября 2008

Есть другое решение, которое падает где-то между ними. Вы можете использовать столбец типа xml для ключей и значений. Таким образом, вы сохраняете поле itemid, а затем поле xml, которое содержит xml, определенный для некоторых пар ключ-значение, таких как <items> <item key="colour" value="red"/><item key="xxx" value="blah"/></items> Затем, когда вы извлекаете данные из базы данных, вы можете обрабатывать xml различными способами. В зависимости от вашего использования. Это расширяемое решение.

16 голосов
/ 24 сентября 2008

В большинстве случаев вы используете первый метод, это потому, что вы на самом деле не сели и не продумали свою модель. «Ну, мы еще не знаем, какие ключи будут». Вообще, это довольно плохой дизайн. Это будет медленнее, чем на самом деле иметь ваши ключи в виде столбцов, какими они и должны быть.

Я бы также спросил, почему твой идентификатор varchar.

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

например,

CREATE TABLE valid_keys ( 
    id            NUMBER(10) NOT NULL,
    description   varchar(32) NOT NULL,
    CONSTRAINT pk_valid_keys PRIMARY KEY(id)
);

CREATE TABLE item_values ( 
    item_id NUMBER(10) NOT NULL,
    key_id  NUMBER(10) NOT NULL,
    item_value VARCHAR2(32) NOT NULL,
    CONSTRAINT pk_item_values PRIMARY KEY(item_id),
    CONSTRAINT fk_item_values_iv FOREIGN KEY (key_id) REFERENCES valid_keys (id)
);

После этого вы даже можете сходить с ума и добавить «ТИП» к клавишам, что позволит провести некоторую проверку типов.

13 голосов
/ 12 октября 2009

Однажды я использовал пары ключ-значение в базе данных с целью создания электронной таблицы (используемой для ввода данных), в которой кассир суммировал бы свою деятельность, работая в кассе. Каждая пара k / v представляла именованную ячейку, в которую пользователь вводил денежную сумму. Основная причина такого подхода состоит в том, что электронная таблица сильно подвержена изменениям. Новые продукты и услуги добавлялись регулярно (таким образом, появлялись новые ячейки). Кроме того, определенные клетки не нужны в определенных ситуациях и могут быть отброшены.

Приложение, которое я написал, было переписано как приложение, которое разбивало лист кассира на отдельные секции, каждая из которых представлена ​​в отдельной таблице. Проблема заключалась в том, что по мере добавления продуктов и услуг требовались изменения схемы. Как и во всех вариантах дизайна, есть свои плюсы и минусы в том, чтобы выбрать одно направление по сравнению с другим. Мой редизайн, безусловно, выполнялся медленнее и быстрее занимал место на диске; однако он был очень гибким и позволял добавлять новые продукты и услуги в считанные минуты. Однако единственное, что следует отметить, это потребление диска; не было никаких других головных болей, которые я могу вспомнить.

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

Если нет необходимости извлекать данные по этим атрибутам или поиск может быть отложен до приложения после извлечения фрагмента данных, я рекомендую хранить все атрибуты в одном текстовом поле (с использованием JSON, YAML, XML , так далее.). Если есть необходимость извлекать данные по этим атрибутам, они становятся беспорядочными.

Вы можете создать одну таблицу «атрибутов» (id, item_id, key, value, data_type, sort_value), в которой столбец сортировки охватывает фактическое значение в виде с сортировкой по строке. (например, дата: «2010-12-25 12:00:00», номер: «0000000001») Или вы можете создавать отдельные таблицы атрибутов по типу данных (например, string_attributes, date_attributes, number_attributes). Среди многочисленных плюсов и минусов обоих подходов: первый проще, второй быстрее. И то и другое заставит вас писать некрасивые сложные запросы.

6 голосов
/ 24 сентября 2008

Исходя из опыта, я обнаружил, что определенные ключи будут более широко использоваться или запрашиваться чаще. Затем мы обычно немного нормализовали дизайн, чтобы включить определенное поле обратно в основную таблицу «item».

например. Если у каждого элемента есть Цвет, вы можете добавить столбец Цвет в таблицу элементов. Ткань и Размер могут использоваться реже и могут храниться отдельно в таблице пар ключ-значение. Вы даже можете сохранить цвет в таблице пар ключ-значение, но дублировать данные в таблице элементов, чтобы получить выигрыш в производительности.

Очевидно, что это зависит от данных и от того, насколько гибкими вам нужны пары ключ-значение. Это также может привести к тому, что данные вашего атрибута не будут постоянно расположены. Тем не менее, нормализация значительно упрощает запросы и повышает их производительность.

Я бы обычно рассматривал возможность отмены нормализации только при повышении производительности, а не просто упрощал запрос.

2 голосов
/ 24 сентября 2008

Я не понимаю, почему SQL для извлечения данных должен быть сложным для вашего первого проекта. Конечно, чтобы получить все значения для элемента, вы просто делаете это:

SELECT itemkey,itemvalue FROM key_value_pairs WHERE itemid='123';

или если вы просто хотите один конкретный ключ для этого элемента:

SELECT itemvalue FROM key_value_pairs WHERE itemid='123' AND itemkey='Fabric';

Первый дизайн также дает вам возможность легко добавлять новые ключи в любое время.

2 голосов
/ 27 мая 2009

Я думаю, что лучший способ создания таких таблиц заключается в следующем:

  • Сделать часто используемые поля столбцами в базе данных.
  • Предоставить столбец Misc, который содержит словарь (в формате JSON / XML / другие строки), который будет содержать поля в виде пар ключ-значение.

Существенные пункты:

  • В большинстве случаев вы можете написать свои обычные запросы SQL для запроса SQL.
  • Вы можете выполнить FullTextSearch для пар ключ-значение. MySQL имеет механизм полнотекстового поиска, иначе вы можете использовать «похожие» запросы, которые немного медленнее. Хотя полнотекстовый поиск плох, мы предполагаем, что таких запросов меньше, поэтому это не должно вызывать слишком много проблем.
  • Если ваши пары ключ-значение являются простыми логическими флагами, этот метод имеет ту же силу, что и отдельный столбец для ключа. Любая более сложная операция с парами ключ-значение должна выполняться вне базы данных.
  • Анализ частоты запросов за определенный период времени покажет, какие пары ключ-значение необходимо преобразовать в столбцы.
  • Этот метод также позволяет легко устанавливать ограничения целостности для базы данных.
  • Он предоставляет разработчикам более естественный путь для перефакторинга их схемы и кода.
2 голосов
/ 22 сентября 2015

PostgreSQL 8.4 поддерживает тип данных hstore для хранения наборов пар (ключ, значение) в одном поле данных PostgreSQL. Пожалуйста, обратитесь http://www.postgresql.org/docs/8.4/static/hstore.html за информацией об использовании. Хотя это очень старый вопрос, но он решил передать эту информацию, думая, что она может кому-то помочь.

1 голос
/ 24 сентября 2008

Первый метод гораздо более гибкий по цене, которую вы упоминаете.

И второй подход никогда не бывает жизнеспособным, как вы показали. Вместо этого вы бы сделали (в соответствии с вашим первым примером)

create table item_config (item_id int, colour varchar, size varchar, fabric varchar)

конечно, это будет работать только тогда, когда объем данных известен и не сильно изменится.

Как правило, любому приложению, требующему изменения DDL таблиц для нормальной работы, следует дать вторую и третью мысли.

1 голос
/ 24 сентября 2008

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

Или это так, что каждый элемент может иметь только конечное количество ключей, но ключи могут быть чем-то из большого набора?

Вы также можете рассмотреть возможность использования Object Relational Mapper для упрощения запросов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...