0 フォロワー · 496 投稿

  

InterSystems CachéはマルチモデルのDBMSおよびアプリケーションサーバーです

詳細はこちらをご覧ください

ドキュメント

記事 Hiroshi Sato · 3月 3, 2021 2m read

これは InterSystems FAQ サイトの記事です。
 

InterSystems Data Platform自身には、ソース管理を行う機能はありません。

2016.2以降のバージョンをご使用の場合、統合開発環境として VSCode をご利用いただくことで、VSCode でご利用いただけるソース管理機能をご使用いただけます。

VSCode で ObjectScriptの操作を行うためには、専用エクステンションのインストールが必要です。 

使用方法については、コミュニティの記事「VSCode を使ってみよう!」をご参照ください。

また、「VSCodeでのソースコード管理について」についても併せてご参照ください。

2016.2より前のバージョンをご使用の方でも使用できる方法としては、InterSystems製品 のIDEであるスタジオに、外部のソース管理ツール、製品と連携するためのフック機能があります。

このフック機能は、複数のAPIで構成されており、お客様毎にご自身の環境に合わせて、作りこむことを前提に提供されております。

よく使われるマイクロソフトのVisual Source SafeとSubversion(SVN)については、テンプレートがあります。

このテンプレートを利用すると、作りこみの作業を短縮することが可能です。

0
0 468
記事 Megumi Kakechi · 3月 2, 2021 2m read

これは InterSystems FAQ サイトの記事です。


管理ポータル:システムオペレーション > データベース にあるオプションボタンラジオボタン)「空き容量ビュー」で表示される内容は、システムクラス SYS.Database のFreeSpace クエリで取得可能です。

    

次のようなコードでクエリを実行します。

例:
(%SYSネームスペースにて作成、実行します)

 /// ZISJ.mac
 Set stmt=##class(%SQL.Statement).%New()
 Set status=stmt.%PrepareClassQuery("SYS.Database","FreeSpace")
 Set rs=stmt.%Execute()
 While rs.%Next() {
   Write !
   For i=1:1:9 {
     Write rs.%GetData(i),","
   }
 } 


もしくは、以下のようにも行えます。

0
0 373
記事 Toshihiko Minamoto · 3月 1, 2021 11m read

グローバルをクラスにマッピングする技術 (4/3)

三連載のはずが 4 記事目に突入してしまいました。『銀河ヒッチハイク・ガイド』のファンという方はいませんか?

古くなった MUMPS アプリケーションに新たな生命を吹き込みたいとお考えですか? 以下にご紹介するステップを実行すれば、グローバルをクラスにマッピングし、美しいデータを Object や SQL に公開できます。

上の内容に馴染みが無い方は、以下の記事を初めからお読みください。

グローバルをクラスにマッピングする技術 (1/3)

グローバルをクラスにマッピングする技術 (2/3)

グローバルをクラスにマッピングする技術 (3/3)

この記事は Joel、あなたのために書きます!  前回の例で定義した親子関係を土台に、今度は孫クラスを作成し、^ParentChild グローバルに追加された季節情報を処理したいと思います。

前回と同じ免責事項:  これらの記事を読んでもグローバルがよく理解できないという方は、WRC (Support@InterSystems.com) までメールでお問い合わせください。喜んでサポートさせていただきます。

グローバルをクラスにマッピングするステップ。

  1. グローバルデータが繰り返し使用されるパターンを特定する。
  2. 固有キーの構成を特定する。
  3. プロパティとそれぞれの型を特定する。
  4. クラス内のプロパティを定義する (変数の添え字をお忘れなく)。
  5. IdKey のインデックスを定義する。
  6. Storage Definition を以下の手順で定義する。
    1. 添え字を IdKey まで (IdKey を含む) 定義する。
    2. Data セクションを定義する。
    3. Row ID セクションには触れない。  デフォルトが 99% の割合で適切なので、これはシステムに任せます。
</ol>  7. クラス / テーブルをコンパイルし、テストします。

^ParentChild(1)="Brendan^45956"

^ParentChild(1,"Hobbies",1)="Pit Crew"

^ParentChild(1,"Hobbies",1,"Seasons")="Fall*Winter"

^ParentChild(1,"Hobbies",2)="Kayaking"

^ParentChild(1,"Hobbies",2,"Seasons")="Spring*Summer*Fall"

^ParentChild(1,"Hobbies",3)="Skiing"

^ParentChild(1,"Hobbies",3,"Seasons")="Summer*Winter"

^ParentChild(2)="Sharon^46647"

^ParentChild(2,"Hobbies",1)="Yoga"

^ParentChild(2,"Hobbies",1,"Seasons")="Spring*Summer*Fall*Winter"

^ParentChild(2,"Hobbies",2)="Scrap booking"

^ParentChild(2,"Hobbies",2,"Seasons")="Spring*Summer*Fall*Winter"

^ParentChild(3)="Kaitlin^56009"

^ParentChild(3,"Hobbies",1)="Lighting Design"

^ParentChild(3,"Hobbies",1,"Seasons")="Spring*Summer*Fall*Winter"

^ParentChild(3,"Hobbies",2)="pets"

^ParentChild(3,"Hobbies",2,"Seasons")="Spring*Summer*Fall*Winter"

^ParentChild(4)="Melissa^56894"

^ParentChild(4,"Hobbies",1)="Marching Band"

^ParentChild(4,"Hobbies",1,"Seasons")="Fall"

^ParentChild(4,"Hobbies",2)="Pep Band"

^ParentChild(4,"Hobbies",2,"Seasons")="Winter"

^ParentChild(4,"Hobbies",3)="Concert Band"

^ParentChild(4,"Hobbies",3,"Seasons")="Spring*Summer*Fall*Winter"

^ParentChild(5)="Robin^57079"

^ParentChild(5,"Hobbies",1)="Baking"

^ParentChild(5,"Hobbies",1,"Seasons")="Spring*Summer*Fall*Winter"

^ParentChild(5,"Hobbies",2)="Reading"

^ParentChild(5,"Hobbies",2,"Seasons")="Spring*Summer*Fall*Winter"

^ParentChild(6)="Kieran^58210"

^ParentChild(6,"Hobbies",1)="SUBA"

^ParentChild(6,"Hobbies",1,"Seasons")="Summer"

^ParentChild(6,"Hobbies",2)="Marching Band"

^ParentChild(6,"Hobbies",2,"Seasons")="Fall"

^ParentChild(6,"Hobbies",3)="Rock Climbing"

^ParentChild(6,"Hobbies",3,"Seasons")="Spring*Summer*Fall"

^ParentChild(6,"Hobbies",4)="Ice Climbing"

^ParentChild(6,"Hobbies",4,"Seasons")="Winter"

ステップ 1:

この新しいクラスも繰り返し使用されるデータを見つけるのは簡単ですね、そう Season サブノードです。  “Spring*Summer*Fall” をすべて同じ行に並べる代わりに、“Spring”、“Summer”、“Fall” という個別の行を 3 つ作成する、という場合に少しややこしくなります。 

^ParentChild(1)="Brendan^45956"

^ParentChild(1,"Hobbies",1)="Pit Crew"

^ParentChild(1,"Hobbies",1,"Seasons")="Fall*Winter"

^ParentChild(1,"Hobbies",2)="Kayaking"

^ParentChild(1,"Hobbies",2,"Seasons")="Spring*Summer*Fall"

^ParentChild(1,"Hobbies",3)="Skiing"

^ParentChild(1,"Hobbies",3,"Seasons")="Summer*Winter"

各趣味 (Hobby) には、季節を最大 4 つ割り当てることができます。  Example3Child クラスにプロパティをあと 4 つ作成できないことはないですが、誰かが新しい季節を勝手に作ってしまったらどうなるでしょう。 そこで、もっと柔軟な解決策として、孫テーブルを作り、季節の数を動的に割り当てられるようにします。

ステップ 2:

添え字に注目すれば、IdKey に含まれる部分が分かるので、添え字 1 と 3 は IdKey に含まれるということが分かります。ところが、それぞれの季節を一意に識別するにはもう 1 つ添え字が必要なのですが、使える添え字が残っていません!

マッピングに入力している情報は、クエリを実行するためのコードを生成するために使用されます。  どのような COS コマンドを使えばこの情報を取得できるのかと考えることで、マッピングを定義しやすくなるかもしれません。  ここで、実行する必要のある重要なコマンドが 3 つあります。

                SET sub1=$ORDER(^Parentchild(sub1))

                SET sub2=$ORDER(^Parentchild(sub1,”Hobbies”,sub2))

                SET season=$PIECE(^ParentChild(sub1,”Hobbies”,sub2”,”Seasons”),”*”,PC)

マッピングの Subscripts セクションでも同じことを行えます。  Caché SQL Storage は、4 種類の添え字をサポートしています (Piece、Global、Sub、Other)。  ここまでは、デフォルトの Sub を使っています。必需品的な存在として活躍する $ORDER() ループを使えるのもそのおかげです。 

この例では、Piece オプションをご紹介します。  このレベルで使用するプロパティは、Piece Counter (上の例で PC として表示) として使用されます。  デフォルトの動作として、文字列の最後に達するまでこれを 1 ずつ増加させます。

ステップ 3:

3 つのプロパティ:  Data は単純な Season、そして Relationship プロパティ HobbyRef があり、最後に childsub として PieceCount が必要になります。

ステップ 4:

Property Season As %String;
Property PieceCounter As %Integer;
Relationship HobbyRef As Mapping.Example3Child [ Cardinality = parent, Inverse = Seasons ];

ステップ 5:

Subscripts のマッピングを見ると、変数のレベルが 3 つありますが、IdKey のインデックスでは、2 つのプロパティ HobbyRef と PieceCounter だけを参照しています。

Index Master On (HobbyRef, PieceCounter) [ IdKey ];

