XQuery 開発者ガイド

     Previous  Next    Open TOC in new window     
Content starts here

XQuery エンジン および SQL

この章では、AquaLogic Data Services Platform がリレーショナル データを処理する方法について概要を説明します。特に XQuery と SQL の間で行わなければならない変換に重点を置きます。つまり、リレーショナル データ ソースが AquaLogic Data Services Platform へインポートされるときに何が起こるか、SQL データ型はどのように XQuery データ型にマップされるのか、その逆の場合はどうか、データ サービス対応のアプリケーションをデプロイすると実行時には何が起こるか、さまざまなタイプのクエリはどのように処理されるのか、またどのようなパフォーマンスが期待できるのか、などです。

WebLogic Workshop で BEA AquaLogic Data Services Platform の GUI ツール コンポーネントが利用できるため、SQL 開発者やアプリケーション チューニングのエキスパートは、多くの点でこのような詳細にかかわらずにすむようになりましたが、以下の作業ができるように AquaLogic Data Services Platform によるリレーショナル データの処理方法を理解しておく必要があります。

こうしたタスクを開発者が実行しやすいように、この章では以下のトピックについて説明します。

注意 : 単純化のために、この章では、実際には一部の特定の機能が他の補助的なサブシステム (「コンセプト ガイド」 の 「AquaLogic Data Services Platform コンポーネント アーキテクチャ」 図に示されたデータ ソース API 等のシステム コンポーネントなど) によって処理される場合でも、常に XQuery エンジンとして参照しています。

 


はじめに

BEA AquaLogic Data Services Platform の中核には、データ処理エンジン (通常は単に XQuery エンジンと呼ばれる) があります。XQuery エンジンは、 1-3 ページの 「サポートされる XQuery 仕様」 に挙げた標準に基づく、XQuery 言語の堅牢なエンタープライズ クラスの実装です。また、「BEA の XQuery 実装」 で詳細に説明する追加の拡張機能を備えています。

XQuery および XML 勧告への準拠に加えて、AquaLogic Data Services Platform XQuery エンジンは SQL と XML の世界を橋渡しする ANSI/ISO 標準 (「SQL/XML (ISO-ANSI Working Draft) XML-Related Specifications」 WD 9075-14 (SQL/XML)、2002年8月) にも準拠しています。Java アプリケーション (J2EE サーバー アプリケーション) として、AquaLogic Data Services Platform は JDBC を使用して SQL クエリを生成し、データ サービスを含む適切な RDBMS に送信します。これはつまり、AquaLogic Data Services Platform が、SQL および JDBC の両方における違いを以下のように調整する必要があるということです。

BEA AquaLogic Data Services Platform ではこれらの要因を考慮し、次の節で定義するベース サポートとコア サポートという 2 つの異なるレベルの SQL サポートを RDBMS 用に提供します。

ベースおよびコア RDBMS サポート

AquaLogic Data Services Platform は、リレーショナル データ ソース用に 2 つの異なるレベルのサポートを提供します。

XQuery エンジンにおける SQL データ ソースのサポート方法

BEA AquaLogic Data Services Platform は、メタデータ インポートから、クエリ プラン最適化、クエリのランタイム実行、およびエンド ユーザ (あるいはその他の) アプリケーションへのデータ デリバリまで、データ サービス プロジェクトのライフ サイクル全体を通して SQL (リレーショナル) データ ソースをサポートします。具体的には、XQuery エンジンは以下のものを提供します。

メタデータおよびデータ型マッピングをアノテーション付きファイルへ格納する

AquaLogic Data Services Platform に (GUI のソース メタデータ インポート機能を使用して) メタデータがインポートされるテーブルとビューのそれぞれについて、以下の 2 つのファイルが生成されます。

実行時の接続管理 — 接続の共有

実行時、XQuery エンジンは以下を行います。

データベース接続 (接続プール) は、WebLogic Server の JNDI (Java Naming and Directory Interface) ツリーに登録されます (サーバー上の権限を持つ管理者は、接続プールがアクセス可能な接続プール、データソース、および 接続プールへのアクセスを可能にする JNDI 名を設定できます)。

サブプラン実行の完了時には、通常、接続が解放されて WebLogic Server に戻されることはありません。XQuery エンジンは、XQuery の間ずっと (単に SQL の間だけでなく) 接続を保持し、既に取得された接続を使用して、同じリレーショナル データ ソースに対する後続のクエリを実行できるようにします (これによりパフォーマンスも改善されます)。XQuery エンジンが接続を共有することができるかどうかは、基になるデータ ソースと JDBC ドライバに依存します (表 3-2 を参照)。

データ ソース RDBMS または JDBC ドライバが接続の共有をサポートしない場合、かつ AquaLogic Data Services Platform が同じデータ ソースに対して複数の接続を開いた場合、XQuery エンジンはデータ ソースへの初期接続を XQuery の実行中開いたままにしますが、同じデータ ソースへの後続の接続は XQuery エンジンが SQL の結果全体を受信したらすべて解放されます。初期接続は、使用可能になると、後続の SQL クエリによって再使用されます。

表 3-2 実行時の接続管理
RDBMS
サポート
ベース RDBMS
接続の共有なし。
IBM DB2/NT 8
Microsoft SQL Server 2000
Oracle 8.1.x、9.x、10.x
Sybase Adaptive Server Enterprise 12.5.2 以上
各 JNDI データ ソースに対して単一の共有接続を提供し、各接続は複数のアクティブな SQL クエリをサポートする。
Pointbase 4.4 以上
接続の共有なし。各アクセスには専用の接続が必要。

 


XQuery - SQL データ型マッピング

