Skip to main content

This documentation is for an older version of this product. See the latest version of this content.Opens in a new tab

ダイナミック SQL の使用

この章では、InterSystems IRIS® データ・プラットフォームから実行時に作成され、実行されるクエリおよびその他の SQL 文であるダイナミック SQL について説明します。

この章では、ダイナミック SQL の推奨実装である、%SQL.StatementOpens in a new tab クラスを使用したダイナミック SQL プログラミングについて説明します。この章および弊社のドキュメント全体にわたって、ダイナミック SQL に関するすべての文は、%SQL.StatementOpens in a new tab の実装を指しています。

ダイナミック SQL の概要

ダイナミック SQL は、実行時に作成され、実行される SQL 文です。ダイナミック SQL では、SQL コマンドの作成と実行は別個の操作です。ダイナミック SQL により、InterSystems IRIS でも ODBC アプリケーションや JDBC アプリケーションと似た方法でプログラムを作成できます (データベース・エンジンと同じプロセス・コンテキストで SQL 文を実行している場合は除きます)。ダイナミック SQL は ObjectScript プログラムから呼び出されます。

ダイナミック SQL クエリは、コンパイル時ではなく、プログラム実行時に作成されます。つまり、コンパイラはコンパイル時にエラーのチェックを実行できず、プリプロセッサ・マクロをダイナミック SQL 内で使用することはできません。また、実行プログラムはユーザやその他の入力に応答して、専用のダイナミック SQL クエリを生成できます。

ダイナミック SQL を使用して SQL クエリを実行できます。また、他の SQL 文の発行にも使用できます。この章の例では、SELECT クエリを実行します。CREATE TABLEINSERTUPDATEDELETE、または CALL を呼び出すダイナミック SQL プログラムの例については、"InterSystems SQL リファレンス" でこれらのコマンドを参照してください。

ダイナミック SQL は、InterSystems IRIS SQL シェルの実行、InterSystems IRIS 管理ポータルの [クエリ実行] インタフェースSQL コードのインポート・メソッド、およびデータのインポート/エクスポート・ユーティリティで使用されます。

ダイナミック SQL (およびそれを使用するアプリケーション) の最大行サイズは、3,641,144 文字です。

ダイナミック SQL と埋め込み SQL

ダイナミック SQL と埋め込み 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 により、クエリのメタデータ (列の数や名前など) を簡単に見ることができます。

  • ダイナミック SQL では SQL 特権の確認が既定で行われるので、テーブルやフィールドなどにアクセスするときやこれらを変更するときは、適切な特権が必要です。埋め込み SQL では、SQL 特権の確認は行われません。詳細は、SQL "%CHECKPRIV" 文を参照してください。

  • ダイナミック 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.StatementResultOpens in a new tab のインスタンス) であり、予想される結果セットの機能をサポートします。

次の ObjectScript コードでは、ダイナミック SQL クエリを作成および実行します。

  /* Simple %SQL.Statement example */
  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.StatementOpens in a new tab クラスおよび %SQL.StatementResultOpens in a new tab クラスに関連付けられているメソッドを使用します。

オブジェクト・インスタンスの作成

%New()Opens in a new tab クラス・メソッドを使用して、%SQL.StatementOpens in a new tab クラスのインスタンスを作成できます。

  set tStatement = ##class(%SQL.Statement).%New()

この時点で結果セット・オブジェクトは SQL 文を作成できる状態です。%SQL.StatementOpens in a new tab クラスのインスタンスを作成すると、そのインスタンスを使用して、複数のダイナミック SQL クエリの発行や、INSERTUPDATE、または DELETE の各操作の実行が可能です。

%New() では、コンマで区切られた 3 つのオプション・パラメータを次の順序で指定できます。

  1. %SelectMode : データ入力とデータ表示に使用するモードを指定します。

  2. %SchemaPath : 未修飾のテーブル名のスキーマ名を指定するために使用する検索パスを指定します。

  3. %Dialect : Transact-SQL (TSQL) Sybase 言語または MSSQL 言語を指定します。既定は IRIS (InterSystems 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 = 表示のいずれかのモードを指定します。これらのモードは、データ値を入力および表示する方法を指定します。このモードは、通常、日時の値や、%List データ (エンコードされたリストを含む文字列) の表示に最もよく使用されます。データは論理モードで格納されます。

SELECT クエリは、%SelectMode 値を使用してデータ表示に使用する形式を決定します。

INSERT または UPDATE 操作は、%SelectMode 値を使用してデータ入力に使用できる形式を決定します。

%SelectMode は、データ表示のために使用されます。SQL 文は、内部的に論理モードで実行されます。例えば、ORDER BY 節は、%SelectMode の設定に関係なく、レコードをその論理値に基づいて並べ替えます。SQL 関数は、%SelectMode の設定に関係なく、論理値を使用します。SQLPROC として投影されたメソッドも論理モードで実行されます。SQL 文で関数として呼び出された SQL ルーチンは、論理形式で関数値を返す必要があります。

  • SELECT クエリでは、%SelectMode により、データの表示に使用する形式を指定します。%SelectMode を ODBC または表示に設定すると、比較述語の値の指定に使用されるデータ形式にも影響します。述語の値には、%SelectMode 形式で指定する必要があるものや、%SelectMode に関係なく、論理形式で指定する必要があるものがあります。詳細は、"InterSystems SQL リファレンス" の "述語の概要" を参照してください。

    • %SelectMode=1Time データ型データ (ODBC) では秒の小数部を表示できます。これは実際の ODBC 時間とは異なります。InterSystems IRIS 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) は、リスト要素をコンマで区切って表示します。この要素区切り文字は、CollectionOdbcDelimiter パラメータとして指定されます。%SelectMode=2 の %List データ型 (表示) は、リスト要素を $CHAR(10,13) (改行、キャリッジ・リターン) で区切って表示します。この要素区切り文字は、CollectionDisplayDelimiter パラメータとして指定されます。

  • INSERT または UPDATE 操作の場合、%SelectMode は論理ストレージ形式に変換される入力データの形式を指定します。このデータ変換を行うには、INSERT または UPDATE の実行時に表示または ODBC %SelectMode が使用されるように、SQL コードが RUNTIME 選択モード (既定) を使用してコンパイルされている必要があります。日時に使用できる入力値については、DATE データ型および TIME データ型を参照してください。詳細は、"InterSystems 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.Util.GetOption("SelectMode")Opens in a new tab メソッドを使用します。現在のプロセスで既定の SelectMode 設定を変更できますが、そのためには、$SYSTEM.SQL.Util.SetOption("SelectMode",n)Opens in a new tab メソッドを使用します。n には、0 = 論理、1 = ODBC、または 2 = 表示のいずれかを指定します。%SelectMode による設定は、現在のオブジェクト・インスタンスの既定の設定よりも優先して適用されます。SelectMode プロセスの既定の設定は変更されません。

SelectMode オプションの詳細は、このドキュメントの “InterSystems IRIS SQL の基礎” の章にある “データ表示オプション” を参照してください。

%SchemaPath プロパティ

%SchemaPathOpens in a new tab プロパティは、未修飾のテーブル名、ビュー名、またはストアド・プロシージャ名のスキーマ名の指定に使用する検索パスを指定します。スキーマ検索パスは、SELECTCALLINSERTTRUNCATE TABLE などのデータ管理操作に使用され、DROP TABLE などのデータ定義操作では無視されます。

検索パスは、単独のスキーマ名を引用符で囲んで指定するか、複数のスキーマ名をコンマで区切って記述したリストを引用符で囲んで指定します。このリストに記述したスキーマは左から右に検索されます。一致するテーブル名、ビュー名、またはストアド・プロシージャ名が検出されるまで、指定した各スキーマが検索されます。スキーマは指定された順序で検索されるため、あいまいなテーブル名は検出されません。検索対象となるのは、現在のネームスペースに存在するスキーマ名のみです。