ステップ 6:

これまで使用してきたお馴染みの 3 つのセクションです。  Row ID は引き続きそのままにしておきます。  このステップでは、Subscripts についてもう少し詳しく説明して、Access Type を定義する必要があります。  このクラスでは、‘Sub’ と ‘Piece’ を使います。  ‘Global’ と ‘Other’ を使った例をご覧になりたい方は、私が例をまとめた zip ファイルをダウンロードしてください。

ステップ 6a:

Subscripts のメインページはいつもと同じですが、レベルが 2 つ追加されています。  Parent 参照の 2 つの部分については、先ほど参照した 2 つのクラス {Mapping.Example3Parent} と {Mapping.Example3Child} を参照し直す必要があることを覚えておきましょう。  ウィンドウの左側にある Subscripts Levels の 1 つをクリックすると、違いが表示されます。

下の画像では、Subscripts Level で行える様々なアクションをご覧いただけます。  ‘Sub’ の Access Type では、「“”」からはじめて、「“”」 に到達するまで $ORDER() を実行しようとお考えではないでしょうか。  そうでない場合は、Start Value または Stop Value またはその両方を指定できます。 

‘Data Access’ を使うと、確認する内容を前のレベルから変更できます (イテレーションするグローバルを変更するなど)。 

‘Next Code’ と ‘Invalid Conditions’ は一緒に使います。このウィンドウで一番頻繁に使うことになるでしょう。  シンプルな $ORDER() では有効な値を順に取得できないという場合は、代わりに独自のコードを書いてください。 

‘Next Code’ は、$ORDER() と同様、1 つの有効な値からその次の有効な値に移動するために使用されます。 

‘Invalid Condition’ は特定の値を評価するのに使用されます。  ‘subscriptX’ の値を指定した場合は、それを見つけるためにわざわざ ‘Next’ を呼び出す必要がなくなります。  必要なのは、その値が有効なのかどうかを見極めるコードです。 

長い間にわたってお約束してきた zip ファイルには、Next Code’ と ‘Invalid Conditions’ を使うクラスがたくさん入っています。 

ページの最後に登場する ‘Access Variables’ ですが、使うことは滅多にありません。  簡単に言うと、変数を設定し、1 つの Subscript Level で値を割り当て、それを上位の Subscript Level で使用するためのものです。  スコーピングは生成されるテーブルのコードが代わりにやってくれます。

Subscript Level 5 では、‘Access Type’ は ‘Piece’、‘Delimiter’ は “*” となります。  生成されたコードは、Piece 1 からスタートし、$PIECE の値がなくなるまで 1 ずつ増加していきます。  ここでも、Start Value または Stop Value またはその両方を指定すれば、これを制御できます。

ステップ 6b:

Data セクションにはプロパティが 1 つしかないので、‘Piece’ も ‘Delimiter’ も必要ありません。  このテーブルにもっとフィールドがあれば、‘Piece’ と ‘Delimiter’ を指定することになると思いますが、それはそれで問題ありません。

ステップ 6c:

まだ空白のままにしておきます。

ステップ 7:

すべてが正常にコンパイルします。

Compilation started on 11/30/2016 08:17:42 with qualifiers 'uk/importselectivity=1 /checkuptodate=expandedonly'
Compiling 2 classes, using 2 worker jobs
Compiling class Mapping.Example3Child
Compiling class Mapping.Example3GrandChild
Compiling table Mapping.Example3GrandChild
Compiling table Mapping.Example3Child
Compiling routine Mapping.Example3Child.1
Compiling routine Mapping.Example3GrandChild.1
Compilation finished successfully in 1.021s.

3 つのテーブルの結合

SELECT P.ID, P.Name, P.DateOfBirth,

C.ID, C.Hobby, G.ID, G.Season

FROM Mapping.Example3Parent P

JOIN Mapping.Example3Child C ON P.ID = C.ParentRef

JOIN Mapping.Example3Grandchild G ON C.ID = G.HobbyRef

WHERE P.Name = 'Kieran'

この結果、以下が出力されます。

<td><p  style-"margin-left: 9pt; text-align: center;">
  <strong>Name</strong></p>
</td>

<td><p  style-"margin-left: 9pt; text-align: center;">
  <strong>DateOfBirth</strong></p>
</td>

<td style="margin-left: 9pt; text-align: center;">
  <strong>ID</strong></p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  <strong>Hobby</strong></p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  <strong>ID</strong></p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  <strong>Season</strong></p>
</td>
<td><p style="margin-left: 9pt; text-align: center;">
  Kieran</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  05/16/2000</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  6||1</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  SUBA</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  6||1||1</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  Summer</p>
</td>
<td><p style="margin-left: 9pt; text-align: center;">
  Kieran</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  05/16/2000</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  6||2</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  Marching Band</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  6||2||1</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  Fall</p>
</td>
<td><p style="margin-left: 9pt; text-align: center;">
  Kieran</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  05/16/2000</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  6||3</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  Rock Climbing</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  6||3||1</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  Spring</p>
</td>
<td><p style="margin-left: 9pt; text-align: center;">
  Kieran</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  05/16/2000</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  6||3</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  Rock Climbing</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  6||3||2</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  Summer</p>
</td>
<td><p style="margin-left: 9pt; text-align: center;">
  Kieran</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  05/16/2000</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  6||3</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  Rock Climbing</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  6||3||3</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  Fall</p>
</td>
<td><p style="margin-left: 9pt; text-align: center;">
  Kieran</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  05/16/2000</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  6||4</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  Ice Climbing</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  6||4||1</p>
</td>

<td><p style="margin-left: 9pt; text-align: center;">
  Winter</p>
</td>

ID

6

6

6

6

6

6

 

子テーブルの IdKey は、常に Parent 参照と Childsub の 2 つで構成されると説明したことを覚えておいてください。 

最初の行は、Example3Child ID が 6||1、 Parent 参照が 6、Childsub が 1 となっています。 

Example3GrandChild の IdKey は、3 つの部分 (6||1||1) で構成されていますが、それでも Parent 参照と Childsub を表しています。  Parent 参照は少し複雑な感じに 6||1 となって 、Childsub は 1 となっています。 

Example3Child では、Subscripts のプロパティの数が IdKey のプロパティの数に一致しています。 親子構造の入れ子状態が深まると、IdKey は複合化され、添え字の数は増えていきます。

今回の例で使用した 3 つのクラスはこちらにエクスポートしておきました: MappingExample4.zip。

0
0 212
記事 Mihoko Iijima · 2月 26, 2021 2m read

これは InterSystems FAQ サイトの記事です。

%SYS.ProcessQuery クラスの AllFields クエリを使用すると取得できます。

詳細は、ドキュメント「プロセス(ジョブ)について【IRIS】 / プロセス(ジョブ)について」もご参照ください。

ターミナルでの実行例は以下の通りです。

0
0 293
記事 Mihoko Iijima · 2月 25, 2021 2m read

これは InterSystems FAQ サイトの記事です

復旧を優先される場合を除き 【トラブル発生状態のまま】弊社サポートセンターまでご連絡ください。

その際、専用ツールを利用して情報収集いただくことで(所要時間約 5分)、サポートセンターによる状況確認がスムーズに行えます。

ツール使用方法については、PDF または以下ビデオでご紹介しています。

※ InterSystems IRIS / IRIS for Health をご利用の方は、こちらの記事をご参照ください。

ぜひ 1 度、テスト/開発環境で実行をお試しいただき、万が一の場合に備えていただければ思います。

ビデオの目次(YouTubeでもご覧いただけます)

0:00~1:40 情報収集ツールを使用する上での大事なポイント

1:41~2:15 ツールの種類について

2:15~3:45 どのツールを実行したらいいか困った時の考え方

3:45~5:04 管理ポータルの診断レポートの例

5:04~6:00 ^Buttonsの実行例(Cache)

6:00~7:12 ^Buttonsの実行例(Ensemble / Cacheベースの HealthConnect)

7:12~8:27 CacheHungスクリプトの実行例(Windowsの例)

8:27~9:30 CacheHungスクリプトの実行例(Linuxの例)

0
0 206
記事 Toshihiko Minamoto · 2月 25, 2021 9m read

古くなった MUMPS アプリケーションに新たな生命を吹き込みたいとお考えですか? 以下にご紹介するステップを実行すれば、グローバルをクラスにマッピングし、美しいデータを Object や SQL に公開できます。

上の内容に馴染みが無い方は、以下の記事を初めからお読みください。

グローバルをマッピングする技術 1

グローバルをマッピングする技術 2

この記事の例では、典型的な親子構造をマッピングする方法をお見せします。

前回と同じ免責事項:  これらの記事を読んでもグローバルがよく理解できないという方は、WRC (Support@InterSystems.com) までメールでお問い合わせください。喜んでサポートさせていただきます。

グローバルをクラスにマッピングするステップ。

  1. グローバルデータが繰り返し使用されるパターンを特定する。
  2. 固有キーの構成を特定する。
  3. プロパティとそれぞれの型を特定する。
  4. クラス内のプロパティを定義する (変数の添え字をお忘れなく)。
  5. IdKey のインデックスを定義する。
  6. Storage Definition を以下の手順で定義する。
    1. 添え字を IdKey まで (IdKey を含む) 定義する。
    2. Data セクションを定義する。
    3. Row ID セクションには触れない。  デフォルトが 99% の割合で適切なので、これはシステムに任せます。
</ol>  7. クラス / テーブルをコンパイルし、テストします。

 

前回の例では、家族のメンバーは Activity をそれぞれ 1 つずつしか持てませんでしたが、それでは面白くないので、今回は複数の Activity を持てるようにしたいと思います。  以下の例のステップ 1 は、繰り返し使用されるデータが 2 種類あるので、少しだけ複雑になります。

