关系数据库中的键值对

是否有人将键值对存储在数据库中?

我一直在使用这种types的表格:

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 

这种scheme的麻烦是提取数据所需的SQL语法是相当复杂的。 最好是创build一系列键/值列?

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

这将更容易和更快的查询,但缺乏第一种方法的可扩展性。 有什么build议?

在继续使用之前,我会虚心地build议你退后一步,考虑是否真的想将这些数据存储在“Key-Value Pair”表中。 我不知道你的申请,但是我的经验表明,每当我做完你正在做的事情后,我希望我已经创build了一个颜色表,一个结构表和一个尺寸表。

考虑引用完整性约束,如果采用键值对方法,数据库无法告诉您何时尝试在大小字段中存储颜色标识

考虑join具有10个值的表的性能优势与可能具有跨多个域的数千个值的通用值的关系。 关键值真的会变得有用吗?

通常背后做的原因是因为域需要是“用户可定义的”。 如果是这样的话,我甚至不会推动你即时创build表格(尽pipe这是一个可行的方法)。

但是,如果您的推理是因为您认为pipe理比多个表更容易,或者您正在设想一个对所有域都通用的维护用户界面,那么在继续之前应停下来思考。

还有另外一个解决办法落在两者之间。 您可以为键和值使用xmltypes的列。 所以你保留itemid字段,然后有一个xml字段,其中包含为一些键值对定义的xml,如<items> <item key="colour" value="red"/><item key="xxx" value="blah"/></items>然后,当您从数据库中提取数据时,您可以通过多种不同的方式处理xml。 取决于您的使用情况。 这是一个可扩展的解决scheme。

为了创build一个电子表格(用于数据input),我曾经在数据库中使用了键值对,在这个电子表格中,一个出纳员会从一个现金抽屉中总结他的活动。 每个k / v对代表用户input金额的命名单元。 这种方法的主要原因是电子表格极易改变。 新的产品和服务经常被添加(因此出现了新的细胞)。 另外,某些情况下某些细胞不需要,可能会被丢弃。

我写的应用程序是重写了一个应用程序,它将出纳员表分成不同的表格,每个表格都用不同的表格表示。 麻烦在于,随着产品和服务的增加,需要对模式进行修改。 与所有的deviseselect一样,有一些优点和缺点可以相对于另一个方向发展。 我的重新devise肯定执行速度更慢,更快速地消耗磁盘空间; 然而,它非常灵活,并允许在几分钟内添加新的产品和服务。 唯一值得注意的问题是磁盘消耗; 没有其他令人头痛的事情可以想起。

如前所述,我通常认为键值对方法的原因在于,用户(这可能是业务所有者)想要创build具有特定于用户的属性集合的types。 在这种情况下,我有以下的决心。

如果不需要通过这些属性来检索数据,或者一旦检索到大量数据就可以将检索推迟到应用程序,我build议将所有属性存储在单个文本字段中(使用JSON,YAML,XML等)。 )。 如果强烈需要通过这些属性检索数据,则会变得杂乱无章。

您可以创build一个单独的“属性”表(id,item_id,key,value,data_type,sort_value),其中sorting列将实际值转换为string可sorting的表示forms。 (例如date:“2010-12-25 12:00:00”,number:“0000000001”)或者你可以通过数据types创build单独的属性表(例如string_attributes,date_attributes,number_attributes)。 在这两种方法的利弊中:第一种更简单,第二种更快。 两者都会导致你写难看,复杂的查询。

在大多数情况下,你会使用第一种方法,这是因为你没有真正坐下来想出你的模型。“好吧,我们不知道密钥将会是什么”。 通常这是非常糟糕的devise。 这将比实际上把你的键作为列要慢。

我也想问为什么你的id是一个varchar。

在很less的情况下,你真的必须实现一个键/值表,第一个解决scheme是好的,但是,我通常希望有在一个单独的表中的密钥,所以你不存储变种作为键/价值表。

例如,

 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”,允许进行一些types检查。

从经验来看,我发现某些密钥会更频繁地被广泛使用或查询。 然后,我们通常稍微将devise归一化,以便在主“项目”表中包含特定字段。

例如。 如果每个项目都有一个颜色,您可以将颜色列添加到您的项目表。 结构和尺寸可以使用较less,并可以在键值对表中分开保存。 您甚至可以保留键值对表中的颜色,但可以复制项目表中的数据以获得性能优势。

显然,这取决于数据以及您需要键值对的灵活性。 它也会导致您的属性数据不一致。 然而,反规范化大大简化了查询并提高了性能。

我通常只会考虑在性能成为问题时进行反规范化,而不仅仅是为了简化查询。

我不明白为什么SQL提取数据应该是复杂的,你的第一个devise。 当然要得到一个项目的所有值,你只需要这样做:

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

或者如果你只是想要一个特定的项目的关键:

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

第一种devise也使您可以随时轻松地添加新键。

