Skip to main content

This is documentation for Caché & Ensemble. See the InterSystems IRIS version of this content.Opens in a new tab

For information on migrating to InterSystems IRISOpens in a new tab, see Why Migrate to InterSystems IRIS?

永続オブジェクトと Caché SQL

Caché の主な特徴は、オブジェクト・テクノロジと SQL との結合にあります。どのようなシナリオに対しても、最も便利なアクセス・モードを使用できます。この章では、Caché でこの機能がどのように提供されているのかを説明し、格納されているデータを操作するためのオプションの概要について説明します。以下のトピックについて説明します。

はじめに

Caché は、オブジェクト・データベースと呼ばれることもある、オブジェクト指向のプログラミング言語と結合されたデータベースを提供します。その結果、以下をすべて実行できる柔軟なコードを作成できます。

  • SQL を介してデータの一括挿入を実行します。

  • オブジェクトを開き、変更し、保存します。したがって、SQL を使用しないで 1 つ以上のテーブルのデータを変更します。

  • 新しいオブジェクトを作成、保存し、SQL を使用しないで 1 つ以上のテーブルに行を追加します。

  • 多数のオブジェクトに繰り返し処理を実行せずに、SQL を使用して、レコードから、指定された条件に一致する値を取得します。

  • オブジェクトを削除し、SQL を使用せずに 1 つ以上のテーブルからレコードを削除します。

つまり、いつでもニーズにあったアクセス・モードを選択できます。

内部では、すべてのアクセスは、直接グローバル・アクセスを介して実行され、必要に応じてユーザもその方法で自身のデータにアクセスできます。(クラス定義がある場合、直接グローバル・アクセスを使用してデータに変更を行うことはお勧めしません。)

Caché SQL

Caché は、Caché SQL と呼ばれる SQL の実装を提供します。

Caché SQL は、いくつかの例外を除き、すべての初級 SQL-92 標準をサポートし、いくつかの拡張もあります。Caché SQL は、インデックス、トリガ、BLOB、およびストアド・プロシージャもサポートしています (これらは、RDBMS の機能であり、SQL-92 標準には含まれません)。完全なリストについては、"Caché SQL の使用法" を参照してください。

Caché SQL を使用する場所

Caché SQL は、ルーチン内およびメソッド内で使用できます。これらのコンテキストで SQL を使用するには、次のツールのいずれかまたは両方を使用します。

  • 埋め込み SQL。以下に例を示します。

     &sql(SELECT COUNT(*) INTO :myvar FROM Sample.Person)
     Write myvar

    埋め込み SQL は、ObjectScript で作成されたメソッドおよび ObjectScript ルーチンで使用できます。

  • ダイナミック SQL (%SQL.StatementOpens in a new tab クラスおよび %SQL.StatementResultOpens in a new tab クラス)。以下に例を示します。

     SET myquery = "SELECT TOP 5 Name,DOB FROM Sample.Person"
     SET tStatement = ##class(%SQL.Statement).%New()
     SET tStatus = tStatement.%Prepare(myquery)
     SET rset = tStatement.%Execute()
     //now use proprties of rset object
    
    

    ダイナミック SQL はどのようなコンテキストでも使用できます。

また、Caché SQL は、SQL シェル内 (ターミナル) および管理ポータルで直接実行できます。これらには、それぞれ、クエリ・プランを表示するためのオプションがあり、クエリをさらに効率的にする方法を特定する際に役立ちます。

SQL へのオブジェクト拡張

オブジェクト・アプリケーションで SQL を使用しやすくするために、Caché には SQL へのオブジェクト拡張が多数あります。

たいへん興味深い拡張として、リファレンス演算子 (“–>”) を使用して、オブジェクト参照を実行する機能があります。例えば、2 つのクラス、ContactRegion を参照する Vendor クラスがあるとします。リファレンス演算子を使用すると、関連するクラスのプロパティを参照できます。

SELECT ID,Name,ContactInfo->Name
FROM Vendor
WHERE Vendor->Region->Name = 'Antarctica'

また、SQL JOIN 構文を使用しても、同様のクエリ式を記述できます。リファレンス演算子構文の利点は、簡潔で理解しやすい点です。

永続クラスに対する特別なオプション