^ParentChild(1)="Brendan^45956"

^ParentChild(1,"Hobbies",1)="Pit Crew"

^ParentChild(1,"Hobbies",2)="Kayaking"

^ParentChild(1,"Hobbies",3)="Skiing"

^ParentChild(2)="Sharon^46647"

^ParentChild(2,"Hobbies",1)="Yoga"

^ParentChild(2,"Hobbies",2)="Scrap booking"

^ParentChild(3)="Kaitlin^56009"

^ParentChild(3,"Hobbies",1)="Lighting Design"

^ParentChild(3,"Hobbies",2)="pets"

^ParentChild(4)="Melissa^56894"

^ParentChild(4,"Hobbies",1)="Marching Band"

^ParentChild(4,"Hobbies",2)="Pep Band"

^ParentChild(4,"Hobbies",3)="Concert Band"

^ParentChild(5)="Robin^57079"

^ParentChild(5,"Hobbies",1)="Baking"

^ParentChild(5,"Hobbies",2)="Reading"

^ParentChild(6)="Kieran^58210"

^ParentChild(6,"Hobbies",1)="SUBA"

^ParentChild(6,"Hobbies",2)="Marching Band"

^ParentChild(6,"Hobbies",3)="Rock Climbing"

^ParentChild(6,"Hobbies",4)="Ice Climbing"

ステップ 1:

                このグローバルには、繰り返し使用されるデータが 2 種類あります。1 つ目のデータは、添え字の最初のレベルにあり、個人情報が格納されています。

               ^ParentChild(1)="Brendan^45956"

^ParentChild(2)="Sharon^46647"

^ParentChild(3)="Kaitlin^56009"

^ParentChild(4)="Melissa^56894"

^ParentChild(5)="Robin^57079"

^ParentChild(6)="Kieran^58210"

2 つ目のデータは、添え字の 3 つ目のレベルにあり、趣味が格納されています。

^ParentChild(1,"Hobbies",1)="Pit Crew"

^ParentChild(1,"Hobbies",2)="Kayaking"

^ParentChild(1,"Hobbies",3)="Skiing"

^ParentChild(2,"Hobbies",1)="Yoga"

^ParentChild(2,"Hobbies",2)="Scrap booking"

                …

               クラスは合計 2 つ、繰り返し使用されるデータのブロックそれぞれに 1 つずつ定義します。

ステップ 2:

クラスが 2 つあるということは、固有の識別子が各テーブル (Example3Parent と Example3Child) に 1 つずつ、合計で 2 つ必要になるということです。  Parent テーブルは添え字が 1 つしかないので簡単です。  一方の Child テーブルには、最初の添え字 (Parent への参照) と 3 つ目の添え字 (childsub) を組み合わせた複合キーが使用されます。

ステップ 3:

データを見ていると、簡単に 5 種類のプロパティを特定できます。  Example3Parent には、ParentId、Name、DateOfBirth と、プロパティが 3 つあり、 Example3Child には 2 つ、ChildId と Hobby があります。  この例では、プロパティをあと 2 つ定義する必要があります。  これらは、Relationship Properties (関係プロパティ) と呼ばれています。  別のクラスとの関係を定義するもので、各クラスに 1 つずつ記述します。  親クラスは子クラスを多く持てる一方で、子クラスは親クラスを 1 つしか持てません。

親クラスには、以下が定義されています。

Relationship HobbyRef As Mapping.Example3Child [ Cardinality = children, Inverse = ParentRef ];

子クラスでは、以下が定義されています。

Relationship ParentRef As Mapping.Example3Parent [ Cardinality = parent, Inverse = HobbyRef ];

これらのプロパティは、Property ウィザードを使えば定義できます。  Relationship オプションボタンをクリックすると、以下のページが表示されます。

ステップ 4:

ステップ 3 で少し先走って Relationship プロパティを紹介しましたが、 それ以外のプロパティは以下のとおりです。

                Example3Parent:

Property Name As %String;
Property DateOfBirth As %Date;
Property ParentId As %Integer;

                Example3Child:

Property Hobby As %String;
Property ChildId As %Integer;

ステップ 5:

                Example3Parent の IdKey はいたって単純。添え字のレベルは 1 つだけです。

      Index Master On ParentId [ IdKey ];

一方の Example3Child の IdKey は、添え字 1 と 3 に基づいています。しかし、親子関係は少しややこしくなります (十代の女の子をお持ちの親御さんなら共感していただけるのではないかと思います)。  IdKey インデックスには、ParentRef と ChildId の両方、または ChildId だけを指定することができますが、どちらの場合でもきちんとコンパイルします。

Index Master On (ParentRef, ChildId) [ IdKey ];

もしくは

Index Master On ChildId [ IdKey ];

個人的には最初の方が適切だと思いますが、私は面倒くさがり屋なので、自分がお見せする例には 2 つ目の例が登場する可能性が高いです。

ステップ 6:

Example3Parent については何の特記事項もありませんので、細い説明は割愛します。 

一方の Example3Child では、添え字の扱いが少し違うので、その手順を説明します。

ステップ 6a:

下にあるのは、子クラスのグローバルです。

                ^ParentChild({ParentRef},"Hobbies",{ChildId})=””

添え字は 3 つ必要なことが分かります。  1 つ目の添え字に手こずる人が多いです。  先ほどお見せした Relationship プロパティは使わず、代わりに親クラスのプロパティを使います。  それを行うには、完全なフィールド構文 {SchemaName.TableName.FieldName} を使います。  今回の例だと、1 つ目の添え字は {Mapping.Example3Parent.ParentId} となり、

2 つ目の添え字は定数の “Hobbies”、

そして 3 つ目にはフィールド {ChildId} を使います。

ステップ 6b:

Data セクションは極めてシンプル。フィールド {Hobby} だけです。Piece も Delimiter もありません。

ステップ 6c:

では皆さんご一緒に「お見せするものがないので、空白にしておきます!」(笑)

ステップ 7:

後はコンパイルするだけです。

Compilation started on 11/28/2016 08:26:31 with qualifiers 'fuk/importselectivity=1 /checkuptodate=expandedonly'
Compiling 2 classes, using 2 worker jobs
Compiling class Mapping.Example3Parent
Compiling class Mapping.Example3Child
Compiling table Mapping.Example3Parent
Compiling table Mapping.Example3Child
Compiling routine Mapping.Example3Parent.1
Compiling routine Mapping.Example3Child.1
Compilation finished successfully in 0.900s.

そして、単純な JOIN を実行し、データが正しく表示されるのを確認します。

SELECT P.ParentId, P.Name, P.DateOfBirth, C.ID, C.Hobby

FROM Mapping.Example3Parent P

JOIN Mapping.Example3Child C ON P.ParentId = C.ParentRef

<td>
  Name
</td>

<td>
  DateOfBirth
</td>

<td>
  ID
</td>

<td>
  Hobby
</td>
<td>
  Brendan
</td>

<td>
  10/28/1966
</td>

<td>
  1||1
</td>

<td>
  Pit Crew
</td>
<td>
  Brendan
</td>

<td>
  10/28/1966
</td>

<td>
  1||2
</td>

<td>
  Kayaking
</td>
<td>
  Brendan
</td>

<td>
  10/28/1966
</td>

<td>
  1||3
</td>

<td>
  Skiing
</td>
<td>
  Sharon
</td>

<td>
  09/18/1968
</td>

<td>
  2||1
</td>

<td>
  Yoga
</td>
<td>
  Sharon
</td>

<td>
  09/18/1968
</td>

<td>
  2||2
</td>

<td>
  Scrap booking
</td>
<td>
  Kaitlin
</td>

<td>
  05/07/1994
</td>

<td>
  3||1
</td>

<td>
  Lighting Design
</td>
<td>
  Kaitlin
</td>

<td>
  05/07/1994
</td>

<td>
  3||2
</td>

<td>
  pets
</td>
<td>
  Melissa
</td>

<td>
  10/08/1996
</td>

<td>
  4||1
</td>

<td>
  Marching Band
</td>
<td>
  Melissa
</td>

<td>
  10/08/1996
</td>

<td>
  4||2
</td>

<td>
  Pep Band
</td>
<td>
  Melissa
</td>

<td>
  10/08/1996
</td>

<td>
  4||3
</td>

<td>
  Concert Band
</td>
<td>
  Robin
</td>

<td>
  04/11/1997
</td>

<td>
  5||1
</td>

<td>
  Baking
</td>
<td>
  Robin
</td>

<td>
  04/11/1997
</td>

<td>
  5||2
</td>

<td>
  Reading
</td>
<td>
  Kieran
</td>

<td>
  05/16/2000
</td>

<td>
  6||1
</td>

<td>
  SUBA
</td>
<td>
  Kieran
</td>

<td>
  05/16/2000
</td>

<td>
  6||2
</td>

<td>
  Marching Band
</td>
<td>
  Kieran
</td>

<td>
  05/16/2000
</td>

<td>
  6||3
</td>

<td>
  Rock Climbing
</td>
<td>
  Kieran
</td>

<td>
  05/16/2000
</td>

<td>
  6||4
</td>

<td>
  Ice Climbing
</td>
         ParentId
1
1
1
2
2
3
3
4
4
4
5
5
6
6
6
6

 

子クラスに ParentId_”||”_ChildId で構成される ID というフィールドがあることに注目してください (どのクラスにも ID というフィールド / プロパティがあります)。

自分で入力したくないという方は、グローバルとクラスが記述されたこちらのファイルをお使いください:  MappingExample3.zip

0
0 227
記事 Hiroshi Sato · 2月 24, 2021 3m read

以下のドキュメントでご紹介しています通り、ECPは分散キャッシュ用のアプリケーションサーバーを並列に増設することで、処理量に応じてスケールアウトすることを主目的として設計されています。

ECP機能紹介

