#グローバル

0 フォロワー · 35 投稿

グローバルは、InterSystemsデータプラットフォームに格納されている多次元スパース配列です。 InterSystems製品のすべては、クラス、テーブル、ドキュメント、コードなどのグローバルに格納されます。 ドキュメント

InterSystems公式 Seisuke Nakahashi · 10月 23, 2025

インターシステムズは、InterSystems IRIS® data platformInterSystems IRIS® for HealthTMHealthShare® Health Connect のメンテナンスバージョン 2025.1.2 および 2024.1.5 をリリースしました。今回のリリースでは、最近お知らせした以下の警告や勧告の修正が含まれています。

製品の品質改善のために、開発者コミュニティを通じてぜひご意見をお聞かせください。

ドキュメント

詳細な変更リストとアップグレードチェックリストはこちらのドキュメントをご参照ください(すべて英語, 2025.1):

早期アクセスプログラム (Early Access Programs; EAPs)

0
0 25
InterSystems公式 Ayumu Tanaka · 1月 15, 2025

インターシステムズは、特定の $List シンタックスを使用することで不正なデータベースとジャーナルレコードが作成される問題を修正しました。この問題が発生する可能性は非常に低いものですが、発生した場合の影響は非常に大きなものとなります。

この問題は、以下の製品およびそれらベースとしたその他のインターシステムズ製品に存在します:

  • InterSystems IRIS® : 2023.3, 2024.1.0, 2024.1.1, 2024.1.2, 2024.2, 2024.3
  • InterSystems IRIS® for Health: 2023.3, 2024.1.0, 2024.1.1, 2024.1.2, 2024.2, 2024.3
  • HealthShare® Health Connect: 2023.3.0, 2024.1, 2024.1.1, 2024.1.2, 2024.2, 2024.3
  • HealthShare® Unified Care Record と関連製品: 2024.2

この問題はUnicode版の製品でのみ発生します。

以下のシンタックスでグローバル上のリスト形式データに新しい要素を追加する際に問題が発生します。

SET $LIST(<グローバル>, *+1) = 値

0
0 66
記事 Toshihiko Minamoto · 2月 16, 2021 8m read

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

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

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

本記事を含む合計 3 回の連載を通じてご紹介する簡単なステップを使えば、すべてのグローバル (特殊なものは除く) を Caché のクラスにマッピングできるようになります。  特殊なものについては、私が長年に渡って集めた様々な種類のマッピングを zip ファイルにまとめて提供いたします。  これは新しいデータを対象としたステップではありません。グローバルがないという方は、デフォルトのストレージをお使いください。

グローバルデータについて理解できないという方は、Support@InterSystems.com までデータをお送りください。喜んでサポートさせていただきます。

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

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

それでは例をご紹介します!

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

^mapping("Simple",1)="Brendan Bannon^55192^SQL Manager"

^mapping("Simple",2)="Nicole Aaron^63375^Support Specialist"

^mapping("Simple",3)="Kyle Baxter^61155^Senior Support Specialist"

^mapping("Simple",4)="Prasad Kari^58471^Support Specialist"

^mapping("Simple",5)="Clive Morgan^57982^Senior Support Specialist"

^index("Simple","HireDate",55192,1)=""

^index("Simple","HireDate",57982,5)=""

^index("Simple","HireDate",58471,4)=""

^index("Simple","HireDate",61155,3)=""

^index("Simple","HireDate",63375,2)=""

それでは、以下 7 つのステップを実行し、SQL や Object を使ってこのデータの確認ができるクラスを作成しましょう。

ステップ 1

^mapping グローバルはいたってシンプルです。  各ノードには同じ型のデータが含まれています。  ^index グローバルについては、^mapping グローバルの後に解説します。

ステップ 2

最初の添え字は単なる定数です。 2 つ目の添え字は、1 つずつ増加するカウンタで、データの各行で異なる数字になっています。

ステップ 3

プロパティは、それぞれ Name、HireDate、Title のようです。  55192 というのは、^index グローバルを参照したり、データの用途を把握している人に確認したりしないと、それが日付であることは分かりにくいと思います。採用日であることはなおさら分からないでしょう。  2 つ目の添え字の値のプロパティを忘れずに定義しておきましょう。

ステップ 4

Property Name As %String;

Property HireDate As %Date;

Property Title As %String;

Property Sub2 As %Integer;

ステップ 5

Caché SQL Storage を使用する各クラスの IdKey インデックスをそれぞれ定義する必要があります。

     Index Master On Sub2 [ IdKey ];

インデックスマップのインデックスを定義する必要はありませんが、定義しておくと良いでしょう。

     Index  hireDateindex On HireDate;

ステップ 6

では、ここでクラスとグローバルの間でマッピングを作成する必要があります。  私はウィザードを使うのが好きなので、そのスクリーンショットをお見せします。XML を書くのが得意な方は、Caché SQL Storage を手動で定義していただけます。

Storage アイコン  をクリックします。  Storage Definition には好きな名前を付けてください。私はデフォルト名を使いました。  Caché SQL Storage をクリックし、「完了」をクリックします。

(補足)
Caché SQL Storage を作成しますと、以下のように右側のインスペクタ画面にそのプロパティが表示されますので、「SQL storage map」欄をクリックして右にある「...」をクリックします。

私は下のウィンドウで Map Name を変更しましたが、Map1 を使っても構いません。  唯一入力する必要があるのは、Global Name です。  「^」を付けるのを忘れないでください。  永続クラスにはそれぞれデータマップが 1 つずつ必要です。  複数のインデックスマップを持たせることができます。

「OK」をクリックするとウィザードを終了してしまうので、すべて完了するまではクリックしないでください。

ステップ 6a

次は添え字を定義したいと思います。  ここでは、グローバルの添え字を IdKey まで (IdKey を含む) すべて定義します (IdKey には 複数の添え字が使われる場合があります)。  式ボックスでフィールドを参照するには、そのフィールドを {} の中に入れます。  有効な COS 式であれば、どのような式でも入力できます。  ここではシンプルに、Subscript 1 には定数を、Subscript 2 にはフィールドを使用しています。 

ステップ 6b

データについては、IdKey の後に来るすべての添え字と「=」サインの右側にあるすべてのデータを説明しています。  Node 列は、追加の添え字がある場合に使用するものです (この例は次の記事でご紹介します)。  Piece と Delimiter は、グローバルの中にあるプロパティの位置を説明しています。  グローバルデータを解析する場合、デフォルトでは $PIECE コマンドが使用されます。

ステップ 6c

ここでは表示するものがありません。空白にしておきます。

では、^index グローバルに対してもステップ 6 を実行してください。

ステップ 6a

今回は添え字を 4 つ (定数 2 つ、HireDate、IdKey (Sub2)) を使います。  各インデックスマップは IdKey を作成できる必要があります。  通常は、添え字の中にあるものですが、データの一部となっている場合もあります。

ステップ 6b

追加の添え字もデータもないので、このステップは空白にしておきます

ステップ 6c

ここも特になしです。

ステップ 7

残るはクラスをコンパイルしてから、テーブルに対しクエリを実行し、データが正しく表示されることを確認するだけです。

Compilation started on 08/15/2016 15:20:58 with qualifiers 'fck /checkuptodate=expandedonly'
Compiling class Mapping.Example1
Compiling table Mapping.Example1
Compiling routine Mapping.Example1.1
Compilation finished successfully in 0.270s.

SELECT Sub2, Name, HireDate, Title FROM Mapping.Example1

Sub2              Name                                    HireDate              Title

1                      Brendan Bannon              1992-02-10          SQL Manager

2                      Nicole Aaron                      2014-07-07          Support Specialist

3                      Kyle Baxter                         2008-06-08          Senior Support Specialist

4                      Prasad Kari                          2001-02-01          Support Specialist

5                      Clive Morgan                     1999-10-01          Senior Support Specialist

また、自分で入力したいという方のために、XML で記述された Storage の定義を下にお見せしておきます。

/// アクティブな Storage Definition は各クラスごとに 1 つしか記述できません。
/// 各 Storage Definition にはそれぞれ複数のマップを持たせることができます。
Storage NewStorage1
{
<SQLMap name="HireDateIndex">
<ConditionalWithHostVars></ConditionalWithHostVars>
<Global>^index</Global>
<Subscript name="1">
<Expression>"Simple"</Expression>
</Subscript>
<Subscript name="2">
<Expression>"HireDate"</Expression>
</Subscript>
<Subscript name="3">
<Expression>{HireDate}</Expression>
</Subscript>
<Subscript name="4">
<Expression>{Sub2}</Expression>
</Subscript>
<Type>index</Type>
</SQLMap>
<SQLMap name="SimpleDataMap">
<Data name="HireDate">
<Delimiter>"^"</Delimiter>
<Piece>2</Piece>
</Data>
<Data name="Name">
<Delimiter>"^"</Delimiter>
<Piece>1</Piece>
</Data>
<Data name="Title">
<Delimiter>"^"</Delimiter>
<Piece>3</Piece>
</Data>
<Global>^mapping</Global>
<Subscript name="1">
<Expression>"Simple"</Expression>
</Subscript>
<Subscript name="2">
<Expression>{Sub2}</Expression>
</Subscript>
<Type>data</Type>
</SQLMap>
<StreamLocation>^Mapping.Example1S</StreamLocation>
<Type>%CacheSQLStorage</Type>
}

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

さらに知識を深めたいという方は、是非パート 2 もお読みください

1
1 446
記事 Toshihiko Minamoto · 7月 5, 2023 39m read

この記事では、InterSystems IRIS の学習に関連したトピックについて、開発者コミュニティでの厳選された記事にアクセスすることができます。機械学習や Embedded Python、JSON、API と REST アプリ、InterSystems環境の構築と管理、DockerとCloud、VSCode、SQL、Analytics/BI、グローバル、セキュリティ、DevOps、インターオペラビリティNative API、それぞれでランク付けされたトップの記事を見ることができます。ぜひ、楽しみながら学んでください!  

機械学習

機械学習は、高度なデータ分析を構築し、優れた効率で手動活動を自動化するための必須技術です。既存のデータから学習する認知モデルを作成し、自己調整されたアルゴリズムに基づいて予測、確率計算、分類、識別、「非創造的」な人間の活動の自動化を実行します。

すべてのシナリオにおいて、InterSystems IRISは、これらのマシンラーニングモデルを作成、実行、利用可能にし、使用するためのデータプラットフォームおよび環境として機能します。IRISは、SQLコマンドからのML利用(IntegratedML)、Embedded PythonやPMML(Predictive Model Markup Language)による機械学習が可能です。以下の記事でその機能を確認することができます。

名称概要URL
IntegratedMLハンズオンラボIntegratedMLの実践的な概要https://community.intersystems.com/post/integratedml-hands-lab
InterSystems IRISデータプラットフォームによるAIロボット化IRISプロダクションのAIhttps://community.intersystems.com/post/ai-robotization-intersystems-iris-data-platform
IRIS IntegratedMLを使った糖尿病予測WebアプリIntegratedMLサンプルhttps://jp.community.intersystems.com/node/535221
妊産婦の健康リスクの予測IntegratedMLサンプルhttps://community.intersystems.com/post/predict-maternal-health-risks
機械学習によるコミュニティー記事の整理 - 1Python MLライブラリの利用https://community.intersystems.com/post/using-machine-learning-organize-community-1

 

ObjectScript言語

ObjectScript は InterSystems のオフィシャルプログラミング言語です。簡単で柔軟性があり、バックエンド、統合、および分析アプリケーションの作成に非常に強力です。詳細については、以下の記事を参照してください。

名称概要URL
InterSystems ObjectScript 101++ (EN)ObjectScriptを学ぶビデオシリーズhttps://community.intersystems.com/post/intersystems-objectscript-101-en
$Sequence関数について数列の作成するhttps://community.intersystems.com/post/sequence-function
Caché ObjectScript でのパフォーマンスの高いループの作成ループの作成https://jp.community.intersystems.com/node/481811
データの匿名化、iris-Disguiseの導入ObjectScript の永続的なクラスとプロパティをカスタマイズする方法について説明するhttps://jp.community.intersystems.com/node/510731
ObjectScriptのエラー処理に関するスニペット例外処理https://jp.community.intersystems.com/node/491451
ObjectScriptにおける従来のデバッグデバッグ手法https://community.intersystems.com/post/traditional-debugging-objectscript
Caché での正規表現の使用正規表現を使った作業https://jp.community.intersystems.com/node/481816
ObjectScript における堅牢なエラー処理とクリーンアップ品質の高いコードを書くhttps://jp.community.intersystems.com/node/486226
InterSystems Ensembleを愛し、心配することをやめた理由プロダクションでのJSON処理https://community.intersystems.com/post/how-we-learned-stop-worrying-and-love-intersystems-ensemble
より使いやすくなったオブジェクト・ダンプダンプするオブジェクトhttps://community.intersystems.com/post/more-usefull-object-dump
InterSystems IRISのマクロを使ったロギングマクロを使ったロギングhttps://jp.community.intersystems.com/node/503796
SYSLOG - その実態と意味することシスログのデバッグ情報https://jp.community.intersystems.com/node/492146
%Statusを使ったデバッグのヒント%Statusを使ったデバッグhttps://jp.community.intersystems.com/node/503801
$Queryの有効活用$Query を使ってデータを探すhttps://community.intersystems.com/post/making-most-query
多次元プロパティの永続性 - Part 1 (クラシック)多次元永続プロパティhttps://community.intersystems.com/post/multidimensional-property-persistence-part-1-classic
採用されたBitmapBitmap インデックスhttps://community.intersystems.com/post/adopted-bitmap
タイムロードになる方法 - 誕生日付と時刻のAPIhttps://jp.community.intersystems.com/node/527796
正確なバージョン情報($zv / $zversion)の重要性と収集についてIRISバージョン取得https://community.intersystems.com/post/importance-and-collection-exact-version-information-zv-zversion
1840年12月以前の日付 ? $H( orolog )がネガティブ?ネガティブな日付https://community.intersystems.com/post/date-dec1840-negative-horolog
Caché でのカスタム・インデックス・タイプの作成カスタムインデックスの作成https://jp.community.intersystems.com/node/479316
$LIST 文字列フォーマットと %DynamicArray および %DynamicObject クラス$LIST、%DynamicObject、%DynamicArrayの使用法https://jp.community.intersystems.com/node/483711
^ERRORグローバルに対するSQLSQLを使ってエラーの内容の確認https://community.intersystems.com/post/sql-error-global
コードによるデフォルト設定値の追加デフォルト値の設定https://community.intersystems.com/post/add-default-setting-value-code
ダイナミックオブジェクトの反復処理イテレート(反復処理)の使用https://community.intersystems.com/post/iterate-over-dynamic-object
クラスのすべてのプロパティをリストアップする (ObjectScriptがお気に入りな理由)ObjectScriptプロパティの反復使用https://jp.community.intersystems.com/node/515786
いつも使っているtry catchブロックTry Catchのハンドリングhttps://community.intersystems.com/post/try-catch-block-i-usually-use-intersystems-objectscript
ObjectScriptでシェルコマンドの実行ObjectScriptでシェルコマンドの実行https://community.intersystems.com/post/running-shell-commands-objectscript

Embedded Python