スキーマ検索パスには、リテラルのスキーマ名と、CURRENT_PATH、CURRENT_SCHEMA、および DEFAULT_SCHEMA の各キーワードの両方を記述できます。

  • CURRENT_PATH は、前もって %SchemaPath プロパティで定義した現在のスキーマ検索パスを示します。これは、通常、既存のスキーマ検索パスの先頭または末尾にスキーマを付加するときに使用します。

  • CURRENT_SCHEMA は、%SQL.Statement 呼び出しがクラス・メソッド内から行われた場合の、現在のスキーマのコンテナのクラス名を指定します。クラスのメソッドで #sqlcompile path マクロ指示文を定義している場合、CURRENT_SCHEMA は現在のクラス・パッケージにマップされたスキーマになります。そうでない場合には、CURRENT_SCHEMA は DEFAULT_SCHEMA と同じです。

  • DEFAULT_SCHEMA は、システム全体の既定のスキーマを指定します。このキーワードを使用すると、リストされている他のスキーマを検索する前に、スキーマ検索パス内の項目としてシステム全体の既定のスキーマを検索できます。パスに指定されているすべてのスキーマを検索して一致が見つからなかった場合、システム全体の既定のスキーマは常に、スキーマ検索パスの検索後に検索されます。

%SchemaPath は、InterSystems IRIS が一致テーブル名を最初に探す場所です。%SchemaPath が指定されていない場合、または一致するテーブル名が含まれるスキーマを表示しない場合、InterSystems IRIS はシステム全体の既定のスキーマを使用します。

スキーマ検索パスを指定できますが、そのためには、%SchemaPath プロパティを指定するか、%New() クラス・メソッドの 2 つ目のパラメータを指定します。以下に 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 に、特定のクラス名に対して定義された検索パスを設定することができます。

  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、または IRIS (InterSystems SQL) を指定できます。Sybase または MSSQL 設定によって、指定した Transact-SQL 言語を使用して SQL 文が処理されます。

Sybase 言語および MSSQL 言語が言語内でサポートする SQL 文は限定されています。サポートされる文は、SELECTINSERTUPDATEDELETE、および EXECUTE です。また、CREATE TABLE 文は、永続的なテーブルに対してはサポートされますが、一時テーブルに対してはサポートされません。CREATE VIEW はサポートされます。CREATE TRIGGERDROP TRIGGER もサポートされます。ただし、この実装では、CREATE TRIGGER 文が部分的に成功してもクラス・コンパイルに対しては失敗する場合には、トランザクション・ロールバックはサポートされません。CREATE PROCEDURECREATE FUNCTION はサポートされます。

Sybase 言語および MSSQL 言語は、IF 制御フロー文をサポートします。このコマンドは IRIS (InterSystems SQL) 言語ではサポートされていません。

既定は、空の文字列 ("") で表される、または "IRIS" として指定される InterSystems SQL です。

%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="IRIS"
   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 値を返します。

  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 は主に、フィールド名プロパティを使用して結果セットから値を返す際に使用します。これについては、この章の “結果セットからの特定値の返送” セクションの "フィールド名プロパティ" に、例を用いた詳しい説明があります。

%ObjectSelectMode=1 は、SELECT リスト内のフィールドがコレクション・プロパティにリンクされている場合に使用できます。%ObjectSelectMode はコレクションをスウィズルします。%SelectMode=1 または 2 の場合、スウィズリングの前にコレクションのシリアル値が論理モード形式に変換されます。結果として得られる oref は、完全なコレクション・インタフェースをサポートします。

SQL 文の作成

SQL 文の作成では、その文を検証し、以降の実行に向けて準備して、その SQL 文のメタデータを生成します。

%SQL.StatementOpens in a new tab クラスを使用して SQL 文を作成する方法は 3 つあります。

  • %Prepare() : 後続の %Execute() に対して SQL 文 (クエリなど) を作成します。

  • %PrepareClassQuery() : 既存のクエリへの呼び出し文を作成します。呼び出し文を作成したら、後続の %Execute() を使用してこのクエリを実行できます。

  • %ExecDirect() : SQL 文を作成および実行します。%ExecDirect() については、“SQL 文の実行” に説明があります。

  • %ExecDirectNoPriv() : SQL 文を作成および実行しますが、特権は確認しません。%ExecDirectNoPriv() は、"“SQL 文の実行”" を参照してください。

$SYSTEM.SQL.Prepare()Opens in a new tab メソッドを使用することで、オブジェクト・インスタンスを作成することなく SQL 文を作成することもできます。以下のターミナル例では、Prepare() メソッドが使用されています。

USER>set topnum=5
USER>set prep=$SYSTEM.SQL.Prepare("SELECT TOP :topnum Name,Age FROM Sample.Person WHERE Age=?")

USER>do prep.%Display()

SQL 文の作成によって、クエリ・キャッシュが作成されます。クエリ・キャッシュを使用すると、SQL 文を再作成する必要なしに同じ SQL クエリを複数回実行できます。クエリ・キャッシュは、任意のプロセスによって 1 回以上実行することができます。異なる入力パラメータ値を使用して実行することができます。

SQL 文を準備するたびに、InterSystems IRIS はクエリ・キャッシュを検索して、同じ SQL 文が既に準備されキャッシュされているかどうかを判別します (2 つの SQL 文で、リテラルおよび入力パラメータの値だけが異なる場合、これらの文は "同一" と見なされます)。準備済みの文がクエリ・キャッシュ内に存在しない場合、InterSystems IRIS はクエリ・キャッシュを作成します。準備済みの文が既にクエリ・キャッシュ内に存在する場合は、新しいクエリ・キャッシュは作成されません。このため、ループ構造内で準備文をコード化しないことが重要です。

%Prepare()

SQL 文の作成には、%SQL.StatementOpens in a new tab クラスの %Prepare()Opens in a new tab インスタンス・メソッドを使用できます。%Prepare() メソッドは、最初の引数として SQL 文を取ります。次の例に示すように、この引数には、引用符付きの文字列、または引用符付きの文字列に解決される変数を指定できます。

  set 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 tStatement = ##class(%SQL.Statement).%New()
  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 tStatement = ##class(%SQL.Statement).%New()
  set qStatus = tStatement.%Prepare(.myquery)

InterSystems IRIS は SQL 文の実行時に、それぞれの入力ホスト変数を定義済みのリテラル値に置換します。ただし、このコードがメソッドとして呼び出された場合、minage 変数を Public にする必要があります。既定では、メソッドは ProcedureBlocks です。これは、メソッド (%Prepare() など) がその呼び出し元によって定義された変数を認識できないことを意味します。この既定をオーバーライドするには、クラスを [ Not ProcedureBlock ] として指定して、メソッドを [ ProcedureBlock = 0] として指定するか、または [ PublicList = minage ] を指定します。

Note:

プログラム記述時には、入力変数を SQL コードに挿入する前に、その変数に適切な値が含まれていること必ず確認することをお勧めします。

? 入力パラメータを使用して、クエリにリテラル値を渡すこともできます。InterSystems IRIS は、%Execute() メソッドに渡された対応するパラメータ値を使用して、それぞれの ? 入力パラメータをリテラル値に置換します。%Prepare() の後に、%GetImplementationDetails() メソッドを使用して、クエリ内の ? 入力パラメータと入力ホスト変数をリスト表示できます。

%Prepare() メソッドは %Status 値を返します。成功の場合、1 のステータスを返します (クエリ文字列が有効で、参照したテーブルが現在のネームスペース内に存在しています)。失敗の場合、0 の後にエンコードされたエラー情報が続くオブジェクト式を返します。このため、失敗には status=0 テストは実行できません。失敗には $$$ISOK(status)=0 macro テストを実行できます。

%Prepare() メソッドは、先に定義されている %SchemaPath プロパティを使用して、修飾されていない名前を解決します。

Note:

ダイナミック SQL のパフォーマンスは、できる限り完全修飾名を使用することによって大幅に向上させることができます。

