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?

クラス・クエリの定義と使用

この章では、クラス・クエリについて説明します。クラス・クエリは、クラス構造の一部である名前付きクエリとして機能し、ダイナミック SQL を通じてアクセスできます。以下のトピックについて説明します。

このドキュメントをオンラインで表示している場合は、このドキュメントの "序文" を使用すると、関連のあるトピックをすばやく見つけることができます。

クラス・クエリの概要

クラス・クエリとは、ダイナミック SQL と共に使用することを目的として、クラスに含まれているツールです。これにより、指定した条件を満たすレコードを検索します。クラス・クエリを使用すると、アプリケーションに応じて事前に定義した検索を作成できます。例えば、名前でレコードを検索したり、特定の条件のセット (パリからマドリッドまでのすべての航空便など) を満たすレコードのリストを提供したりできます。

クラス・クエリを作成することで、特定のオブジェクトの内部 ID を使用してそのオブジェクトの検索を避けることができます。その代わり、必要とする任意のクラス・プロパティに基づいて検索するクエリを作成できます。これは、実行時にユーザによって指定することも可能です。

カスタム・クラス・クエリを定義すると、検索ロジックに ObjectScript を使用することも、検索ロジックを任意の複合式にすることもできます。

クラス・クエリには、以下の 2 種類があります。

クラス・クエリはどのクラス内でも定義できます。クラス・クエリは永続クラス内に含める必要はありません。

Important:

別のクラス・クエリの結果に依存するクラス・クエリを定義しないでください。このような依存関係はサポートされません。

クラス・クエリの使用法

クラス・クエリを定義する方法について説明する前に、その使用方法を知っておくと便利です。サーバ側のコードでは、以下のようにクラス・クエリを使用できます。

  1. %New() を使用して、%SQL.StatementOpens in a new tab のインスタンスを作成します。

  2. そのインスタンスの %PrepareClassQuery() メソッドを呼び出します。引数として、以下をこの順番で使用します。

    1. 使用するクエリを定義しているクラスの完全修飾名。

    2. そのクラスに含まれるクエリの名前。

    このメソッドは %StatusOpens in a new tab 値を返すので、その値を確認する必要があります。

  3. %SQL.StatementOpens in a new tab インスタンスの %Execute() メソッドを呼び出します。これにより %SQL.StatementResultOpens in a new tab のインスタンスが返されます。

  4. %SQL.StatementResultOpens in a new tab のメソッドを使用して、結果セットからデータを取得します。詳細は、"Caché SQL の使用法" の “ダイナミック SQL” を参照してください。

同じ方法で、従来のダイナミック SQL API (%ResultSetOpens in a new tab) を使用できます。

以下に、SAMPLES ネームスペースで使用できる簡単な例を示します。この例では、Sample.PersonOpens in a new tabByName クエリを使用します。

 // classquerydemo

#include %occInclude
 
 set statement=##class(%SQL.Statement).%New()
 set status=statement.%PrepareClassQuery("Sample.Person","ByName")
 if $$$ISERR(status) { do $system.OBJ.DisplayError(status) }
 set resultset=statement.%Execute()
 while resultset.%Next() {
    write !, resultset.%Get("Name")
 }

Caché Java バインディングまたは ActiveX バインディングを使用している場合は、そのバインディングに含まれる結果セット・クラスを使用できます。

クエリに SqlProc のマークが付けられている (クエリが ODBC または JDBC ストアド・プロシージャとして定義されている) 場合、そのクエリは SQL コンテキストからストアド・プロシージャとして呼び出すことができます。"Caché SQL の使用法" の "ストアド・プロシージャの定義と使用" を参照してください。

基本クラス・クエリの定義