Python は、世界で最も人気があり、よく使われているプログラミング言語の 1 つです (https://www.tiobe.com/tiobe-index/)。InterSystems IRIS は、すべての主要なプログラミング言語に対して開かれたデータ・プラットフォームです。しかし、Python は、この素晴らしい言語とそのライブラリは、クラス、SQL、および統合/プロダクショ ンなど、IRIS のあらゆる場所で使用することができます。ObjectScript ( InterSystems のプログラミング言語 ) を知らない、または知りたくない人にとって、Python は素晴らしい選択肢となります。そのやり方については、以下の記事を参照してください。

名称概要URL
機械と戦おうEmbedded Pythonを使ったチックタックトー・ゲームの構築https://community.intersystems.com/post/lets-fight-against-machines
InterSystems IRIS 2021.2+ Python サンプル ( Embedded, Native API およびノートPC)複数のPythonノートPCでPythonとIRISを見るhttps://community.intersystems.com/post/intersystems-iris-20212-python-examples-embedded-native-apis-and-notebooks
Embedded PythonによるWebSocketクライアントCustom Socket サンプルhttps://community.intersystems.com/post/websocket-client-embedded-python
AWS LambdaにおけるIRIS Python Native APIAWSでのPythonhttps://community.intersystems.com/node/485361
JupyterノートPCにObjectScriptを追加する方法ノートPCでのIRIShttps://jp.community.intersystems.com/node/521496
ようこそDjangoIRISをデータベースとしたPython Djangoアプリの作成https://jp.community.intersystems.com/node/527801
IRISとGoogle Maps APIによるジオコーディングGeocoding python ライブラリの使用https://community.intersystems.com/post/geocoding-iris-and-google-maps-api
IRISとPython gTTSを用いたテキストから音声への変換のためのRESTサービスgTTSを使用したPythonサンプルhttps://community.intersystems.com/post/rest-service-convert-text-audio-using-iris-and-python-gtts
Python Flask WebフレームワークによるIRISレスポンシブダッシュボードの作成IRISによるFlask Webアプリhttps://community.intersystems.com/post/building-iris-responsive-dashboard-python-flask-web-framework

JSON

JSON は、マーケットで最も広く使用されている、データの送受信のための相互運用性フォーマットの 1 つです。InterSystems IRIS は、いくつかの方法でこの形式をサポートしています。JSON (DocDB) でネイティブ・データベースを持ち、オブジェクトを直列化および非直列化し、特に REST サービスからの要求と応答を JSON で処理することが可能です。以下の記事を確認してください。

名称概要URL
Caché 2016.1における新しいJSON機能の紹介ObjectScript JSON API の紹介https://community.intersystems.com/post/introducing-new-json-capabilities-cach%C3%A9-20161
JSONの機能強化JSON Adaptor APIhttps://jp.community.intersystems.com/node/481776

APIとRESTアプリ

バックグラウンドアプリケーションは現在、REST(Representational State Transfer)パラダイムで開発され、Web APIとして公開されています。以下の記事で、その仕組みを確認してください。

名称概要URL
InterSystemsのデータプラットフォームのためのGraphQLGraphQLスタイルでREST APIの作成https://jp.community.intersystems.com/node/481796
InterSystems API Managerの紹介API Managementの概要https://community.intersystems.com/post/introducing-intersystems-api-manager
RESTの高度なURLマッピングAPIへの経路のマッピングhttps://jp.community.intersystems.com/node/497976
AppS.REST: InterSystems IRISのための新しいRESTフレームワークRESTアプリを簡単に作成https://jp.community.intersystems.com/node/497991
RESTForms : クラスのためのREST APICRUDアプリケーションのためのREST APIの開発https://jp.community.intersystems.com/node/479226
スペックファーストのアプローチによるREST APIの作成Contract First ApproachによるAPI開発https://jp.community.intersystems.com/node/476556
ObjectScript REST API クックブックREST API 開発のヒントhttps://community.intersystems.com/post/objectscript-rest-api-cookbook
永続クラスとシリアルクラスからSwaggerスペックを生成するContract First ApproachによるAPI開発https://jp.community.intersystems.com/node/490976
InterSystems IRIS REST アプリケーションのパターンIRISによるAPI RESTの作成https://community.intersystems.com/post/intersystems-iris-rest-application-patterns
SUSHIでFHIRプロファイルを作成しよう 第1回カスタムFHIRプロファイルの作成https://jp.community.intersystems.com/node/493351
ゼロから使いこなすIAMIAMでAPIの管理https://jp.community.intersystems.com/node/493416
InterSystems API Management を使用してAPIの負荷を分散するAPIMによるAPIのロードバランスhttps://jp.community.intersystems.com/node/482711
InterSystems API Management で OAuth 2.0 による API のセキュリティの確保 - 第1回APIMによるAPI のセキュリティの確保hhttps://jp.community.intersystems.com/node/497946
InterSystems IRISアプリケーションのAngular UIを5分で取得IRISとAngularによるFull Stackアプリhttps://community.intersystems.com/post/getting-angular-ui-your-intersystems-iris-application-5-minutes
InterSystems IRIS REST APIへのアップロードREST APIによるファイル保存https://community.intersystems.com/post/upload-intersystems-iris-rest-api

InterSystems 環境の管理と設定

IRIS環境を適切に管理・設定することは、ユーザーが使用するアプリケーションのパフォーマンス、セキュリティ、可用性、信頼性にとって不可欠です。これらの記事は、これを行うための優れたヒントを与えてくれるでしょう。

名称概要URL
InterSystemsデータプラットフォームにおける容量計画およびパフォーマンスのシリーズのインデックス性能とパフォーマンスの向上https://jp.community.intersystems.com/node/477596
InterSystems Cache での %Installer によるアプリケーションのデプロイメント%Installer によるネームスペース、データベース、およびアプリケーションの構成の作成https://jp.community.intersystems.com/node/478966
InterSystems IRISによる水平方向のスケーラビリティIRISインスタンスを設定し、水平方向のスケーラビリティの実現https://jp.community.intersystems.com/node/477591
Raspberry Pi Raspberry で動作する InterSystems Iris Fhirserver が FHIRserver として動作Raspberry PI内部でIRISの動作https://jp.community.intersystems.com/node/516361
バーチャルIPアドレスを使用しないデータベースミラーリングVIPによるミラーの設定https://jp.community.intersystems.com/node/493401
DockerによるApache Web GatewayWebアプリケーションのSSLとWeb Gatewayの設定https://jp.community.intersystems.com/node/542181
IRISにおけるSAMLとの連携Webサービス向けSAMLhttps://community.intersystems.com/post/work-saml-iris
SYSTEM.Encryption クラスの習得IRISによる暗号化・復号化https://jp.community.intersystems.com/node/523406

Docker と Cloud

新しいアプリケーション・アーキテクチャは、コンテナ Docker と Cloud において動作し、弾力的なスケーラビリティ、インストール、設定、プロビジョニング時間の短縮、インフラの複雑性とコストの削減を実現することを目的としています。これらの記事を読んで、IRISをクラウド化する方法を学んでください。

名称概要URL
Kubernetesにおけるミラーリングを使用しない高可用性IRISデプロイKubernetesによるIRISをクラウドクラスターで利用するhttps://jp.community.intersystems.com/node/490971
Amazon Web Services (AWS)のためのInterSystems IRISリファレンス・アーキテクチャAWSでのIRIShttps://jp.community.intersystems.com/node/481326
Microsoft Azure Resource Manager (ARM)のInterSystems製リファレンス・アーキテクチャ安価なマシン(ARM machine)を使ったAzureでのIRIShttps://jp.community.intersystems.com/node/478971
Dockerfileと仲間たち、またはInterSystems IRISでのObjectScriptプロジェクトの実行と共同作業の方法Dockerプロジェクトにおける重要なファイルについて知ることhttps://community.intersystems.com/post/dockerfile-and-friends-or-how-run-and-collaborate-objectscript-projects-intersystems-iris
CloudFormationテンプレートを使用したAWS向けInterSystems IRISデプロイメントガイドCloudFormationを使ったAWSで使うIRIShttps://jp.community.intersystems.com/node/486206
Google Cloud Platform(GCP) におけるInterSystems IRIS のリファレンス・アーキテクチャGoogle Cloudで使うIRIShttps://jp.community.intersystems.com/node/479806
InterSystems IRISでAWS Glueの使用IRISとAWS Glue(AWSのETLツール)の利用https://jp.community.intersystems.com/node/485971
AmazonのEKSとIRIS。高可用性とバックアップAWSによるHAで使うIRIShttps://jp.community.intersystems.com/node/501186 AWSによるHAでのIRIS
コンテナでの InterSystems レポートの動かしてみるDockerに関するIRISのレポートhttps://jp.community.intersystems.com/node/501656
InterSystems IRIS を Kubeless を使って FaaS モードで実行Kubernetesで使うIRIShttps://jp.community.intersystems.com/node/523446
InterSystems Kubernetes Operator Deep Dive ‐ Kubernetes Operatorの紹介Kubernetesで使うIRIShttps://community.intersystems.com/post/intersystems-kubernetes-operator-deep-dive-introduction-kubernetes-operators
クラウドホストのスケーリングとInterSystems IRISの再構築AWS、Azure、またはGCPでのIRISのスケーリングhttps://community.intersystems.com/post/scaling-cloud-hosts-and-reconfiguring-intersystems-iris
Amazon EKSを用いたシンプルなIRISベースのWebアプリケーションのデプロイメントAWSで使うIRIShttps://jp.community.intersystems.com/node/478961

VSCode

VSCodeは世界で最も使われているIDEの1つです。IRISはこのIDEをフルサポートしています。以下の記事をご覧ください。

名称概要URL
VSCode-ObjectScriptのGitHubでの使用Web Github VSCodeでIRISアプリの開発https://jp.community.intersystems.com/node/510736
IRISによるGitHubのコードスペースGithubでIRISアプリの開発https://jp.community.intersystems.com/node/510736
VSCodeのヒントとコツ - SOAPウィザードVSCodeにショートカットのオプションの作成https://community.intersystems.com/post/vscode-tips-tricks-soap-wizard
VS Codeへの独自のスニペットの追加スニペットの作成https://community.intersystems.com/post/adding-your-own-snippets-vs-code

SQL

SQLは、リレーショナルデータベースを扱うのに最もよく使われる言語の1つです。これらの記事は、クエリの実行方法とデータの永続性を示しています。

名称概要URL
フリーテキスト検索:SQL開発者が隠しているテキストフィールドの検索方法*インデックスの活用で高度な検索を促進https://jp.community.intersystems.com/node/479321
日付範囲クエリのSQLパフォーマンスの向上日付を使ったSQLクエリの実行https://jp.community.intersystems.com/node/479286
スタティックWHERE条件永続的なクラ使うWherehttps://community.intersystems.com/post/static-where-conditions
2021.2 SQL機能スポットライト - ランタイムプランの選択ランタイムSQL実行プランの選択https://jp.community.intersystems.com/node/510746
2020.1 の新機能:ユニバーサルクエリキャッシュSQL Cachehttps://jp.community.intersystems.com/node/535211
マテリアライズド・ビュー永続的なクラスの中にビューの作成https://community.intersystems.com/post/materialized-views
SQLを使ったデバッグのコツSQLコマンドのデバッグhttps://community.intersystems.com/post/debugging-trick-sql
ClassQueries()をテーブルとして使用ビューの作成https://community.intersystems.com/post/using-classqueries-tables
M:Nの関係N対Nの関係性のマッピングhttps://community.intersystems.com/post/mn-relationship
IRISでCOVIDにたいしてのAWS S3データをSQLテーブルとして読み込むAWS S3からCSVデータをIRISのテーブルに取得https://community.intersystems.com/post/reading-aws-s3-data-covid-sql-table-iris
知っておくと便利なクエリパフォーマンスのコツ - Tune TableSQLチューニングhttps://jp.community.intersystems.com/node/535211
データストレージ:開発がうまくいくために知っておくべき情報より高いパフォーマンスを得るために、データストレージ部を永続的なクラスで構成するhttps://community.intersystems.com/post/data-storage-information-you-must-know-make-good-decisions-when-developing
日付範囲クエリのSQLパフォーマンスを改善する vol2SQLクエリの日付に関するチューニングhttps://jp.community.intersystems.com/node/479291
スクロール可能なResultSetのページネーションのサンプルSQLの結果をページ分割する(コメントも参照)https://community.intersystems.com/post/scrollable-resultset-pagination-sample
1日目 InterSystems ObjectsとSQLを用いた開発InterSystems IRISへのSQLに関するコンセプトhttps://community.intersystems.com/post/day-1-developing-intersystems-objects-and-sql
SQLgatewayを利用したDBマイグレーションPostgreSQL、MySQL、その他のデータベースからIRISへのマイグレーションhttps://jp.community.intersystems.com/node/518861
InterSystems IRIS の既存のテーブルに CSV のインポートCSVからSQLテーブルへのインポートhttps://community.intersystems.com/post/importing-csv-existing-table-intersystems-iris
データベースの4つのAPISQLの APIhttps://community.intersystems.com/post/four-database-apis
アトミックでない属性のインデックス作成高度なインデックスのオプションの作成https://jp.community.intersystems.com/node/486236
インデックスについてインデックス作成の基礎知識https://jp.community.intersystems.com/node/492126
Dynamic SQLからDynamic ObjectへDynamicSQLの使用https://community.intersystems.com/post/dynamic-sql-dynamic-object
データ移行ツール - その1:PostgresからIRISへ一般的なデータベースからIRISデータベースへの移行方法を紹介する連載記事https://jp.community.intersystems.com/node/518871

アナリティクスとビジネスインテリジェンス(BI)

アナリティクスとBIは、グラフ、ダッシュボード、サマリー、詳細表などのデータ分析、およびアナリスト・ユーザーによるナビゲーションとデータ探索に基づいて意思決定を行うことを可能にします。ここでは、IRISを使った分析アプリケーションの構築方法を紹介します。

名称概要URL
InterSystems IRISのCOVID-19アナリティクスInterSystems IRISにおけるCOVID-19アナリティクスhttps://community.intersystems.com/post/covid-19-analytics-intersystems-iris
DeepSeeトラブルシューティングガイド不具合修正https://jp.community.intersystems.com/node/542206
AnalyzeThis - InterSystems BIへのクイックスタートInterSystems BIへのクイックスタートhttps://community.intersystems.com/post/analyzethis-%E2%80%93-quick-start-intersystems-bi
InterSystems IRIS用のPower BIコネクタ パート1Power BIでIRISのデータの利用https://jp.community.intersystems.com/node/482606
DeepSee でのポートレットの作成IRIS BIによるアナリティクスポートレットhttps://community.intersystems.com/post/creating-portlets-deepsee
Game Of Throne Analytics、またはアリア スタークリストの長さアナリティクスのサンプルhttps://community.intersystems.com/post/game-throne-analytics-or-how-long-aryas-stark-list
DeepSee Web。AngularJSによるInterSystems Analyticsのビジュアライゼーション。第 1 部Angularを使用するWebダッシュボードhttps://community.intersystems.com/post/deepsee-web-intersystems-analytics-visualization-angularjs-part-1
IRIS でアナリティクスソリューションを構築するIRISでアナリティクスを行うための主なオプションの紹介https://jp.community.intersystems.com/node/501571

グローバル

IRIS では、SQL、クラス、JSON ドキュメント、BI キューブ、その他のカスタム形式など、データを柔軟に保存および取得するための重要なメカニズムとして、グローバルが使用されています。以下の記事で、その方法を垣間見てください:

名称概要URL
グローバルをクラスにマッピングする技術 :1 / 3グローバルの SQL テーブルおよびオブジェクトへのマッピングhttps://jp.community.intersystems.com/node/486176
グローバルは、データ管理の魔法の剣。第1回グローバルに関する基礎知識https://jp.community.intersystems.com/node/476486
GlobalToJSON-embeddedPython-pureグローバルをJSONへの書き出しhttps://community.intersystems.com/post/globaltojson-embeddedpython-pure
InterSystems IRIS のグローバルを使ったトランザクショングローバルパーシスタンスのトランザクション管理https://jp.community.intersystems.com/node/486476
グローバルによる マインドマップの保存グローバルを使ってマインドマップデータの永続化https://jp.community.intersystems.com/node/516226

セキュリティ

どのようなアプリケーションでも、セキュリティを確保することは非常に重要です。セキュリティは、アクセスや承認の管理、トランザクションの追跡と監査、保存および転送されるコンテンツの暗号化、感性的なリソースの保護を保証するための正しい設定パラメータに関連しています。これらの記事を読んで、セキュリティを確立する方法について理解を深めてください。

名称概要URL
InterSystems IRIS Open Authorization Framework (OAuth 2.0) の実装 - 第1回OAuthの使用https://jp.community.intersystems.com/node/478821
WebのデバッグCSPおよびRESTアプリのデバッグhttps://jp.community.intersystems.com/node/501166
InterSystems IRIS のクラスクエリ永続クラス内部でのSQL Queryの定義https://jp.community.intersystems.com/node/483716
TLS/SSLでOSの証明書ストアの使用SSLを行うためにOSの証明書の使用https://community.intersystems.com/post/using-os-certificate-store-tlsssl
インテグリティチェック :スピードアップまたはスピードダウンインテグリティの確保https://community.intersystems.com/post/integrity-check-speeding-it-or-slowing-it-down
データ変更の追跡 - 監査ログ - 1 / 2監査データの保存https://jp.community.intersystems.com/node/483691
TLS/SSL/HTTPS による管理ポータル(プライベート Web サーバー)の運用IRIS Web サーバーへの SSL の設定https://community.intersystems.com/post/running-management-portal-private-web-server-over-tlssslhttps
OAuth認証とInterSystems IRIS:信頼プロトコルのテイム化OAuthの使用https://community.intersystems.com/post/oauth-https://jp.community.intersystems.com/node/493421
SOAP(Web)サービスでのOauth2の利用についてSOAPサービスにおけるOauthの設定https://jp.community.intersystems.com/node/483696
DeepSee: セキュリティの設定 - 1/5IRIS BIにおけるセキュリティhttps://community.intersystems.com/post/deepsee-setting-security-part-1-5
システムのセキュリティレベルの変更についてデフォルトでセキュリティhttps://community.intersystems.com/post/changes-security-level-system

DevOps

DevOpsとは、ソースコードの開発(Dev)から本番運用(Ops)への高速かつ高品質な移行を自動化することを可能にするプラクティスやツールを採用する方法です。IRISでその方法をご覧ください。

名称概要URL
GitLabを使ったInterSystemsソリューションの継続的デリバリー - 第1回:GitGitLabによる継続的デリバリーhttps://jp.community.intersystems.com/node/476396
InterSystems ObjectScripts パッケージ・マネージャの紹介ZPMを使用して、アプリケーション内のサードパーティパッケージを設定およびインストールhttps://jp.community.intersystems.com/node/486186
ZPMshow - 疲れた指のためのヘルパーZPM - IRISパッケージマネージャの使用方法https://community.intersystems.com/post/zpmshow-helper-tired-fingers
プログラムによるミラーのセットアップ方法新しいミラーの作成を自動化するhttps://jp.community.intersystems.com/node/516091
ObjectScript パッケージマネージャにおけるユニットテストとテストカバレッジObjectScriptのコード品質のためのUnit Testsの作成https://jp.community.intersystems.com/node/516111
DockerとMergeCPFを使ったシャードクラスターの展開cpfファイルによる設定の自動化https://community.intersystems.com/post/deploying-sharded-cluster-docker-and-mergecpf
Caché ObjectScript クイックリファレンスObjectScriptリファレンスpdfドキュメントhttps://community.intersystems.com/post/cach%C3%A9-objectscript-quick-reference
ZPMモジュールの解剖学:InterSystems Solution のパッケージングZPMを使用してデプロイメントの自動化https://jp.community.intersystems.com/node/487071
IRISコンテナへのVSCodeの追加VSCodeをdockerインスタンスに埋め込むhttps://community.intersystems.com/post/adding-vscode-your-iris-container
InterSystems IRIS用の新しいデータベース、ネームスペース、およびWebアプリケーションをプログラムによって作成する方法データベースとネームスペースの作成の自動化https://community.intersystems.com/post/how-create-new-database-namespace-and-web-application-intersystems-iris-programmatically
ユニットテスト: ObjectScript コードの品質ユニットテストによる品質保証https://community.intersystems.com/post/unit-tests-quality-your-objectscript-code
インターシステムズ開発者コミュニティのDockerイメージDockerコミュニティイメージhttps://community.intersystems.com/post/some-intersystems-developer-community-docker-images

インターオペラビリティ

IRISは、強力なデータおよびアプリケーションのインタラクティブなバスを備えています。以下の記事でその使い方をご覧ください。

名称概要URL
EnsembleからTelegramでアラートの送信テレグラムにデータを送信するためのプロダクションhttps://community.intersystems.com/post/sending-alerts-ensemble-telegram
[初めてのInterSystems IRIS] インターオペラビリティを使ってみようビジネスサービス、オペレーション、プロセス、プロダクションの作成https://jp.community.intersystems.com/node/483021
Embedded PythonによるInterSystems IRISのインターオペラビリティーPythonによるビジネスサービス、オペレーション、プロセス、プロダクションの作成https://jp.community.intersystems.com/node/518846
Ensemble / Interoperabilityトレーニングコースプロダクションの作り方を学ぶのに最適なサンプルhttps://community.intersystems.com/post/ensemble-interoperability-training-course
プログラムによるインターオペラビリティーのサンプルPythonまたはObjectScriptを使用したプログラムによるプロダクションhttps://jp.community.intersystems.com/node/521511
フォルダ内のファイルのリスティングフォルダー内のファイルをリスト化するhttps://community.intersystems.com/post/listing-files-folder
.Net/Java Gatewayのコンテナ化(またはKafka統合のデモ)Javaまたは.Net Native APIを使用したKafkaサポートhttps://jp.community.intersystems.com/node/542191
PEXを使用した.NETまたはJavaでのIRIS統合の実装PEXによるJavaまたは.Netを使ったプロダクションの作成https://community.intersystems.com/post/implementing-iris-integrations-net-or-java-using-pex
Java Business HostからPEXへの移行PEXの使用https://jp.community.intersystems.com/node/486231
Tesseract OCRとJava Gatewayの使用についてJava PEXの使用https://community.intersystems.com/post/using-tesseract-ocr-and-java-gateway
PEXのビジネスオペレーションを作成についてCreate Java PEX Business Operationhttps://community.intersystems.com/post/creating-pex-business-operation
OCRとNLPを統合したInterSystems IRISJava PEX のサンプルhttps://community.intersystems.com/post/ocr-and-nlp-together-intersystems-iris
HTTP Adapterを使用したカスタムインターオペラビリティビジネスサービスの作成ビジネスサービスの作成https://community.intersystems.com/post/creating-custom-interoperability-business-service-using-http-adapter

Native API

IRISは、市場で最も使用されているプログラミング言語(Java、Javascript/NodeJS、.Net、C++、Python)を使用することに前向きです。これを実現するために、これらの言語ごとにNative APIを使用しています。以下の記事をご覧ください。

名称概要URL
Docker Micro ServerとしてIRIS Native APIを使用したWebSocket Client JSIRISとNodeJSを使ってWebSocketを行うhttps://jp.community.intersystems.com/node/507846
ObjectScript用IRIS Native APINative APIの使用https://community.intersystems.com/post/iris-native-api-objectscript
Node.jsでのZPMの使用Node.jsプロジェクトでのZPMの使用https://jp.community.intersystems.com/node/507866
テキストファイルからPDFファイルの作成PDFファイルの生成用Java Native APIhttps://community.intersystems.com/post/creating-pdf-text-file
InterSystems IRISを使った開発を1分以内に始める方法IRISを使った開発の開始https://community.intersystems.com/post/how-start-development-intersystems-iris-less-minute
Python + IRIS Globals を使ったブログの作成Python Native API用ブログhttps://jp.community.intersystems.com/node/501856
0
2 245
記事 Tomoko Furuzono · 4月 11, 2023 2m read

これは、InterSystems FAQサイトの記事です。
%Library.GlobalクラスのExport()メソッドを使用してエクスポートする際に、エクスポート形式(第4引数:OutputFormat)を 7 の「ブロックフォーマット/Cachéブロックフォーマット(%GOF)」にした場合、マッピングされたグローバルはエクスポートできない仕様となっています(対象はネームスペースのデフォルトグローバルデータベースのグローバルのみ)。
マッピングされたグローバルを「ブロックフォーマット/Cachéブロックフォーマット(%GOF)」でエクスポートする為には、%Library.Global.Export()の第1パラメータにマッピング先のデータベースディレクトリを指定します。
実行例は以下の通りです。 

 set DB = "^^c:\InterSystems\Cache\Mgr\Test\"  ; "^^\<データベースフォルダーのパス>\"
 set sc = ##class(%Library.Global).Export(DB,"TESTGBL.gbl",FULLPATH,7,,"")
0
0 210
記事 Mihoko Iijima · 4月 4, 2023 7m read

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

Python Native APIを利用すると、IRISにあるグローバル変数の参照/更新をPythonから行えたり、メソッドやルーチンをPythonから実行することができます。

この記事では「AWS Lambda の IRIS Python Native API IRIS」の記事を参考に、NativeAPIを利用してPythonからIRISに接続するAWS Lambda関数を作成する流れで必要となる、レイヤー作成と関数用コードの作成例をご紹介します。

※ 事前にAWSのEC2インスタンス(Ubuntu 20.04を選択)にIRISをインストールした環境を用意した状態からの例でご紹介します。

0
0 388
記事 Toshihiko Minamoto · 10月 25, 2022 8m read

私が一番興味を持っているのは、組み込み Python におけるグローバルの使用についてです。
そこで、提供されている公式ドキュメントを確認しました。

#1 グローバルの導入
グローバルとは何かについての一般的な説明。 次の章につながっています。

#2 ObjectScript の詳細について
組み込み Python の記述はありません。
さらに先に進むと...

#3 組み込み Python

3.1 組み込み Python の概要
3.1.1 グローバルの使用

グローバルを使ったことなければ、素晴らしい内容です。
が、驚くほど原始的な例が使われています。
3.2 組み込み Python の使用
最後の望み: >>> でも、目に見えるものが何もありません
残念どころではありません! Python 用の IRIS Native API でさえ、もっと説明されています。
何を期待していたかと言うと...

グローバルノードの SET、GET、KILL

Native API: 基本的なノード操作  そして

$DATA()、$ORDER()、$QUERY() によるナビゲーション

Native API: nextSubscript() と isDefined() によるイテレーション
そこで、自分で調査し、リバースエンジニアリングを行って、実験しなければなりませんでした。

そしてわかったこと:

すべての例は IRIS for Windows (x86-64) 2022.1 (Build 209U) にある Python Shell で説明されており、
暗黙的な print() 関数を集中的に使用している。

グローバル

何をするにも、iris.gref クラスを使って、グローバルの参照オブジェクトを作成することから始める必要があります。
グローバル名は、直接文字列として、またはCOS/ISOS の間接式のように変数として渡されます。
グローバルを扱っていることが明確であるため、最初のキャレット (^) は不要です!

>>> globalname='rcc'
   >>> nglob=iris.gref(globalname)
   >>> glob=iris.gref('rcc')
   >>> cglob=iris.gref('^rcc')
上記は、同じグローバルへの 3 つのグローバル参照です。
単なる参照であり、このグローバルが存在するかことを示すものではありません。
対話式ドキュメント:  print(glob.__doc__)
InterSystems IRIS グローバル参照オブジェクト。
グローバルへの参照を取得するには、iris.gref() メソッドを使用します。
 
サブスクリプト

すべてのグローバルサブスクリプトは、Py リストの [sub1,sub2] として渡されます。 COS/ISOS とあまり変わりません。
トップレベルに特別な処理が必要なだけです。
サブスクリプトなしを示すには、空のリストではなく、この [None] を使用します。

SET

グローバルを設定するには、COS/ISOS と同様に「直接」行うことができます。

>>> glob[1,1]=11

または、gref.set() メソッドを使用することもできます。

>>> glob.set([1,3],13)

対話式ドキュメント:  print(glob.set.__doc__)
グローバルのキーが指定されている場合、グローバルのそのキーに格納された値を設定します。  
例: g.set([i,j], 10) は、グローバル g のキー i,j にあるノードの値を 10 に設定します。

グローバルノードのコンテンツにアクセスするには、COS/ISOS と同様に「直接」行うことができます。

>>> glob[1,3]
   13

または、gref.get() メソッドを使用することもできます。

>>> glob.get([1,1])
   11

対話式ドキュメント: print(glob.get.__doc__)
グローバルのキーが指定されている場合、グローバルのそのノードに格納された値を返します。
例: x = g.get([i,j]) は x を、グローバル g のキー i,j に格納された値に設定します。

注意: これは COS/ISOS の $GET() ではありません

>>> glob.get([1,99])
   Traceback (most recent call last):
   File "", line 1, in &lt;module>
   KeyError: 'Global Undefined'
   >>>

ただし、直接使用すると、COS/ISOS の $GET() のように動作します。

>>> x=glob[1,99]
   >>> print(x)
   None
   >>>

この None は、SQL が表現するところの NULL を指します。 後で、もう一度出現します。

KILL

期待される結果を達成する gref.kill() メソッドのみがあります。

>>> glob.kill([1,3])
   >>> y=glob[1,3]
   >>> print(y)
   None
   >>>

対話式ドキュメント: print(glob.kill.__doc__)
グローバルのキーが指定されている場合、グローバルのそのノードとサブツリーをキルします。
例: g.kill([i,j]) は、グローバル g のキー i,j に格納されたノードとその子孫ノードをキルします。

$DATA()

関連するメソッドは gref.data() です。
対話式ドキュメント: print(glob.data.__doc__)
グローバルのキーが指定されている場合、その状態を返します。
例: x = g.data([i,j]) は x を 0、1、10、11 に設定します。
0-if が未定義、1-定義済み、10-未定義で子孫あり、11-値と子孫あり
期待どおりに動作します。

>>> glob.data()
   10
   >>> glob.data([None])
   10
   >>> glob[None]=9
   >>> glob.data([None])
   11
   >>> glob.data([1,1])
   1
   >>> glob.data([1,3])
   0
   >>>

$ORDER()

この例では、グローバル ^rcc にノードをいくつか追加しました。

>zw ^rcc
   ^rcc=9
   ^rcc(1,1)=11
   ^rcc(1,2)=12
   ^rcc(2,3,4)=234
   ^rcc(2,3,5)=235
   ^rcc(2,4,4)=244
   ^rcc(7)=7

関連するメソッドは gref.order() です。
対話式ドキュメント: print(glob.order.__doc__)
グローバルのキーが指定されている場合、そのグローバルの次のキーを返します。
例: j = g.order([i,j]) は j を、グローバル g の次の第 2 レベルのキーに設定します。
つまり、以下のようになります。

>>> print(glob.order([]))
   1
   >>> print(glob.order([1]))
   2
   >>> print(glob.order([2]))
   7
   >>> print(glob.order([7]))
   None
   >>> print(glob.order([1,'']))
   1
   >>> print(glob.order([1,1]))
   2
   >>> print(glob.order([2,3,]))
   4
   >>> print(glob.order([2,3,""]))
   4
   >>> print(glob.order([2,3,4]))   
   5
   >>> print(glob.order([2,4,4]))
   None
   >>>

ここでは、参照として欠落しているサブスクリプトまたは空の文字列は同等です。

$QUERY()

関連するメソッドは gref.query() です。
対話式ドキュメント: print(glob.query.__doc__)
指定されたキーからグローバルをトラバースし、各キーと値をタプルとして返します。
例: for (key, value) in g.query([i,j]) は、キー i,j から g をトラバースし、各キーと値を返します。

このメソッドの動作は COS/ISOS と異なります。

  • 開始ノード以降のすべてのノードを返します。
  • 格納されたコンテンツを含めます。
  • None と示されているコンテンツのない仮想ノードも返します。 ここでの小さな例は、次のようになります(読みやすいように囲んでいます)。
>>> print(list(glob.query()))
   [(['1'], None), (['1', '1'], 11), (['1', '2'], 12), (['2'], None), 
        (['2', '3'], None), (['2', '3', '4'], 234), (['2', '3', '5'], 235), 
        (['2', '4'], None), (['2', '4', '4'], 244), (['7'], 7)]
   >>>

もっと読みやすくすると、次のようになります。

>>> for (key, value) in glob.query():
   ...  print(key,''.ljust(20-len(str(list(key))),'>'),value)
   ...
   ['1'] >>>>>>>>>>>>>>> None
   ['1', '1'] >>>>>>>>>> 11
   ['1', '2'] >>>>>>>>>> 12
   ['2'] >>>>>>>>>>>>>>> None
   ['2', '3'] >>>>>>>>>> None
   ['2', '3', '4'] >>>>> 234
   ['2', '3', '5'] >>>>> 235
   ['2', '4'] >>>>>>>>>> None
   ['2', '4', '4'] >>>>> 244
   ['7'] >>>>>>>>>>>>>>> 7
   >>>

絶対に ZWRITE ではありません!

もう 1 つのオプションは、gref.keys() のみを使用してサブスクリプトを取得する方法です。
対話式ドキュメント: print(glob.keys.__doc__)
指定されたキーからグローバルをトラバースし、そのグローバルの各キーを返します。
例: for key in g.keys([i, j]) は、キー i,j から g をトラバースし、各キーを返します。 >>>

>>> list(glob.keys())
   [['1'], ['1', '1'], ['1', '2'], ['2'], ['2', '3'], ['2', '3', '4'], 
                 ['2', '3', '5'], ['2', '4'], ['2', '4', '4'], ['7']]
   >>>

そして、これで gref.orderiter() を見つけました。
対話式ドキュメント: print(glob.orderiter.__doc__)
指定されたキーからグローバルをトラバースし、次のキーと値をタプルとして返します。
例: for (key, value) in g.orderiter([i,j]) は、キー i,j から g をトラバースし、次のキーと値を返します。

$QUERY() のようにコンテンツをフェッチして、
そのコンテンツを次のサブノードに提供することも行う $ORDER() のように動作します。
以下を見てください。

>>> list(glob.orderiter([]))
   [(['1'], None), (['1', '1'], 11)]
   >>> list(glob.orderiter([1]))
   [(['2'], None), (['2', '3'], None), (['2', '3', '4'], 234)]
   >>> list(glob.orderiter([2]))
   [(['7'], 7)]
   >>>

最後に、gref.getAsBytes() メソッドがあります。
対話式ドキュメント: print(glob.getAsBytes.__doc__)
グローバルのキーが指定されている場合、そのグローバルのそのノードに格納されている文字列をバイトで返します。
例: x = g.getAsBytes([i,j]) は x をグローバル g のキー i,j に格納されている値をバイトとして設定します。

数値には失敗しますが、 文字列には有効です。

   >>> glob[5]="robert"
   >>> glob.get([5])
   'robert'
   >>> glob.getAsBytes([5])
   b'robert'

また、COS/ISOS で set ^rcc(9)=$lB(99,"robert") を実行すると
以下のようになります。

>>> glob[9]
   '\x03\x04c\x08\x01robert'
   >>> glob.getAsBytes([9])
   b'\x03\x04c\x08\x01robert'
   >>>

これらのメソッドを以下のようにして検出しました。

>>> for meth in glob.__dir__():
   ...  meth
   ...
   '__len__'
   '__getitem__'
   '__setitem__'
   '__delitem__'
   '__new__'
   'data'
   'get'
   'set'
   'kill'
   'getAsBytes'
   'order'
   'query'
   'orderiter'
   'keys'
   '__doc__'
   '__repr__'
   '__hash__'
   '__str__'
   '__getattribute__'
   '__setattr__'
   '__delattr__'
   '__lt__'
   '__le__'
   '__eq__'
   '__ne__'
   '__gt__'
   '__ge__'
   '__init__'
   '__reduce_ex__'
   '__reduce__'
   '__subclasshook__'
   '__init_subclass__'
   '__format__'
   '__sizeof__'
   '__dir__'
   '__class__'
   >>>

これで、組み込み Python からグローバルに直接アクセスする必要がある場合に、作業が楽になることを願っています。
個人的に学んだこと: ほとんどの場合に、ドキュメントがあります。 . . . どこにあるかは別ですが。
ただ、探し回る必要があるだけです。

動画デモ

Traduction française

0
1 305
記事 Mihoko Iijima · 6月 28, 2022 5m read

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

この記事では、Embedded Pythonをご自身の好きなタイミングで学習できる📚セルフラーニングビデオ📚の YouTube プレイリストをご紹介します!

👆こんな具合に👆学習内容別 Embedded Python セルフラーニングビデオを公開しています!

この記事では、これから Embedded Python でプログラミングを開始してみたい方向けに最適なビデオをご紹介します!
​​​​​​

◆ Embedded Python概要から学習を始めたい方はこちら👇

以下の内容を確認できるプレイリスト:1-Embedded Python概要編 - YouTube をご用意しています。

  • Embedded Pythonとは?
  • Python開発者から見た使い道(解説&実演)
  • IRIS開発者からみた使い道(解説&実演)

この後、実際の操作を試されたい場合は、次のプレイリスト:2-Embedded Python利用前の準備 - YouTube が最適です。

◆ Embedded Python利用前の準備 を知りたい方はこちら👇

操作を開始する前に、必要な利用前の準備についてご紹介しているプレイリスト:2-Embedded Python利用前の準備 - YouTube をご用意しています。

0
0 803
記事 Mihoko Iijima · 4月 13, 2022 11m read

  

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

この記事では、InterSystems Global コンテストで見事優勝🏆された @Yuri Marx さんの作品をご紹介します!(ご本人が投稿された記事を使ってご紹介しますlaugh

InterSystems IRIS の「Global」はデータベースに格納される変数で、キーバリュー形式でアクセスできます。キーが複数ある場合は、配列の添え字を利用して階層のようなイメージで格納することもできます。

ここでご紹介する優勝作品は、配列をうまく利用した作品で、MindMapで書いた情報そのまま(見たまま)をグローバルに登録しています。

MindMapの表示部分については、オープンソースの Mind-elixir を使用されているようです。

ソースコードは OpenExchange で公開中で、git clone 後、 docker-compose up -d --build したらすぐ動きます!(docker 🐳ほんとに便利ですね!)

また、ご本人の解説ビデオも YouTube で絶賛公開中です!お手元で動かさなくてもどんな感じで Global が使われているか、どんな感じで Web アプリを作成されているのかを確認できますので、ぜひご覧ください!

Global の登録内容は 2:26秒辺りから、フロントエンドの解説は 3:11 辺りから

0
0 153
お知らせ Mihoko Iijima · 3月 11, 2022

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

次のコンテストへの参加準備はよろしいですか? 19 回目の InterSystems オンラインプログラミングコンテストのテーマは・・・

🏆 InterSystems Global Contest 🏆 です!

(もう 18 回も開催してたんですね。びっくり ( ゚Д゚) いたしました)

応募期間は 2022年3月21日~4月3日 です。 

💰 賞金総額: $10K 💰


0
0 193
記事 Megumi Kakechi · 3月 3, 2022 3m read

これは、InterSystems FAQサイトの記事です。
ある処理において、データを無期限に保存する必要がなくグローバルの強力な性能が必要になる場合に、IRISTEMP/CACHETEMP データベースに保存される一時グローバルが使用されます。
IRISTEMP/CACHETEMPデータベースはジャーナルされないので、一時グローバルの使用ではジャーナルファイルは作成されません。

IRISTEMP/CACHETEMP データベースは、システムで一時ストレージ用に使用され、ユーザも同じ用途で使用することができます。

一時グローバルとIRISTEMPデータベースの詳細については、以下のドキュメントをご覧ください。
一時グローバルと IRISTEMP データベース


一時グローバルとして使用されるグローバルには以下のようなものがあります。

0
1 444
記事 Toshihiko Minamoto · 2月 1, 2022 5m read
これはIRIS 2020.2で動作するコーディングの例です 
最新バージョンとは同期していません。
また、InterSystemsのサポートによるサービスはありません

動作中のデモを確認できるデモビデオを以下で公開しています。https://youtu.be/dSV-0RJ5Olg

皆さんこんにちは
完全に新しいIRISイメージと**たった4行**のDockerコマンドを使って実行するイメージを使ってマイクロサービスのデモを行いましょう。 
2020年6月1日 - rcc

すべてのパーツを1つのコンテナイメージにまとめたコンパクトなオールインワンバージョンが公開されました。
詳細はこちら: IRIS-NativeAPI-Nodejs-compact
2020年5月24日 - rcc

Dockerを使った簡易インストールを追加しました。コンテキストを参照
2020年5月25日 - rcc

Linux & Windowsに最適な検証済みの強化スクリプトはこちら
https://github.com/rcemper/WSockClientMicroSV/blob/master/READMEwindows.MD
2020年5月26日 - rcc

このデモは、Caché用にすでに存在するNode.jsに基づくWebSocketクライアントを再設計したものです。 主に以下のような変更点があります。

  • 新しいIRIS Native API for Node.jsの使用。特にグローバル配列を操作する場合
  • 直接トリガーされたクライアントからサーバー設計への変更
  • マイクロサービス/マイクロサーバーの例として、結果を別のdockerイメージに配置
  • マイクロサービスの実行を制御するための単純なインターフェースをIRISに追加

ユーティリティをIRISホストで直接呼び出す代わりに、System Interoperability(別名 ENSEMBLE)で通常行うようにして、work-packagesをMicroServiceに送信します。
もちろん、2つ以上のWebSocketサーバーオプションがあります。
WebSocketクライアントサービスがジョブを完了したら、その結果を取得します。

組み込みのWebSocketクライアントに対するメリットは、すべてのネットワーク、セキュリティ、ファイアウォールの問題を主要なデータサーバーに寄せ付けないことです。 Node.js がこの分野で持っている経験と品質については言うまでもありません。

デモは wss: デフォルトのechoサーバーとして使用します。
次に、テキストを数行入力します。
独自のテキストの任意の場所に「Lorem Ipsum」を追加して、コンテンツを長くすると良いでしょう。
そして、それをサービスに送信して、エコーを待ちます。
また、制御プログラムをExitまたはサービスをStopする前に、
テキストを変更するオプションもあります。

この処理はすべて非同期で実行されます。
完了を待たずに、それまでにechoサーバーから受信した内容を、リスナーが
定期的に表示します。

インストールするには、以下が必要です。

  • IRISのdocker イメージ(intersystems/iris-community:2020.2.0.199.0)

  • WebSocketマイクロサーバーのdockerイメージ(docker pull rcemper/rcc:demoJS

  • 簡略化されたinit: initに次のdockerコマンドを実行してください。 docker run --name ini1 --init -it --rm \ -privileged -v $(pwd):/external \ rcemper/rcc:demoJS bash /rcc/init.sh ### 元のアプローチは有効ではありますが、必須ではありません。 ###

  • IRIS-Docker-micro-Durabilityを利用するためにOpen Exchangeまたはここから入手したWSockClientMicroSV.tar.gz

  • demoディレクトリの確認: Dockerイメージはnobodyであるため、保護をrwx (chmod 777) に設定します。###

これを実行するには、まずdemoディレクトリからIRISを起動します(-dまたは-itを実行して動作を観察します)。(!)

docker run --name iris1 --init --rm -d \
-p 52773:52773 -p 51773:51773 \
-v $(pwd):/external \
intersystems/iris-community:2020.2.0.199.0 \
-b /external/pre.copy

次にMicroServerを起動します。

docker run --name rcc1 --init -it --rm \
rcemper/rcc:demoJS /usr/bin/node /rcc/nodejs/WSockIris.js $(hostname -I)

. -itを使って起動したため、以下が表示されます。

platform = linux: ubuntu  

    *****************************  
    Connect to IRIS on: 192.168.0.23  
Successfully connected to InterSystems IRIS.  
    *** wait 3sec for request ***  
    ******* Startup done ********  

    *** wait 3sec for request ***  
    *** wait 3sec for request ***  

次に、新しいLinuxターミナルで制御アプリケーションを実行します。

docker exec -it iris1 iris session iris ZSocket  

以下が表示されます。

*** Welcome to WebSocket Micoservice demo ***  
Known Hosts (*=Exit) [1]:  
1  wss://echo.websocket.org/  
2  --- server 2 ----  
3  --- server 3 ----  
select (1):  ==> wss://echo.websocket.org/  
#
Enter text to get echoed from WebSocketClient Service
Terminate with * at first position
or get generated text by %
or append new text with @

1    hello socket microServer
2    now you got 2 lines
3    *

Select action for WebClient Service
New EchoServer (E), Send+Listen(S),New Text(N),Exit(X), Exit+Stop Client(Z) [S]s
%%%%%%%%%%%%%%%%%%%%%%%%%%

******* 0 Replies *******

******* 2 Replies *******
1    hello socket microServer
2    now you got 2 lines


Select action for WebClient Service

マイクロサービスでは以下が表示されます。

*** wait 3sec for request ***
echoserver:  wss://echo.websocket.org/
** Lines to process: 1 **
********* next turn *********
* WebSocket Client connected *
****** Client is ready ****** 

Line: 1 text> 'hello socket microServer '
Received: 1 > 'hello socket microServer '

Line: 2 text> 'now you got 2 lines '
Received: 2 > 'now you got 2 lines '

******* lines sent: 2 ******
*** replies received: 2 ****

*** wait 3sec for request ***

事後警告。 イメージのバージョンを必ず確認してください!
私はntersystems/iris-community:2020.2.0.
204にハマってしまいました。

注意: すべてのスクリプトはLinuxでのみテストされています!

0
0 313
記事 Toshihiko Minamoto · 12月 14, 2021 5m read

着想: @Evgeny Shvarovとその記事より
Deploying InterSystems IRIS Embedded Python Solutions with ZPM Package Manager
このアイデアを発展させ、同じことを**Node.js.**のモジュールで行ってみました。
このケースは、私の「IRIS Native API for Node.js」の例に基づいています。

InterSystems IRIS はクライアントとしてネイティブでWebSocketsをサポートしているというわかりきった返答を期待して:
その通りです。そして、私がその昔書いた関連記事OEXのサンプルへのリンクはこちらです。

ZPMで適用される原理は、Pythonの場合と似ており、完全に機能します。

変更内容

  • その他すべての必須Node.jsコンポーネントは、ランタイム時にインストールできるようになりました。   
  • Dockerビルド中には、_intersystems-iris-native_モジュールのみがプリインストールされます。    
  • Node.jsが起動すると、デバッグ目的でそのプロセスIDが表示されます。  
  • 一部の視覚的な外観とランタイムが改善されました。  

Node.jsでは、追加の必須コンポーネントをインストールする際に、npm
使用して、十分な権限でインストールしなければならないという課題に直面します。  

このステップとアクセス権の調整は、Dockerfileでカバーされています。  
しかし、すべての .jsモジュールを処理するのは、ZPMです。

この例のロジックは変更されていません。

  • Nodes.jsサービスが開始される
  • 選択したエコーサーバーのアドレスがそれに渡される > E
  • 送信されるテキストを作成する > N
  • それを送信して、返信がどのようにドロップされるかを確認する > S
  • サービスを停止して終了する > X

元の例とは異なり、サービスはバックグラウンドで実行しています。
従って、その出力は見えませんが、アクションによって表示できるログファイルに書き込まれます > L

前提条件

gitDocker desktopがインストールされていることを確認してください。

インストール

このリポジトリを任意のローカルディレクトリにClone/git pullします。

$ git clone https://github.com/rcemper/Using-ZPM-for-Node.js

このディレクトリでターミナルを開き、以下を実行します。

$ docker-compose build

これが完了するまでしばらく時間が掛かることがあります。

このプロジェクトで IRIS コンテナを実行します。

$ docker-compose up -d

テスト方法

IRIS ターミナルを使用します。

$ docker-compose exec iris iris session iris "##class(rccjs.WSockNodeJs).Run()"

*** Welcome to WebSocket by Node.js Native API Demo ***    

********* Node.js process id = 1650  *********   

Known Hosts (*=Exit) [1]:
1  ws://echo.websocket.org/
2  --- server 2 ----
3  --- server 3 ----
select (1): 1 ==> ws://echo.websocket.org/
#
Enter text to get echoed from WebSocketClient Service
Terminate with * at first position
or get generated text by %
or append new text with @

1    hello this is connected over
2    IRIS Native API for Node.js
3    -----------------
4    *

Select action for WebClient Service
New EchoServer (E), New Text(N), Send+Listen(S)
Show Log (L), Exit+Stop WsClient(X) [S] :s
%%%%%%%%%%%%%%%%%%%%%%%%%%

******* 3 Replies *******
1    hello this is connecte over 
2    IRIS Native API for Node.js 
3    ----------------- 

Select action for WebClient Service
New EchoServer (E), New Text(N), Send+Listen(S)
Show Log (L), Exit+Stop WsClient(X) [S] &lt;strong>:&lt;span style="font-size:16px;">L&lt;/span>&lt;/strong>
%%%%%%%%%%%%%%%%%%%%%%%%%%

platform = linux: ubuntu

        *****************************
        *** no IRIS host defined ****
        Connect to IRIS on: localhost
Successfully connected to InterSystems IRIS.
        *** wait 3sec for request ***
        ******* Startup done ********

        *** wait 3sec for request ***
        *** wait 3sec for request ***
        *** wait 3sec for request ***
        *** wait 3sec for request ***
        echoserver:  ws://echo.websocket.org/
        ** Lines to process: 3 **
        ********* next turn *********

        * WebSocket Client connected *
        ****** Client is ready ******
Line: 1 text> 'hello this is connecte over '
Received: 1 > 'hello this is connecte over '
Line: 2 text> 'IRIS Native API for Node.js '
Received: 2 > 'IRIS Native API for Node.js '
Line: 3 text> '----------------- '
Received: 3 > '----------------- '

        ******* lines sent: 3 ******
        *** replies received: 3 ****

        *** wait 3sec for request ***
        *** wait 3sec for request ***

Select action for WebClient Service
New EchoServer (E), New Text(N), Send+Listen(S)
Show Log (L), Exit+Stop WsClient(X) [S] :x
%%%%%%%%%%%%%%%%%%%%%%%%%%
0
0 154
記事 Tomoko Furuzono · 12月 13, 2021 1m read

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

グローバル単位でジャーナルのON/OFF設定を行いたい場合は、グローバルマッピング設定で、
 ・ジャーナルしたいグローバル ⇒ ジャーナルON(「はい」)のデータベースにマッピング
 ・ジャーナルしたくないグローバル ⇒ ジャーナルOFF(「いいえ」)のデータベースにマッピング
と、分けることで可能です。 但し、通常のデータベースは、ジャーナルOFF設定であっても、トランザクション中の更新についてはジャーナルファイルに記録されます。

一時グローバルを保存するIRISTEMP/CACHETEMPデータベースは、トランザクション中でもジャーナルファイルに記録されませんので、トランザクションデータもジャーナル記録したくない場合にはこれをマッピング先にすることも可能です。
但し、IRISTEMP/CACHETEMPデータベースは一時データベースであるため、インスタンス再起動でグローバルデータは失われますので注意が必要です。
一時グローバルと IRISTEMP データベース

0
0 188
記事 Toshihiko Minamoto · 10月 20, 2021 4m read

インターネットを使うようになってから (1990年代後半)、PythonとIRIS グローバルを使ってブログを書いていますが、常にCMS (コンテンツ管理システム) でブログ、ソーシャルメディア、さらには企業ページに情報を簡単に投稿できるようにしていました。 数年後、自分がマークダウンファイルに収めて使ってきたすべてのコードをgithubに入れました。 ネイティブAPIでデータをIntersystems IRISに入れて永続化するのはとても簡単だったので、このアプリケーションを作成して少しSQLを忘れ、キー・バリュー・データベースモデルを受け入れることにしました! picture

ブログとは?

これはWEB LOGの略名で、基本的にはユーザーが書いてページに投稿、公開できるプラットフォームです。

マークダウンフォーマットとは?

マークダウンとは、フォーマットしたテキストを作成するマークアップ言語であり、HTMLより簡単で、多くのCMSプラットフォームで人気があります。

例:

# Header 1
## Header 2
### Header 3

結果:

Header 1

Header 2

Header 3


IRISで作成するメリットとは?

IRIS Globalsを使用して各投稿のデータを永続化しながら、各データコンサルタントはキー・バリュー・データベースとして動作するIRISのスピードというメリットを得ます。すでに作業でIris Instanceを使用している場合は、このようなアプリケーションを作成するために別の技術を使用する必要はありません。

そしてPythonとは?

Pythonは最も人気の高いプログラミング言語の一つです。チューリング完全言語であり、開発課題のほとんどを楽にしてくれるオープンソースライブラリがたくさんあります。 私は、PythonとIrisを統合するために、IRISNative APIを使用しました。

データベースモデル キー値

私のエンジンは、これでもかというほどシンプルに動作します。 各投稿を永続化するために、サブスクリプトが「post」、その次のサブスクリプトが投稿IDのグローバル「^blog」を作成します。 このグローバルに投稿コンテンツを入れて、終わりです! これだけで、テーブル、インデックスなどを作成する必要はありません。

 ^blog("post", "1") = "# post 1 content..."
 ^blog("post", "2") = "# post 2..."
 ^blog("post", "3") = "# post 3 markdown content..." 

HTMLでマークダウンをレンダリングするには?

これで、オープンソースモジュール使用可能なPythonのメリットを無限に得るこquoとができます。私の選択は、たった1行のコマンドで簡単にマークダウンをレンダリングできるDashライブラリでした(^_^)

 import dash_core_components as dcc
 import dash
 import irisnative

 #creating a connection with an IRIS Instance
 conn = irisnative.createConnection("host","port","namespace","username","password") obj_iris = irisnative.createIris(conn)

 #getting the content of one post with id 1
 content = obj_iris.get("blog", "post", "1")

 #creating the dash application
 app = dash.Dash(__name__)

 #rendering a markdown value
 rendered_markdown_in_html = dcc.Markdown(content)

 #showing on the page the rendender markdown
 app.layout = html.Div([rendered_markdown_in_html])

すべての投稿を表示するには?

サブスクリプト「^blog("post",)」繰り返して、上記と同じ方法でレンダリングされたマークダウンをページにプリントできます。 データベースをモデル化し、これよりも速く簡単に動作するフォームを作ったことがありますか?回答はコメント欄に書いてください!

自分でビルドしたくないけど、動作する様子は見たい!

簡単です!ここをクリックして、御覧ください。http://iris-multimodel-suite.eastus.cloudapp.azure.com/blog-post (サイトは既に閉鎖されています。ご了承ください)

この記事とアプリケーションをお楽しみいただけましたか?

ここではマルチモデルコンテストでの私のアプリケーションの一部をご説明しています。よろしければ、私のアプリに投票してください。

0
0 234
記事 Toshihiko Minamoto · 9月 14, 2021 10m read

より産業向けのグローバルストレージスキーム

この連載の第1回では、リレーショナルデータベースにおけるEAV(Entity-Attribute-Value)モデルを取り上げ、テーブルにエンティティ、属性、および値を保存することのメリットとデメリットについて確認しました。 このアプローチには柔軟性という点でメリットがあるにもかかわらず、特にデータの論理構造と物理ストレージの基本的な不一致などによりさまざまな問題が引き起こされるという深刻なデメリットがあります。

こういった問題を解決するために、階層情報の保存向けに最適化されたグローバル変数を、EAVアプローチが通常処理するタスクに使用できるかどうかを確認することにしました。

パート1では、オンラインストア向けのカタログをテーブルを使って作成し、その後で1つのグローバル変数のみで作成しました。 それでは、複数のグローバル変数で同じ構造を実装してみることにしましょう。

最初のグローバル変数^catalogには、ディレクトリ構造を保存します。 2つ目のグローバル変数^goodには、店の商品を保存します。 ^indexグローバルには、店のインデックスを保存します。 プロパティは階層的なカタログに関連付けられているため、プロパティ用の個別のグローバル変数は作成しません。

このアプローチでは、エンティティごとに(プロパティを除く)、個別のグローバル変数を使用しているため、論理の観点では優れています。 グローバルカタログ構造は次のようになります。

Set ^сatalog(root_id, "Properties", "capacity", "name") = "Capacity, GB"
Set ^сatalog(root_id, "Properties", "capacity", "sort") = 1

Set ^сatalog(root_id, sub1_id, "Properties", "endurance", "name") = "Endurance, TBW"
Set ^сatalog(root_id, sub1_id, "Properties", "endurance", "sort") = 2

Set ^сatalog(root_id, sub1_id, "goods", id_good1) = 1
Set ^сatalog(root_id, sub1_id, "goods", id_good2) = 1

Set ^сatalog(root_id, sub2_id, "Properties", "avg_seek_time", "name") = "Rotate speed, ms"
Set ^сatalog(root_id, sub2_id, "Properties", "avg_seek_time", "sort") = 3

Set ^сatalog(root_id, sub2_id, "goods", id_good3) = 1
Set ^сatalog(root_id, sub2_id, "goods", id_good4) = 1

 

商品のグローバル変数は、次のようになります。

Set ^good(id_good, property1) = value1
Set ^good(id_good, property2) = value2
Set ^good(id_good, property3) = value3
Set ^good(id_good, "catalog") = catalog_id

 

もちろん、商品のあるすべてのカタログセクションで、必要なプロパティで並べ替えを行えるようにインデックスが必要となります。 インデックスグローバルは、次のような構造になります。

Set ^index(id_catalog, property1, id_good) = 1
; To quickly get the full path to concrete sub-catalog
Set ^index("path", id_catalog) = "^catalog(root_id, sub1_id)"

 

したがって、カタログのすべてのセクションで、リストを並べ替えることができます。 インデックスグローバルはオプションです。 カタログのこのセクションの商品数が多い場合にのみ役立ちます。

デモデータを操作するためのObjectScriptコード

では、データを操作するために、ObjectScriptを使用しましょう。 まず、特定の商品のプロパティを取得することから始めます。 特定の商品のIDがあり、そのプロパティを並べ替えの値で指定された順序で表示する必要があります。 そのためのコードは次のようになります。

get_sorted_properties(path, boolTable)
{
  ; remember all the properties in the temporary global
  While $QLENGTH(@path) > 0 {
    if ($DATA(@path("Properties"))) {
      set ln=""
      for {
        Set ln = $order(@path("Properties", ln))
        Quit: ln = ""

        IF boolTable & @path("Properties", ln, "table_view") = 1 {
          Set ^tmp(@path("Properties", ln, "sort"), ln) = @path("Properties", ln, "name")
        }
      ELSE {
        Set ^tmp(@path("Properties", ln, "sort"), ln) = @path("Properties", ln, "name")
      }
    }
  }
}

print_sorted_properties_of_good(id_good)
{
  Set id_catalog = ^good(id_good, "catalog")
  Set path = ^index("path", id_catalog)

  Do get_sorted_properties(path, 0)

  set ln =""
  for {
   Set ln = $order(^tmp(ln))
   Quit: ln = ""
   Set fn = ""
   for {
    Set fn = $order(^tmp(ln, fn))
    Quit: fn = ""
    Write ^tmp(ln, fn), " ", ^good(id_good, fn),!
   }
  }
}

 

次に、id_catalogに基づいて、カタログセクションの商品を表形式で取得します。

print_goods_table_of_catalog(id_catalog)
{ 
  Set path = ^index("path", id_catalog)
  Do get_sorted_properties(path, 1)

  set id=""
  for {
    Set id = $order(@path("goods"), id)
    Quit: id = ""

    Write id," ", ^good(id, "price"), " "

    set ln =""
    for {
      Set ln = $order(^tmp(ln))
      Quit: ln = ""
      Set fn = ""
      for {
        Set fn = $order(^tmp(ln, fn))
        Quit: fn = ""
        Write ^tmp(ln, fn), " ", ^good(id, fn)
      }
      Write !
    }
  }
}

 

可読性: EAV SQLとグローバル変数

では、EAVとSQLの使用をグローバル変数の使用と比較してみましょう。 コードの明確さについては、これが主観的なパラメーターであることは明らかです。 しかし、例として新しい商品の作成方法を見てみましょう。

SQLを使用したEAVアプローチから確認します。 まず、オブジェクトのプロパティリストを取得する必要があります。 これは別のタスクであり、非常に時間がかかります。 capacityweight、およびenduranceという3つのプロパティのIDがすでに分かっているとします。

START TRANSACTION
INSERT INTO good (name, price, item_count, catalog_id) VALUES ('F320 3.2TB AIC SSD', 700, 10, 15);

SET @last_id = LAST_INSERT_ID ();

INSERT INTO NumberValues ​​Values​​(@last_id, @id_capacity, 3200);
INSERT INTO NumberValues ​​Values​​(@last_id, @id_weight, 0.4);
INSERT INTO NumberValues ​​Values​​(@last_id, @id_endurance, 29000);
COMMIT

 

この例ではプロパティが3つしかないため、例にはそれほど圧倒されません。 一般的なケースでは、トランザクション内のテキストテーブルにいくつかの挿入があります。

INSERT INTO TextValues ​​Values​​(@last_id, @ id_text_prop1, 'Text value of property 1');
INSERT INTO TextValues ​​Values​​(@last_id, @ id_text_prop2, 'Text value of property 2');
...
INSERT INTO TextValues Values (@last_id, @id_text_propN, 'Text value of property N');

 

もちろん、数値の代わりに「capacity」を使うというように、IDプロパティの代わりにテキスト表記を使用すれば、SQLバージョンをもう少し簡略することも可能ですが、 SQLの世界では、これは受け入れられません。 エンティティのインスタンスを列挙するには、数値IDを使用するのが慣例です。 このため、インデックス処理が高速化し(インデックス処理のバイトが少なくなるため)、一意性を追跡しやすくなり、新しいIDを自動的に作成しやすくなります。 この場合、挿入フラグメントは次のようになります。

INSERT INTO NumberValues ​​Values​​(@last_id, 'capacity', 3200);
INSERT INTO NumberValues ​​Values​​(@last_id, 'weight', 0.4);
INSERT INTO NumberValues ​​Values​​(@last_id, 'endurance', 29000);

 

次は、同じ例をグローバル変数を使用した場合のコードです。

TSTART
Set ^good(id, "name") = "F320 3.2TB AIC SSD"
Set ^("price") = 700, ^("item_count") = 10, ^("reserved_count") = 0, ^("catalog") = id_catalog
Set ^("capacity") = 3200, ^("weight") = 0.4, ^("endurance") = 29000
TCOMMIT

 

では、EAVアプローチで商品を削除してみましょう。

START TRANSACTION
DELETE FROM good WHERE id = @ good_id;
DELETE FROM NumberValues ​​WHERE good_id = @ good_id;
DELETE FROM TextValues ​​WHERE good_id = @ good_id;
COMMIT

 

そして、グローバル変数でも同じことを行います。

Kill ^good(id_good)

2つのアプローチをコードの長さの観点から比較することもできます。 上記の例からわかるように、グローバル変数を使用した方が、コードは短くなります。 これはメリットです。 コードが短くなるほど、エラーの数も減り、コードを理解して管理するのも容易になります。

一般に、コードが短いほど処理が高速化します。 そして、この場合には、グローバル変数はリレーショナルテーブルよりも低位データ構造であるため、確かにそのとおりです。

EAVとグローバル変数におけるデータのスケーリング

次に、水平方向のスケーリングを見てみましょう。 EAVアプローチでは、少なくとも3つの最も大きなテーブル(Good、NumberValues、TextValues)を複数のサーバーに分散する必要があります。 エンティティと属性のあるテーブルにはほとんど情報がないため、これらのテーブルは単純にすべてのサーバーに丸ごとコピーすることができます。

各サーバーでは、水平方向のスケーリングにより、さまざまな商品がGood、NumberValues、およびTextValuesテーブルに保存されます。 異なる商品でIDが重複しないように、各サーバーの商品に対して特定のIDブロックを割り当てる必要があります。

グローバルを使って水平方向のスケーリングを行う場合、グローバルでID範囲を構成し、グローバル範囲を各サーバーに割り当てる必要があります。

複雑さは、EAVとグローバルであまり変わりませんが、EAVアプローチの場合は、3つのテーブルにID範囲を構成しなければなりません。 グローバルの場合は、1つのグローバル変数のみにIDを構成するだけで済みます。 つまり、水平方向のスケーリングを調整するには、グローバル変数の方が簡単と言えます。

EAVとグローバル変数におけるデータ損失

最後に、データベースファイルの破損によるデータ損失のリスクを検討してみましょう。 5つのテーブルか3つのグローバル(インデックスグローバルを含む)のどちらにすべてのデータを保存する方が簡単でしょうか。

3つのグローバルの方が簡単だと思います。 EAVアプローチでは、さまざまな商品のデータがテーブルに混在しているのに対し、グローバルでは情報がより全体的に保存されています。 基盤のブランチは、保存されて順に並べ替えられています。 そのため、データがパスタが絡み合うように保存されるEAVアプローチに比べれば、グローバルの一部の破損によってダメージにつながる可能性は低くなります。

データ回復におけるもう1つの悩みの種は、情報の表示方法です。 EAVアプローチでは、情報は複数のテーブルに分割されているため、1つにまとめるには特別なスクリプトが必要です。 グローバルの場合は、ZWRITEコマンドを使用するだけで、ノードのすべての値と基盤のブランチを表示することができます。

InterSystems IRISのグローバル: より優れたアプローチ?

EAVアプローチは、階層データを保存するためのトリックとして出現しました。 テーブルは元々、ネストされたデータを保存するようには設計されてはいなかったため、 テーブルでグローバルをエミュレーションするのがEAVの事実上のアプローチです。 テーブルがグローバルよりも高位で低速のデータストレージ構造であることを考えると、EAVアプローチは、グローバルと比較した場合に失敗となります。

個人的な意見を言えば、階層データ構造の場合、グローバルの方がプログラミングの点でより利便性が高く理解しやすいと思います。また、より高速でもあります。

プロジェクトでEAVアプローチを計画している場合は、InterSystems IRISのグローバルを使用して階層データを保存することを検討するようお勧めします。

0
0 430
記事 Toshihiko Minamoto · 9月 9, 2021 10m read

はじめに

この連載の最初の記事では、リレーショナルデータベースのEAV(Entity–Attribute–Value)モデルを見て、それがどのように使用されて、何に役立つのかを確認しましょう。 その上で、EAVモデルの概念とグローバル変数と比較します。

原則として検索する必要のある、フィールド数、または階層的にネストされたフィールドの数が不明なオブジェクトがある場合があります。

たとえば、多様な商品群を扱うオンラインストアを考えてみましょう。 商品群ごとに固有の一意のプロパティセットがあり、共通のプロパティもあります。 たとえば、SSDとHDDドライブには共通の「capacity」プロパティがありますが、SSDには「Endurance, TBW」、HDDには「average head positioning time」という一意のプロパティもあります。

場合によっては、同じ商品でも別のメーカーが製造した場合には、それぞれに一意のプロパティが存在します。

では、50種の商品群を販売するオンラインストアがあるとしましょう。 各商品群には、数値またはテキストの固有のプロパティが5つあります。

実際に使用するのは5個だけであっても、各商品に250個のプロパティがあるテーブルを作成するのであれば、ディスク容量の要件が大幅に増える(50倍!)だけでなく、有用性のない空のプロパティによってキャッシュが詰まってしまうため、データベースの速度特性が大幅に減少してしまいます。

さらに、それだけではありません。 固有のプロパティを持つ新しい商品群を追加するたびに、ALTER TABLEコマンドを使用してテーブルの構造を変更する必要があります。 大規模なテーブルであれば、この操作には数時間、さらには数日間かかる可能性もあり、ビジネスでは許容しかねます。

これを注意深く読んでいる方は「商品群ごとに異なるテーブルを用意しては?」と言うでしょう。 もちろんその通りではありますが、このアプローチを使用すると、大型ストアの場合には、数万個ものテーブルでデータベースを作成することになり、管理が困難になります。 さらに、サポートする必要のあるコードがますます複雑化してしまいます。

一方、新しい商品群を追加するときに、データベースの構造を変更する必要はありません。 新しい商品群向けの新しいテーブルを追加すればよいだけだからです。

いずれにせよユーザーは、ストア内の商品を簡単に検索できること、現在のプロパティを示す便利な表形式で商品を表示できること、そして商品を比較できることが必要です。

ご想像のとおり、商品群の5個のプロパティのみが必要であるにもかかわらず、商品テーブルにはさまざまなプロパティを示す250個のフィールドがあれば不便であるのと同様に、250個のフィールドを使った検索フォームは、ユーザーにとって非常に不便です。 これは商品の比較にも当てはまります。

マーケティングデータベースも別の有用な例と言えるでしょう。 それに格納されている人ごとに、絶えず追加、変更、または削除される可能性のある多数のプロパティが必要です(多くの場合はネストされています)。 過去にある商品を特定の数量で購入した、特定の商品群を購入した、何かに参加した、どこかで勤務した、親戚がいる、この都市に住む、特定の社会階級に属する、などのプロパティがあります。 フィールド数は数千個にもなり、変化も絶えないでしょう。 マーケターは常に、さまざまな顧客グループを区別して魅力的な特別オファーを提供する方法を考えています。

これらの問題を解決すると同時に、明確で確定的なデータベース構造を得るために、Entity-Attribute-Valueアプローチが編み出されました。

EAVアプローチ

EAVアプローチの本質は、エンティティ、属性、および属性値を個別に保存することにあります。 一般的に、EAVアプローチを説明するために、Entity、Attribute、およびValueという3つのテーブルのみが使用されます。

保存するデモデータの構造。

テーブルを使用したEAVアプローチの実装

5つ(最後の2つのテーブルを1つに統合することにした場合は4つ)のテーブルを使用したより複雑な例を考察してみましょう。

最初のテーブルはСatalogです。

CREATE TABLE Catalog (
id INT,
name VARCHAR (128),
parent INT
);

このテーブルは実際、EAVアプローチのエンティティに対応しています。 階層的な商品カタログのセクションを保存します。

2つ目のテーブルは ****Fieldです。

CREATE TABLE Field (
id INT,
name VARCHAR (128),
typeOf INT,
searchable INT,
catalog_id INT,
table_view INT,
sort INT
);

このテーブルでは、属性の名前、型、および属性が検索可能であるかどうかを指定します。 また、プロパティが属する商品を保持しているカタログのセクションも指定します。 catalog_id以下のカタログセクションにあるすべての商品には、このテーブルに保存されているさまざまなプロパティがある場合があります。

3つ目のテーブルはGoodです。 商品を、商品の価格、商品の合計数量、商品の予約数量、および商品名とともに保存するように設計されています。 厳密にはこのテーブルは必要ではありませんが、個人的には、商品用に別のテーブルを用意しておくと便利だと思います。

CREATE TABLE Good (
id INT,
name VARCHAR (128),
price FLOAT,
item_count INT,
reserved_count,
catalog_id INT
);

4つ目のテーブル(TextValues)と5つ目のテーブル(NumberValues)は、商品のテキストの値と数値属性を保存するように設計されており、構造も似ています。

CREATE TABLE TextValues ​​(
good_id INT,
field_id INT,
fValue TEXT
);

CREATE TABLE NumberValues ​​(
good_id INT,
field_id INT,
fValue INT
);

テキスト値と数値に個別のテーブルを使用する代わりに、次の構造で単一のCustomeValuesテーブルを使用することもできます。

CREATE TABLE CustomValues ​​(
good_id INT,
field_id INT,
text_value TEXT,
number_value INT
);

データ型ごとに個別に保存しておけば、速度が向上し、容量を節約できるため、私は別々に保存する方を好んでいます。

EAVアプローチを使用したデータへのアクセス

SQLを使用して、カタログ構造マッピングを表示してみましょう。

SELECT * FROM Catalog ORDER BY id;

これらの値からツリーを作成するには、個別のコードが必要となります。 PHPでは、次のようになります。

$stmt = $ pdo-> query ('SELECT * FROM Catalog ORDER BY id');
$aTree = [];
$idRoot = NULL;

while ($row = $ stmt->fetch())
{
    $aTree [$row ['id']] = ['name' => $ row ['name']];

    if (! $row['parent'])
      $idRoot = $row ['id'];
    else
      $aTree [$row['parent']] ['sub'] [] = $row['id'];
}

将来的には、ルートノードの $aTree[$ idRoot] から始めると、ツリーを簡単に描画できるようになります。

では、特定の商品のプロパティを取得しましょう。 

まず、この商品に固有のプロパティのリストを取得し、その後で、それらのプロパティとデータベースにあるプロパティを接続します。 実際には、示されるすべてのプロパティが入力されているわけではないため、LEFT JOINを使用する必要があります。

SELECT * FROM
(
SELECT g. *, F.name, f.type_of, val.fValue, f.sort FROM Good as g
INNER JOIN Field as f ON f.catalog_id = g.catalog_id
LEFT JOIN TextValues ​​as val ON tv.good = g.id AND f.id = val.field_id
WHERE g.id = $ nGood AND f.type_of = 'text'
UNION
SELECT g. *, F.name, f.type_of, val.fValue, f.sort FROM Good as g
INNER JOIN Field as f ON f.catalog_id = g.catalog_id
LEFT JOIN NumberValues ​​as val ON val.good = g.id AND f.id = val.field_id
WHERE g.id = $nGood AND f.type_of = 'number'
) t
ORDER BY t.sort;

数値とテキスト値の両方を保存するために1つのテーブルのみを使用すると、クエリを大幅に簡略化できます。

SELECT g. *, F.name, f.type_of, val.text_value, val.number_value, f.sort FROM Good as g
INNER JOIN Field as f ON f.catalog = g.catalog
LEFT JOIN CustomValues ​​as val ON tv.good = g.id AND f.id = val.field_id
WHERE g.id = $nGood
ORDER BY f.sort;

では、$nCatalogカタログセクションに含まれる商品を表形式で取得します。 まず、カタログのこのセクションのテーブルビューに反映する必要があるプロパティのリストを取得します。

SELECT f.id, f.name, f.type_of FROM Catalog as c
INNER JOIN Field as f ON f.catalog_id = c.id
WHERE c.id = $nCatalog AND f.table_view = 1
ORDER BY f.sort;

次に、テーブルを作成するクエリを構築します。 表形式ビューには、3つの追加プロパティ(Goodテーブルのプロパティのほかに)が必要だとします。 クエリを単純化するために、次を前提としています。

SELECT g.if, g.name, g.price,
            f1.fValue as f1_val,
            f2.fValue as f2_val,
            f3.fValue as f3_val,
FROM Good
LEFT JOIN TextValue as f1 ON f1.good_id = g.id
LEFT JOIN NumberValue as f2 ON f2.good_id = g.id
LEFT JOIN NumberValue as f3 ON f3.good_id = g.id
WHERE g.catalog_id = $nCatalog;

EAVアプローチの長所と短所

EAVアプローチは明らかに柔軟性のメリットがあります。 テーブルなどの固定されたデータ構造を使用すると、オブジェクトの広範なプロパティセットを保存することが可能になります。 また、データベースのスキーマを変更せずに、別のデータ構造を保存することができます。 

また、非常に多くの開発者に馴染みのあるSQLも使用することができます。 

最も明白なデメリットは、データの論理構造と物理ストレージの不一致であり、これによって様々な問題が引き起こされます。 

さらに、プログラミングには、非常に複雑なSQLクエリが伴うこともよくあります。 EAVデータの表示には標準的に使用されていないツールの作成が必要となるため、デバッグが困難になることがあります。 また、LEFT JOINクエリを使用する必要がある場合があるため、データベースの速度が低下してしまいます。

グローバル変数: EAVの代替

私はSQLの世界とグローバル変数の世界の両方に精通しているため、EAVアプローチが解決するタスクにグローバルを使用する方がはるかに魅力的になるのではないかと考えました。

グローバル変数はまばらで階層的な情報を保存できるデータ構造です。 グローバル変数は階層情報を保存するために慎重に最適化されているというのが非常に重要なポイントです。 グローバル変数自体はテーブルよりも低レベルの構造であるため、テーブルよりもはるかに素早く動作します。

同時に、グローバル構造自体をデータ構造に従って選択できるため、コードを非常に単純で明確にすることができます。

デモデータを保存するためのグローバル構造

グローバル変数はデータを保存する上で非常に柔軟でエレガントな構造であるため、1つのグローバル変数を管理するだけでカタログセクション、プロパティ、および商品などのデータを保存することができます。

グローバル構造がデータ構造にどれほど似ているのかに注目してください。 このコンプライアンスによって、コーディングとデバッグが大幅に簡略化されます。

実際には、全ての情報を1つのグローバルに保存したい気持ちが非常に強くても、複数のグローバルを使用することをお勧めします。 インデックス用に別のグローバルを作成することが合理的です。 また、ディレクトリのパーティション構造のストレージを商品から分離することもできます。

この続きは?

この連載の2つ目の記事では、EAVモデルに従う代わりに、InterSystems Irisのグローバルにデータを保存する方法の詳細とメリットについて説明します。

0
0 2052
記事 Mihoko Iijima · 5月 27, 2021 2m read

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

HL7 メッセージの送受信を行うプロダクションでは、以下3個のグローバルが非常に大きくなることがあります。

グローバルの大きさを確認する場合は、^%GSIZEユーティリティを利用します。詳細は関連トピック/記事をご参照ください。

^EnsHL7.Segment
^EnsLib.H.MessageD
^EnsLib.H.MessageI

HL7メッセージは EnsLib.HL7.Message.cls で定義されます。
^EnsLib.H.MessageD はデータを保存するグローバル、^EnsLib.H.MessageI はインデックスを保存するグローバルです。

また、HL7メッセージは多数のセグメントで構成されており、メッセージデータを含むそれらのセグメントは ^EnsHL7.Segment に保存されます。

^EnsLib.H.MessageD グローバルの値は、^EnsHL7.Segment グローバルの添え字にリンクしています。

これらメッセージ関連のグローバルの削除については、任意の保持日数を指定して手動および定期的に削除することができます。
パージ(Purge)用ユーティリティについては、以下のドキュメントをご参照ください。

プロダクション・データのパージ【IRIS】

プロダクション・データのパージ

関連トピック/記事

0
0 191
記事 Mihoko Iijima · 3月 26, 2021 1m read

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

管理ポータルを使用して確認する場合は、以下の手順で参照できます。

管理ポータル > [システムエクスプローラ] > [グローバル] の画面で、ネームスペースではなく データベース を指定し、IRISTEMP / CACHETEMP データベースを選択すると参照できます。

 

ターミナル上で確認する場合は、znspace(省略形:zn)コマンドのパラメータとしてネームスペース名ではなく、データベースディレクトリ名を指定することで該当データベースのみに存在するグローバルを参照できます。

IRISTEMP データベース上のグローバルを参照する場合、以下例のように移動します。

USER>zn "^^../IRISTEMP"
..iris\mgr\iristemp\>
0
0 279
記事 Toshihiko Minamoto · 3月 15, 2021 16m read

InterSystems IRIS では、情報を格納する「グローバル」というユニークなデータ構造をサポートしています。 基本的に、グローバルとは、マルチレベルのインデックスを持つ永続配列であり、トランザクションの実行やツリー構造のスピーディなトラバーサルといった機能が備えられているほか、ObjectScript として知られるプログラミング言語にも対応しています。

ここから先、少なくともコードサンプルについては、グローバルの基礎を理解されているという想定のもとに話しを進めていきます。

グローバルはデータを保存するための魔法の剣です パート1
グローバルはデータを保存するための魔法の剣ですパート2 - ツリー
グローバルはデータを保存するための魔法の剣です パート3 - 疎な配列

グローバルは、普通のテーブルとは全く異なる構造でデータを格納し、OSI モデルの下位層で動作します。 それでは、グローバルを使ったトランザクションとはいかなるもので、どのような特性が見られるのでしょうか。

リレーショナルデータベースの理論では、ACID テスト (Wikipedia で ACID を参照する) に合格するトランザクションこそが、適切に実装されたトランザクションとされています。

  • 不可分性: トランザクションで発生する変更内容がすべて記録されるか、そのいずれも記録されない。 Wikipedia で不可分性 (データベースシステム) を見る
  • 一貫性: トランザクションが完了した後、データベース内部の論理状態が一貫している。 いろいろな意味で、この要件はプログラマーに適用されるものですが、SQL データベースの場合では、外部キーにも適用されます。
  • 分離性: 並列に実行されるトランザクションの結果はお互いを影響しない。
  • 永続性 (あるいは持続性): トランザクションが完了した後に、OSI モデルの物理層の問題 (電源障害など) が発生しても、トランザクションで変更されたデータには影響しない。

グローバルは、非リレーショナルなデータ構造です。 メモリーフットプリントを最小限に抑えながらハードウェアの超高速作業をサポートできるようにデザインされています。 それでは、IRIS/docker-image を使って、トランザクションがグローバルに実装される仕組みを見てみましょう。 

1. 不可分性

3 の値を一緒にデータベースに保存する必要があるが、そうならない場合はそのいずれも保存されないという状況について考えます。

不可分性を一番手っ取り早く確認するには、ターミナルで次のコードを入力します。 

Kill ^a
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3
TCOMMIT

最後に次を入力します。

ZWRITE ^a

結果は以下のようになるはずです。

^a(1)=1
^a(2)=2
^a(3)=3

予想とおり、不可分性を確認できました。 では、わざとエラーを導入してタスクを少し複雑にします。トランザクションが部分的に保存されるのか、全く保存されないのかを見てみましょう。 まずは、先ほどと同じように不可分性を確認します。 

Kill ^a
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3

今回は、ここで docker kill my-iris というコマンドを使い、コンテナを強制的に終了します。これにより SIGKILL (即座にプロセスを停止する) シグナルが送られるため、電源を強制的にオフにしているようなものです。 コンテナを再起動してから、グローバルの中身をチェックして、結果を確認します。 トランザクションは部分的に保存されているでしょうか?

ZWRITE ^a

何も表示されませんでした

いいえ、何も保存されませんでした。 これにより、アクシデントでサーバーが停止する場合、IRIS データベースでは不可分性が保証されることが分かりました。

では、変更内容を意図的にキャンセルするとしたらどうでしょう? 次のように rollback コマンドを使って試してみます。

Kill ^a

TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3
TROLLBACK 1

ZWRITE ^a

Nothing got out

またしても、何も保存されませんでした。

2. 永続性

グローバルは、リレーショナルテーブルよりも低い層でデータを格納する構造であることを覚えていますか?また、グローバルデータベースでは、インデックスもグローバルとして格納されます。 従い、永続性の要件を満たすには、グローバルノードの値が変更される同じトランザクションにインデックの変更も含める必要があります。

例えば、^person というグローバルがあり、それに個人情報を格納するとします。キーにはソーシャルセキュリティ番号 (SSN) を使用します。 

^person(1234567, 'firstname') = 'Sergey'
^person(1234567, ‘lastname’) = ‘Kamenev’
^person(1234567, ‘phone’) = ‘+74995555555
...

以下のように、^index というキーを作成し、ラストネームだけ、もしくはファーストネームとラストネームの組み合わせを使って、素早く検索できるようにしました。

^index(‘Kamenev’, ‘Sergey’, 1234567) = 1

データベースの永続性を維持するには、person を以下のように追加する必要があります。

TSTART
^person(1234567, ‘firstname’) = ‘Sergey’
^person(1234567, ‘lastname’) = ‘Kamenev’
^person(1234567, ‘phone’) = ‘+74995555555
^index(‘Kamenev’, ‘Sergey’, 1234567) = 1
TCOMMIT

従い、person を削除する場合は、以下のトランザクションを使う必要があります。

TSTART
Kill ^person(1234567)
Kill ^index(‘Kamenev’, ‘Sergey’, 1234567)
TCOMMIT

つまり、グローバルのような下位層の格納形式を使う場合、どのようなロジックを使ってアプリケーションの永続性の要件を満たすかは、プログラマー次第ということです。 

幸い、IRIS にはトランザクションを整理して、アプリケーションの永続性を保証するためのコマンドが備えられています。 SQL が使用される場合、IRIS は内部でこれらのコマンドを実行して、INSERT、UPDATE、DELETE 式が実行されるときに、基となるグローバルのデータ構造を持続します。 もちろん、トランザクションを開始および停止する SQL コマンドは、IRIS SQL にも備えられているので、(SQL) アプリケーションロジックに活用することができます。

3. 分離性

ここからが本番です。 多くのユーザーが同時に同じデータベースにアクセスして、同じデータを変更するとしましょう。 多くのデベロッパーが同じコードリポジトリにアクセスし、多くのファイルの変更内容を同時にコミットしようと試みる状況に似ています。

データベースはすべてをリアルタイムに処理しなくてはいけません。 大手企業なら通常はバージョンコントロール (ブランチのマージや競合解決の管理など) の担当者がいて、かつ データベースはこれをリアルタイムに処理する必要があることを考えると、その問題の複雑さや、データベースとその機能を支えるコードを適切に設計することが大切なのは言うまでもありません。

データベースは、ユーザーの操作が意味することを理解できなければ、ユーザーが同じデータを操作する際に起こる競合を回避することもできません。 他のトランザクションと矛盾するトランザクションを 1 つキャンセルするか、それらを順番に実行することしかできません。

また、トランザクションの実行中 (コミットの前) は、データベースの状態の一貫性が失われている能性もあります。 一貫性を失ったデータベースの状態に他のトランザクションがアクセスできるのはよくありません。 しかし、リレーショナルデータベースでは、スナップショットを作成したり、複数バージョンの行を使用したりするなど、アクセスできてしまう方法がたくさんあります。

複数のトランザクションが並列に実行される場合は、お互いを干渉しないことが大切です。 それを確保するのが分離性です。 

SQL では、分離性は分離する程度の順に 4 つのレベルで定義されます。 以下がその 4 つのレベルです。

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE

では、それぞれのレベルを一つずつ見ていきましょう。 各レベルの実装コストは、スタックを上がるにつれてほぼ指数関数的に増加していきますので、ご注意ください。

READ UNCOMMITTED は、分離の一番低いレベルですが、一番速いレベルでもあります。 トランザクションは、別のトランザクションによりコミットされた変更内容を読み取ることができます。

READ COMMITTED は、次の分離レベルで、譲歩することを意味します。 トランザクションは、コミットの前にお互いの変更を読み取ることはできませんが、コミットの後ならどの変更でも読み取ることができます。

長時間に渡って実行されるトランザクション (T1) があり、その間に T1 と同じデータを操作するトランザクション T2、T3 ... Tn がそれぞれでコミットしたとします。 このような場合は、T1 のデータをリクエストする度に異なる結果が得られる可能性があります。 これは Non-Repeatable Read (非再現リード) と呼ばれています。

次の分離レベルは、REPEATABLE READ です。データの読み取りをリクエストすると、その都度結果のスナップショットがとられるため、ここではもはや非再現リードは起りません。 同じトランザクションの実行中に同じデータがもう一度リクエストされることがあれば、スナップショットが使用されます。 但し、この分離レベルでは、ファンタムデータ (並列に実行されていた別のトランザクションによってコミットされた新しい文字列) が読み取られる可能性があります。

一番高い分離レベルは、SERIALIZABLE です。 トランザクションで使用 (読み取りまたは変更) されたデータに他のトランザクションがアクセスできるのは、最初のトランザクションが終了した後に限定されるという特徴があります。

まずは、トランザクションのあるスレッドとないスレッドとの間で操作が分離されているかどうかを確認してみましょう。 ターミナルを 2 つ開いて、以下を入力します。

<td>
  TSTART Set ^t(1)=2
</td>
Kill ^t Write ^t(1) 2

分離はされていません。 片方のスレッドはトランザクションを開くと、もう片方のスレッドの動作を見ることができます。

それでは、異なるスレッドで実行中のトランザクションがお互いの中の状況を見れるかどうかを確認しましょう。 ターミナルウィンドウを 2 つ開き、2 つのトランザクションを並列に実行します。
 

<td>
  TSTART Set ^t(1)=2
</td>
Kill ^t TSTART Write ^t(1) 2​​​

 

画面に A 3 が表示されます。 ここでは、一番シンプルで一番速い分離レベル READ UNCOMMTTED が設定されています。

原則として、グローバルのような常にスピードを優先する下位層のデータ表現手段では、このレベルが主流となっています。 IRIS SQL では、トランザクションの異なる分離レベルを選択できますが、直接グローバルを操作する時にもっと高い分離レベルが必要になったらどうすればよいでしょう?

ここで、分離レベルの目的とそれぞれの仕組みについて考える必要があります。 例えば、低い分離レベルは、データベースを高速化するための譲歩を目的にしています。

最高の分離レベルを提供する SERIALIZABLE では、並列に実行されたトランザクションの結果がそれらを順に実行した場合の結果と同じになることが保証されます。 これにより、衝突を完全に防ぐことができます。 これは、ObjectScript で適切にロックを使用しても実現できる上に、適用方法も多数あります。 つまり、LOCK コマンドを使えば、普通のロックやインクリメンタルロックを作成したり、ロックを複数個作成することもできます。

それでは、ロックを使って異なる分離レベルを作る方法を見てみましょう。 ObjectScript では、LOCK オペレーターを使います。 このオペレーターは、データを変更するのに必要な排他ロックに限らず、共有ロックと呼ばれるロックも許可します。 共有ロックは、複数のスレッドがデータを読み取る目的で同時にアクセスできるロックであり、そのデータが読み取りプロセス中に他のプロセスによって変更されることはありません。 

ロック方式に関する詳細については、「ロックと並行処理の制御」と題した記事をお読みください。 ツーフェーズロック方式の詳細については、Wikipedia で「ツーフェーズロック」と題された記事をお読みください。

難しいのは、トランザクションの実行中に、データの状態の一貫性が失われ、そのデータが他のプロセスに表示されてしまうという点です。 これはどうすれば回避できるのか? この例では、ロックを使って、データベースの状態が一貫して持続される可視性ウィンドウを作成します。 可視性ウィンドウには、ロックを使ってアクセスします。

同じデータに使われているロックは、再利用できるほか、複数のプロセスによる取得が可能です。 これらのロックを使うことで、データが他のプロセスにより変更されるのを防ぎます。 つまり、データベースの状態を一貫させるためのウィンドウを形成するのに使用されるというわけです。

一方の排他ロックは、データを変更するときに使用されるもので、一度に 1 つのプロセスしか取得できません。 

排他ロック方式を使用できるシナリオは、2 つあります。 1 つ目は、対象となるデータに対し他のプロセスがロックを取得していないため、どのプロセスでもアクセスできるという場合。 2 つ目は、対象となるデータに対し共有ロックを取得し、かつ排他ロックを最初にリクエストしたプロセスだけがそのデータにアクセスできるという場合。

可視性ウィンドウが狭ければ狭いほど、他のプロセスが待機する時間が長くなる一方で、その中にあるデータベースの状態の一貫性は高まります。

READ COMMITTED では、他のスレッドがコミットしたデータしか見えないようになります。 他のトランザクションのデータがまだコミットされていない場合は、古いバージョンが表示されます。 このおかげで、ロックがリリースされるのを待たずに、作業を並列化することができます。

IRIS では、データの古いバージョンを見るには、特殊な裏技が必要になるので、ロックで何とか対処するしかありません。 共有ロックを使って、一貫性が維持されているポイントでのみデータの読み取りを許可する必要があります。

例えば、複数のユーザー「^person」で構成されるデータベースがあり、ユーザー間で資金の送金が行われるとします。 以下のコードは、person 123 から person 242 に資金が送金されるポイントを示しています。

LOCK +^person(123), +^person(242)
TSTART
Set ^person(123, amount) = ^person(123, amount) - amount
Set ^person(242, amount) = ^person(242, amount) + amount
TCOMMIT
LOCK -^person(123), -^person(242)

金額が差し引かれる前の段階で、person 123 に送金のリクエストが出るポイントでは、(デフォルトで) 排他ロックが取得されている必要があります。

LOCK +^person(123)
Write ^person(123)

ですが、ユーザーの個人アカウントのアカウント状況を表示する必要がある場合は、共有ロックを使うか、ロックを一切使わないという選択肢があります。

LOCK +^person(123)#”S”
Write ^person(123)
LOCK -^person(123)#”S”

但し、データベースの操作がほぼ瞬間的に実行されることを許可するのであれば (グローバルはリレーショナルテーブルよりもずっと下位のレベルの構造であることをお忘れなく)、より高い分離レベルが優先されるのでこのレベルはさほど必要ではなくなります。

以下は、READ COMMITTED の完全な例です。

LOCK +^person(123)#”S”, +^person(242)#”S”

Read data (сoncurrent committed transactions can change the data)

LOCK +^person(123), +^person(242)
TSTART
Set ^person(123, amount) = ^person(123, amount) - amount
Set ^person(242, amount) = ^person(242, amount) + amount
TCOMMIT
LOCK -^person(123), -^person(242)

Read data (сoncurrent committed transactions can change the data)

LOCK -^person(123)#”S”, -^person(242)#”S”

REPEATABLE READ は、2 番目に高い分離レベルです。 このレベルでは、1 つのトランザクションでデータが複数回読み取られ、その都度同じ結果が出ること、かつ並列に実行されているトランザクションがそのデータを変更できることを許可します。

分離レベルを確実に REPEATABLE READ にするには、データに対し排他ロックを取得します。それにより、分離レベルは自動的に SERIALIZABLE に引き上げられます。

LOCK +^person(123, amount)

read ^person(123, amount)

その他の操作 (並列ストリームは ^person(123, amount) を変更しようと試みますができません)

change ^person(123, amount)

read ^person(123, amount)

LOCK -^person(123, amount)

ロックがコンマで区切られている場合は、連続的に取得されていることを意味します。 ですが、以下のようにリストアップされている場合は、一斉にアトミックに取得されます。 

LOCK +(^person(123),^person(242))

SERIALIZABLE は、最も高い分離レベルであり、コストも一番高くなります。 上の例で行ったように、従来のロックを操作する場合は、同じデータを持つすべてのトランザクションが連続的に実行されるようにロックを設定する必要があります。 このアプローチでは、ほぼすべてのロックが排他ロックである必要があり、パフォーマンスを確保するためにグローバルの最も小さいフィールドに対し取得される必要もあります。 

^person グローバルから金額を差し引くという場合、SERIALIZABLE 以外のレベルは使えません。 資金の消費は、あくまで連続的なアクションである必要があり、そうでなければ同じ金額が複数回も消費されることになります。

4. 永続性

docker kill my-iris コマンドを使い、コンテナの hard cut-off をテストしたところ、 データベースはこれらのテストに対して良い結果を出し、 問題は一切見られませんでした。

グローバルとロックを管理するためのツール

IRIS Management ポータルでご紹介している以下のツールがお役に立つかもしれません:  

ロックを管理

グローバル

結論

InterSystems IRIS は、グローバルを使ったアトミックかつ永続的なトランザクションをサポートしています。 データベースとグローバルの一貫性を確保するには、ある程度のプログラミングやトランザクションが必要です。それは、外部キーなどの複雑な構造が組み込まれていないためです。

ロックを取得せずにグローバルを使うのは、READ UNCOMMITTED の分離レベルを使うのと同じことですが、ロックを使えばそのレベルを SERIALIZABLE に引き上げることができます。 グローバルを使って実現可能な正確性およびトランザクションの処理速度は、プログラマーのスキルと意図によって大きく左右します。 データを読み取るときに、共有ロックが使用される幅が広ければ広いほど、それだけ分離レベルは高くなります。 また、排他ロックが使用される幅が狭ければ狭いほど、それだけトランザクションの処理速度も速くなります。

0
0 960
記事 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
記事 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
記事 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
記事 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
お知らせ Mihoko Iijima · 1月 11, 2021

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

第9回のマルチモデルコンテストの 続報 📣 の「テクノロジーボーナス」についてご紹介します。

  • InterSystems Globals (key-value)
  • InterSystems SQL
  • InterSystems Objects 
  • Your data model
  • ZPM Package deployment
  • Docker container usage

詳細は以下ご参照ください。<--break->

InterSystems Globals (key-value) - 2 points

InterSystems グローバルは、InterSystems IRIS に任意のデータを格納するために使用される多次元スパース配列です。
各グローバル・ノードはキーとみなされ、値(バリュー)を設定することができます。InterSystems IRIS は、グローバルを管理するための ObjectScript のコマンドや Native API を含む一連の API を提供しています。

ObjectScript または Native API を介してグローバルを使用すると、2 ポイント獲得できます。

ツール:

ドキュメント:

記事:

0
0 125
記事 Toshihiko Minamoto · 11月 18, 2020 5m read

クラス、テーブル、グローバルとその仕組み

InterSystems IRIS を技術的知識を持つ人々に説明する際、私はいつもコアとしてマルチモデル DBMSであることから始めます。

個人的には、それが(DBMSとして)メインの長所であると考えています。 また、データが格納されるのは一度だけです。 ユーザーは単に使用するアクセス API を選択するだけです。

  • データのサマリをソートしたいですか?SQL を使用してください!
  • 1 つのレコードを手広く操作したいですか?オブジェクトを使用してください!
  • あなたが知っているキーに対して、1 つの値にアクセスしたりセットしたいですか? グローバルを使用してください!

これは短く簡潔なメッセージで、一見すると素晴らしく聞こえます。しかし、実際には intersystems IRIS を使い始めるたユーザーには クラス、テーブル、グローバルはそれぞれどのように関連しているのだろうか? 互いにどのような存在なのだろうか? データは実際にどのように格納されているのだろうか?といった疑問が生じます。

この記事では、これらの疑問に答えながら実際の動きを説明するつもりです。

パート 1. モデルに対する偏見。

データを処理するユーザーは多くの場合、処理対象のモデルに偏見を持っています。

開発者はオブジェクトで考えます。 このようなユーザーにとって、データベースとテーブルは CRUD(Create-Read-Update-Delete、ORM の使用が望ましい)を介して操作する箱のようなものですが、その基礎となる概念モデルはオブジェクトです(これは主に私たちのような多くのオブジェクト指向言語の開発者に当てはまります)。

一方、リレーショナル DBMS に多くの時間を費やしているデータベース管理者は往々にしてデータをテーブルと見なしています。 この場合、オブジェクトはレコードの単なるラッパー扱いです。

また、InterSystems IRIS では永続クラスはデータをグローバルに格納するテーブルでもあるため、いくつかの説明が必要になります。

パート 2. 具体例

次のような Point クラスを作成したとします。

Class try.Point Extends %Persistent [DDLAllowed]
{
    Property X;
    Property Y;
}

次のように DDL/SQL を使用して同じクラスを作成することもできます。

CREATE Table try.Point (
    X VARCHAR(50),
    Y VARCHAR(50))

コンパイル後、新しいクラスがグローバルにネイティブに格納されているデータをカラム(またはオブジェクト指向のユーザーの場合はプロパティ)にマッピングするストレージ構造を自動生成します。

Storage Default
{
<Data name="PointDefaultData">
    <Value name="1">
        <Value>%%CLASSNAME</Value>
    </Value>
    <Value name="2">
        <Value>X</Value>
    </Value>
    <Value name="3">
        <Value>Y</Value>
    </Value>
</Data>
<DataLocation>^try.PointD</DataLocation>
<DefaultData>PointDefaultData</DefaultData>
<IdLocation>^try.PointD</IdLocation>
<IndexLocation>^try.PointI</IndexLocation>
<StreamLocation>^try.PointS</StreamLocation>
<Type>%Library.CacheStorage</Type>
}

ここでは何が起きているのでしょうか?

下から順番に説明します(太字の単語が重要です。残りは無視してください)。

  • Type - 生成されたストレージタイプ。この場合は永続オブジェクトのデフォルトストレージです。
  • StreamLocation - ストリームを格納するグローバルです。
  • IndexLocation - インデックス用のグローバルです。
  • IdLocation - ID の自動インクリメントカウンターを格納するグローバルです。
  • DefaultData - グローバルの値をカラム/プロパティにマッピングするストレージの XML 要素です。
  • DataLocation - データを格納するグローバルです。

ここでは「DefaultData」が PointDefaultData となっていますので、その構造をもう少し詳しく見てみましょう。 基本的に、グローバルノードは次の構造を持っていると言われています。

  • 1 - %%CLASSNAME
  • 2 - X
  • 3 - Y

したがって、グローバルは次のようになると予想されます。

^try.PointD(id) = %%CLASSNAME, X, Y

しかし、グローバルを出力すると空になります。ここではデータを追加していなかったためです。

zw ^try.PointD

オブジェクトを 1 つ追加しましょう。

set p = ##class(try.Point).%New()
set p.X = 1
set p.Y = 2
write p.%Save()

すると、グローバルはこのようになります。

zw ^try.PointD
^try.PointD=1
^try.PointD(1)=$lb("",1,2)

ご覧のように、期待する構造 %%CLASSNAME, X, Y はオブジェクトの X プロパティと Y プロパティに対応する $lb("",1,2) とセットになっています(%%CLASSNAME はシステムプロパティですので無視してください)。

次のように SQL を使用してレコードを追加することもできます。

INSERT INTO try.Point (X, Y) VALUES (3,4)

すると、グローバルの内容は次のようになります。

zw ^try.PointD
^try.PointD=2
^try.PointD(1)=$lb("",1,2)
^try.PointD(2)=$lb("",3,4)

つまり、オブジェクトまたは SQL を介して追加するデータは、ストレージ定義に従ってグローバルに格納されます(補足:PointDefaultData の X と Y を置き換えることでストレージ定義を手動で変更できます。その場合に新しいデータがどうなるかを確認してください!)。

では、SQL クエリを実行したい場合はどうなるのでしょうか?

SELECT * FROM try.Point

これは ^try.PointD グローバルを反復処理し、ストレージ定義(正確にはその PointDefaultData 部分)に基づいてカラムにデータを入力する ObjectScript コードに変換されます。

今度は変更を行います。 テーブルからすべてのデータを削除しましょう。

DELETE FROM try.Point

すると、この時点でグローバルの内容は次のようになります。

zw ^try.PointD
^try.PointD=2

ここでは ID カウンターのみが残っているため、新しいオブジェクト/レコードの ID は 3 になることに注意してください。 また、クラスとテーブルは引き続き存在します。

しかし、次を実行するとどうなるでしょうか。

DROP TABLE try.Point

これはテーブルとクラスを破棄し、グローバルを削除します。

zw ^try.PointD

皆さんがこの具体例に従い、グローバル、クラス、テーブルがどのように統合され、相互に補完しているかをより深く理解できたことを願っています。 手元の仕事に適切な API を使用すれば、開発がより高速かつアジャイルになり、バグが少なくなります。

0
0 557
記事 Toshihiko Minamoto · 10月 27, 2020 14m read

秩序(順序)はだれにとっても必要であるが、皆が同じように秩序(順序)を理解しているわけではない (ファウスト・セルチニャーニ)

免責事項: この記事では、例としてロシア語とキリル文字を使用しますが、英語以外のロケールでCachéを使用するすべての方に関連のある記事です。この記事は主にNLS照合について言及しており、SQL照合とは異なることに注意してください。 SQL照合(SQLUPPER、SQLSTRING、照合なしを意味するEXACT、TRUCATEなど)は、値に明示的に適用される実際の関数であり、その結果はグローバルサブスクリプトに明示的に格納されることがあります。 サブスクリプトに格納されると、これらの値は当然、有効なNLS照合(「SQLおよびNLS照合」)に従うことになります。

Cachéのデータ、メタデータ、クラス、ルーチンはすべてグローバルに格納されます。 グローバルは永続的です。 グローバルノードはサブスクリプト値によって順序付けされ、検索やディスクフェッチのパフォーマンスを向上させるために、挿入された順ではなくソート順でストレージデバイスに格納されます。

USER>set ^a(10)=""
USER>set ^a("фф")=""
USER>set ^a("бб")=""
USER>set ^a(2)=""
USER>zwrite ^a
^a(2)=""
^a(10)=""
^a("бб")=""
^a("фф")=""

ソート中、Cachéは数値と文字列を区別します。2は数値として扱われ、10より前にソートされます。 コマンドZWriteおよび関数$Order$Queryは、これらのサブスクリプトが格納された順でグローバルサブスクリプトを返します。初めに空の文字列(サブスクリプトとして使用できません)、その後に負の数値、ゼロ、正の数値、そして照合で定義された順で文字列を返します(照合)。

Cachéの標準的な照合は(当然ながら)Caché標準と呼ばれており、 文字列はUnicode文字コードに従ってソートされます。

現在のプロセスのローカル配列の照合はロケールによって定義されます(管理ポータル > システム管理 > 構成 > システム構成 > 国言語設定 > Locale Definitions)。 CachéのUnicodeインストールのロシア語ロケールはruswで、ruswのデフォルトの照合はCyrillic3です。 ruswロケールで指定できる照合には、Caché標準、Cyrillic1、Cyrillic3、Cyrillic4、Ukrainian1があります。

ClassMethod ##class(%Collate).SetLocalName()は現在のプロセスのローカル配列に使用する照合を設定します。

USER>write ##class(%Collate).GetLocalName()
Cyrillic3
USER>write ##class(%Collate).SetLocalName("Cache standard")
1
USER>write ##class(%Collate).GetLocalName()
Cache standard
USER>write ##class(%Collate).SetLocalName("Cyrillic3")
1
USER>write ##class(%Collate).GetLocalName()
Cyrillic3

すべての照合には、数値を文字列としてソートする照合があります。 その照合の名前には末尾に「string」が含まれています。

USER>write ##class(%Collate).SetLocalName("Cache standard string")
1
USER>kill test

USER>set test(10) = "", test(2) = "", test("фф") = "", test("бб") = ""

USER>zwrite test
test(10)=""
test(2)=""
test("бб")=""
test("фф")=""

USER>write ##class(%Collate).SetLocalName("Cache standard")
1
USER>kill test

USER>set test(10) = "", test(2) = "", test("фф") = "", test("бб") = ""

USER>zwrite test
test(2)=""
test(10)=""
test("бб")=""
test("фф")=""

Caché標準とCyrillic3

Caché標準は、コードに従って文字をソートします。

 write ##class(%Library.Collate).SetLocalName("Cache standard"),!
 write ##class(%Library.Collate).GetLocalName(),!
 set letters = "абвгдеёжзийклмнопрстуфхцчщщьыъэюя"
 set letters = letters _ $zconvert(letters,"U")
 kill test

 //fill local array “test” with data
 for i=1:1:$Length(letters) {
     set test($Extract(letters,i)) = ""
 }

 //print test subscripts in sorted order
 set l = "", cnt = 0
 for  {
     set l = $Order(test(l))
     quit:l=""
     write l, " ", $Ascii(l),","
     set cnt = cnt + 1
     write:cnt#8=0 !
 }

USER>do ^testcol
1
Cache standard
Ё 1025,А 1040,Б 1041,В 1042,Г 1043,Д 1044,Е 1045,Ж 1046,
З 1047,И 1048,Й 1049,К 1050,Л 1051,М 1052,Н 1053,О 1054,
П 1055,Р 1056,С 1057,Т 1058,У 1059,Ф 1060,Х 1061,Ц 1062,
Ч 1063,Щ 1065,Ъ 1066,Ы 1067,Ь 1068,Э 1069,Ю 1070,Я 1071,
а 1072,б 1073,в 1074,г 1075,д 1076,е 1077,ж 1078,з 1079,
и 1080,й 1081,к 1082,л 1083,м 1084,н 1085,о 1086,п 1087,
р 1088,с 1089,т 1090,у 1091,ф 1092,х 1093,ц 1094,ч 1095,
щ 1097,ъ 1098,ы 1099,ь 1100,э 1101,ю 1102,я 1103,ё 1105,

キリル文字は、ロシア語のアルファベット順に出力されますが、「ё」と「Ё」は例外です。 これらのUnicode文字コードは順不同であり、 「Ё」は「Е」と「Д」の間、「ё」は「е」と「д」の間で照合されます。 そのため、ロシア語ロケールには独自の照合として、ロシア語のアルファベットと同じ順に並ぶ文字を使用するCyrillic3が必要となります。

USER>do ^testcol
1
Cyrillic3
А 1040,Б 1041,В 1042,Г 1043,Д 1044,Е 1045,Ё 1025,Ж 1046,
З 1047,И 1048,Й 1049,К 1050,Л 1051,М 1052,Н 1053,О 1054,
П 1055,Р 1056,С 1057,Т 1058,У 1059,Ф 1060,Х 1061,Ц 1062,
Ч 1063,Щ 1065,Ъ 1066,Ы 1067,Ь 1068,Э 1069,Ю 1070,Я 1071,
а 1072,б 1073,в 1074,г 1075,д 1076,е 1077,ё 1105,ж 1078,
з 1079,и 1080,й 1081,к 1082,л 1083,м 1084,н 1085,о 1086,
п 1087,р 1088,с 1089,т 1090,у 1091,ф 1092,х 1093,ц 1094,
ч 1095,щ 1097,ъ 1098,ы 1099,ь 1100,э 1101,ю 1102,я 1103,

Cachéのobjectscriptには、「後にソート」という特殊なバイナリ演算子「]]」があります。 最初のオペランドを持つサブスクリプトが2番目のオペランドを持つサブスクリプトの後にソートされる場合は1を返し、そうでない場合は0を返す演算子です。

USER>write ##class(%Library.Collate).SetLocalName("Cache standard"),!
1
USER>write "А" ]] "Ё"
1
USER>write ##class(%Library.Collate).SetLocalName("Cyrillic3"),!
1
USER>write "А" ]] "Ё"
0