ベースおよびコア RDBMS サポート」で説明したように、XQuery-SQL データ型マッピングは、RDBMS のバージョンおよび JDBC ドライバによって異なります。コア RDBMS ごとの固有のデータ型マッピング、および任意のベース RDBMS に対する汎用的なマッピングについては、「XQuery - SQL マッピング参照」 で説明します。ただし、XQuery と SQL は以下のようないくつかの点で異なり、その違いが XQuery - SQL 変換に影響することがあります。 これらの違いはすべての RDBMS に該当します。

日付/時間データ型の相違 : タイムゾーンおよび時間精度

XQuery 言語では、日付および時間情報 (時間関連データ) を処理するために SQL より豊富なデータ型が定義されています。これらのデータ型では、より多くの情報 (タイムゾーン データなど)、 またはより高い精度 (時間または日付の一部として端数秒が無制限、など) が提供されます。日付および時間情報には、以下の 3 つの組み込み XQuery データ型があります。

すべての RDBMS には、少なくとも日付データおよび時間データの両方を保持する 1 つのデータ型があります。このデータ型は XQuery の xs:dateTime データ型にマップされます。一部の RDBMS では、日付データと時間データを別々に格納するために追加の SQL データ型が用意されています (表 3-3 を参照)。

(AquaLogic Data Services Platform がサポートするすべての RDBMS のうち、Oracle 9.x 以上だけが、タイムゾーン データ付きのデータ型を用意しています (TIMESTAMP WITH TIMEZONE、TIMESTAMP WITH LOCAL TIMEZONE)。)

表 3-3 時間関連のデータ型マッピング
 
xs:date
xs:dateTime
xs:time
ベース RDBMS
特定の RDBMS 用の JDBC ドライバによって報告される。
IBM DB2/NT 8
DATE
TIMESTAMP
TIME
Microsoft SQL Server 2000
 
DATETIME1、SMALLDATETIME2
 
Oracle 8.1.x
 
DATE3
 
Oracle 9.x、10.x
 
DATE、TIMESTAMP、TIMESTAMP WITH LOCAL TIMEZONE、TIMESTAMPWITH TIMEZONE
 
Pointbase 4.4 以上
DATE
TIMESTAMP
TIME
Sybase Adaptive Server Enterprise 12.5.2 以上
DATE
SMALLDATETIME2、DATETIME1
TIME

13 桁までの端数秒 (ミリ秒) をサポート。

2精度は 1 分単位です。

3日付および時間データの両方を提供するが、端数秒データとタイムゾーン データのどちらもサポートされない (端数秒データは切り捨て)。

AquaLogic Data Services Platform XQuery エンジンは、データや正確さを失うことなく、(たとえば、新しいデータ ソースのメタデータインポート中に) すべてのSQL 日付/時間データ型を XQuery データ型にマップします。

ただし、この逆は行われません。 特定のデータ ソースのための特定の RDBMS (また JDBC ドライバ) によっては、XQuery の時間関連のデータ型を SQL にマップするときのデータ損失を最小限にするため、またタイムゾーン情報を処理するために、XQuery エンジンが追加の処理を実行しなければならない場合があります。

AquaLogic Data Services Platform におけるタイムゾーン情報の処理方法

クエリがタイムゾーン データをサポートしない RDBMS までプッシュダウンされている場合、AquaLogic Data Services Platform XQuery エンジンは日付および時間データを基になるアプリケーション サーバの現地時間に変換し、タイムゾーン情報を削除します。この変換は、以下のようにタイムゾーン データが含まれる日付または時間データがデータ ソースに送られるたびに行われます。

AquaLogic Data Services Platform における端数秒の処理方法

XQuery 言語では端数秒の精度は無制限にサポートされますが、AquaLogic Data Services Platform XQuery エンジンでは最大 7 桁のみ (の端数秒) がサポートされます。ただし、RDBMS によって、端数秒のサポートが 7 桁よりはるかに小さい場合もあります。あるいは、端数秒がまったくサポートされない場合もあります (Oracle 8.1.x など)。XQuery から SQL に変換する際、AquaLogic Data Services Platform は、その RDBMS によってサポートされる精度に端数秒を切り捨てます。

たとえば、Microsoft の DATETIME データ型は 3 桁 (ミリ秒) までの端数時間精度をサポートしているため、AquaLogic Data Services Platform が Microsoft SQL Server 2000 に datetime 値を送信すると、その値はまず現地タイムゾーンに変換され、続いて、端数秒があればすべて許容されている 3 桁のミリ秒に変換されます。

端数秒の精度が要求される (ただし、データ ソースが端数秒を適切にサポートしていない) 場合、fn-bea:fence() 関数を使用して日付/時間データ型と操作のプッシュダウンを無効にし、XQuery エンジンが時間および日付に関連するクエリを処理できるようにします。 (詳細については、3-32 ページの「SQL プッシュダウンの回避」を参照)。

コアおよびベース RDBMS の時間/日付データ型の詳細については、「XQuery - SQL マッピング参照」を参照してください。

式およびデータ型のスコープの差異

XQuery 言語は、式およびデータ型のスコープに関して SQL 言語ほど限定的ではありません。たとえば、ほとんどの RDBMS では、ブールを戻す SQL クエリは、WHERE 句の内部でのみ使用できます。XQuery にはそのような制限はないため、結果として、有効な XQuery 式をプッシュダウンできない場合があります。プッシュできない式およびデータ型には以下のようなものがあります。

 


SQL プッシュダウン: パフォーマンスの最適化

AquaLogic Data Services Platform は、SQL プッシュダウン を実行することによりクエリの最適なパフォーマンスを実現します。プッシュダウンは、ネイティブ SQL クエリをデータ ソースへ送ることにより、 XQuery エンジンで処理するのがクエリへの応答に最小限必要な結果セットのみとなるように、XQuery エンジンの処理をオフロードする最適化技術です。

