2008-09-24 3 views
61

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

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

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 . . . 
) 

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

ответ

110

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

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

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

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

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

1

первый способ вполне нормально. вы можете создать UDF, который извлекает нужные данные и просто вызывает это.

5

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

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

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

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

0

Вторая таблица плохо деформирована. Я бы придерживался первого подхода.

1

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

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

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

+0

ORM упрощает запрос, но не повышает производительность. Ручной кодированный SQL-запрос может дать лучшую производительность. – mansu 2009-05-27 16:33:29

+0

Это может быть. Но, вероятно, нет, и скорость не была чем-то, о чем он просил. – 2009-05-28 06:00:36

1

Первый способ намного более гибкий за счет того, что вы упомянули.

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

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

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

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

0

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

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

Я могу написать более подробную информацию о том, что мы делаем, если потребуется.

2

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

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

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

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

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

+0

усложняется, если одно из значений - это дата и вы хотите выполнить поиск между датами для определенных ключей. – 2008-09-24 14:17:41

+3

Подумайте о запросе в обратном порядке - найдите itemid для набора пар ключ/значение, для этого требуется каскадный набор объединений. Это еще больше осложняется необходимостью избегать выбора надмножества; например find (Color = Red, Size = Medium) musn't return itemid 123, так как этот набор содержит другую строку (Fabric = Cotton) – horace 2008-10-01 08:53:36

0

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

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

Если есть среднее количество известных фиксированных ключей (10 - 30), возможно, у вас есть другая таблица для хранения элементов item_details.

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

16

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

+0

Это один из возможных сценариев. Я бы предпочел бы просто концепцию KV. Отделяет данные (ключ, значение) от своих метаданных (например, в конфигурации XML в отдельных свойствах -column). Гибкий, расширяемый и легко обрабатываемый (например, JAXB). Вам не нужно менять схему БД все время, когда вы расширяете свою бизнес-логику. Логику продолжительности (загрузка/сохранение) и интерфейс к домену можно развить с помощью «Соглашения по конфигурации» и не нужно касаться изменений/расширений. – 2016-02-19 11:41:01

1

Нарушение правил нормализации в порядке, пока деловое требование все еще может быть выполнено. Имея key_1, value_1, key_2, value_2, ... key_n, value_n, все в порядке, вплоть до нужной вам точки key_n+1, value_n+1.

Мое решение было таблицей данных для общих атрибутов и XML для уникальных атрибутов. Это означает, что я использую оба. Если все (или большинство вещей) имеют размер, то размер - это столбец в таблице. Если только объект A имеет атрибут Z, то Z сохраняется как XML, как уже было сказано в ответе Питера Маршалла.

13

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

Я также задал вопрос, почему ваш идентификатор является 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(id), 
    CONSTRAINT fk_item_values_iv FOREIGN KEY (key_id) REFERENCES valid_keys (id) 
); 

Вы можете даже пойти орехи и добавить «TYPE» для ключей, что позволяет некоторые проверки типов.

0

Если вы идете по маршруту таблицы KVP, и я должен сказать, что мне вообще не нравится эта техника, так как ее действительно сложно запросить, тогда вам следует рассмотреть возможность кластеризации значений для одного элемента id вместе используя подходящую технику для любой платформы, на которой вы находитесь.

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

-1

Ваш пример - не очень хороший пример использования пар значений ключа. Лучшим примером может быть использование чего-то вроде таблицы Плата таблицы Customer и таблицы Customer_Fee в приложении для выставления счетов.Таблица Платы будет состоять из таких областей, как: fee_id, FEE_NAME, fee_description таблицы Customer_Fee будет состоять из таких областей, как: Customer_ID, fee_id, fee_value

2

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

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

Заметные точки:

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

Я когда-то использовал пары ключ-значение в базе данных с целью создания электронной таблицы (используемой для ввода данных), в которой кассир суммировал свою деятельность с работой денежного ящика. Каждая пара 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). Среди многочисленных плюсов и минусов обоих подходов: первое проще, второе - быстрее. Оба заставят вас писать уродливые сложные запросы.

0

Времена изменились. Теперь у вас есть другие типы баз данных, которые вы можете использовать рядом с реляционными базами данных. Варианты NOSQL теперь включают в себя: Столбцы, Хранилища документов, График и Мультимодель (см.: http://en.wikipedia.org/wiki/NoSQL).

Для баз данных с ключевым значением ваши варианты включают (но не ограничиваются) CouchDb, Redis и MongoDB.

1

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

Смежные вопросы