グローバルと照合

同一のデータベースに含まれるグローバルには、さまざまな照合が使用される場合があります。 各データベースには構成オプションがあり、新しいグローバルにはデフォルト照合が使用されます。 インストール直後、USERを除くすべてのデータベースはCaché標準のデフォルト照合を使用します。 USERデータベースのデフォルト照合はインストールロケールによって決まるため、 ruswの場合はCyrillic3となります。

データベースに対しデフォルト以外の照合を使用してグローバルを作成するには、##class(%GlobalEdit).Createメソッドを使用します。

USER>kill ^a
USER>write ##class(%GlobalEdit).Create(,"a",##class(%Collate).DisplayToLogical("Cache standard"))

管理ポータル(システムエクスプローラ > Globals)のグローバルのリストに、各グローバルの照合列があります。

既存のグローバルの照合を変更することはできないため、 新しい照合でグローバルを作成して、Mergeコマンドを使ってデータをコピーする必要があります。 グローバルの一括変換を行うには##class(SYS.Database).Copy()を使用します。

Cyrillic4、Cyrillic3、およびウムラウト

文字列サブスクリプトを内部形式に変換するには、Cyrillic3照合には、Caché標準照合にかかる時間よりもはるかに長い時間がかかるため、Cyrillic3照合を使用したグローバル(またはローカル)配列の挿入とルックアップの処理が遅くなります。 Caché 2014.1には新しい照合であるCyrillic4が含まれており、Cyrillic3と同じ正しい文字順になりますが、パフォーマンスが改善されます。