Caché では、すべての永続クラスは %Library.PersistentOpens in a new tab (%PersistentOpens in a new tab とも呼ぶ) を拡張します。このクラスは、Caché でのオブジェクト SQL 対応のためのフレームワークの大部分を提供します。永続クラス内では、以下のようなオプションがあります。

  • オブジェクトを開く、保存する、および削除するためのメソッド

    永続オブジェクトを開く場合、永続オブジェクトは、複数のユーザまたは複数のプロセスによって使用される可能性があるため、同時処理ロックの程度を指定します。

    オブジェクト・インスタンスを開き、オブジェクト値プロパティを参照すると、システムによってそのオブジェクトも自動的に開かれます。このプロセスをスウィズリングといいます (また、遅延ロードということもあります)。それにより、そのオブジェクトも操作できます。以下はその例です。

     Set person=##class(Sample.Person).%OpenId(10)
     Set person.Name="Andrew Park"
     Set person.Address.City="Birmingham" 
     Do person.%Save()

    同様に、オブジェクトを保存すると、システムによって、そのすべてのオブジェクト値プロパティも自動的に保存されます。これをディープ・セーブといいます。代わりに、シャロウ・セーブを実行するオプションもあります。

  • このクラスのオブジェクトのデータが格納される SQL 結果セットである既定のクエリ (エクステント・クエリ)。

    このクラス (または他のクラス) では、追加のクエリを定義できます。このドキュメントで前述した “クラス・クエリ” を参照してください。

  • 外部キーとして SQL に投影されるクラス間のリレーションシップを定義する機能。

    リレーションシップは、2 つ以上のオブジェクト・インスタンスの相互の関連性を定義する、オブジェクト値プロパティの特別なタイプです。すべてのリレーションシップは 2 つで 1 組みです。リレーションシップのすべての定義には、対応する逆のリレーションシップがあり、それが反対側を定義します。Caché ではデータの参照整合性が自動的に適用され、一方での操作を、他方ですぐに見ることができます。リレーションシップは、メモリ内の振る舞いやディスク上の振る舞いを自動的に管理します。それらは、オブジェクト・コレクションを使用した優れたスケーリングと同時処理も提供します (前の章の “コレクション・クラス” を参照してください)。

  • 外部キーを定義する機能。実際は、外部キーを追加して、既存のアプリケーションに参照整合性制約を追加します。新しいアプリケーションの場合、代わりにリレーションシップを定義したほうが簡単です。

  • これらのクラスにインデックスを定義する機能。

    インデックスは、永続クラスのインスタンス全体にわたる検索を最適化するためのメカニズムを提供します。インデックスは、クラスに関連付けられた一般的に要求されるデータのサブセットを定義します。これらは、パフォーマンス・クリティカルな検索のオーバーヘッドを削減するのに非常に役立ちます。

    インデックスは、それらのクラスに属する 1 つ、または複数のプロパティで並べ替えできます。これは、返される結果の順序を制御するのに大変便利です。

    またインデックスは、並べ替えされたプロパティに基づいて、クエリで頻繁に要求される他のデータを格納できます。インデックスの一部として他のデータを含めることによって、インデックスを使用するクエリの性能が大きく向上します。メイン・データ・ストレージにアクセスしなくても、クエリがインデックスを使用して結果セットを生成できます。

  • 行が挿入、変更、または削除されるときに何が行われるのかを制御するために、これらのクラスにトリガを定義する機能。

  • メソッドおよびクラス・クエリを SQL ストアド・プロシージャとして投影する機能。

  • SQL へのプロジェクションを細かく調整する (例えば、SQL クエリのようにテーブルと列名を指定する) 機能。

  • オブジェクトのデータを格納するグローバルの構造を細かく調整する機能。

永続クラスの SQL プロジェクション

永続クラスの場合、クラスの各インスタンスは、SQL を使用してクエリおよび操作できる、テーブルの行として使用できます。この例を示すために、このセクションでは管理ポータルとターミナルを使用します。これらについては、このドキュメントで後で説明します。

オブジェクト SQL プロジェクションのデモ

SAMPLES の Sample.PersonOpens in a new tab クラスを考えてみます。管理ポータルを使用して、このクラスに対応するテーブルのコンテンツを表示すると、以下に類似したものが表示されます。

