2011-01-03 3 views
3

私は、mnesiaがMySQLや他のRDBMSのように自動インクリメント機能をサポートしていないことに気付きました。mnesiaのドキュメントで述べられているカウンターは実際にはよく説明されていません。私はカウンター
mnesia DBMSで増分カウンタを管理していますか?

mnesia:dirty_update_counter({Tab::atom(),Key::any()}, Val::positive_integer())

を操作するドキュメント全体にSOFAR一つの機能を発見した例えば、これはこれも不明で、おそらくいないのerlang帳ためであるタイプ

{TabName, Key, Integer}
の記録と作品だって時間のために私を妨げています私は自分のカウンタ操作APIを実装しなければなりませんでした。カウンターを使って自分のレコードにアクセスして管理したいと思ったので、レコードに「カウンター」というフィールドを含める必要がありました、カウンターを持つ予定のテーブルのレコードを追加する時点で、私は次のようにします:

#recordname{field1 = Val1,...,counter = auto_increment(?THIS_TABLE)}

カウンタフィールドの位置は重要ではありません。カウンタフィールドは、私が無理そうならば、その理由を終了するには、このコードを実行しようとしているプロセスを聞かせてテーブルの属性をISNOT場合は、これを説明するため

%% @doc this function is called whenever u are writing a new record in the table
%% by giving its result to the counter field in your record.
%% @end
%%
%% @spec auto_increment(TableName::atom()) -> integer() | exit(Reason)
auto_increment(TableName)-> case lists:member(counter,table_info(TableName,attributes)) of false -> erlang:exit({counter,field,not_found,in_table,TableName}); true -> table_info(TableName,size) + 1 end.
table_info(Tab,Item)-> F = fun({X,Y}) -> mnesia:table_info(X,Y) end, mnesia:activity(transaction,F,[{Tab,Item}],mnesia_frag).

:APIは以下のように実装されていますプログラマはtry ... catch、またはbody(catch ...)の中でこれを呼び出すと、何が間違っているのかを簡単に知ることができます。また、mnesia:is_transaction()を使用してこのコードフラグメントがトランザクション内で実行されているかどうかを尋ねることができ、これがtrueを返す場合はmnesia:abort/1に、falseの場合はreason.Alsoで終了できます。また、mnesia_fragをmnesia_frag実装はテーブルの断片化プロパティに関係なく機能します。mnesia:transaction(Fun)を使用すると、この呼び出しは最初のフラグメント(基本テーブル)にしかアクセスしないため、フラグメント化されたテーブルの矛盾が生じます。
これで、カウンタを持つテーブルからレコードが削除されたときに、テーブル内の順序を変更する必要があります。カウンタが5のレコードを削除すると、counter = 6のレコードはcounter = 5になる必要があります。カウンターが削除されたものより大きいすべてのレコードは減分する必要があります。だから、ここで
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), 
    case lists:member(counter,Attrs) of 
     false -> erlang:exit({counter,field,not_found,in_table,TableName}); 
     true -> 
      Counter_position = position(counter,Attrs) + 1,   
      Iterator = fun(Rec,_) when element(Counter_position,Rec) > Counter_deleted -> 
          Count = element(Counter_position,Rec), 
          New_rec = erlang:setelement(Counter_position,Rec,Count - 1), 
          mnesia:write(TableName,New_rec,read), 
          []; 
         (_,_) -> [] 
         end, 
      Find = fun({Fun,Table}) -> mnesia:foldl(Fun, [],Table) end, 
      mnesia:activity(transaction,Find,[{Iterator,TableName}],mnesia_frag) 
    end. 


あなたは、私が私を助け、コードを持っていることに気づきますレコードからカウンタフィールドの位置を動的に探します。私はこれを行うことができますコードを以下に示します。

 

position(_,[]) -> -1; 
position(Value,List)-> 
    find(lists:member(Value,List),Value,List,1). 

find(false,_,_,_) -> -1; 
find(true,V,[V|_],N)-> N; 
find(true,V,[_|X],N)-> 
    find(V,X,N + 1). 

find(V,[V|_],N)-> N; 
find(V,[_|X],N) -> find(V,X,N + 1). 
 


このモジュールは、カウンタの値にアクセスするためにcounters.Henceで彼を助けるためにプログラマのレコードのいずれかを知ってはいけないので、これがそうですelement(N::integer(),Tuple::tuple())のようなタプル操作関数を使用してレコードから、私はレコードのタプル表現でその位置を動的に計算しなければなりません。 、ダイナミックな制約のあるテーブルを照会下記のコードのこれらの作品を検討するQLC(クエリリストの内包表記)を使用して、例えば

These two functions have worked for me and are still working till auto_increment
is implemented in mnesia.

、:在庫管理システムでは

 

select(Q)-> 
    F = fun(QH) -> qlc:e(QH) end, 
    mnesia:activity(transaction,F,[Q],mnesia_frag). 

read_by_custom_validation(Validation_fun,From_table)-> 
    select(qlc:q([X || X <- mnesia:table(From_table),Validation_fun(X) == true])). 

%% Applying the two functions.... 
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}) when N >= From_this,N =< To_That -> true; (_) -> false end, read_by_custom_validation(F,TableName).

、これが働いています...



([email protected])6> stock:get_items_in_range(1,4). 
[#item{item_id = "D694",name = "cement", 
     time_stamp = {"30/12/2010","11:29:10 am"}, 
     min_stock = 500,units = "bags",unit_cost = 20000, 
     state = available,last_modified = undefined, 
     category = "building material",counter = 1}, 
#item{item_id = "131B",name = "nails", 
     time_stamp = {"30/12/2010","11:29:10 am"}, 
     min_stock = 20000,units = "kgs",unit_cost = 1000, 
     state = available,last_modified = undefined, 
     category = "building material",counter = 2}, 
#item{item_id = "FDD9",name = "iron sheets", 
     time_stamp = {"30/12/2010","11:29:10 am"}, 
     min_stock = 20,units = "bars",unit_cost = 50000, 
     state = available,last_modified = undefined, 
     category = "building material",counter = 3}, 
#item{item_id = "09D4",name = "paint", 
     time_stamp = {"30/12/2010","11:29:10 am"}, 
     min_stock = 30000,units = "tins",unit_cost = 5000, 
     state = available,last_modified = undefined, 
     category = "building material",counter = 4}] 
([email protected])7> 

これは私のために働いています。それ以外のカウンターをどうしたらいいか教えてください。そうでなければ、どうしたらいいのか教えてください。

答えて

4

問題に最適な解決策はありますか?私は高度に分散されたシステムがおそらくそれらを持っていないほうが良いと考えているので、それらは除外されています。

  1. テーブルに含まれる要素の数をカウントする場合は、レコード{counts、TableName、Count}の別のテーブルを持ち、ストレージテーブルのトランザクションの追加と削除でdirty_update_counter操作を使用します。
  2. 特定の順序(たとえば、追加された順序)でデータを取得する場合は、OrderedByソリューション(here)を使用して調べます。
  3. 高速な外部キー参照が必要な場合は、キーのタイプ(item_idのバイナリ値がおそらく速い)で遊んでみて、大幅な改善があればテストします。

また、テーブルエントリが削除されたときに、すべてのレコードのカウンタを更新する必要がある理由はわかりません。私はMySQLや他のRDBMSがこれをしたとは思わなかった。

+0

ありがとう、Taylor! –

+0

リンクが死んでいます。これは正確に外部サイトの回答にリンクするのではなく、ここで完全な回答を提供する理由です。 – drozzy

関連する問題