従ってインターシステムズデータプラットフォーム上で稼働する複数のアプリケーション間の簡便なデータ交換手段としてECPをご使用いただくことは推奨していません。

以下その理由についてご説明します。

データ量の増加、処理量の増加に伴いスケーラビリティに関する要件も今後益々厳しくなる中、将来にわたってより良いスケーラビリティ性能を得られるようインターシステムズの開発部門は、より良いアルゴリズムの開発や処理のチューニングをECPに対して継続的に行っております。


その成果は順次最新バージョンに反映していきます。


従ってある時点での最高のECP性能を得る方法は、アプリケーションサーバーとデータベースサーバーのバージョンをその時点の最新版に揃えることになります。

一方でインターシステムズは、ECPを上記の用途以外で利用することを明確に非推奨とするなどの勧告を過去に行っていなかったため、複数のアプリケーション間でデータをピアツーピアで参照、更新する手段として使われているケースも多く見受けられます。

0
0 575
記事 Mihoko Iijima · 2月 19, 2021 4m read

開発者の皆さん、こんにちは!

VSCode の ObjectScript エクステンションで、プロセスにアタッチしてデバッグする方法についてご紹介します。

ObjectScript エクステンションの基本的な操作方法については、こちらの記事をぜひご参照ください。

解説ビデオ(4分ちょっと)もあります。ぜひご参照ください。

 

手順1:launch.json の用意

VSCode のデバッグ実行に関連する各種の構成情報を記述するための launch.json に ObjectScript エクステンション用の設定を記述します。

(ビデオだと、最初~1:31 で解説しています)

プロセスにアタッチするデバッグ設定例は以下の通りです。

2
0 14362
記事 Mihoko Iijima · 2月 19, 2021 2m read

これは InterSystems FAQ サイトの記事です。

Java のガベージコレクションは、使用しなくなったメモリ上のオブジェクトを自動開放するしくみですが、InterSystems data platform(以下IRISと表記)のガベージコレクションは、意味が異なります。

IRIS では、ディスク上の大量のデータノードを 1 コマンド(killコマンド)で削除する機能があります。

通常、データ容量が増えれば、それに伴い、削除に必要な時間が増加しますが、そうすると、そのkillコマンドを発行したプロセスが、削除が終わるのを待つ時間が増加することになってしまいます。

その様な事象の発生を避けるため、IRIS では、ユーザプロセスが大量のデータを削除する kill コマンドを発行した際に、その場で削除に必要な全ての処理を行うのではなく、削除が必要な開始の場所だけを示して、次の処理に進むようになっています。

その後、ガベージコレクタというシステムプロセスに起動がかかり、その指示されたポイントから大量データの削除に伴う領域の開放処理をバックグラウンドで処理していきます。

つまり、論理的な削除は、一瞬で終わるが、実際の物理的な削除は、遅延して行われる仕組みとなります。

このような仕組みをガベージコレクションと呼んでいます。

0
0 129
記事 Mihoko Iijima · 2月 19, 2021 1m read

これは InterSystems FAQ サイトの記事です。

インターシステムズは、個々の仮想化ソフトウェアに対して、弊社製品の動作検証は行なっておりません。

インターシステムズでは、弊社製品がサポートするプラットフォームをサーバプラットフォームという単位で定義しています。

サーバプラットフォームは、オペレーティングシステムとそのバージョン、およびそのオペレーティングシステムが動作するプロセッサタイプの 3 つの要素で定義されます。

従いまして、ある仮想ソフト上で InterSystems 製品がサポートしているサーバプラットフォームが稼動し、その上で InterSystems 製品が動作している限りにおいて、製品のサポートを提供します。

0
0 106
記事 Toshihiko Minamoto · 2月 18, 2021 9m read

 古くなった MUMPS アプリケーションの新たな生命を吹き込みたいとお考えでしたら、以下にご紹介するステップを実行すれば、グローバルをクラスにマッピングし、美しいデータを Object や SQL に公開できます。

今回ご紹介する例には、パート 1 ではカバーしなかった内容を 4 つないし 5 つ程度盛り込んでいます。 

その後は親子マッピングの例を紹介して完結となります。それを修得したらマッピングはもう完璧でしょう。

前回と同じ免責事項:  これらの記事を読んでもグローバルがよく理解できないという方は、WRC (Support@InterSystems.com) までメールでお問い合わせください。喜んでサポートさせていただきます。 

グローバルをクラスにマッピングするステップ。

  1. グローバルデータが繰り返し使用されるパターンを特定する。
  2. 固有キーの構成を特定する。
  3. プロパティとそれぞれの型を特定する。
  4. クラス内のプロパティを定義する (変数の添え字をお忘れなく)。
  5. IdKey のインデックスを定義する。
  6. Storage Definition を以下の手順で定義する。
    1. 添え字を IdKey まで (IdKey を含む) 定義する。
    2. Data セクションを定義する。
    3. Row ID セクションには触れない。  デフォルトが 99% の割合で適切なので、これはシステムに任せます。
</ol>  
  1. クラス / テーブルをコンパイルし、テストします。

 

以下のようなグローバルが 2 種類 (mapping と index) あるとします。

^mapping("Less Simple",1,1)="Bannon,Brendan^Father"

^mapping("Less Simple",1,1,"Activity")="Rock Climbing"

^mapping("Less Simple",1,2)="Bannon,Sharon^Mother"

^mapping("Less Simple",1,2,"Activity")="Yoga"

^mapping("Less Simple",1,3)="Bannon,Kaitlin^Daughter"

^mapping("Less Simple",1,3,"Activity")="Lighting Design"

^mapping("Less Simple",1,4)="Bannon,Melissa^Daughter"

^mapping("Less Simple",1,4,"Activity")="Marching Band"

^mapping("Less Simple",1,5)="Bannon,Robin^Daughter"

^mapping("Less Simple",1,5,"Activity")="reading"

^mapping("Less Simple",1,6)="Bannon,Kieran^Son"

^mapping("Less Simple",1,6,"Activity")="Marching Band"

^index("Less Simple","FName","BRENDAN",1,1)=""

^index("Less Simple","FName","KAITLIN",1,3)=""

^index("Less Simple","FName","KIERAN",1,6)=""

^index("Less Simple","FName","MELISSA",1,4)=""

^index("Less Simple","FName","ROBIN",1,5)=""

^index("Less Simple","FName","SHARON",1,2)=""

そうです、グローバルマッピングについて学ぶと同時に、私の家族についても知っていただく必要があります。  創造力が足らなくて、これ以外のデータは思いつきませんでした。

ステップ 1:

今回は、繰り返しデータが、1 つではなく、2 つのグローバルノードに広がっています。

ステップ 2:

データがこれしかないので、確信は持てませんが、 1 つ目の添え字、もしくは 1 つ目と 2 つ目の添え字の両方が定数だと思います。  今回の例では、2 つ目の添え字が変数 FamilyId、3 つ目が PersonId であると想定します。

ステップ 3:

変数の添え字は FamilyId と PersonId の 2 つ、さらに Name、Relation、Activity があります。  ヒントを求めてインデックスのグローバルをもう一度確認した結果、Name を 2 つのプロパティ FirstName と LastName にマッピングすることにしました。  これで、定義する必要のあるプロパティが全部で 6 個になりました。

ステップ 4:

ここで新しいものが 2 つ登場します。  すべてのプロパティに SQL Field Name があり、FirstName の照合が UPPER になっています。  SQL Field Names を定義しておけば、プロパティの話なのか、SQLフィールドの話なのかがはっきりします。  照合については、後ほどインデックスのマッピングと一緒に説明します。

Property FamilyId As %Integer [ SqlFieldName = Family_Id ];
Property PersonId As %Integer[ SqlFieldName = Person_Id ];
Property FirstName As %String(COLLATION = "UPPER")[ SqlFieldName = First_Name ];
Property LastName As %String[ SqlFieldName = Last_Name ];
Property Relation As %String[ SqlFieldName = Relation ];
Property Activity As %String[ SqlFieldName = Activity ];

ステップ 5:

変数の添え字が 2 つあるため、IdKey は 2 つのプロパティに基づくことを意味します。


Index Master On (FamilyId, PersonId)[IdKey];

ここでも、1 つのインデックスが 1 つのプロパティ FirstName に対して定義されています。

Index FNameIndex On FirstName;

ステップ 6:

まず最初に Storage Definition を作成します。  Storage アイコンをクリックするか、Inspector を使って、Storage を選択し、New Storage を右クリックします。  今回は、Map Name をデフォルト値のままにして、Global Name と ^mapping だけを入力しました。

最初の例では、Storage Definition を作成してからウィザードに戻る方法を説明していなかったので、ここでご紹介いたします。  ウィザードに戻るには、Inspector から Storage を選択した後に、Storage Name (今回は NewStorage1) を選択し、SQL Storage Map と右側の端の方にあるボックスを順にクリックします。

ステップ 6a:

もう一度 Subscripts セクションから始めます。  まず最初に、IdKey 以下のすべてを定義するために添え字のレベルが 3 つ (定数が 1 つ、フィールドが 2 つ) 必要になります。  このウィンドウに表示されるフィールドは、IdKey のインデックスに定義されているプロパティと一致している必要があります。

注意:  Storage Definition では、常に、プロパティ名 FamilyId ではなく、SQL のフィールド名 Family_Id を参照しています。

ステップ 6b:

Data セクションでは、1 つのグローバルノードにフィールドが 3 つ、そして添え字の下位レベル ( “Activity”) にはフィールドが 1 つ格納されています。  ファーストネームとラストネームを 2 つの個別のフィールドとして取得するには、入れ子になった $PIECE() に対応する Piece と Delimiter を複数個使う必要があります。  これは、ObjectScript では「set FirstName=$PIECE($PIECE(^mapping(“Less Simple”,1,1),”^”,1),”,”,2)」のように記述されます。 ウィザードでは、最も内側にある $PIECE() からすべての Delimiter と Piece を順にリストアップする必要があります。  Activity は別のグローバルノードにあるため、Node には定数を追加します。  Piece と Delimiter の既定値は、それぞれ 1 と “^” なので、そのままにしています。