SQL プッシュダウンは、AquaLogic Data Services Platform XQuery 処理エンジンによって送信および処理されるデータ量を削減します。この技術により、全体的なパフォーマンスが (特にテーブルを結合する場合に) 劇的に改善されます。

たとえば、結合条件の処理のために XQuery エンジンにデータをすべて渡すのではなく、2 つのテーブルに対する結合操作を基になる RDBMS によって実行し、最終的な結果のみを返すことができます。ソート条件もまたデータ ソースによって処理されるため、XQuery エンジン内でデータを再ソートする必要がなくなります。

XQuery エンジンは、すべてのコア RDBMS について、等価の SQL 操作に変換できる XQuery コンストラクトおよび操作を識別します。これには以下のようなものがあります。

すべてのクエリをプッシュダウンできる (またはする必要がある) わけではありません。XQuery エンジンでは、以下はプッシュダウンされません。

この節の残りの部分では、SQL プッシュダウンについて、図 3-4 に示したテーブル構造に基づいた構文例と共に詳細に説明します (読みやすいように、ネームスペース参照はクエリ例内には含まれません)。場合によっては、クエリは SQL としてプッシュダウンされないものの、カラム名などのクエリの一部分がプロジェクト リストにプッシュされます。

図 3-4 SQL プッシュダウン例のテーブル構造

SQL プッシュダウン例のテーブル構造

関数および演算子プッシュダウン

XQuery 関数および演算子は以下の場合のみ SQL に変換されます。

表 3-5 に、XQuery ステートメントとそれに対応する「プッシュダウン」 (SQL 変換) を示します (Oracle 構文を使用)。

表 3-5 関数プッシュダウン例
XQuery ステートメント
SQL 変換 (Oracle 構文)
for $c in CUSTOMER()
return lower-case($c/LAST_NAME)
SELECT LOWER(t1."LAST_NAME") AS c1
FROM "CUSTOMER" t1

関数または演算子に対するいくつかの引数が直接プッシュできないが、パラメータで置き換え可能な場合、XQuery エンジンは引数をパラメータで置き換えて SQL をプッシュダウンします。たとえば、XQuery の string-join() 関数に等価の SQL が明示的には存在しない場合、パラメータで置き換えられます (詳細については、表 3-6 を参照)。

表 3-6 外部変数プッシュダウン
XQuery ステートメント
SQL ステートメント
declare variable $p as xs:string external;
...
for $c in CUSTOMER()
where starts-with($c/LAST_NAME, string-join( ("a", "b"), $p ))
return $c/FIRST_NAME
SELECT t1."FIRST_NAME" AS c1
FROM "CUSTOMER" t1
WHERE t1."LAST_NAME" LIKE ?

集約関数

AquaLogic Data Services Platform は、XQuery 1.0 および XPath 2.0 の集約関数を、対応する SQL 集約関数に変換します (表 3-7)。  

