ダイナミック SQL の使用
この章では、実行時に作成および実行されるクエリおよびその他 SQL 文であるダイナミック SQL について説明します。ここでは、以下のトピックについて説明します。
この章では、ダイナミック SQL の推奨実装である、%SQL.StatementOpens in a new tab クラスを使用したダイナミック SQL プログラミングについて説明します。この章および弊社のドキュメント全体にわたって、ダイナミック SQL に関するすべての文は、特に明記のない限り %SQL.StatementOpens in a new tab の実装を指しています。従来の %ResultSet.SQLOpens in a new tab クラスまたは %Library.ResultSetOpens in a new tab クラスを使用したダイナミック SQL プログラムも、このドキュメントの "従来の %ResultSet.SQL クラスと %Library.ResultSet クラスを使用したダイナミック SQL" の章の説明に従って作成できます。
ダイナミック SQL の概要
ダイナミック SQL は、実行時に作成され、実行される SQL 文です。ダイナミック SQL により、Caché でも ODBC アプリケーションや JDBC アプリケーションと似た方法でプログラムを作成できます (データベース・エンジンと同じプロセス・コンテキストで SQL 文を実行している場合は除きます)。
ダイナミック SQL は ObjectScript プログラムまたは Caché Basic プログラムから呼び出すことができます。
ダイナミック SQL を使用して SQL クエリを実行できます。また、他の SQL 文の発行にも使用できます。この章の例では、SELECT クエリを実行します。CREATE TABLE、INSERT、UPDATE、DELETE、または CALL を呼び出すダイナミック SQL プログラムの例は、"Caché SQL リファレンス" を参照してください。
ダイナミック SQL は、Caché SQL シェルの実行、Caché 管理ポータルの [クエリ実行] インタフェース、SQL コードのインポート・メソッド、およびデータのインポート/エクスポート・ユーティリティで使用されます。ダイナミック SQL (およびそれを使用するアプリケーション) の最大行サイズは、32,767 文字です。この制限は、長い文字列の演算を構成することで大幅に拡張できます。
ダイナミック SQL と埋め込み SQL
ダイナミック SQL と埋め込み SQL の相違点は以下のとおりです。
-
ダイナミック SQL クエリは、コンパイル時ではなく、プログラム実行時に作成されます。つまり、コンパイラはコンパイル時にエラーのチェックを実行できず、プリプロセッサ・マクロをダイナミック SQL 内で使用することはできません。また、実行プログラムはユーザやその他の入力に応答して、専用のダイナミック SQL クエリを生成できます。
-
ダイナミック SQL では、CREATE TABLE または CREATE VIEW を発行し、同じルーチン内のそのテーブルまたはビューで INSERT または SELECT を実行できます。埋め込み SQL は、コンパイルされているので、これを実行できません。
-
ダイナミック SQL はクエリ用のインライン・コードを生成しないため、埋め込み SQL よりもやや非効率的です。ただし、ダイナミック SQL はクエリ・キャッシュをサポートしているため、ダイナミック SQL クエリの再実行はその初回実行よりもはるかに高速です。
-
ダイナミック SQL は、クエリに入力されるリテラル値を 2 つの形で受け付けることができます。1 つは、“?” 文字を使用して指定された入力パラメータであり、もう 1 つは入力ホスト変数です (:var など)。埋め込み SQL では、入力/出力ホスト変数が使用されます (:var など)。
-
ダイナミック SQL の出力値は、結果セット・オブジェクト (つまり Data プロパティ) の API を使用して取得します。埋め込み SQL は、SELECT 文の INTO 節でホスト変数 (:var など) を使用して、値を出力します。
-
ダイナミック SQL では、%SQLCODE、%Message、%ROWCOUNT、および %ROWID というオブジェクト・プロパティが設定されます。埋め込み SQL では、対応するローカル変数である SQLCODE、%msg、%ROWCOUNT、および %ROWID が設定されます。ダイナミック SQL では SELECT クエリに対して %ROWID が設定されませんが、埋め込み SQL では、カーソル・ベースの SELECT クエリに対して %ROWID が設定されます。
-
ダイナミック SQL は ObjectScript または Caché Basic から呼び出すことができます。埋め込み SQL は ObjectScript からのみ呼び出すことができます。
-
ダイナミック SQL により、クエリのメタデータ (列の数や名前など) を簡単に見ることができます。
-
ダイナミック SQL によって作成されるクエリは、クエリ・キャッシュ内で維持されるため、後続のクエリが同じクエリを作成する場合は、既に生成されているコードを再利用できます。埋め込み SQL は、コンパイル時にインライン・コードを生成するため、クエリ・キャッシュを使用する必要がありません。ダイナミック SQL では、これらの文は一般的には 1 回しか使用されないので、非クエリ SQL 文はキャッシュされません。詳細は、"Caché SQL 最適化ガイド" の “クエリ・キャッシュ” の章を参照してください。
-
ダイナミック SQL では SQL 特権の確認が行われるので、テーブルやフィールドなどにアクセスするときやこれらを変更するときは、適切な特権が必要です。埋め込み SQL では、SQL 特権の確認は行われません。
-
ダイナミック SQL では、プライベート・クラス・メソッドにアクセスできません。既存のクラス・メソッドにアクセスするには、そのメソッドをパブリックにする必要があります。これは SQL の一般的な制限です。ただし、埋め込み SQL の場合は、埋め込み SQL の操作自体が同じクラスのメソッドなので、この制限を回避できます。
ダイナミック SQL と埋め込み SQL は同じデータ表示 (既定では論理モードですが、変更できます)、および NULL 処理を使用します。
%SQL.Statement クラス
ダイナミック SQL の推奨インタフェースは、%SQL.StatementOpens in a new tab クラスです。ダイナミック SQL 文を作成および実行するには、%SQL.StatementOpens in a new tab のインスタンスを使用します。ダイナミック SQL 文の実行結果は、%SQL.StatementResultOpens in a new tab クラスのインスタンスである SQL 文の結果オブジェクトになります。SQL 文の結果オブジェクトは、単一値、結果セット、またはコンテキスト・オブジェクトのいずれかです。いずれの場合も、結果オブジェクトは標準インタフェースをサポートします。各結果オブジェクトは %SQLCODE、%Message、およびその他の結果オブジェクトのプロパティを初期化します。これらのプロパティが設定される値は、発行される SQL 文によって異なります。SELECT 文が正常に実行された場合、オブジェクトは、結果セット (具体的には %SQL.IResultSetOpens in a new tab のインスタンス) であり、予想される結果セットの機能をサポートします。
次の ObjectScript コードでは、ダイナミック SQL クエリを作成および実行します。
/* Simple %SQL.Statement example */
ZNSPACE "SAMPLES"
SET myquery = "SELECT TOP 5 Name,DOB FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
DO rset.%Display()
WRITE !,"End of data"
次のコードは、同じダイナミック SQLクエリを Caché Basic で記述したものです。
myquery = "SELECT TOP 5 Name,DOB FROM Sample.Person"
tStatement = New %SQL.Statement()
qStatus = tStatement.%Prepare(myquery)
If qStatus<>1 Then
PrintLn "%Prepare failed: "
PrintLn Piece(qStatus," ",3,10)
Else
rset = tStatement.%Execute()
CALL rset.%Display()
PrintLn
PrintLn "End of data"
End If
この章の例では、%SQL.StatementOpens in a new tab クラスおよび %SQL.StatementResultOpens in a new tab クラスに関連付けられているメソッドを使用します。
オブジェクト・インスタンスの作成
ObjectScript の %New()Opens in a new tab クラス・メソッドを使用して、%SQL.StatementOpens in a new tab クラスのインスタンスを作成できます。
SET tStatement = ##class(%SQL.Statement).%New()
Caché Basic では次のようになります。
tStatement = New %SQL.Statement()
この時点で結果セット・オブジェクトは SQL 文を作成できる状態です。%SQL.StatementOpens in a new tab クラスのインスタンスを作成すると、そのインスタンスを使用して、複数のダイナミック SQL クエリの発行や、INSERT、UPDATE、または DELETE の各操作の実行が可能です。
%New() では、コンマで区切られた 3 つのオプション・パラメータを次の順序で指定できます。
-
%SelectMode : データ表示モードを指定します。
-
%SchemaPath : 未修飾のテーブル名のスキーマ名を指定するために使用する検索パスを指定します。
-
%Dialect : Transact-SQL (TSQL) Sybase 言語または MSSQL 言語を指定します。既定は Caché SQL です。
%ObjectSelectMode プロパティもありますが、これは %New() パラメータとして設定できません。%ObjectSelectMode は、フィールドのデータ型のバインディングを関連するオブジェクト・プロパティに指定します。
以下の ObjectScript の例では、%SelectMode に 2 (表示モード) を指定し、%SchemaPath に既定のスキーマとして “Sample“ を指定しています。
SET tStatement = ##class(%SQL.Statement).%New(2,"Sample")
以下の ObjectScript の例では、%SelectMode には値を指定せず (該当の位置にはコンマのみが記述されています)、%SchemaPath には、3 つのスキーマ名を記述したスキーマ検索パスを指定しています。
SET tStatement = ##class(%SQL.Statement).%New(,"MyTests,Sample,Cinema")
%SelectMode プロパティ
%SelectModeOpens in a new tab プロパティはデータ表示モードを指定します。0 = 論理 (既定値)、1 = ODBC、2 = 表示です。
%SelectMode プロパティは SELECT クエリ操作、および INSERT と UPDATE 操作で使用します。このモードは、通常、日付と時刻の値や、%List データの表示に使用します。
%SelectMode は、データ表示のために使用されます。SQL 文は、内部的に論理モードで実行されます。例えば、ORDER BY 節は、%SelectMode の設定に関係なく、レコードをその論理値に基づいて並べ替えます。SQL 関数は、%SelectMode の設定に関係なく、論理値を使用します。SQLPROC として投影されたメソッドも論理モードで実行されます。SQL 文で関数として呼び出された SQL ルーチンは、論理形式で関数値を返す必要があります。
-
SELECT クエリでは、%SelectMode により、データの表示に使用する形式を指定します。%SelectMode を ODBC または表示に設定すると、比較述語の値の指定に使用されるデータ形式にも影響します。述語の値には、%SelectMode 形式で指定する必要があるものや、%SelectMode に関係なく、論理形式で指定する必要があるものがあります。詳細は、"Caché SQL リファレンス" の "述語の概要" を参照してください。
-
%SelectMode=1 の Time データ型 (ODBC) では秒の小数部を表示できます。これは実際の ODBC 時間とは異なります。Caché Time データ型は秒の小数部をサポートします。対応する ODBC TIME データ型 (TIME_STRUCT 標準ヘッダ定義) では秒の小数部はサポートされません。ODBC TIME データ型は、指定された時間値を整数秒に切り捨てます。ADO DotNet および JDBC では、この制限はありません。
-
%SelectMode=0 (論理) の %List データ型では、内部ストレージ値は表示されません。これは、%List データが出力不能文字を使用してエンコードされるためです。代わりに、ダイナミック SQL は %List データ値を $LISTBUILD 文として表示します。例えば、$lb("White","Green") のようになります。使用例は、"%Print() メソッド" を参照してください。%SelectMode=1 の %List データ型 (ODBC) は、リスト要素をコンマで区切って表示します。この要素区切り文字は、CollectionOdbcDelimiterOpens in a new tab パラメータとして指定されます。%SelectMode=2 の %List データ型 (表示) は、リスト要素を $CHAR(10,13) (改行、キャリッジ・リターン) で区切って表示します。この要素区切り文字は、CollectionDisplayDelimiterOpens in a new tab パラメータとして指定されます。
-
-
INSERT または UPDATE 操作では、%SelectMode により入力データを表示形式から論理格納形式に変換するかどうかを指定します。このデータ変換を行うには、SQL コードが RUNTIME 選択モードを使用してコンパイルされている必要があります実行時には、%SelectMode を 0 (論理) に設定する必要があります。詳細については、"Caché SQL リファレンス" の "INSERT" もしくは "UPDATE" 文を参照してください。
%SelectMode は、以下の 2 つの例に示すように、%New() クラス・メソッドの最初のパラメータとして指定するか、または直接設定できます。
SET tStatement = ##class(%SQL.Statement).%New(2)
SET tStatement = ##class(%SQL.Statement).%New()
SET tStatement.%SelectMode=2
以下の例は %SelectMode の現在の値を返します。
SET tStatement = ##class(%SQL.Statement).%New()
WRITE !,"default select mode=",tStatement.%SelectMode
SET tStatement.%SelectMode=2
WRITE !,"set select mode=",tStatement.%SelectMode
現在のプロセスの SelectMode の既定の設定を確認するには、$SYSTEM.SQL.GetSelectMode()Opens in a new tab メソッドを使用します。現在のプロセスの SelectMode の既定の設定を変更するには、$SYSTEM.SQL.SetSelectMode(n)Opens in a new tab メソッドを使用します。n には、0 = 論理、1 = ODBC、または 2 = 表示のいずれかを指定します。%SelectMode による設定は、現在のオブジェクト・インスタンスの既定の設定よりも優先して適用されます。SelectMode プロセスの既定の設定は変更されません。
SelectMode オプションの詳細は、このドキュメントの “Caché SQL の基礎” の章にある “データ表示オプション” を参照してください。
%SchemaPath プロパティ
%SchemaPathOpens in a new tab プロパティは、未修飾のテーブル名、ビュー名、またはストアド・プロシージャ名のスキーマ名の指定に使用する検索パスを指定します。検索パスは、単独のスキーマ名を引用符で囲んで指定するか、複数のスキーマ名をコンマで区切って記述したリストを引用符で囲んで指定します。このリストに記述したスキーマは左から右に検索されます。一致するテーブル名、ビュー名、またはストアド・プロシージャ名が検出されるまで、指定した各スキーマが検索されます。スキーマは指定された順序で検索されるため、あいまいなテーブル名は検出されません。検索対象となるのは、現在のネームスペースに存在するスキーマ名のみです。
スキーマ検索パスには、リテラルのスキーマ名と、CURRENT_PATH、CURRENT_SCHEMA、および DEFAULT_SCHEMA の各キーワードの両方を記述できます。
-
CURRENT_PATH は、前もって %SchemaPath プロパティで定義した現在のスキーマ検索パスを示します。これは、通常、既存のスキーマ検索パスの先頭または末尾にスキーマを付加するときに使用します。
-
CURRENT_SCHEMA は、%SQL.Statement 呼び出しがクラス・メソッド内から行われた場合の、現在のスキーマのコンテナのクラス名を指定します。クラスのメソッドで #SQLCompile Path マクロ指示文を定義している場合、CURRENT_SCHEMA は現在のクラス・パッケージにマップされたスキーマになります。そうでない場合には、CURRENT_SCHEMA は DEFAULT_SCHEMA と同じです。
-
DEFAULT_SCHEMA は、現在のネームスペースのシステム既定のスキーマを指定します。
%SchemaPath は、Caché が最初にスキーマ名を探す場所です。%SchemaPath が指定されていないか、または一致するスキーマ名がない場合は、Caché は管理ポータルの [システム管理]、[構成]、[SQL およびオブジェクトの設定]、[一般SQL設定] (システム, 構成, 一般SQL設定) で定義されるシステム全体の既定の SQL スキーマ名を使用します。詳細は、"システム全体のデフォルト・スキーマ" を参照してください。
%SchemaPath は、以下の 2 つの例に示すように、%New() クラス・メソッドの 2 番目のパラメータとして指定するか、または直接設定できます。
SET path="MyTests,Sample,Cinema"
SET tStatement = ##class(%SQL.Statement).%New(,path)
SET tStatement = ##class(%SQL.Statement).%New()
SET tStatement.%SchemaPath="MyTests,Sample,Cinema"
%SchemaPath は、このキーワードを使用する %Prepare() メソッドより前であればどの場所で設定してもかまいません。
以下の例は %SchemaPath の現在の値を返します。
SET tStatement = ##class(%SQL.Statement).%New()
WRITE !,"default path=",tStatement.%SchemaPath
SET tStatement.%SchemaPath="MyTests,Sample,Cinema"
WRITE !,"set path=",tStatement.%SchemaPath
%ClassPath()Opens in a new tab メソッドを使用して、%SchemaPath に、特定のクラス名に対して定義された検索パスを設定することができます。
ZNSPACE "SAMPLES"
SET tStatement = ##class(%SQL.Statement).%New()
SET tStatement.%SchemaPath=tStatement.%ClassPath("Sample.Person")
WRITE tStatement.%SchemaPath
%Dialect プロパティ
%DialectOpens in a new tab プロパティは、SQL 文の言語を指定します。Sybase または MSSQL を指定できます。この設定によって、指定した Transact-SQL 言語を使用して SQL 文が処理されます。
Sybase 言語および MSSQL 言語が言語内でサポートする SQL 文は限定されています。サポートされる文は、SELECT、INSERT、UPDATE、DELETE、および EXECUTE です。また、CREATE TABLE 文は、永続的なテーブルに対してはサポートされますが、一時テーブルに対してはサポートされません。CREATE VIEW はサポートされます。CREATE TRIGGER と DROP TRIGGER もサポートされます。ただし、この実装では、CREATE TRIGGER 文が部分的に成功してもクラス・コンパイルに対しては失敗する場合には、トランザクション・ロールバックはサポートされません。CREATE PROCEDURE と CREATE FUNCTION はサポートされます。
Sybase 言語および MSSQL 言語は、IF 制御フロー文をサポートします。このコマンドは Caché SQL 言語ではサポートされていません。
既定は Caché SQL です。これは、NULL と “CACHE“ のいずれかで表すことができます。
%Dialect は、以下の 3 つの例に示すように、%New() クラス・メソッドの 3 番目のパラメータとして指定するか、プロパティとして直接設定するか、またはメソッドを使用して設定することができます。
%New() クラス・メソッドで %Dialect を設定する :
SET tStatement = ##class(%SQL.Statement).%New(,,"Sybase")
WRITE "language mode set to=",tStatement.%Dialect
%Dialect プロパティを直接設定する :
SET tStatement = ##class(%SQL.Statement).%New()
SET defaultdialect=tStatement.%Dialect
WRITE "default language mode=",defaultdialect,!
SET tStatement.%Dialect="Sybase"
WRITE "language mode set to=",tStatement.%Dialect,!
SET tStatement.%Dialect="Cache"
WRITE "language mode reset to default=",tStatement.%Dialect,!
エラー・ステータスを返す %DialectSet()Opens in a new tab インスタンス・メソッドを使用して %Dialect プロパティを設定する :
SET tStatement = ##class(%SQL.Statement).%New()
SET tStatus = tStatement.%DialectSet("Sybase")
IF tStatus'=1 {WRITE "%DialectSet failed:" DO $System.Status.DisplayError(tStatus) QUIT}
WRITE "language mode set to=",tStatement.%Dialect
%DialectSet() メソッドは %Status 値を返します。成功の場合、1 のステータスを返します。失敗の場合、0 の後にエンコードされたエラー情報が続くオブジェクト式を返します。このため、失敗には tStatus=0 テストは実行できません。失敗には $$$ISOK(tStatus)=0 macro テストを実行できます。
%ObjectSelectMode プロパティ
%ObjectSelectModeOpens in a new tab プロパティはブーリアン値です。%ObjectSelectMode=0 (既定値) の場合、SELECT リストのすべての列が、結果セットでリテラル・タイプのプロパティに結合されます。%ObjectSelectMode= 1 の場合、SELECT リストの列は、関連付けたプロパティ定義で定義したタイプのプロパティに結合されます。
%ObjectSelectMode では、タイプ・クラスがスウィズル可能クラスである列を、SELECT 文から生成された結果セット・クラスでどのように定義するかを指定できます。%ObjectSelectMode=0 の場合、スウィズル可能列に対応するプロパティは、結果セットにおいて、SQL テーブルの ROWID タイプに対応する単純なリテラル・タイプとして定義されます。%ObjectSelectMode=1 の場合、そのプロパティは、その列の宣言されているタイプで定義されます。つまり、結果セットのプロパティにアクセスするとスウィズリングがトリガされます。
%ObjectSelectMode は、%New() のパラメータとしては設定できません。
以下の例は、%ObjectSelectMode の既定値を返し、%ObjectSelectMode を設定したうえで、その新しい %ObjectSelectMode 値を返します。
ZNSPACE "SAMPLES"
SET myquery = "SELECT TOP 5 %ID AS MyID,Name,Age FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New()
WRITE !,"default ObjectSelectMode=",tStatement.%ObjectSelectMode
SET tStatement.%ObjectSelectMode=1
WRITE !,"set ObjectSelectMode=",tStatement.%ObjectSelectMode
%ObjectSelectMode=1 は主に、フィールド名プロパティを使用して結果セットから値を返す際に使用します。これについては、この章の “結果セットからの特定値の返送” セクションの "フィールド名プロパティ" に、例を用いた詳しい説明があります。
SQL 文の作成
SQL 文の作成では、その文を検証し、以降の実行に向けて準備して、その SQL 文のメタデータを生成します。
文を作成するには、以下の 3 つの方法があります。
-
%Prepare() : 後続の %Execute() に対して SQL 文 (クエリなど) を作成します。
-
%PrepareClassQuery() : 既存のクエリへの呼び出し文を作成します。呼び出し文を作成したら、後続の %Execute() を使用してこのクエリを実行できます。
-
%ExecDirect() : SQL 文を作成および実行します。%ExecDirect() については、“SQL 文の実行” に説明があります。
SQL 文の準備では、文をクエリ・キャッシュに配置します。これにより、SQL 文を再準備する必要なしに同じクエリを複数回実行することができます。クエリ・キャッシュは、任意のプロセスによって 1 回以上実行することができます。異なる入力パラメータ値を使用して実行することができます。
SQL 文を準備するたびに、Caché はクエリ・キャッシュを検索して、同じ SQL 文が既に準備されキャッシュされているかどうかを判別します。準備されていない場合は、準備済みの文をクエリ・キャッシュに配置します。準備済みの文が既にクエリ・キャッシュ内に存在する場合は、新しいクエリ・キャッシュは作成されません。このため、ループ構造内で準備文をコード化しないことが重要です。
%Prepare()
SQL 文の作成には、%SQL.StatementOpens in a new tab クラスの %Prepare()Opens in a new tab インスタンス・メソッドを使用できます。%Prepare() メソッドは、最初のパラメータ数として SQL 文を取ります。この引数には、引用符付きの文字列、または引用符付きの文字列に解決される変数を指定できます。
例えば、ObjectScript では以下のようになります。
SET qStatus = tStatement.%Prepare("SELECT Name,Age FROM Sample.Person")
Caché Basic では次のようになります。
qStatus = tStatement.%Prepare("SELECT Name,Age FROM Sample.Person")
次の例に示すように、参照渡しの添え字付き配列を使用することによって、より複雑なクエリを指定できます。
SET myquery = 3
SET myquery(1) = "SELECT %ID AS id, Name, DOB, Home_State"
SET myquery(2) = "FROM Person WHERE Age > 80"
SET myquery(3) = "ORDER BY 2"
SET qStatus = tStatement.%Prepare(.myquery)
クエリには、重複するフィールド名とフィールド名エイリアスを含めることができます。
%Prepare() に渡されるクエリには、次の例で示すように入力ホスト変数を含めることができます。
SET minage = 80
SET myquery = 3
SET myquery(1) = "SELECT %ID AS id, Name, DOB, Home_State"
SET myquery(2) = "FROM Person WHERE Age > :minage"
SET myquery(3) = "ORDER BY 2"
SET qStatus = tStatement.%Prepare(.myquery)
Caché は SQL 文の実行時に、入力ホスト変数に、設定済みのリテラル値を代わりに使用します。ただし、このコードがメソッドとして呼び出された場合、minage 変数を Public にする必要があります。既定では、メソッドは ProcedureBlocks です。これは、メソッド (%Prepare() など) がその呼び出し元によって定義された変数を認識できないことを意味します。この既定をオーバーライドするには、クラスを [ Not ProcedureBlock ] として指定して、メソッドを [ ProcedureBlock = 0] として指定するか、または [ PublicList = minage ] を指定します。
プログラム記述時には、入力変数を SQL コードに挿入する前に、その変数に適切な値が含まれていることを必ず確認することをお勧めします。
? 入力パラメータを使用して、クエリにリテラル値を渡すこともできます。Caché は、%Execute() メソッドに渡された対応するパラメータ値を使用して、それぞれの ? 入力パラメータをリテラル値に置換します。%Prepare() の後に、%GetImplementationDetails() メソッドを使用して、クエリ内の ? 入力パラメータと入力ホスト変数をリスト表示できます。
%Prepare() メソッドは %Status 値を返します。成功の場合、1 のステータスを返します (クエリ文字列が有効で、参照したテーブルが現在のネームスペース内に存在しています)。失敗の場合、0 の後にエンコードされたエラー情報が続くオブジェクト式を返します。このため、失敗には status=0 テストは実行できません。失敗には $$$ISOK(status)=0 macro テストを実行できます。
%Prepare() メソッドは、先に定義されている %SchemaPath プロパティを使用して、修飾されていない名前を解決します。
ダイナミック SQL のパフォーマンスは、できる限り完全修飾名を使用することによって大幅に向上させることができます。
SQL 文で入力パラメータを指定するには、“? ” 文字を使用します。
SET myquery="SELECT TOP ? Name,Age FROM Sample.Person WHERE Age > ?"
SET qStatus = tStatement.%Prepare(myquery)
クエリの実行時に、%Execute() インスタンス・メソッドの各 "?" 入力パラメータに値を指定します。入力パラメータは、リテラル値を受け取るか、評価結果がリテラル値になる式を受け取る必要があります。入力パラメータは、フィールド名値やフィールド名エイリアスを受け取ることはできません。入力パラメータは、PUBLIC として宣言し、SELECT 文がそれを直接参照できるようにする必要があります。
クエリには、フィールドのエイリアスを含めることができます。この場合、Data プロパティは、フィールド名でなく、エイリアスを使用してデータにアクセスします。
ダイナミック SQL では、SELECT 文に限らず、%Prepare() インスタンス・メソッドを使用して、CALL、INSERT、UPDATE、DELETE などの他の SQL 文も作成できます。
現在作成している文の情報を表示するには、次の例に示すように、%Display()Opens in a new tab インスタンス・メソッドを使用します。
ZNSPACE "SAMPLES"
SET tStatement = ##class(%SQL.Statement).%New(,"Sample")
SET myquery = 3
SET myquery(1) = "SELECT TOP ? Name,DOB,Home_State"
SET myquery(2) = "FROM Person"
SET myquery(3) = "WHERE Age > 60 AND Age < 65"
SET qStatus = tStatement.%Prepare(.myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
DO tStatement.%Display()
WRITE !,"End of %Prepare display"
この情報は、実装クラス、引数 (リテラル値または ? 入力パラメータによる実際の引数のコンマ区切りのリスト)、および文のテキストで構成されます。
%PrepareClassQuery()
既存の SQL クエリを使用する場合は、%SQL.StatementOpens in a new tab クラスの %PrepareClassQuery()Opens in a new tab インスタンス・メソッドを使用できます。%PrepareClassQuery() メソッドは、既存のクエリのクラス名およびクエリ名の 2 つのパラメータを取ります。両方の引数に、引用符付きの文字列、または引用符付きの文字列に解決される変数を指定できます。
例えば、ObjectScript では以下のようになります。
SET qStatus = tStatement.%PrepareClassQuery("User.queryDocTest","DocTest")
Caché Basic では次のようになります。
qStatus = tStatement.%PrepareClassQuery("User.queryDocTest","DocTest")
%PrepareClassQuery() メソッドは %Status 値を返します。成功の場合、1 のステータスを返します。失敗の場合、0 の後にエンコードされたエラー情報が続くオブジェクト式を返します。このため、失敗には qStatus=0 テストは実行できません。失敗には $$$ISOK(qStatus)=0 macro テストを実行できます。
%PrepareClassQuery() メソッドは、先に定義されている %SchemaPath プロパティを使用して、修飾されていない名前を解決します。
%PrepareClassQuery() は、実行に CALL 文を使用します。このため、実行されたクラス・クエリには、SqlProc パラメータが存在する必要があります。
以下の例では、%PrepareClassQuery() は Sample.Person クラスで定義されている ByName クエリを呼び出しています。このとき、文字列を渡すことで、その文字列値で始まる名前を返すように制限しています。
SET statemt=##class(%SQL.Statement).%New()
SET cqStatus=statemt.%PrepareClassQuery("Sample.Person","ByName")
IF cqStatus'=1 {WRITE "%PrepareClassQuery failed:" DO $System.Status.DisplayError(cqStatus) QUIT}
SET rset=statemt.%Execute("L")
DO rset.%Display()
以下は、%PrepareClassQuery() を使用して既存のクエリを呼び出す例です。
SET tStatement=##class(%SQL.Statement).%New()
SET cqStatus=tStatement.%PrepareClassQuery("%SYS.GlobalQuery","Size")
IF cqStatus'=1 {WRITE "%PrepareClassQuery failed:" DO $System.Status.DisplayError(cqStatus) QUIT}
SET install=$SYSTEM.Util.InstallDirectory()
SET rset=tStatement.%Execute(install_"mgr\Samples")
DO rset.%Display()
以下の例では、CREATE QUERY 文を準備する %Prepare() を示してから、このクラス・クエリを呼び出す %PrepareClassQuery() を示します。
ZNSPACE "SAMPLES"
/* Creating the Query */
SET myquery=4
SET myquery(1)="CREATE QUERY DocTest() SELECTMODE RUNTIME PROCEDURE "
SET myquery(2)="BEGIN "
SET myquery(3)="SELECT TOP 5 Name,Home_State FROM Sample.Person ; "
SET myquery(4)="END"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(.myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
IF rset.%SQLCODE=0 { WRITE !,"Created a query",! }
ELSEIF rset.%SQLCODE=-361 { WRITE !,"Query exists: ",rset.%Message,! }
ELSE { WRITE !,"CREATE QUERY error: ",rset.%SQLCODE," ",rset.%Message QUIT}
/* Calling the Query */
WRITE !,"Calling a class query"
SET cqStatus = tStatement.%PrepareClassQuery("User.queryDocTest","DocTest")
IF cqStatus'=1 {WRITE !,"%PrepareClassQuery failed:" DO $System.Status.DisplayError(cqStatus) QUIT}
SET rset = tStatement.%Execute()
WRITE "Query data",!,!
WHILE rset.%Next() {
DO rset.%Print() }
WRITE !,"End of data"
/* Deleting the Query */
&sql(DROP QUERY DocTest)
IF SQLCODE=0 { WRITE !,"Deleted the query" }
格納されたクエリにより取得されるデータの行を表示するには、この例で示すように %Print() メソッドを使用できます。格納されたクエリにより取得された特定の列データを表示するには、%Get("fieldname") メソッドまたは %GetData(colnum) メソッドのいずれかを使用する必要があります。“結果セットの繰り返し処理” を参照してください。
引数を受け入れるようにクエリが定義されている場合、“?” 文字を使用して、SQL 文で入力パラメータを指定できます。クエリの実行時に、%Execute() メソッドの各 "?" 入力パラメータに値を指定します。入力パラメータは、PUBLIC として宣言し、SELECT 文がそれを直接参照できるようにする必要があります。
現在作成しているクエリの情報を表示するには、次の例に示すように、%Display() メソッドを使用します。
ZNSPACE "SAMPLES"
/* Creating the Query */
SET myquery=4
SET myquery(1)="CREATE QUERY DocTest() SELECTMODE RUNTIME PROCEDURE "
SET myquery(2)="BEGIN "
SET myquery(3)="SELECT TOP 5 Name,Home_State FROM Sample.Person ; "
SET myquery(4)="END"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(.myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
IF rset.%SQLCODE=0 { WRITE !,"Created a query",! }
ELSEIF rset.%SQLCODE=-361 { WRITE !,"Query exists: ",rset.%Message }
ELSE { WRITE !,"CREATE QUERY error: ",rset.%SQLCODE," ",rset.%Message QUIT}
/* Preparing and Displying Info about the Query */
WRITE !,"Preparing a class query"
SET cqStatus = tStatement.%PrepareClassQuery("User.queryDocTest","DocTest")
IF cqStatus'=1 {WRITE !,"%PrepareClassQuery failed:" DO $System.Status.DisplayError(cqStatus) QUIT}
DO tStatement.%Display()
WRITE !,"End of %Prepare display"
/* Deleting the Query */
&sql(DROP QUERY DocTest)
IF SQLCODE=0 { WRITE !,"Deleted the query" }
この情報は、実装クラス、引数 (リテラル値または ? 入力パラメータによる実際の引数のコンマ区切りのリスト)、および文のテキストで構成されます。
詳細は、"Caché オブジェクトの使用法" の “クラス・クエリの定義と使用” を参照してください。
作成が成功した場合の結果
作成が正常に完了したら (%Prepare()、%PrepareClassQuery()、または %ExecDirect())、%SQL.StatementOpens in a new tab %Display()Opens in a new tab インスタンス・メソッドまたは %GetImplementationDetails()Opens in a new tab インスタンス・メソッドを呼び出して、現在作成済みの文の結果を返すことができます。例えば、以下のようになります。
%Display() :
SET myquery = "SELECT TOP 5 Name,Age FROM Sample.Person WHERE Age > 21"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
DO tStatement.%Display()
SET rset = tStatement.%Execute()
%GetImplementationDetails() :
SET myquery = "SELECT TOP 5 Name,Age FROM Sample.Person WHERE Age > 21"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET bool = tStatement.%GetImplementationDetails(.pclassname,.ptext,.pargs)
IF bool=1 {WRITE "Implementation class= ",pclassname,!
WRITE "Statement text= ",ptext,!
WRITE "Arguments= ",$LISTTOSTRING(pargs),! }
ELSE {WRITE "%GetImplementationDetails() failed",!}
SET rset = tStatement.%Execute()
これらのメソッドは、以下の情報を提供します。
-
実装クラス : クエリ・キャッシュに対応するクラス名。例 : %sqlcq.SAMPLES.cls49。
-
引数 : クエリ引数の指定順のリスト。
%Display() は、クエリ引数のカンマ区切りリストを表示します。各引数には、リテラル値、入力ホスト変数の名前 (コロンなし)、または入力パラメータを表す疑問符 (?) を指定できます。引数がない場合、このアイテムには <<none>> と表示されます。IN または %INLIST のような複数の値を指定する述語では、各値は別個の引数としてリストされます。
%GetImplementationDetails() は、クエリ引数を %List 構造として返します。各引数は、タイプと値の要素のペアで表されます。タイプ c (定数) の後ろにはリテラル値が続き、タイプ v (変数) の後ろには入力ホスト変数 (コロンなし) の名前が続きます。タイプ ? は入力パラメータで、後ろに 2 つ目の疑問符が続きます。引数がない場合、引数リストは空の文字列になります。IN または %INLIST のような複数の値を指定する述語では、各値は別個のタイプと値のペアとしてリストされます。
-
文のテキスト : クエリ・テキスト。%Prepare() では、SELECT TOP ? Name FROM User.Clients のようになります。%PrepareClassQuery() では、call Sample.SP_Sample_By_Name(?) のようになります。
作成済みクエリに対して生成されるその他のメタデータ情報は、"SQL メタデータ" を参照してください。
SQL 文の実行
%SQL.StatementOpens in a new tab クラスを使用して SQL 文を実行する方法は 2 つあります。
-
%Execute() : %Prepare() または %PrepareClassQuery() を使用して作成済みの SQL 文を実行します。
-
%ExecDirect() : SQL 文を作成し、実行します。
%SYSTEM.SQL.Execute()Opens in a new tab メソッドを使用することにより、オブジェクト・インスタンスを作成せずに SQL 文を実行することもできます。このメソッドは、SQL 文の作成と実行の両方を行います。また、クエリ・キャッシュを作成します。Execute() メソッドについては、次のターミナルの例で示しています。
USER>SET topnum=5
USER>SET rset=$SYSTEM.SQL.Execute("SELECT TOP :topnum Name,Age FROM Sample.Person")
USER>DO rset.%Display()
%Execute()
クエリを作成した後は、%SQL.StatementOpens in a new tab クラスの %Execute()Opens in a new tab インスタンス・メソッドを呼び出すことで、そのクエリを実行できます。SELECT 以外の文の場合は、INSERT の実行などの目的の操作を %Execute() で呼び出すことができます。SELECT クエリの場合、以降の検索とデータ取得に使用する結果セットを %Execute() で生成できます。
例えば、ObjectScript では以下のようになります。
SET rset = tStatement.%Execute()
Caché Basic では次のようになります。
rset = tStatement.%Execute()
%Execute() メソッドは、すべての SQL 文に対して %SQL.StatementResultOpens in a new tab クラス・プロパティ %SQLCODEOpens in a new tab および %MessageOpens in a new tab を設定します。%Execute() は、以下のようにその他の %SQL.StatementResultOpens in a new tab プロパティを設定します。
-
INSERT、UPDATE、INSERT OR UPDATE、DELETE、および TRUNCATE TABLE 文は、操作によって影響を受ける行数に %ROWCOUNTOpens in a new tab を設定し、挿入、更新、または削除された最後のレコードに %ROWIDOpens in a new tab を設定します。
-
SELECT 文は、結果セットの作成時に %ROWCOUNTOpens in a new tab プロパティを 0 に設定します。%ROWCOUNT は、プログラムが結果セットのコンテンツを繰り返し処理を行うときに、(例えば %Next() メソッドを使用して) インクリメントされます。%Next() は、行上に配置されている場合は 1 を返し、最後の行の後 (結果セットの最後) に配置されている場合は 0 を返します。カーソルが最後の行の後に配置されている場合、%ROWCOUNT の値は結果セットに含まれる行数を示します。
SELECT クエリが集約関数のみを返す場合、すべての %Next() で %ROWCOUNT=1 が設定されます。最初の %Next() では、テーブルにデータがない場合でも、常に %SQLCODE=0 が設定されます。後続の %Next() では %SQLCODE=100 および %ROWCOUNT=1 が設定されます。
また、SELECT は %CurrentResultOpens in a new tab および %ResultColumnCountOpens in a new tab も設定します。SELECT は、%ROWID を設定しません。
詳細は、このドキュメントの “埋め込み SQL の使用法” の章の対応する SQL システム変数の説明を参照してください。%Dialect を Sybase または MSSQL に設定して TSQL コードを実行した場合、エラーはその SQL 言語の標準プロトコルと、Caché の %SQLCODE プロパティおよび %Message プロパティの両方で報告されます。
入力パラメータを持つ %Execute()
%Execute() メソッドは、作成された SQL 文の入力パラメータ (“?” に示される) に対応する 1 つ以上のパラメータを取得できます。%Execute() パラメータは、SQL 文内で “?” 文字が現れる順序に対応します。最初のパラメータは最初の “?” に対応し、2 番目のパラメータは、2 番目の “?” に対応し、以下同様に続きます。複数の %Execute() パラメータは、コンマで区切られます。プレースホルダとしてコンマを指定することによって、そのパラメータ値を省略できます。%Execute() パラメータの数は、“?” 入力パラメータ数と一致していなければなりません。%Execute() のパラメータの数が、対応する “?” 入力パラメータの数と一致しない場合は、%SQLCODE プロパティが SQLCODE -400 エラーに設定された状態でメソッドの実行に失敗します。
入力パラメータの個数に制限はありません。入力パラメータを使用して、リテラル値や式を SELECT リストや他のクエリ節 (TOP 節や WHERE 節など) に渡すことができます。入力パラメータを使用して、列名や列名エイリアスを SELECT リストや他のクエリ節に渡すことはできません。
作成に続き、作成の引数メタデータを使用して ? 入力パラメータのカウントと必要なデータ型を返すことができます。%GetImplementationDetails() メソッドを使用して、作成済みクエリ内の ? 入力パラメータのリストと、? 入力パラメータがコンテキスト内で示されたクエリ・テキストを返すことができます。
次の ObjectScript の例では、2 つの入力パラメータを持つクエリを実行します。%Execute() メソッドで入力パラメータ値 (21 と 26) を指定しています。
ZNSPACE "SAMPLES"
SET tStatement = ##class(%SQL.Statement).%New(1)
SET tStatement.%SchemaPath = "MyTests,Sample,Cinema"
SET myquery=2
SET myquery(1)="SELECT Name,DOB,Age FROM Person"
SET myquery(2)="WHERE Age > ? AND Age < ? ORDER BY Age"
SET qStatus = tStatement.%Prepare(.myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute(21,26)
WRITE !,"Execute OK: SQLCODE=",rset.%SQLCODE,!!
DO rset.%Display()
WRITE !,"End of data: SQLCODE=",rset.%SQLCODE
次の ObjectScript の例では、同じクエリを実行します。%Execute() メソッドは、dynamic dispatch (...) を使用して、不確定数の入力パラメータ値を指定できます。この場合は、dynd 配列の添え字です。dynd 変数は 2 に設定されていますが、これは、2 つの添え字値を示しています。
ZNSPACE "SAMPLES"
SET tStatement = ##class(%SQL.Statement).%New(1)
SET tStatement.%SchemaPath = "MyTests,Sample,Cinema"
SET myquery=2
SET myquery(1)="SELECT Name,DOB,Age FROM Person"
SET myquery(2)="WHERE Age > ? AND Age < ? ORDER BY Age"
SET dynd=2,dynd(1)=21,dynd(2)=26
SET qStatus = tStatement.%Prepare(.myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute(dynd...)
WRITE !,"Execute OK: SQLCODE=",rset.%SQLCODE,!!
DO rset.%Display()
WRITE !,"End of data: SQLCODE=",rset.%SQLCODE
作成された 1 つの結果セットに対して、複数の %Execute() 操作を発行できます。これにより、クエリを複数回、それぞれ異なる入力パラメータ値を指定して実行することが可能になります。次の例に示すように、複数の %Execute() 操作の間で結果セットを閉じる必要はありません。
ZNSPACE "SAMPLES"
SET myquery="SELECT Name,SSN,Age FROM Sample.Person WHERE Name %STARTSWITH ?"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute("A")
DO rset.%Display()
WRITE !,"End of A data",!!
SET rset = tStatement.%Execute("B")
DO rset.%Display()
WRITE !,"End of B data"
TRY/CATCH の使用による %Execute エラーの処理
TRY ブロック構造内でダイナミック SQL を実行して、関連付けられた CATCH ブロック例外ハンドラに実行時エラーを渡すことができます。%Execute() エラーの場合は、%Exception.SQLOpens in a new tab クラスを使用して例外インスタンスを作成してから、それを CATCH 例外ハンドラに THROW できます。
以下の例では、%Execute() エラーが発生したときに SQL 例外インスタンスを作成します。この場合は、? 入力パラメータの数 (1) と %Execute() のパラメータの数 (3) とでカーディナリティが一致しないというエラーが起こります。これにより、%SQLCODE プロパティと %Message プロパティの値 (Code および Data として) が CATCH 例外ハンドラにスローされます。この例外ハンドラは、%IsA() インスタンス・メソッドを使用して例外タイプをテストし、%Execute() エラーを表示します。
TRY {
ZNSPACE "SAMPLES"
SET myquery = "SELECT TOP ? Name,DOB FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute(7,9,4)
IF rset.%SQLCODE=0 { WRITE !,"Executed query",! }
ELSE { SET badSQL=##class(%Exception.SQL).%New(,rset.%SQLCODE,,rset.%Message)
THROW badSQL }
DO rset.%Display()
WRITE !,"End of data"
RETURN
}
CATCH exp { WRITE "In the CATCH block",!
IF 1=exp.%IsA("%Exception.SQL") {
WRITE "SQLCODE: ",exp.Code,!
WRITE "Message: ",exp.Data,! }
ELSE { WRITE "Not an SQL exception",! }
RETURN
}
%ExecDirect()
%SQL.StatementOpens in a new tab クラスには、クエリの作成と実行の両方を単一の操作で処理する %ExecDirect()Opens in a new tab クラス・メソッドがあります。これは、指定したクエリ (%Prepare() など) または既存のクラス・クエリ (%PrepareClassQuery() など) のいずれかを作成できます。
%ExecDirect() は、指定したクエリを作成して実行します。
SET myquery=2
SET myquery(1)="SELECT Name,Age FROM Sample.Person"
SET myquery(2)="WHERE Age > 21 AND Age < 30 ORDER BY Age"
SET rset = ##class(%SQL.Statement).%ExecDirect(,.myquery)
IF rset.%SQLCODE=0 { WRITE !,"ExecDirect OK",!! }
ELSE { WRITE !,"ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message QUIT}
DO rset.%Display()
WRITE !,"End of data: SQLCODE=",rset.%SQLCODE
%ExecDirect() は、既存のクラス・クエリを作成して実行します。
SET mycallq = "?=CALL Sample.PersonSets('A','NH')"
SET rset = ##class(%SQL.Statement).%ExecDirect(,mycallq)
IF rset.%SQLCODE=0 { WRITE !,"ExecDirect OK",!! }
ELSE { WRITE !,"ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message QUIT}
DO rset.%Display()
WRITE !,"End of data: SQLCODE=",rset.%SQLCODE
次の例に示すように、%ExecDirect() クラス・メソッドの 3 番目以降のパラメータとして入力パラメータの値を指定できます。
SET myquery=2
SET myquery(1)="SELECT Name,Age FROM Sample.Person"
SET myquery(2)="WHERE Age > ? AND Age < ? ORDER BY Age"
SET rset = ##class(%SQL.Statement).%ExecDirect(,.myquery,12,20)
IF rset.%SQLCODE'=0 {WRITE !,"1st ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message QUIT}
DO rset.%Display()
WRITE !,"End of teen data",!!
SET rset2 = ##class(%SQL.Statement).%ExecDirect(,.myquery,19,30)
IF rset2.%SQLCODE'=0 {WRITE !,"2nd ExecDirect SQLCODE=",rset2.%SQLCODE,!,rset2.%Message QUIT}
DO rset2.%Display()
WRITE !,"End of twenties data"
%ExecDirect() の入力パラメータは、SQL 文内で “?” 文字が現れる順序に対応します。すなわち、3 番目のパラメータは最初の “?” に対応し、4 番目のパラメータは、2 番目の “?” に対応し、以下同様に続きます。プレースホルダとしてコンマを指定することによって、そのパラメータ値を省略できます。%ExecDirect() の入力パラメータの数が、対応する “?” 入力パラメータの数より少ない場合、既定値が存在すればその既定値が使用されます。
次の例では、最初の %ExecDirect() では、3 つの “?” 入力パラメータのすべてを指定しています。2 番目の %ExecDirect() では、2 番目の ? 入力パラメータのみを指定し、1 番目と 3 番目のパラメータは省略しています。3 番目の入力パラメータには、Sample.PersonSets() の既定値 ('MA') が使用されます。
SET mycall = "?=CALL Sample.PersonSets(?,?)"
SET rset = ##class(%SQL.Statement).%ExecDirect(,mycall,"","A","NH")
IF rset.%SQLCODE'=0 {WRITE !,"1st ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message QUIT}
DO rset.%Display()
WRITE !,"End of A people data",!!
SET rset2 = ##class(%SQL.Statement).%ExecDirect(,mycall,,"B")
IF rset2.%SQLCODE'=0 {WRITE !,"2nd ExecDirect SQLCODE=",rset2.%SQLCODE,!,rset2.%Message QUIT}
DO rset2.%Display()
WRITE !,"End of B people data"
%ExecDirect() は、%SQL.StatementOpens in a new tab %Display()Opens in a new tab インスタンス・メソッドまたは %GetImplementationDetails()Opens in a new tab インスタンス・メソッドを呼び出して、現在作成済みの文の結果を返すことができます。%ExecDirect() は、指定したクエリまたは既存のクラス・クエリを作成して実行できるので、%GetImplementationDetails() pStatementType パラメータを使用して、作成されたクエリのタイプを判断できます。
SET mycall = "?=CALL Sample.PersonSets('A',?)"
SET rset = ##class(%SQL.Statement).%ExecDirect(tStatement,mycall,,"NH")
IF rset.%SQLCODE'=0 {WRITE !,"ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message QUIT}
SET bool = tStatement.%GetImplementationDetails(.pclassname,.ptext,.pargs,.pStatementType)
IF bool=1 {IF pStatementType=1 {WRITE "Type= specified query",!}
ELSEIF pStatementType=45 {WRITE "Type= existing class query",!}
WRITE "Implementation class= ",pclassname,!
WRITE "Statement text= ",ptext,!
WRITE "Arguments= ",$LISTTOSTRING(pargs),!! }
ELSE {WRITE "%GetImplementationDetails() failed"}
DO rset.%Display()
WRITE !,"End of data"
詳細は、"作成が成功した場合の結果" を参照してください。
完全な結果セットの返送
%Execute() または %ExecDirect() のいずれかで文を実行すると、%SQL.StatementResultOpens in a new tab インタフェースを実装するオブジェクトが返されます。このオブジェクトは、単一値、結果セット、または CALL 文から返されたコンテキスト・オブジェクトのいずれかです。
%Display() メソッド
%SQL.StatementResultOpens in a new tab クラスの %Display()Opens in a new tab インスタンス・メソッドを呼び出すことで、結果セット (結果オブジェクトの内容) 全体を表示できます。
例えば、ObjectScript では以下のようになります。
DO rset.%Display()
Caché Basic では次のようになります。
CALL rset.%Display()
%Display() メソッドは %Status 値を返さない点に注意してください。
%Display() によるクエリ結果セットの表示の最後には、“影響を受けた行数 5” のように行数が表示されます (これは、%Display が結果セットを繰り返し処理した後の %ROWCOUNT 値です)。%Display() では、行数を通知するこの文の後には改行が発行されません。
%DisplayFormatted() メソッド
%Display() を呼び出すのではなく、%SQL.StatementResultOpens in a new tab クラスの %DisplayFormatted()Opens in a new tab インスタンス・メソッドを呼び出すことで、結果セットの内容の再フォーマットおよび生成されたファイルへのリダイレクトができます。
文字列オプション %DisplayFormatted("HTML") または対応する整数コード %DisplayFormatted(1) のいずれかを指定することにより、結果セットのフォーマットを指定できます。利用可能なフォーマットは、XML (整数コード 0)、HTML (整数コード 1)、PDF (整数コード 2)、TXT (整数コード 99)、または CSV (整数コード 100) です (CSV フォーマットは、本来のカンマ区切り値出力としては実装されておらず、列の区切りにタブを使用していることに注意してください)。TXT 形式 (整数コード 99) の最後には、(“影響を受けた行数 5” のように) 行数が表示されますが、他の形式には行数は含まれません。Caché は、指定したタイプのファイルを生成し、適切なファイル名拡張子を付けます。
結果セットのファイル名は指定しても省略してもかまいません。
-
宛先ファイル (例えば %DisplayFormatted(99,"myresults")) を指定した場合、指定した名前と適切な接尾辞 (ファイル名拡張子) を持つファイルが、現在のネームスペースのサブディレクトリ内にある mgr ディレクトリに生成されます。例えば、C:\InterSystems\Cache\mgr\user\myresults.txt です。その接尾辞を持つ指定ファイルが既に存在する場合は、Caché によってファイルが新しいデータで上書きされます。
-
宛先ファイル (例えば %DisplayFormatted(99)) を指定していない場合、ランダムに生成された名前と適切な接尾辞 (ファイル名拡張子) を持つファイルが、mgr ディレクトリ内にある Temp サブディレクトリに生成されます。例えば、C:\InterSystems\Cache\mgr\Temp\w4FR2gM7tX2Fjs.txt です。クエリが実行されるたびに、新しい宛先ファイルが生成されます。
以下に、Windows のファイル名の例を示します。Caché は以下のその他のオペレーティング・システムの同等の場所をサポートします。
指定したファイルを開くことができない場合、この操作は 30 秒後にタイムアウトしてエラー・メッセージが表示されます。これは、指定されたディレクトリ (ファイル・フォルダ) に対する書き込み権限をユーザが持っていない場合によく発生します。
必要に応じて、指定された形式変換の実行時に %DisplayFormatted() によって使用される、変換テーブルの名前を指定できます。
結果セット・シーケンス内に複数の結果セットがある場合は、各結果セットの内容は、それぞれ固有のファイルに書き込まれます。メッセージは、別個のファイルに書き込まれます。
以下の Windows の例では、2 つの PDF (整数コード 2) 結果セット・ファイルが C:\InterSystems\Cache\mgr\user\ 内に作成されます。
ZNSPACE "SAMPLES"
SET myquery=2
SET myquery(1)="SELECT Name,Age FROM Sample.Person"
SET myquery(2)="WHERE Age > ? AND Age < ? ORDER BY Age"
SET rset = ##class(%SQL.Statement).%ExecDirect(,.myquery,12,20)
IF rset.%SQLCODE'=0 {WRITE !,"1st ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message QUIT}
DO rset.%DisplayFormatted(2,"Teenagers")
WRITE !,"End of teen data",!!
SET rset2 = ##class(%SQL.Statement).%ExecDirect(,.myquery,19,30)
IF rset2.%SQLCODE'=0 {WRITE !,"2nd ExecDirect SQLCODE=",rset2.%SQLCODE,!,rset2.%Message QUIT}
DO rset2.%DisplayFormatted(2,"Twenties")
WRITE !,"End of twenties data"
結果セットのページ付け
ビュー ID (%VID) を使用して、結果セットのページ付けを行うことができます。次の例は、結果セットからページを返します。各ページに 5 行ずつ格納されています。
ZNSPACE "SAMPLES"
SET q1="SELECT %VID AS RSRow,* FROM "
SET q2="(SELECT Name,Home_State FROM Sample.Person WHERE Home_State %STARTSWITH 'M') "
SET q3="WHERE %VID BETWEEN ? AND ?"
SET myquery = q1_q2_q3
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus=tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
FOR i=1:5:25 {
WRITE !!,"Next Page",!
SET rset=tStatement.%Execute(i,i+4)
DO rset.%Display()
}
結果セットからの特定値の返送
クエリ結果セットから特定の値を返すには、一度に 1 行ずつ繰り返し結果セットを処理する必要があります。結果セットを繰り返し処理するには、%Next()Opens in a new tab インスタンス・メソッドを使用します (単一値の場合、結果オブジェクトに行が存在しないため、%Next() は 0 を返しますが、これはエラーではありません)。そして、%Print()Opens in a new tab メソッドを使用して現在の行全体の結果を表示するか、または現在の行内で指定列の値を取得することができます。
%Next() メソッドは、クエリの結果の次の行のデータを取り出し、そのデータを結果セット・オブジェクトの Data プロパティに入れます。%Next() は、クエリ結果内の行に配置されていることを示す場合は 1 を返します。%Next() は、最後の行の後 (結果セットの最後) に配置されている場合は 0 を返します。1 を返す %Next() が呼び出されるたびに、%ROWCOUNT がインクリメントされます。カーソルが最後の行の後に配置されている場合 (%Next() が 0 を返す)、%ROWCOUNT は結果セット内の行数を示します。
SELECT クエリが集約関数のみを返す場合、すべての %Next() で %ROWCOUNT=1 が設定されます。最初の %Next() では、テーブルにデータがない場合でも、1 が返され、%SQLCODE=0 および %ROWCOUNT=1 が設定されます。後続の %Next() では 0 が返され、%SQLCODE=100 および %ROWCOUNT=1 が設定されます。
結果セットから行を取得した後、以下のいずれかの方法で、その行から返されたデータを表示できます。
-
rset.%Print() : クエリ結果セットから、現在行のすべてのデータ値を返します。
-
rset.name : クエリ結果セットから、プロパティ名、フィールド名、エイリアス・プロパティ名、またはエイリアス・フィールド名によりデータ値を返します。
-
rset.%Get("fieldname") : クエリ結果セットまたは格納クエリのいずれかから、フィールド名またはエイリアス・フィールド名によりデータ値を返します。
-
%GetData() : クエリ結果セットまたは格納クエリのいずれかから、列番号によりデータ値を返します。
%Print() メソッド
%Print()Opens in a new tab インスタンス・メソッドは、結果セットにある現在のレコードを取得します。既定では、%Print() は、データ・フィールド値の間に空白の区切り文字を挿入します。%Print() は、レコードの最初のフィールド値の前や最後のフィールド値の後には空白を挿入しません。レコードの末尾では改行を発行します。データ・フィールド値に既に空白が含まれている場合、そのフィールド値は、区切り文字と区別するために引用符で囲まれます。例えば、%Print() が都市名を返す場合は、次のように返します。Chicago "New York" Boston Atlanta "Los Angeles" "Salt Lake City" Washington。%Print() は、結果セットにフィールドが 1 つしかない場合など、%Print() 区切り文字が使用されていないときでも、データ値の一部として区切り文字が含まれているフィールド値を引用符で囲みます。
%Print() では、フィールド値の間に配置する別の区切り文字を指定するオプションのパラメータを指定できます。別の区切り文字を指定すると、空白を含むデータ文字列の引用符はオーバーライドされます。この %Print() 区切り文字には 1 つ以上の文字を使用できます。区切り文字は、引用符で囲んだ文字列として指定します。一般に、%Print() 区切り文字には、結果セットのデータでは見かけない文字や文字列を指定することが推奨されます。ただし、結果セットのフィールド値に %Print() 区切り文字 (または文字列) が含まれる場合、そのフィールド値は、区切り文字と区別するために、引用符で囲んで返されます。
結果セットのフィールド値に改行文字が含まれる場合、フィールド値は引用符で区切って返されます。
以下の ObjectScript の例では、この区切り文字として "^|^" を指定したうえで %Print() を使用してクエリ結果セットの各レコードを表示し、この結果セットを繰り返し処理しています。%Print() で、要素のエンコードされたリストである FavoriteColors フィールドからどのようにデータが表示されるかに注意してください。
ZNSPACE "SAMPLES"
SET q1="SELECT TOP 5 Name,DOB,Home_State,FavoriteColors "
SET q2="FROM Sample.Person WHERE FavoriteColors IS NOT NULL"
SET myquery = q1_q2
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
WHILE rset.%Next() {
WRITE "Row count ",rset.%ROWCOUNT,!
DO rset.%Print("^|^")
}
WRITE !,"End of data"
WRITE !,"Total row count=",rset.%ROWCOUNT
Caché Basic では、以下のとおりです。
q1 = "SELECT TOP 5 Name,DOB,Home_State,FavoriteColors "
q2 = "FROM Sample.Person WHERE FavoriteColors IS NOT NULL"
myquery = q1 & q2
tStatement = New %SQL.Statement()
qStatus = tStatement.%Prepare(myquery)
If qStatus<>1 Then
PrintLn "%Prepare failed:"
PrintLn Piece(qStatus," ",3,10)
Else
rset = tStatement.%Execute()
While (rset.%Next())
PrintLn "Row count ",rset.%ROWCOUNT
rtn=rset.%Print("^|^")
Wend
PrintLn "End of data"
PrintLn "Total row count=",rset.%ROWCOUNT
End If
以下の例では、区切り文字を含むフィールド値が引用符で囲んで返される様子を示します。この例では、大文字の A がフィールド区切り文字として使用されています。そのため、大文字 A のリテラルを含むフィールド値 (名前、番地、または州の省略形) は、引用符で区切られて返されます。
ZNSPACE "SAMPLES"
SET myquery = "SELECT TOP 25 Name,Home_Street,Home_State,Age FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
WHILE rset.%Next() {
DO rset.%Print("A")
}
WRITE !,"End of data"
WRITE !,"Total row count=",rset.%ROWCOUNT
rset.name プロパティ
Caché は結果セットを生成するときに、その結果セット内のそれぞれのフィールド名とフィールド名エイリアスに対応する固有のプロパティが含まれた結果セット・クラスを作成します。
rset.name プロパティを使用すると、プロパティ名、フィールド名、プロパティ名エイリアス、またはフィールド名エイリアスでデータ値を返すことができます。
-
プロパティ名 : フィールド・エイリアスが定義されていない場合、フィールド・プロパティ名は rset.PropName と指定します。結果セットのフィールド・プロパティ名は、テーブル定義クラス内の対応するプロパティ名から取られます。
-
フィールド名 : フィールド・エイリアスが定義されていない場合、フィールド名 (またはプロパティ名) は rset."fieldname" と指定します。これはテーブル定義で指定されている SqlFieldName です。Caché はこのフィールド名を使用して、対応するプロパティ名を見つけます。多くの場合、プロパティ名とフィールド名 (SqlFieldName) は同一となります。
-
エイリアス・プロパティ名 : フィールド・エイリアスが定義されている場合、エイリアス・プロパティ名は rset.AliasProp と指定します。エイリアス・プロパティ名は、SELECT 文の列名エイリアスから生成されます。定義されたエイリアスがあるフィールドのフィールド・プロパティ名は指定できません。
-
エイリアス名 : フィールド・エイリアスが定義されている場合、このエイリアス名 (またはエイリアス・プロパティ名) は rset."alias" と指定します。これは SELECT 文内の列名エイリアスです。定義されたエイリアスがあるフィールドのフィールド名は指定できません。
-
集約、式、またはサブクエリ:Caché はこれらの select-items に Aggregate_n、Expression_n、または Subquery_n というフィールド名を割り当てます (整数 n はクエリで指定された select-item リストの順序に対応しています)。これらの select-item の値を取得するには、フィールド名 (rset."SubQuery_7" : 大文字と小文字の区別なし)、対応するプロパティ名 (rset.Subquery7 : 大文字と小文字の区別あり)、またはユーザ定義のフィールド名エイリアスを使用します。rset.%GetData(n) を使用して、select-item のシーケンス番号のみを指定することもできます。
プロパティ名を指定するときには、大文字/小文字を正しく使用する必要があります。フィールド名を指定するときには、大文字/小文字を区別する必要はありません。
プロパティ名を使用した rset.name のこの呼び出しは、以下のような結果になります。
-
大文字/小文字 : プロパティ名は大文字/小文字を区別します。フィールド名は、大文字と小文字が区別されません。ダイナミック SQL が、指定されたフィールドまたはエイリアスの名前と、対応するプロパティ名との間の大文字/小文字の相違を自動的に解決します。ただし、大文字/小文字の解決には時間がかかります。パフォーマンスを最大化するには、プロパティ名またはエイリアスの大文字/小文字を正しく指定する必要があります。
-
非英数字 : プロパティ名には英数字のみを含めることができます (先頭の % 文字は除く)。対応する SQL フィールド名またはフィールド名エイリアスに非英数字が含まれている場合 (例えば Last_Name など)、以下のいずれかのようにすることができます。
-
フィールド名を引用符で囲んで指定します。例えば、rset."Last_Name" などとします。このような区切り文字の使用においては、区切り識別子を有効とする必要はありません。大文字/小文字の解決が実行されます。
-
対応するプロパティ名を、非英数字を削除して指定します。例えば、rset.LastName (または rset."LastName") などとします。プロパティ名には大文字/小文字を正しく指定する必要があります。
-
-
% プロパティ名 : 一般に、% 文字で始まるプロパティ名は、システムによる使用のために予約されています。フィールド・プロパティ名またはエイリアスが % 文字で始まり、その名前がシステム定義のプロパティと競合する場合、システム定義のプロパティが返されます。例えば、SELECT Notes AS %Message の場合、rset.%Message を呼び出すと、Notes フィールドの値ではなく、文の結果クラスに対して定義されている %MessageOpens in a new tab プロパティが返されます。フィールド値が返されるようにするには、rset.%Get("%Message") を使用できます。
-
列エイリアス : エイリアスが指定されている場合、ダイナミック SQL は、フィールド名またはフィールド・プロパティ名とではなく、必ずエイリアスとマッチングします。例えば、SELECT Name AS Last_Name の場合、データは rset.Name を使用してではなく、rset.LastName または rset."Last_Name" を使用してのみ取得できます。
-
重複名 : 複数の名前が同一のプロパティ名に解決されると、それらは重複します。重複名は、単一テーブル内の同じフィールドを複数参照するか、単一テーブル内の複数のフィールドをエイリアス参照するか、または複数のテーブル内の複数のフィールドを参照する可能性があります。例えば、SELECT p.DOB,e.DOB は 2 つの重複名を指定していますが、これらの名前は複数のテーブル内のフィールドを参照しています。
SELECT 文に同じフィールド名またはフィールド名エイリアスの複数のインスタンスが含まれている場合、rset.PropName または rset."fieldname" は必ず、SELECT 文で指定された最初のインスタンスを返します。例えば、SELECT c.Name,p.Name FROM Sample.Person AS p,Sample.Company AS c の場合に、rset.Name を使用すると、会社名フィールドのデータを取得します。SELECT c.Name,p.Name AS Name FROM Sample.Person AS p,Sample.Company AS c の場合、rset."name" を使用したときも、会社名フィールドのデータを取得します。クエリ内に重複する Name フィールドがある場合、フィールド名 (Name) の最後の文字は、一意のプロパティ名を作成するための文字 (複数の文字の場合あり) に置換されます。このため、クエリ内の重複する Name フィールド名には、対応する一意のプロパティ名があり、これは Nam0 (最初の重複) から始まり Nam9 まで続き、さらに大文字の付いた NamA から NamZ へと続きます。
%Prepare()Opens in a new tab を使用して作成されたユーザ指定クエリでは、それ自体でプロパティ名を使用できます。%PrepareClassQuery()Opens in a new tab を使用して作成された格納クエリでは、%Get("fieldname")Opens in a new tab メソッドを使用する必要があります。
以下の例では、プロパティ名で指定された 3 つのフィールドの値を返します。その内の 2 つのフィールド値はプロパティ名によるもの、3 つ目のフィールド値はエイリアス・プロパティ名によるものとなります。これらの場合、指定されたプロパティ名はフィールド名またはフィールド・エイリアスと同一になります。
ZNSPACE "SAMPLES"
SET myquery = "SELECT TOP 5 Name,DOB AS bdate,FavoriteColors FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New(1)
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
WHILE rset.%Next() {
WRITE "Row count ",rset.%ROWCOUNT,!
WRITE rset.Name
WRITE " prefers ",rset.FavoriteColors
WRITE " birth date ",rset.bdate,!!
}
WRITE !,"End of data"
WRITE !,"Total row count=",rset.%ROWCOUNT
上記の例で、返されるフィールドの 1 つは FavoriteColors フィールドです。これには %List データが含まれます。このデータを表示するため、%New(1) クラス・メソッドにより %SelectMode プロパティ・パラメータが 1 (ODBC) に設定され、このプログラムが %List データをコンマで区切られた文字列および ODBC 形式の生年月日として表示するようにしています。
以下の例では、Home_State フィールドを返します。プロパティ名にアンダースコア文字を含めることができないため、この例では引用符で区切られたフィールド名 (SqlFieldName) を指定しています ("Home_State")。また、引用符なしで対応する生成プロパティ名を指定することもできます (HomeState)。区切られたフィールド名 ("Home_State") は大文字と小文字が区別されないが、生成されたプロパティ名 (HomeState) では区別されることに注意してください。
ZNSPACE "SAMPLES"
SET myquery = "SELECT TOP 5 Name,Home_State FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New(2)
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
WHILE rset.%Next() {
WRITE "Row count ",rset.%ROWCOUNT,!
WRITE rset.Name
WRITE " lives in ",rset."Home_State",!
}
WRITE !,"End of data"
WRITE !,"Total row count=",rset.%ROWCOUNT
%ObjectSelectMode=1 を使用したフィールド名・プロパティのスウィズリング
以下の例は %ObjectSelectMode=1 で作成したもので、これによりタイプ・クラスがスウィズル可能な型 (永続クラス、シリアル・クラス、またはストリーム・クラス) であるフィールドが、フィールド名プロパティを使用して値を返す際に自動的にスウィズルします。フィールド値のスウィズリングの結果は対応するオブジェクト参照 (oref) となります。Caché では、%Get() または %GetData() メソッドを使用してフィールドにアクセスした場合、スウィズリング処理は実行されません。この例では、rset.Home がスウィズルされても、同じフィールドを参照する rset.%GetData(2) はスウィズルされません。
ZNSPACE "SAMPLES"
SET myquery = "SELECT TOP 5 Name,Home FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New(0)
SET tStatement.%ObjectSelectMode=1
WRITE !,"set ObjectSelectMode=",tStatement.%ObjectSelectMode,!
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
WHILE rset.%Next() {
WRITE "Row count ",rset.%ROWCOUNT,!
WRITE rset.Name
WRITE " ",rset.Home,!
WRITE rset.%GetData(1)
WRITE " ",$LISTTOSTRING(rset.%GetData(2)),!!
}
WRITE !,"End of data"
WRITE !,"Total row count=",rset.%ROWCOUNT
以下の例では、%ObjectSelectMode に 1 を指定して、選択したレコードの Home_State 値を一意のレコード ID (%ID) から取得しています。ここでは、元のクエリで Home_State フィールドが選択されていません。
ZNSPACE "SAMPLES"
SET myquery = "SELECT TOP 5 %ID AS MyID,Name,Age FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New()
SET tStatement.%ObjectSelectMode=1
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
WHILE rset.%Next() {
WRITE rset.Name
WRITE " Home State:",rset.MyID.Home.State,!
}
WRITE !,"End of data"
WRITE !,"Total row count=",rset.%ROWCOUNT
スウィズルされるプロパティが定義されていても参照できない場合、<SWIZZLE FAIL> エラーが生成されます。これは参照されるプロパティがディスクから予期せず削除されてしまった場合、もしくは他のプロセスによりロックされた場合に起こり得ます。スウィズル失敗の原因を判別するには、<SWIZZLE FAIL> エラーの直後に %objlasterror を調べて、この %Status の値をデコードします。
SET ^%SYS("ThrowSwizzleError")=0 に設定するか、Caché 管理ポータルを使用すると、この動作をグローバルでオフにできます。[システム管理] から、[構成]、[SQL およびオブジェクトの設定]、[一般オブジェクト設定] (システム, 構成, 一般オブジェクト設定) を選択します。この画面で、<SWIZZLE FAIL> オプションを設定できます。
%Get("fieldname") メソッド
フィールド名またはフィールド名エイリアスでデータ値を返すには、%Get("fieldname")Opens in a new tab インスタンス・メソッドを使用できます。ダイナミック SQL は、必要に応じて大文字/小文字を解決します。指定したフィールド名またはフィールド名エイリアスが存在しない場合、<PROPERTY DOES NOT EXIST> エラーが生成されます。
以下の例は、クエリ結果セットから、Home_State フィールドの値と Last_Name エイリアスの値を返します。
ZNSPACE "SAMPLES"
SET myquery = "SELECT TOP 5 Home_State,Name AS Last_Name FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New(2)
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
WHILE rset.%Next() {
WRITE rset.%Get("Home_State")," : ",rset.%Get("Last_Name"),!
}
WRITE !,"End of data"
WRITE !,"Total row count=",rset.%ROWCOUNT
%PrepareClassQuery()Opens in a new tab を使用して作成された既存クエリから、フィールド・プロパティ名により個別のデータ項目を取得するには、%Get("fieldname")Opens in a new tab インスタンス・メソッドを使用する必要があります。フィールド・プロパティ名が存在しない場合、<PROPERTY DOES NOT EXIST> エラーが生成されます。
以下の例では、組み込みのクエリからフィールド・プロパティ名により Nsp (ネームスペース) フィールドを返します。このクエリは既存の格納クエリなので、このフィールド取得には %Get("fieldname") メソッドを使用する必要があります。"Nsp" はプロパティ名なので、大文字と小文字が区別されることに注意してください。
SET tStatement = ##class(%SQL.Statement).%New(2)
SET qStatus = tStatement.%PrepareClassQuery("%SYS.Namespace","List")
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
WHILE rset.%Next() {
WRITE "Namespace: ",rset.%Get("Nsp"),!
}
WRITE !,"End of data"
WRITE !,"Total row count=",rset.%ROWCOUNT
重複名 : 複数の名前が同一のプロパティ名に解決されると、それらは重複します。重複名は、同じフィールドを複数参照するか、1 テーブル内の異なるフィールドを参照するか、または異なるテーブル内の複数のフィールドを参照する可能性があります。SELECT 文に同じフィールド名またはフィールド名エイリアスの複数のインスタンスが含まれている場合、%Get("fieldname") は必ず、クエリで指定された重複名の最後のインスタンスを返します。これは、クエリで指定された重複名の最初のインスタンスを返す rset.PropName とは逆です。以下に例を示します。
ZNSPACE "SAMPLES"
SET myquery = "SELECT c.Name,p.Name FROM Sample.Person AS p,Sample.Company AS c"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
WHILE rset.%Next() {
WRITE "Prop=",rset.Name," Get=",rset.%Get("Name"),! }
WRITE !,rset.%ROWCOUNT," End of data"
%GetData(n) メソッド
%GetData(n)Opens in a new tab インスタンス・メソッドは、結果セットの整数値列番号でインデックス指定された現在の行に対してデータを返します。%Prepare() を使用して作成された指定クエリ、もしくは %PrepareClassQuery() を使用して作成された格納クエリのいずれかで %GetData(n) を使用できます。
整数 n はクエリ内で指定された select-item リストのシーケンスに対応しています。ID フィールドには、select-item リストで明示的に指定されていない限り、整数 n の値は示されません。n が、クエリ内の select-item の数より大きいか、0 であるか、または負数である場合、ダイナミック SQL は値を返さず、エラーも発行しません。
%GetData(n) は、特定の重複フィールド名または重複エイリアスを返す唯一の方法です。rset.Name は最初の重複を返し、%Get("Name") は最後の重複を返します。
ObjectScript では以下のようになります。
ZNSPACE "SAMPLES"
SET myquery="SELECT TOP 5 Name,SSN,Age FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
WHILE rset.%Next() {
WRITE "Years:",rset.%GetData(3)," Name:",rset.%GetData(1),!
}
WRITE "End of data"
WRITE !,"Total row count=",rset.%ROWCOUNT
Caché Basic では、以下のとおりです。
myquery = "SELECT TOP 5 Name,SSN,Age FROM Sample.Person"
tStatement = New %SQL.Statement()
qStatus = tStatement.%Prepare(myquery)
If qStatus<>1 Then
PrintLn "%Prepare failed:"
PrintLn Piece(qStatus," ",3,10)
Else
rset = tStatement.%Execute()
While (rset.%Next())
Print "Years:",rset.%GetData(3)
PrintLn " Name:",rset.%GetData(1)
Wend
PrintLn "End of data"
PrintLn "Total row count=",rset.%ROWCOUNT
End If
複数の結果セットの返送
CALL 文は、結果セット・シーケンス (RSS) と呼ばれる集合として複数のダイナミック結果セットを返すことができます。
次の例では、%NextResult()Opens in a new tab メソッドを使用して複数の結果セットを別々に返します。
ZNSPACE "SAMPLES"
SET mycall = "CALL Sample.CustomSets()"
SET rset = ##class(%SQL.Statement).%ExecDirect(,mycall)
IF rset.%SQLCODE'=0 {WRITE !,"ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message QUIT}
SET rset1=rset.%NextResult()
DO rset1.%Display()
WRITE !,"End of 1st Result Set data",!!
SET rset2=rset.%NextResult()
DO rset2.%Display()
WRITE !,"End of 2nd Result Set data"
SQL メタデータ
ダイナミック SQL では、以下のタイプのメタデータが用意されています。
-
作成後、クエリ引数を記述するメタデータ : ? パラメータ、:var パラメータ、および定数
%SQL.StatementMetadataOpens in a new tab プロパティ値は、作成操作 (%Prepare()、%PrepareClassQuery()、または %ExecDirect()) の後に利用可能です。
-
最新の %Prepare() の %SQL.StatementMetadataOpens in a new tab プロパティを直接返すことができます。
-
%SQL.StatementMetadataOpens in a new tab プロパティの oref が含まれる %SQL.StatementOpens in a new tab %Metadata プロパティを返すことができます。これにより、複数の作成操作のメタデータを返すことができます。
文タイプのメタデータ
以下の例のように、%SQL.StatementOpens in a new tab クラスを使用した作成の後に、%SQL.StatementMetadataOpens in a new tab statementType プロパティを使用して、作成された SQL 文のタイプを判断できます。この例では、%SQL.StatementOpens in a new tab %Metadata プロパティを使用して、2 つの作成操作のメタデータを保持して比較しています。
SET tStatement = ##class(%SQL.Statement).%New()
SET myquery1 = "SELECT TOP ? Name,Age,AVG(Age),CURRENT_DATE FROM Sample.Person"
SET myquery2 = "CALL Sample.SP_Sample_By_Name(?)"
SET qStatus = tStatement.%Prepare(myquery1)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET meta1 = tStatement.%Metadata
SET qStatus = tStatement.%Prepare(myquery2)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET meta2 = tStatement.%Metadata
WRITE "Statement type query 1: ",meta1.statementType,!
WRITE "Statement type query 2: ",meta2.statementType,!
WRITE "End of metadata"
statementTypeOpens in a new tab プロパティの Class Reference エントリは、文タイプの整数コードをリストします。最も一般的なコードは 1 (SELECT クエリ) と 45 (ストアド・クエリの呼び出し) です。
"作成が成功した場合の結果" で説明されているように、%GetImplementationDetails()Opens in a new tab インスタンス・メソッドを使用して同じ情報を返すことができます。
クエリの実行後、結果セットから 文のタイプ名 (例えば SELECT) を返すことができます。
select-item メタデータ
%SQL.StatementOpens in a new tab クラスを使用した作成の後に、そのクエリで指定されている各 select-item 列に関するメタデータを返すことができます。そのためには、メタデータをすべて表示するか、個々のメタデータ項目を指定します。列のメタデータには、ODBC データ型情報と、クライアント・タイプ、InterSystems オブジェクトのプロパティの起源、およびクラス・タイプ情報が含まれます。
以下の例は、最後に作成されたクエリで指定されている列の数を返します。
SET myquery = "SELECT %ID AS id,Name,DOB,Age,AVG(Age),CURRENT_DATE,Home_State FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
WRITE "Number of columns=",tStatement.%Metadata.columnCount,!
WRITE "End of metadata"
以下の例は、各 select-item フィールドの列名 (または列のエイリアス)、ODBC データ型、最大データ長 (精度)、およびスケールを返します。
ZNSPACE "SAMPLES"
SET myquery=2
SET myquery(1)="SELECT Name AS VendorName,LastPayDate,MinPayment,NetDays,"
SET myquery(2)="AVG(MinPayment),$HOROLOG,%TABLENAME FROM Sample.Vendor"
SET rset = ##class(%SQL.Statement).%New()
SET qStatus = rset.%Prepare(.myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET x=rset.%Metadata.columns.Count()
SET x=1
WHILE rset.%Metadata.columns.GetAt(x) {
SET column=rset.%Metadata.columns.GetAt(x)
WRITE !,x," ",column.colName," is data type ",column.ODBCType
WRITE " with a size of ",column.precision," and scale = ",column.scale
SET x=x+1 }
WRITE !,"End of metadata"
以下の例では、%SQL.StatementMetadataOpens in a new tab %Display()Opens in a new tab インスタンス・メソッドを使用して列のメタデータをすべて表示します。
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare("SELECT %ID AS id,Name,DOB,Age,AVG(Age),CURRENT_DATE,Home_State FROM Sample.Person")
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
DO tStatement.%Metadata.%Display()
WRITE !,"End of metadata"
これは、選択したフィールドの 2 つのテーブル・リストを返します。最初の列のメタデータ・テーブルには、以下のような列定義情報が示されます。
列名 | 列の名前。列にエイリアスが指定されている場合は、そのエイリアスがここに示されます。SELECT 項目が式または集計である場合は、割り当てられた “Expression_n” ラベルまたは “Aggregate_n” ラベルが示されます (n は、SELECT 項目のシーケンス番号を表します)。式または集計にエイリアスを割り当てていた場合は、そのエイリアスがここに示されます。 |
タイプ | ODBC データ型の整数コード。このコードについては、"InterSystems SQL リファレンス" の "データ型" リファレンス・ページの "データ型の整数コード" のセクションに一覧があります。これらの ODBC データ型コードは、%Library.ResultSetOpens in a new tab クラス・メソッド GetColumnType(n) で返されるクライアント・データ型コードとは異なります。 |
精度 | 最大データ・サイズ (文字数)。 |
スケール | 小数点以下の最大桁数。 |
ヌル | 列に NULL を許可しないか (0)、許可するか (1) を示すブーリアン値。SELECT 項目が式または集計であり、その結果が NULL になる可能性がある場合、この項目は 1 に設定されます。SELECT 項目が、システムの提供する値 (現在の日付や Pi を返すシステム変数または関数など) を持つ式である場合、この項目は 2 に設定されます。 |
ラベル | 列名またはエイリアス。 |
テーブル | テーブル名。テーブルにエイリアスを指定していた場合でも、ここには実際のテーブル名が常に示されます。SELECT 項目が式または集計である場合には、テーブル名は示されません。 |
スキーマ | テーブルのスキーマ名。SELECT 項目が式または集計である場合には、スキーマ名は示されません。 |
CType |
2 番目の列のメタデータ・テーブルには、より詳細な列情報が示されます。Extended Column Info テーブルの各列には、Y (Yes) または N (No) と指定された 12 のブーリアン・フラグが示されます (SQLRESULTCOL)。それらのフラグは、1:AutoIncrement、2:CaseSensitive、3:Currency、4:ReadOnly、5:RowVersion、6:Unique、7:Aliased、8:Expression、9:Hidden、10:Identity、11:KeyColumn、12:RowId です。
また、Extended Column Info メタデータ・テーブルには、選択したフィールドのオブジェクト・クラス・プロジェクションも示されます。この例では、Name フィールドおよび DOB フィールドは、リンクするプロパティ Sample.Person.Name および Sample.Person.DOB に投影されることに注意してください。ただし、Home_State フィールドは、リンクするプロパティ Sample.Address.State に投影されます。これは、このフィールドが、リンクするコンテナ・プロパティにアクセスするためです。Sample.Person の Home_State は、Sample.Address の Home プロパティのタイプ・クラスの State プロパティから投影されます。
ダイナミック SQL のメタデータには、列ごとのより詳細なメタデータが含まれます。この、より詳細なメタデータには、その列がテーブルの列に戻るためにリンク可能な場合、ディクショナリ内のプロパティ定義の ID が含まれます。列がコンテナ・プロパティから投影される場合にリンクされるこのプロパティ ID が、コンテナ・プロパティのタイプ・クラス内のプロパティの ID に設定されます。例えば、Sample.Person の Home_City は、Sample.Address の Home プロパティのタイプ・クラスの City プロパティから投影されます。したがって、リンクされるプロパティの ID は、"Sample.Address||City" です。
以下の例は、文パラメータでもある 1 つの仮パラメータで呼び出されたストアド・プロシージャのメタデータを返します。
ZNSPACE "SAMPLES"
SET mysql = "CALL Sample.SP_Sample_By_Name(?)"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(.mysql)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
DO tStatement.%Metadata.%Display()
WRITE !,"End of metadata"
これは、列 (フィールド) 情報だけでなく、文パラメータ、仮パラメータ、およびオブジェクトの値も返します。
以下の例は、3 つの仮パラメータを持つメタデータを返します。これら 3 つのパラメータの 1 つでは、疑問符 (?) が使用されており、これが文パラメータになります。
ZNSPACE "SAMPLES"
SET mycall = "CALL personsets(?,'MA')"
SET tStatement = ##class(%SQL.Statement).%New(0,"sample")
SET qStatus = tStatement.%Prepare(mycall)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
DO tStatement.%Metadata.%Display()
WRITE !,"End of metadata"
このメタデータは列情報は返さず、文パラメータと仮パラメータのリストに列名とデータ型が含まれることに注意してください。
クエリ引数のメタデータ
作成の後に、%SQL.StatementOpens in a new tab クラスを使用して、入力パラメータ (疑問符 (?) として指定)、入力ホスト変数 (:varname として指定)、および定数 (リテラル値) の各クエリ引数に関するメタデータを返すことができます。以下のメタデータを返すことができます。
-
? パラメータのカウント : parameterCountOpens in a new tab プロパティ
-
? パラメータの ODBC データ型 : %SQL.StatementMetadataOpens in a new tab %Display()Opens in a new tab インスタンス・メソッドの文パラメータ・リスト
-
?、v (:var)、および c (定数) のパラメータのリスト : "作成が成功した場合の結果" で説明されている %GetImplementationDetails()Opens in a new tab インスタンス・メソッド
-
?、v (:var)、および c (定数) のパラメータの ODBC データ型 : formalParametersOpens in a new tab プロパティ
%SQL.StatementMetadataOpens in a new tab %Display()Opens in a new tab インスタンス・メソッドの仮パラメータ・リスト
-
これらの引数を示すクエリのテキスト : "作成が成功した場合の結果" で説明されている %GetImplementationDetails()Opens in a new tab インスタンス・メソッド
文のメタデータ %Display() メソッドは、文パラメータと仮パラメータをリストします。パラメータごとに、連続したパラメータ番号、ODBC データ型、精度、スケール、NULL 値が許容されるかどうか (2 は、値が常に指定されていることを意味します)、および対応するプロパティ名 (colName) および列タイプが示されます。
一部の ODBC データ型は、負の整数として返されることに注意してください。ODBC データ型の整数コードのテーブルについては、"InterSystems SQL リファレンス" の "データ型" リファレンス・ページを参照してください。
以下の例は、各クエリ引数 (?、:var、および定数) の ODBC データ型を順番に返します。TOP ALL を指定することが可能なため、TOP 引数は 4 (INTEGER) ではなくデータ型 12 (VARCHAR) として返されることに注意してください。
SET myquery = 4
SET myquery(1) = "SELECT TOP ? Name,DOB,Age+10 "
SET myquery(2) = "FROM Sample.Person"
SET myquery(3) = "WHERE %ID BETWEEN :startid :endid AND DOB=?"
SET myquery(4) = "ORDER BY $PIECE(Name,',',?)"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(.myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET prepmeta = tStatement.%Metadata
WRITE "Number of ? parameters=",prepmeta.parameterCount,!
SET formalobj = prepmeta.formalParameters
SET i=1
WHILE formalobj.GetAt(i) {
SET prop=formalobj.GetAt(i)
WRITE prop.colName," type= ",prop.ODBCType,!
SET i=i+1 }
WRITE "End of metadata"
実行後は、クエリ結果セットのメタデータから引数メタデータを利用することはできません。結果セットでは、すべてのパラメータが解決済みです。したがって、parameterCount = 0 となり、formalParameters にデータは含まれません。
クエリ結果セットのメタデータ
%SQL.StatementOpens in a new tab クラスを使用した実行の後に、以下を呼び出して結果セットのメタデータを返すことができます。
-
%SQL.StatementResultOpens in a new tab クラス・プロパティ
-
%SQL.StatementMetadataOpens in a new tab クラス・プロパティにアクセスする %SQL.StatementResultOpens in a new tab %GetMetadata()Opens in a new tab メソッド
%SQL.StatementResult プロパティ
[クエリ実行] 操作の後、%SQL.StatementResultOpens in a new tab は以下を返します。
-
%StatementTypeOpens in a new tab。これは、最後に実行された SQL 文に対応する整数コードを返します。この整数コードには、1 = SELECT 文、2 = INSERT 文、3 = UPDATE 文、4 = DELETE 文、9 = CREATE TABLE 文、45 = CALL 文などがあります。すべてのコード値のリストは、"インターシステムズ・クラス・リファレンス" の "%SQL.StatementResultOpens in a new tab" を参照してください。
-
%StatementTypeNameOpens in a new tab プロパティ。これは、最後に実行された SQL 文のコマンド名を返します。この名前は大文字で返されます。
-
%ResultColumnCountOpens in a new tab プロパティ。これは、結果セット行の列数を返します。
以下の例は、これらのプロパティを示しています。
SET myquery = "SELECT TOP ? Name,DOB,Age FROM Sample.Person WHERE Age > ?"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute(10,55)
IF rset.%SQLCODE=0 {
WRITE "Statement type=",rset.%StatementType,!
WRITE "Statement name=",rset.%StatementTypeName,!
WRITE "Column count=",rset.%ResultColumnCount,!
WRITE "End of metadata" }
ELSE { WRITE !,"SQLCODE=",rset.%SQLCODE," ",rset.%Message }
%SQL.StatementResult %GetMetadata()
実行の後に、%SQL.StatementResultOpens in a new tab %GetMetadata()Opens in a new tab メソッドを使用して %SQL.StatementMetadataOpens in a new tab クラス・プロパティにアクセスできます。これらは、作成の後に %SQL.StatementOpens in a new tab %Metadata プロパティでアクセスするプロパティと同じです。
以下の例は、これらのプロパティを示しています。
SET myquery=2
SET myquery(1)="SELECT Name AS VendorName,LastPayDate,MinPayment,NetDays,"
SET myquery(2)="AVG(MinPayment),$HOROLOG,%TABLENAME FROM Sample.Vendor"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(.myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute()
IF rset.%SQLCODE=0 {
SET rsmeta=rset.%GetMetadata()
SET x=rsmeta.columns.Count()
SET x=1
WHILE rsmeta.columns.GetAt(x) {
SET column=rsmeta.columns.GetAt(x)
WRITE !,x," ",column.colName," is data type ",column.ODBCType
WRITE " with a size of ",column.precision," and scale = ",column.scale
SET x=x+1 }
}
ELSE { WRITE !,"SQLCODE=",rset.%SQLCODE," ",rset.%Message }
WRITE !,"End of metadata"
結果セットのメタデータでは引数メタデータは提供されないことに注意してください。これは、[実行] 操作によってすべてのパラメータが解決されるためです。したがって、結果セットでは、parameterCount = 0 となり、formalParameters にデータは含まれません。
ダイナミック SQL の監査
Caché では、ダイナミック SQL 文の監査オプションをサポートしています。ダイナミック SQL の監査は、%System/%SQL/DynamicStatement システム監査イベントを有効化しているときに実行されます。既定では、このシステム監査イベントは有効化されていません。
%System/%SQL/DynamicStatement を有効にすると、システム全体で実行されるすべての %SQL.StatementOpens in a new tab ダイナミック文が自動的に監査されます。監査の情報は、監査ログに記録されます。
監査済みの SQL 文には以下の情報が記録されます。
-
イベントの説明。これには、SQL 文のタイプと、その文に使用されたダイナミック SQL の実装が含まれます。例えば、SQL SELECT Statement (%SQL.Statement) または SQL CREATE VIEW Statement (%Library.ResultSet) です。
-
監査レコードのイベント・データ。これには、実行された SQL 文と、その文の引数の値が含まれます。以下はその例です。
SELECT Name,City,Age FROM Sample.Mytest WHERE City = ? AND Age > ? ORDER BY Name %CallArgs(1)="New York" %CallArgs(2)=45
イベント・データの合計長。これには文とパラメータが含まれ、3,632,952 文字です。文とパラメータが 3,632,952 文字を超える場合、イベント・データは切り詰められます。
また、Caché は xDBC 文 (ODBC および JDBC) の監査と、埋め込み SQL 文の監査もサポートしています。