我最近一直潜入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中代表多对多关系会有什么更好的方式?
我更喜欢使用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}).
其中entries
和users
是ids的列表.当然,这取决于您想要的查询.
编辑:固定为多对多而不是多对一.
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
如果您经常需要为用户检索所有文章的列表,则可能需要辅助索引.由于此数据分为两个表,因此您必须在应用程序代码中强制执行任何完整性约束(例如,不接受没有有效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}).
,其中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).