表 3-7 集約関数
XQuery 集約関数
SQL 集約関数
fn:avg()
AVG()
fn:count()
COUNT()
fn:max()
MAX()
fn:min()
MIN()
fn:sum()
SUM()
fn:count(fn:distinct-values()
COUNT(DISTINCT ...)

表 3-8 に示すように、distinct-values() XQuery 集約関数は fn:count() 関数と組み合わされたうえで、SQL COUNT(DISTINCT...) 操作に変換されます。集約関数が他の式と組み合わされて SQL プッシュダウンの結果に影響を与える例については、「グループ化および集約」 を参照してください。

生成される SQL ステートメント内のパラメータ

AquaLogic Data Services Platform XQuery エンジンは、SQL エンジンの使用に必要な場合、変数、関数、演算子、およびキャスト演算からパラメータを生成します。関数に対する引数のすべてがパラメータである場合、関数全体がパラメータとしてプッシュされます。

プッシュダウンできる機能はデータベースによって異なります。詳細については、「XQuery - SQL マッピング参照」を参照してください。

キャスト演算プッシュダウン

関数および演算子と同様に、キャスト演算プッシュダウンのサポートは RDBMS によって異なりますが、キャストのプッシュダウンはコア (ベースではなく) RDBMS でのみ使用可能です。XQuery エンジンでは、データ ソース RDBMS が以下の場合にキャスト演算をプッシュダウンできます。

表 3-8 には、XQuery のキャストが Microsoft SQL Server 2000 データ ソースにプッシュダウンされる方法の例を示しています。

表 3-8 キャスト演算プッシュダウン
XQuery ステートメント
SQL ステートメント (Microsoft SQL Server 2000 構文)
for $c in CUSTOMER()
where xs:string($c/ZIP_CODE) eq "95131"
return $c/CUSTOMER_ID
SELECT t1."CUSTOMER_ID" AS c1
FROM "CUSTOMER" t1
WHERE CAST(t1."ZIP_CODE" AS VARCHAR) = '95131'

パス式プッシュダウン

XQuery エンジンは、対応する行要素の子である XML 要素にテーブルのカラムをマップします。単純な XQuery パス式は、XQuery エンジンによって カラム アクセサとして認識されます。たとえば、$c/ZIP_CODE および $c/LAST_NAME (表 3-10 を参照) によって、ZIP_CODE および LAST_NAME カラムにアクセスできるようになります。

定数プッシュダウン

データ ソースが等価の SQL データ型を持っている場合のみ、AquaLogic Data Services Platform XQuery エンジンは XQuery 定数を SQL 定数に変換します。表 3-9 には FLWOR 式で使用される定数の例と、その定数を SQL ステートメントに変換する方法を示しています。

表 3-9 定数の SQL プッシュダウン
XQuery ステートメント
SQL ステートメント
for $c in CUSTOMER()
where $c/ZIP_CODE eq 95131
return $c/LAST_NAME
SELECT t1."LAST_NAME" AS c1
FROM "CUSTOMER" t1
WHERE t1."ZIP_CODE" = 95131

変数プッシュダウン

変数データ型が XQuery エンジンでサポートされ、かつ以下の場合、XQuery 式の外部変数と内部変数はどちらも (生成された SQL 内の) SQL パラメータ に変換できます。

表 3-10 に、変数プッシュダウンの例を示します。

表 3-10 変数プッシュダウン
XQuery ステートメント
SQL ステートメント
declare variable $extVar
as xs:string external;
for $c in CUSTOMER()
where $c/CUSTOMER_ID eq $extVar
return $c/LAST_NAME
SELECT t1."LAST_NAME" as c1
FROM "CUSTOMER" t1
WHERE t1."CUSTOMER_ID" = ?

共通のクエリ パターン

それぞれのリレーショナル データ ソースで、プッシュダウンされる式の厳密なセットは基になる RDBMS の機能によって異なります。 詳細については、「XQuery エンジンおよび SQL」を参照してください。

単純射影クエリ

表 3-11 に示された各 XQuery 例は、CUSTOMER テーブルから LAST_NAME カラムの値を含む要素を戻します。すべての場合において、XQuery エンジンが生成する SQL ステートメントは同じです (表 3-11 を参照)。

表 3-11 射影クエリ
XQuery ステートメント
SQL ステートメント
for $c in CUSTOMER() return $c/LAST_NAME
SELECT t1."LAST_NAME" AS
c1 FROM "CUSTOMER" t1
CUSTOMER()/LAST_NAME
for $c in CUSTOMER() return data($c/LAST_NAME)
data(CUSTOMER()/LAST_NAME)

最初の 2 つのクエリと残りの 2 つのクエリの違いは、結果を値のみに限定するために fn:data() 関数がクエリで使用されていることです。fn:data() 関数がないと、結果は対応するカラム値を含む <LAST_NAME> 要素のリストになります。カラム値が NULL の場合、要素はスキップされます。fn:data() 関数を使用すると、結果は実際の値になります。

Where 句プッシュダウン

XQuery の where 句は、通常 SQL の WHERE 句に変換されます。以下の場合、XQuery の where 句は SQL としてプッシュダウンされます。

表 3-12 に、where 句プッシュダウンの例を示します。

表 3-12 Where 句プッシュダウン
XQuery ステートメント
SQL ステートメント
for $c in CUSTOMER()
where $c/CUSTOMER_ID eq "CUSTOMER01"
return $c/LAST_NAME
SELECT t1."LAST_NAME" AS c1
FROM "CUSTOMER" t1
WHERE t1."CUSTOMER_ID" = `CUSTOMER01'
for $c in CUSTOMER()
where year-from-dateTime($c/BIRTH_DAY)
eq
year-from-date(current-date())
return
$c/LAST_NAME
(DB2 構文)
SELECT t1."LAST_NAME" AS c1
FROM "CUSTOMER" t1
WHERE
YEAR(t1."BIRTH_DAY") = ?

ただし、WHERE 句が group by 句の後にある場合、その WHERE 句は HAVING 句に変換されます。「ネストした Where 句を含む Group-By を SQL HAVING 句に変換する」を参照してください。

Order By 句プッシュダウン

XQuery の order by 式には以下が含まれます。

XQuery エンジンは、ソート式が以下の場合に、プロパティを含むソート式の SQL をプッシュダウンできます。

表 3-13 には、order by 句プッシュダウンの例を示します。

表 3-13 Order By プッシュダウン
XQuery ステートメント
SQL ステートメント
for $c in CUSTOMER()
 order by $c/CUSTOMER_ID  descending
return $c/CUSTOMER_ID
SELECT t1."CUSTOMER_ID" AS c1
FROM "CUSTOMER" t1
ORDER BY t1."CUSTOMER_ID" DESC

表 3-14 は、RDBMS で NULL のソート順を動的に設定可能な場合、XQuery 句内の NULL が可能なカラム (ADDRESS2) でソートしたときに行われる SQL プッシュダウンの例を示します。

表 3-14 NULL のソート順が動的に設定される場合の Order By クエリ
XQuery ステートメント
SQL ステートメント (Oracle 構文)
for $c in CUSTOMER()
order by $c/ADDRESS2 ascending
empty greatest
return $c/CUSTOMER_ID, $c/ADDRESS2
SELECT t1."CUSTOMER_ID" AS c1,
t1."ADDRESS2" AS c2
FROM "CUSTOMER" t1
ORDER BY t1."ADDRESS2" ASC NULLS LAST

データソース RDBMS で必要な空 (NULL) のソート順がサポートされていない場合、order by はプッシュダウンされません。

この他の最適化として、AquaLogic Data Services Platform XQuery エンジンは、生成された SQL ステートメントに order by 句を挿入し (元の XQuery ステートメントに order by 句が含まれていない場合でも)、コストの高いソート操作を RDBMS にオフロードします。挿入は、XQuery オプティマイザによって実行前に自動的に行われます。これはクエリ プラン ビューでも表示できます。

内部結合プッシュダウン

複数のソースからデータを結合するのは、きわめて一般的なデータ統合タスクです。SQL では、内部結合とは、あるテーブル (またはビュー) の各行を別のテーブルまたはビューの対応する 1 つ以上の行に関連付けることです。XQuery では、内部結合は、データ ソースに対して反復する for 句、結合述部を指定する where 句、およびデータ値を戻す return 句を含む FLWR 式として表すことができます。

2 つのリレーショナル ソースが同じデータベースにある場合、データ ソースの RDBMS に応じて、SQL-92 構文または SQL-89 構文のどちらかを使用し、単一の SQL ステートメントとして内部結合をプッシュダウンできることがあります。

以下の場合、内部結合はプッシュダウンできます。

図 3-15 の例は、2 つのブランチ間の内部結合を示しますが、XQuery エンジンもまた、それぞれに異なる for ステートメントを含む n ブランチの結合をサポートします。 表 3-16 も参照してください。

表 3-16 XQuery 内部結合を SQL-92 構文および SQL-89 構文として表現
SQL-92 構文
SQL-89 構文
SELECT t1."LAST_NAME" AS c1, t2."ORDER_ID" AS c2
FROM "CUSTOMER" t1 JOIN "CUST_ORDER" t2
ON t1."CUSTOMER_ID" = t2."CUSTOMER_ID"
SELECT t1."LAST_NAME" AS c1, t2."ORDER_ID" AS c2
FROM "CUSTOMER" t1, "CUST_ORDER" t2
WHERE t1."CUSTOMER_ID" = t2."CUSTOMER_ID"

外部結合プッシュダウン

XQuery エンジンは、以下の場合、ネストされた FLWR 式を外部結合として解釈し (図 3-17 を参照)、 データ ソースに対して SQL を生成できます。

XQuery エンジンで生成された SQL コードは、ソース データベースでサポートされる SQL 方言によって異なります (詳細については、「XQuery - SQL マッピング参照」を参照)。 表 3-18 には SQL-92 構文、および 図 3-17 に示されたクエリの独自構文の例を示します。

表 3-18 SQL-92 と独自の外部結合の構文比較
SQL-92 構文
Oracle 8 構文
SELECT t1."LAST_NAME" AS c1, t2."ORDER_ID" AS c2
FROM "CUSTOMER" t1 OUTER JOIN "CUST_ORDER" t2
ON t1."CUSTOMER_ID" = t2."CUSTOMER_ID"
SELECT t1."LAST_NAME" AS c1, t2."ORDER_ID" AS c2
FROM "CUSTOMER" t1, "CUST_ORDER" t2
WHERE t1."CUSTOMER_ID" = t2."CUSTOMER_ID" (+)

外部結合パターンのバリエーションは、等価の XQuery 式を使用して元のクエリから取得されます。図 3-19 は、図 3-17 に示したパターンと等価のクエリの例です。 このクエリでも外部結合を含む SQL ステートメントが生成されます。

図 3-19 外部結合パターン

外部結合パターン

Semi-Join (半結合) および Anti-Semi-Join

semi-join は、結合条件が満たされている場合に、結合条件の 1 つのブランチからデータを戻します。 anti-semi-join は、結合条件が満たされていない場合に、1 つのブランチからデータを戻します。XQuery 言語には semi-join と anti-semi-join 用の特定のコンストラクトはありませんが、以下を前提として、XQuery エンジンはいくつかの特定の FLWR パターンを SQL の semi-join または anti-semi-join パターンに変換します。

XQuery は、内部存在量化式が含まれる FLWR クエリを semi-join として解釈し、その式を WHERE 句内で EXISTS チェックを行う SQL クエリに変換します。

全称量化式もサポートされますが、その SQL 生成はもう少し複雑です。XQuery エンジンは where 句内に exist() または empty() 述部を持つ FLWR を semi-join に変換します。表 3-20 に、複数のパターン例を示します。

表 3-20 Semi-Join と Anti-Semi-Join SQL を生成できるさまざまな XQuery パターン
 
XQuery ステートメント
SQL ステートメント
存在 (some) 量化子を持つ FLWR [semi-join]
for $customer in CUSTOMER()
where
some $c_order in CUST_ORDER()
satisfies ($customer/CUSTOMER_ID eq $c_order/ORDER_ID)
and
($c_order/STATUS eq "OPEN")
return
$customer/CUSTOMER_ID
SELECT t1."CUSTOMER_ID" AS c1
FROM "CUSTOMER" t1
WHERE EXISTS(
SELECT 1
FROM "CUST_ORDER" t2
WHERE t1."CUSTOMER_ID" = t2."CUSTOMER_ID" AND t2."STATUS" = 'OPEN'
)
否定の存在量化子を持つ FLWR [anti-semi join]
for $customer in CUSTOMER()
where not(
some $c_order in CUST_ORDER()
satisfies ($customer/CUSTOMER_ID eq $c_order/ORDER_ID)
and
($c_order/STATUS eq "OPEN")
)
return
$customer/CUSTOMER_ID
SELECT t1."CUSTOMER_ID" AS c1
FROM "CUSTOMER" t1
WHERE NOT EXISTS(
SELECT 1
FROM "CUST_ORDER" t2
WHERE t1."CUSTOMER_ID" = t2."CUSTOMER_ID" AND t2."STATUS" = 'OPEN'
)
全称 (every) 量化式を持つ FLWR
for $customer in CUSTOMER()
where
every $c_order in CUST_ORDER()
satisfies ($customer/CUSTOMER_ID eq $c_order/ORDER_ID) and
($c_order/STATUS eq "OPEN")
return
$customer/CUSTOMER_ID
SELECT t1."CUSTOMER_ID" AS c1
FROM "CUSTOMER" t1
WHERE NOT EXISTS(
SELECT 1
FROM "CUST_ORDER" t2
WHERE NOT(t1."CUSTOMER_ID" = t2."CUSTOMER_ID" AND t2."STATUS" = 'OPEN')
)
exists() 述部を持つ FLWR
for $customer in CUSTOMER()
where exists(
for $c_order in CUST_ORDER()
where ($customer/CUSTOMER_ID eq $c_order/ORDER_ID) and
($c_order/STATUS eq "OPEN")
return $c_order
)
return
$customer/CUSTOMER_ID
SELECT t1."CUSTOMER_ID" AS c1
FROM "CUSTOMER" t1
WHERE EXISTS(
SELECT 1
FROM "CUST_ORDER" t2
WHERE t1."CUSTOMER_ID" = t2."CUSTOMER_ID" AND t2."STATUS" = 'OPEN'
)
empty() 述部を持つ FLWR
for $customer in CUSTOMER()
where empty(
for $c_order in CUST_ORDER()
where ($customer/CUSTOMER_ID eq $c_order/ORDER_ID) and
($c_order/STATUS eq "OPEN")
return $c_order
)
return
$customer/CUSTOMER_ID
SELECT t1."CUSTOMER_ID" AS c1
FROM "CUSTOMER" t1
WHERE NOT(EXISTS(
SELECT 1
FROM "CUST_ORDER" t2
WHERE t1."CUSTOMER_ID" = t2."CUSTOMER_ID" AND t2."STATUS" = 'OPEN'
))

グループ化および集約

XQuery エンジンでは、group by プッシュダウンおよび集約関数プッシュダウンについて複数のパターンがサポートされます。

Group By プッシュダウン

Group By 句 は XQuery 言語に対する BEA の機能拡張です (詳細については、2-29 ページの「汎用 FLWGOR (group by)」を参照)。XQuery エンジンは、プッシュダウンとクエリ実行の効率を高めるために、暗黙的に group by 式をいくつかのパターンに追加します。

図 3-21 Group By を含む XQuery

Group By を含む XQuery

以下の場合、XQuery エンジンは group-by 句を等価の SQL GROUP BY に変換します。

図 3-21 のクエリは上記の条件を満たしているので、以下の SQL ステートメントが生成されます。

SELECT t1."CATEGORY" AS c1, COUNT(*) AS c2
FROM "PRODUCT" t1
GROUP BY t1."CATEGORY"

group-by プッシュダウンは Distinct-by プッシュダウンと緊密に関係しています。group-by 句にパーティション変数が含まれていない場合、XQuery エンジンで DISTINCT キーワードを含む SQL が生成されます。 これについては次の節で説明します。

Distinct-by プッシュダウン

Group By 句 (パーティション定義なし) を含む XQuery は、結果の重複をなくすために SQL DISTINCT キーワードを使用する SQL クエリに生成できます。たとえば、表 3-22 の XQuery ステートメントは group-by 句を使用していますが、パーティション定義がないので、AquaLogic Data Services Platform で作成された SQL ステートメントが DISTINCT キーワードを使用して結果を洗練します。

表 3-22 Distinct By プッシュダウン
XQuery ステートメント
SQL ステートメント
for $product in PRODUCT()
group by $product/CATEGORY_ID as $category
return $category
SELECT DISTINCT t1."CATEGORY_ID" AS c1
FROM "PRODUCT" t1

単純集約パターン

データ ソースからの単一カラムに対して動作する集約関数は、XQuery エンジンがサポートする集約パターンのなかで最も単純なものの 1 つですが、あまり直感的な方法ではサポートされていません。この集約パターンでは、定数が単一のグループ化式として使用されます (...GROUP ...BY n)。RDBMS が 定数に対する GROUP BY 操作またはサブ句内のサブクエリをサポートしている場合、XQuery エンジンは SQL をプッシュダウンできます (表 3-23 を参照)。

表 3-23 集約プッシュダウン
XQuery ステートメント
SQL ステートメント1
SQL ステートメント2
for $product in PRODUCT()
group $product/LIST_PRICE
as $price_group
by 1
return min($price_group)
SELECT MIN(t1."LIST_PRICE") AS c1
FROM "PRODUCT" t1
GROUP BY 1
SELECT MIN(t2.c2) AS c3
FROM (
SELECT 1 AS c1, t1."LIST_PRICE" AS c2
FROM "PRODUCT" t1
) t2
GROUP BY t2.c1

1RDBMS は GROUP BY 定数をサポートする。

2RDBMS は GROUP BY をサポートしないが、FROM 句内のサブクエリをサポートする。

ネストした Where 句を含む Group-By を SQL HAVING 句に変換する

リレーショナル データ ソースがネストされた WHERE 句をサポートしている場合、where 句が XQuery - SQL 変換のその他の条件を満たしていれば、XQuery エンジンは group-by 句の後にある where 句を SQL HAVING 句に変換できます (表 3-24 を参照)。

表 3-24 ネストされた WHERE 句
XQuery ステートメント
SQL ステートメント
for $product in PRODUCT()
group $product/LIST_PRICE as $price_group
by $product/CATEGORY as $category
where max($price_group) gt 1000
return
<t>
{
$category,
min($price_group)
}
</t>
SELECT t1."CATEGORY" AS c1, MIN(t1."LIST_PRICE") AS c2
FROM "PRODUCT" t1
GROUP BY t1."CATEGORY"
HAVING MAX(t1."LIST_PRICE") > 1000

集約パターンを含む外部結合

XQuery エンジンがサポートするもう 1 つの共有パターンは、右ブランチの集約を含む外部結合です。 XQuery では、このパターンは内部レベルの集約関数が含まれるネストされた FLWR 式として表されます (表 3-25)。

表 3-25 集約を含む外部結合
XQuery ステートメント
SQL ステートメント
for $customer in CUSTOMER()
return
<customer>
<name>{ data($customer/LAST_NAME) }</name>
<order-amount>
{
sum(
for $c_order in CUST_ORDER()
where $customer/CUSTOMER_ID eq $c_order/CUSTOMER_ID
return $c_order/ORDER_AMOUNT
)
}
</order-amount>
</customer>
SELECT t1."LAST_NAME" AS c1, SUM(t2."ORDER_AMOUNT") AS c2
FROM "CUSTOMER" t1
LEFT OUTER JOIN "CUST_ORDER" t2
ON (t2."CUSTOMER_ID" = t1."CUSTOMER_ID")
GROUP BY t1."CUSTOMER_ID"

このタイプのクエリを使用して、クエリをデータ ソースへできるだけプッシュするため、XQuery エンジンは最初に外部結合を評価し、続いて集約を計算するために、左ブランチの主キーのカラムに対して group-by を実行します。XQuery エンジンは、クエリの左ブランチに主キーのカラムがある場合のみ、この最適化を実行できます。表 3-26 では、CUSTOMER が該当するため、最適化が行われます。

最終的には、XML の作成のみが XQuery エンジンによって実行されます。

If-Then-Else パターン

SQL:1992 で導入された CASE 式によって、プロシージャを呼び出す必要なしに、if-then-else ロジックを SQL ステートメントで使用できます。CASE 式は値のリストと選択肢を関連付けます。

以下の場合、XQuery if-then-else パターンを SQL CASE 式に変換できます。

then 式および else 式にはパラメータを組み込む、またはパラメータのみを含めることができます。if-then-else 式がデータ ソースに依存しない場合、式全体がパラメータとしてプッシュされます。

表 3-26 に例を示します。

表 3-26 If-Then-Else プッシュダウン
XQuery ステートメント
SQL ステートメント
for $i in CUST_ORDER()
return
if ($i/STATUS eq "SHIPPED")
then data($i/STATUS)
else data($i/CUSTOMER_ID)
SELECT
CASE WHEN (t1."STATUS" = 'SHIPPED')
THEN t1."STATUS"
ELSE t1."CUSTOMER_ID" END AS c1
FROM "CUST_ORDER" t1

Subsequence プッシュダウン

典型的な RDBMS アプリケーションでは、結果へのページ付けはきわめて一般的です (印刷等の目的で 1 ページ当たり 20 顧客レコードのみを出力する、など)。XQuery は subsequence( ) 関数でこのニーズを満たします。XQuery は、表 3-27 および 表 3-28 に示す 2 つの異なる subsequence 関数を提供します。

表 3-27 XQuery Subsequence 関数の 2 引数および 3 引数のバリアント
2 引数バリアント
3 引数バリアント
fn:subsequence(
 $sourceSeq as item()*,
 $startingLoc as xs:double
) as item()*
fn:subsequence(
 $sourceSeq as item()*,
 $startingLoc as xs:double,
 $length as xs:double
) as item()*

表 3-28 Subsequence プッシュダウン
XQuery ステートメント
SQL ステートメント (Oracle)
let $s :=
  for $i in t2:PRODUCT()
 order by $i/LIST_PRICE descending
 return $i
for $p in subsequence($s, 1, 10)
return <product>
  <name>
   { data($p/PRODUCT_NAME) } </name>
  <price>
   { data($p/LIST_PRICE) }
  </price>
</product>
};
SELECT t3.c1, t3.c2 FROM(
 SELECT ROWNUM as c3, t2.c1, t2.c2
  FROM(
   SELECT t1."LIST_PRICE" as c1,
    t1."PRODUCT_NAME" as c2
    FROM "RTLALL"."PRODUCT" t1
    ORDER BY t1."LIST_PRICE" DESC
   )t2
  )t3
WHERE(t3.c3 <11)

2 引数バリアントは、$startingLoc を開始点として入力シーケンスの残りの項目を戻します。3 引数バリアントは、$startingLoc を開始点として入力シーケンスの $length 項目を戻します。 表 3-29 では、具体的なクエリで使用される subsequence 関数のさまざまな例を示します。

表 3-29 Subsequence 関数を使用する XQuery の例
クエリ ステートメント
XQuery 式
最も価格の高い 10 製品のみを戻す。
let $s :=
  for $i in PRODUCT()
  order by $i/LIST_PRICE descending
  return $i
for $p in subsequence($s, 1, 10)
return <product>
  <name> { data($p/PRODUCT_NAME) } </name>
  <price> { data($p/LIST_PRICE) } </price>
</product>
最も価格の高い 10 製品のそれぞれに対して開かれたサービス ケースすべてを戻す (外部結合)。
let $s :=
  for $i in PRODUCT()
  order by $i/LIST_PRICE descending
  return $i
for $p in subsequence($s, 1, 10)
return <product>
<name> { data($p/PRODUCT_NAME) } </name>
{
  for $sc in SERVICE_CASE()
  where $p/PRODUCT_ID eq $sc/PRODUCT_ID and
    $sc/STATUS = `Open'
  return <case>{ data($sc/CASE_ID) }</case>
}
</product>
最も価格の高い 10 製品のそれぞれに対して開かれたサービス ケースの総数を戻す (集約)。
let $s :=
  for $i in PRODUCT()
  order by $i/LIST_PRICE descending
  return $i