基本クラス・クエリを定義するには、以下のようにクエリを定義します。

  • (単純なクラス・クエリの場合) タイプは %SQLQueryOpens in a new tab にする必要があります。

  • 引数リストに、クエリが受け入れる引数を指定します。

  • 定義の本文に、SQL SELECT 文を記述します。

    この文では、引数名の先頭にコロン (:) を付けて、引数を参照します。

    この SELECT 文には、INTO 節を含めないでください。

  • クエリの ROWSPEC パラメータを指定します (クエリ・タイプの後の括弧内で指定します)。このパラメータは、クエリの結果セットの各行に含まれるフィールドの名前、データ型、ヘッダ、および順序に関する情報を提供します。詳細は、2 番目のサブセクションで説明します。

  • 必要に応じて、クエリの CONTAINID パラメータを指定します (クエリ・タイプの後の括弧内で指定します)。このパラメータでは、特定の行の ID を格納するフィールドの列番号を指定します (フィールドが存在する場合)。既定値は 1 です。詳細は、3 番目のサブセクションで説明します。

    ROWSPEC パラメータと CONTAINID パラメータをまとめて、クエリ仕様と呼びます。

  • クエリ定義に SqlProc キーワードを含めます。

    %ResultSetOpens in a new tab を使用するクエリの呼び出しを計画しており、ストアド・プロシージャとしてクエリを呼び出す必要がない場合には、この手順は省略できます。%SQL.StatementOpens in a new tab を使用するクエリの呼び出しを計画している場合は、SqlProc キーワードを指定する必要があります

  • オプションとして、ストアド・プロシージャに既定の名前とは別の名前を付ける場合は、クエリ定義内で SqlName キーワードを指定します。

    これらは、コンパイラ・キーワードであるため、クエリ・タイプ (%SQLQueryOpens in a new tab) の後のパラメータに続く角括弧の内側に含めます。

スタジオには、このような基本クラス・クエリの定義に使用できるウィザード (新規クエリ・ウィザード) があります。以下のサブセクションに例を示します。

以下に簡単な例を示します。

Query ListEmployees(City As %String = "") 
   As %SQLQuery (ROWSPEC="ID:%Integer,Name:%String,Title:%String", CONTAINID = 1) [SqlProc, SqlName=MyProcedureName]
{
SELECT ID,Name,Title FROM Employee
 WHERE (Home_City %STARTSWITH :City)
 ORDER BY Name
}

ROWSPEC パラメータ

クエリの ROWSPEC パラメータでは、各行に含まれるフィールドの名前、データ型、ヘッダ、および順序に関する情報を提供します。これらの変数名は、引用符付きのコンマ区切りのリストで、データ型の形式は以下のとおりです。

ROWSPEC = "Var1:%Type1,Var2:%Type2[:OptionalDescription],Var3"

ROWSPEC は、コンマ区切りのリストとしてフィールドの順序を指定します。各フィールドの情報は、そのフィールドの名前、データ型 (対応するプロパティのデータ型と異なる場合)、およびヘッダ (オプション) をコロンで区切ったリストで構成されています。ROWSPEC を編集するには、以下のオプションがあります。

  • コードを直接編集します。

  • 既存のクエリの場合は、そのクエリをスタジオの [インスペクタ] ウインドウで表示し、パラメータのリストを展開して、使用可能なダイアログ・ボックスを使用します。

ROWSPEC パラメータの要素数は、クエリのフィールド数と一致している必要があります。数が異なる場合、Caché は “Cardinality Mismatch” エラーを返します。

例えば、SAMPLES データベースの場合、Sample.PersonOpens in a new tab サンプル・クラスの ByName クエリは以下のようになります。

Query ByName(name As %String = "") 
    As %SQLQuery(CONTAINID = 1, ROWSPEC = "ID:%Integer,Name,DOB,SSN", SELECTMODE = "RUNTIME") 
   [ SqlName = SP_Sample_By_Name, SqlProc ]
{
        SELECT ID, Name, DOB, SSN
            FROM Sample.Person
            WHERE (Name %STARTSWITH :name)
            ORDER BY Name
}

ここでは、最初のフィールドが行 ID (既定) であることを CONTAINID パラメータで指定しています。SELECT 文で指定されている最初のフィールドも ID である点に注意してください。ROWSPEC パラメータでは、各フィールドが ID (整数として処理される)、NameDOB、および SSN であることを指定しています。同様に、SELECT 文にも IDNameDOB、および SSN の各フィールドが、この順序で含まれています。

CONTAINID パラメータ

