4D 自力distinct

4D 自力distinctを作ってみたので紹介する。このコマンドはv17、v18で動作確認済み。

4DのDistinctコマンド

4DにはDistinctというコマンドがある。テーブルをクエリーしてセレクションができて、そのテーブルのフィールドの重複を排除したユニークな値の配列を取得できる便利なコマンドだ。

Distinctにはフィールドを一つしか与えることができない。たとえばユニークなコードの配列をください、そのコードに対応した名前の配列(ユニークでないかもしれない)を同時にください、というときは不便。Distinctを実行してもセレクションができるわけではないので、別のフィールドの値を取得するには、得られた配列のユニーク値をもとにしてクエリーし直さなくてはならない。

自力ディスティンクト

そこで「自力ディスティンクト」を作ってみた。ディスティンクトで得られるユニークなフィールドの配列を、データベース側のセットに実現することでセレクションを作る。このモジュールを呼び出すとIDがユニークな配列が返されると同時に、その配列の元になるカレントセレクションが用意される。呼び出し元ではユニークフィールド以外のフィールドの値はSelection to arrayで簡単に持ってこれる。セットを使っているので高速に動く。

このモジュールの特徴は、テーブル名を決め打ちしていることと、ユニークフィールドを整数に限定していること。別のテーブル用とかユニークフィールドが文字列以外の場合はこのモジュールを複製・修正して使う。MVCのモデルメソッドという位置付けである。

//JO_Distinct_byAC_ID
//20200821 wat
//自力Distinct //ジャーナルの勘定科目とカテゴリー
//セレクションは上位で作成しておく
//distinctキーとなるフィールドのポインタは整数、整数の結果配列を返す。

C_POINTER($1;$keyFldPtr)
$keyFldPtr:=$1
C_POINTER($2;$outIDAryPtr)
$outIDAryPtr:=$2 //結果の、IDの配列

C_LONGINT($ac_id)
C_LONGINT($i;$numOfRecs)
C_TEXT($setOrg;$setName)
$setOrg:=”set_org”
CREATE SET([JOURNAL];$setOrg) //最初は全件、評価したら除いていく
$setName:=”result_set”
CREATE EMPTY SET([JOURNAL];$setName) //最初は0件、まだないIDを追加していく

//元セットにレコードがなくなるまで繰り返す
USE SET($setOrg)
$numOfRecs:=Records in selection([JOURNAL])
FIRST RECORD([JOURNAL])
$ac_id:=$keyFldPtr->

While ($numOfRecs>0)
//結果セットを日付でクエリして、なければ追加、
USE SET($setName)
QUERY SELECTION([JOURNAL];$keyFldPtr->=$ac_id)
If (Records in selection([JOURNAL])=0)
//DB全体をクエリして最初のレコードを取得して、セットに追加
QUERY([JOURNAL];$keyFldPtr->=$ac_id)
FIRST RECORD([JOURNAL])
ADD TO SET([JOURNAL];$setName)

End if

//元のセットを日付でクエリして、元のセットからのぞく
USE SET($setOrg)
QUERY SELECTION([JOURNAL];$keyFldPtr->=$ac_id)
For ($i;1;Records in selection([JOURNAL]))
GOTO SELECTED RECORD([JOURNAL];$i)
REMOVE FROM SET([JOURNAL];$setOrg)

End for

//リデュースされた元セットの最初のレコードを取得
USE SET($setOrg)
$numOfRecs:=Records in selection([JOURNAL])
FIRST RECORD([JOURNAL])
$ac_id:=$keyFldPtr->

End while
CLEAR SET($setOrg)

//転送
USE SET($setName)
CLEAR SET($setName)


SELECTION TO ARRAY($keyFldPtr->;$outIDAryPtr->) //カレントセレクションがあるので、呼び出し元ではお好みのフィールドの値をSELECTION TO ARRAYで取得できる

使い方

呼び出し元では次のようにコーディング。この例では元となるセレクションは全件、このように元となるセレクションを作っておいて、そのセレクションの中でユニーク評価をして新たなセレクションを作る。呼び出し後に必要なフィールドの値を転送。

READ ONLY([JOURNAL])
ALL RECORDS([JOURNAL])<;br> JO_Distinct_byAC_ID (->[JOURNAL]JO_D_AC_ID;->$aryAC_ID)
SELECTION TO ARRAY([JOURNAL]JO_D_AC_CODE;$aryAC_CODE)
// SELECTION TO ARRAY(…