SQL 文で入力パラメータを指定するには、“? ” 文字を使用します。

  set myquery="SELECT TOP ? Name,Age FROM Sample.Person WHERE Age > ?"
  set tStatement = ##class(%SQL.Statement).%New()
  set qStatus = tStatement.%Prepare(myquery)

クエリの実行時に、%Execute() インスタンス・メソッドの各 "?" 入力パラメータに値を指定します。入力パラメータは、リテラル値を受け取るか、評価結果がリテラル値になる式を受け取る必要があります。入力パラメータは、フィールド名値やフィールド名エイリアスを受け取ることはできません。入力パラメータは、PUBLIC として宣言し、SELECT 文がそれを直接参照できるようにする必要があります。

クエリには、フィールドのエイリアスを含めることができます。この場合、Data プロパティは、フィールド名でなく、エイリアスを使用してデータにアクセスします。

ダイナミック SQL では、SELECT 文に限らず、%Prepare() インスタンス・メソッドを使用して、CALLINSERTUPDATEDELETE などの他の SQL 文も作成できます。

現在作成している文の情報を表示するには、次の例に示すように、%Display()Opens in a new tab インスタンス・メソッドを使用します。

  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"

この情報は、実装クラス、引数 (リテラル値または ? 入力パラメータによる実際の引数のコンマ区切りのリスト)、および文のテキストで構成されます。

%Prepare() はオプションの 2 番目の引数として checkPriv を取ります。この引数は、InterSystems IRIS で文に対する特権が確認されるかどうかを指定する論理値です。checkPriv が 0 の場合、特権は確認されません。特権の確認を無効化することで、ダイナミック・クエリの実行をアプリケーションで詳しく制御できるようになりますが、セキュリティ・リスクは高くなります。既定値は 1 で、この場合は特権が確認されます。以下に例を示します。

  set statement = ##class(%SQL.Statement).%New()
  set status =statement.%Prepare("DELETE FROM T",0) // No privileges checked

  set statement2 = ##class(%SQL.Statement).%New()
  set status =statement2.%Prepare("DELETE FROM T") // Privilege is checked

%PrepareClassQuery()

既存の SQL クエリを使用する場合は、%SQL.StatementOpens in a new tab クラスの %PrepareClassQuery()Opens in a new tab インスタンス・メソッドを使用できます。%PrepareClassQuery() メソッドは、既存のクエリのクラス名およびクエリ名の 2 つのパラメータを取ります。次の例に示すように、両方の引数に、引用符付きの文字列、または引用符付きの文字列に解決される変数を指定できます。

  set 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.DataDirectory()
   set rset=tStatement.%Execute(install_"mgr\User")
   do rset.%Display()  

以下の例では、CREATE QUERY 文を準備する %Prepare() を示してから、このクラス・クエリを呼び出す %PrepareClassQuery() を示します。

  /* Creating the Query */
  set query=4
  set query(1)="CREATE QUERY DocTest() SELECTMODE RUNTIME PROCEDURE "
  set query(2)="BEGIN "
  set query(3)="SELECT TOP 5 Name,Home_State FROM Sample.Person ; "
  set query(4)="END"
  
  set statement = ##class(%SQL.Statement).%New()
  set qStatus = statement.%Prepare(.query)
  if qStatus '= 1 {write "%Prepare failed:" do $System.Status.DisplayError(qStatus) quit}
  
  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  write !,"Created a query",!

  /* Calling the Query */
  write !,"Calling a class query..."
  set cqStatus = statement.%PrepareClassQuery("User.queryDocTest","DocTest")
  if cqStatus '= 1 {write "%PrepareClassQuery failed:" do $SYSTEM.Status.DisplayError(cqStatus) quit}

  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}

  write "Query data:",!,!
  while rset.%Next()
  {
    do rset.%Print()
  }
  if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  write !,"End of data."

  /* Deleting the Query */
  &sql(DROP QUERY DocTest)
  if SQLCODE < 0 {write !,"Error deleting query:", SQLCODE, " ", %msg quit}
  write !,"Deleted the query."

格納されたクエリにより取得されるデータの行を表示するには、この例で示すように %Print() メソッドを使用できます。格納されたクエリにより取得された特定の列データを表示するには、%Get("fieldname") メソッドまたは %GetData(colnum) メソッドのいずれかを使用する必要があります。“結果セットの繰り返し処理” を参照してください。

引数を受け入れるようにクエリが定義されている場合、“?” 文字を使用して、SQL 文で入力パラメータを指定できます。“” クエリの実行時に、%Execute() メソッドの各 "?" 入力パラメータに値を指定します。入力パラメータは、PUBLIC として宣言し、SELECT 文がそれを直接参照できるようにする必要があります。

現在作成しているクエリの情報を表示するには、次の例に示すように、%Display() メソッドを使用します。

  /* 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 statement = ##class(%SQL.Statement).%New()
  set qStatus = statement.%Prepare(.query)
  if qStatus '= 1 {write "%Prepare failed:" do $System.Status.DisplayError(qStatus) quit}
  
  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  write !,"Created a query",!

  /* Preparing and Displying Info about the Query */
  write !,"Preparing a class query..."
  set cqStatus = statement.%PrepareClassQuery("User.queryDocTest","DocTest")
  if cqStatus '= 1 {write "%PrepareClassQuery failed:" do $SYSTEM.Status.DisplayError(cqStatus) quit}

  do statement.%Display()
  write !,"End of %Prepare display"

  /* Deleting the Query */
  &sql(DROP QUERY DocTest)
  if SQLCODE < 0 {write !,"Error Deleting query:",SQLCODE," ",%msg  quit }
  write !,"Deleted the query"

この情報は、実装クラス、引数 (リテラル値または ? 入力パラメータによる実際の引数のコンマ区切りのリスト)、および文のテキストで構成されます。

詳細は、"クラスの定義と使用" の “クラス・クエリの定義と使用” を参照してください。

正常な作成の結果

正常に作成できると (%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 ? Name,Age FROM Sample.Person WHERE Age > 21 AND Name=:fname"
  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),!  }  // returns "?,?,c,21,v,fname
   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 :n Name FROM Clients。例えば、%PrepareClassQuery() の場合、call Sample.SP_Sample_By_Name(?)

作成済みのクエリに関して生成されるその他のメタデータ情報については、"SQL メタデータ" を参照してください。

preparse() メソッド

preparse()Opens in a new tab メソッドを使用することで、SQL クエリを作成しなくても、クエリ引数の %List 構造を返すことができます。クエリ引数は、%GetImplementationDetails()同じ形式で返されます。

preparse() メソッドは、クエリ・テキストも返します。ただし、指定されたとおりにクエリ・テキストを返す %Display()%GetImplementationDetails() とは異なり、preparse() メソッドは各クエリ引数を ? 文字に置換し、コメントを削除し、空白を正規化します。既定のスキーマ名は返しません。以下の例で、preparse() メソッドは解析バージョンのクエリ・テキストおよびクエリ引数の %List 構造を返します。

  set myq=2
  set myq(1)="SELECT TOP ? Name /* first name */, Age "
  set myq(2)="FROM Sample.MyTable WHERE Name='Fred' AND Age > :years -- end of query"
  do ##class(%SQL.Statement).preparse(.myq,.stripped,.args)
  write "preparsed query text: ",stripped,!
  write "arguments list: ",$listtostring(args)

SQL 文の実行

%SQL.StatementOpens in a new tab クラスを使用して SQL 文を実行する方法は 2 つあります。

  • %Execute()%Prepare() または %PrepareClassQuery() を使用して作成済みの SQL 文を実行します。

  • %ExecDirect() : SQL 文を作成し、実行します。

  • %ExecDirectNoPriv() : 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() で生成できます。例えば以下のようにします。

  set rset = tStatement.%Execute()