CONTAINID は、ID を返す列の番号 (既定では 1) に設定する必要があります。ID を返す列がない場合は 0 に設定します。クエリの作成に新規クエリ・ウィザードを使用すると、スタジオは、ウィザードで指定した順序に基づいて適切な値を CONTAINID に自動的に割り当てます。

Note:

Caché は CONTAINID の値を検証しません。ユーザがこのパラメータに対して無効な値を指定した場合でも、Caché はエラーを返しません。つまり、ユーザのクエリ処理ロジックがこの情報に基づいている場合で、CONTAINID パラメータが不正に設定されているときは、不整合が生じる可能性があります。

クエリ・クラスのその他のパラメータ

ROWSPECCONTAINID に加え、以下に示すクエリのパラメータも指定できます。これらは、%SQLQueryOpens in a new tab のクラス・パラメータです。

  • SELECTMODE

  • COMPILEMODE

詳細は、%Library.SQLQueryOpens in a new tab と、そのスーパークラス %Library.QueryOpens in a new tab のクラスリファレンスを参照してください。

カスタム・クラス・クエリの定義

単純な %SQLQueryOpens in a new tab クエリは、ユーザ向けの結果セットの管理はすべて実行しますが、特定のアプリケーションにとっては十分ではありません。そのような場合、Caché では、カスタム・クエリを作成できます。このクエリは、メソッド内で定義します (既定では、ObjectScript で記述します)。カスタム・クエリを定義するには、この章で前述した手順を使用しますが、その手順に以下の変更を加えます。

  • クエリ・タイプには、%QueryOpens in a new tab を指定します。

  • クエリ定義の本体は空のままにしておきます。以下に例を示します。

    Query All() As %Query(CONTAINID = 1, ROWSPEC = "Title:%String,Author:%String")
    {
    }
  • 同一クラス内に、以下のクラス・メソッドを定義します。

    • querynameExecute — このメソッドでは、1 回限りの任意の設定を実行する必要があります。

    • querynameFetch — このメソッドでは、結果セットの行を返す必要があります。後続の呼び出しごとに、その次の行を返します。

    • querynameClose — このメソッドでは、クリーンアップ処理をすべて実行する必要があります。

    queryname はクエリの名前です。

    これらのメソッドは、それぞれ 1 つの引数 (qHandle) を受け入れます。この引数は、参照で渡します。この引数は、これらのメソッド間で情報を渡すために使用できます。

    これらのメソッドでクエリを定義します。詳細は、以下の各サブセクションで説明します。

基本的なデモンストレーションのために、最初の 3 つのサブセクションでは、基本クラス・クエリとして実装することも可能な簡単な例を示します。この例は、SAMPLES ネームスペースで使用できます。これらのメソッドは、以下のクエリのコードを実装します。

Query AllPersons() As %Query(ROWSPEC = "ID:%String,Name:%String,DOB:%String,SSN:%String")
{
}

より複雑な例は、次のセクションを参照してください。その他の使用例について、“カスタム・クエリの使用法” も参照してください。

querynameExecute() メソッドの定義

querynameExecute() メソッドでは、必要とされる設定ロジックをすべて提供する必要があります。メソッドの名前は、querynameExecute にする必要があります。queryname はクエリの名前です。このメソッドには、以下のシグネチャが必要です。

ClassMethod queryNameExecute(ByRef qHandle As %Binary, 
                             additional_arguments) As %Status

以下はその説明です。

  • qHandle は、このクエリを実装する他のメソッドとの通信に使用します。

    このメソッドでは、querynameFetch メソッドの要求に応じて、qHandle を設定する必要があります。

    qHandle は、正式には %BinaryOpens in a new tab タイプですが、任意の値 (OREF や多次元配列を含む) を保持できます。

  • additional_arguments は、クエリで使用できる任意の実行時パラメータです。

このメソッドの実装では、以下の一般的なロジックを使用します。

  1. 1 回限りの任意の設定手順を実行します。

    SQL コードを使用するクエリの場合、このメソッドには、一般的にカーソルの宣言とオープンも含めます。

  2. querynameFetch メソッドの要求に応じて、qHandle を設定します。

  3. ステータス値を返します。