ステップ 6c:

ここはまだお見せできるものがありません。 

Row ID セクションを定義する必要があるケースの例を見たくてしょうがない、という皆さまのお気持はよく分かります。  後ほどすべての例を取り込んだ zip ファイルをお見せするときに、Mapping.RowIdSpec という名前の例を入れておきますので、それを見てご確認ください。

それでは、インデックスマップに対してステップ 6 を実行します。

ステップ 6

Map2 を作成し、Global Name を「^index」に設定します。

ステップ 6a

インデックスグローバルには、定数が 2 個、IdKey フィールドが 2 個、インデックス付きフィールド First_Name が 1 つ、と添え字は全部で 5 つあります。  ^mapping グローバルと ^index グローバルを見ると、First_Name の値が違うことが分かります。  ^mapping グローバルでは、名前に大文字と小文字が混ざっている一方で、^index グローバルの名前は大文字だけが使われています。  Caché には、文字列データの変換に使えるコレーション機能がいくつか備え付けられています。  使用できる各機能については、こちらのリンクをご覧ください。

http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GSQL_basics#GSQL_basics_collation

Caché SQL Storage では、コレーションのレガシータイプが必要になる可能性が強いです。

http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GSQL_basics#GSQL_basics_collationlegacy

ここで使用する照合機能がプロパティ定義の照合と一致するものであることは極めて重要です。  インデックスマップがクエリオプティマイザーに使用されない最も一般的な理由は、これらが一致しないということがあるためです。  %String のデフォルトの照合は SQLUPPER となっていますが、マッピングで照合機能を提供しないことは、EXACT 照合を使うのと同じであることを覚えておきましょう。

ステップ 6b

^index グローバルにはデータがないため、ここで定義するものはありません。

ステップ 6c

もうお分かりでしょう、ここもなしです。

ステップ 7:

Compilation started on 08/22/2016 15:42:16 with qualifiers 'fck /checkuptodate=expandedonly'
Compiling class Mapping.Example2
Compiling table Mapping.Example2
Compiling routine Mapping.Example2.1
Compilation finished successfully in 0.144s.

SELECT Family_ID, Person_ID, First_Name, Last_Name, Relation, Activity

FROM Mapping.Example2

Family_Id            Person_Id           First_Name        Last_Name         Relation               Activity

1                              1                              Brendan               Bannon                 Father                   Rock Climbing

1                              2                              Sharon                  Bannon                 Mother                 Yoga

1                              3                              Kaitlin                    Bannon                 Daughter             Lighting Design

1                              4                              Melissa                 Bannon                 Daughter             Marching Band

1                              5                              Robin                    Bannon                 Daughter             Reading

1                              6                              Kieran                   Bannon                 Son                        Marching Band

今回、Storage Definition はスキップしておきます。  確認したい方は、mapping.example2.zip をダウンロードして、XML ファイルを読み込んでください。

0
0 284
記事 Megumi Kakechi · 2月 15, 2021 7m read

これは InterSystems FAQ サイトの記事です。
 

データベースキャッシュおよびルーチンキャッシュをモニターし、最適値を調べる方法をご紹介します。
 

(1) データベースキャッシュ

現状の設定値で問題ないかは、^GLOSTAT ユーティリティ のCache Efficency値(キャッシュ効率)でモニターします。

Cache Efficiency 値は大きければ大きいほど良いですが、目安として100 以上であれば、設定サイズは問題ありません。

実行例)

0
0 545
記事 Megumi Kakechi · 2月 15, 2021 4m read

これは InterSystems FAQ サイトの記事です。


「特権ルーチンアプリケーション」を使用し、コード中に $system.Security.AddRoles()メソッドを使用してロールを付与する仕組みを利用します。

※ロールベースで必要な特権を付与するため、予め特定の特権を持ったロールを作成する必要があります。

詳細は、以下ドキュメントをご参照ください。
特権ルーチン・アプリケーション【IRIS
特権ルーチン・アプリケーションについて


例えば、特定ルーチン(またはクラスメソッド)実行時のみデータベースの更新を許可するための設定は、以下のとおりです。

(接続するデータベースに対してはREAD許可だけを持ち、あるルーチン実行時のみデータベースに対するREAD/WRITE許可を持つように設定します。)
 

1)  データベース:Aのリソース定義を確認する

 データベース:Aのリソースに %DB_%DEFAULT が設定されている場合は、独自リソースを作成します。

 (データベースに割り当てられたリソースの確認は、管理ポータル > [システム管理] > [構成] > [システム構成] > [ローカルデータベース] > リソース で確認できます)

0
0 180
記事 Mihoko Iijima · 2月 12, 2021 3m read

これは InterSystems FAQ サイトの記事です。

以下例のクラスメソッド getLatestID() のように ObjectScript のクラスメソッドを用意します。
返したい値を戻り値に指定し、SQLストアドプロシージャ(SqlProc)キーワードを指定するだけで値が返せます。

ClassMethod getLatestID() As %Integer [ SqlName = getLatestID, SqlProc ]
{
    set latestID=$Order(^ISJ.TestClass1D(""),-1)
    quit latestID
}

 

操作を試す場合は、以下のクラス定義をご準備ください。

Class ISJ.TestClass1 Extends (%Persistent, %Populate)
{
Property name As %String;

ClassMethod getLatestID() As %Integer [ SqlName = getLatestID, SqlProc ]
{
    set latestID=$Order(^ISJ.TestClass1D(""),-1)
    quit latestID
}
}
0
0 424
記事 Mihoko Iijima · 2月 12, 2021 5m read

これは InterSystems FAQ サイトの記事です。

XMLファイルの内容を格納する永続クラス定義を作成し、%XML.Adaptor を追加で継承します。

例は以下の通りです(右端の %XML.Adaptorクラスを追加で継承します)。

Class ISJ.Class1 Extends (%Persistent, %Populate, %XML.Adaptor)

 

次に、%XML.Reader クラスを使用して格納先のインスタンスへ、タグとクラスの関連付け(Correlate())を行い、reader.Next() でXMLを取り込みます。

set sc=reader.OpenFile(filename)
do reader.Correlate(tag,class)
while reader.Next(.x,.sc) { do x.%Save() } 

 

サンプルコードは以下の通りです。

0
0 2179
記事 Toshihiko Minamoto · 2月 8, 2021 10m read

これで 3 記事目になります (パート 1パート 2 をご覧ください) が、引き続き Caché データベースの内部構造をご紹介いたします。 今回は、興味深い内容をいくつかご紹介し、私の Caché Blocks Explorer プロジェクトを使って作業の生産性をアップさせる方法について説明します。

この画像に表示されているものに見覚えがあるという方はたくさんおられると思います (クリック可能)。 グローバルの断片化した状態を視覚化する必要があったとき、様々なディスクデフラグツールが私の頭をよぎりました。 なんとか、そういったツールと同等の効果を発揮する製品を開発できたと願っています。

このユーティリティには複数のブロックで構成されたマップが表示されています。 四角はそれぞれブロックを表し、その色はレジェンドセクションに表示されている特定のグローバルに対応しています。 また、ブロックそのものは格納されているデータの量を示しているため、マップにさっと目を通すだけで、データベース全体の使用量を素早く推定できます。 グローバルレベルのブロックとマップレベルのブロックは未だ実装されていませんので、空のブロック同様に白のブロックとして表示されます。

データベースを選択すれば、すぐにブロックのマップが読み込みを開始します。 情報はそれぞれ順番にではなく、ブロックツリーに並ぶブロックの順番に従って読み込まれるため、そのプロセスは以下の画像のようになる可能性があります。

データベースは、前回の記事で使用したものを引き続き使用しましょう。 グローバルは必要ありませんので、すべて削除しています。 また、SAMPLES データベースの Sample クラスパッケージを基に新しいデータを生成しました。 そのために、HABR と名付けた私のネームスペースへのパッケージマッピングを設定しています。

以下のようにデータ生成コマンドを実行しました。

do ##class(Sample.Utils).Generate(20000)

マップには以下の結果が表示されました。

ブロックが埋まり始める場所はファイルの先頭でないことにお気づきでしょうか。 ブロック 16 からトップレベルのポインタブロックが、そしてブロック 50 からデータブロックが始まります。 デフォルト値は 16 と 50 ですが、必要に応じて変更できます。 ポインタブロックの先頭は SYS.Database クラスの_NewGlobalPointerBlock_ プロパティで定義されます。これにより、新しいグローバルのデフォルト値が設定されます。 既存のグローバルの場合は、PointerBlock プロパティを使って%Library.GlobalEdit クラスの中で変更できます。 一連のデータブロックを開始するブロックは、SYS.Database クラスの_NewGlobalGrowthBlock_ プロパティに指定されます。 個別のグローバルの場合は、%Library.GlobalEdit クラスの GrowthBlock プロパティを使っても同じことができます。 こういったプロパティの変更は、トップポインタブロックやデータブロックの現在の位置には一切影響しないため、まだデータを格納していないグローバルに対してだけ行うことが理に適っていると言えます。 

ここで、989 個のブロックを持つ ^Sample.PersonD グローバルは 83% 埋まっており、それに次いで 573 個のブロックを持つ ^Sample.PersonI グローバルは 70% 埋まっていることが分かります。 どのグローバルを選択しても、それに割り当てられたブロックを確認できます。 ^Sample.PersonI グローバルを選択すると、ほぼ空になっているブロックがいくつかあるのが分かります。 また、これら 2 つのグローバルに属するブロックが混合していることも分かります。 実はこれには理由があります。 新しいオブジェクトが作成されると、これら 2 つのグローバルは、片方はデータで、もう片方は Sample.Person テーブルのインデックスで埋まってしまうのです。

