カスタム・クラス・クエリの定義
基本的なクラス・クエリは、ユーザ向けの結果セットの管理はすべて実行しますが、特定のアプリケーションにとっては十分ではありません。そのような場合、カスタム・クラス・クエリを作成できます。このクエリは、連携する一連のメソッドにより定義されます。これらのメソッドは既定で ObjectScript に書き込まれます。
カスタム・クラス・クエリは、シャード・クラスではサポートされていません。
カスタム・クラス・クエリの定義
カスタム・クエリを定義するには、基本クラス・クエリの定義手順を使用しますが、その手順に以下の変更を加えます。
-
クエリ・タイプには、%QueryOpens in a new tab を指定します。
-
クエリの ROWSPEC パラメータを指定します (クエリ・タイプの後の括弧内で指定します)。このパラメータは、クエリの結果セットの各行に含まれるフィールドの名前、データ型、ヘッダ、および順序に関する情報を提供します。"ROWSPEC パラメータ" を参照してください。
-
必要に応じて、クエリの CONTAINID パラメータを指定します (クエリ・タイプの後の括弧内で指定します)。このパラメータでは、特定の行の ID を格納するフィールドの列番号を指定します (フィールドが存在する場合)。既定値は 1 です。"CONTAINID パラメータ" を参照してください。
ROWSPEC パラメータと CONTAINID パラメータをまとめて、クエリ仕様と呼びます。
-
クエリ定義の本体は空のままにしておきます。以下に例を示します。
Query All() As %Query(CONTAINID = 1, ROWSPEC = "Title:%String,Author:%String") { }
-
同一クラス内に、以下のクラス・メソッドを定義します。
-
querynameExecute — このメソッドでは、1 回限りの任意の設定を実行する必要があります。
-
querynameFetch — このメソッドでは、結果セットの行を返す必要があります。後続の呼び出しごとに、その次の行を返します。
-
querynameClose — このメソッドでは、クリーンアップ処理をすべて実行する必要があります。
queryname はクエリの名前です。
これらのメソッドは、それぞれ 1 つの引数 (qHandle) を受け入れます。この引数は、参照で渡します。この引数は、これらのメソッド間で情報を渡すために使用できます。
これらのメソッドでクエリを定義します。次のセクションで詳細を説明します。
-
メソッドの定義
基本的なデモンストレーションのために、このセクションでは、基本クラス・クエリとして実装することも可能な簡単な例を示します。これらのメソッドは、以下のクエリのコードを実装します。
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 回限りの任意の設定手順を実行します。
SQL コードを使用するクエリの場合、このメソッドには、一般的にカーソルの宣言とオープンも含めます。
-
querynameFetch メソッドの要求に応じて、qHandle を設定します。
-
ステータス値を返します。
次に示す単純な例は、前述の 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() メソッドから返された値です。
前述したように、このクラス・クエリは、カスタム・クラス・クエリではなく、基本クラス・クエリとして実装することもできます。ただし、カスタム・クラス・クエリには、開始点としてダイナミック 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 は、このクエリを実装する他のメソッドとの通信に使用します。
このメソッドの実行を InterSystems IRIS® データ・プラットフォームが開始するときに、qHandle は querynameExecute メソッドで設定された値か、このメソッドの前回の呼び出し (ある場合) で設定された値を保持しています。このメソッドでは、それ以降のロジックの要求に応じて、qHandle を設定する必要があります。
qHandle は、正式には %BinaryOpens in a new tab タイプですが、任意の値 (OREF や多次元配列を含む) を保持できます。
-
Row は、返されるデータの行を表す値の %List にするか、Null 文字列 (返す値がない場合) にする必要があります。
-
AtEnd は、最後のデータの行に到達したときに 1 にする必要があります。
-
PlaceAfter メソッド・キーワードでは、生成されたルーチン・コード内で、このメソッドの位置を制御します。querynameExecute は、具体的な querynameExecute() メソッドの名前で置き換えます。これは、クエリで SQL カーソルを使用する場合には必ず含めます。(この順序を制御する機能は高度な機能であり、慎重に使用する必要があります。インターシステムズは、このキーワードの一般的な使用をお勧めしていません。)
このメソッドの実装では、以下の一般的なロジックを使用します。
-
返す必要のある結果が、まだあるかどうかを判断するための確認を行います。
-
適切な場合は、データの行を取得し、%List オブジェクトを作成して、Row 変数に格納します。
-
qHandle は、このメソッドの以降の呼び出し (ある場合) で要求される場合や、querynameClose() メソッドで要求される場合に設定します。
-
以降の行が存在しない場合は、Row を Null 文字列に設定して、AtEnd を 1 に設定します。
-
ステータス値を返します。
AllPersons の例では、AllPersonsFetch() メソッドは以下のようになります。
ClassMethod AllPersonsFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status
[ PlaceAfter = AllPersonsExecute ]
{
set rset = $get(qHandle)
if rset = "" quit $$$OK
if rset.%Next() {
set Row=$lb(rset.%GetData(1),rset.%GetData(2),rset.%GetData(3),rset.%GetData(4))
set AtEnd=0
} else {
if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
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 は、このクエリを実装する他のメソッドとの通信に使用します。
このメソッドの実行を InterSystems IRIS が開始するときに、qHandle は querynameFetch メソッドの最後の呼び出しで設定された値を保持しています。
-
PlaceAfter メソッド・キーワードでは、生成されたルーチン・コード内で、このメソッドの位置を制御します。querynameFetch は、具体的な querynameFetch() メソッドの名前で置き換えます。これは、クエリで SQL カーソルを使用する場合には必ず含めます。(この順序を制御する機能は高度な機能であり、慎重に使用する必要があります。インターシステムズは、このキーワードの一般的な使用をお勧めしていません。)
このメソッドの実装では、メモリからの変数の削除、SQL カーソルのクローズ、またはその他のクリーンアップを必要に応じて実行します。このメソッドはステータス値を返す必要があります。
AllPersons の例では、AllPersonsClose() メソッドは以下のようになります。
例えば、ByNameClose() メソッドのシグニチャは以下のようになります。
ClassMethod AllPersonsClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = AllPersonsFetch ]
{
Set qHandle=""
Quit $$$OK
}
カスタム・クエリ用に生成されるメソッド
システムは、querynameGetInfo() と querynameFetchRows() を自動的に生成します。ユーザのアプリケーションは、上記のメソッドを直接呼び出すことはありません。
クラス・クエリのパラメータ
ここでは、カスタム・クラス・クエリ内で指定するパラメータの詳細について説明します。
ROWSPEC パラメータ
クエリの ROWSPEC パラメータでは、各行に含まれるフィールドの名前、データ型、ヘッダ、および順序に関する情報を提供します。これらの変数名は、引用符付きのコンマ区切りのリストで、データ型の形式は以下のとおりです。
ROWSPEC = "Var1:%Type1,Var2:%Type2[:OptionalDescription],Var3"
ROWSPEC は、コンマ区切りのリストとしてフィールドの順序を指定します。各フィールドの情報は、そのフィールドの名前、データ型 (対応するプロパティのデータ型と異なる場合)、およびヘッダ (オプション) をコロンで区切ったリストで構成されています。
ROWSPEC パラメータの要素数は、クエリのフィールド数と一致している必要があります。数が異なる場合、InterSystems IRIS® データ・プラットフォームは Cardinality Mismatch エラーを返します。
例えば、Sample.Person クラスの 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 (整数として処理される)、Name、DOB、および SSN であることを指定しています。同様に、SELECT 文にも ID、Name、DOB、および SSN の各フィールドが、この順序で含まれています。
CONTAINID パラメータ
CONTAINID は、ID を返す列の番号 (既定では 1) に設定する必要があります。ID を返す列がない場合は 0 に設定します。
システムは CONTAINID の値を検証しません。ユーザがこのパラメータに対して無効な値を指定した場合でも、エラー・メッセージは発行されません。つまり、ユーザのクエリ処理ロジックがこの情報に基づいている場合で、CONTAINID パラメータが不正に設定されているときは、不整合が生じる可能性があります。
クエリ・クラスのその他のパラメータ
ROWSPEC と CONTAINID に加え、以下に示すクエリのパラメータも指定できます。これらは、%SQLQueryOpens in a new tab のクラス・パラメータです。
-
SELECTMODE
-
COMPILEMODE
詳細は、%Library.SQLQueryOpens in a new tab と、そのスーパークラス %Library.QueryOpens in a new tab のクラスリファレンスを参照してください。
カスタム・クエリのパラメータの定義
カスタム・クエリでパラメータを受け入れる必要がある場合は、以下の手順に従います。
-
それらをクエリ・クラス・メンバの引数リストに含めます。以下の例は、MyParm という名前のパラメータを使用しています。
Query All(MyParm As %String) As %Query(CONTAINID = 1, ROWSPEC = "Title:%String,Author:%String") { }
-
同じパラメータを、クエリ・クラス・メンバの順序と同じ順序で、querynameExecute メソッドの引数リストに含めます。
-
querynameExecute メソッドの実装で、必要に応じたパラメータを使用します。
ADO.NET、ODBC、または JDBC を使用してクラス・クエリを呼び出す場合、既定では、文字列パラメータが 50 文字に切り捨てられます。パラメータの最大文字列長を長くするには、以下の例のようにシグニチャで MAXLEN を指定します。
Query MyQuery(MyParm As %String(MAXLEN = 200)) As %Query [SqlProc]
管理ポータルまたは ObjectScript からクエリを呼び出す場合、この切り捨ては行われません。
カスタム・クエリを使用する場合
以下のリストでは、カスタム・クエリが適切になる、いくつかのシナリオを示しています。
-
返されるデータに特定の行を含めるかどうかを決定するために、非常に複雑なロジックを使用する必要がある場合。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 を使用してください。