for collation="Cache standard","Cyrillic3","Cyrillic4" {
     write ##class(%Library.Collate).SetLocalName(collation),!
     write ##class(%Library.Collate).GetLocalName(),!
     do test(100000)
 }
 quit
test(C)
 set letters = "абвгдеёжзийклмнопрстуфхцчщщьыъэюя"
 set letters = letters _ $zconvert(letters,"U")

 kill test
 write "test insert: "
 //fill local array “test” with data
 set z1=$zh
 for c=1:1:C {
     for i=1:1:$Length(letters) {
         set test($Extract(letters,i)_"плюс длинное русское слово" _ $Extract(letters,i)) = ""
     }
 }
 write $zh-z1,!

 //looping through test subscripts
 write "test $Order: "
 set z1=$zh
 for c=1:1:C {
     set l = ""
     for  {
         set l = $Order(test(l))
         quit:l=""
     }
 }
 write $zh-z1,!

USER>do ^testcol
1
Cache standard
test insert: 1.520673
test $Order: 2.062228
1
Cyrillic3
test insert: 3.541697
test $Order: 5.938042
1
Cyrillic4
test insert: 1.925205
test $Order: 2.834399

Cyrillic4は、まだruswロケールのデフォルトの照合ではありませんが、ruswに基づいて独自のロケールを定義し、Cyrillic4をローカル配列のデフォルトの照合として指定することができます。 または、データベース設定でグローバルの新しいデフォルト照合としてCyrillic4を設定することもできます。