generated description: smp sql browse sampleperson

(このサンプルはリリースごとに再構築されるため、これは実際に表示されるものと同じデータではありません。)以下の点に注意してください。

  • ここに表示される値は、表示値であり、ディスクに格納されている論理値ではありません。

  • 最初の列 (#) は、表示されているこのページ内の行番号です。

  • 2 番目の列 ([ID]) は、このテーブルの行に対する一意の識別子であり、このクラスのオブジェクトを開くときは、この識別子を使用します (このクラスではこれらの識別子は整数ですが、整数ではないこともあります)。

    このテーブルは、SAMPLES データベースが構築されるたびに新しく生成されるため、この場合、これらの番号は同一になっています。実際のアプリケーションでは、いくつかのレコードが削除されている可能性があるため、[ID] の値には相違があり、それらの値は行番号と一致しません。

ターミナルで、一連のコマンドを使用して、最初の人を調べることができます。

SAMPLES>set person=##class(Sample.Person).%OpenId(1)
 
SAMPLES>write person.Name
Van De Griek,Charlotte M.
SAMPLES>write person.FavoriteColors.Count()
1
SAMPLES>write person.FavoriteColors.GetAt(1)
Red
SAMPLES>write person.SSN
571-15-2479

これらは、SQL を使用した場合と同じ値です。

オブジェクト SQL プロジェクションの基本事項

継承はリレーショナル・モデルの一部ではないので、クラス・コンパイラは、永続クラスを “平坦化” したものをリレーショナル・テーブルとして投影します。以下の表は、さまざまなオブジェクト要素のいくつかが SQL にどのように投影されるのかを示しています。

オブジェクト・コンセプト SQL コンセプト
パッケージ スキーマ
クラス テーブル
データ型プロパティ フィールド
埋め込みオブジェクト 一連のフィールド
リスト・プロパティ リスト・フィールド
配列プロパティ 子テーブル
ストリーム・プロパティ BLOB
インデックス インデックス
ストアド・プロシージャとしてマークされるクラス・メソッド ストアド・プロシージャ

投影されたテーブルには、継承されたフィールドを含む、そのクラスに適切なすべてのフィールドが含まれます。

クラスとエクステント

Caché は、従来にない強力な解釈によるオブジェクトテーブル・マッピングを使用します。

永続クラスのすべての格納されているインスタンスによって、クラスのエクステントと呼ばれるものが構成され、インスタンスは、それがインスタンスとなっているクラスそれぞれのエクステントに属します。したがって、以下のようになります。

  • 永続クラス Person にサブクラス Student がある場合、Person エクステントには、Person のすべてのインスタンスと Student のすべてのインスタンスが含まれます。

  • クラス Student のどのインスタンスの場合も、そのインスタンスは Person エクステントおよび Student エクステントに含まれます。

インデックスは、インデックスが定義されるクラスの全範囲を自動的にカバーします。Person に定義されているインデックスには、Person インスタンスと Student インスタンスの両方が含まれます。Student エクステントで定義されたインデックスは、Student インデックスだけを含みます。

サブクラスは、そのスーパークラスで定義されていない追加のプロパティも定義できます。これらは、サブクラスのエクステントでは使用できますが、スーパークラスのエクステントでは使用できません。例えば、Student エクステントには FacultyAdvisor フィールドが含まれますが、これは Person エクステントには含まれません。

前述の点は、Caché では、同じタイプのレコードをすべて取得するクエリを比較的簡単に作成できることを意味します。例えば、すべてのタイプの人々をカウントする場合、Person テーブルに対してクエリを実行できます。学生のみをカウントする場合、同じクエリを Student テーブルに対して実行します。これとは対照的に、他のオブジェクト・データベースでは、すべてのタイプの人々をカウントするには、テーブルを結合する複雑なクエリを作成する必要があり、別のサブクラスが追加されるたびにこのクエリを更新する必要があります。

オブジェクト ID

各オブジェクトは、それが属する各エクステント内で一意の ID を持っています。多くの場合、この ID を使用してそのオブジェクトを操作します。この ID は、%PersistentOpens in a new tab クラスの、以下のよく使用されるメソッドの引数です。

  • %DeleteId()

  • %ExistsId()

  • %OpenId()

このクラスには、ID を使用する他のメソッドもあります。

ID はどのように決まるか

最初にオブジェクトを保存するときに、Caché によって ID 値が割り当てられます。この割り当ては永続的であり、オブジェクトの ID は変更できません。他のオブジェクトが削除されたり、変更された場合でも、オブジェクトに新しい ID が割り当てられることはありません。

どの ID も、そのエクステント内で一意です。

オブジェクトの ID は、以下のように決定されます。

  • 大部分のクラスでは、ID は、既定ではそのクラスのオブジェクトが保存されるときに順番に割り当てられる整数です。

  • 親子リレーションシップの子として使用されるクラスでは、ID は以下のようにフォーマットされます。

    parentID||childID
    

    parentID は親オブジェクトの ID であり、childID は、子オブジェクトが親子リレーションシップの子として使用されていない場合に受け取る ID です。例 :

    104||3
    

    この ID は 3 番目に保存された子であり、その親はそれ自体のエクステントに ID 104 を持っています。

  • このクラスが、タイプ IdKey のインデックスを持っていて、そのインデックスが特定のプロパティに対するものである場合、そのプロパティ値が ID として使用されます。

    SKU-447
    

    また、そのプロパティ値も変更できません。

  • このクラスが、タイプ IdKey のインデックスを持っていて、そのインデックスが複数のプロパティに対するものである場合、それらのプロパティ値が連結されて ID を形成します。以下に例を示します。

    CATEGORY12||SUBCATEGORYA
    

    また、これらのプロパティ値も変更できません。

ID へのアクセス

オブジェクトの ID 値にアクセスするには、そのオブジェクトが %PersistentOpens in a new tab から継承する %Id() インスタンス・メソッドを使用します。

SQL では、オブジェクトの ID 値は、[%Id] という擬似フィールドとして使用できます。管理ポータルでテーブルを参照する場合には、[%Id] 擬似フィールドは ID というキャプションで表示されます。

generated description: smp sql browse sampleperson

キャプションはこのようになっていても、擬似フィールドの名前は [%Id] です。

ストレージ

各永続クラス定義には、そのクラス・プロパティが、それが実際に格納されているグローバルにどのようにマップされるかを示す情報が含まれています。この情報は、クラス・コンパイラによってクラスに対して生成され、開発者が変更してリコンパイルしたときに更新されます。

ストレージ定義の確認

この情報を確認すると役立つ場合があり、まれに、その詳細のいくつかを (注意深く) 変更することが必要になります。永続クラスの場合、スタジオはクラス定義の一部として、次に示すような定義を表示します。

<Storage name="Default">
<Data name="PersonDefaultData"><Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>Name</Value>
</Value>
<Value name="3">
<Value>SSN</Value>
</Value>
<Value name="4">
<Value>DOB</Value>
</Value>
...
</Storage>

永続クラスによって使用されるグローバル

ストレージ定義には、データが格納されるグローバルを指定するいくつかの要素が含まれています。

<DataLocation>^Sample.PersonD</DataLocation>
<IdLocation>^Sample.PersonD</IdLocation>
<IndexLocation>^Sample.PersonI</IndexLocation>
...
<StreamLocation>^Sample.PersonS</StreamLocation>

既定では、既定のストレージで以下のようになります。

  • クラス・データは、そのクラスのデータ・グローバルに格納されます。その名前は、完全なクラス名 (パッケージ名を含む) で始まります。その名前に “D” が追加されます。例えば、Sample.PersonD のようになります。

  • インデックス・データは、そのクラスのインデックス・グローバルに格納されます。その名前は、クラス名で始まり、“I” で終わります。例えば、Sample.PersonI のようになります。

  • 保存されるストリーム・プロパティは、そのクラスのストリーム・グローバルに格納されます。その名前は、クラス名で始まり、“S” で終わります。例えば、Sample.PersonS のようになります。

Important:

完全なクラス名が長い場合、システムでは、代わりにクラス名のハッシュした形式が自動的に使用されます。したがって、ストレージ定義を表示したときに、^package1.pC347.VeryLongCla4F4AD のようなグローバル名が表示されることがあります。何らかの理由で、クラスのデータ・グローバルを使用して直接作業する場合は、ストレージ定義を調べて、グローバルの実際の名前を確認する必要があります。

非公式には、これらのグローバルはクラス・グローバルと呼ばれることがありますが、これは誤解を招きやすい用語です。クラス定義は、これらのグローバルには格納されません。

格納されるオブジェクトの既定の構造

一般的なクラスの場合、大部分のデータはデータ・グローバルに格納され、それには以下のノードが含まれています。

ノード ノードのコンテンツ
^full_class_name("id")

full_class_name は、パッケージを含む完全なクラス名であり、31 文字までの長さにする必要がある場合はハッシュされます。また、id はオブジェクト ID であり、これについては、“オブジェクト ID” で説明しています。

$ListBuild によって返されるフォーマットのリスト。

このリストでは、格納されるプロパティは、ストレージ定義の <Value> 要素の name 属性によって指定される順序でリストされます。

当然ながら、一時プロパティは格納されません。ストリーム・プロパティは、そのクラスのストリーム・グローバルに格納されます。

使用例は、この章で後述する “格納されているデータの確認” を参照してください。

メモ

以下の点に注意してください。

  • 格納されたデータを持つクラスのストレージを再定義したり、削除しないでください。その場合、ストレージを手動で再作成しなければならなくなります。それは、次にそのクラスをコンパイルしたときに作成される新しい既定のストレージは、そのクラスに必要なストレージと合わない可能性があるためです。

  • 開発中に、クラスのストレージ定義のリセットが必要になる場合があります。それは、データも削除して、後でそれを再ロードまたは再生成する場合に実行できます。

    詳細は、このドキュメントで後述する “習得する必要がある役立つスキル” を参照してください。

  • 既定では、開発中にプロパティを追加および削除すると、システムによってスキーマ進化というプロセスが使用されて自動的にストレージ定義が更新されます。

    例外は、<Type> 要素に対して既定以外のストレージ・クラスを使用する場合です。既定は %Library.CacheStorageOpens in a new tab です。このストレージ・クラスを使用しない場合、Caché によってストレージ定義が更新されることはありません。その他の一般的なオプションは、%Library.CacheSQLStorageOpens in a new tab です。これは、主に Caché でクラスが提供される前に作成されたアプリケーションをサポートするために使用します。

永続クラスおよびテーブルを作成するためのオプション

永続クラスとそれに対応する SQL テーブルを作成するには、次のいずれかを実行します。

  • スタジオを使用して、%PersistentOpens in a new tab をベースにしたクラスを定義します。そのクラスをコンパイルすると、システムによってテーブルが作成されます。

  • 管理ポータルで、データ移行ウィザードを使用できます。これにより、外部テーブルが読み取られ、いくつかの詳細設定を求めるプロンプトが表示され、%PersistentOpens in a new tab をベースにしたクラスが作成され、対応する SQL テーブルにレコードがロードされます。

    後で、ウィザードを再び実行することで、クラスを再定義しないで追加のレコードをロードできます。

  • 管理ポータルで、リンク・テーブル・ウィザードを使用できます。これにより、外部テーブルが読み取られ、いくつかの詳細設定を求めるプロンプトが表示され、外部テーブルにリンクされているクラスが生成されます。クラスは、実行時にデータを外部テーブルから取得します。

    これは、特別な場合であり、このドキュメントでは詳しく説明しません。

  • 管理ポータルで、FileMan ウィザードを使用できます。これにより、FileMan ファイルが読み取られ、クラスが作成されます。

  • Caché SQL で、CREATE TABLE または他の DDL 文を使用します。この方法でもクラスが作成されます。

  • ターミナル (またはコード) で、%SQL.Util.ProceduresOpens in a new tabCSVTOCLASS() メソッドを使用します。詳細は、%SQL.Util.ProceduresOpens in a new tab のクラスリファレンスを参照してください。

データへのアクセス

永続クラスに関連付けられたデータにアクセス、そのデータを変更および削除するには、以下のいずれかまたはすべてを実行するコードを作成できます。

  • 永続クラスのインスタンスを開き、それらを変更および保存します。

  • 永続クラスのインスタンスを削除します。

  • 埋め込み SQL を使用します。

  • ダイナミック SQL (SQL 文と結果セット・インタフェース) を使用します。

  • 直接グローバル・アクセスのための低レベルのコマンドおよび関数を使用します。この手法は、格納されている値を取得する場合以外は、お勧めしません。それは、この手法では、オブジェクトおよび SQL インタフェースによって定義されているロジックがバイパスされるためです。

Caché SQL は、以下のような場合に適しています。

  • 最初の時点では開くインスタンスの ID が不明であり、代わりに、入力条件に基づいて 1 つ以上のインスタンスを選択する場合。

  • 一括ロードまたは一括変更を実行する場合。

  • オブジェクト・インスタンスを開かずにそのデータを表示する場合

    (ただし、オブジェクト・アクセスを使用する場合、同時処理ロックの程度を制御できます。データを変更する予定がない場合は、最小限の同時処理ロックを使用できます)。

  • SQL に精通している場合。

オブジェクト・アクセスは、以下のような場合に適しています。

  • 新しいオブジェクトを作成する場合。

  • 開くインスタンスの ID がわかっている場合。

  • SQL を使用するよりも、プロパティの値を設定するほうが直感的に理解しやすい場合。

格納されているデータの確認

このセクションでは、任意の永続オブジェクトに対して、オブジェクト・アクセス、SQL アクセス、および直接グローバル・アクセスを使用して同じ値が見えることを例示します。

スタジオでは、Sample.PersonOpens in a new tab クラスを表示すると、次のプロパティ定義が表示されます。

/// Person's name.
Property Name As %String(POPSPEC = "Name()") [ Required ];

...

/// Person's age.<br> 
/// This is a calculated field whose value is derived from <property>DOB</property>. 
Property Age As %Integer [ details removed for this example ]; 

/// Person's Date of Birth.
Property DOB As %Date(POPSPEC = "Date()");

ターミナルでは、格納されているオブジェクトを開き、そのプロパティ値を書き込めます。

SAMPLES>set person=##class(Sample.Person).%OpenId(1)
 
SAMPLES>w person.Name
Newton,Dave R.
SAMPLES>w person.Age
14
SAMPLES>w person.DOB
58153

ここでは、DOB プロパティのリテラルの格納されている値 (リテラル) が表示されます。代わりに、このプロパティの表示値を返すメソッドを呼び出すこともできます。

SAMPLES>write person.DOBLogicalToDisplay(person.DOB)
03/20/2000

管理ポータルでは、このクラスの格納されているデータを参照できます。以下のように表示されます。

generated description: smp sql browse sampleperson

この場合、DOB プロパティの表示値を表示できます。(ポータルでは、クエリを実行するもう 1 つのオプションがあり、そのオプションを使用すると、その結果に対して論理モードと表示モードのどちらを使用するのか制御できます。)

ポータルでは、このクラスのデータが格納されているグローバルを参照することもできます。

generated description: smp global sampleperson

また、ターミナルでは、このインスタンスを含むグローバル・ノードの値を書き込むことができます。

zw ^Sample.PersonD("1")
^Sample.PersonD(1)=$lb("","Newton,Dave R.","384-10-6538",58153,$lb("6977 First Street","Pueblo","AK",63163),
$lb("9984 Second Blvd","Washington","MN",42829),"",$lb("Red"))

スペースの関係で、最後の例には追加の改行が含まれています。

Caché SQL に対して生成されたコードのストレージ

Caché SQL の場合 (埋め込み SQL として使用される場合を除く)、データにアクセスするための再使用可能なコードがシステムによって生成されます。

最初に SQL 文を実行するときに、Caché によってクエリが最適化され、データを取得するコードが生成され、格納されます。コードは、最適化されたクエリ・テキストと共にクエリ・キャッシュに格納されます。このキャッシュは、コードのキャッシュであり、データのキャッシュではありません。

後で SQL 文を実行すると、Caché によってそれが最適化され、そのクエリのテキストと、クエリ・キャッシュに格納されている項目が比較されます。Caché によって、格納されているクエリが、指定されたクエリと一致していると判断された場合 (空白などのわずかな相違を除く)、そのクエリに対して格納されているコードが使用されます。

クエリ・キャッシュを表示し、その中に含まれている任意の項目を削除できます。

詳細

この章で説明したトピックの詳細は、以下を参照してください。

FeedbackOpens in a new tab