%Execute() メソッドは、すべての SQL 文に対して %SQL.StatementResultOpens in a new tab クラス・プロパティ %SQLCODEOpens in a new tab および %MessageOpens in a new tab を設定します。文の実行が成功すると、%SQLCODE が 0 に設定されます。これは、文によって結果が問題なく取得されたことを示しているわけではありません。同様に、文によって結果が取得されていない場合、%Execute() では %SQLCODE が 100 に設定されません。%Next() メソッドの使用などによって一度に 1 行の結果を取得すると、結果が確認され、続いて %SQLCODE が 0、100、または負数のエラー値に設定されます。

%Execute() は、以下のようにその他の %SQL.StatementResultOpens in a new tab プロパティを設定します。

  • INSERTUPDATEINSERT OR UPDATEDELETE、および TRUNCATE TABLE 文は、%ROWCOUNTOpens in a new tab を操作の影響を受ける行の数に設定します。TRUNCATE TABLE では、削除される実際の行数は特定できず、%ROWCOUNT は -1 に設定されます。

    INSERTUPDATEINSERT OR UPDATE、および DELETE は、%ROWIDOpens in a new tab を最後に挿入、更新、または削除されたレコードの RowID 値に設定します。操作でレコードが挿入、更新、または削除されなかった場合、%ROWID は定義されないか、前の値に設定されたままになります。TRUNCATE TABLE は %ROWID を設定しません。

  • 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 を設定しません。

ZWRITE を使用して、すべての %SQL.StatementResultOpens in a new tab クラス・プロパティの値を返すことができます。

詳細は、このドキュメントの “埋め込み SQL の使用法” の章の対応する SQL システム変数の説明を参照してください。%Dialect を Sybase または MSSQL に設定して TSQL コードを実行した場合、エラーはその SQL 言語の標準プロトコルと、InterSystems IRIS の %SQLCODE プロパティおよび %Message プロパティの両方で報告されます。

入力パラメータを持つ %Execute()

%Execute() メソッドは、作成された SQL 文の入力パラメータ (“?” に示される) に対応する 1 つ以上のパラメータを取得できます。%Execute() パラメータは、SQL 文内で “?” 文字が現れる順序に対応します。最初のパラメータは最初の “?” に対応し、2 番目のパラメータは、2 番目の “?” に対応し、以下同様に続きます。複数の %Execute() パラメータは、コンマで区切られます。プレースホルダとしてコンマを指定することによって、そのパラメータ値を省略できます。%Execute() パラメータの数は、“?” 入力パラメータ数と一致していなければなりません。%Execute() のパラメータの数が、対応する “?” 入力パラメータの数と一致しない場合は、%SQLCODE プロパティが SQLCODE -400 エラーに設定された状態でメソッドの実行に失敗します。

入力パラメータを使用して、リテラル値や式を SELECT リストや他のクエリ節 (TOP 節や WHERE 節など) に渡すことができます。入力パラメータを使用して、列名や列名エイリアスを SELECT リストや他のクエリ節に渡すことはできません。

明示的な %Execute() パラメータとして指定した場合の入力パラメータの最大数は 255です。可変長配列 %Execute(vals...) を使用して指定した場合の入力パラメータの最大数は 380です。

Prepare の後に、Prepare の引数メタデータを使用して、? 入力パラメータの数と必要なデータ型を返すことができます。%GetImplementationDetails() メソッドを使用して、作成済みクエリ内の ? 入力パラメータのリスト、および ? 入力パラメータをコンテキストで示したクエリ・テキストを返すことができます。

次の ObjectScript の例では、2 つの入力パラメータを持つクエリを実行します。%Execute() メソッドで入力パラメータ値 (21 と 26) を指定しています。

  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() メソッドの仮パラメータ・リストは、可変長配列 (dynd...) を使用して、不確定数の入力パラメータ値を指定できます。この場合は、dynd 配列の添え字です。dynd 変数は 2 に設定されていますが、これは、2 つの添え字値を示しています。

  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() 操作の間で結果セットを閉じる必要はありません。

  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 {
  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"

詳細は、"正常な作成の結果" を参照してください。

%ExecDirectNoPriv()

%SQL.StatementOpens in a new tab クラスには、%ExecDirect() のようにクエリの作成と実行の両方を単一の操作で処理する %ExecDirectNoPriv() クラス・メソッドがあります。%ExecDirectNoPriv() では、クエリの準備中に文に対する特権確認も無効になります。特権の確認を無効化することで、ダイナミック・クエリの実行をアプリケーションで詳しく制御できるようになりますが、セキュリティ・リスクは高くなります。

完全な結果セットの返送

%Execute() または %ExecDirect() のいずれかで文を実行すると、%SQL.StatementResultOpens in a new tab インタフェースを実装するオブジェクトが返されます。このオブジェクトは、単一値、結果セット、または CALL 文から返されたコンテキスト・オブジェクトのいずれかです。

%Display() メソッド

次の例に示すように、%SQL.StatementResultOpens in a new tab クラスの %Display()Opens in a new tab インスタンス・メソッドを呼び出すことで、結果セット (結果オブジェクトの内容) 全体を表示できます。

  do rset.%Display()

%Display() メソッドは %Status 値を返さない点に注意してください。

%Display() によるクエリ結果セットの表示の最後には、“影響を受けた行数 5” のように行数が表示されます (これは、%Display() が結果セットを繰り返し処理した後の %ROWCOUNT 値です)。%Display() では、行数を通知するこの文の後には改行が発行されません。

%Display() には 2 つのオプションの引数があります。

  • 区切り文字 : データ列とデータ・ヘッダの間に挿入される文字列。結果セット列の間、ヘッダまたはデータ値の直前に表示されます。既定では、区切り文字はありません。省略されている場合は、Column Alignment フラグの前にプレースホルダのコンマを指定します。

  • Column Alignment : データ列とデータ・ヘッダの間の空白の算出方法を指定する整数フラグ。使用可能なオプションは以下のとおりです。

    • 0 : 結果セット・ヘッダ/データ列は、標準の区切り文字 (タブ) に基づいて配置されます。これが既定値です。

    • 1 : 結果セット・ヘッダ/データ列は、列ヘッダの長さと標準の区切り文字 (タブ) に基づいて配置されます。

    • 2 : 結果セット・ヘッダ/データ列は、列データ・プロパティの精度/長さと標準の区切り文字 (タブ) に基づいて配置されます。

%DisplayFormatted() メソッド

%Display() を呼び出すのではなく、%SQL.StatementResultOpens in a new tab クラスの %DisplayFormatted()Opens in a new tab インスタンス・メソッドを呼び出すことで、結果セットの内容の再フォーマットおよび生成されたファイルへのリダイレクトができます。

文字列オプション %DisplayFormatted("HTML") または対応する整数コード %DisplayFormatted(1) を指定することで、結果セットの形式を指定できます。InterSystems IRIS は、指定されたタイプのファイルを生成し、適切なファイル名拡張子を付けます。次の表は、指定できるオプションと生成できるファイルを示します。

文字列オプション 整数コード 生成されるファイルの拡張子
"XML" 0 .xml
"HTML" 1 .html
"PDF" 2 .pdf
"TXT" 99 .txt
"CSV" 100 .csv

生成された CSV ファイル内の値は、コンマではなく、タブで区切られます。

他の数値または文字列を指定すると、%DisplayFormatted() はテキスト・ファイル (.txt) を生成します。テキスト・ファイルは、行カウントで終わります (“5 Rows(s) Affected” など)。他の形式は行カウントを含みません。

結果セットのファイル名を指定することも、省略することもできます。

  • 宛先ファイルを指定した場合 (例えば、%DisplayFormatted(99,"myresults"))、この名前と該当する接尾辞 (ファイル名拡張子) が付いたファイルが、mgr ディレクトリの現在のネームスペースのサブディレクトリ内に生成されます。例えば、C:\InterSystems\IRIS\mgr\user\myresults.txt です。その接尾辞が付いた指定のファイルが既に存在する場合、そのファイルは新しいデータで上書きされます。

  • 宛先ファイルを指定しない場合 (例えば、%DisplayFormatted(99))、ランダムに生成された名前と該当する接尾辞 (ファイル名拡張子) が付いたファイルが、mgr ディレクトリの Temp サブディレクトリ内に生成されます。例えば、C:\InterSystems\IRIS\mgr\Temp\w4FR2gM7tX2Fjs.txt です。クエリが実行されるたびに、新しい宛先ファイルが生成されます。

以下の例は、Windows のファイル名を示しています。InterSystems IRIS では、その他のオペレーティング・システムの同等の場所がサポートされます。

指定したファイルを開くことができない場合、この操作は 30 秒後にタイムアウトしてエラー・メッセージが表示されます。これは、指定されたディレクトリ (ファイル・フォルダ) に対する書き込み権限をユーザが持っていない場合に多く発生します。

データを指定された形式でレンダリングできない場合、宛先ファイルは作成されますが、結果セット・データは書き込まれません。代わりに、適切なメッセージが宛先ファイルに書き込まれます。例えば、ストリーム・フィールドの OID には、XML および HTML の特殊な書式設定文字と競合する文字が含まれます。この XML および HTML のストリーム・フィールドの問題は、ストリーム・フィールドで XMLELEMENT 関数を使用することで解決できます (例 : SELECT Name,XMLELEMENT("Para",Notes))。

必要に応じて、指定された形式変換の実行時に %DisplayFormatted() によって使用される、変換テーブルの名前を指定できます。

結果セット・シーケンス内に複数の結果セットがある場合は、各結果セットの内容は、それぞれ固有のファイルに書き込まれます。

オプションの 3 番目の %DisplayFormatted() 引数は、メッセージを別個の結果セットに格納することを指定します。正常に完了すると、以下のようなメッセージが返されます。

Message
21 row(s) affected.

以下の Windows の例では、2 つの PDF (整数コード 2) 結果セット・ファイルが C:\InterSystems\IRIS\mgr\user\ 内に作成されます。メッセージ用の mess 結果セットを作成してから、%Display() を使用してターミナルにメッセージを表示します。

  set $NAMESPACE="USER"
  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",.mess)
  do mess.%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.%DisplayFormatted(2,"Twenties",.mess)
  do mess.%Display()
  write !,"End of twenties data"

結果セットのページ付け

ビュー ID (%VID) を使用して、結果セットのページ付けを行うことができます。次の例は、結果セットからページを返します。各ページに 5 行ずつ格納されています。

  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()
      }

