0

レコード内の値に応じて、データベースを介してプログラムがレコードを追跡するのに少し問題がありました。簡単に言えば、これが私が達成しようとしていることです。

これは、私が使用しているテーブルの簡略化されたバージョンです。

Record Name  |  Val1  | Val2  | Link |  Prev Link |
Rec1         |   5    |  3    | Rec2 |            |
Rec2         |   2    |  4    | Rec6 |  Rec1      |
Rec3         |   1    |  8    | Rec4 |            |
Rec4         |   1    |  1    |      |  Rec3      |
Rec5         |   8    |  3    |      |            |
Rec6         |   9    |  3    |      |  Rec2      |

私のプログラムは、上記の表を調べ、特定の1つのレコードに対して情報を保存し、リンクされたレコードに移動し、前のレコードの値をクリアしてから、新しいレコードに追加する必要があります(最後に到達するまでこれを実行し続ける必要があります)チェーンの)、ここでの例として、私のプログラムを実行した後に何が起こるべきかを示します。

Record Name  |  Val1  | Val2  | Link |  Prev Link |
Rec1         |   0    |  0    | Rec2 |            |
Rec2         |   0    |  0    | Rec6 |  Rec1      |
Rec3         |   0    |  0    | Rec4 |            |
Rec4         |   2    |  9    |      |  Rec3      |
Rec5         |   8    |  3    |      |            |
Rec6         |   16   |  10   |      |  Rec2      |

私が使用している現在の手順は、次の場所にあります:http: //pastebin.com/A10hW0C6

私が直面している主な問題は、プログラムが各レコードを通過し、リンクをたどってから、中断した場所に戻って見逃さないようにすることができないことです。また、プログラムを無視させるにはどうすればよいですか。ループの一部としてすでに終わっていることを記録しますか?

どんな助けもいただければ幸いです:)

4

3 に答える 3

0

できることは、次のようなリンク開始レコードを保持する別のクエリを用意することです。

qry1.sql := 'select * from table where prev_link is null;';

これは与える

Record Name  |  Val1  | Val2  | Link |  Prev Link |       
Rec1         |   5    |  3    | Rec2 |            |       
Rec3         |   1    |  8    | Rec4 |            |       
Rec5         |   8    |  3    |      |            |       

次に、結果データセットをたどり、他のクエリ (query2) をシーク/検索して、そこに処理ロジックを適用できます。

結果の日付セットを終了するまでに、作業は完了です。もちろん、これは、データが正当である、つまりリンク切れや循環リンクなどがないことを前提としています。

いくつかの強化。「ステータス」という列を追加して、レコードのステータスを反映できます。たとえば、ステータス = 0 は「未処理」、「1」は処理済み、「2」は壊れたリンク、「3」は循環リンクなどを意味します。すべてのステータス列に 0 (未処理) を入力することから始めることができます。

[リンク] 列でレコードを検索/検索しても見つからない場合 (何らかの理由で削除された可能性があります)、ステータスに「2」のフラグを立てることができます。

リンクをたどるたびに、たどった記録を追跡します。たとえば、リストを使用できます。「リンク」列のレコードをたどる前に、リストを確認してください。レコードがリストにある場合は、循環リンクがあります。フォローを停止し、ステータスを '3' (循環リンク) にマークし、リストをクリアして、query1 の次のレコードから開始します。循環リンクを扱うことは重要です。

リンクのチェーンの処理が終了したら、リスト内のすべてのレコードのステータスを「1」にフラグします。

データベース トランザクション (begin transaction ...end transaction) を利用できるため、リンク チェーンでデッドリンクまたは循環リンクが発生した場合、変更された値をロールバックし、それに応じてステータスにフラグを立てることができます。

終了後、ステータス列を確認できます。すべて「1」の場合、すべて正常 (処理済み) であることを意味します。そうでない場合は、次に何をすべきかを決めることができます。

列のステータスを使用して、後続の別の操作で既に処理されたレコードを除外することができるため、上記のクエリを変更できます。

qry1.sql := 'select * from table where prev_link is null and status = 0;';

もちろん、これは予備的な戦略であり、自分に合わせて変更できます。

于 2012-07-25T02:18:15.560 に答える
0

使用しているデータベースによっては、これはサーバー上の SQL だけで解決できます。Firebird の例を次に示します (リンクが一貫しており、参照整合性の制約とトリガーによって強制される可能性のある無限再帰がないことを前提としています。これらはここには含まれていません)。

/* metadata */
set sql dialect 3;

create table test (
    id       integer not null,
    val1     integer,
    val2     integer,
    next_id  integer,
    prev_id  integer
);

alter table test add constraint pk_test primary key (id);
alter table test add constraint fk_test_next_id foreign key (next_id) references test (id) on update cascade;
alter table test add constraint fk_test_prev_id foreign key (prev_id) references test (id) on update cascade;