テストデータがいくつか手に入ったところで、Caché が提供するデータベース管理機能を活用して結果を確認することができます。 まずは、データを少し取り除いて、いかにもデータを追加したり、削除したりするアクティビティが実行されているかのように見せます。 ランダムなデータをいくつか削除するコードを実行します。

    set id=""
    set first=$order(^Sample.PersonD(""),1)
    set last=$order(^Sample.PersonD(""),-1)
    for id=first:$random(5)+1:last {
        do ##class(Sample.Person).%DeleteId(id)
    }

このコードを実行すると、以下の結果が表示されます。 空のブロックがいくつかある一方で、64~67% 埋まっているブロックもあります。  

このデータベースは、%SYS ネームスペースから ^DATABASE ツールを使って操作することができます。 では、その機能をいくつか使ってみましょう。

まずは、ブロックがほとんど埋まっていないので、データベース内のすべてのグローバルを圧縮したらどうなるか試してみましょう。

ご覧のとおり、圧縮したことで、占有率を必要な 90% の値に限りなく近づけることができました。 この結果、空であったブロックは他のブロックから移動されてきたデータでいっぱいになりました。 データベースのグローバルは、^DATABASE ツール (アイテム 7) を使うか、以下のコマンドにデータベースへのパスを 1 つ目のパラメーターとして渡して実行すれば圧縮できます。

do ##class(SYS.Database).CompactDatabase("c:\intersystems\ensemble\mgr\habr\")

また、すべての空のブロックをデータベースの最後に移動することもできます。 これは、例えば、膨大なデータを削除したからデータベースを圧縮したいというときに必要となるかもしれません。 このデモとして、私たちのテスト用データベースからデータを削除する作業をもう一度実行してみましょう。

    set gn=$name(^Sample.PersonD)
    set first=$order(@gn@(""),1)
    set last=$order(@gn@(""),-1)
    for i=1:1:10 {
        set id=$random(last)+first
        write !,id
        set count=0
        for {
            set id=$order(@gn@(id))
            quit:id=""
            do ##class(Sample.Person).%DeleteId(id)
            quit:$increment(count)>1000
        }
    }

以下はデータを削除した結果です。

空のブロックがいくつかあるのが分かります。 Caché では、こういった空のブロックをデータベースファイルの末尾に移動して圧縮することができます。 空のブロックを移動するには、システムネームスペースにある SYS.Database クラスの FileCompact メソッドを使うか、^DATABASE ツール (アイテム 13) を利用しましょう。 このメソッドには、データベースへのパス、ファイルの末尾の理想的な空きスケース (デフォルトは 0)、戻り値パラメーター (最終的な空きスペース) の 3 つのパラメーターを渡すことができます。

do ##class(SYS.Database).FileCompact("c:\intersystems\ensemble\mgr\habr\",999)

結果、空のブロックがなくなりました。 先頭のブロックは、設定通りに (上位のトップレベルポインタとデータブロックを開始する位置として) 置かれているだけなので、考慮しません。

デフラグ (最適化)

これでグローバルを最適化する作業に取りかかれます。 このプロセスを実行すると、各グローバルのブロックの順番が並び替えられます。 デフラグを実行するには、データベースファイルの最後に、ある程度の空きスペースが必要になる場合があり、それが必要な状況ではスペースが追加される可能性があります。 このプロセスは、^DATABASE ツールのアイテム 14 から始めるか、以下のコマンドを実行して始めることができます。

d ##class(SYS.Database).Defragment("c:\intersystems\ensemble\mgr\habr\")

空きスペースを増やす

グローバルがきちんと並べられているのはいいのですが、 どうやら、デフラグによってデータベースファイルのスペースが余分に使われてしまったようです。 このスペースを開放するには、^DATABASE ツールのアイテム 12 を使用するか、以下のコマンドを実行します。

d ##class(SYS.Database).ReturnUnusedSpace("c:\intersystems\ensemble\mgr\habr\")

データベースの占有スペースは大幅に減りましたが、データベースファイルには空きスケースが 1 MB しか残っていません。 ブロックを移動し、空きスペースを作ることによってデータベース内のグローバルをデフラグし、空きスペースを管理するという可能性が発表されたのはつい最近の話です。 それまでは、データベースをデフラグしてデータベースファイルのサイズを縮小する必要があったときは、^GBLOCKCOPY ツールを使う必要がありました。 このツールは、ソースデータベースの各ブロックを新しく作成されたデータベースに 1 つずつコピーするもので、ユーザーはコピーするグローバルを選択できました。 このツールは現在も使用可能です。

0
1 178
記事 Megumi Kakechi · 2月 8, 2021 1m read

これは InterSystems FAQ サイトの記事です。
 

システムのパフォーマンスが低下した場合、OSやインターシステムズ製品の様々なツールを使用して情報収集を行い
  ”通常時と比較して、どこがどの程度変わっているか”
を確認することで、問題のある箇所を特定できます。
(逆に、通常時の状況が不明な場合、パフォーマンス問題点の切り分けが非常に困難となる場合もあります。)

いざ という時に備え、通常時のパフォーマンスを確認することは、大変重要な情報となります。


収集情報詳細は、以下のドキュメントをご参照ください。

パフォーマンス調査ガイド

ガイド内でご紹介しているサンプルは、https://github.com/Intersystems-jp/performance-sample からダウンロードいただけます。


パフォーマンス低下時の情報収集ツールについては、以下の関連FAQトピックをご参照ください。

【FAQ】パフォーマンス低下時の情報収集ツールについて教えてください。

0
0 168
記事 Megumi Kakechi · 2月 2, 2021 2m read

これは InterSystems FAQ サイトの記事です。


※こちらの方法は、ミラーリング、シャドウイング、またはその他のメカニズムを使用して複製したデーターベースを比較したい場合に利用します。

グローバル変数の比較には、DATACHECKユーティリティを利用できます。以下ドキュメントをご参照ください。
DataCheckの概要【IRIS】

DATACHECK ユーティリティの実行サンプルは、添付のPDFをご覧ください。
 

***

ルーチンの比較は、システムルーチン %RCMP か、管理ポータルを使用します。

以下は、管理ポータルでの使用方法になります。

  例えば、以下ルーチンがUSERネームスペースにあるとします。

test() public{
 quit "hello"
}

以下ルーチンがUSER2ネームスペースにあるとします。

test() public{
 quit "こんにちは"
}

以下は、USERネームスペースに接続したターミナルで %RCOM を実行した結果になります。

※ Compare: にルーチン名を記述し、 with: に比較したいルーチン名を記載します。
  別ネームスペースにあるルーチンを指定する場合は |"ネームスペース名"|ルーチン名.MAC で指定します。

0
0 410
記事 Toshihiko Minamoto · 2月 2, 2021 9m read

この記事は Caché データベースの内部構造を説明したこちらの記事の続編です。 そちらの記事では、様々なブロックタイプ、それぞれのつながりやグローバルとの関係について説明しました。 純粋に理論を述べた記事でした。 ブロックツリーを視覚化するのに役立つプロジェクトを作成しましたので、この記事ではその仕組みを詳しく説明します。

デモを行うために、新しいデータベースを作成しましたが、Caché のデフォルト機能としてすべての新しいデータベースで初期化されるグローバルは消去しています。 それでは、シンプルなグローバルを作成しましょう。
set ^colors(1)="red"
 set ^colors(2)="blue"
 set ^colors(3)="green"
​ set ^colors(4)="yellow"

作成されたグローバルのブロックを表す画像をご覧ください。 これはシンプルなものなので、その説明は Type 9 のブロック (グローバルカタログのブロック) に記載されています。 次にくるのが、「上位ポインタと下位ポインタ」のブロック (Type 70) です (グローバルツリーはまだ浅いため)。ここでは、まだ 8KB の単一のブロックに収まるデータブロックへのポインタを使用できます。

それでは、単一のブロックには収まりきらないほどの数の値を別のグローバルに書き込んでみます。そして、最初のブロックに収まらなかった新しいデータブロックにポイントするポインタブロックの中に新しいノードが表示されます。

それでは、それぞれ 1000 文字を持つ値を 50 個書き込んでみましょう。 このデータベースのブロックサイズは 8192 バイトであることを覚えておいてください。

   set str=""
   for i=1:1:1000 {
       set str=str_"1"
   }
   for i=1:1:50 {
       set ^test(i)=str
   }
​   quit

下の画像をご覧ください。

ポインタブロックレベルでデータブロックにポイントするノードがいくつかあります。 各データブロックには、次のブロックをポイントするポインタがあります (「適切なリンク」)。 Offset は、このデータブロック内で占有されているバイト数をポイントしています。

それでは、ブロックの分割をシミュレートしてみましょう。 ブロックの合計サイズが 8KB をオーバーしてしまうほどの数の値をブロックに追加しましょう。それにより、ブロックは半分に分割されます。

サンプルコード

   set str=""
   for i=1:1:1000 {
       set str=str_"1"
   }
   set ^test(3,1)=str
   set ^test(3,2)=str
​   set ^test(3,3)=str

結果は以下の通りです。

ブロック #50 が分割され、新しいデータが入っているのが分かります。 ブロック #50 から取り出された値はブロック #58 に置かれ、このブロックにポイントするポインタがポインタブロックに表示されているのが分かります。 他のブロックに変化はありません。

長い文字列を使った例

8KB (データブロックのサイズ) よりも長い文字列を使うと、「長いデータ」で構成されるブロックができます。 そのような状況は、例えば、文字列を 10000 バイトとして書き込んでシミュレートします。