我认为devise这种表格的最佳方法如下:

  • 将经常使用的字段作为数据库中的列。
  • 提供一个Misc列,其中包含一个字典(在JSON / XML /其他stringformeat),将包含字段作为键值对。

要点:

  • 在大多数情况下,您可以编写正常的SQL查询来查询SQL。
  • 您可以在键值对上执行FullTextSearch。 MySQL有一个全文search引擎,否则你可以使用比较慢的“like”查询。 虽然全文search不好,但是我们认为这样的查询less了,所以不应该引起太多的问题。
  • 如果你的键值对是简单的布尔标志,那么这个技巧就像拥有一个单独的键列一样。 对键值对的任何更复杂的操作都应该在数据库之外完成。
  • 查看一段时间内的查询频率将告诉您哪些键值对需要在列中转换。
  • 这种技术也可以很容易地强制数据库的完整性约束。
  • 它为开发人员重新构build模式和代码提供了更自然的途径。

第一种方法是相当好的。 你可以创build一个UDF来提取所需的数据,然后调用它。

如果你有很less的可能的键,那么我只是将它们存储为列。 但是,如果可能的键集很大,那么你的第一种方法是好的(第二种方法是不可能的)。

还是这样,每个项目只能有一个有限数量的钥匙,但钥匙可能是一个大集合的东西?

您也可以考虑使用对象关系映射器来简化查询。

第一种方法比你提到的成本更灵活。

而第二种方法是不可行的,就像你展示的那样。 相反,你会(根据你的第一个例子)

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

当然这只有在数据量已知并且变化不大的情况下才起作用。

作为一般规则,任何要求改变桌面的DDL进行正常工作的应用程序都应该有第二和第三个想法。

只要业务要求能够实现,违反规范化规则就没有问题。 有key_1, value_1, key_2, value_2, ... key_n, value_n可以正常,直到你需要key_n+1, value_n+1

我的解决scheme是共享属性的数据表和唯一属性的XML表。 这意味着我使用两个。 如果一切(或大多数东西)都有一个大小,那么大小就是表格中的一列。 如果只有对象A具有属性Z,则Z被存储为XML,类似于Peter Marshall已经给出的答案。

第二个表格严重不规范。 我会坚持第一种方法。

我认为你做的是正确的事情,只要一个给定types的项目的键/值经常变化。
如果它们相当静态,那么简单地把项目表放大就更有意义了。

我们使用类似(但更复杂)的方法,在键/值周围有大量的逻辑,以及每个键所允许的值types的表。
这使我们可以将项目定义为密钥的另一个实例,而我们的中心表将任意密钥types映射到其他任意密钥types。 它可以迅速将你的大脑连接起来,但是一旦你编写并封装了处理这一切的逻辑,就有很大的灵活性。

如果需要,我可以写更多的细节。

如果这些密钥是dynamic的,或者它们是负载的,那么使用第一个例子中的映射表。 此外,这是最通用的解决scheme,随着您添加更多的密钥,它的规模将变得最好,可以很容易地编写SQL来获取数据,数据库将能够比您想象的更好地优化查询也就是说,我不会费力过早地优化这种情况,除非事后certificate是testing的瓶颈,在这种情况下,你可以考虑下面的两个选项)。

如果键是一个已知的集合,并不是很多(<10,也许<5),那么我不认为在他们作为价值栏的项目上的问题。

如果有中等数量的已知固定键(10 – 30),那么可能有另一个表来容纳item_details。

不过,我从来没有看到需要使用第二个示例结构,它看起来很麻烦。

如果你走KVP表的路线,而且我不得不说我根本就不喜欢这种技术,因为确实很难查询,那么你应该考虑使用适当的技术将单个项目id的值集中在一起无论你在哪个平台上

RDBMS有分散行的趋势,以避免块插入争用,如果你有8个行来检索,你可以很容易地发现自己访问8块表读取它们。 在Oracle上,你最好考虑使用散列簇来存储这些散列,这将极大地提高访问给定项目id的值的性能。

时代已经改变。 现在,您可以在关系数据库旁边使用其他数据库types。 现在NOSQL的select包括:列存储,文档存储,graphics和多模式(参见: http : //en.wikipedia.org/wiki/NoSQL )。

对于键值数据库,您的select包括(但不限于)CouchDb,Redis和MongoDB。

PostgreSQL 8.4支持hstore数据types,用于在单个PostgreSQL数据字段中存储(键,值)对的集合。 请参考http://www.postgresql.org/docs/8.4/static/hstore.html获取使用信息。; 虽然这是一个非常古老的问题,但想通过这个信息认为它可能会帮助某人。

你的例子不是使用键值对的一个很好的例子。 一个更好的例子是在计费应用程序中使用类似费用表的Customer表和Customer_Fee表。 费用表将由以下字段组成:fee_id,fee_name,fee_description Customer_Fee表由以下字段组成:customer_id,fee_id,fee_value