結果セットから行 (レコード) のグループを返す別の方法については、"%GetRows()" を参照してください。

結果セットからの特定値の返送

クエリ結果セットから特定の値を返すには、一度に 1 行ずつ繰り返し結果セットを処理する必要があります。結果セットを繰り返し処理するには、%Next()Opens in a new tab インスタンス・メソッドを使用します そして、%Print()Opens in a new tab メソッドを使用して現在の行全体の結果を表示するか、または現在の行内で指定列の値を取得することができます。

%Next() メソッドは、クエリの結果の次の行のデータを取り出し、そのデータを結果セット・オブジェクトの Data プロパティに入れます。%Next() は以下のいずれかの値を返します。

  • %Next() = 1 — クエリ結果のいずれかの行にカーソルが配置されています。

  • %Next() = 0 — 最後の行の後にカーソルが達して、これ以上返す行がないこと、またはクエリから行が返されなかったことを示しています。

%Next() の呼び出しで 1 が返されるたびに、結果セットの %ROWCOUNT プロパティ値に 1 が加算されます。カーソルが最後の行の後に達している場合 (%Next() が 0 を返す場合)、%ROWCOUNT は結果セットにある行の数を示します。

%Next() を呼び出すたびに、結果セットの %SQLCODE プロパティも更新されます。更新後の %SQLCODE 値は、取得した結果によって以下のように異なります。

  • %SQLCODE = 0%Next() で結果の行を正常に取得しました。

  • %SQLCODE = 100%Next() で行が取得されませんでした。クエリから何の結果も返されなかったか、カーソルが最後の行の後に達していて、これ以上取得する行がありません。

  • %SQLCODE < 0%Next() で行の取得に失敗しました。%Next() によって、%SQLCODE には取得が失敗する原因となったエラーの SQLCODE が設定されます。また、結果セットの %Message プロパティにはエラー・メッセージのテキストが設定されます。%Next() をループで反復して呼び出す場合は、エラーが発生しても通知されない状況に対処するために、%SQLCODE に負数値が設定されていないか確認し、%SQLCODE のエラー値と %Message のエラー・メッセージのテキストを表示します。以下に例を示します。

      while rset.%Next()
      {
        write "%Next succeeded."
      }
      if (rset.%SQLCODE < 0)
      {
        write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message
        quit
      }

SELECT クエリから集約関数のみが返されると、すべての %Next()%ROWCOUNT=1 が設定されます。最初の %Next() で 1 が返され、テーブルにデータがない場合でも、%SQLCODE=0 と %ROWCOUNT=1 が設定されます。以降の %Next() では 0 が返され、%SQLCODE=100 と %ROWCOUNT=1 が設定されます。

結果セットから行が取得されると、その行のデータを以下の方法で表示できます。

  • rset.%Print() : クエリ結果セットから、現在行のすべてのデータ値を返します。

  • rset.%GetRow() and rset.GetRows() : クエリ結果セットから、行のデータ値を、エンコードされたリスト構造の要素として返します。

  • 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 フィールドからどのようにデータが表示されるかに注意してください。

  set q1="SELECT TOP 5 Name,DOB,Home_State,FavoriteColors "
  set q2="FROM Sample.Person WHERE FavoriteColors IS NOT NULL"
  set query = q1_q2
  set statement = ##class(%SQL.Statement).%New()

  set status = statement.%Prepare(query)
  if $$$ISERR(status) {write "%Prepare failed:" do $SYSTEM.Status.DisplayError(status) quit}

  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}

  while rset.%Next()
  {
    write "Row count ",rset.%ROWCOUNT,!
    do rset.%Print("^|^")
  }
  if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  write !,"End of data"
  write !,"Total row count=",rset.%ROWCOUNT

以下の例では、区切り文字を含むフィールド値が引用符で囲んで返される様子を示します。この例では、大文字の A がフィールド区切り文字として使用されています。そのため、大文字 A のリテラルを含むフィールド値 (名前、番地、または州の省略形) は、引用符で区切られて返されます。

  set query = "SELECT TOP 25 Name,Home_Street,Home_State,Age FROM Sample.Person"
  set statement = ##class(%SQL.Statement).%New()

  set status = statement.%Prepare(query)
  if $$$ISERR(status) {write "%Prepare failed:" do $SYSTEM.Status.DisplayError(status) quit}
  
  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}

  while rset.%Next()
  {
     do rset.%Print("A")
  }
  if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  write !,"End of data"
  write !,"Total row count=",rset.%ROWCOUNT

%GetRow() および %GetRows() メソッド

%GetRow()Opens in a new tab インスタンス・メソッドは、結果セットから、現在の行 (レコード) をフィールド値の要素のエンコードされたリストとして取得します。

  set myquery = "SELECT TOP 17 %ID,Name,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()
  for {set x=rset.%GetRow(.row,.status)
        if x=1 {write $listtostring(row," | "),! }
        else {write !,"End of data"
              write !,"Total row count=",rset.%ROWCOUNT
              return }
      }

%GetRows()Opens in a new tab インスタンス・メソッドは、結果セットから、指定されたサイズの行 (レコード) のグループを取得します。各行は、フィールド値の要素のエンコードされたリストとして返されます。

以下の例では、結果セットの最初の行、6 番目の行、および 11 番目の行を返します。この例では、%GetRows() の最初のパラメータ (5) は、%GetRows() が連続する 5 行のグループを取得することを指定します。5 行のグループを正常に取得した場合、%GetRows() は 1 を返します。.rows パラメータは、参照によってこれら 5 行の添え字付き配列を渡すため、rows(1) は 5 行の各セットの最初の行 (行 1、6、11) を返します。rows(2) を指定すると、行 2、7、12 が返されます。

  set myquery = "SELECT TOP 17 %ID,Name,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()
  for {set x=rset.%GetRows(5,.rows,.status)
        if x=1 {write $listtostring(rows(1)," | "),! }
        else {write !,"End of data"
              write !,"Total row count=",rset.%ROWCOUNT
              return }
       }

