-
Notifications
You must be signed in to change notification settings - Fork 49
UserGuide_DynamicParameterizedQuery.ja
2016年10月3日
- Open 棟梁を用いたアプリケーション開発を行う、SE・開発者
本ドキュメントは、フレームワークの持つ動的パラメタライズド・クエリ機能の利用方法について纏めています。
本ドキュメントに記載の会社名・商品名は、各社の商標または登録商標です。
本ドキュメントは、クリエイティブ・コモンズ CC BY 2.1 JP ライセンスの下で利用可能です。
"Open 棟梁" の動的パラメタライズド・クエリは、XML タグを使用したクエリ記述ルールを採用しており、このクエリ記述ルールに従ってクエリを記述し、API からパラメタを設定することで、JOIN・WHERE 句や、条件式の有効化・無効化などを制御できます。以下に記述例を示します。
図 1.1-1 "Open 棟梁" の動的パラメタライズド・クエリの記述例
<?xml version="1.0" encoding="shift_jis" ?>
<ROOT>
SELECT DISTINCT ctm.companyname, ctm.contactname, ctm.contacttitle FROM orders AS o
INNER JOIN customers AS ctm ON o.customerid = ctm.customerid
<JOIN name="j1">
INNER JOIN shippers AS s ON o.shipvia = s.shipperid
</JOIN>
<JOIN name="j2">
INNER JOIN [order details] AS od ON o.orderid = od.orderid
INNER JOIN products AS p ON od.productid = p.productid
INNER JOIN categories AS cgy ON p.categoryid = cgy.categoryid
</JOIN>
<WHERE>WHERE<IF>s.companyname=@p1</IF><IF>AND cgy.categoryname=@p2</IF></WHERE>
ORDER BY [<VAL name="COLUMN"/>] <VAL name="SEQUENCE"/>
</ROOT>なお、動的パラメタライズド・クエリ (XML) の作成には Visual Studio を利用できます。タグの総数が 200 個を超えると性能的に負荷が高くなるので注意します。
以下、動的パラメタライズド・クエリ部の概要を説明します。
図 1.1-2 "Open 棟梁" の動的パラメタライズド・クエリの概要説明(1)
-
JOIN 句の制御が可能:
<JOIN>タグは JOIN 句の有効・無効を制御する。 -
WHERE 句の制御が可能:
<IF>タグは検索条件の有効・無効を制御する。また<WHERE>タグは検索条件がすべて無効となった場合は WHERE 句自体を削除する。 -
任意の文字列挿入が可能:
<VAL>タグは任意の文字列挿入が可能なため、どのようなパターンの SQL 整形にも対応可能である。ただし利用の際は SQL インジェクションが発生しないかの注意が必要になる。
上記 SQL 定義をプログラム上から利用する場合は、次のように API を使用してパラメタを設定します。
this.SetSqlByFile2("ファイル名");
// パラメタライズド・クエリのパラメタに対して、動的に値を設定する。
this.SetParameter("j1", true);
this.SetParameter("j2", true);
this.SetParameter("p1", "United Package");
this.SetParameter("P2", "Beverages");
// ユーザ定義パラメタを置換する。
this.SetUserParameter("COLUMN", "ctm.companyname");
this.SetUserParameter("SEQUENCE", "DESC");
図 1.1-3 "Open 棟梁" の動的パラメタライズド・クエリの概要説明(2)
パラメタ指定により整形された SQL(上記例の実行結果):
SELECT DISTINCT ctm.companyname, ctm.contactname, ctm.contacttitle FROM orders AS o
INNER JOIN customers AS ctm ON o.customerid = ctm.customerid
INNER JOIN shippers AS s ON o.shipvia = s.shipperid
INNER JOIN [order details] AS od ON o.orderid = od.orderid
INNER JOIN products AS p ON od.productid = p.productid
INNER JOIN categories AS cgy ON p.categoryid = cgy.categoryid
WHERE s.companyname=@p1 AND cgy.categoryname=@p2
ORDER BY ctm.[companyname] DESCサブクエリや IN 句のパラメタリストも制御できます。<SUB> タグでサブクエリを、<LIST> タグに ArrayList を指定することで IN 句のパラメタリストの自動展開が可能です。
<?xml version="1.0" encoding="shift_jis" ?>
<ROOT>
SELECT * FROM PRODUCTS
<WHERE>WHERE
<SUB name="SUB1">CATEGORYID IN(SELECT CATEGORYID FROM CATEGORIES
<WHERE>WHERE
<SUB name="SUB2">CATEGORYID IN(SELECT CATEGORYID FROM CATEGORIES
<WHERE>WHERE
<IF name="ISNOTNULL1">CATEGORYID IS NOT NULL
<ELSE>CATEGORYID IS NULL</ELSE>
</IF>
</WHERE>)
</SUB>
<IF name="ISNOTNULL2">AND CATEGORYID IS NOT NULL
<ELSE>AND CATEGORYID IS NULL</ELSE>
</IF>
</WHERE>)
</SUB>
<IF name="ISNOTNULL3">AND CATEGORYID IS NOT NULL
<ELSE>AND CATEGORYID IS NULL</ELSE>
</IF>
<LIST>AND CATEGORYID IN(@PLIST)</LIST>
AND DISCONTINUED = @BIT
</WHERE>
ORDER BY [<VAL name="COLUMN"/>] <VAL name="SEQUENCE"/>
</ROOT>this.SetSqlByFile2("ファイル名");
this.SetParameter("SUB1", true);
this.SetParameter("SUB2", true);
this.SetParameter("ISNOTNULL1", true);
this.SetParameter("ISNOTNULL2", true);
this.SetParameter("ISNOTNULL3", true);
this.SetParameter("PLIST", new ArrayList(new short[] { 1, 2, 3, 4, 5, 6, 7, 8 }));
this.SetParameter("BIT", true);
this.SetUserParameter("COLUMN", "SUPPLIERID");
this.SetUserParameter("SEQUENCE", "DESC");"Open 棟梁" の動的パラメタライズド・クエリで使用する XML タグを、以下の表 1.2-1 に示します。
表 1.2-1 "Open 棟梁" の動的パラメタライズド・クエリで使用する XML タグ
| 項番 | タグ名称 | タグ表記 | 説明/ネストについて [1] |
|---|---|---|---|
| 1 | ROOT タグ | <ROOT>…</ROOT> |
クエリ定義は全て、このタグの中に収める。ROOT タグは、ROOT タグ以外の全てのタグをネストできる。 |
| 2 | VAL タグ | <VAL name="xxx"/> |
パラメタに設定した文字列へ置換する。任意の文字列挿入が可能。パラメタ名は name 属性に指定する (タグ内パラメタ)。null/未設定の場合は無効になり削除される。SQL インジェクションが可能なため、ユーザ入力を直接指定しない。ネスト不可。 |
| 3 | INSCOL タグ | <INSCOL name="xxx">…</INSCOL> |
INSERT 句のカラム リスト専用 (通常は自動生成 SQL でのみ利用)。name 属性のタグ内パラメタが設定されていればカラム情報を有効化。カラム情報と VAL タグのみネスト可能。 |
| 4 | IF タグ |
<IF>…</IF> / <IF name="xxx">…</IF>
|
WHERE 句内の条件式を囲い、条件式の有効・無効を制御する。テキスト内パラメタ (先頭のパラメタ 1 つ) が設定されれば有効、未設定なら無効。name 属性のタグ内パラメタは true/false を許可 (false で ELSE 有効、無ければエラー)。条件式・ELSE・VAL タグのみネスト可能。 例: <IF>AND XXX=@P1<ELSE>AND XXX IS NULL</ELSE></IF>
|
| 5 | ELSE タグ | <ELSE>…</ELSE> |
IF タグで ELSE が有効になる場合の条件式を記述する。テキスト内・タグ内パラメタは記述不可 (XXX IS NULL などの条件式を記述)。条件式・VAL タグのみネスト可能。 |
| 6 | SELECT-CASE-DEFAULT タグ | <SELECT name="xxx"><CASE value="A">…</CASE><DEFAULT>…</DEFAULT></SELECT> |
name 属性のパラメタ値によって、一致する CASE、若しくは DEFAULT のステートメントを有効にする (DEFAULT は省略可能)。null/未設定なら削除。VAL タグのみネスト可能。 |
| 7 | LIST タグ | <LIST>…IN句の条件式…</LIST> |
IN 句の条件式を囲い、パラメタリストの指定と IN 句の有効・無効を制御する。テキスト内パラメタをパラメタ名に使用。値は ArrayList 型 (実行時に @名_1, @名_2… と展開)。要素数 0/null/未設定なら削除。IN 句の条件式と VAL タグのみネスト可能。 |
| 8 | JOIN タグ | <JOIN name="xxx">…JOIN句…</JOIN> |
JOIN 句の有効・無効を制御する。パラメタ値 true で有効、false/null/未設定で削除 (他の値はエラー)。ROOT・PARAM・DIV 以外の全タグをネスト可能。 |
| 9 | SUB タグ | <SUB name="xxx">…サブクエリの条件式…</SUB> |
WHERE 句内のサブクエリの条件式の有効・無効を制御する。パラメタ値 true で有効、false/null/未設定で削除。ROOT・PARAM・DIV 以外の全タグをネスト可能。 |
| 10 | WHERE タグ | <WHERE>…WHERE句…</WHERE> |
IF/LIST/SUB の処理で WHERE 句内に条件式が無くなる場合や「WHERE AND 条件式」等の不正な状態になり得る場合、不要な WHERE 句や演算子 (AND、OR) を削除する。ROOT・PARAM・DIV 以外の全タグをネスト可能。 |
| 11 | DELCMA タグ | <DELCMA>…カンマ区切りのリスト…</DELCMA> |
生成した文字列の前後に余分に付与されたカンマを削除する (通常は自動生成の更新系 SQL でのみ利用)。ROOT・PARAM・DIV 以外の全タグをネスト可能。 |
| 12 | PARAM タグ | <PARAM>…パラメタ情報…</PARAM> |
分析ツールで、PARAM タグ間に指定したパラメタ値でテスト実行するために使用する (記述ルールは下記)。パラメタの記述と DIV タグのみネスト可能。 |
| 13 | DIV タグ | <DIV/> |
PARAM タグ内でパラメタを区切るために使用する。ネスト不可。 |
PARAM タグのパラメタ記述ルール(パラメタ名, …<DIV/> で区切る):
- ユーザ定義パラメタ (VAL タグ):
P1, xxx<DIV/> - 通常のパラメタ:
P1, String, xxx<DIV/>/P2, Int32, 123<DIV/> - 配列のパラメタ:
P1, String[], xxx, yyy, zzz<DIV/> - ArrayList 型のパラメタ:
P1, String, xxx, yyy, zzz<DIV/>(値のリストが 2 つ以上) - DBNull 型のパラメタ:
P1, DBNull[, 任意]<DIV/> - null のパラメタ:
P1, (任意), null<DIV/>
補足1:テキスト内パラメタ (@p などの DBMS のパラメタ) は、タグの処理後もパラメタのコレクションに残される (DBMS のパラメタは複数個所に作用するため)。タグ内パラメタ (タグの name 属性) は処理後に消去される。このため、テキスト内パラメタは全てのタグに作用し、タグ内パラメタは最初の 1 つのタグにしか作用しない [2]。
PARAM タグで使用する文字列と型の対応を、以下の表 1.2-2 に示します。
表 1.2-2 PARAM タグで使用する文字列
| 項番 | パラメタの型名 | 型を表す文字列 (通常時/配列時) | 値を表す文字列 (の制限) |
|---|---|---|---|
| 1 | System.Boolean | Boolean/Boolean[] | 「true」or「false」の文字列のみ指定可能 |
| 2 | System.Byte | Byte/Byte[] | Byte に収まる文字 |
| 3 | System.UInt16 | UInt16/UInt16[] | UInt16 に収まる数値を表す文字列 |
| 4 | System.UInt32 | UInt32/UInt32[] | UInt32 に収まる数値を表す文字列 |
| 5 | System.UInt64 | UInt64/UInt64[] | UInt64 に収まる数値を表す文字列 |
| 6 | System.SByte | SByte/SByte[] | SByte に収まる文字 |
| 7 | System.Int16 | Int16/Int16[] | Int16 に収まる数値を表す文字列 |
| 8 | System.Int32 | Int32/Int32[] | Int32 に収まる数値を表す文字列 |
| 9 | System.Int64 | Int64/Int64[] | Int64 に収まる数値を表す文字列 |
| 10 | System.Decimal | Decimal/Decimal[] | Decimal に収まる数値を表す文字列 |
| 11 | System.Single | Single/Single[] | Single に収まる数値を表す文字列 |
| 12 | System.Double | Double/Double[] | Double に収まる数値を表す文字列 |
| 13 | System.Char | Char/Char[] | Char に収まる文字 |
| 14 | System.String | String/String[] | 任意の文字列 |
| 15 | System.DateTime | DateTime/DateTime[] | DateTime (日付型) に変換可能な文字列 |
| 16 | System.DBNull | DBNull/サポートしない | - |
| 17 | null 値の場合 | -/サポートしない | 「null」という文字列のみ指定可能 |
補足2:「動的パラメタライズド・クエリ」の XML 中に「<、>」の演算子があると XML のタグと認識され構文チェックでエラーとなります。このため、XML 中に「<、>」の演算子を記述する場合は
<>を利用します (最新バージョンでは CDATA セクション<![CDATA[…]]>も使用できます)。
動的パラメタライズド・クエリでは、2WAY-SQL [3] を実現する専用のツール (動的パラメタライズド・クエリ分析ツール) を使用して、クエリ中に埋め込んだパラメタ情報をもとにクエリの実行をテストできます。
- ランタイムに .NET Framework 2.0 が必要です。
- インストールは不要で、フォルダ コピーで利用できます。配置したフォルダ直下の「DPQuery_Tool.exe」をダブル クリックして実行します。
- データ プロバイダの DLL はフォルダに同梱されているため、全てのデータ プロバイダ (SQLClient、ODP.NET、DB2.NET) を実行環境にインストールしておかなくても利用可能です (ただし未インストールのプロバイダを選択・接続するとエラー)。
基本的な利用方法は、次の流れになります。
- コンボ ボックスからデータ プロバイダを選択する。
- テキスト ボックスに接続文字列を入力する。
- 接続ボタンを押して DB に接続する。
- 実行するクエリを直接入力するか、クエリ ファイルを作成 (指定) する。
- 入力したクエリ (指定したクエリ ファイル) を実行する。
図 2.2-1 動的パラメタライズド・クエリ分析ツール
なお、動的パラメタライズド・クエリ中には、以下のように <PARAM> タグによりパラメタ情報を埋め込む必要があります。
図 2.2-2 <PARAM> タグによるパラメタ情報の埋め込み
<?xml version="1.0" encoding="shift_jis" ?>
<ROOT>
SELECT DISTINCT ctm.companyname, ctm.contactname, ctm.contacttitle FROM orders AS o
INNER JOIN customers AS ctm ON o.customerid = ctm.customerid
<JOIN name="j1">INNER JOIN shippers AS s ON o.shipvia = s.shipperid</JOIN>
<JOIN name="j2">
INNER JOIN [order details] AS od ON o.orderid = od.orderid
INNER JOIN products AS p ON od.productid = p.productid
INNER JOIN categories AS cgy ON p.categoryid = cgy.categoryid
</JOIN>
<WHERE>WHERE<IF>s.companyname=@p1</IF><IF>AND cgy.categoryname=@p2</IF></WHERE>
ORDER BY [<VAL name="COLUMN"/>] <VAL name="SEQUENCE"/>
<PARAM>
j1, Boolean, true<DIV/>
j2, Boolean, true<DIV/>
p1, String, United Package<DIV/>
p2, String, Beverages<DIV/>
COLUMN, ctm.companyname<DIV/>
SEQUENCE, DESC<DIV/>
</PARAM>
</ROOT>クエリ実行時、動的パラメタライズド・クエリであるか、静的パラメタライズド・クエリ、SQL であるかが自動判定され、クエリの定義に合った方法で自動実行されます。
図 2.2-3 定義に合った方法で自動実行
実行結果は次のダイアログに表示されます。ダイアログには 3 つのタブがあり、それぞれ「結果タブ:SQL 実行結果」「SQL タブ:実行された SQL」「LOG タブ:ログ出力される SQL」を確認できます。
図 2.2-4 SQL 実行結果
図 2.2-5 実行された SQL
図 2.2-6 ログ出力される SQL
[設定] グループ ボックスでは、設定の新規作成から、セーブ・ロードができます。セーブ・ロード先はスロット 1 〜 5 まで用意されており、[スロット選択] コンボ ボックスで選択できます。
図 2.3-1 [設定] グループボックス
図 2.3-2 新規作成時の入力ダイアログ
設定を新規作成した後に、[データ プロバイダ選択] コンボ ボックスでデータ プロバイダを変更すると、接続文字列の例が自動的に組み立てられます。必要に応じて修正し、[接続] ボタンで接続を確認したら、[切断] してから [設定のセーブ] ボタンで保存しておくと良いでしょう。
[トランザクション] グループ ボックスでは、トランザクションに関する詳細な制御が可能です。
図 2.3-3 [トランザクション] グループボックス
- [分離レベル] コンボ ボックスで分離レベルを選択できます (DBMS によってはサポートされない分離レベルもあり、その場合はエラー メッセージが表示されます)。
- [制御方式] コンボ ボックスでトランザクション制御方式を「Auto」・「Manual」から選択できます。
- Auto:クエリ実行のたびにトランザクションが開始され、実行後に自動的にコミット (エラー時はロールバック) される。
- Manual:クエリの実行に関係なく、[開始] 〜 [コミット]・[ロールバック] ボタンで手動制御する。
- [インターフェイス] コンボ ボックスでは、クエリを実行する際に使用する API を選択します。
[クエリ ファイル] グループ ボックスでは、SQL の新規作成・編集が可能です。
図 2.3-4 [クエリ ファイル] グループボックス
クエリ ファイル…[を開く]、[を閉じる]、[を上書き保存]、[に保存] の 4 つのボタンが利用できます ([を開く] は SQL ファイルを Form にドラッグ&ドロップする動作で代替可能。ショート カットも割り当て済み)。その他、右クリックのコンテキスト メニューから、サンプルやテンプレート、タグの挿入が可能です。
図 2.3-5 動的パラメタライズド・クエリのサンプルやテンプレート、タグの挿入
[インターフェイス] コンボ ボックスの上部にある 2 つの設定項目が実行オプションです。
図 2.3-6 実行オプション
- [型推測モード(型指定)] チェック ボックス:型指定用メソッドのテストの際にチェックする (通常は型指定用メソッドを使用する必要はない)。DB 部品の SetParameter メソッド (型指定ありのオーバーロード) が実行される。
- [配列バインド数指定] スピン ボックス:ODP.NET で利用可能な配列バインドを使用する場合、0 以上の値を指定する (OracleCommand.ArrayBindCount に設定される)。
動的パラメタライズド・クエリ分析ツールでは、静的パラメタライズド・クエリ、SQL の実行もサポートします。静的パラメタライズド・クエリの場合、パラメタ設定用の SQL コメントを使用してパラメタを設定できます。
図 2.4 静的パラメタライズド・クエリへのパラメタ設定方法
SELECT * FROM Employees
WHERE
FirstName=@FN
AND LastName=@LN1
AND EmployeeID IN (SELECT EmployeeID FROM Employees WHERE LastName=@LN2)
AND ReportsTo IN ( @P1 , @P2 )
ORDER BY %COLUMN% %SEQUENCE%
/*PARAM* FN, String, Nancy *PARAM*/
/*PARAM* LN1, String, Davolio *PARAM*/
/*PARAM* LN2, String, Davolio *PARAM*/
/*PARAM* P1, String, 2 *PARAM*/
/*PARAM* P2, String, 5 *PARAM*/
/*PARAM* COLUMN, EmployeeID *PARAM*/
/*PARAM* SEQUENCE, DESC *PARAM*/クエリは、動的パラメタライズド・クエリであるか、静的パラメタライズド・クエリ、SQL であるかが自動判定され、定義に合った方法で自動実行されます。このため、動的パラメタライズド・クエリであってもクエリ実行方法は変わりません。
クエリを設定する API(図 3-1:ファイルから読み込む、図 3-2:直接指定する):
// -- ファイルから読み込む場合。
this.SetSqlByFile(
Path.Combine(GetConfigParameter.GetConfigValue("sqlTextFilePath"), "ファイル名"));
// -- ファイル、埋め込まれたリソースから読み込む場合。
// 埋め込まれたリソースから読み込む場合、次の設定が必要になる。
// MyBaseDao.UseEmbeddedResource = true;
this.SetSqlByFile2("ファイル名");
// -- 直接指定する場合。
this.SetSqlByCommand("動的パラメタライズド・クエリ");パラメタを設定する API(図 3-3:通常のパラメタ、図 3-4:ユーザ定義パラメタ):
// パラメタライズド・クエリのパラメタに対して、動的に値を設定する(先頭記号は設定不要 [6])。
this.SetParameter("P1", testParameter.ShipperID);
// ユーザ定義パラメタを置換する(%XXXX% や VAL タグを指定の文字列で置換する)。
this.SetUserParameter("UP1", "XXXXX");SQL を実行する API(図 3-5:SQL の実行には以下の 5 つのメソッドを利用できる):
// -- 追加、更新、削除の場合(件数を確認できる)
obj = this.ExecInsUpDel_NonQuery();
// -- 1セル分の情報を返す SELECT クエリを実行する場合
obj = this.ExecSelectScalar();
// -- テーブル(or レコード)の情報を返す SELECT クエリ(引数 = データテーブル)
obj = new DataTable();
this.ExecSelectFill_DT((DataTable)obj);
// -- テーブル(or レコード)の情報を返す SELECT クエリ(引数 = データセット)
obj = new DataSet();
this.ExecSelectFill_DS((DataSet)obj);
// -- データリーダを返す
IDataReader idr = (IDataReader)this.ExecSelect_DR();このように動的パラメタライズド・クエリであっても、静的パラメタライズド・クエリ、SQL の場合に利用する API と変わりません [7]。
API に関する詳細は、「利用ガイド (開発者編)」の 3 章:「D層フレームワークの利用方法」を参照してください。
- 全てのタグは、コメントと CDATA セクションのネストが可能となっている。
- 複数の種類のタグに跨って使用できるか、タグ内・テキスト内パラメタで混在できるかなどを考慮すると仕様が複雑になるため。
- 2WAY-SQL は、バインディングの情報を SQL コメントで埋め込むことで、通常の SQL として直に実行 (テスト) できるという特徴を持つ。
- [型推測モード] チェック ボックスは、型指定の API のテスト用に設けられているため、通常は使用する必要はない。API に指定する型情報は、規定の型推測アルゴリズムで自動的に推測される (.NET型 → 各データ プロバイダ固有の DBType)。
- メッセージ・ボックスのキャプションには、実行する SQL の取得元情報 (TextBox からか、開いているファイルからか) が表示される。ファイルから実行する場合は、TextBox 上の内容をファイルに保存してから実行しないと、異なる SQL が実行されるため注意。
- DB2 のデータ プロバイダは、他 (Oracle、SQL Server) と異なりパラメタ名にパラメタの先頭記号を含めることが必須だが、この差異は D 層部品で吸収している。HiRDB のデータ プロバイダは順番バインドのみサポートだが、本フレームワークでは名前バインドのみ可能。
- API に関する詳細は、「利用ガイド (開発者編)」の 3 章を参照。
-以上-