次に示す単純な例は、前述の AllPersons クエリの AllPersonsExecute() メソッドです。

ClassMethod AllPersonsExecute(ByRef qHandle As %Binary) As %Status
{
    set statement=##class(%SQL.Statement).%New()
    set status=statement.%PrepareClassQuery("Sample.Person","ByName")
    if $$$ISERR(status) { quit status }
    set resultset=statement.%Execute()
    set qHandle=resultset
    Quit $$$OK
}

このシナリオでは、このメソッドで qHandle が OREF になるように設定します。具体的には、この OREF は %SQL.StatementResultOpens in a new tab のインスタンスであり、%Execute() メソッドから返された値です。これは、1 つの可能性に過ぎません。別の方法については、“カスタム・クラスのその他の例” を参照してください。

前述したように、このクラス・クエリは、カスタム・クラス・クエリではなく、基本クラス・クエリとして実装することもできます。ただし、カスタム・クラス・クエリには、開始点としてダイナミック SQL を使用するものもあります。

querynameFetch() メソッドの定義

querynameFetch() メソッドは、単一のデータ行を $List 形式で返す必要があります。メソッドの名前は、querynameFetch にする必要があります。queryname はクエリの名前です。このメソッドには、以下のシグネチャが必要です。

ClassMethod queryNameFetch(ByRef qHandle As %Binary, 
                           ByRef Row As %List,
                           ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = querynameExecute ]

以下はその説明です。

  • qHandle は、このクエリを実装する他のメソッドとの通信に使用します。

    このメソッドの実行を Caché が開始するときに、qHandlequerynameExecute メソッドで設定された値か、このメソッドの前回の呼び出し (ある場合) で設定された値を保持しています。このメソッドでは、それ以降のロジックの要求に応じて、qHandle を設定する必要があります。

    qHandle は、正式には %BinaryOpens in a new tab タイプですが、任意の値 (OREF や多次元配列を含む) を保持できます。

  • Row は、返されるデータの行を表す値の %List にするか、Null 文字列 (返す値がない場合) にする必要があります。

  • AtEnd は、最後のデータの行に到達したときに 1 にする必要があります。

  • PlaceAfter メソッド・キーワードでは、生成されたルーチン・コード内で、このメソッドの位置を制御します。querynameExecute は、具体的な querynameExecute() メソッドの名前で置き換えます。これは、クエリで SQL カーソルを使用する場合には必ず含めます。(この順序を制御する機能は高度な機能であり、慎重に使用する必要があります。インターシステムズは、このキーワードの一般的な使用をお勧めしていません。)

このメソッドの実装では、以下の一般的なロジックを使用します。

  1. 返す必要のある結果が、まだあるかどうかを判断するための確認を行います。

  2. 適切な場合は、データの行を取得し、%List オブジェクトを作成して、Row 変数に格納します。

  3. qHandle は、このメソッドの以降の呼び出し (ある場合) で要求される場合や、querynameClose() メソッドで要求される場合に設定します。

  4. 以降の行が存在しない場合は、Row を Null 文字列に設定して、AtEnd を 1 に設定します。

  5. ステータス値を返します。

AllPersons の例では、AllPersonsFetch() メソッドは以下のようになります。

ClassMethod AllPersonsFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status 
[ PlaceAfter = AllPersonsExecute ]
{
    set rs=$get(qHandle)
    if rs="" quit $$$OK

    if rs.%Next() {
        set Row=$lb(rs.%GetData(1),rs.%GetData(2),rs.%GetData(3),rs.%GetData(4))
        set AtEnd=0
    } else {
        set Row=""
        set AtEnd=1
    }
    Quit $$$OK
}

このメソッドでは qHandle 引数を使用している点に注目してください。この引数は、%SQL.StatementResultOpens in a new tab オブジェクトを提供します。その後で、そのクラスのメソッドを使用してデータを取得しています。このメソッドでは、$List を構築して、それを Row 変数に格納しています。これは、単一のデータ行として返されます。さらに、このメソッドには、取得できるデータがなくなったときに、AtEnd 変数を設定するロジックが含まれている点にも注目してください。