サンプルコード

   set str=""
   for i=1:1:10000 {
       set str=str_"1"
   }
   for i=1:1:50 {
       set ^test(i)=str
​   }

結果を見てみましょう。

結果としては、新しいグローバルノードは加えずに、値を変更しただけなので、画像に表示されているブロック構造に変更はありません。 しかし、すべてのブロックで、Offset の値 (占有されているバイト数) に変化がありました。 例えば、ブロック #51 の Offset の値は、7088 から 172 に変わっています。 新しい値がブロックに収まらない場合は、データの最後のバイトへのポインタが変更されるということが分かりました。しかし、データはどこに行ったのでしょう? 現時点では、「大きなブロック」に関する情報を示す技術的な可能性はありません。 それでは、^REPAIR ツールを使って、ブロック #51 の新しいデータに関する情報を取得してみましょう。

このツールの仕組みを詳しく説明いたします。 右側のブロック #52 へのポインタがあり、同じ番号が次のノードの親ポインタブロックで指定されているのが分かります。 グローバルの照合順序は Type 5 に設定されています。 長い文字列を持つノードの数は 7 個です。 場合によっては、1 つのブロックの中に、いくつかのノードのデータ値と別のノードの長い文字列の両方が含まれる場合があります。 また、次のブロックの先頭で予測できる次のポインタ参照も表示されています。

長い文字列のブロックについて: キーワード「BIG」がグローバルの値をとして指定されているのが分かります。 それは、データが実際には「大きなブロック」に保管されていることを意味します。 同じ行には、含まれている文字列の長さの合計とこの値を保管するブロックの一覧が表示されています。 それでは、ブロック #73 (長い文字列のブロック) を見てみましょう。

残念ながら、このブロックはエンコードされた状態で表示されています。 しかし、ブロックヘッダーのサービス情報 (長さは常に 28 バイト) に続いて、私たちのデータが表示されているのが分かります。 データ型が分かっていると、ヘッダーの内容をとても簡単にデコードできます。

<td style="width: 90px">
  値
</td>

<td>
  説明
</td>

<td>
  コメント
</td>
<td>
  E4 1F 00 00
</td>

<td>
  データの最後をポイントする Offset
</td>

<td>
  8164 バイトとヘッダーの 28 バイトを合わせて合計 8192 バイトあり、ブロックは満タンです。
</td>
<td>
  18
</td>

<td>
  ブロックタイプ
</td>

<td>
  記憶にあるかと思いますが、24 は長い文字列の型指定子です。
</td>
<td>
  05
</td>

<td>
  照合順序
</td>

<td>
  照合順序 5 は「標準の Caché」を意味します
</td>
<td>
  4A 00 00 00
</td>

<td>
  適切なリンク
</td>

<td>
  ここは 74 になっています。記憶にあるかと思いますが、値はブロック #73 と #74 に保管されます。
</td>
位置
0-3
4
5
8-11

ブロック #51 のデータはわずか 172 バイトしか占有していないことを覚えていますか? 大きな値を保存したにも関わらずです。 つまり、有効なデータがわずか 172 バイトとなり、ブロックはほぼ空になったように思えますが、それでも 8KB を占有しているのです! そのような場合、空きスペースには新しい値が入力されることが分かりましたが、Caché ではそのようなグローバルを圧縮することもできます。 %Library.GlobalEdit クラスに CompactGlobal メソッドがあるのはそのためです。 このメソッドの効率を確認するために、サンプルコードを使って大規模のデータを作成してみましょう。例えば、ノードを 500 個作成します。

こちらのコードを実行します。

   kill ^test
   for l=1000,10000 {
       set str=""
       for i=1:1:l {
           set str=str_"1"
       }
       for i=1:1:500 {
           set ^test(i)=str
       }
   }
   quit

すべてのブロックを表示するのは控えますが、要点は理解していただけると思います。 データブロックはたくさんありますが、ノードの数は少なくなっています。

以下のように CompactGlobal メソッドを実行します

write ##class(%GlobalEdit).CompactGlobal("test","c:\intersystems\ensemble\mgr\test")

結果を見てみましょう。 ポインタブロックにはノードが 2 個しかありません。つまり、最初はポインタブロックにノードが 72 個もあったのに対し、実際はすべての値が 2 個のノードに移動されているのです。 従い、70 個ものノードを取り除いたことになり、ブロックの読み取り操作を行う回数が減ったため、グローバルをイテレーションしてデータにアクセスする時間が短縮されました。

CompactGlobal には、グローバルの名前やデータベース、ターゲットとするフィル値 (デフォルトは 90%) など、様々なパラメーターを渡すことができます。 そして、Offset (占有されているバイト数) の値は 7360 となり、デフォルトのフィル値 90% に近くなったことが分かります。 関数には、処理されたメガバイト数や圧縮後のメガバイト数など、複数の出力パラメーターがあります。 以前、グローバルは、今や廃止ツールとされる ^GCOMPACT を使って圧縮されていました。

ちなみに、ブロックが部分的に満たされた状態で変化しないというのはいたって普通のことです。 また、グローバルを圧縮するのは好ましくないと考えられる場合もあります。 ですが、例えば、ほぼ読み取るだけで、変更することが滅多にないというグローバルは、圧縮すると良いかもしれません。 但し、グローバルがしょっちゅう変更される場合なら、データブロックの密度が低いと頻繁にブロックを分割する手間が省けるほか、新しいデータもより素早く保存できます。

本記事のまたさらに次の続編では、InterSystems School 2015 で初の開催となった InterSystems ハッカソン (hackathon) の最中に導入された、私自作のプロジェクトのまた別の機能「データベースブロックの分布状況を表すマップ」とその実用的な活用方法について解説いたします。

0
0 309
記事 Hiroshi Sato · 2月 1, 2021 1m read

これは InterSystems FAQ サイトの記事です。
 

$IsObject()を使用して判別できます。 調べたい変数をvとすると、

$IsObject(v)=1 // vはOREF
$IsObject(v)=0 // vはOREFではない
$IsObject(v)=-1 // vはOREFだが、有効なオブジェクトを指していない

となります。

vが未定義の場合は、$IsObject(v)はUNDEFINEDエラーとなりますので、ご注意ください。

UNDEFINEDエラーを防止するには、次のように$Getを使用することをお勧めします。

$IsObject($Get(v))
0
0 299
記事 Hiroshi Sato · 2月 1, 2021 1m read

これは InterSystems FAQ サイトの記事です。
 

ドキュメント上は明確に記述されていませんが、エクスポートの追加ボタンを押した時に表示されるファイルダイアログのファイル名にグローバル名 + .GBLと入力することでグローバルをエクスポートの対象にすることができます。
(例: ^aaaの場合、^aaa.gblと指定)

この機能はクラス、ルーチン等と一緒にグローバルデータを一括してエクスポート/インポートする際に、便利な機能ですが、XMLで表現されるため、データ容量は実データに比較し大きくなりますので、大量データを処理する場合には必要な容量に注意が必要です。

0
0 360
記事 Mihoko Iijima · 1月 29, 2021 4m read

これは InterSystems FAQ サイトの記事です。

%SYS.Journal.File クラスの ByTimeReverseOrder クエリ と %SYS.Journal.Record クラスの List クエリを使用して検索することができます。

それぞれのクエリの役割は以下の通りです。

A) %SYS.Journal.File クラスの ByTimeReverseOrder クエリ

ジャーナルファイル名を取得できます。ジャーナルファイル名の降順で結果が返ります。​​

0
0 493
記事 Mihoko Iijima · 1月 29, 2021 2m read

これは InterSystems FAQ サイトの記事です。

システム提供の %SYS.GlobalQueryクラス の Size クエリーを使用することで取得できます。

使用例は、以下のサンプルコードをご参照ください。
※カラムやパラメータの指定はクラスリファレンスをご確認ください。

 set dir="C:\intersystems\iris\mgr\user" // IRIS.DAT(またはCACHE.DAT)フォルダ
 set rs = ##class(%ResultSet).%New("%SYS.GlobalQuery:Size")
 do rs.Execute(dir) // 第3引数でマスク指定も可
 while (rs.Next()) { 
   set gname= rs.Get("Name") // グローバル名
   set gsize= rs.Get("Used MB") // グローバルサイズ(MB)
   write gname," : ",gsize,!
 }

 

グローバル変数のサイズが大きい場合、取得時間がかかる場合もあります。その場合、Size クエリー 実行時、第 6 引数に 1 を指定してクエリを再実行してみてください。

0
0 407
記事 Hiroshi Sato · 1月 27, 2021 1m read

これは InterSystems FAQ サイトの記事です。
 

InterSystems ObjectScript言語では引数の異なる同名のメソッドを定義することはできません。

InterSystems ObjectScript言語は一般的に動的言語と呼ばれるプログラミング言語に分類されます。

ObjectScriptではメソッド実行時にどの引数を使用するかどうかは自由に制御可能ですので、動的プログラミング言語ではないJava等の言語と異なり、コンパイルの段階で厳密に引数の数でメソッドを区別する必要がありません。

従って ObjectScript言語は、オーバーロードと一般に呼ばれる言語仕様を含んでいません。

Java等で記述されたプログラムを移植する際にオーバーロード相当の機能を実現するには、

ClassMethod test(args... as %String)

のように引数の後ろに ... を付加します。

これにより、可変長引数を渡すことが可能です。

このメソッドに複数の引数が渡された場合は、args(1)=第一引数 args(2)=第二引数というように順次設定されます。

これを使用して、メソッドコード中で渡された引数の個数を取得して処理を分岐させることができます。

※ただし、引数のデータタイプを判別することはできません。

0
0 282
記事 Hiroshi Sato · 1月 27, 2021 4m read

これは InterSystems FAQ サイトの記事です。
 