/* data */
insert into test (id, val1, val2, next_id, prev_id) values (1, 5, 3, 2, null);
insert into test (id, val1, val2, next_id, prev_id) values (2, 2, 4, 6, 1);
insert into test (id, val1, val2, next_id, prev_id) values (3, 1, 8, 4, null);
insert into test (id, val1, val2, next_id, prev_id) values (4, 1, 1, null, 3);
insert into test (id, val1, val2, next_id, prev_id) values (5, 8, 3, null, null);
insert into test (id, val1, val2, next_id, prev_id) values (6, 9, 3, null, 2);

/* update statement (could also be a stored procedure) */
execute block
as
  declare variable id integer;
  declare variable val1 integer;
  declare variable val2 integer;
begin
  for with recursive test_list (
    id,
    val1,
    val2,
    next_id
  )
  as (
    select
      t.id,
      t.val1,
      t.val2,
      t.next_id
    from test t
    where (t.prev_id is null)
    union all
    select
      t.id,
      t.val1 + tl.val1,
      t.val2 + tl.val2,
      t.next_id
    from test t
    join test_list tl on (tl.id = t.prev_id)
  )
  select
    tl.id,
    iif(tl.next_id is null, tl.val1, 0) val1,
    iif(tl.next_id is null,  tl.val2, 0) val2
  from test_list tl
  order by tl.id
  into
    :id, 
    :val1,
    :val2
  do
    update test set
      val1 = :val1,
      val2 = :val2
    where (id = :id);
end
于 2012-07-25T08:05:44.597 に答える
0

TOndrej は非常に良い答えをくれましたが、そのためには SQL をよく知っている必要があります。それは報われます-正しいSQLを作成すると、データベースの信頼性が高まり、プログラムのエラーによってデータベースデータが破損することはありません. しかし、学ぶ時間はあるでしょう。また、彼の SQL はかなり最近の Firebird バージョンの機能を使用しており、言葉は NexusDB や他のサーバーにはほとんど当てはまりません。

これはもっとばかげたアプローチです。あなたも試してみてください。

それで、私があなたの仕事を理解していれば、テーブルはチェーンのセットに分割されますか? ここでは、 Rec1->Rec2->Rec6Rec3->Rec4Rec5の 3 つのチェーンがあります。

新しい要素を追加すると、常に末尾に移動するため、Rec1->Rec2->Rec6->NewRec7になりますが、 NewRec7->Rec1->Rec2->Rec6Rec1->Rec2->NewRec7-になることはありません。 >Rec6 .

それはすべてそうですか?

次に、ルートからの列の距離を追加できます

Record Name  |  Val1  | Val2  | Link |  Prev Link | Dist |
Rec1         |   5    |  3    | Rec2 |            |  0   |
Rec2         |   2    |  4    | Rec6 |  Rec1      |  1   |  
Rec3         |   1    |  8    | Rec4 |            |  0   |  
Rec4         |   1    |  1    |      |  Rec3      |  1   |  
Rec5         |   8    |  3    |      |            |      |  
Rec6         |   9    |  3    |      |  Rec2      |  2   |  

これは、SQL トリガーまたはプログラムで計算および更新できます。SQL トリガーの方が優れており、信頼性が高くなります。しかし、あなたはそれらを把握する必要があります。 レコードの追加、レコードの削除、またはレコード リンクの変更を行うときはいつでも、影響を受けるすべてのレコードの距離を再計算する必要があります。たとえば、Rec6 を切り取って Rec1->Rec2、Rec6->Rec3->Rec4 に再接続すると、Rec6 と Rec3 と Rec4 の距離を再計算する必要があります。

1) データベースから max(dist) を取得します。

THE_TABLEからMAX(DIST)を選択

2) 0 から max(dist)-1 までのすべての selected_dist に対して

2.1) そのような距離を持つすべてのレコードに対して

SELECT RecordName, Link, Val1, Val2 FROM THE_TABLE WHERE dist = :choosen_dist AND Link IS NOT NULL

2.1.1) 値をインクリメントする次のリンクされたものを更新する

UPDATE THE-TABLE SET Val1 = Val1 + :PrevLinked_Val1; Val2 = Val2 + :PrevLinked_Val2 WHERE RecordName = :NextLink

2.1.2) 選択したものを更新し、値をクリアします

UPDATE THE-TABLE SET Val1 = 0; Val2 = 0 WHERE レコード名 = :現在のレコード名

このアプローチはもっと悪いです:

  1. それほど高速ではありません-1つのプロシージャにバンドルされて1回呼び出される代わりに、多くの個別のステートメントが繰り返されます。
  2. それほど高速ではなく、信頼性も低くなります-プログラムから1行ずつ呼び出すと、サーバー内のすべての計算ではなく、データがサーバーからプログラムへとネットワークを介して飛んでいきます
  3. 距離を計算するときは、非常に注意する必要があります。それらをすべて削除し、何かが壊れた場合に再計算するまで。

また、それはより良いです:

  1. SQL の初心者にとって理解しやすい

  2. 非常に基本的な SQL のみを使用するため、NexusDB、Firebird、SQLite など、任意のサーバーにアクセスできます。

于 2012-07-25T08:57:49.523 に答える