是否有人有将密钥值对存储在数据库中的经验?
我一直在使用这种类型的表:
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 . . .
)
查询会更容易,更快,但缺乏第一种方法的可扩展性。 有什么建议吗?
答案 0 :(得分:119)
在继续你的方法之前,我会谦虚地建议你退一步考虑是否真的想将这些数据存储在“键 - 值对”表中。我不知道你的应用程序,但我的经验表明,每次我做了你正在做的事情,后来我希望我创建了一个颜色表,一个织物表和一个尺寸表。
考虑引用完整性约束,如果采用键值对方法,数据库无法告诉您何时尝试在大小字段中存储颜色ID
考虑加入具有10个值的表的性能优势与可能在多个域中具有数千个值的通用值。关键值的索引真的有用吗?
通常,做你正在做的事情背后的原因是因为域名需要是“用户可定义的”。如果是这种情况,那么即使我不打算让你动态创建表格(尽管这是一种可行的方法)。
但是,如果您的推理是因为您认为它比多个表更容易管理,或者因为您正在设想一个适用于所有域的维护用户界面,那么在继续之前停下来思考一下。
答案 1 :(得分:16)
在大多数情况下,你会使用第一种方法,这是因为你没有真正坐下来思考你的模型。 “好吧,我们还不知道钥匙会是什么”。一般来说,这是非常糟糕的设计。它实际上比将键作为列更慢,这应该是它们。
我也会质疑为什么你的id是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)
);
然后你甚至可以坚持并在键上加一个“TYPE”,允许进行一些类型检查。
答案 2 :(得分:16)
还有另一种解决方案介于两者之间。您可以使用xml类型列作为键和值。所以你保留itemid字段,然后有一个xml字段,其中包含为某些键值对定义的xml,如<items> <item key="colour" value="red"/><item key="xxx" value="blah"/></items>
然后,当您从数据库中提取数据时,您可以通过多种不同方式处理xml。取决于您的使用情况。这是一个可扩展的解决方案。
答案 3 :(得分:13)
我曾经在数据库中使用键值对来创建电子表格(用于数据输入),其中出纳员将通过现金抽屉工作来总结他的活动。每个k / v对表示用户输入货币金额的命名单元格。这种方法的主要原因是电子表格很容易发生变化。常规添加新产品和服务(因此出现了新的细胞)。此外,在某些情况下不需要某些细胞,可能会被丢弃。
我写的应用程序是对应用程序的重写,该应用程序确实将出纳员表分成不同的部分,每个部分在不同的表中表示。这里的问题是,随着产品和服务的增加,需要进行架构修改。与所有设计选择一样,与另一个相比,采取某种方向是有利有弊的。我的重新设计肯定表现得更慢,更快地消耗了磁盘空间;但是,它非常灵活,允许在几分钟内添加新产品和服务。然而,唯一需要注意的问题是磁盘消耗;我记不起其他的头痛了。
如前所述,我通常考虑键值对方法的原因是用户 - 这可能是业务所有者 - 想要创建具有用户特定属性集的自己的类型。在这种情况下,我做出了以下决定。
如果不需要通过这些属性检索数据,或者一旦检索到一大块数据就可以将搜索推迟到应用程序,我建议将所有属性存储在单个文本字段中(使用JSON,YAML,XML等)。如果强烈需要通过这些属性检索数据,则会变得混乱。
您可以创建单个“属性”表(id,item_id,key,value,data_type,sort_value),其中sort列将实际值转换为字符串可排序表示。 (例如日期:“2010-12-25 12:00:00”,编号:“0000000001”)或者您可以按数据类型创建单独的属性表(例如string_attributes,date_attributes,number_attributes)。两种方法的众多优点和缺点:第一种更简单,第二种更快。两者都会让你写出丑陋复杂的查询。
答案 4 :(得分:6)
根据经验,我发现某些密钥将被更广泛地使用或更频繁地查询。然后,我们通常会稍微对设计进行非规范化,以在主“项目”表中包含特定字段。
例如。如果每个项目都有一个颜色,您可以将颜色列添加到项目表中。 Fabric和Size可以较少使用,并且可以在键值对表中保持独立。您甚至可以将颜色保留在键值对表中,但是复制项目表中的数据以获得性能优势。
显然,这取决于数据以及您需要键值对的灵活性。它还可能导致您的属性数据无法正确定位。但是,反规范化确实极大地简化了查询并提高了性能。
我通常只考虑在性能变得和问题时进行去规范化,而不仅仅是简化查询。
答案 5 :(得分:2)
PostgreSQL 8.4支持hstore数据类型,用于在单个PostgreSQL数据字段中存储(键,值)对的集合。 请参阅http://www.postgresql.org/docs/8.4/static/hstore.html了解其使用信息。虽然这是一个非常古老的问题,但想过传递这个信息,认为它可能对某人有帮助。
答案 6 :(得分:2)
我认为设计此类表格的最佳方法如下:
突出点:
答案 7 :(得分:2)
我不明白为什么提取数据的SQL应该对你的第一个设计来说很复杂。当然要获得项目的所有值,您只需执行此操作:
SELECT itemkey,itemvalue FROM key_value_pairs WHERE itemid='123';
或者如果您只想要该项目的一个特定键:
SELECT itemvalue FROM key_value_pairs WHERE itemid='123' AND itemkey='Fabric';
第一种设计还可以让您随时轻松添加新密钥。
答案 8 :(得分:1)
只要仍然可以满足业务要求,违反规范化规则就可以了。 key_1, value_1, key_2, value_2, ... key_n, value_n
可以正常,直到您需要key_n+1, value_n+1
。
我的解决方案是用于共享属性的数据表和用于唯一属性的XML。这意味着我同时使用两者。如果所有(或大多数东西)都有大小,那么size是表中的一列。如果只有对象A具有属性Z,则Z被存储为XML,类似于Peter Marshall的回答。
答案 9 :(得分:1)
第一种方法在你提到的成本上更加灵活。
第二种方法永远不可行,如你所示。相反,你会做(根据你的第一个例子)
create table item_config (item_id int, colour varchar, size varchar, fabric varchar)
当然,只有在知道数据量并且不会发生很大变化时,这才会起作用。
作为一般规则,任何要求更改表格的DDL以进行正常工作的应用程序都应该给出第二和第三个想法。
答案 10 :(得分:1)
如果您的密钥很少,那么我只将它们存储为列。但是如果可能的密钥集很大,那么你的第一种方法是好的(第二种方法是不可能的)。
或者是这样每个项目只能拥有有限数量的密钥,但密钥可能来自大型密钥?
您还可以考虑使用对象关系映射器来简化查询。
答案 11 :(得分:1)
第一种方法很好。您可以创建一个提取所需数据的UDF,然后调用它。
答案 12 :(得分:0)
如果这些键是动态的,或者有很多键,那么请使用您拥有的映射表作为第一个示例。此外,这是最通用的解决方案,随着您添加更多密钥,将来最佳扩展,可以轻松编写SQL以获取数据,并且数据库将能够比您想象的更好地优化查询(即,我不会过早地优化这种情况,除非事后证明它是测试的瓶颈,在这种情况下你可以考虑下面的两个选项。)
如果密钥是已知集,并且其中没有多少(&lt; 10,可能&lt; 5),那么我没有看到将它们作为项目上的值列的问题。
如果有中等数量的已知固定密钥(10 - 30),则可能有另一个表来保存item_details。
但是我没有看到需要使用你的第二个示例结构,它看起来很麻烦。
答案 13 :(得分:0)
我认为你做的是正确的,只要给定类型的项目的键/值经常变化。
如果它们是静态的,那么简单地使项目表更宽更有意义。
我们使用类似(但更复杂)的方法,围绕键/值有很多逻辑,以及每个键允许的值类型的表。
这允许我们将项目定义为键的另一个实例,并且我们的中心表将任意键类型映射到其他任意键类型。它可以迅速将你的大脑系在一起,但是一旦你编写并封装了逻辑来处理它,你就会有很大的灵活性。
如果需要,我可以写下我们所做的更多细节。
答案 14 :(得分:0)
如果你走KVP表的路线,我不得不说我自己不喜欢这种技术,因为它确实难以查询,那么你应该考虑使用一个项目id聚集在一起的值适用于任何平台的适当技术。
RDBMS倾向于分散行以避免插入时出现块争用,如果要检索8行,则很容易发现自己正在访问表的8个块来读取它们。在Oracle上,您最好考虑使用哈希集群来存储这些内容,这将极大地提高访问给定项ID的值的性能。
答案 15 :(得分:0)
您的示例不是使用键值对的非常好的示例。一个更好的例子是在计费应用程序中使用诸如Fee表,Customer表和Customer_Fee表之类的东西。费用表包括以下字段: fee_id,fee_name,fee_description Customer_Fee表将包含以下字段: customer_id,fee_id,fee_value
答案 16 :(得分:0)
第二个表严重失调。我会坚持第一种方法。
答案 17 :(得分:0)
时代变了。现在您可以在关系数据库旁边使用其他数据库类型。 NOSQL选择现在包括,列存储,文档存储,图形和多模型(请参阅:http://en.wikipedia.org/wiki/NoSQL)。
对于键值数据库,您的选择包括(但不限于)CouchDb,Redis和MongoDB。