我已经意识到 mnesia 不像 MySQL 或其他 RDBMS 那样支持自动增量功能。在 mnesia 文档中讨论的计数器没有得到很好的解释。例如,到目前为止,我在整个文档中发现了一个操作计数器的函数
mnesia:dirty_update_counter({Tab::atom(),Key::any()}, Val::positive_integer())
所以,这困扰了我一段时间,因为它适用于类型的记录
{TabName,键,整数}这也不清楚,可能是因为没有 erlang 书籍或 mnesia 文档提供示例来解释它。这迫使我实现自己的计数器操作 API。因为我希望能够使用计数器访问和管理我的记录,所以我不得不在我的记录中包含一个名为“计数器”的字段,然后在将记录添加到旨在具有计数器的表中时,我这样做:
#recordname{field1 = Val1,...,counter = auto_increment(?THIS_TABLE)}
计数器字段的位置无关紧要。API的实现如下:
%% @doc 每当您在表
%% 中写入新记录时,都会调用此函数,方法是将其结果提供给记录中的计数器字段。
%% @end
%%
%% @spec auto_increment(TableName::atom()) -> integer() | 退出(原因)
自动增量(表名)-> 案例列表:member(counter,table_info(TableName,attributes)) of false -> erlang:exit({counter,field,not_found,in_table,TableName}); true -> table_info(TableName,size) + 1 结尾。
table_info(Tab,Item)-> F = fun({X,Y}) -> mnesia:table_info(X,Y) 结束, mnesia:activity(transaction,F,[{Tab,Item}],mnesia_frag)。
为了解释这一点,如果计数器字段不是表的属性,我会强制让试图执行此代码的进程退出,因此如果程序员在 try ...catch 或 case ( catch ...) 的 body ,他们很容易看出哪里出了问题。或者,我可以通过 using 询问此代码片段是否正在事务中执行mnesia:is_transaction()
,如果返回 true,我调用mnesia:abort/1
,如果 false,我可以退出原因。此外,我在 mnesia 活动函数中使用 mnesia_frag,因为此实现将无论表的碎片属性如何,都可以工作。如果我使用mnesia:transaction(Fun)
,碎片表将变得不一致,因为此调用只会访问初始碎片(基表)。
现在,当从带有计数器的表中删除一条记录时,我们需要重新排列表中的顺序。此操作代价高昂,因为它需要对整个表进行迭代。因为如果他们删除计数器 = 5 的记录,则计数器 = 6 的记录必须变为计数器 = 5,依此类推。计数器大于删除的记录的所有记录都必须递减。因此,通过传递删除的计数器值和 TableName,可以使用mnesia:foldl/3 or mnesia:foldr/3 , the difference between these two comes in only with ordered table types
以下函数迭代表:
auto_decrement(Counter_deleted,TableName)-> Attrs = table_info(TableName,attributes), 案例列表:member(counter,Attrs) of false -> erlang:exit({counter,field,not_found,in_table,TableName}); 真-> Counter_position = position(counter,Attrs) + 1, Iterator = fun(Rec,_) 当元素(Counter_position,Rec) > Counter_deleted -> 计数=元素(Counter_position,Rec), New_rec = erlang:setelement(Counter_position,Rec,Count - 1), mnesia:write(TableName,New_rec,read), []; (_,_) -> [] 结尾, Find = fun({Fun,Table}) -> mnesia:foldl(Fun, [],Table) end, mnesia:activity(transaction,Find,[{Iterator,TableName}],mnesia_frag) 结尾。
您注意到我有代码可以帮助我从记录中动态找到计数器字段的位置。帮助我执行此操作的代码如下所示:
位置(_,[])->-1; 位置(值,列表)-> 查找(列表:成员(值,列表),值,列表,1)。 查找(假,_,_,_)->-1; 找到(真,V,[V|_],N)-> N; 找到(真,V,[_|X],N)-> 找到(V,X,N + 1)。 查找(V,[V|_],N)-> N; 查找(V,[_|X],N)-> 查找(V,X,N + 1)。
这是因为这个模块必须不知道任何程序员的记录来帮助他使用计数器。因此,为了使用元组操作函数从记录中访问计数器的值,例如element(N::integer(),Tuple::tuple())
,我必须计算它在元组中的位置动态地表示记录。
这两个函数对我有用,并且
在 mnesia 中实现 auto_increment 之前仍然有效。
例如,使用 qlc(查询列表理解)查询具有动态约束的表,请考虑以下这些代码:
选择(Q)-> F = fun(QH) -> qlc:e(QH) 结束, mnesia:activity(transaction,F,[Q],mnesia_frag)。 read_by_custom_validation(Validation_fun,From_table)-> 选择(qlc:q([X || X <- mnesia:table(From_table),Validation_fun(X) == true]))。 %% 应用这两个函数.... find_records_with_counter(From_this,To_that) when
is_integer(From_this),is_integer(To_that),To_that > From_this -> F = fun(#recordName{counter = N}) 当 N >= From_this,N =< To_That -> true; (_) -> 错误 结尾, read_by_custom_validation(F,TableName)。
在库存管理系统中,这是有效的......
(stock_project@127.0.0.1)6> 股票:get_items_in_range(1,4)。 [#item{item_id = "D694",name = "水泥", time_stamp = {"30/12/2010","11:29:10 am"}, min_stock = 500,units = "袋子",unit_cost = 20000, state = 可用,last_modified = 未定义, category = "建筑材料",counter = 1}, #item{item_id = "131B",name = "钉子", time_stamp = {"30/12/2010","11:29:10 am"}, min_stock = 20000,units = "kgs",unit_cost = 1000, state = 可用,last_modified = 未定义, category = "建筑材料",counter = 2}, #item{item_id = "FDD9",name = "铁皮", time_stamp = {"30/12/2010","11:29:10 am"}, min_stock = 20,units = "bars",unit_cost = 50000, state = 可用,last_modified = 未定义, category = "建筑材料",counter = 3}, #item{item_id = "09D4",name = "paint", time_stamp = {"30/12/2010","11:29:10 am"}, min_stock = 30000,units = "罐子",unit_cost = 5000, state = 可用,last_modified = 未定义, 类别=“建筑材料”,计数器= 4}] (stock_project@127.0.0.1)7>
这对我有用。请告诉我我应该如何处理柜台。或者你可以告诉我你是如何在那边处理它们的。