添え字によって個々の行を取得する代わりに、ZWRITE rows コマンドを使用して、取得した配列のすべての添え字を返すことができます。上の例で、ZWRITE rows は結果セットの 16 番目と 17 番目の行は返さないことに注意してください。これらの行は、5 行の最後のグループが取得された後の、残りの部分であるためです。

rset.name プロパティ

InterSystems IRIS は結果セットを生成するときに、その結果セット内のそれぞれのフィールド名とフィールド名エイリアスに対応する固有のプロパティが含まれた結果セット・クラスを作成します。

rset.name プロパティを使用すると、プロパティ名、フィールド名、プロパティ名エイリアス、またはフィールド名エイリアスでデータ値を返すことができます。

  • プロパティ名 : フィールド・エイリアスが定義されていない場合、フィールド・プロパティ名は rset.PropName と指定します。結果セットのフィールド・プロパティ名は、テーブル定義クラス内の対応するプロパティ名から取られます。

  • フィールド名 : フィールド・エイリアスが定義されていない場合、フィールド名 (またはプロパティ名) は rset."fieldname" と指定します。これはテーブル定義で指定されている SqlFieldName です。InterSystems IRIS はこのフィールド名を使用して、対応するプロパティ名を見つけます。多くの場合、プロパティ名とフィールド名 (SqlFieldName) は同一となります。

  • エイリアス・プロパティ名 : フィールド・エイリアスが定義されている場合、エイリアス・プロパティ名は rset.AliasProp と指定します。エイリアス・プロパティ名は、SELECT 文の列名エイリアスから生成されます。定義されたエイリアスがあるフィールドのフィールド・プロパティ名は指定できません。

  • エイリアス名 : フィールド・エイリアスが定義されている場合、このエイリアス名 (またはエイリアス・プロパティ名) は rset."alias" と指定します。これは SELECT 文内の列名エイリアスです。定義されたエイリアスがあるフィールドのフィールド名は指定できません。

  • 集約、式、またはサブクエリ : InterSystems IRIS はこれらの 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 つ目のフィールド値はエイリアス・プロパティ名によるものとなります。これらの場合、指定されたプロパティ名はフィールド名またはフィールド・エイリアスと同一になります。

  set query = "SELECT TOP 5 Name,DOB AS bdate,FavoriteColors FROM Sample.Person"
  set statement = ##class(%SQL.Statement).%New(1)

  set status = statement.%Prepare(query)
  if $$$ISERR(status) {write "%Prepare failed:" do $SYSTEM.Status.DisplayError(status) quit}
  
  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}

  while rset.%Next()
  {
    write "Row count ",rset.%ROWCOUNT,!
    write rset.Name
    write " prefers ",rset.FavoriteColors
    write ", Birth date ",rset.bdate,!!
  }
  if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  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) では区別されることに注意してください。

  set query = "SELECT TOP 5 Name,Home_State FROM Sample.Person"
  set statement = ##class(%SQL.Statement).%New(2)

  set status = statement.%Prepare(query)
  if $$$ISERR(status) {write "%Prepare failed:" do $SYSTEM.Status.DisplayError(status) quit}
  
  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}

  while rset.%Next()
  {
     write "Row count ",rset.%ROWCOUNT,!
     write rset.Name
     write " lives in ",rset."Home_State",!
  }
  if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  write !,"End of data"
  write !,"Total row count=",rset.%ROWCOUNT

%ObjectSelectMode=1 を使用したフィールド名・プロパティのスウィズリング

以下の例は %ObjectSelectMode=1 で作成したもので、これによりタイプ・クラスがスウィズル可能な型 (永続クラス、シリアル・クラス、またはストリーム・クラス) であるフィールドが、フィールド名プロパティを使用して値を返す際に自動的にスウィズルします。フィールド値のスウィズリングの結果は対応するオブジェクト参照 (oref) となります。InterSystems IRIS では、%Get() または %GetData() メソッドを使用してフィールドにアクセスした場合、スウィズリング処理は実行されません。この例では、rset.Home がスウィズルされても、同じフィールドを参照する rset.%GetData(2) はスウィズルされません。

  set query = "SELECT TOP 5 Name,Home FROM Sample.Person"
  set statement = ##class(%SQL.Statement).%New(0)
  set statement.%ObjectSelectMode = 1
  write !,"set ObjectSelectMode=",statement.%ObjectSelectMode,!

  set status = statement.%Prepare(query)
  if $$$ISERR(status) {write "%Prepare failed:" do $SYSTEM.Status.DisplayError(status) quit}

  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}

  while rset.%Next()
  {
     write "Row count: ",rset.%ROWCOUNT,!
     write rset.Name,!
     write " ",rset.Home,!
     write rset.%GetData(1)
     write " ",$listtostring(rset.%GetData(2)),!!
  }
  if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  write !,"End of data"
  write !,"Total row count=",rset.%ROWCOUNT

以下の例では、%ObjectSelectMode に 1 を指定して、選択したレコードの Home_State 値を一意のレコード ID (%ID) から取得しています。ここでは、元のクエリで Home_State フィールドが選択されていません。

  set query = "SELECT TOP 5 %ID AS MyID,Name,Age FROM Sample.Person"
  set statement = ##class(%SQL.Statement).%New()
  set statement.%ObjectSelectMode=1

  set status = statement.%Prepare(query)
  if $$$ISERR(status) {write "%Prepare failed:" do $SYSTEM.Status.DisplayError(status) quit}

  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}

  while rset.%Next()
  {
     write rset.Name
     write " Home State:",rset.MyID.Home.State,!
  }
  if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  write !,"End of data"
  write !,"Total row count=",rset.%ROWCOUNT

構成されている場合、スウィズルされるプロパティが定義されていても参照できない場合、<SWIZZLE FAIL> エラーが生成されます。これは参照されるプロパティがディスクから予期せず削除されてしまった場合、もしくは他のプロセスによりロックされた場合に起こり得ます。スウィズル失敗の原因を判別するには、<SWIZZLE FAIL> エラーの直後に %objlasterror を調べて、この %Status の値をデコードします。

既定では、<SWIZZLE FAIL> は構成されていません。set ^%SYS("ThrowSwizzleError")=1 を設定するか、InterSystems IRIS の管理ポータルを使用することで、この動作をグローバルに設定できます。[システム管理] から、[構成][SQL およびオブジェクトの設定][オブジェクト] の順に選択します。この画面で、<SWIZZLE FAIL> オプションを設定できます。

%Get("fieldname") メソッド

フィールド名またはフィールド名エイリアスでデータ値を返すには、%Get("fieldname")Opens in a new tab インスタンス・メソッドを使用できます。ダイナミック SQL は、必要に応じて大文字/小文字を解決します。指定したフィールド名またはフィールド名エイリアスが存在しない場合、<PROPERTY DOES NOT EXIST> エラーが生成されます。

以下の例は、クエリ結果セットから、Home_State フィールドの値と Last_Name エイリアスの値を返します。

  set query = "SELECT TOP 5 Home_State,Name AS Last_Name FROM Sample.Person"
  set statement = ##class(%SQL.Statement).%New(2)

  set status = statement.%Prepare(query)
  if $$$ISERR(status) {write "%Prepare failed:" do $SYSTEM.Status.DisplayError(status) quit}

  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}

  while rset.%Next()
  {
    write rset.%Get("Home_State")," : ",rset.%Get("Last_Name"),!
  }
  if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  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 statement = ##class(%SQL.Statement).%New(2)
  set status = statement.%PrepareClassQuery("%SYS.Namespace","List")
  if $$$ISERR(status) {write "%Prepare failed:" do $SYSTEM.Status.DisplayError(status) quit}
  
  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}

  while rset.%Next()
  {
     write "Namespace: ",rset.%Get("Nsp"),!
  }
  if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  write !,"End of data"
  write !,"Total row count=",rset.%ROWCOUNT