Cyrillic3は、2つの文字列を各文字コードに基づいてソートするのに比べてさらに一般的なアルゴリズムに基づいているため、Caché標準とCyrillic4よりも低速になります。

ドイツ語の場合、ソート時には文字はssとして照合されます。 Cachéは次の通り、そのルールを尊重します。

USER>write ##class(%Collate).GetLocalName()
German3
USER>set test("Straßer")=1
USER>set test("Strasser")=1
USER>set test("Straster")=1
USER>zwrite test
test("Strasser")=1
test("Straßer")=1
test("Straster")=1

サブスクリプトの文字列のソート順に注目してください。 特に、最初の文字列の頭の4文字は「Stras」で、次に「Straß」、そしてもう一度「Stras」となっているところです。 照合が個別の文字のコードに基づいてソートされているだけであれば、この方法で文字列をソートすることは不可能です。

もう1つの例として、フィンランド語の場合、「v」と「w」は同一の文字として照合される必要があります。 ロシア語の照合ルールはより単純であり、各文字にある特定のコードを与え、これらのコードでソートするだけで十分です。 この方法で、照合Cyrillic4 のパフォーマンスはCyrillic3と比べて改善されています。

照合とSQL

配列の照合とSQL照合を混同しないようにしてください。 SQL照合は、比較の前に文字列に適用される変換、またはインデックスグローバルでサブスクリプトとして使用する変換です。 CachéのデフォルトのSQL照合はSQLUPPERで、 すべての文字を大文字に変換し、スペース文字を削除して、文字列の先頭にスペースを1つ追加します。 ほかのSQL照合(EXACT、SQLSTRING、TRUNCATE)については、こちらのドキュメントに説明されています。