前述したように、このクラス・クエリは、カスタム・クラス・クエリではなく、基本クラス・クエリとして実装することもできます。この例は、Row 変数と AtEnd 変数の設定についての実例を示すことを目的としています。

querynameClose() メソッド

querynameClose() メソッドでは、データ取得の完了後に必要になるクリーンアップをすべて実行する必要があります。メソッドの名前は、querynameClose にする必要があります。queryname はクエリの名前です。このメソッドには、以下のシグネチャが必要です。

ClassMethod queryNameClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = querynameFetch ]

以下はその説明です。

  • qHandle は、このクエリを実装する他のメソッドとの通信に使用します。

    このメソッドの実行を Caché が開始するときに、qHandlequerynameFetch メソッドの最後の呼び出しで設定された値を保持しています。

  • PlaceAfter メソッド・キーワードでは、生成されたルーチン・コード内で、このメソッドの位置を制御します。querynameFetch は、具体的な querynameFetch() メソッドの名前で置き換えます。これは、クエリで SQL カーソルを使用する場合には必ず含めます。(この順序を制御する機能は高度な機能であり、慎重に使用する必要があります。インターシステムズは、このキーワードの一般的な使用をお勧めしていません。)

このメソッドの実装では、メモリからの変数の削除、SQL カーソルのクローズ、またはその他のクリーンアップを必要に応じて実行します。このメソッドはステータス値を返す必要があります。

AllPersons の例では、AllPersonsClose() メソッドは以下のようになります。

例えば、ByNameClose() メソッドのシグニチャは以下のようになります。

ClassMethod AllPersonsClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = AllPersonsFetch ]
{
        Set qHandle=""
        Quit $$$OK
}

カスタム・クエリ用に生成されるメソッド

システムは、querynameGetInfo()querynameFetchRows() を自動的に生成します。ユーザのアプリケーションは、上記のメソッドを直接呼び出すことはありません。%Library.ResultSetOpens in a new tab オブジェクトが、これらのメソッドを使用してクエリの要求を処理します。

カスタム・クエリのパラメータの定義

カスタム・クエリでパラメータを受け入れる必要がある場合は、以下の手順に従います。

  • それらをクエリ・クラス・メンバの引数リストに含めます。以下の例は、MyParm という名前のパラメータを使用しています。

    Query All(MyParm As %String) As %Query(CONTAINID = 1, ROWSPEC = "Title:%String,Author:%String")
    {
    }
  • 同じパラメータを、クエリ・クラス・メンバの順序と同じ順序で、querynameExecute メソッドの引数リストに含めます。

  • querynameExecute メソッドの実装で、必要に応じたパラメータを使用します。

カスタム・クエリのその他の例

前のセクションでは、簡単なカスタム・クラス・クエリの例について説明しました。これは、基本クラス・クエリとして簡単に実装できるものでした。このセクションでは、より一般的な Caché クラス・ライブラリの例を示します。別の構想については、次のセクションも参照してください。

Important:

この例は、使用可能な手段についての用例を示すためのものであり、クラス・ライブラリで特定の機能を実装する方法をドキュメント化するものではありません。そのため、このセクションでは、現時点でこのコードを含むクラスを示しません。また、今後そのクラスが変更された場合に、それを反映してこのセクションの内容が更新されることもありません。

この例では、クエリでプロセス・プライベート・グローバルを作成して使用しています。このクエリは以下のように定義されます。

Query ByServer() As %Query(ROWSPEC = "Name,Port,PingPort,Renderer,State,StateEx") [ SqlProc ]
{
}

querynameExecute() メソッドは、以下のとおりです。

ClassMethod ByServerExecute(ByRef qHandle As %Binary) As %Status [ Internal ]
{
    Set tSC = $$$OK
    Try {
        Set tRS = ##class(%ResultSet).%New("%ZEN.Report.RenderServer:ByName")
        Kill ^||%ISC.ZRS
        Set tSC = tRS.Execute()
        For {
            Quit:'tRS.Next()
            Set tType = tRS.Get("ServerType")
            If (tType'=0) && (tType'="") Continue // Not a Render Server
            Set name = tRS.Get("Name")
            Set ^||%ISC.ZRS(name) = $LB(name,tRS.Get("Port"),tRS.Get("PingPort"),tRS.Get("Renderer"))
        }
    }
    Catch (ex) {
        Set tSC = ex.AsStatus()
    }
    Set qHandle = $LB("")
    Quit tSC
}

