10

我最近一直在研究 Erlang,我决定使用 Mnesia 来完成我的数据库工作,因为它可以毫无问题地存储任何类型的 Erlang 数据结构、轻松扩展、与列表推导一起使用等。

来自标准 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 中表示多对多关系的更好方法是什么?

4

2 回答 2

9

我更喜欢使用 GUID 而不是自动递增整数作为人工外键。GitHub 上提供了一个Erlang uuid 模块{now(), node()},或者您可以使用,因为该now/0文档说:“还可以保证对该 BIF 的后续调用返回不断增加的值。”

在我看来,使用可以改变的东西作为主键似乎是一个独立于数据库系统的坏主意。

不要忘记您不需要将 Mnesia 中的数据标准化为第一范式;在您的示例中,我将考虑以下结构:

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

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

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

于 2008-12-27T19:59:49.787 回答
8

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

Ulf Wiger 在他的rdbms 包中在 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如果您经常需要为用户检索所有文章的列表,则可能需要二级索引。由于此数据被拆分到两个表中,因此您必须在应用程序代码中强制执行任何完整性约束(例如,不接受没有有效用户名的条目)。

mnesia 中的每个字段值都可以是任何 erlang 术语,因此,如果您对任何一个特定字段的唯一键感到茫然,您通常可以组合一些字段来为您提供一个始终唯一的值 - 可能是 {Username,发布日期,发布时间}。Mnesia 允许您通过mnesia:select(Table, MatchSpec). MatchSpecs 很难手动编写,因此请记住,它ets:fun2ms/1可以为您将伪 erlang 函数转换为 matchspec。

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

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