同一のデータベース内の異なるグローバルにさまざまな照合が使用されており、ローカル配列にも別の照合が使用されている場合、たやすく混乱が生じます。 SQLは一時データにCACHETEMPデータベースを使用しますが、 CACHETEMPのグローバルのデフォルト照合は、Cachéのインストールロケールの照合とは異なる可能性があります。

主なルールが1つあります。期待される順序で行を返すSQLクエリのORDER BYについては、データと関連するテーブルのインデックスがソートされるグローバルの照合は、CACHETEMPデータベースのデフォルトの照合とローカル配列の照合と同一である必要があります。 詳細については、ドキュメントの「SQLとNLS照合」の段落を参照してください。

では、テストクラスを作成しましょう。

Class Collation.test Extends %Persistent
{

Property Name As %String;

Property Surname As %String;

Index SurInd On Surname;

ClassMethod populate()
{
    do ..%KillExtent()

    set t = ..%New()
    set t.Name = "Павел", t.Surname = "Ёлкин"
    write t.%Save()

    set t = ..%New()
    set t.Name = "Пётр", t.Surname = "Иванов"
    write t.%Save()

    set t = ..%New()
    set t.Name = "Прохор", t.Surname = "Александров"
    write t.%Save()
}

}

クラスにデータを入力します(後で、ドイツ語を使った前の例の単語を使用してください)。