このメソッドでは、qHandle 変数ではなく、プロセス・プライベート・グローバルにデータを保存しています。また、このメソッドでは従来のダイナミック SQL クラス (%ResultSetOpens in a new tab) を使用している点に注意してください。

querynameFetch() メソッドは、以下のとおりです。

ClassMethod ByServerFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) 
As %Status [ Internal, PlaceAfter = ByServerExecute ]
{
    Set index = $List(qHandle,1)
    Set index = $O(^||%ISC.ZRS(index))
    If index="" {
        Set Row = ""
        Set AtEnd = 1
    }
    Else {
        Set Row = ^||%ISC.ZRS(index)
        Set stInt = ..GetState($List(Row,2),$List(Row,3),$List(Row,4))
        Set stExt = $Case(stInt,0:$$$Text("Inactive"),1:$$$Text("Active"),
                   2:$$$Text("Unresponsive"),3:$$$Text("Troubled"),4:$$$Text("Error"),
                   5:$$$Text("Mismatch"),:"")
        Set $List(Row,5) = stInt, $List(Row,6) = stExt
    }
    Set qHandle = $LB(index)
    Quit $$$OK
}

最終的に、querynameClose() メソッドは以下のようになります。

ClassMethod ByServerClose(ByRef qHandle As %Binary) As %Status [ Internal, PlaceAfter = ByServerExecute ]
{
    Set qHandle = ""
    Kill ^||%ISC.ZRS
    Quit $$$OK
}

カスタム・クエリを使用する場合

以下のリストでは、カスタム・クエリが適切になる、いくつかのシナリオを示しています。

  • 返されるデータに特定の行を含めるかどうかを決定するために、非常に複雑なロジックを使用する必要がある場合。querynameFetch() メソッドには、任意の複雑なロジックを含めることができます。

  • 現在の使用事例には不便な形式でデータを返す API がある場合。このようなシナリオでは、Row 変数の要求に応じて、その形式のデータを $List に変換する querynameFetch() メソッドを定義することになります。

  • クラス・インタフェースを備えていないグローバルに、データが格納されている場合。

  • データにアクセスするために、ロール・エスカレーションが必要になる場合。このシナリオでは、querynameExecute() メソッド内でロール・エスカレーションを実行します。

  • データにアクセスするために、ファイル・システムへの外部呼び出しが必要になる場合 (例えば、ファイルのリストを構築する場合)。このシナリオでは、querynameExecute() メソッド内からコールアウトを実行して、その結果を qHandle か、グローバルに格納します。

  • データを取得する前に、セキュリティ・チェック、接続のチェック、またはその他の特別な設定作業を実行する必要がある場合。そのような作業は、querynameExecute() メソッド内で実行することになります。

SQL カーソルとクラス・クエリ

クラス・クエリで SQL カーソルを使用する場合は、以下の点に注意してください。

  • %SQLQueryOpens in a new tab タイプのクエリから生成されたカーソルは、自動的に Q14 などの名前を持ちます。

    カーソルに固有の名前が与えられていることを確認してください。

  • エラー・メッセージは、通常、桁を 1 桁余分に持っている内部カーソル名を参照します。したがって、カーソル Q140 に対するエラー・メッセージは、おそらく Q14 を参照しています。

  • クラス・コンパイラは、カーソルを使用する前に、カーソル宣言を見つける必要があります。そのため、カーソルを使用するカスタム・クエリを定義する際には、特別な注意を払う必要があります。

    DECLARE 文 (通常は querynameExecute() メソッド内) は、Close や Fetch と同じ MAC ルーチン内にあることが必要です。また、Close や Fetch よりも先に来る必要があります。このようにするには、この章で前述したように、querynameFetch()querynameClose() の両方のメソッド定義で、メソッド・キーワード PlaceAfter を使用してください。

FeedbackOpens in a new tab