for $p in subsequence($s, 1, 10)
return
<product>
<name> { data($p/PRODUCT_NAME) } </name>
{
 let $scs :=
  for $sc in SERVICE_CASE()
  where $p/PRODUCT_ID eq $sc/PRODUCT_ID and $sc/STATUS = `Open'
  return $sc
 return <case_count>{ count($scs) }</case_count>
}
</product>

以下の場合、XQuery subsequence パターンは SQL subsequence 式に変換できます。

AquaLogic Data Services Platform では subsequence パターンを基になる RDBMS にプッシュダウンできるため、その RDBMS がサポートする限度までパフォーマンスが高められます。

注意 : Subsequence プッシュダウンは、Pointbase、 Sybase、 またはどのベース RDBMS でもサポートされません (その他のコアおよびベース RDBMS 情報については、「XQuery - SQL マッピング参照」を参照)。

Direct SQL データ サービスおよびプッシュダウン

AquaLogic Data Services Platform では、リレーショナル テーブルとビューからだけでなく、SQL クエリからもデータ サービスを作成できます。この Direct SQL データ サービスは、呼び出されると、XQuery エンジンによっても構成することができ、以下の場合、対象 RDBMS にネイティブ SQL としてプッシュダウンできます。

RDBMS がサブクエリ (FROM 句) をサポートしていない場合、プッシュダウンは発生しません。

たとえば、ユーザ定義 SQL クエリ 「recent_order」は、リレーショナル ソースとして以下のようにコンフィグレーションされます。

SELECT * from RECENT_ORDER

データ サービスで作成される XQuery と、結果として生成され、XQuery エンジンによってプッシュダウンされる SQL を表 3-30 に示します。  

表 3-30 Direct SQL データ サービスの例
XQuery ステートメント
SQL ステートメント
declare variable
$external_variable as xs:string external;
for $recent_order in RECENT_ORDER()
where $recent_order/ORDER_ID eq $external_variable
return $recent_order/ORDER_AMOUNT
SELECT t1."ORDER_AMOUNT" AS c1
FROM (
SELECT * FROM RECENT_ORDER
) t1
WHERE t1."ORDER_ID" = ?

Direct SQL をベースにした SQL プッシュダウンは、単純な select-project クエリに限定されません。操作で、テーブルおよびビュー ソースについてプッシュダウンがサポートされていれば、Direct SQL に対して作成されたデータ サービスについてもサポートされます。たとえば、表 3-31 は、結合クエリと生成された結果です。

表 3-31 結合条件を含む Direct SQL データ サービス
XQuery ステートメント
SQL ステートメント
for $customer in CUSTOMER()
for $recent_order in RECENT_ORDER()
where $customer/CUSTOMER_ID eq $recent_order/CUSTOMER_ID
return
<t>{ $customer/CUSTOMER_ID, $recent_order/ORDER_ID }</t>
SELECT t1."CUSTOMER_ID" AS c1, t2."ORDER_ID" AS c2
FROM "CUSTOMER" t1
JOIN (
SELECT * FROM RECENT_ORDER
) t2
ON t1."CUSTOMER_ID" = t2."CUSTOMER_ID"

分散クエリ プッシュダウン

AquaLogic Data Services Platform では、可能な場合は常に SQL プッシュダウンを使用して、基になるデータ ソース RDBMS にクエリ処理をオフロードします。ただし、「XQuery エンジンにおける SQL データソースのサポート方法」で説明したように、SQL プッシュダウンはいつでも可能なわけではなく、また有効なわけでもありません。たとえば、2 つのデータ ソースが 2 つの異なるシステム上で稼動している場合、またはクエリがリレーショナル データを非リレーショナル データと結合する場合、SQL プッシュダウンでパフォーマンス上のメリットが得られないことがあります。

このような場合、AquaLogic Data Services Platform はクエリの外側部分 (左ブランチ) をバッチ処理し、データのクラスタ (チャンク) をパラメータとして右ブランチに送信するための特別な手法を使用します (表 3-32 を参照)。以下の場合、XQuery エンジンは分散クエリに対して、この最適化手法 (クラスタ化パラメータ パッシング結合または PPK と呼ばれる) を選択します。

これらすべての条件が満たさなれない限り、XQuery エンジンは最適化手法を使用できませんが、代わりに単一パラメータ結合 (PP1 join) が使用されます。

 


SQL プッシュダウンの回避

XQuery エンジンが SQL プッシュダウンのクエリ フラグメントを評価するときに無視しなければならない XQuery コードのセクションを区別するために、開発者は fn-bea:fence() 関数 (XQuery 関数および操作に対する BEA 機能拡張) を使用して SQL プッシュダウンをコントロールできます。

表 3-33 の例のように、upper-case 関数は RDBMS までプッシュダウンできますが、そのプッシュダウンは fence() 関数によって阻止され、また、upper-case 関数は XQuery エンジンによって実行されます。lower-case 関数を含むフラグメントだけが SQL プッシュダウンとしてクエリ プランに含まれます。SQL の結果が XQuery エンジンに返され、XQuery エンジンはその結果に対して XQuery upper-case 関数を使用します。

表 3-3 fn-bea:fence() 関数の使用
XQuery ステートメント
SQL ステートメント
for $c in CUSTOMER()
return
upper-case(
fn-bea:fence(
lower-case( $c/LAST_NAME )
)
)
SELECT LOWER(t1."LAST_NAME") AS c1
FROM "CUSTOMER" t1

SQL をそのまま RDBMS へ送信する場合は必ず fence() 関数を使用します。たとえば、ヒントと Oracle のルール ベース オプティマイザを使用する Oracle 8.5.x RDBMS にアクセスする場合、ヒント付きの SQL クエリを fence() 関数でラップしてデータ ソースへ送信する必要があります。

特定の句について SQL プッシュダウンを回避するには、以下に示すように fence() 関数を句の最上部に使用し、句を別個の FLWOR 式として抽出します。

for $x in 
fn-bea:fence
 (
  for $c in CUSTOMER()
  return $c/LAST_NAME
 )
order by $x
return $x

リレーショナル データ ソースを使用するデータ サービスを開発する際、fence() 関数を使用した結果を確認するには AquaLogic Data Services Platform クエリ プラン ビューを使用します (図 3-34)。この例では、order by 句は SQL としてプッシュダウンされずに XQuery エンジンで実行されます。

図 3-34 fn-bea:fence() 関数がない場合 (左) とある場合 (右) の XQuery プランの例

fn-bea:fence() 関数がある場合 (左) とない場合 (右) の XQuery プランの例

図 3-34 の SQL 部分にある赤い三角が XQuery ステートメントから where 句が欠落していることに注意を促すアラートです。


  Back to Top       previous  next