USER>do ##class(Collation.test).populate()

クエリを実行します。

![](https://community.intersystems.com/sites/default/files/inline/images/0-order-by-name-wrong.png)

予想外の結果となりました。 主な疑問は、名前がなぜアルファベット順に並べられないのか (Павел、Пётр、Прохор)というところにあります。 クエリプランを見てみましょう。

![](https://community.intersystems.com/sites/default/files/inline/images/1-order-by-name-plan.png)

このプランのキーワードは、「一時ファイルを作成する」です。 SQLエンジンは、このクエリの実行に一時的な構造を使用することに決定しました。 「ファイル」と呼ばれてはいますが、実際にはプロセスプライベートのグローバルであり、場合によってはローカル配列です。 このグローバルのサブスクリプトは、この特定のケースでは個人名で順序付けする値です。 プロセスプライベートのグローバルは、CACHETEMPデータベースに格納され、CACHETEMPの新しいグローバルのデフォルト照合はCaché標準です。

また、「ё」がなぜ最後ではなく最初に返されているのかという別の合理的な疑問もあります(Caché標準では「ё」はロシア語のすべての文字と「Ё」の前ではなく、後にソートされます)。 一時グローバルのサブスクリプトはNameフィールドの実際の値ではなく、Nameを大文字化した値(SQLUPPERは文字列のデフォルトのSQL照合)であるため、「Ё」がほかの文字の前に返されているのです。

%Exact関数を使用してデフォルトの照合を変更すると、依然として誤りではありますが、少なくとも「ё」がほかの文字の後にソートされるという、期待された結果が得られます。

![](https://community.intersystems.com/sites/default/files/inline/images/2-order-by-exact-name.png)

ここでは、CACHETEMPのデフォルトの照合を変更せずに、Surname列のクエリを確認してみましょう。 この列のインデックスは、^Collation.testIグローバルに格納されます。 そのグローバルの照合はCyrillic3であるため、行の正しい順序を確認できるはずです。

![](https://community.intersystems.com/sites/default/files/inline/images/3-order-by-surname-wrong.png)

またしても、誤りです。「Ё」は 「А」と「И」の間である必要があります。 クエリプランを見てみましょう。

![](https://community.intersystems.com//sites/default/files/inline/images/4-order-by-surname-plan.png)

SQLUPPERがSurInd インデックスの値に適用されているため、Surnameフィールドの元の値を出力できる十分なインデックスデータがありません。 SQLエンジンは、Name列と同様に、テーブル自体の値を使用して一時ファイルの値を並べ替えるように決定しています。

クエリに、Surnameが大文字でも良いことを記述することができます。 行はインデックスグローバル ^Collation.testI から直接取得されるため、順序は正しくなります。

![](https://community.intersystems.com//sites/default/files/inline/images/5-order-by-surname-sqlupper.png)

クエリプランは期待通りです。

![](https://community.intersystems.com//sites/default/files/inline/images/6-order-by-surname-sqlupper-plan.png)

では、ずっと前に行っているはずの、CACHETEMPデータベースのデフォルト照合をCyrillic3(またはCyrillic4)に変更する作業を行いましょう。

一時ファイルを使用するクエリは、正しい順で行を出力します。

![](https://community.intersystems.com//sites/default/files/inline/images/7-order-by-name-ok.png)
![](https://community.intersystems.com//sites/default/files/inline/images/8-order-by-surname-ok.png)

要約

  • ローカルアルファベットのニュアンスを気にしない場合は、Caché標準の照合を使用します。
  • 一部の照合(Cyrillic4)は、ほかの照合(Cyrillic3)よりもパフォーマンスが優れています。
  • CACHETEMPの照合がメインデータベースとローカル配列の照合と同じであることを確認します。
0
0 569