重複名 : 複数の名前が同一のプロパティ名に解決されると、それらは重複します。重複名は、同じフィールドを複数参照するか、1 テーブル内の異なるフィールドを参照するか、または異なるテーブル内の複数のフィールドを参照する可能性があります。SELECT 文に同じフィールド名またはフィールド名エイリアスの複数のインスタンスが含まれている場合、%Get("fieldname") は必ず、クエリで指定された重複名の最後のインスタンスを返します。これは、クエリで指定された重複名の最初のインスタンスを返す rset.PropName とは逆です。以下に例を示します。

  set query = "SELECT c.Name,p.Name FROM Sample.Person AS p,Sample.Company AS c"
  set statement = ##class(%SQL.Statement).%New()

  set status = statement.%Prepare(query)
  if $$$ISERR(status) {write "%Prepare failed:" do $SYSTEM.Status.DisplayError(status) quit}

  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}

  while rset.%Next()
  {
    write "Prop=",rset.Name," Get=",rset.%Get("Name"),!
  }
  if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  write !,rset.%ROWCOUNT," End of data"

%GetData(n) メソッド

%GetData(n)Opens in a new tab インスタンス・メソッドは、結果セットの整数値列番号でインデックス指定された現在の行に対してデータを返します。%Prepare() を使用して作成された指定クエリ、もしくは %PrepareClassQuery() を使用して作成された格納クエリのいずれかで %GetData(n) を使用できます。

整数 n はクエリ内で指定された select-item リストのシーケンスに対応しています。RowID フィールドには、select-item リストで明示的に指定されていない限り、整数 n の値は示されません。n が、クエリ内の select-item の数より大きいか、0 であるか、または負数である場合、ダイナミック SQL は値を返さず、エラーも発行しません。

%GetData(n) は、特定の重複フィールド名または重複エイリアスを返す唯一の方法です。rset.Name は最初の重複を返し、%Get("Name") は最後の重複を返します。

  set query = "SELECT TOP 5 Name,SSN,Age FROM Sample.Person"
  set statement = ##class(%SQL.Statement).%New()

  set status = statement.%Prepare(query)
  if $$$ISERR(status) {write "%Prepare failed:" do $SYSTEM.Status.DisplayError(status) quit}

  set rset = statement.%Execute()
  if (rset.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}

  while rset.%Next()
  {
    write "Years:",rset.%GetData(3)," Name:",rset.%GetData(1),!
  }
  if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}
  write "End of data"
  write !,"Total row count=",rset.%ROWCOUNT

複数の結果セットの返送

CALL 文は、結果セット・シーケンス (RSS) と呼ばれる集合として複数のダイナミック結果セットを返すことができます。

次の例では、%NextResult()Opens in a new tab メソッドを使用して複数の結果セットを別々に返します。

  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 には、以下のメタデータ・タイプが用意されています。

%SQL.StatementMetadataOpens in a new tab プロパティの値は、Prepare 操作 (%Prepare()%PrepareClassQuery()、または %ExecDirect()) の後に取得できます。

SELECT または CALL 文は、このメタデータすべてを返します。INSERTUPDATE、または DELETE は、文のタイプを示すメタデータと仮パラメータを返します。

文のタイプを示すメタデータ

以下に示すように、%SQL.StatementOpens in a new tab クラスを使用した Prepare の後に %SQL.StatementMetadataOpens in a new tabstatementType プロパティを使用して、作成されている SQL 文のタイプを判別できます。この例では、%SQL.StatementOpens in a new tab%Metadata プロパティを使用し、2 つの Prepare 操作のメタデータを保持して比較しています。

   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 (ストアド・クエリに対する CALL) です。

"正常な作成の結果" で説明しているように、%GetImplementationDetails()Opens in a new tab インスタンス・メソッドを使用しても同じ情報を返すことができます。

クエリの実行後に、結果セットから文のタイプ名 (例えば、SELECT) を返すことができます。

select-item のメタデータ

%SQL.StatementOpens in a new tab クラスを使用した SELECT または CALL 文の Prepare の後に、クエリに指定されている各 select-item 列に関するメタデータを返すことができます。そのためには、メタデータをすべて表示するか、個々のメタデータ項目を指定します。この列のメタデータには、ODBC データ型情報、クライアント・タイプ、インターシステムズのオブジェクトのプロパティ起源、およびクラス・タイプ情報が含まれます。

以下の例では、直前に作成されたクエリに指定されている列の数が返されます。

  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 データ型、最大データ長 (有効桁数)、およびスケールが返されます。

  set $NAMESPACE="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 query = "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(query)
  if qStatus'=1 {write "%Prepare failed:" do $System.Status.DisplayError(qStatus) quit}
  do tStatement.%Metadata.%Display()
  write !,"End of metadata"

これは、選択したフィールドの 2 つのテーブル・リストを返します。最初の列のメタデータ・テーブルには、以下のような列定義情報が示されます。

表示ヘッダ %SQL.StatementColumnOpens in a new tab プロパティ 説明
列名 colName

列の SQL 名。列にエイリアスが指定されている場合は、フィールド名ではなく列エイリアスがここに示されます。名前とエイリアスは、12 文字に切り捨てられます。

式、集計、リテラル、ホスト変数、またはサブクエリの場合、割り当てられている “Expression_n”、“Aggregate_n”、“Literal_n”、“HostVar_n”、または “Subquery_n” ラベルがリストされます (n は SELECT 項目のシーケンス番号)。式、集計、リテラル、ホスト変数、またはサブクエリにエイリアスを割り当てていた場合は、そのエイリアスがここに示されます。

タイプ ODBCType ODBC データ型の整数コード。このコードについては、"InterSystems SQL リファレンス" の "データ型" リファレンス・ページの "データ型の整数コード" セクションに一覧があります。これらの ODBC データ型コードは、CType データ型コードとは異なる点に注意してください。
Prec precision 文字の精度または最大長。TIME データ型の精度およびスケールのメタデータについては、"日付、時刻、PosixTime、およびタイムスタンプのデータ型" を参照してください。
スケール scale 小数点以下の最大桁数。整数または非数値の場合は 0 を返します。TIME データ型の精度およびスケールのメタデータについては、"日付、時刻、PosixTime、およびタイムスタンプのデータ型" を参照してください。
ヌル isNullable 列に NULL を許可しないか (0)、許可するか (1) を示す整数値。RowID は 0 を返します。SELECT 項目が集計またはサブクエリであり、その結果が NULL になる可能性がある場合、または NULL リテラルを指定する場合、この項目は 1 に設定されます。SELECT 項目が式またはホスト変数の場合、この項目は 2 に設定されます (特定できません)。
ラベル label 列名または列エイリアス (列名と同じ)。
テーブル tableName SQL テーブル名。テーブルにエイリアスを指定していた場合でも、ここには実際のテーブル名が常に示されます。SELECT 項目が式または集計である場合には、テーブル名は示されません。SELECT 項目がサブクエリである場合、サブクエリ・テーブル名が示されます。
スキーマ schemaName テーブルのスキーマ名。スキーマ名が指定されていない場合、システム全体の既定スキーマを返します。SELECT 項目が式または集計である場合には、スキーマ名は示されません。SELECT 項目がサブクエリである場合には、スキーマ名は示されません。
CType clientType クライアント・データ型の整数コード。値のリストについては、"%SQL.StatementColumnOpens in a new tab" の "clientTypeOpens in a new tab" プロパティを参照してください。

2 番目の列のメタデータ・テーブルには、より詳細な列情報が示されます。Extended Column Info テーブルの各列には、Y (Yes) または N (No) と指定された 12 のブーリアン・フラグが示されます (SQLRESULTCOL)。

