みんなに優しく、解りやすくをモットーに開設しています。 以下のルールを守りみんなで助け合いましょう。
1.ファイルメーカーで解らない事があればここで質問して下さい。 何方でも、ご質問・ご回答お願いします。 (優しく回答しましょう)
You are not logged in.
Pages: 1
いつも参考にさせて頂いております。
Filemaker Pro15
windows7,10(端末混在)
社内イントラネットで使用しています。
基幹システムからデータをインポートして、ユーザーが検索したり中身を確認して処理済みチェックをつけたりするような仕組みを作ろうとしています。
インポートするデータは以下のような構成です。
テーブルA―テーブルBーテーブルCーテーブルD
AーB(1:多)
BーC(1:多)
CーD(1:1 DのないCもある)
レコード数 A:8千件、B:3万件、C:8万件、D:2千件
A〜Dの元データは定期的に(1分毎とか)全入替です。
ユーザーはAから任意のレコードを検索して選択し、Cで詳細を確認して処理します。Dは備考テキストです。
ユーザーの検索が重くならないように、データ取り込み時に、Cの内容に基づく条件を全置換でAに転記したり、計算しないと出ない条件は「テキスト(計算値置き換え)」で書き出しておいたりしようとしています。またCでも検索する場合があり、非保存でない形で条件を保存しておこうとしています。
その中で1つ時間のかかってしまう処理があるので、なにかいい方法があるかお知恵をお借りしたく、投稿いたしました。
テーブルCにあるフィールドのテキストと、そのCに関連するDがあればそのDのテキストをリストにして書き出した計算フィールド(非保存)があります。
フィールドc1:テキスト例「自由入力コメント」
フィールドd1:テキスト例「4月5日から開始」
フィールドc2:以下の非保存計算
let([
_c1=テーブルC::フィールドc1
;_d1=テーブルD::フィールドd1
];
case(
_d1="";_c1
;_c1 & ¶ & _d1))
これの、テーブルBとのリレーションが同じものを1フィールドにまとめ、Cのすべてのレコードに書き込みたいのです。
レコードC1::フィールドc2
「自由入力コメント
4月5日から開始」
レコードC2::フィールドc2
「<<青色>>」
レコードC1C2がテーブルBの同じレコードの関連レコードである場合、両方のフィールドc3
「自由入力コメント
4月5日から開始
<<青色>>」
このフィールドc3を、索引のできるフィールドにしたく、list関数でテーブルBに書き出したものを全置換したり、テーブルCを自己リレーションしてlistを全置換で書き込んだり、計算値をcsvにエクスポートしたりしてみたのですが、結局8万件分list計算しているので3分ほどかかってしまいます。
なにか良い方法はないものでしょうか。
ご教示のほど、どうぞよろしくおねがいいたします。
Offline
「A〜Dの元データは定期的に(1分毎とか)全入替」
これがそもそも無理な気がしますが...
確かにそうでした。理想は1分なのですが、そんなにこまかくいらないし、データインポートに1分位かかるので、取り込み+諸々の加工処理を2分位で終わらせて3分休んでまた処理開始、の5分毎くらいでいいかなと考えています。なので1処理に3分かかるのはちょっと困ったな、というところでのご相談です。よろしくおねがいします。
Offline
その場合実質稼働率が60%なシステムですけど...
基幹システムから取り出す時点で加工できないんですか?SQLサーバとかなら、そっちでやった方が速そうな気がします。
更新用に1端末使ってまして、インポートと加工が終わったら更新前のデータに不使用フラグを立てて差し替えとするので、他端末では稼働率が下がらない形です。
加工には基幹システムにない別テーブルのデータも当てているので、取り出し時の加工は難しいかもです。けどそちらも検討してみますね。ありがとうございます。差し支えなければ本題の方へのご意見もよろしくお願いいたします。
Last edited by masaki (2021-04-10 18:43:47)
Offline
データを全置換するデータベースでは、置換のたびにインデックスを全て作り直す事になりますので、早く動かすことは無理だと思います。
既存のデータの置き換えは必要なのですか。
具体的にどの様なシステムなんでしょうか。医療系の検査結果の管理ですか?
Offline
医療系の処方情報の利用です。現在、差分のみ定期的に更新インポートしていますが、取り込み漏れが生じることがあり、いっそ全て取り込み直しでと考えた次第です。やはり早く動かすのは無理ですか。差分更新の精度見直しのほうが現実的でしょうか。
Offline
処方ということは、薬袋の印刷ですか。薬品監査でしょうか。
テーブルC::フィールドc1、テーブルD::フィールドd1、レコードC1::フィールドc2のフィールドを、目的のテーブルにルックアップで取り込んでおくと、それを利用した計算フィールドも索引をつくれます。または、自動入力での計算式に設定しておいてもいいでしょう。
取り込み漏れは、おそらく、他のユーザーによるアクティブレコードに対する漏れでしょう。対策はエラーを取得するといいのでしょうが。エラーがあれば、インポートされたレコードと、受信したレコードの差がエラーになっているはずです。
別の方向からの対策として、更新用端末の中にインポートの中間ファイルを作られればいかがでしょう。インポートするデータをそのまま保存しておくテーブルで、ユーザーが直接アクセスしません。索引は作らないので、処理は早くなります。
そのテーブルから今と同じようにインポートするのですが、インポートした時点でターゲットのテーブルはエラーのないレコードが対象になっています。そこから中間テーブルへ関連テーブルへ移動させ、対象外を表示にすればエラーのレコードが見えます。そこだけを再インポートさせればいいです。
または、loop で1レコードごとに、リレーションを通して運用テーブルへフィールド設定していく方法でもいいでしょう。
この処理をインポートに続けで頻回に動かしておけば、最小限のタイムラグでの同期が可能だと思います。実運用率も100%近くになるでしょう。
Last edited by Shin (2021-04-11 08:45:04)
Offline
ご教示ありがとうございます。薬品監査と薬歴管理です。
ルックアップや計算値などで必要なデータを1テーブルに保存した上で扱うのが最速ということですね。フィールドの関係と設定を見直してみます。
インポートの中間ファイルについては現行の差分取り込みでも作成しているのですが、取り込み後エラーチェックせずに消して次のデータを入れていました。関連レコードへ移動はほとんど使ったことがなかったですがこういうときに使えるのですね。
基幹→中間ファイル(加工・計算)→メインへインポート→古いのを消す→メインから中間ファイルの関連レコードへ移動→対象外を表示→メインへ追加インポート
こんな流れでしょうか。早速週明けに組んでみたいと思います。ありがとうございました。
Last edited by masaki (2021-04-11 10:53:56)
Offline
フィールドc1:テキスト例「自由入力コメント」
フィールドd1:テキスト例「4月5日から開始」
フィールドc2:以下の非保存計算
let([
_c1=テーブルC::フィールドc1
;_d1=テーブルD::フィールドd1
];
case(
_d1="";_c1
;_c1 & ¶ & _d1))
これは、List ( _c1 ; _d1 ) だけでいいのでは。末尾に改行が追加されるので、不要ならば適当に処理。
これの、テーブルBとのリレーションが同じものを1フィールドにまとめ、Cのすべてのレコードに書き込みたいのです。
テーブルB に、List ( テーブルC::フィールドc2 ) という計算フィールドを作っておき、テーブルC から参照するといいのです。c3は自動入力でルックアップにしておくと索引を作れます。うまくルックアップしてくれなければ、再ルックアップです。
または、テーブルBからテーブルCへのリレーションを一つ追加して、テーブルC の中に、List ( テーブルC2::フィールドc2 ) という計算を自動入力にしたフィールドでもうまく動くかもしれません。
Offline
ところで、このフィールドは、テーブルBにあるべきなのでは。
索引が必要なのは、何故ですか。
Offline
ご指摘ありがとうございます。計算フィールドが減らせますね。リレーションとルックアップも明日やってみます。
ところで、このフィールドは、テーブルBにあるべきなのでは。
索引が必要なのは、何故ですか。
たしかに、A-B-C-Dをセットで扱う分にはテーブルBにあるべきフィールドです。あえてCに転記しようとしているのは、テーブルCのデータの一部だけ別に保存して薬歴管理に使用したいからです。
具体的に申しますと、テーブルAが伝票、BがRp、Cが処方内容(薬品用法日数の行混在で処方1行1レコード)、Dが自由入力コメントです。はじめに上げたレコード数は半月分で、当日の前後規定日数分だけ取得し、日が経てば消していきます。
消える前に、Cの薬品のレコードだけに同じRpの用法行の内容や伝票番号を合体させて単独で扱えるようにして取っておき、薬歴として半年〜1年分程度見られるように使いたいのです。
と、書いていて気づきましたが、これ索引不要ですかね。保存用のCにはd1の転記も入れるので、検索が必要ならc1やd1転記(あるいは非保存でないc2)を使って、c3は表示するとき計算すればすむような気がしてきました……
Offline
別に保存するのでしたら、その際に合体させればいいのでは。
上のレコード数を考えて、1年で千万レコード未満ですよね。サーバーが十分な容量と速度を持っていれば、その程度のレコードは十分に管理できますよ。それと、テーブルBは、持つ情報量は少ないでしょうから、そのまま持たせておいてもいいのでは。また、処方内容について、同じ人には同じ処方内容が続くことが多いと思います。それをまとめる工夫をされると、レコード数は数分の一になるのでは。
以前管理していた医療機関のデータベースでは、薬歴より病歴のレコード数が多くて、数千万病名が入っていました。FM11でしたが、特に問題なく動いていたようです。
Offline
数式とフィールドの持ち方を変えたところ、3分かかっていた処理は90秒でできました。(ルックアップの時間は別にかかります)
あらためて考えて、#13でご提案頂いたように、各テーブルをそのまま置いてデータを足して行く方が無理なく扱えるように思いました。各フィールドについて、非保存で良いか索引が必要か見直してみようと思います。
ありがとうございました。
Offline
実際の運用を考えて、90秒になっても根本的な解決にはなっていないはずです。
サーバー稼働率を十分高くして、ユーザーとのバッティングが少ない方法として、#8の最後に提案している方式を取られることを強くお薦めします。
差分を中間ファイルへインポートさせるとして、中間ファイルから運用ファイルへリレーションを張っておき、(仮にIDで張るとして)
エラー処理(オン)
検索実行 //未同期のレコードを選択
レコードへ移動(最初の)
loop
計算結果を挿入 [選択; リレーション::ID; ID]
If ( Get ( 最終エラー ) = 0 )
レコード確定
フィールドへ移動[リレーション::ID]
フィールド設定[リレーションフィールドA;フィールドA]
・・・・
フィールド設定[同期成功;Get(タイムスタンプ)]
endIf
レコードへ移動(次の;最後で終了)
end loop
というスクリプトを適当な時間毎に走らせておきます。インポートと一体にしておいてもいいでしょう。
この方式でしたら、理論嫡には中間サーバーへのインポートは脱落が無いはずです。中間サーバーからアクセスする瞬間のみユーザーからのアクセスは拒否され、ユーザーがアクティブにしているレコードは、次のタイミングで再実行されますので、更新のタイミングは遅れますが、ユーザーが忘れていて数時間後に開放しても、最終的に動機は問題ありません。
差分レコードは、情報量から考えて1分に1レコード程度でしょうから、ゆっくりの更新でも間に合うのではないでしょうか。
Last edited by Shin (2021-04-15 10:53:36)
Offline
全レコードを中間テーブルで加工してからインポートしたら10分かかったのでさすがにあきらめて、データ作成日時を頼りに差分インポートする形に戻しました。それでもフィールドなどを整理して大分すっきりしましたし、教えていただいた関連レコードでの再取り込みも組み込んだので今後の動きが楽しみです。ありがとうございました。
ところで再取り込みもインポートで組んでしまったのですが、loopを使う#15は、中間テーブルのレイアウトに、
運用テーブル(リレーションによるレコード作成可)の必要フィールドを配置して、未同期のデータについてのレコードを作成していく、という理解で良いでしょうか。件数が少ないとこちらのほうこちらの方が速そうですね。
Offline
中間テーブルのレイアウトに配置するのは、運用テーブルのキーになるフィールドのみで、その他のフィールドは配置不要です。(動きを見るために配置してもいいですが、遅くなります)
こちらの方が遅いのですが、インポートで同期すると、エラーになったレコードを中間テーブルで抽出する手間が大きく、データの不整合のエラーは検出が難しいことがあるので、エラーが出た場合には時間がかかります。
Offline
テストしてみました。たしかに同期の検索は時間かかるときがありますのでこちらさくさくで良いですね。レイアウトにフィールドを配置しないとできない操作となくてもできる操作が覚えきれておらず、そのあたりにも軽量化する余地がありそうです。ありがとうございます。
#15の意味の確認ですが、
中間テーブルレイアウトで、
キーになるフィールド「*」を検索
対象外を表示
(=合致しないレコード)
リレーションキーを挿入して運用テーブルにレコード作成
できたら他の項目も転記
エラー(他ユーザーが使っている)なら飛ばす
ということでよいでしょうか。
レコード確定後、一度キーフィールドを選択しているのは、なにか大切な動きですか?
Last edited by masaki (2021-04-16 16:00:05)
Offline
スクリプトにコメントを追加しておきます。
エラー処理(オン)
検索実行 //未同期のレコードを選択:同期成功 = ""
レコードへ移動(最初の)
Loop
計算結果を挿入 [選択; リレーション::ID; ID] //新規レコードを作成、または、既存レコードでは上書きし、エラーになれば別のクライアントがアクセス中
If ( Get ( 最終エラー ) = 0 )
レコード確定
フィールドへ移動[リレーション::ID] //そのレコードをアクティブにして、他のクライアントへ排他をかける
フィールド設定[リレーションフィールドA;フィールドA]
・・・・
フィールド設定[同期成功;Get(タイムスタンプ)] //同期完了
end If
レコードへ移動(次の;最後で終了)
end Loop
もっと厳密に排他管理を行うには、
フィールドへ移動[リレーション::ID]
If ( Get ( 最終エラー ) = 0 )
フィールド設定[リレーションフィールドA;フィールドA]
・・・・
フィールド設定[同期成功;Get(タイムスタンプ)]
end If
を追加します。
なお、中間テーブルへは、常に新規インポートのみを行います。同期が終わったレコードは、同期の検証のために保存がお勧めですが、随時削除してもいいでしょう。
Last edited by Shin (2021-04-17 13:36:13)
Offline
ご説明ありがとうございます。できました。
中間テーブルのレコードは、取り込みできたものは削除するようにしました。
過去1時間に更新された差分取り込みで30秒弱。
ストレスなくさくさく回っています。
Offline
Pages: 1
[ Generated in 0.027 seconds, 9 queries executed - Memory usage: 577.29 KiB (Peak: 613.83 KiB) ]