クラス定義のプロパティの表示順は、スタジオのプロパティウィザードを利用して登録した場合は、末尾に追記されます。

また、エディタ上の任意の場所でプロパティ定義文を記述する場合は、その場所に追記され、クラス定義が登録されます。


つまり、定義者が記述した順番に登録されます。

(スタジオが並び換えを行ったりはしません。)

作成したクラス定義が、PersistentやSerialのようにデータベースに格納する属性を持ったクラス定義である場合、”初回のコンパイル”で クラス定義に対応するグローバル変数の定義情報=ストレージ定義を作成します。

初回コンパイル以降に、プロパティ定義の追加が行われれば、そのプロパティに対応するグローバル変数のスロット番号を、末尾に追加し、ストレージ定義を更新します。

以下の例は、クラス定義に対応するストレージ定義の例です。

(初回コンパイル時の状態)

0
0 177
記事 Hiroshi Sato · 1月 27, 2021 1m read

これは InterSystems FAQ サイトの記事です。
 

一括コメントを付与したい行全体を選択後、Ctrl + / を入力することで、選択範囲を一括でコメントにすることができます。

コメントの一括解除には、コメントを解除したい行全体を選択後 Ctrl + Shift + / を入力します。

0
0 446
記事 Toshihiko Minamoto · 1月 21, 2021 9m read

InterSystems Caché のグローバルは、デベロッパーにとって非常に便利な機能を提供します。 しかし、グローバルが高速な上に効率が良いのはなぜでしょう?

理論

基本的に、Caché データベースとは、データベースと同じ名前を持ち、CACHE.DAT ファイルを含んだカタログのことです。 Unix システムでは、このデータベースを普通のディスクパーティションにすることもできます。

Caché のデータはすべてブロックとして保管され、バランスド B* ツリーとして整理されます。 基本的にすべてのグローバルがツリーに保管されると考えると、グローバルのサブスクリプトはツリーの枝を意味する一方で、グローバルのサブスクリプトの値はツリーの葉として保管されると言えます。 バランスド B* ツリーと通常の B ツリーの違いは、前者の枝には $Order$Query の両関数を使ってサブスクリプト (この記事ではグローバル) のイテレーションをスピーディに実行するのに役立つ適切なリンクがあり、ツリーの幹に戻る必要がないという点です。 

デフォルトで、データベースファイルの各ブロックのサイズは 8,192 バイトに固定されています。 既存のデータベースのブロックサイズは変更できません。 新しいデータベースを作成する場合は、保管するデータ型に合わせて、16kB、32 kB、または 64 kB をブロックサイズに選択できます。 但し、すべてのデータはブロック毎に読み取られることを覚えておきましょう。つまり、1 バイトの値を 1 つだけリクエストする場合でも、システムはリクエストされたデータブロックの前にいくつかのブロックを読み取ります。 また、Caché はデータを再利用するためにデータベースブロックをメモリに格納するグローバルバッファを使うことと、ブロックサイズと同じサイズとグローバルバッファを使用することを覚えておきましょう。 対応するブロックサイズのグローバルバッファがシステムにない場合は、そのブロックサイズの既存のデータベースをマウントしたり、新しいデータベースを作成したりはできません。 実際使用するブロックサイズでメモリサイズを定義することをおすすめします。 データベースブロックよりも大きいバッファブロックを使用することは可能ですが、その場合、各バッファブロックには 1 つのデータベースブロック、もしくはさらに小さいブロックしか保管されません。


この画像では、8 kB のグローバルバッファのメモリがそれぞれ 8 kB のブロックで構成されるデータベースに割り当てられています。 このデータベースのブロックのうち、空でないものは、それぞれのマップが 62,464 個のブロック (それぞれ 8 kB のブロック) を定義するかたちでマップに定義されています。 

ブロックタイプ

システムは複数のブロックタイプに対応しています。 各レベルにおいて、ブロックの適切なリンクは同じタイプのブロックまたはデータの終わりを意味する Null ブロックにポイントしている必要があります。

  • タイプ 9: グローバルカタログのブロック。 通常このブロックでは、既存のすべてのグローバルがそれぞれのパラメーターと併せて説明されます。このパラメーターには、グローバルのサブスクリプトのコレーション (照合順序) が含まれています。大切なパラメーターの 1 つで、グローバル作成後には変更できません。
  • タイプ 66: 高レベルポインタのブロック。 このブロックの上に置けるのは、グローバルカタログのブロックのみです。
  • タイプ 6: 低レベルポインタのブロック。 このブロックの上に置けるのは高レベルポインタのブロックのみで、これより下に置けるのはデータブロックのみです。
  • タイプ 70: 高レベルポインタと低レベルポイントの両方で構成されるブロック。 このブロックは、対応するグローバルに保管される値の数が少ないため、複数のレベルのブロックを設ける必要がない場合に使用されます。 このブロックは通常、グローバルカタログのブロックと同様、データブロックにポイントします。
  • タイプ 2: 比較的大きなグローバルを保管するポインターのブロック。 値を複数のデータブロックに均等に割り当てるには、ポインタのブロックにレベルを追加すると便利です。 このブロックは通常、ポインタのブロックの間に置かれます。
  • タイプ 8: データブロック。 通常このブロックには、1 つのノードの値ではなく、複数のグローバルノードの値が保管されます。
  • タイプ 24: ラージストリングのブロック。 1 つのグローバルの値がブロックサイズよりも大きい場合、その値はラージストリングの特殊ブロックに記録される一方で、データブロックのノードにはラージストリングのブロック一覧へのリンクが合計サイズと共に保管されます。
  • タイプ 16: マップブロック。 このブロックは、未割り当てのブロックに関する情報を保管するようにデザインされています。

典型的な Caché データベースの最初のブロックには、データベースファイルそのものに関するサービス情報、2 つ目のブロックには複数のブロックからなるマップが含まれます。 最初のカタログブロックは 3 番目 (ブロック #3) に位置付けされ、1 つのデータベースに複数のカタログブロックを保管できます。 この後は、ポインタブロック (枝)、データブロック (葉)、そしてラージストリングのブロックが続きます。 先ほども触れましたが、グローバルカタログのブロックには、データベースやグローバル設定に存在するすべてのグローバルに関する情報が保管されます (そうでない場合は、そのようなグローバルに置かれます)。 この場合、そのようなグローバルを説明するノードには、低レベルの Null ポインタが割り当てられます。 既存のグローバルの一覧は、管理ポータルのグローバルカタログから表示できます。 また、このポータルでは、削除したグローバルをカタログに保存 (照合順序を保存するなど) したり、デフォルトの、またはカスタマイズしたコレ―ションを設定した新しいグローバルを作成したりできます。

一般的に、ブロックのツリーは下の画像のように描くことができます。 赤く表示されているのは、ブロックへのリンクです。

データベースの整合性

Caché の現在のバージョンでは、データベースの最も重大な問題を解決しましたので、データベースのパフォーマンスが低下するリスクは極めて低くなっています。 それでも、定期的に ^Integrity ツールを使って自動整合性チェックを実行することをおすすめします。同ツールは、データベースページもしくはタスクマネージャーのマネジメントポータルを使って、%SYS ネームスペースのターミナルで起動できます。 自動整合性チェックは、デフォルトでセットアップされており、事前に定義もされているので、有効化するだけで実行できます。

 ​​​​​​​

整合性チェックでは、OSI モデルの下位層でのリンク検証、ブロックタイプの確認、適切なリンクの分析、グローバルノードと適用される照合順序のマッチングが行われます。 整合性チェックの最中にエラーが出る場合は、%SYS ネームスペースから ^REPAIR ツールを実行できます。 このツールを使えば、どのブロックでも表示できる上に、必要であれば、データベースを修正するなどの変更を施すことができます。 

実践

しかし、ここまで紹介した内容は理論にすぎません。 グローバルとそのブロックの実態を見極めるのは未だに容易なことではありません。 現時点で、ブロックを表示するには、上述した ^REPAIR ツールを使う方法しかありません。 下に示すのがこのプログラムの典型的な出力です。

 

1 年前、私は新しいツールを開発するプロジェクトに着手しました。それは、データベースを破損させるリスクなしにブロックのツリーをイテレーションし、それらのブロックをウェブ UI に視覚化し、その視覚化されたイメージを SVG または PNG に保存するオプションを提供するというものです。 このプロジェクトは名付けて CacheBlocksExplorer。ソースコードは、Github からダウンロードしていただけます。

実装した機能には以下の通りです。

  • システム内の構成済みのデータベースや単純にマウントされたデータベースを表示する。
  • 情報をブロック別、ブロックタイプ別、右ポインタ別、リンク付ノードの一覧別に表示する。
  • 前述のレベルよりも低いレベルのブロックにポイントしているノードに関する詳細を表示する。
  • ブロックへのリンクを削除することにより、そのブロックを非表示にする (そのブロックに保管されているデータを損傷することはありません)。

To-Do リスト:

  • 適切なリンクを表示する: 現在のバージョンでは、適切なリンクはブロックに関する情報の中に表示されていますが、矢印として表示する方がベターでしょう。

  • ラージストリングのブロックを表示する: 現在のバージョンでは表示されていない。

  • グローバルカタログのブロックを3 番目のブロックだけでなく、すべて表示する。

  • ツリー全体も表示したいのですが、何十万個もあるブロックとそれぞれのリンクを一緒にすばやくレンダリングできるライブラリは、まだ見つかっていません。現在のライブラリは、ウェブブラウザーにレンダリングしていますが、そのスピードは Caché が構造全体を読み取るスピードよりもかなり遅いです。

    次回の記事では、私の自作ツール Cache Block Explorer を実際に使い、その機能をさらに詳しく説明するほか、使用例をいくつか紹介し、グローバルやブロックに関する実行可能なデータを数多く取得する方法を披露したいと思います。

    0
    1 557