与Mnesia保持关系完整性

时间:2008-12-27 17:43:22

标签: database erlang mnesia

我最近一直在潜入Erlang,我决定使用Mnesia来完成我的数据库工作,因为它可以存储任何类型的Erlang数据结构而没有问题,可以轻松扩展,与列表推导一起使用等等。 / p>

来自标准SQL数据库,大多数行可以并且应该由主键标识,通常是自动递增整数。默认情况下,Mnesia将行的第一个字段视为其键。据我所知,它也没有办法让自动递增的整数键。

鉴于我有这些虚构的记录代表我的表:

-record(user, {name, salt, pass_hash, email}).
-record(entry, {title, body, slug}).
-record(user_entry, {user_name, entry_title}).

我认为使用用户名可能已经足够用于某些目的,就像条目标题一样,为了识别资源,但我该如何保持完整性呢?

假设用户更改了其名称,或者在编辑后更改了条目的标题。如何确保我的数据仍然正确相关?无论如何使用用户名更改每个表,听起来都是一个糟糕的主意。

在Mnesia中实施某种主键系统的最佳方法是什么?

另外,如果第一个字段通常是密钥,那么像'user_entry'这样的中间表会怎么做?否则,在Mnesia中表达多对多关系会有什么更好的方式?

2 个答案:

答案 0 :(得分:9)

我更喜欢使用GUID而不是自动递增整数作为人工外键。有Erlang uuid module可用 GitHub,或者您可以使用{now(), node()},因为now/0 doc说:“还保证随后对此BIF的调用会不断返回增加的值。”

使用可以作为主键更改的内容在我看来是一个独立于数据库系统的坏主意。

不要忘记您不需要将Mnesia中的数据标准化,即使是第一次正常形式;在你的例子中,我会考虑以下结构:

-record(user, {id, name, salt, pass_hash, email, entries}).
-record(entry, {id, title, body, slug, users}).

其中entriesusers是ID列表。当然,这取决于您想要的查询。

编辑:固定为多对多而不是多对一。

答案 1 :(得分:8)

Mnesia确实以mnesia:dirty_update_counter(Table, Key, Increment)的形式支持序列(自动递增整数)。要使用它,您需要一个包含两个属性Key和Count的表。尽管有这个名字,但dirty_update_counter是原子的,即使它不在事务中运行。

Ulf Wiger在他的rdbms package中在mnesia上提供了典型的RDBMS功能。他的代码提供了外键约束,参数化索引,字段值约束等。不幸的是,这段代码在两年内没有更新,如果没有相当多的Erlang经验,可能很难运行。

在设计和使用mnesia时,您应该记住mnesia不是关系数据库。它是一个事务性键/值存储,并且在您不规范化时更容易使用。

如果您的用户名是唯一的,则可以使用架构:

-record(user, {name, salt, pass_hash, email}).
-record(entry, {posted, title, body, slug, user_name}).

posted是文章上传时的erlang:now()时间。如果您经常需要检索用户的所有文章列表,user_name可能需要二级索引。由于此数据分为两个表,因此您必须在应用程序代码中强制执行任何完整性约束(例如,不接受没有有效user_name的条目)。

mnesia中的每个字段值都可以是任何erlang术语,因此如果您在任何一个特定字段上输入唯一键,您通常可以组合一些字段来为您提供始终唯一的值 - 也许{Username,DatePosted,TimePosted}。 Mnesia允许您通过mnesia:select(Table, MatchSpec)搜索部分密钥。 MatchSpecs很难手工编写,所以请记住ets:fun2ms/1可以将psuedo erlang函数转换为matchspec。

在此示例中,fun2ms为我们生成了一个matchspec,用于搜索其中键为-record(entry, {key, title, slug, body}).的博客条目表{Username, {Year, Month, Day}, {Hour, Minute, Second}} - 作者的用户名以及文章发布的日期和时间。以下示例在2008年12月期间按TargetUsername检索所有博客帖子的标题。

ets:fun2ms(fun (#entry{key={U, {Y,M,_D}, _Time}, title=T})
             when U=:=TargetUsername, Y=:=2008, M=:=12 ->
               T
           end).