ブーリアン・フラグ %SQL.StatementColumnOpens in a new tab プロパティ 説明
1: AutoIncrement isAutoIncrement RowID および IDENTITY フィールドは Y を返します。
2: CaseSensitive isCaseSensitive

%EXACT 照合を持つ文字列データ型フィールドは Y を返します。

%SerialObject 埋め込みオブジェクトを参照するプロパティは Y を返します。

3: Currency isCurrency MONEY データ型など、%Library.CurrencyOpens in a new tab データ型で定義されたフィールド。
4: ReadOnly isReadOnly 式、集約、リテラル、HostVar、またはサブクエリは Y を返します。RowID、IDENTITY、および RowVersion フィールドは Y を返します。
5: RowVersion isRowVersion RowVersion フィールドは Y を返します。
6: Unique isUnique 一意の値制約があると定義されたフィールド。RowID および IDENTITY フィールドは Y を返します。
7: Aliased isAliased 非フィールド選択項目にはエイリアスが指定されます。このため、ユーザが列エイリアスを指定してシステム・エイリアスを置き換えたかどうかに関係なく、式、集計、リテラル、ホスト変数、またはサブクエリは Y を返します。このフラグは、ユーザが指定した列エイリアスには影響されません。
8: Expression isExpression 式は Y を返します。
9: Hidden isHidden テーブルが %PUBLICROWID または SqlRowIdPrivate=0 (既定) を指定して定義されている場合、RowID フィールドは N を返します。そうでない場合、RowID フィールドは Y を返します。%SerialObject 埋め込みオブジェクトを参照するプロパティは Y を返します。
10: Identity isIdentity IDENTITY フィールドとして定義されたフィールドは、Y を返します。RowID が非表示でない場合、RowID フィールドは Y を返します。
11: KeyColumn isKeyColumn 主キー・フィールドまたは外部キー制約の対象として定義されたフィールド。RowID フィールドは Y を返します。
12: RowID isRowId RowID および IDENTITY フィールドは Y を返します。
13: isList isList

データ型 %Library.List または %Library.ListOfBinary として定義されたフィールド、あるいはリストまたは配列コレクションであるフィールドは、Y を返します。CType (クライアント・データ型)=6。

$LISTBUILD または $LISTFROMSTRING 関数を使用してリストを生成する式は、Y を返します。

Extended Column Info メタデータ・テーブルには、選択した各フィールドの Column Name (SQL 名または列エイリアス)、Linked Prop (リンクされた永続クラス・プロパティ) および Type Class (データ型クラス) がリストされます。Linked Prop には、永続クラス名 (SQL テーブル名ではなく) とプロパティ名 (列エイリアスではなく) がリストされることに注意してください。

  • 通常のテーブル・フィールドの場合 (SELECT Name FROM Sample.Person) : Linked Prop=Sample.Person.Name、Type Class=%Library.String。

  • テーブルの RowID の場合 (SELECT %ID FROM Sample.Person) : Linked Prop= [none]、Type Class=Sample.Person。

  • 式、集計、リテラル、ホスト変数、またはサブクエリの場合 (SELECT COUNT(Name) FROM Sample.Person) : Linked Prop= [none]、Type Class=%Library.BigIn。

  • 参照されている %SerialObject 埋め込みオブジェクト・プロパティの場合 (SELECT Home_State FROM Sample.Person) : Linked Prop=Sample.Address.State、Type Class=%Library.String。

  • %SerialObject 埋め込みオブジェクトを参照しているフィールドの場合 (SELECT Home FROM Sample.Person) :Linked Prop=Sample.Person.Home、Type Class=Sample.Address。

この例では、Sample.Person の Home_State フィールドは、%SerialObject クラス Sample.Address の State プロパティを参照します。

以下の例は、文パラメータでもある 1 つの仮パラメータで呼び出されたストアド・プロシージャのメタデータを返します。

  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 つでは、疑問符 (?) が使用されており、これが文パラメータになります。

  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 クラスを使用した Prepare の後に、入力パラメータ (疑問符 (?) として指定)、入力ホスト変数 (:varname として指定)、および定数 (リテラル値) のクエリ引数に関するメタデータを返すことができます。以下のメタデータを返すことができます。

文メタデータの %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"

Execute の後に、引数メタデータをクエリ結果セットのメタデータから取得することはできません。結果セットでは、すべてのパラメータが解決済みです。このため、parameterCount = 0 で、formalParameters にはデータが含まれていません。

クエリ結果セットのメタデータ

%SQL.StatementOpens in a new tab クラスを使用した Execute の後に、以下を呼び出すことで結果セットのメタデータを返すことができます。

%SQL.StatementResult のプロパティ

Execute クエリ操作の後に、%SQL.StatementResultOpens in a new tab は以下を返します。

  • %StatementTypeOpens in a new tab プロパティは、直前に実行された SQL 文に対応する整数コードを返します。この整数コードには、1 = SELECT、2 = INSERT、3 = UPDATE、4 = DELETE または TRUNCATE TABLE、9 = CREATE TABLE、15 = CREATE INDEX、45 = CALL などがあります。すべてのコード値のリストは、"インターシステムズ・クラス・リファレンス" の "%SQL.StatementResultOpens in a new tab" を参照してください。

  • %StatementTypeNameOpens in a new tab 計算プロパティは、%StatementType に基づいて、直前に実行された SQL 文のコマンド名を返します。この名前は大文字で返されます。TRUNCATE TABLE 操作は、DELETE として返されることに注意してください。INSERT OR UPDATE は、更新操作を実行する場合でも、INSERT として返されます。

  • %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()

Execute の後に、%SQL.StatementResultOpens in a new tab %GetMetadata()Opens in a new tab メソッドを使用して、%SQL.StatementMetadataOpens in a new tab クラスのプロパティにアクセスできます。これらのプロパティは、Prepare の後に %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"

結果セットのメタデータは、引数メタデータを提供しないことに注意してください。これは、Execute 操作がすべてのパラメータを解決するからです。このため、結果セットでは、parameterCount = 0 で、formalParameters にはデータが含まれていません。

ダイナミック SQL の監査

InterSystems IRIS では、ダイナミック SQL 文の監査をオプションでサポートしています。ダイナミック SQL の監査は、%System/%SQL/DynamicStatement システム監査イベントを有効化しているときに実行されます。既定では、このシステム監査イベントは有効化されていません。

%System/%SQL/DynamicStatement を有効にすると、システム全体で実行されるすべての %SQL.StatementOpens in a new tab ダイナミック文が自動的に監査されます。監査の情報は、監査データベースに記録されます。

監査データベースを表示するには、管理ポータルに移動し、[システム管理][セキュリティ][監査][監査データベースの閲覧] の順に選択します。DynamicStatement に [イベント名] フィルタを設定して、[監査データベースの閲覧] をダイナミック SQL 文に制限することができます。監査データベースには、イベントの時間 (ローカル・タイムスタンプ)、ユーザ、PID (プロセス ID)、および説明がリストされます。説明では、ダイナミック SQL 文のタイプを指定します。例えば、SQL SELECT Statement (%SQL.Statement)SQL CREATE VIEW Statement (%SQL.Statement) です。

イベントの [詳細] リンクを選択することで、[イベントデータ] などの追加情報をリストできます。イベント・データには、実行された SQL 文と、文の引数の値が含まれます。以下に例を示します。

SELECT TOP ? Name , Age FROM Sample . MyTest WHERE Name %STARTSWITH ?
/*#OPTIONS {"DynamicSQLTypeList":",1"} */ 
Parameter values:
%CallArgs(1)=5 
%CallArgs(2)="Fred"

文とパラメータが含まれるイベント・データの最大合計長は、3,632,952 文字です。文とパラメータの合計長が 3,632,952 文字を超える場合、イベント・データは切り捨てられます。

InterSystems IRIS では、ODBC および JDBC 文 (Event Name=XDBCStatement) の監査、および埋め込み SQL 文 (Event Name=EmbeddedStatement) の監査もサポートしています。

FeedbackOpens in a new tab